00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "config.h"
00024
00025 #include <stdio.h>
00026 #include <errno.h>
00027 #include <slang.h>
00028
00029 #include <png.h>
00030
00031 SLANG_MODULE(png);
00032
00033 static char *Module_Version_String = "0.1.1";
00034 #define MODULE_VERSION_NUMBER (0*10000 + 1*100 + 1)
00035
00036
00037
00038 static int Is_Little_Endian;
00039
00040 static void byte_swap32 (unsigned char *p, unsigned char *t, unsigned int n)
00041 {
00042 unsigned char *pmax, ch;
00043
00044
00045 pmax = p + 4 * n;
00046 while (p < pmax)
00047 {
00048 ch = *p;
00049 *t = *(p + 3);
00050 *(t + 3) = ch;
00051
00052 ch = *(p + 1);
00053 *(t + 1) = *(p + 2);
00054 *(t + 2) = ch;
00055 p += 4;
00056 t += 4;
00057 }
00058 }
00059
00060 static void byte_swap16 (unsigned char *p, unsigned char *t, unsigned int n)
00061 {
00062 unsigned char *pmax, ch;
00063
00064 pmax = p + 2 * n;
00065 while (p < pmax)
00066 {
00067 ch = *p;
00068 *t = *(p + 1);
00069 *(t + 1) = ch;
00070 p += 2;
00071 t += 2;
00072 }
00073 }
00074
00075
00076
00077
00078
00079 typedef struct
00080 {
00081 FILE *fp;
00082 int mode;
00083 png_struct *png;
00084 png_info *info;
00085 }
00086 Png_Type;
00087
00088 static void free_png_type (Png_Type *p)
00089 {
00090 if (p == NULL)
00091 return;
00092 if (p->png != NULL)
00093 {
00094 if (p->mode == 'r')
00095 {
00096 if (p->info != NULL)
00097 png_destroy_read_struct (&p->png, &p->info, NULL);
00098 else
00099 png_destroy_read_struct (&p->png, NULL, NULL);
00100 }
00101 else
00102 {
00103 if (p->info != NULL)
00104 png_destroy_write_struct (&p->png, &p->info);
00105 else
00106 png_destroy_write_struct (&p->png, NULL);
00107 }
00108 }
00109
00110 if (p->fp != NULL)
00111 fclose (p->fp);
00112
00113 SLfree ((char *) p);
00114 }
00115
00116 static Png_Type *alloc_png_type (int mode)
00117 {
00118 Png_Type *p;
00119
00120 if (NULL != (p = (Png_Type *)SLmalloc (sizeof (Png_Type))))
00121 {
00122 memset ((char *) p, 0, sizeof (Png_Type));
00123 p->mode = mode;
00124 }
00125 return p;
00126 }
00127
00128
00129
00130
00131 static png_byte **allocate_image_pointers (png_uint_32 height, png_byte *data, png_uint_32 rowbytes, int flip)
00132 {
00133 png_byte **image_pointers;
00134 png_uint_32 i;
00135
00136 if (NULL == (image_pointers = (png_byte **) SLmalloc (height * sizeof (png_byte *))))
00137 return NULL;
00138
00139 if (flip)
00140 {
00141 i = height;
00142 while (i != 0)
00143 {
00144 i--;
00145 image_pointers[i] = data;
00146 data += rowbytes;
00147 }
00148 return image_pointers;
00149 }
00150 for (i = 0; i < height; i++)
00151 {
00152 image_pointers[i] = data;
00153 data += rowbytes;
00154 }
00155 return image_pointers;
00156 }
00157
00158 static void free_image_pointers (png_byte **image_pointers)
00159 {
00160 if (image_pointers == NULL)
00161 return;
00162
00163 SLfree ((char *) image_pointers);
00164 }
00165
00166
00167
00168 static Png_Type *open_png_file (char *file)
00169 {
00170 png_byte header[8];
00171 Png_Type *p;
00172
00173 if (NULL == (p = alloc_png_type ('r')))
00174 return NULL;
00175
00176 if ((NULL == (p->fp = fopen (file, "rb")))
00177 || (8 != fread (header, 1, 8, p->fp))
00178 || (0 != png_sig_cmp(header, 0, 8)))
00179 {
00180 SLang_verror (SL_Open_Error, "Unable to open %s as a png file", file);
00181 free_png_type (p);
00182 return NULL;
00183 }
00184
00185 if (NULL == (p->png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)))
00186 {
00187 SLang_verror (SL_Open_Error, "Unable to read png structure from %s", file);
00188 free_png_type (p);
00189 return NULL;
00190 }
00191
00192 if (NULL == (p->info = png_create_info_struct (p->png)))
00193 {
00194 SLang_verror (SL_Read_Error, "Unable to create info struct for %s", file);
00195 free_png_type (p);
00196 return NULL;
00197 }
00198
00199 return p;
00200 }
00201
00202 static void fixup_array_rgb (SLang_Array_Type *at)
00203 {
00204 SLindex_Type num_rows, num_cols, row;
00205 unsigned char *data;
00206
00207 num_rows = at->dims[0];
00208 num_cols = at->dims[1];
00209 data = (unsigned char *) at->data;
00210
00211
00212 for (row = 0; row < num_rows; row++)
00213 {
00214 unsigned char *p = data + 3*num_cols;
00215 unsigned char *q = p + num_cols;
00216 while (p != data)
00217 {
00218 *(--q) = *(--p);
00219 *(--q) = *(--p);
00220 *(--q) = *(--p);
00221 *(--q) = 0;
00222 }
00223 data += 4*num_cols;
00224 }
00225
00226 if (Is_Little_Endian)
00227 byte_swap32 ((unsigned char *)at->data, (unsigned char *)at->data, at->num_elements);
00228 }
00229
00230 static void fixup_array_rgba (SLang_Array_Type *at)
00231 {
00232 unsigned char *data, *data_max;
00233
00234 data = (unsigned char *) at->data;
00235 data_max = data + at->num_elements;
00236
00237
00238 while (data < data_max)
00239 {
00240 unsigned char a = data[3];
00241 data[3] = data[2];
00242 data[2] = data[1];
00243 data[1] = data[0];
00244 data[0] = a;
00245 data += 4;
00246 }
00247 if (Is_Little_Endian)
00248 byte_swap32 ((unsigned char *)at->data, (unsigned char *)at->data, at->num_elements);
00249 }
00250
00251 static void fixup_array_ga (SLang_Array_Type *at)
00252 {
00253 if (Is_Little_Endian)
00254 byte_swap16 ((unsigned char *)at->data, (unsigned char *) at->data, at->num_elements);
00255 }
00256
00257
00258
00259
00260 static SLang_Array_Type *read_image_internal (char *file, int flip, int *color_typep)
00261 {
00262 Png_Type *p;
00263 png_uint_32 width, height, rowbytes;
00264 png_struct *png;
00265 png_info *info;
00266 int bit_depth;
00267 int interlace_type;
00268 int color_type;
00269 unsigned int sizeof_type;
00270 SLindex_Type dims[2];
00271 SLtype data_type;
00272 png_byte **image_pointers = NULL;
00273 png_byte *data = NULL;
00274 SLang_Array_Type *at;
00275 void (*fixup_array_fun) (SLang_Array_Type *);
00276
00277 if (NULL == (p = open_png_file (file)))
00278 return NULL;
00279
00280 png = p->png;
00281 if (setjmp (png_jmpbuf (png)))
00282 {
00283 free_png_type (p);
00284 if (data != NULL) SLfree ((char *) data);
00285 free_image_pointers (image_pointers);
00286 SLang_verror (SL_Read_Error, "Error encountered during I/O to %s", file);
00287 return NULL;
00288 }
00289
00290 png_init_io (png, p->fp);
00291 png_set_sig_bytes (png, 8);
00292 info = p->info;
00293 png_read_info(png, info);
00294
00295 width = png_get_image_width (png, info);
00296 height = png_get_image_height (png, info);
00297 interlace_type = png_get_interlace_type (png, info);
00298 bit_depth = png_get_bit_depth (png, info);
00299
00300 if (bit_depth == 16)
00301 png_set_strip_16 (png);
00302
00303 switch (png_get_color_type (png, info))
00304 {
00305 case PNG_COLOR_TYPE_GRAY:
00306 if (bit_depth < 8) png_set_gray_1_2_4_to_8 (png);
00307 break;
00308 case PNG_COLOR_TYPE_GRAY_ALPHA:
00309
00310 break;
00311
00312 case PNG_COLOR_TYPE_PALETTE:
00313 png_set_palette_to_rgb (png);
00314 break;
00315 }
00316
00317 if (png_get_valid(png, info, PNG_INFO_tRNS))
00318 png_set_tRNS_to_alpha(png);
00319
00320 png_read_update_info (png, info);
00321
00322 color_type = png_get_color_type (png, info);
00323 switch (color_type)
00324 {
00325 case PNG_COLOR_TYPE_RGBA:
00326 sizeof_type = 4;
00327 fixup_array_fun = fixup_array_rgba;
00328 data_type = SLang_get_int_type (32);
00329 break;
00330
00331 case PNG_COLOR_TYPE_RGB:
00332 sizeof_type = 4;
00333 fixup_array_fun = fixup_array_rgb;
00334 data_type = SLang_get_int_type (32);
00335 break;
00336
00337 case PNG_COLOR_TYPE_GRAY_ALPHA:
00338 sizeof_type = 2;
00339 fixup_array_fun = fixup_array_ga;
00340 data_type = SLang_get_int_type (16);
00341 break;
00342
00343 case PNG_COLOR_TYPE_GRAY:
00344 sizeof_type = 1;
00345 fixup_array_fun = NULL;
00346 data_type = SLANG_UCHAR_TYPE;
00347 break;
00348
00349 default:
00350 SLang_verror (SL_Read_Error, "Unsupported PNG color-type");
00351 free_png_type (p);
00352 return NULL;
00353 }
00354 *color_typep = color_type;
00355
00356
00357 rowbytes = png_get_rowbytes (png, info);
00358 if (rowbytes > width * sizeof_type)
00359 {
00360 SLang_verror (SL_INTERNAL_ERROR, "Unexpected value returned from png_get_rowbytes");
00361 free_png_type (p);
00362 return NULL;
00363 }
00364
00365 if (NULL == (data = (png_byte *) SLmalloc (height * width * sizeof_type)))
00366 {
00367 free_png_type (p);
00368 return NULL;
00369 }
00370
00371 if (NULL == (image_pointers = allocate_image_pointers (height, data, width * sizeof_type, flip)))
00372 {
00373 SLfree ((char *) data);
00374 free_png_type (p);
00375 return NULL;
00376 }
00377 png_read_image(png, image_pointers);
00378
00379 dims[0] = height;
00380 dims[1] = width;
00381
00382 if (NULL == (at = SLang_create_array (data_type, 0, (VOID_STAR) data, dims, 2)))
00383 {
00384 SLfree ((char *) data);
00385 free_image_pointers (image_pointers);
00386 free_png_type (p);
00387 return NULL;
00388 }
00389 free_png_type (p);
00390 free_image_pointers (image_pointers);
00391 if (fixup_array_fun != NULL)
00392 (*fixup_array_fun) (at);
00393 return at;
00394 }
00395
00396 static void read_image (int flipped)
00397 {
00398 int color_type;
00399 char *file;
00400 SLang_Ref_Type *ref = NULL;
00401 SLang_Array_Type *at;
00402
00403 if ((SLang_Num_Function_Args == 2)
00404 && (-1 == SLang_pop_ref (&ref)))
00405 return;
00406
00407 if (-1 == SLang_pop_slstring (&file))
00408 {
00409 file = NULL;
00410 goto free_return;
00411 }
00412
00413 if (NULL == (at = read_image_internal (file, flipped, &color_type)))
00414 goto free_return;
00415
00416 if ((ref != NULL)
00417 && (-1 == SLang_assign_to_ref (ref, SLANG_INT_TYPE, &color_type)))
00418 {
00419 SLang_free_array (at);
00420 goto free_return;
00421 }
00422
00423 (void) SLang_push_array (at, 1);
00424
00425 free_return:
00426 SLang_free_slstring (file);
00427 if (ref != NULL)
00428 SLang_free_ref (ref);
00429 }
00430
00431
00432
00433 static void write_gray_to_gray (png_struct *png, png_byte *data, SLindex_Type num_cols, png_byte *tmpbuf)
00434 {
00435 (void) num_cols;
00436 (void) tmpbuf;
00437 png_write_row (png, data);
00438 }
00439
00440 static void write_gray_to_gray_alpha (png_struct *png, png_byte *data, SLindex_Type num_cols, png_byte *tmpbuf)
00441 {
00442 SLindex_Type i, j;
00443
00444 j = 0;
00445 for (i = 0; i < num_cols; i++)
00446 {
00447 tmpbuf[j] = data[i];
00448 tmpbuf[j+1] = 0xFF;
00449 j += 2;
00450 }
00451 png_write_row (png, tmpbuf);
00452 }
00453
00454 static void write_gray_alpha_to_gray (png_struct *png, png_byte *data, SLindex_Type num_cols, png_byte *tmpbuf)
00455 {
00456 SLindex_Type i;
00457
00458 if (Is_Little_Endian == 0)
00459 data++;
00460
00461 for (i = 0; i < num_cols; i++)
00462 {
00463 tmpbuf[i] = *data;
00464 data += 2;
00465 }
00466 png_write_row (png, tmpbuf);
00467 }
00468
00469 static void write_gray_alpha_to_gray_alpha (png_struct *png, png_byte *data, SLindex_Type num_cols, png_byte *tmpbuf)
00470 {
00471 if (Is_Little_Endian == 0)
00472 {
00473 png_write_row (png, data);
00474 return;
00475 }
00476
00477 byte_swap16 ((unsigned char *) data, (unsigned char *) tmpbuf, num_cols);
00478 png_write_row (png, tmpbuf);
00479 }
00480
00481 static void write_rgb_alpha_to_rgb_alpha (png_struct *png, png_byte *data, SLindex_Type num_cols, png_byte *tmpbuf)
00482 {
00483 unsigned char *data_max;
00484 unsigned char *p;
00485
00486 if (Is_Little_Endian)
00487 {
00488 byte_swap32 ((unsigned char *) data, (unsigned char *) tmpbuf, num_cols);
00489 data = tmpbuf;
00490 }
00491 data_max = data + 4 * num_cols;
00492 p = tmpbuf;
00493
00494 while (data < data_max)
00495 {
00496 unsigned char a = data[0];
00497 p[0] = data[1];
00498 p[1] = data[2];
00499 p[2] = data[3];
00500 p[3] = a;
00501 data += 4;
00502 p += 4;
00503 }
00504 png_write_row (png, tmpbuf);
00505 }
00506
00507 static void write_rgb_to_rgb (png_struct *png, png_byte *data, SLindex_Type num_cols, png_byte *tmpbuf)
00508 {
00509 SLindex_Type i;
00510 png_byte *p, *q;
00511
00512 if (Is_Little_Endian)
00513 {
00514 byte_swap32 ((unsigned char *) data, (unsigned char *) tmpbuf, num_cols);
00515 p = tmpbuf;
00516 }
00517 else p = data;
00518
00519
00520 q = tmpbuf;
00521 for (i = 0; i < num_cols; i++)
00522 {
00523 p++;
00524 *q++ = *p++;
00525 *q++ = *p++;
00526 *q++ = *p++;
00527 }
00528 png_write_row (png, tmpbuf);
00529 }
00530
00531
00532 static int write_array (png_struct *png, png_byte **image_pointers, SLindex_Type num_rows, SLindex_Type num_cols,
00533 void (*write_row_func) (png_struct *, png_byte *, SLindex_Type, png_byte *),
00534 png_byte *tmpbuf)
00535 {
00536 int num_pass;
00537 SLindex_Type i;
00538
00539 num_pass = png_set_interlace_handling(png);
00540 while (num_pass > 0)
00541 {
00542 num_pass--;
00543 for (i = 0; i < num_rows; i++)
00544 (*write_row_func) (png, image_pointers[i], num_cols, tmpbuf);
00545 }
00546 return 0;
00547 }
00548
00549 static int write_image_internal (char *file, SLang_Array_Type *at,
00550 int color_type,
00551 void (*write_fun)(png_struct *, png_byte *p, SLindex_Type, png_byte *),
00552 int flip)
00553 {
00554 FILE *fp;
00555 Png_Type *p = NULL;
00556 png_struct *png;
00557 png_info *info;
00558 SLindex_Type width, height;
00559 png_byte **image_pointers;
00560 int bit_depth;
00561 int status = -1;
00562 png_byte *tmpbuf;
00563
00564 bit_depth = 8;
00565
00566 height = at->dims[0];
00567 width = at->dims[1];
00568
00569 if (NULL == (image_pointers = allocate_image_pointers (height, (png_byte *)at->data, width * at->sizeof_type, flip)))
00570 return -1;
00571
00572 if (NULL == (tmpbuf = (png_byte *)SLmalloc (4*width)))
00573 {
00574 free_image_pointers (image_pointers);
00575 return -1;
00576 }
00577
00578 if (NULL == (fp = fopen (file, "wb")))
00579 {
00580 (void)SLerrno_set_errno (errno);
00581 SLang_verror (SL_Open_Error, "Unable to open %s", file);
00582 goto return_error;
00583 }
00584
00585 if (NULL == (p = alloc_png_type ('w')))
00586 goto return_error;
00587
00588 p->fp = fp;
00589
00590 if (NULL == (p->png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)))
00591 {
00592 SLang_verror (SL_Open_Error, "png_create_write_struct failed");
00593 goto return_error;
00594 }
00595 png = p->png;
00596 if (NULL == (p->info = png_create_info_struct (png)))
00597 {
00598 SLang_verror (SL_Open_Error, "png_create_info_struct failed");
00599 goto return_error;
00600 }
00601 info = p->info;
00602 if (setjmp(png_jmpbuf(png)))
00603 {
00604 SLang_verror (SL_Write_Error, "PNG I/O error");
00605 goto return_error;
00606 }
00607 png_init_io(png, fp);
00608
00609 png_set_IHDR (png, info, width, height,
00610 bit_depth, color_type, PNG_INTERLACE_NONE,
00611 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
00612 png_write_info(png, info);
00613
00614 if (-1 == write_array (png, image_pointers, height, width, write_fun, tmpbuf))
00615 goto return_error;
00616
00617 png_write_end(png, NULL);
00618 if (EOF == fclose (p->fp))
00619 {
00620 SLang_verror (SL_Write_Error, "Error closing %s", file);
00621 SLerrno_set_errno (errno);
00622 }
00623 else status = 0;
00624
00625 p->fp = NULL;
00626
00627 return_error:
00628 if (tmpbuf != NULL)
00629 SLfree ((char *) tmpbuf);
00630 free_image_pointers (image_pointers);
00631 if (p != NULL)
00632 free_png_type (p);
00633
00634 return status;
00635 }
00636
00637
00638 static void write_image (int flip)
00639 {
00640 char *file;
00641 SLang_Array_Type *at;
00642 int with_alpha = 0;
00643 int has_with_alpha = 0;
00644 int color_type;
00645 void (*write_fun) (png_struct *, png_byte *, SLindex_Type, png_byte *);
00646
00647 if (SLang_Num_Function_Args == 3)
00648 {
00649 if (-1 == SLang_pop_int (&with_alpha))
00650 return;
00651 has_with_alpha = 1;
00652 }
00653
00654 if (-1 == SLang_pop_array (&at, 0))
00655 return;
00656
00657 if (at->num_dims != 2)
00658 {
00659 SLang_verror (SL_InvalidParm_Error, "Expecting a 2-d array");
00660 SLang_free_array (at);
00661 return;
00662 }
00663
00664 switch (SLang_get_int_size (at->data_type))
00665 {
00666 case -8:
00667 case 8:
00668 if (with_alpha)
00669 {
00670 write_fun = write_gray_to_gray_alpha;
00671 color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
00672 }
00673 else
00674 {
00675 write_fun = write_gray_to_gray;
00676 color_type = PNG_COLOR_TYPE_GRAY;
00677 }
00678 break;
00679 case -16:
00680 case 16:
00681 if (has_with_alpha && (with_alpha == 0))
00682 {
00683 write_fun = write_gray_alpha_to_gray;
00684 color_type = PNG_COLOR_TYPE_GRAY;
00685 }
00686 else
00687 {
00688 write_fun = write_gray_alpha_to_gray_alpha;
00689 color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
00690 }
00691 break;
00692 case -32:
00693 case 32:
00694 if (with_alpha)
00695 {
00696 write_fun = write_rgb_alpha_to_rgb_alpha;
00697 color_type = PNG_COLOR_TYPE_RGBA;
00698 }
00699 else
00700 {
00701 write_fun = write_rgb_to_rgb;
00702 color_type = PNG_COLOR_TYPE_RGB;
00703 }
00704 break;
00705 default:
00706 SLang_verror (SL_InvalidParm_Error, "Expecting an 8, 16, or 32 bit integer array");
00707 SLang_free_array (at);
00708 return;
00709 }
00710
00711 if (-1 == SLang_pop_slstring (&file))
00712 {
00713 SLang_free_array (at);
00714 return;
00715 }
00716 (void) write_image_internal (file, at, color_type, write_fun, flip);
00717 SLang_free_slstring (file);
00718 SLang_free_array (at);
00719 }
00720
00721
00722 static void read_intrin (void)
00723 {
00724 read_image (0);
00725 }
00726 static void read_flipped_intrin (void)
00727 {
00728 read_image (1);
00729 }
00730
00731 static void write_intrin (void)
00732 {
00733 write_image (0);
00734 }
00735 static void write_flipped_intrin (void)
00736 {
00737 write_image (1);
00738 }
00739
00740 static SLang_Intrin_Fun_Type Module_Intrinsics [] =
00741 {
00742 MAKE_INTRINSIC_0("png_read", read_intrin, SLANG_VOID_TYPE),
00743 MAKE_INTRINSIC_0("png_read_flipped", read_flipped_intrin, SLANG_VOID_TYPE),
00744 MAKE_INTRINSIC_0("png_write", write_intrin, SLANG_VOID_TYPE),
00745 MAKE_INTRINSIC_0("png_write_flipped", write_flipped_intrin, SLANG_VOID_TYPE),
00746 SLANG_END_INTRIN_FUN_TABLE
00747 };
00748
00749 static SLang_Intrin_Var_Type Module_Variables [] =
00750 {
00751 MAKE_VARIABLE("_png_module_version_string", &Module_Version_String, SLANG_STRING_TYPE, 1),
00752 SLANG_END_INTRIN_VAR_TABLE
00753 };
00754
00755 static SLang_IConstant_Type Module_IConstants [] =
00756 {
00757 MAKE_ICONSTANT("PNG_COLOR_TYPE_GRAY", PNG_COLOR_TYPE_GRAY),
00758 MAKE_ICONSTANT("PNG_COLOR_TYPE_GRAY_ALPHA", PNG_COLOR_TYPE_GRAY_ALPHA),
00759 MAKE_ICONSTANT("PNG_COLOR_TYPE_RGB", PNG_COLOR_TYPE_RGB),
00760 MAKE_ICONSTANT("PNG_COLOR_TYPE_RGBA", PNG_COLOR_TYPE_RGBA),
00761
00762 MAKE_ICONSTANT("_png_module_version", MODULE_VERSION_NUMBER),
00763 SLANG_END_ICONST_TABLE
00764 };
00765
00766 int init_png_module_ns (char *ns_name)
00767 {
00768 unsigned short x;
00769
00770 SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
00771 if (ns == NULL)
00772 return -1;
00773
00774 x = 0xFF;
00775 Is_Little_Endian = (*(unsigned char *)&x == 0xFF);
00776
00777 if (
00778 (-1 == SLns_add_intrin_var_table (ns, Module_Variables, NULL))
00779 || (-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL))
00780 || (-1 == SLns_add_iconstant_table (ns, Module_IConstants, NULL))
00781 )
00782 return -1;
00783
00784 return 0;
00785 }
00786
00787
00788 void deinit_png_module (void)
00789 {
00790 }