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();
51 static char const * const
54 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
57 #ifdef PNG_READ_SUPPORTED
60 #ifdef PNG_WRITE_SUPPORTED
63 #ifdef PNG_MNG_FEATURES_SUPPORTED
66 #ifdef PNG_CHECK_cHRM_SUPPORTED
69 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
76 i_png_features(void) {
81 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
82 io_glue *ig = png_get_io_ptr(png_ptr);
83 ssize_t rc = i_io_read(ig, data, length);
84 if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
88 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
90 io_glue *ig = png_get_io_ptr(png_ptr);
91 rc = i_io_write(ig, data, length);
92 if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
96 wiol_flush_data(png_structp png_ptr) {
97 io_glue *ig = png_get_io_ptr(png_ptr);
99 png_error(png_ptr, "Error flushing output");
103 error_handler(png_structp png_ptr, png_const_charp msg) {
104 mm_log((1, "PNG error: '%s'\n", msg));
106 i_push_error(0, msg);
107 longjmp(png_jmpbuf(png_ptr), 1);
112 For writing a warning might have information about an error, so send
113 it to the error stack.
117 write_warn_handler(png_structp png_ptr, png_const_charp msg) {
118 mm_log((1, "PNG write warning '%s'\n", msg));
120 i_push_error(0, msg);
123 #define PNG_DIM_MAX 0x7fffffffL
126 i_writepng_wiol(i_img *im, io_glue *ig) {
128 png_infop info_ptr = NULL;
129 i_img_dim width,height;
130 volatile int cspace,channels;
132 int is_bilevel = 0, zero_is_white;
134 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
138 if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
139 i_push_error(0, "image too large for PNG");
146 /* if we ever have 64-bit i_img_dim
147 * the libpng docs state that png_set_user_limits() can be used to
148 * override the PNG_USER_*_MAX limits, but as implemented they
149 * don't. We check against the theoretical limit of PNG here, and
150 * try to override the limits below, in case the libpng
151 * implementation ever matches the documentation.
153 * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
154 * fixed in libpng 1.5.3
156 if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
157 i_push_error(0, "Image too large for PNG");
161 channels=im->channels;
163 if (i_img_is_monochrome(im, &zero_is_white)) {
166 cspace = PNG_COLOR_TYPE_GRAY;
167 mm_log((1, "i_writepng: bilevel output\n"));
169 else if (im->type == i_palette_type) {
170 int colors = i_colorcount(im);
172 cspace = PNG_COLOR_TYPE_PALETTE;
174 while ((1 << bits) < colors) {
177 mm_log((1, "i_writepng: paletted output\n"));
182 cspace = PNG_COLOR_TYPE_GRAY;
185 cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
188 cspace = PNG_COLOR_TYPE_RGB;
191 cspace = PNG_COLOR_TYPE_RGB_ALPHA;
194 fprintf(stderr, "Internal error, channels = %d\n", channels);
197 bits = im->bits > 8 ? 16 : 8;
198 mm_log((1, "i_writepng: direct output\n"));
201 mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
203 /* Create and initialize the png_struct with the desired error handler
204 * functions. If you want to use the default stderr and longjump method,
205 * you can supply NULL for the last three parameters. We also check that
206 * the library version is compatible with the one used at compile time,
207 * in case we are using dynamically linked libraries. REQUIRED.
210 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
211 error_handler, write_warn_handler);
213 if (png_ptr == NULL) return 0;
216 /* Allocate/initialize the image information data. REQUIRED */
217 info_ptr = png_create_info_struct(png_ptr);
219 if (info_ptr == NULL) {
220 png_destroy_write_struct(&png_ptr, &info_ptr);
224 /* Set error handling. REQUIRED if you aren't supplying your own
225 * error hadnling functions in the png_create_write_struct() call.
227 if (setjmp(png_jmpbuf(png_ptr))) {
228 png_destroy_write_struct(&png_ptr, &info_ptr);
232 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
234 /* Set the image information here. Width and height are up to 2^31,
235 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
236 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
237 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
238 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
239 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
240 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
243 /* by default, libpng (not PNG) limits the image size to a maximum
244 * 1000000 pixels in each direction, but Imager doesn't.
245 * Configure libpng to avoid that limit.
247 png_set_user_limits(png_ptr, width, height);
249 png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
250 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
252 if (!set_png_tags(im, png_ptr, info_ptr)) {
253 png_destroy_write_struct(&png_ptr, &info_ptr);
258 if (!write_bilevel(png_ptr, info_ptr, im)) {
259 png_destroy_write_struct(&png_ptr, &info_ptr);
263 else if (im->type == i_palette_type) {
264 if (!write_paletted(png_ptr, info_ptr, im, bits)) {
265 png_destroy_write_struct(&png_ptr, &info_ptr);
269 else if (bits == 16) {
270 if (!write_direct16(png_ptr, info_ptr, im)) {
271 png_destroy_write_struct(&png_ptr, &info_ptr);
276 if (!write_direct8(png_ptr, info_ptr, im)) {
277 png_destroy_write_struct(&png_ptr, &info_ptr);
282 png_write_end(png_ptr, info_ptr);
284 png_destroy_write_struct(&png_ptr, &info_ptr);
294 } i_png_read_state, *i_png_read_statep;
297 read_warn_handler(png_structp, png_const_charp);
300 cleanup_read_state(i_png_read_statep);
303 i_readpng_wiol(io_glue *ig, int flags) {
307 png_uint_32 width, height;
308 int bit_depth, color_type, interlace_type;
310 unsigned int sig_read;
316 mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
319 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs,
320 error_handler, read_warn_handler);
322 i_push_error(0, "Cannot create PNG read structure");
325 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
327 #if defined(PNG_BENIGN_ERRORS_SUPPORTED)
328 png_set_benign_errors(png_ptr, (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) ? 1 : 0);
329 #elif PNG_LIBPNG_VER >= 10400
330 if (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) {
331 i_push_error(0, "libpng not configured to ignore benign errors");
332 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
336 if (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) {
337 i_push_error(0, "libpng too old to ignore benign errors");
338 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
343 info_ptr = png_create_info_struct(png_ptr);
344 if (info_ptr == NULL) {
345 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
346 i_push_error(0, "Cannot create PNG info structure");
350 if (setjmp(png_jmpbuf(png_ptr))) {
351 if (im) i_img_destroy(im);
352 mm_log((1,"i_readpng_wiol: error.\n"));
353 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
354 cleanup_read_state(&rs);
358 /* we do our own limit checks */
359 png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
361 png_set_sig_bytes(png_ptr, sig_read);
362 png_read_info(png_ptr, info_ptr);
363 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
365 mm_log((1, "png_get_IHDR results: width %u, height %u, bit_depth %d, color_type %d, interlace_type %d\n",
366 (unsigned)width, (unsigned)height, bit_depth,color_type,interlace_type));
368 CC2C[PNG_COLOR_TYPE_GRAY]=1;
369 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
370 CC2C[PNG_COLOR_TYPE_RGB]=3;
371 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
372 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
373 channels = CC2C[color_type];
375 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
377 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
378 mm_log((1, "i_readpnm: image size exceeds limits\n"));
379 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
383 if (color_type == PNG_COLOR_TYPE_PALETTE) {
384 im = read_paletted(png_ptr, info_ptr, channels, width, height);
386 else if (color_type == PNG_COLOR_TYPE_GRAY
388 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
389 im = read_bilevel(png_ptr, info_ptr, width, height);
391 else if (bit_depth == 16) {
392 im = read_direct16(png_ptr, info_ptr, channels, width, height);
395 im = read_direct8(png_ptr, info_ptr, channels, width, height);
399 get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
401 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
405 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
408 cleanup_read_state(&rs);
410 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
416 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
417 i_img_dim width, i_img_dim height) {
418 i_img * volatile vim = NULL;
419 int color_type = png_get_color_type(png_ptr, info_ptr);
420 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
422 int number_passes, pass;
425 unsigned char * volatile vline = NULL;
427 if (setjmp(png_jmpbuf(png_ptr))) {
428 if (vim) i_img_destroy(vim);
429 if (vline) myfree(vline);
434 number_passes = png_set_interlace_handling(png_ptr);
435 mm_log((1,"number of passes=%d\n",number_passes));
437 png_set_strip_16(png_ptr);
438 png_set_packing(png_ptr);
440 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
441 png_set_expand(png_ptr);
443 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
445 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
446 png_set_expand(png_ptr);
449 png_read_update_info(png_ptr, info_ptr);
451 im = vim = i_img_8_new(width,height,channels);
453 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
457 line = vline = mymalloc(channels * width);
458 for (pass = 0; pass < number_passes; pass++) {
459 for (y = 0; y < height; y++) {
461 i_gsamp(im, 0, width, y, line, NULL, channels);
462 png_read_row(png_ptr,(png_bytep)line, NULL);
463 i_psamp(im, 0, width, y, line, NULL, channels);
469 png_read_end(png_ptr, info_ptr);
475 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
476 i_img_dim width, i_img_dim height) {
477 i_img * volatile vim = NULL;
479 int number_passes, pass;
482 unsigned char * volatile vline = NULL;
484 unsigned * volatile vbits_line = NULL;
487 if (setjmp(png_jmpbuf(png_ptr))) {
488 if (vim) i_img_destroy(vim);
489 if (vline) myfree(vline);
490 if (vbits_line) myfree(vbits_line);
495 number_passes = png_set_interlace_handling(png_ptr);
496 mm_log((1,"number of passes=%d\n",number_passes));
498 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
500 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
501 png_set_expand(png_ptr);
504 png_read_update_info(png_ptr, info_ptr);
506 im = vim = i_img_16_new(width,height,channels);
508 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
512 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
513 line = vline = mymalloc(row_bytes);
514 memset(line, 0, row_bytes);
515 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
516 for (pass = 0; pass < number_passes; pass++) {
517 for (y = 0; y < height; y++) {
519 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
520 for (x = 0; x < width * channels; ++x) {
521 line[x*2] = bits_line[x] >> 8;
522 line[x*2+1] = bits_line[x] & 0xff;
525 png_read_row(png_ptr,(png_bytep)line, NULL);
526 for (x = 0; x < width * channels; ++x)
527 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
528 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
536 png_read_end(png_ptr, info_ptr);
542 read_bilevel(png_structp png_ptr, png_infop info_ptr,
543 i_img_dim width, i_img_dim height) {
544 i_img * volatile vim = NULL;
546 int number_passes, pass;
549 unsigned char * volatile vline = NULL;
552 if (setjmp(png_jmpbuf(png_ptr))) {
553 if (vim) i_img_destroy(vim);
554 if (vline) myfree(vline);
559 number_passes = png_set_interlace_handling(png_ptr);
560 mm_log((1,"number of passes=%d\n",number_passes));
562 png_set_packing(png_ptr);
564 png_set_expand(png_ptr);
566 png_read_update_info(png_ptr, info_ptr);
568 im = vim = i_img_pal_new(width, height, 1, 256);
570 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
574 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
575 palette[0].channel[3] = 0;
576 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
577 palette[1].channel[3] = 255;
578 i_addcolors(im, palette, 2);
580 line = vline = mymalloc(width);
581 memset(line, 0, width);
582 for (pass = 0; pass < number_passes; pass++) {
583 for (y = 0; y < height; y++) {
585 i_gpal(im, 0, width, y, line);
586 /* expand indexes back to 0/255 */
587 for (x = 0; x < width; ++x)
588 line[x] = line[x] ? 255 : 0;
590 png_read_row(png_ptr,(png_bytep)line, NULL);
592 /* back to palette indexes */
593 for (x = 0; x < width; ++x)
594 line[x] = line[x] ? 1 : 0;
595 i_ppal(im, 0, width, y, line);
601 png_read_end(png_ptr, info_ptr);
606 /* FIXME: do we need to unscale palette color values from the
609 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
610 i_img_dim width, i_img_dim height) {
611 i_img * volatile vim = NULL;
612 int color_type = png_get_color_type(png_ptr, info_ptr);
613 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
615 int number_passes, pass;
618 unsigned char * volatile vline = NULL;
620 png_colorp png_palette;
621 png_bytep png_pal_trans;
622 png_color_16p png_color_trans;
625 if (setjmp(png_jmpbuf(png_ptr))) {
626 if (vim) i_img_destroy(vim);
627 if (vline) myfree(vline);
632 number_passes = png_set_interlace_handling(png_ptr);
633 mm_log((1,"number of passes=%d\n",number_passes));
635 png_set_strip_16(png_ptr);
636 png_set_packing(png_ptr);
638 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
639 png_set_expand(png_ptr);
641 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
642 i_push_error(0, "Paletted image with no PLTE chunk");
646 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
654 png_read_update_info(png_ptr, info_ptr);
656 im = vim = i_img_pal_new(width, height, channels, 256);
658 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
662 for (i = 0; i < num_palette; ++i) {
665 c.rgba.r = png_palette[i].red;
666 c.rgba.g = png_palette[i].green;
667 c.rgba.b = png_palette[i].blue;
668 if (i < num_pal_trans)
669 c.rgba.a = png_pal_trans[i];
672 i_addcolors(im, &c, 1);
675 line = vline = mymalloc(width);
676 for (pass = 0; pass < number_passes; pass++) {
677 for (y = 0; y < height; y++) {
679 i_gpal(im, 0, width, y, line);
680 png_read_row(png_ptr,(png_bytep)line, NULL);
681 i_ppal(im, 0, width, y, line);
687 png_read_end(png_ptr, info_ptr);
692 struct png_text_name {
697 static const struct png_text_name
699 { "Author", "png_author" },
700 { "Comment", "i_comment" },
701 { "Copyright", "png_copyright" },
702 { "Creation Time", "png_creation_time" },
703 { "Description", "png_description" },
704 { "Disclaimer", "png_disclaimer" },
705 { "Software", "png_software" },
706 { "Source", "png_source" },
707 { "Title", "png_title" },
708 { "Warning", "png_warning" }
711 static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
713 static const char * const
715 "png_chroma_white_x",
716 "png_chroma_white_y",
719 "png_chroma_green_x",
720 "png_chroma_green_y",
725 static const int chroma_tag_count = sizeof(chroma_tags) / sizeof(*chroma_tags);
728 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr,
729 int bit_depth, int color_type) {
730 png_uint_32 xres, yres;
733 i_tags_set(&im->tags, "i_format", "png", -1);
734 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
735 mm_log((1,"pHYs (%u, %u) %d\n", (unsigned)xres, (unsigned)yres, unit_type));
736 if (unit_type == PNG_RESOLUTION_METER) {
737 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
738 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
741 i_tags_setn(&im->tags, "i_xres", xres);
742 i_tags_setn(&im->tags, "i_yres", yres);
743 i_tags_setn(&im->tags, "i_aspect_only", 1);
747 int interlace = png_get_interlace_type(png_ptr, info_ptr);
749 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
751 case PNG_INTERLACE_NONE:
752 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
755 case PNG_INTERLACE_ADAM7:
756 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
760 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
765 /* the various readers can call png_set_expand(), libpng will make
766 it's internal record of bit_depth at least 8 in that case */
767 i_tags_setn(&im->tags, "png_bits", bit_depth);
769 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
771 if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
772 i_tags_setn(&im->tags, "png_srgb_intent", intent);
776 /* Ignore these if there's an sRGB chunk, libpng simulates
777 their existence if there's an sRGB chunk, and the PNG spec says
778 that these are ignored if the sRGB is present, so ignore them.
783 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
784 i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
787 if (png_get_cHRM(png_ptr, info_ptr, chroma+0, chroma+1,
788 chroma+2, chroma+3, chroma+4, chroma+5,
789 chroma+6, chroma+7)) {
792 for (i = 0; i < chroma_tag_count; ++i)
793 i_tags_set_float2(&im->tags, chroma_tags[i], 0, chroma[i], 4);
801 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
803 int custom_index = 0;
804 for (i = 0; i < num_text; ++i) {
807 int compressed = text[i].compression == PNG_ITXT_COMPRESSION_zTXt
808 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt;
810 for (j = 0; j < text_tags_count; ++j) {
811 if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
813 i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
815 sprintf(tag_name, "%s_compressed", text_tags[j].tagname);
816 i_tags_setn(&im->tags, tag_name, 1);
825 sprintf(tag_name, "png_text%d_key", custom_index);
826 i_tags_set(&im->tags, tag_name, text[i].key, -1);
827 sprintf(tag_name, "png_text%d_text", custom_index);
828 i_tags_set(&im->tags, tag_name, text[i].text, -1);
829 sprintf(tag_name, "png_text%d_type", custom_index);
830 i_tags_set(&im->tags, tag_name,
831 (text[i].compression == PNG_TEXT_COMPRESSION_NONE
832 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
833 "text" : "itxt", -1);
835 sprintf(tag_name, "png_text%d_compressed", custom_index);
836 i_tags_setn(&im->tags, tag_name, 1);
847 if (png_get_tIME(png_ptr, info_ptr, &mod_time)) {
848 char time_formatted[80];
850 sprintf(time_formatted, "%d-%02d-%02dT%02d:%02d:%02d",
851 mod_time->year, mod_time->month, mod_time->day,
852 mod_time->hour, mod_time->minute, mod_time->second);
853 i_tags_set(&im->tags, "png_time", time_formatted, -1);
861 if (png_get_bKGD(png_ptr, info_ptr, &back)) {
862 switch (color_type) {
863 case PNG_COLOR_TYPE_GRAY:
864 case PNG_COLOR_TYPE_GRAY_ALPHA:
866 /* lib png stores the raw gray value rather than scaling it
867 to 16-bit (or 8), we use 8-bit color for i_background */
872 gray = back->gray >> 8;
878 gray = 0x11 * back->gray;
881 gray = 0x55 * back->gray;
884 gray = back->gray ? 0xFF : 0;
889 c.rgb.r = c.rgb.g = c.rgb.b = gray;
893 case PNG_COLOR_TYPE_RGB:
894 case PNG_COLOR_TYPE_RGB_ALPHA:
896 c.rgb.r = bit_depth == 16 ? (back->red >> 8) : back->red;
897 c.rgb.g = bit_depth == 16 ? (back->green >> 8) : back->green;
898 c.rgb.b = bit_depth == 16 ? (back->blue >> 8) : back->blue;
902 case PNG_COLOR_TYPE_PALETTE:
904 c.rgb.g = back->green;
905 c.rgb.b = back->blue;
910 i_tags_set_color(&im->tags, "i_background", 0, &c);
915 #define GET_STR_BUF_SIZE 40
918 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
920 int aspect_only, have_res = 1;
922 if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
923 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
924 ; /* nothing to do */
929 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
936 i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
939 png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
940 aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
945 if (i_tags_get_int(&im->tags, "png_srgb_intent", 0, &intent)) {
946 if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) {
947 i_push_error(0, "tag png_srgb_intent out of range");
950 png_set_sRGB(png_ptr, info_ptr, intent);
953 double chroma[8], gamma;
955 int found_chroma_count = 0;
957 for (i = 0; i < chroma_tag_count; ++i) {
958 if (i_tags_get_float(&im->tags, chroma_tags[i], 0, chroma+i))
959 ++found_chroma_count;
962 if (found_chroma_count) {
963 if (found_chroma_count != chroma_tag_count) {
964 i_push_error(0, "all png_chroma_* tags must be supplied or none");
968 png_set_cHRM(png_ptr, info_ptr, chroma[0], chroma[1], chroma[2],
969 chroma[3], chroma[4], chroma[5], chroma[6], chroma[7]);
972 if (i_tags_get_float(&im->tags, "png_gamma", 0, &gamma)) {
973 png_set_gAMA(png_ptr, info_ptr, gamma);
979 /* png_set_text() is sparsely documented, it isn't indicated whether
980 multiple calls add to or replace the lists of texts, and
981 whether the text/keyword data is copied or not.
983 Examining the linpng code reveals that png_set_text() adds to
984 the list and that the text is copied.
988 /* do our standard tags */
989 for (i = 0; i < text_tags_count; ++i) {
990 char buf[GET_STR_BUF_SIZE];
994 data = get_string2(&im->tags, text_tags[i].tagname, buf, &size);
997 int compression = size > 1000;
998 char compress_tag[40];
1000 if (memchr(data, '\0', size)) {
1001 i_push_errorf(0, "tag %s may not contain NUL characters", text_tags[i].tagname);
1005 sprintf(compress_tag, "%s_compressed", text_tags[i].tagname);
1006 i_tags_get_int(&im->tags, compress_tag, 0, &compression);
1008 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1009 : PNG_TEXT_COMPRESSION_NONE;
1010 text.key = (char *)text_tags[i].keyword;
1011 text.text_length = size;
1012 text.text = (char *)data;
1013 #ifdef PNG_iTXt_SUPPORTED
1014 text.itxt_length = 0;
1016 text.lang_key = NULL;
1019 png_set_text(png_ptr, info_ptr, &text, 1);
1023 /* for non-standard tags ensure keywords are limited to 1 to 79
1028 char key_buf[GET_STR_BUF_SIZE], value_buf[GET_STR_BUF_SIZE];
1029 const char *key, *value;
1030 size_t key_size, value_size;
1032 sprintf(tag_name, "png_text%d_key", i);
1033 key = get_string2(&im->tags, tag_name, key_buf, &key_size);
1037 if (key_size < 1 || key_size > 79) {
1038 i_push_errorf(0, "tag %s must be between 1 and 79 characters in length", tag_name);
1042 if (key[0] == ' ' || key[key_size-1] == ' ') {
1043 i_push_errorf(0, "tag %s may not contain leading or trailing spaces", tag_name);
1047 if (strstr(key, " ")) {
1048 i_push_errorf(0, "tag %s may not contain consecutive spaces", tag_name);
1052 for (k = 0; k < key_size; ++k) {
1053 if (key[k] < 32 || (key[k] > 126 && key[k] < 161)) {
1054 i_push_errorf(0, "tag %s may only contain Latin1 characters 32-126, 161-255", tag_name);
1060 sprintf(tag_name, "png_text%d_text", i);
1061 value = get_string2(&im->tags, tag_name, value_buf, &value_size);
1064 if (memchr(value, '\0', value_size)) {
1065 i_push_errorf(0, "tag %s may not contain NUL characters", tag_name);
1072 int compression = value_size > 1000;
1074 sprintf(tag_name, "png_text%d_compressed", i);
1075 i_tags_get_int(&im->tags, tag_name, 0, &compression);
1077 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1078 : PNG_TEXT_COMPRESSION_NONE;
1079 text.key = (char *)key;
1080 text.text_length = value_size;
1081 text.text = (char *)value;
1082 #ifdef PNG_iTXt_SUPPORTED
1083 text.itxt_length = 0;
1085 text.lang_key = NULL;
1088 png_set_text(png_ptr, info_ptr, &text, 1);
1091 i_push_errorf(0, "tag png_text%d_key found but not png_text%d_text", i, i);
1095 i_push_errorf(0, "tag png_text%d_text found but not png_text%d_key", i, i);
1106 char buf[GET_STR_BUF_SIZE];
1108 const char *timestr = get_string2(&im->tags, "png_time", buf, &time_size);
1111 int year, month, day, hour, minute, second;
1114 if (sscanf(timestr, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6) {
1115 /* rough validation */
1116 if (month < 1 || month > 12
1117 || day < 1 || day > 31
1118 || hour < 0 || hour > 23
1119 || minute < 0 || minute > 59
1120 || second < 0 || second > 60) {
1121 i_push_error(0, "invalid date/time for png_time");
1124 mod_time.year = year;
1125 mod_time.month = month;
1127 mod_time.hour = hour;
1128 mod_time.minute = minute;
1129 mod_time.second = second;
1131 png_set_tIME(png_ptr, info_ptr, &mod_time);
1134 i_push_error(0, "png_time must be formatted 'y-m-dTh:m:s'");
1141 /* no bKGD support yet, maybe later
1142 it may be simpler to do it in the individual writers
1150 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size) {
1153 if (i_tags_find(tags, name, 0, &index)) {
1154 const i_img_tag *entry = tags->tags + index;
1157 *size = entry->size;
1162 *size = sprintf(buf, "%d", entry->idata);
1171 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1172 unsigned char *data, *volatile vdata = NULL;
1175 if (setjmp(png_jmpbuf(png_ptr))) {
1182 png_write_info(png_ptr, info_ptr);
1184 vdata = data = mymalloc(im->xsize * im->channels);
1185 for (y = 0; y < im->ysize; y++) {
1186 i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
1187 png_write_row(png_ptr, (png_bytep)data);
1195 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1196 unsigned *data, *volatile vdata = NULL;
1197 unsigned char *tran_data, * volatile vtran_data = NULL;
1198 i_img_dim samples_per_row = im->xsize * im->channels;
1202 if (setjmp(png_jmpbuf(png_ptr))) {
1211 png_write_info(png_ptr, info_ptr);
1213 vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
1214 vtran_data = tran_data = mymalloc(samples_per_row * 2);
1215 for (y = 0; y < im->ysize; y++) {
1217 unsigned char *p = tran_data;
1218 i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
1219 for (i = 0; i < samples_per_row; ++i) {
1220 p[0] = data[i] >> 8;
1221 p[1] = data[i] & 0xff;
1224 png_write_row(png_ptr, (png_bytep)tran_data);
1233 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
1234 unsigned char *data, *volatile vdata = NULL;
1236 unsigned char pal_map[256];
1237 png_color pcolors[256];
1238 i_color colors[256];
1239 int count = i_colorcount(im);
1242 if (setjmp(png_jmpbuf(png_ptr))) {
1249 i_getcolors(im, 0, colors, count);
1250 if (im->channels < 3) {
1251 /* convert the greyscale palette to color */
1253 for (i = 0; i < count; ++i) {
1254 i_color *c = colors + i;
1255 c->channel[3] = c->channel[1];
1256 c->channel[2] = c->channel[1] = c->channel[0];
1260 if (i_img_has_alpha(im)) {
1262 int bottom_index = 0, top_index = count-1;
1264 /* fill out the palette map */
1265 for (i = 0; i < count; ++i)
1268 /* the PNG spec suggests sorting the palette by alpha, but that's
1269 unnecessary - all we want to do is move the opaque entries to
1271 while (bottom_index < top_index) {
1272 if (colors[bottom_index].rgba.a == 255) {
1273 pal_map[bottom_index] = top_index;
1274 pal_map[top_index--] = bottom_index;
1280 for (i = 0; i < count; ++i) {
1281 int srci = i_img_has_alpha(im) ? pal_map[i] : i;
1283 pcolors[i].red = colors[srci].rgb.r;
1284 pcolors[i].green = colors[srci].rgb.g;
1285 pcolors[i].blue = colors[srci].rgb.b;
1288 png_set_PLTE(png_ptr, info_ptr, pcolors, count);
1290 if (i_img_has_alpha(im)) {
1291 unsigned char trans[256];
1294 for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
1295 trans[i] = colors[pal_map[i]].rgba.a;
1297 png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
1300 png_write_info(png_ptr, info_ptr);
1302 png_set_packing(png_ptr);
1304 vdata = data = mymalloc(im->xsize);
1305 for (y = 0; y < im->ysize; y++) {
1306 i_gpal(im, 0, im->xsize, y, data);
1307 if (i_img_has_alpha(im)) {
1309 for (x = 0; x < im->xsize; ++x)
1310 data[x] = pal_map[data[x]];
1312 png_write_row(png_ptr, (png_bytep)data);
1320 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1321 unsigned char *data, *volatile vdata = NULL;
1324 if (setjmp(png_jmpbuf(png_ptr))) {
1331 png_write_info(png_ptr, info_ptr);
1333 png_set_packing(png_ptr);
1335 vdata = data = mymalloc(im->xsize);
1336 for (y = 0; y < im->ysize; y++) {
1337 i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
1338 png_write_row(png_ptr, (png_bytep)data);
1346 read_warn_handler(png_structp png_ptr, png_const_charp msg) {
1347 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
1351 mm_log((1, "PNG read warning '%s'\n", msg));
1353 /* in case this is part of an error report */
1354 i_push_error(0, msg);
1356 /* and save in the warnings so if we do manage to succeed, we
1357 * can save it as a tag
1359 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
1361 + strlen(msg) /* new text */
1363 workp = myrealloc(rs->warnings, new_size);
1367 strcat(workp, "\n");
1368 rs->warnings = workp;
1372 cleanup_read_state(i_png_read_statep rs) {
1374 myfree(rs->warnings);