7 /* this is a way to get number of channels from color space
8 * Color code to channel number */
10 static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
12 #define PNG_BYTES_TO_CHECK 4
15 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
18 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
21 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
24 read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
27 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
30 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
33 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
36 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im);
39 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth, int color_type);
42 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
45 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size);
48 i_png_lib_version(void) {
49 return png_access_version_number();
52 static char const * const
55 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
58 #ifdef PNG_READ_SUPPORTED
61 #ifdef PNG_WRITE_SUPPORTED
64 #ifdef PNG_MNG_FEATURES_SUPPORTED
67 #ifdef PNG_CHECK_cHRM_SUPPORTED
70 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
77 i_png_features(void) {
82 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
83 io_glue *ig = png_get_io_ptr(png_ptr);
84 ssize_t rc = i_io_read(ig, data, length);
85 if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
89 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
91 io_glue *ig = png_get_io_ptr(png_ptr);
92 rc = i_io_write(ig, data, length);
93 if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
97 wiol_flush_data(png_structp png_ptr) {
98 io_glue *ig = png_get_io_ptr(png_ptr);
100 png_error(png_ptr, "Error flushing output");
104 error_handler(png_structp png_ptr, png_const_charp msg) {
105 mm_log((1, "PNG error: '%s'\n", msg));
107 i_push_error(0, msg);
108 longjmp(png_jmpbuf(png_ptr), 1);
113 For writing a warning might have information about an error, so send
114 it to the error stack.
118 write_warn_handler(png_structp png_ptr, png_const_charp msg) {
119 mm_log((1, "PNG write warning '%s'\n", msg));
121 i_push_error(0, msg);
124 #define PNG_DIM_MAX 0x7fffffffL
127 i_writepng_wiol(i_img *im, io_glue *ig) {
129 png_infop info_ptr = NULL;
130 i_img_dim width,height;
131 volatile int cspace,channels;
133 int is_bilevel = 0, zero_is_white;
135 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
139 if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
140 i_push_error(0, "image too large for PNG");
147 /* if we ever have 64-bit i_img_dim
148 * the libpng docs state that png_set_user_limits() can be used to
149 * override the PNG_USER_*_MAX limits, but as implemented they
150 * don't. We check against the theoretical limit of PNG here, and
151 * try to override the limits below, in case the libpng
152 * implementation ever matches the documentation.
154 * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
155 * fixed in libpng 1.5.3
157 if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
158 i_push_error(0, "Image too large for PNG");
162 channels=im->channels;
164 if (i_img_is_monochrome(im, &zero_is_white)) {
167 cspace = PNG_COLOR_TYPE_GRAY;
168 mm_log((1, "i_writepng: bilevel output\n"));
170 else if (im->type == i_palette_type) {
171 int colors = i_colorcount(im);
173 cspace = PNG_COLOR_TYPE_PALETTE;
175 while ((1 << bits) < colors) {
178 mm_log((1, "i_writepng: paletted output\n"));
183 cspace = PNG_COLOR_TYPE_GRAY;
186 cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
189 cspace = PNG_COLOR_TYPE_RGB;
192 cspace = PNG_COLOR_TYPE_RGB_ALPHA;
195 fprintf(stderr, "Internal error, channels = %d\n", channels);
198 bits = im->bits > 8 ? 16 : 8;
199 mm_log((1, "i_writepng: direct output\n"));
202 mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
204 /* Create and initialize the png_struct with the desired error handler
205 * functions. If you want to use the default stderr and longjump method,
206 * you can supply NULL for the last three parameters. We also check that
207 * the library version is compatible with the one used at compile time,
208 * in case we are using dynamically linked libraries. REQUIRED.
211 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
212 error_handler, write_warn_handler);
214 if (png_ptr == NULL) return 0;
217 /* Allocate/initialize the image information data. REQUIRED */
218 info_ptr = png_create_info_struct(png_ptr);
220 if (info_ptr == NULL) {
221 png_destroy_write_struct(&png_ptr, &info_ptr);
225 /* Set error handling. REQUIRED if you aren't supplying your own
226 * error hadnling functions in the png_create_write_struct() call.
228 if (setjmp(png_jmpbuf(png_ptr))) {
229 png_destroy_write_struct(&png_ptr, &info_ptr);
233 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
235 /* Set the image information here. Width and height are up to 2^31,
236 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
237 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
238 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
239 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
240 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
241 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
244 /* by default, libpng (not PNG) limits the image size to a maximum
245 * 1000000 pixels in each direction, but Imager doesn't.
246 * Configure libpng to avoid that limit.
248 png_set_user_limits(png_ptr, width, height);
250 png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
251 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
253 if (!set_png_tags(im, png_ptr, info_ptr)) {
254 png_destroy_write_struct(&png_ptr, &info_ptr);
259 if (!write_bilevel(png_ptr, info_ptr, im)) {
260 png_destroy_write_struct(&png_ptr, &info_ptr);
264 else if (im->type == i_palette_type) {
265 if (!write_paletted(png_ptr, info_ptr, im, bits)) {
266 png_destroy_write_struct(&png_ptr, &info_ptr);
270 else if (bits == 16) {
271 if (!write_direct16(png_ptr, info_ptr, im)) {
272 png_destroy_write_struct(&png_ptr, &info_ptr);
277 if (!write_direct8(png_ptr, info_ptr, im)) {
278 png_destroy_write_struct(&png_ptr, &info_ptr);
283 png_write_end(png_ptr, info_ptr);
285 png_destroy_write_struct(&png_ptr, &info_ptr);
295 } i_png_read_state, *i_png_read_statep;
298 read_warn_handler(png_structp, png_const_charp);
301 cleanup_read_state(i_png_read_statep);
304 i_readpng_wiol(io_glue *ig, int flags) {
308 png_uint_32 width, height;
309 int bit_depth, color_type, interlace_type;
311 unsigned int sig_read;
317 mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
320 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs,
321 error_handler, read_warn_handler);
323 i_push_error(0, "Cannot create PNG read structure");
326 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
328 #if defined(PNG_BENIGN_ERRORS_SUPPORTED)
329 png_set_benign_errors(png_ptr, (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) ? 1 : 0);
330 #elif PNG_LIBPNG_VER >= 10400
331 if (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) {
332 i_push_error(0, "libpng not configured to ignore benign errors");
333 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
337 if (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) {
338 i_push_error(0, "libpng too old to ignore benign errors");
339 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
344 info_ptr = png_create_info_struct(png_ptr);
345 if (info_ptr == NULL) {
346 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
347 i_push_error(0, "Cannot create PNG info structure");
351 if (setjmp(png_jmpbuf(png_ptr))) {
352 if (im) i_img_destroy(im);
353 mm_log((1,"i_readpng_wiol: error.\n"));
354 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
355 cleanup_read_state(&rs);
359 /* we do our own limit checks */
360 png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
362 png_set_sig_bytes(png_ptr, sig_read);
363 png_read_info(png_ptr, info_ptr);
364 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
366 mm_log((1, "png_get_IHDR results: width %u, height %u, bit_depth %d, color_type %d, interlace_type %d\n",
367 (unsigned)width, (unsigned)height, bit_depth,color_type,interlace_type));
369 CC2C[PNG_COLOR_TYPE_GRAY]=1;
370 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
371 CC2C[PNG_COLOR_TYPE_RGB]=3;
372 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
373 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
374 channels = CC2C[color_type];
376 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
378 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
379 mm_log((1, "i_readpnm: image size exceeds limits\n"));
380 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
384 if (color_type == PNG_COLOR_TYPE_PALETTE) {
385 im = read_paletted(png_ptr, info_ptr, channels, width, height);
387 else if (color_type == PNG_COLOR_TYPE_GRAY
389 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
390 im = read_bilevel(png_ptr, info_ptr, width, height);
392 else if (bit_depth == 16) {
393 im = read_direct16(png_ptr, info_ptr, channels, width, height);
396 im = read_direct8(png_ptr, info_ptr, channels, width, height);
400 get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
402 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
406 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
409 cleanup_read_state(&rs);
411 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
417 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
418 i_img_dim width, i_img_dim height) {
419 i_img * volatile vim = NULL;
420 int color_type = png_get_color_type(png_ptr, info_ptr);
421 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
423 int number_passes, pass;
426 unsigned char * volatile vline = NULL;
428 if (setjmp(png_jmpbuf(png_ptr))) {
429 if (vim) i_img_destroy(vim);
430 if (vline) myfree(vline);
435 number_passes = png_set_interlace_handling(png_ptr);
436 mm_log((1,"number of passes=%d\n",number_passes));
438 png_set_strip_16(png_ptr);
439 png_set_packing(png_ptr);
441 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
442 png_set_expand(png_ptr);
444 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
446 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
447 png_set_expand(png_ptr);
450 png_read_update_info(png_ptr, info_ptr);
452 im = vim = i_img_8_new(width,height,channels);
454 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
458 line = vline = mymalloc(channels * width);
459 for (pass = 0; pass < number_passes; pass++) {
460 for (y = 0; y < height; y++) {
462 i_gsamp(im, 0, width, y, line, NULL, channels);
463 png_read_row(png_ptr,(png_bytep)line, NULL);
464 i_psamp(im, 0, width, y, line, NULL, channels);
470 png_read_end(png_ptr, info_ptr);
476 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
477 i_img_dim width, i_img_dim height) {
478 i_img * volatile vim = NULL;
480 int number_passes, pass;
483 unsigned char * volatile vline = NULL;
485 unsigned * volatile vbits_line = NULL;
488 if (setjmp(png_jmpbuf(png_ptr))) {
489 if (vim) i_img_destroy(vim);
490 if (vline) myfree(vline);
491 if (vbits_line) myfree(vbits_line);
496 number_passes = png_set_interlace_handling(png_ptr);
497 mm_log((1,"number of passes=%d\n",number_passes));
499 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
501 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
502 png_set_expand(png_ptr);
505 png_read_update_info(png_ptr, info_ptr);
507 im = vim = i_img_16_new(width,height,channels);
509 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
513 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
514 line = vline = mymalloc(row_bytes);
515 memset(line, 0, row_bytes);
516 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
517 for (pass = 0; pass < number_passes; pass++) {
518 for (y = 0; y < height; y++) {
520 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
521 for (x = 0; x < width * channels; ++x) {
522 line[x*2] = bits_line[x] >> 8;
523 line[x*2+1] = bits_line[x] & 0xff;
526 png_read_row(png_ptr,(png_bytep)line, NULL);
527 for (x = 0; x < width * channels; ++x)
528 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
529 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
537 png_read_end(png_ptr, info_ptr);
543 read_bilevel(png_structp png_ptr, png_infop info_ptr,
544 i_img_dim width, i_img_dim height) {
545 i_img * volatile vim = NULL;
547 int number_passes, pass;
550 unsigned char * volatile vline = NULL;
553 if (setjmp(png_jmpbuf(png_ptr))) {
554 if (vim) i_img_destroy(vim);
555 if (vline) myfree(vline);
560 number_passes = png_set_interlace_handling(png_ptr);
561 mm_log((1,"number of passes=%d\n",number_passes));
563 png_set_packing(png_ptr);
565 png_set_expand(png_ptr);
567 png_read_update_info(png_ptr, info_ptr);
569 im = vim = i_img_pal_new(width, height, 1, 256);
571 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
575 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
576 palette[0].channel[3] = 0;
577 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
578 palette[1].channel[3] = 255;
579 i_addcolors(im, palette, 2);
581 line = vline = mymalloc(width);
582 memset(line, 0, width);
583 for (pass = 0; pass < number_passes; pass++) {
584 for (y = 0; y < height; y++) {
586 i_gpal(im, 0, width, y, line);
587 /* expand indexes back to 0/255 */
588 for (x = 0; x < width; ++x)
589 line[x] = line[x] ? 255 : 0;
591 png_read_row(png_ptr,(png_bytep)line, NULL);
593 /* back to palette indexes */
594 for (x = 0; x < width; ++x)
595 line[x] = line[x] ? 1 : 0;
596 i_ppal(im, 0, width, y, line);
602 png_read_end(png_ptr, info_ptr);
607 /* FIXME: do we need to unscale palette color values from the
610 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
611 i_img_dim width, i_img_dim height) {
612 i_img * volatile vim = NULL;
613 int color_type = png_get_color_type(png_ptr, info_ptr);
614 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
616 int number_passes, pass;
619 unsigned char * volatile vline = NULL;
621 png_colorp png_palette;
622 png_bytep png_pal_trans;
623 png_color_16p png_color_trans;
626 if (setjmp(png_jmpbuf(png_ptr))) {
627 if (vim) i_img_destroy(vim);
628 if (vline) myfree(vline);
633 number_passes = png_set_interlace_handling(png_ptr);
634 mm_log((1,"number of passes=%d\n",number_passes));
636 png_set_strip_16(png_ptr);
637 png_set_packing(png_ptr);
639 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
640 png_set_expand(png_ptr);
642 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
643 i_push_error(0, "Paletted image with no PLTE chunk");
647 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
655 png_read_update_info(png_ptr, info_ptr);
657 im = vim = i_img_pal_new(width, height, channels, 256);
659 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
663 for (i = 0; i < num_palette; ++i) {
666 c.rgba.r = png_palette[i].red;
667 c.rgba.g = png_palette[i].green;
668 c.rgba.b = png_palette[i].blue;
669 if (i < num_pal_trans)
670 c.rgba.a = png_pal_trans[i];
673 i_addcolors(im, &c, 1);
676 line = vline = mymalloc(width);
677 for (pass = 0; pass < number_passes; pass++) {
678 for (y = 0; y < height; y++) {
680 i_gpal(im, 0, width, y, line);
681 png_read_row(png_ptr,(png_bytep)line, NULL);
682 i_ppal(im, 0, width, y, line);
688 png_read_end(png_ptr, info_ptr);
693 struct png_text_name {
698 static const struct png_text_name
700 { "Author", "png_author" },
701 { "Comment", "i_comment" },
702 { "Copyright", "png_copyright" },
703 { "Creation Time", "png_creation_time" },
704 { "Description", "png_description" },
705 { "Disclaimer", "png_disclaimer" },
706 { "Software", "png_software" },
707 { "Source", "png_source" },
708 { "Title", "png_title" },
709 { "Warning", "png_warning" }
712 static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
714 static const char * const
716 "png_chroma_white_x",
717 "png_chroma_white_y",
720 "png_chroma_green_x",
721 "png_chroma_green_y",
726 static const int chroma_tag_count = sizeof(chroma_tags) / sizeof(*chroma_tags);
729 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr,
730 int bit_depth, int color_type) {
731 png_uint_32 xres, yres;
734 i_tags_set(&im->tags, "i_format", "png", -1);
735 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
736 mm_log((1,"pHYs (%u, %u) %d\n", (unsigned)xres, (unsigned)yres, unit_type));
737 if (unit_type == PNG_RESOLUTION_METER) {
738 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
739 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
742 i_tags_setn(&im->tags, "i_xres", xres);
743 i_tags_setn(&im->tags, "i_yres", yres);
744 i_tags_setn(&im->tags, "i_aspect_only", 1);
748 int interlace = png_get_interlace_type(png_ptr, info_ptr);
750 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
752 case PNG_INTERLACE_NONE:
753 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
756 case PNG_INTERLACE_ADAM7:
757 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
761 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
766 /* the various readers can call png_set_expand(), libpng will make
767 it's internal record of bit_depth at least 8 in that case */
768 i_tags_setn(&im->tags, "png_bits", bit_depth);
770 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
772 if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
773 i_tags_setn(&im->tags, "png_srgb_intent", intent);
777 /* Ignore these if there's an sRGB chunk, libpng simulates
778 their existence if there's an sRGB chunk, and the PNG spec says
779 that these are ignored if the sRGB is present, so ignore them.
784 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
785 i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
788 if (png_get_cHRM(png_ptr, info_ptr, chroma+0, chroma+1,
789 chroma+2, chroma+3, chroma+4, chroma+5,
790 chroma+6, chroma+7)) {
793 for (i = 0; i < chroma_tag_count; ++i)
794 i_tags_set_float2(&im->tags, chroma_tags[i], 0, chroma[i], 4);
802 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
804 int custom_index = 0;
805 for (i = 0; i < num_text; ++i) {
808 int compressed = text[i].compression == PNG_ITXT_COMPRESSION_zTXt
809 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt;
811 for (j = 0; j < text_tags_count; ++j) {
812 if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
814 i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
816 sprintf(tag_name, "%s_compressed", text_tags[j].tagname);
817 i_tags_setn(&im->tags, tag_name, 1);
826 sprintf(tag_name, "png_text%d_key", custom_index);
827 i_tags_set(&im->tags, tag_name, text[i].key, -1);
828 sprintf(tag_name, "png_text%d_text", custom_index);
829 i_tags_set(&im->tags, tag_name, text[i].text, -1);
830 sprintf(tag_name, "png_text%d_type", custom_index);
831 i_tags_set(&im->tags, tag_name,
832 (text[i].compression == PNG_TEXT_COMPRESSION_NONE
833 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
834 "text" : "itxt", -1);
836 sprintf(tag_name, "png_text%d_compressed", custom_index);
837 i_tags_setn(&im->tags, tag_name, 1);
848 if (png_get_tIME(png_ptr, info_ptr, &mod_time)) {
849 char time_formatted[80];
851 sprintf(time_formatted, "%d-%02d-%02dT%02d:%02d:%02d",
852 mod_time->year, mod_time->month, mod_time->day,
853 mod_time->hour, mod_time->minute, mod_time->second);
854 i_tags_set(&im->tags, "png_time", time_formatted, -1);
862 if (png_get_bKGD(png_ptr, info_ptr, &back)) {
863 switch (color_type) {
864 case PNG_COLOR_TYPE_GRAY:
865 case PNG_COLOR_TYPE_GRAY_ALPHA:
867 /* lib png stores the raw gray value rather than scaling it
868 to 16-bit (or 8), we use 8-bit color for i_background */
873 gray = back->gray >> 8;
879 gray = 0x11 * back->gray;
882 gray = 0x55 * back->gray;
885 gray = back->gray ? 0xFF : 0;
890 c.rgb.r = c.rgb.g = c.rgb.b = gray;
894 case PNG_COLOR_TYPE_RGB:
895 case PNG_COLOR_TYPE_RGB_ALPHA:
897 c.rgb.r = bit_depth == 16 ? (back->red >> 8) : back->red;
898 c.rgb.g = bit_depth == 16 ? (back->green >> 8) : back->green;
899 c.rgb.b = bit_depth == 16 ? (back->blue >> 8) : back->blue;
903 case PNG_COLOR_TYPE_PALETTE:
905 c.rgb.g = back->green;
906 c.rgb.b = back->blue;
911 i_tags_set_color(&im->tags, "i_background", 0, &c);
916 #define GET_STR_BUF_SIZE 40
919 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
921 int aspect_only, have_res = 1;
923 if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
924 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
925 ; /* nothing to do */
930 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
937 i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
940 png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
941 aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
946 if (i_tags_get_int(&im->tags, "png_srgb_intent", 0, &intent)) {
947 if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) {
948 i_push_error(0, "tag png_srgb_intent out of range");
951 png_set_sRGB(png_ptr, info_ptr, intent);
954 double chroma[8], gamma;
956 int found_chroma_count = 0;
958 for (i = 0; i < chroma_tag_count; ++i) {
959 if (i_tags_get_float(&im->tags, chroma_tags[i], 0, chroma+i))
960 ++found_chroma_count;
963 if (found_chroma_count) {
964 if (found_chroma_count != chroma_tag_count) {
965 i_push_error(0, "all png_chroma_* tags must be supplied or none");
969 png_set_cHRM(png_ptr, info_ptr, chroma[0], chroma[1], chroma[2],
970 chroma[3], chroma[4], chroma[5], chroma[6], chroma[7]);
973 if (i_tags_get_float(&im->tags, "png_gamma", 0, &gamma)) {
974 png_set_gAMA(png_ptr, info_ptr, gamma);
980 /* png_set_text() is sparsely documented, it isn't indicated whether
981 multiple calls add to or replace the lists of texts, and
982 whether the text/keyword data is copied or not.
984 Examining the linpng code reveals that png_set_text() adds to
985 the list and that the text is copied.
989 /* do our standard tags */
990 for (i = 0; i < text_tags_count; ++i) {
991 char buf[GET_STR_BUF_SIZE];
995 data = get_string2(&im->tags, text_tags[i].tagname, buf, &size);
998 int compression = size > 1000;
999 char compress_tag[40];
1001 if (memchr(data, '\0', size)) {
1002 i_push_errorf(0, "tag %s may not contain NUL characters", text_tags[i].tagname);
1006 sprintf(compress_tag, "%s_compressed", text_tags[i].tagname);
1007 i_tags_get_int(&im->tags, compress_tag, 0, &compression);
1009 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1010 : PNG_TEXT_COMPRESSION_NONE;
1011 text.key = (char *)text_tags[i].keyword;
1012 text.text_length = size;
1013 text.text = (char *)data;
1014 #ifdef PNG_iTXt_SUPPORTED
1015 text.itxt_length = 0;
1017 text.lang_key = NULL;
1020 png_set_text(png_ptr, info_ptr, &text, 1);
1024 /* for non-standard tags ensure keywords are limited to 1 to 79
1029 char key_buf[GET_STR_BUF_SIZE], value_buf[GET_STR_BUF_SIZE];
1030 const char *key, *value;
1031 size_t key_size, value_size;
1033 sprintf(tag_name, "png_text%d_key", i);
1034 key = get_string2(&im->tags, tag_name, key_buf, &key_size);
1038 if (key_size < 1 || key_size > 79) {
1039 i_push_errorf(0, "tag %s must be between 1 and 79 characters in length", tag_name);
1043 if (key[0] == ' ' || key[key_size-1] == ' ') {
1044 i_push_errorf(0, "tag %s may not contain leading or trailing spaces", tag_name);
1048 if (strstr(key, " ")) {
1049 i_push_errorf(0, "tag %s may not contain consecutive spaces", tag_name);
1053 for (k = 0; k < key_size; ++k) {
1054 if (key[k] < 32 || (key[k] > 126 && key[k] < 161)) {
1055 i_push_errorf(0, "tag %s may only contain Latin1 characters 32-126, 161-255", tag_name);
1061 sprintf(tag_name, "png_text%d_text", i);
1062 value = get_string2(&im->tags, tag_name, value_buf, &value_size);
1065 if (memchr(value, '\0', value_size)) {
1066 i_push_errorf(0, "tag %s may not contain NUL characters", tag_name);
1073 int compression = value_size > 1000;
1075 sprintf(tag_name, "png_text%d_compressed", i);
1076 i_tags_get_int(&im->tags, tag_name, 0, &compression);
1078 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1079 : PNG_TEXT_COMPRESSION_NONE;
1080 text.key = (char *)key;
1081 text.text_length = value_size;
1082 text.text = (char *)value;
1083 #ifdef PNG_iTXt_SUPPORTED
1084 text.itxt_length = 0;
1086 text.lang_key = NULL;
1089 png_set_text(png_ptr, info_ptr, &text, 1);
1092 i_push_errorf(0, "tag png_text%d_key found but not png_text%d_text", i, i);
1096 i_push_errorf(0, "tag png_text%d_text found but not png_text%d_key", i, i);
1107 char buf[GET_STR_BUF_SIZE];
1109 const char *timestr = get_string2(&im->tags, "png_time", buf, &time_size);
1112 int year, month, day, hour, minute, second;
1115 if (sscanf(timestr, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6) {
1116 /* rough validation */
1117 if (month < 1 || month > 12
1118 || day < 1 || day > 31
1119 || hour < 0 || hour > 23
1120 || minute < 0 || minute > 59
1121 || second < 0 || second > 60) {
1122 i_push_error(0, "invalid date/time for png_time");
1125 mod_time.year = year;
1126 mod_time.month = month;
1128 mod_time.hour = hour;
1129 mod_time.minute = minute;
1130 mod_time.second = second;
1132 png_set_tIME(png_ptr, info_ptr, &mod_time);
1135 i_push_error(0, "png_time must be formatted 'y-m-dTh:m:s'");
1143 if (i_tags_get_int(&im->tags, "png_compression_level", 0, &level)) {
1144 if (level >= Z_NO_COMPRESSION && level <= Z_BEST_COMPRESSION)
1145 png_set_compression_level(png_ptr, level);
1147 i_push_errorf(0, "png_compression_level must be between %d and %d",
1148 Z_NO_COMPRESSION, Z_BEST_COMPRESSION);
1155 /* no bKGD support yet, maybe later
1156 it may be simpler to do it in the individual writers
1164 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size) {
1167 if (i_tags_find(tags, name, 0, &index)) {
1168 const i_img_tag *entry = tags->tags + index;
1171 *size = entry->size;
1176 *size = sprintf(buf, "%d", entry->idata);
1185 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1186 unsigned char *data, *volatile vdata = NULL;
1189 if (setjmp(png_jmpbuf(png_ptr))) {
1196 png_write_info(png_ptr, info_ptr);
1198 vdata = data = mymalloc(im->xsize * im->channels);
1199 for (y = 0; y < im->ysize; y++) {
1200 i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
1201 png_write_row(png_ptr, (png_bytep)data);
1209 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1210 unsigned *data, *volatile vdata = NULL;
1211 unsigned char *tran_data, * volatile vtran_data = NULL;
1212 i_img_dim samples_per_row = im->xsize * im->channels;
1216 if (setjmp(png_jmpbuf(png_ptr))) {
1225 png_write_info(png_ptr, info_ptr);
1227 vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
1228 vtran_data = tran_data = mymalloc(samples_per_row * 2);
1229 for (y = 0; y < im->ysize; y++) {
1231 unsigned char *p = tran_data;
1232 i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
1233 for (i = 0; i < samples_per_row; ++i) {
1234 p[0] = data[i] >> 8;
1235 p[1] = data[i] & 0xff;
1238 png_write_row(png_ptr, (png_bytep)tran_data);
1247 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
1248 unsigned char *data, *volatile vdata = NULL;
1250 unsigned char pal_map[256];
1251 png_color pcolors[256];
1252 i_color colors[256];
1253 int count = i_colorcount(im);
1256 if (setjmp(png_jmpbuf(png_ptr))) {
1263 i_getcolors(im, 0, colors, count);
1264 if (im->channels < 3) {
1265 /* convert the greyscale palette to color */
1267 for (i = 0; i < count; ++i) {
1268 i_color *c = colors + i;
1269 c->channel[3] = c->channel[1];
1270 c->channel[2] = c->channel[1] = c->channel[0];
1274 if (i_img_has_alpha(im)) {
1276 int bottom_index = 0, top_index = count-1;
1278 /* fill out the palette map */
1279 for (i = 0; i < count; ++i)
1282 /* the PNG spec suggests sorting the palette by alpha, but that's
1283 unnecessary - all we want to do is move the opaque entries to
1285 while (bottom_index < top_index) {
1286 if (colors[bottom_index].rgba.a == 255) {
1287 pal_map[bottom_index] = top_index;
1288 pal_map[top_index--] = bottom_index;
1294 for (i = 0; i < count; ++i) {
1295 int srci = i_img_has_alpha(im) ? pal_map[i] : i;
1297 pcolors[i].red = colors[srci].rgb.r;
1298 pcolors[i].green = colors[srci].rgb.g;
1299 pcolors[i].blue = colors[srci].rgb.b;
1302 png_set_PLTE(png_ptr, info_ptr, pcolors, count);
1304 if (i_img_has_alpha(im)) {
1305 unsigned char trans[256];
1308 for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
1309 trans[i] = colors[pal_map[i]].rgba.a;
1311 png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
1314 png_write_info(png_ptr, info_ptr);
1316 png_set_packing(png_ptr);
1318 vdata = data = mymalloc(im->xsize);
1319 for (y = 0; y < im->ysize; y++) {
1320 i_gpal(im, 0, im->xsize, y, data);
1321 if (i_img_has_alpha(im)) {
1323 for (x = 0; x < im->xsize; ++x)
1324 data[x] = pal_map[data[x]];
1326 png_write_row(png_ptr, (png_bytep)data);
1334 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1335 unsigned char *data, *volatile vdata = NULL;
1338 if (setjmp(png_jmpbuf(png_ptr))) {
1345 png_write_info(png_ptr, info_ptr);
1347 png_set_packing(png_ptr);
1349 vdata = data = mymalloc(im->xsize);
1350 for (y = 0; y < im->ysize; y++) {
1351 i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
1352 png_write_row(png_ptr, (png_bytep)data);
1360 read_warn_handler(png_structp png_ptr, png_const_charp msg) {
1361 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
1365 mm_log((1, "PNG read warning '%s'\n", msg));
1367 /* in case this is part of an error report */
1368 i_push_error(0, msg);
1370 /* and save in the warnings so if we do manage to succeed, we
1371 * can save it as a tag
1373 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
1375 + strlen(msg) /* new text */
1377 workp = myrealloc(rs->warnings, new_size);
1381 strcat(workp, "\n");
1382 rs->warnings = workp;
1386 cleanup_read_state(i_png_read_statep rs) {
1388 myfree(rs->warnings);