6 /* this is a way to get number of channels from color space
7 * Color code to channel number */
9 static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
11 #define PNG_BYTES_TO_CHECK 4
14 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
17 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
20 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
23 read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
26 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
29 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
32 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
35 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im);
38 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth, int color_type);
41 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
44 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size);
47 i_png_lib_version(void) {
48 return png_access_version_number();
52 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
53 io_glue *ig = png_get_io_ptr(png_ptr);
54 ssize_t rc = i_io_read(ig, data, length);
55 if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
59 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
61 io_glue *ig = png_get_io_ptr(png_ptr);
62 rc = i_io_write(ig, data, length);
63 if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
67 wiol_flush_data(png_structp png_ptr) {
68 io_glue *ig = png_get_io_ptr(png_ptr);
70 png_error(png_ptr, "Error flushing output");
74 error_handler(png_structp png_ptr, png_const_charp msg) {
75 mm_log((1, "PNG error: '%s'\n", msg));
78 longjmp(png_jmpbuf(png_ptr), 1);
83 For writing a warning might have information about an error, so send
84 it to the error stack.
88 write_warn_handler(png_structp png_ptr, png_const_charp msg) {
89 mm_log((1, "PNG write warning '%s'\n", msg));
94 #define PNG_DIM_MAX 0x7fffffffL
97 i_writepng_wiol(i_img *im, io_glue *ig) {
99 png_infop info_ptr = NULL;
100 i_img_dim width,height;
101 volatile int cspace,channels;
103 int is_bilevel = 0, zero_is_white;
105 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
109 if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
110 i_push_error(0, "image too large for PNG");
117 /* if we ever have 64-bit i_img_dim
118 * the libpng docs state that png_set_user_limits() can be used to
119 * override the PNG_USER_*_MAX limits, but as implemented they
120 * don't. We check against the theoretical limit of PNG here, and
121 * try to override the limits below, in case the libpng
122 * implementation ever matches the documentation.
124 * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
125 * fixed in libpng 1.5.3
127 if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
128 i_push_error(0, "Image too large for PNG");
132 channels=im->channels;
134 if (i_img_is_monochrome(im, &zero_is_white)) {
137 cspace = PNG_COLOR_TYPE_GRAY;
138 mm_log((1, "i_writepng: bilevel output\n"));
140 else if (im->type == i_palette_type) {
141 int colors = i_colorcount(im);
143 cspace = PNG_COLOR_TYPE_PALETTE;
145 while ((1 << bits) < colors) {
148 mm_log((1, "i_writepng: paletted output\n"));
153 cspace = PNG_COLOR_TYPE_GRAY;
156 cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
159 cspace = PNG_COLOR_TYPE_RGB;
162 cspace = PNG_COLOR_TYPE_RGB_ALPHA;
165 fprintf(stderr, "Internal error, channels = %d\n", channels);
168 bits = im->bits > 8 ? 16 : 8;
169 mm_log((1, "i_writepng: direct output\n"));
172 mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
174 /* Create and initialize the png_struct with the desired error handler
175 * functions. If you want to use the default stderr and longjump method,
176 * you can supply NULL for the last three parameters. We also check that
177 * the library version is compatible with the one used at compile time,
178 * in case we are using dynamically linked libraries. REQUIRED.
181 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
182 error_handler, write_warn_handler);
184 if (png_ptr == NULL) return 0;
187 /* Allocate/initialize the image information data. REQUIRED */
188 info_ptr = png_create_info_struct(png_ptr);
190 if (info_ptr == NULL) {
191 png_destroy_write_struct(&png_ptr, &info_ptr);
195 /* Set error handling. REQUIRED if you aren't supplying your own
196 * error hadnling functions in the png_create_write_struct() call.
198 if (setjmp(png_jmpbuf(png_ptr))) {
199 png_destroy_write_struct(&png_ptr, &info_ptr);
203 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
205 /* Set the image information here. Width and height are up to 2^31,
206 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
207 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
208 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
209 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
210 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
211 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
214 /* by default, libpng (not PNG) limits the image size to a maximum
215 * 1000000 pixels in each direction, but Imager doesn't.
216 * Configure libpng to avoid that limit.
218 png_set_user_limits(png_ptr, width, height);
220 png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
221 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
223 if (!set_png_tags(im, png_ptr, info_ptr)) {
224 png_destroy_write_struct(&png_ptr, &info_ptr);
229 if (!write_bilevel(png_ptr, info_ptr, im)) {
230 png_destroy_write_struct(&png_ptr, &info_ptr);
234 else if (im->type == i_palette_type) {
235 if (!write_paletted(png_ptr, info_ptr, im, bits)) {
236 png_destroy_write_struct(&png_ptr, &info_ptr);
240 else if (bits == 16) {
241 if (!write_direct16(png_ptr, info_ptr, im)) {
242 png_destroy_write_struct(&png_ptr, &info_ptr);
247 if (!write_direct8(png_ptr, info_ptr, im)) {
248 png_destroy_write_struct(&png_ptr, &info_ptr);
253 png_write_end(png_ptr, info_ptr);
255 png_destroy_write_struct(&png_ptr, &info_ptr);
265 } i_png_read_state, *i_png_read_statep;
268 read_warn_handler(png_structp, png_const_charp);
271 cleanup_read_state(i_png_read_statep);
274 i_readpng_wiol(io_glue *ig, int flags) {
278 png_uint_32 width, height;
279 int bit_depth, color_type, interlace_type;
281 unsigned int sig_read;
287 mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
290 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs,
291 error_handler, read_warn_handler);
293 i_push_error(0, "Cannot create PNG read structure");
296 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
298 #if PNG_LIBPNG_VER >= 10400
299 png_set_benign_errors(png_ptr, (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) ? 1 : 0);
301 if (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) {
302 i_push_error(0, "libpng too old to ignore benign errors");
303 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
308 info_ptr = png_create_info_struct(png_ptr);
309 if (info_ptr == NULL) {
310 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
311 i_push_error(0, "Cannot create PNG info structure");
315 if (setjmp(png_jmpbuf(png_ptr))) {
316 if (im) i_img_destroy(im);
317 mm_log((1,"i_readpng_wiol: error.\n"));
318 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
319 cleanup_read_state(&rs);
323 /* we do our own limit checks */
324 png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
326 png_set_sig_bytes(png_ptr, sig_read);
327 png_read_info(png_ptr, info_ptr);
328 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
330 mm_log((1, "png_get_IHDR results: width %u, height %u, bit_depth %d, color_type %d, interlace_type %d\n",
331 (unsigned)width, (unsigned)height, bit_depth,color_type,interlace_type));
333 CC2C[PNG_COLOR_TYPE_GRAY]=1;
334 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
335 CC2C[PNG_COLOR_TYPE_RGB]=3;
336 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
337 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
338 channels = CC2C[color_type];
340 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
342 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
343 mm_log((1, "i_readpnm: image size exceeds limits\n"));
344 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
348 if (color_type == PNG_COLOR_TYPE_PALETTE) {
349 im = read_paletted(png_ptr, info_ptr, channels, width, height);
351 else if (color_type == PNG_COLOR_TYPE_GRAY
353 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
354 im = read_bilevel(png_ptr, info_ptr, width, height);
356 else if (bit_depth == 16) {
357 im = read_direct16(png_ptr, info_ptr, channels, width, height);
360 im = read_direct8(png_ptr, info_ptr, channels, width, height);
364 get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
366 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
370 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
373 cleanup_read_state(&rs);
375 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
381 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
382 i_img_dim width, i_img_dim height) {
383 i_img * volatile vim = NULL;
384 int color_type = png_get_color_type(png_ptr, info_ptr);
385 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
387 int number_passes, pass;
390 unsigned char * volatile vline = NULL;
392 if (setjmp(png_jmpbuf(png_ptr))) {
393 if (vim) i_img_destroy(vim);
394 if (vline) myfree(vline);
399 number_passes = png_set_interlace_handling(png_ptr);
400 mm_log((1,"number of passes=%d\n",number_passes));
402 png_set_strip_16(png_ptr);
403 png_set_packing(png_ptr);
405 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
406 png_set_expand(png_ptr);
408 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
410 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
411 png_set_expand(png_ptr);
414 png_read_update_info(png_ptr, info_ptr);
416 im = vim = i_img_8_new(width,height,channels);
418 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
422 line = vline = mymalloc(channels * width);
423 for (pass = 0; pass < number_passes; pass++) {
424 for (y = 0; y < height; y++) {
426 i_gsamp(im, 0, width, y, line, NULL, channels);
427 png_read_row(png_ptr,(png_bytep)line, NULL);
428 i_psamp(im, 0, width, y, line, NULL, channels);
434 png_read_end(png_ptr, info_ptr);
440 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
441 i_img_dim width, i_img_dim height) {
442 i_img * volatile vim = NULL;
444 int number_passes, pass;
447 unsigned char * volatile vline = NULL;
449 unsigned * volatile vbits_line = NULL;
452 if (setjmp(png_jmpbuf(png_ptr))) {
453 if (vim) i_img_destroy(vim);
454 if (vline) myfree(vline);
455 if (vbits_line) myfree(vbits_line);
460 number_passes = png_set_interlace_handling(png_ptr);
461 mm_log((1,"number of passes=%d\n",number_passes));
463 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
465 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
466 png_set_expand(png_ptr);
469 png_read_update_info(png_ptr, info_ptr);
471 im = vim = i_img_16_new(width,height,channels);
473 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
477 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
478 line = vline = mymalloc(row_bytes);
479 memset(line, 0, row_bytes);
480 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
481 for (pass = 0; pass < number_passes; pass++) {
482 for (y = 0; y < height; y++) {
484 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
485 for (x = 0; x < width * channels; ++x) {
486 line[x*2] = bits_line[x] >> 8;
487 line[x*2+1] = bits_line[x] & 0xff;
490 png_read_row(png_ptr,(png_bytep)line, NULL);
491 for (x = 0; x < width * channels; ++x)
492 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
493 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
501 png_read_end(png_ptr, info_ptr);
507 read_bilevel(png_structp png_ptr, png_infop info_ptr,
508 i_img_dim width, i_img_dim height) {
509 i_img * volatile vim = NULL;
511 int number_passes, pass;
514 unsigned char * volatile vline = NULL;
517 if (setjmp(png_jmpbuf(png_ptr))) {
518 if (vim) i_img_destroy(vim);
519 if (vline) myfree(vline);
524 number_passes = png_set_interlace_handling(png_ptr);
525 mm_log((1,"number of passes=%d\n",number_passes));
527 png_set_packing(png_ptr);
529 png_set_expand(png_ptr);
531 png_read_update_info(png_ptr, info_ptr);
533 im = vim = i_img_pal_new(width, height, 1, 256);
535 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
539 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
540 palette[0].channel[3] = 0;
541 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
542 palette[1].channel[3] = 255;
543 i_addcolors(im, palette, 2);
545 line = vline = mymalloc(width);
546 memset(line, 0, width);
547 for (pass = 0; pass < number_passes; pass++) {
548 for (y = 0; y < height; y++) {
550 i_gpal(im, 0, width, y, line);
551 /* expand indexes back to 0/255 */
552 for (x = 0; x < width; ++x)
553 line[x] = line[x] ? 255 : 0;
555 png_read_row(png_ptr,(png_bytep)line, NULL);
557 /* back to palette indexes */
558 for (x = 0; x < width; ++x)
559 line[x] = line[x] ? 1 : 0;
560 i_ppal(im, 0, width, y, line);
566 png_read_end(png_ptr, info_ptr);
571 /* FIXME: do we need to unscale palette color values from the
574 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
575 i_img_dim width, i_img_dim height) {
576 i_img * volatile vim = NULL;
577 int color_type = png_get_color_type(png_ptr, info_ptr);
578 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
580 int number_passes, pass;
583 unsigned char * volatile vline = NULL;
585 png_colorp png_palette;
586 png_bytep png_pal_trans;
587 png_color_16p png_color_trans;
590 if (setjmp(png_jmpbuf(png_ptr))) {
591 if (vim) i_img_destroy(vim);
592 if (vline) myfree(vline);
597 number_passes = png_set_interlace_handling(png_ptr);
598 mm_log((1,"number of passes=%d\n",number_passes));
600 png_set_strip_16(png_ptr);
601 png_set_packing(png_ptr);
603 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
604 png_set_expand(png_ptr);
606 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
607 i_push_error(0, "Paletted image with no PLTE chunk");
611 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
619 png_read_update_info(png_ptr, info_ptr);
621 im = vim = i_img_pal_new(width, height, channels, 256);
623 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
627 for (i = 0; i < num_palette; ++i) {
630 c.rgba.r = png_palette[i].red;
631 c.rgba.g = png_palette[i].green;
632 c.rgba.b = png_palette[i].blue;
633 if (i < num_pal_trans)
634 c.rgba.a = png_pal_trans[i];
637 i_addcolors(im, &c, 1);
640 line = vline = mymalloc(width);
641 for (pass = 0; pass < number_passes; pass++) {
642 for (y = 0; y < height; y++) {
644 i_gpal(im, 0, width, y, line);
645 png_read_row(png_ptr,(png_bytep)line, NULL);
646 i_ppal(im, 0, width, y, line);
652 png_read_end(png_ptr, info_ptr);
657 struct png_text_name {
662 static const struct png_text_name
664 { "Author", "png_author" },
665 { "Comment", "i_comment" },
666 { "Copyright", "png_copyright" },
667 { "Creation Time", "png_creation_time" },
668 { "Description", "png_description" },
669 { "Disclaimer", "png_disclaimer" },
670 { "Software", "png_software" },
671 { "Source", "png_source" },
672 { "Title", "png_title" },
673 { "Warning", "png_warning" }
676 static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
678 static const char * const
680 "png_chroma_white_x",
681 "png_chroma_white_y",
684 "png_chroma_green_x",
685 "png_chroma_green_y",
690 static const int chroma_tag_count = sizeof(chroma_tags) / sizeof(*chroma_tags);
693 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr,
694 int bit_depth, int color_type) {
695 png_uint_32 xres, yres;
698 i_tags_set(&im->tags, "i_format", "png", -1);
699 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
700 mm_log((1,"pHYs (%u, %u) %d\n", (unsigned)xres, (unsigned)yres, unit_type));
701 if (unit_type == PNG_RESOLUTION_METER) {
702 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
703 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
706 i_tags_setn(&im->tags, "i_xres", xres);
707 i_tags_setn(&im->tags, "i_yres", yres);
708 i_tags_setn(&im->tags, "i_aspect_only", 1);
712 int interlace = png_get_interlace_type(png_ptr, info_ptr);
714 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
716 case PNG_INTERLACE_NONE:
717 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
720 case PNG_INTERLACE_ADAM7:
721 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
725 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
730 /* the various readers can call png_set_expand(), libpng will make
731 it's internal record of bit_depth at least 8 in that case */
732 i_tags_setn(&im->tags, "png_bits", bit_depth);
734 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
736 if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
737 i_tags_setn(&im->tags, "png_srgb_intent", intent);
741 /* Ignore these if there's an sRGB chunk, libpng simulates
742 their existence if there's an sRGB chunk, and the PNG spec says
743 that these are ignored if the sRGB is present, so ignore them.
748 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
749 i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
752 if (png_get_cHRM(png_ptr, info_ptr, chroma+0, chroma+1,
753 chroma+2, chroma+3, chroma+4, chroma+5,
754 chroma+6, chroma+7)) {
757 for (i = 0; i < chroma_tag_count; ++i)
758 i_tags_set_float2(&im->tags, chroma_tags[i], 0, chroma[i], 4);
766 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
768 int custom_index = 0;
769 for (i = 0; i < num_text; ++i) {
772 int compressed = text[i].compression == PNG_ITXT_COMPRESSION_zTXt
773 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt;
775 for (j = 0; j < text_tags_count; ++j) {
776 if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
778 i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
780 sprintf(tag_name, "%s_compressed", text_tags[j].tagname);
781 i_tags_setn(&im->tags, tag_name, 1);
790 sprintf(tag_name, "png_text%d_key", custom_index);
791 i_tags_set(&im->tags, tag_name, text[i].key, -1);
792 sprintf(tag_name, "png_text%d_text", custom_index);
793 i_tags_set(&im->tags, tag_name, text[i].text, -1);
794 sprintf(tag_name, "png_text%d_type", custom_index);
795 i_tags_set(&im->tags, tag_name,
796 (text[i].compression == PNG_TEXT_COMPRESSION_NONE
797 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
798 "text" : "itxt", -1);
800 sprintf(tag_name, "png_text%d_compressed", custom_index);
801 i_tags_setn(&im->tags, tag_name, 1);
812 if (png_get_tIME(png_ptr, info_ptr, &mod_time)) {
813 char time_formatted[80];
815 sprintf(time_formatted, "%d-%02d-%02dT%02d:%02d:%02d",
816 mod_time->year, mod_time->month, mod_time->day,
817 mod_time->hour, mod_time->minute, mod_time->second);
818 i_tags_set(&im->tags, "png_time", time_formatted, -1);
826 if (png_get_bKGD(png_ptr, info_ptr, &back)) {
827 switch (color_type) {
828 case PNG_COLOR_TYPE_GRAY:
829 case PNG_COLOR_TYPE_GRAY_ALPHA:
831 /* lib png stores the raw gray value rather than scaling it
832 to 16-bit (or 8), we use 8-bit color for i_background */
837 gray = back->gray >> 8;
843 gray = 0x11 * back->gray;
846 gray = 0x55 * back->gray;
849 gray = back->gray ? 0xFF : 0;
854 c.rgb.r = c.rgb.g = c.rgb.b = gray;
858 case PNG_COLOR_TYPE_RGB:
859 case PNG_COLOR_TYPE_RGB_ALPHA:
861 c.rgb.r = bit_depth == 16 ? (back->red >> 8) : back->red;
862 c.rgb.g = bit_depth == 16 ? (back->green >> 8) : back->green;
863 c.rgb.b = bit_depth == 16 ? (back->blue >> 8) : back->blue;
867 case PNG_COLOR_TYPE_PALETTE:
869 c.rgb.g = back->green;
870 c.rgb.b = back->blue;
875 i_tags_set_color(&im->tags, "i_background", 0, &c);
880 #define GET_STR_BUF_SIZE 40
883 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
885 int aspect_only, have_res = 1;
887 if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
888 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
889 ; /* nothing to do */
894 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
901 i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
904 png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
905 aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
910 if (i_tags_get_int(&im->tags, "png_srgb_intent", 0, &intent)) {
911 if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) {
912 i_push_error(0, "tag png_srgb_intent out of range");
915 png_set_sRGB(png_ptr, info_ptr, intent);
918 double chroma[8], gamma;
920 int found_chroma_count = 0;
922 for (i = 0; i < chroma_tag_count; ++i) {
923 if (i_tags_get_float(&im->tags, chroma_tags[i], 0, chroma+i))
924 ++found_chroma_count;
927 if (found_chroma_count) {
928 if (found_chroma_count != chroma_tag_count) {
929 i_push_error(0, "all png_chroma_* tags must be supplied or none");
933 png_set_cHRM(png_ptr, info_ptr, chroma[0], chroma[1], chroma[2],
934 chroma[3], chroma[4], chroma[5], chroma[6], chroma[7]);
937 if (i_tags_get_float(&im->tags, "png_gamma", 0, &gamma)) {
938 png_set_gAMA(png_ptr, info_ptr, gamma);
944 /* png_set_text() is sparsely documented, it isn't indicated whether
945 multiple calls add to or replace the lists of texts, and
946 whether the text/keyword data is copied or not.
948 Examining the linpng code reveals that png_set_text() adds to
949 the list and that the text is copied.
953 /* do our standard tags */
954 for (i = 0; i < text_tags_count; ++i) {
955 char buf[GET_STR_BUF_SIZE];
959 data = get_string2(&im->tags, text_tags[i].tagname, buf, &size);
962 int compression = size > 1000;
963 char compress_tag[40];
965 if (memchr(data, '\0', size)) {
966 i_push_errorf(0, "tag %s may not contain NUL characters", text_tags[i].tagname);
970 sprintf(compress_tag, "%s_compressed", text_tags[i].tagname);
971 i_tags_get_int(&im->tags, compress_tag, 0, &compression);
973 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
974 : PNG_TEXT_COMPRESSION_NONE;
975 text.key = (char *)text_tags[i].keyword;
976 text.text_length = size;
977 text.text = (char *)data;
978 #ifdef PNG_iTXt_SUPPORTED
979 text.itxt_length = 0;
981 text.lang_key = NULL;
984 png_set_text(png_ptr, info_ptr, &text, 1);
988 /* for non-standard tags ensure keywords are limited to 1 to 79
993 char key_buf[GET_STR_BUF_SIZE], value_buf[GET_STR_BUF_SIZE];
994 const char *key, *value;
995 size_t key_size, value_size;
997 sprintf(tag_name, "png_text%d_key", i);
998 key = get_string2(&im->tags, tag_name, key_buf, &key_size);
1002 if (key_size < 1 || key_size > 79) {
1003 i_push_errorf(0, "tag %s must be between 1 and 79 characters in length", tag_name);
1007 if (key[0] == ' ' || key[key_size-1] == ' ') {
1008 i_push_errorf(0, "tag %s may not contain leading or trailing spaces", tag_name);
1012 if (strstr(key, " ")) {
1013 i_push_errorf(0, "tag %s may not contain consecutive spaces", tag_name);
1017 for (k = 0; k < key_size; ++k) {
1018 if (key[k] < 32 || (key[k] > 126 && key[k] < 161)) {
1019 i_push_errorf(0, "tag %s may only contain Latin1 characters 32-126, 161-255", tag_name);
1025 sprintf(tag_name, "png_text%d_text", i);
1026 value = get_string2(&im->tags, tag_name, value_buf, &value_size);
1029 if (memchr(value, '\0', value_size)) {
1030 i_push_errorf(0, "tag %s may not contain NUL characters", tag_name);
1037 int compression = value_size > 1000;
1039 sprintf(tag_name, "png_text%d_compressed", i);
1040 i_tags_get_int(&im->tags, tag_name, 0, &compression);
1042 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1043 : PNG_TEXT_COMPRESSION_NONE;
1044 text.key = (char *)key;
1045 text.text_length = value_size;
1046 text.text = (char *)value;
1047 #ifdef PNG_iTXt_SUPPORTED
1048 text.itxt_length = 0;
1050 text.lang_key = NULL;
1053 png_set_text(png_ptr, info_ptr, &text, 1);
1056 i_push_errorf(0, "tag png_text%d_key found but not png_text%d_text", i, i);
1060 i_push_errorf(0, "tag png_text%d_text found but not png_text%d_key", i, i);
1071 char buf[GET_STR_BUF_SIZE];
1073 const char *timestr = get_string2(&im->tags, "png_time", buf, &time_size);
1076 int year, month, day, hour, minute, second;
1079 if (sscanf(timestr, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6) {
1080 /* rough validation */
1081 if (month < 1 || month > 12
1082 || day < 1 || day > 31
1083 || hour < 0 || hour > 23
1084 || minute < 0 || minute > 59
1085 || second < 0 || second > 60) {
1086 i_push_error(0, "invalid date/time for png_time");
1089 mod_time.year = year;
1090 mod_time.month = month;
1092 mod_time.hour = hour;
1093 mod_time.minute = minute;
1094 mod_time.second = second;
1096 png_set_tIME(png_ptr, info_ptr, &mod_time);
1099 i_push_error(0, "png_time must be formatted 'y-m-dTh:m:s'");
1106 /* no bKGD support yet, maybe later
1107 it may be simpler to do it in the individual writers
1115 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size) {
1118 if (i_tags_find(tags, name, 0, &index)) {
1119 const i_img_tag *entry = tags->tags + index;
1122 *size = entry->size;
1127 *size = sprintf(buf, "%d", entry->idata);
1136 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1137 unsigned char *data, *volatile vdata = NULL;
1140 if (setjmp(png_jmpbuf(png_ptr))) {
1147 png_write_info(png_ptr, info_ptr);
1149 vdata = data = mymalloc(im->xsize * im->channels);
1150 for (y = 0; y < im->ysize; y++) {
1151 i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
1152 png_write_row(png_ptr, (png_bytep)data);
1160 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1161 unsigned *data, *volatile vdata = NULL;
1162 unsigned char *tran_data, * volatile vtran_data = NULL;
1163 i_img_dim samples_per_row = im->xsize * im->channels;
1167 if (setjmp(png_jmpbuf(png_ptr))) {
1176 png_write_info(png_ptr, info_ptr);
1178 vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
1179 vtran_data = tran_data = mymalloc(samples_per_row * 2);
1180 for (y = 0; y < im->ysize; y++) {
1182 unsigned char *p = tran_data;
1183 i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
1184 for (i = 0; i < samples_per_row; ++i) {
1185 p[0] = data[i] >> 8;
1186 p[1] = data[i] & 0xff;
1189 png_write_row(png_ptr, (png_bytep)tran_data);
1198 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
1199 unsigned char *data, *volatile vdata = NULL;
1201 unsigned char pal_map[256];
1202 png_color pcolors[256];
1203 i_color colors[256];
1204 int count = i_colorcount(im);
1207 if (setjmp(png_jmpbuf(png_ptr))) {
1214 i_getcolors(im, 0, colors, count);
1215 if (im->channels < 3) {
1216 /* convert the greyscale palette to color */
1218 for (i = 0; i < count; ++i) {
1219 i_color *c = colors + i;
1220 c->channel[3] = c->channel[1];
1221 c->channel[2] = c->channel[1] = c->channel[0];
1225 if (i_img_has_alpha(im)) {
1227 int bottom_index = 0, top_index = count-1;
1229 /* fill out the palette map */
1230 for (i = 0; i < count; ++i)
1233 /* the PNG spec suggests sorting the palette by alpha, but that's
1234 unnecessary - all we want to do is move the opaque entries to
1236 while (bottom_index < top_index) {
1237 if (colors[bottom_index].rgba.a == 255) {
1238 pal_map[bottom_index] = top_index;
1239 pal_map[top_index--] = bottom_index;
1245 for (i = 0; i < count; ++i) {
1246 int srci = i_img_has_alpha(im) ? pal_map[i] : i;
1248 pcolors[i].red = colors[srci].rgb.r;
1249 pcolors[i].green = colors[srci].rgb.g;
1250 pcolors[i].blue = colors[srci].rgb.b;
1253 png_set_PLTE(png_ptr, info_ptr, pcolors, count);
1255 if (i_img_has_alpha(im)) {
1256 unsigned char trans[256];
1259 for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
1260 trans[i] = colors[pal_map[i]].rgba.a;
1262 png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
1265 png_write_info(png_ptr, info_ptr);
1267 png_set_packing(png_ptr);
1269 vdata = data = mymalloc(im->xsize);
1270 for (y = 0; y < im->ysize; y++) {
1271 i_gpal(im, 0, im->xsize, y, data);
1272 if (i_img_has_alpha(im)) {
1274 for (x = 0; x < im->xsize; ++x)
1275 data[x] = pal_map[data[x]];
1277 png_write_row(png_ptr, (png_bytep)data);
1285 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1286 unsigned char *data, *volatile vdata = NULL;
1289 if (setjmp(png_jmpbuf(png_ptr))) {
1296 png_write_info(png_ptr, info_ptr);
1298 png_set_packing(png_ptr);
1300 vdata = data = mymalloc(im->xsize);
1301 for (y = 0; y < im->ysize; y++) {
1302 i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
1303 png_write_row(png_ptr, (png_bytep)data);
1311 read_warn_handler(png_structp png_ptr, png_const_charp msg) {
1312 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
1316 mm_log((1, "PNG read warning '%s'\n", msg));
1318 /* in case this is part of an error report */
1319 i_push_error(0, msg);
1321 /* and save in the warnings so if we do manage to succeed, we
1322 * can save it as a tag
1324 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
1326 + strlen(msg) /* new text */
1328 workp = myrealloc(rs->warnings, new_size);
1332 strcat(workp, "\n");
1333 rs->warnings = workp;
1337 cleanup_read_state(i_png_read_statep rs) {
1339 myfree(rs->warnings);