5 /* this is a way to get number of channels from color space
6 * Color code to channel number */
8 static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
10 #define PNG_BYTES_TO_CHECK 4
13 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
16 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
19 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
22 read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
25 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
28 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
31 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
34 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im);
37 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth, int color_type);
40 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
43 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size);
46 i_png_lib_version(void) {
47 return png_access_version_number();
51 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
52 io_glue *ig = png_get_io_ptr(png_ptr);
53 ssize_t rc = i_io_read(ig, data, length);
54 if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
58 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
60 io_glue *ig = png_get_io_ptr(png_ptr);
61 rc = i_io_write(ig, data, length);
62 if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
66 wiol_flush_data(png_structp png_ptr) {
67 io_glue *ig = png_get_io_ptr(png_ptr);
69 png_error(png_ptr, "Error flushing output");
73 error_handler(png_structp png_ptr, png_const_charp msg) {
74 mm_log((1, "PNG error: '%s'\n", msg));
77 longjmp(png_jmpbuf(png_ptr), 1);
82 For writing a warning might have information about an error, so send
83 it to the error stack.
87 write_warn_handler(png_structp png_ptr, png_const_charp msg) {
88 mm_log((1, "PNG write warning '%s'\n", msg));
93 #define PNG_DIM_MAX 0x7fffffffL
96 i_writepng_wiol(i_img *im, io_glue *ig) {
98 png_infop info_ptr = NULL;
99 i_img_dim width,height;
100 volatile int cspace,channels;
102 int is_bilevel = 0, zero_is_white;
104 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
108 if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
109 i_push_error(0, "image too large for PNG");
116 /* if we ever have 64-bit i_img_dim
117 * the libpng docs state that png_set_user_limits() can be used to
118 * override the PNG_USER_*_MAX limits, but as implemented they
119 * don't. We check against the theoretical limit of PNG here, and
120 * try to override the limits below, in case the libpng
121 * implementation ever matches the documentation.
123 * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
124 * fixed in libpng 1.5.3
126 if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
127 i_push_error(0, "Image too large for PNG");
131 channels=im->channels;
133 if (i_img_is_monochrome(im, &zero_is_white)) {
136 cspace = PNG_COLOR_TYPE_GRAY;
137 mm_log((1, "i_writepng: bilevel output\n"));
139 else if (im->type == i_palette_type) {
140 int colors = i_colorcount(im);
142 cspace = PNG_COLOR_TYPE_PALETTE;
144 while ((1 << bits) < colors) {
147 mm_log((1, "i_writepng: paletted output\n"));
152 cspace = PNG_COLOR_TYPE_GRAY;
155 cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
158 cspace = PNG_COLOR_TYPE_RGB;
161 cspace = PNG_COLOR_TYPE_RGB_ALPHA;
164 fprintf(stderr, "Internal error, channels = %d\n", channels);
167 bits = im->bits > 8 ? 16 : 8;
168 mm_log((1, "i_writepng: direct output\n"));
171 mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
173 /* Create and initialize the png_struct with the desired error handler
174 * functions. If you want to use the default stderr and longjump method,
175 * you can supply NULL for the last three parameters. We also check that
176 * the library version is compatible with the one used at compile time,
177 * in case we are using dynamically linked libraries. REQUIRED.
180 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
181 error_handler, write_warn_handler);
183 if (png_ptr == NULL) return 0;
186 /* Allocate/initialize the image information data. REQUIRED */
187 info_ptr = png_create_info_struct(png_ptr);
189 if (info_ptr == NULL) {
190 png_destroy_write_struct(&png_ptr, &info_ptr);
194 /* Set error handling. REQUIRED if you aren't supplying your own
195 * error hadnling functions in the png_create_write_struct() call.
197 if (setjmp(png_jmpbuf(png_ptr))) {
198 png_destroy_write_struct(&png_ptr, &info_ptr);
202 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
204 /* Set the image information here. Width and height are up to 2^31,
205 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
206 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
207 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
208 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
209 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
210 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
213 /* by default, libpng (not PNG) limits the image size to a maximum
214 * 1000000 pixels in each direction, but Imager doesn't.
215 * Configure libpng to avoid that limit.
217 png_set_user_limits(png_ptr, width, height);
219 png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
220 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
222 if (!set_png_tags(im, png_ptr, info_ptr)) {
223 png_destroy_write_struct(&png_ptr, &info_ptr);
228 if (!write_bilevel(png_ptr, info_ptr, im)) {
229 png_destroy_write_struct(&png_ptr, &info_ptr);
233 else if (im->type == i_palette_type) {
234 if (!write_paletted(png_ptr, info_ptr, im, bits)) {
235 png_destroy_write_struct(&png_ptr, &info_ptr);
239 else if (bits == 16) {
240 if (!write_direct16(png_ptr, info_ptr, im)) {
241 png_destroy_write_struct(&png_ptr, &info_ptr);
246 if (!write_direct8(png_ptr, info_ptr, im)) {
247 png_destroy_write_struct(&png_ptr, &info_ptr);
252 png_write_end(png_ptr, info_ptr);
254 png_destroy_write_struct(&png_ptr, &info_ptr);
264 } i_png_read_state, *i_png_read_statep;
267 read_warn_handler(png_structp, png_const_charp);
270 cleanup_read_state(i_png_read_statep);
273 i_readpng_wiol(io_glue *ig) {
277 png_uint_32 width, height;
278 int bit_depth, color_type, interlace_type;
280 unsigned int sig_read;
286 mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
289 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs,
290 error_handler, read_warn_handler);
292 i_push_error(0, "Cannot create PNG read structure");
295 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
297 info_ptr = png_create_info_struct(png_ptr);
298 if (info_ptr == NULL) {
299 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
300 i_push_error(0, "Cannot create PNG info structure");
304 if (setjmp(png_jmpbuf(png_ptr))) {
305 if (im) i_img_destroy(im);
306 mm_log((1,"i_readpng_wiol: error.\n"));
307 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
308 cleanup_read_state(&rs);
312 /* we do our own limit checks */
313 png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
315 png_set_sig_bytes(png_ptr, sig_read);
316 png_read_info(png_ptr, info_ptr);
317 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
319 mm_log((1, "png_get_IHDR results: width %u, height %u, bit_depth %d, color_type %d, interlace_type %d\n",
320 (unsigned)width, (unsigned)height, bit_depth,color_type,interlace_type));
322 CC2C[PNG_COLOR_TYPE_GRAY]=1;
323 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
324 CC2C[PNG_COLOR_TYPE_RGB]=3;
325 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
326 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
327 channels = CC2C[color_type];
329 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
331 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
332 mm_log((1, "i_readpnm: image size exceeds limits\n"));
333 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
337 if (color_type == PNG_COLOR_TYPE_PALETTE) {
338 im = read_paletted(png_ptr, info_ptr, channels, width, height);
340 else if (color_type == PNG_COLOR_TYPE_GRAY
342 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
343 im = read_bilevel(png_ptr, info_ptr, width, height);
345 else if (bit_depth == 16) {
346 im = read_direct16(png_ptr, info_ptr, channels, width, height);
349 im = read_direct8(png_ptr, info_ptr, channels, width, height);
353 get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
355 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
359 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
362 cleanup_read_state(&rs);
364 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
370 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
371 i_img_dim width, i_img_dim height) {
372 i_img * volatile vim = NULL;
373 int color_type = png_get_color_type(png_ptr, info_ptr);
374 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
376 int number_passes, pass;
379 unsigned char * volatile vline = NULL;
381 if (setjmp(png_jmpbuf(png_ptr))) {
382 if (vim) i_img_destroy(vim);
383 if (vline) myfree(vline);
388 number_passes = png_set_interlace_handling(png_ptr);
389 mm_log((1,"number of passes=%d\n",number_passes));
391 png_set_strip_16(png_ptr);
392 png_set_packing(png_ptr);
394 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
395 png_set_expand(png_ptr);
397 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
399 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
400 png_set_expand(png_ptr);
403 png_read_update_info(png_ptr, info_ptr);
405 im = vim = i_img_8_new(width,height,channels);
407 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
411 line = vline = mymalloc(channels * width);
412 for (pass = 0; pass < number_passes; pass++) {
413 for (y = 0; y < height; y++) {
415 i_gsamp(im, 0, width, y, line, NULL, channels);
416 png_read_row(png_ptr,(png_bytep)line, NULL);
417 i_psamp(im, 0, width, y, line, NULL, channels);
423 png_read_end(png_ptr, info_ptr);
429 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
430 i_img_dim width, i_img_dim height) {
431 i_img * volatile vim = NULL;
433 int number_passes, pass;
436 unsigned char * volatile vline = NULL;
438 unsigned * volatile vbits_line = NULL;
441 if (setjmp(png_jmpbuf(png_ptr))) {
442 if (vim) i_img_destroy(vim);
443 if (vline) myfree(vline);
444 if (vbits_line) myfree(vbits_line);
449 number_passes = png_set_interlace_handling(png_ptr);
450 mm_log((1,"number of passes=%d\n",number_passes));
452 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
454 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
455 png_set_expand(png_ptr);
458 png_read_update_info(png_ptr, info_ptr);
460 im = vim = i_img_16_new(width,height,channels);
462 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
466 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
467 line = vline = mymalloc(row_bytes);
468 memset(line, 0, row_bytes);
469 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
470 for (pass = 0; pass < number_passes; pass++) {
471 for (y = 0; y < height; y++) {
473 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
474 for (x = 0; x < width * channels; ++x) {
475 line[x*2] = bits_line[x] >> 8;
476 line[x*2+1] = bits_line[x] & 0xff;
479 png_read_row(png_ptr,(png_bytep)line, NULL);
480 for (x = 0; x < width * channels; ++x)
481 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
482 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
490 png_read_end(png_ptr, info_ptr);
496 read_bilevel(png_structp png_ptr, png_infop info_ptr,
497 i_img_dim width, i_img_dim height) {
498 i_img * volatile vim = NULL;
500 int number_passes, pass;
503 unsigned char * volatile vline = NULL;
506 if (setjmp(png_jmpbuf(png_ptr))) {
507 if (vim) i_img_destroy(vim);
508 if (vline) myfree(vline);
513 number_passes = png_set_interlace_handling(png_ptr);
514 mm_log((1,"number of passes=%d\n",number_passes));
516 png_set_packing(png_ptr);
518 png_set_expand(png_ptr);
520 png_read_update_info(png_ptr, info_ptr);
522 im = vim = i_img_pal_new(width, height, 1, 256);
524 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
528 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
529 palette[0].channel[3] = 0;
530 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
531 palette[1].channel[3] = 255;
532 i_addcolors(im, palette, 2);
534 line = vline = mymalloc(width);
535 memset(line, 0, width);
536 for (pass = 0; pass < number_passes; pass++) {
537 for (y = 0; y < height; y++) {
539 i_gpal(im, 0, width, y, line);
540 /* expand indexes back to 0/255 */
541 for (x = 0; x < width; ++x)
542 line[x] = line[x] ? 255 : 0;
544 png_read_row(png_ptr,(png_bytep)line, NULL);
546 /* back to palette indexes */
547 for (x = 0; x < width; ++x)
548 line[x] = line[x] ? 1 : 0;
549 i_ppal(im, 0, width, y, line);
555 png_read_end(png_ptr, info_ptr);
560 /* FIXME: do we need to unscale palette color values from the
563 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
564 i_img_dim width, i_img_dim height) {
565 i_img * volatile vim = NULL;
566 int color_type = png_get_color_type(png_ptr, info_ptr);
567 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
569 int number_passes, pass;
572 unsigned char * volatile vline = NULL;
574 png_colorp png_palette;
575 png_bytep png_pal_trans;
576 png_color_16p png_color_trans;
579 if (setjmp(png_jmpbuf(png_ptr))) {
580 if (vim) i_img_destroy(vim);
581 if (vline) myfree(vline);
586 number_passes = png_set_interlace_handling(png_ptr);
587 mm_log((1,"number of passes=%d\n",number_passes));
589 png_set_strip_16(png_ptr);
590 png_set_packing(png_ptr);
592 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
593 png_set_expand(png_ptr);
595 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
596 i_push_error(0, "Paletted image with no PLTE chunk");
600 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
608 png_read_update_info(png_ptr, info_ptr);
610 im = vim = i_img_pal_new(width, height, channels, 256);
612 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
616 for (i = 0; i < num_palette; ++i) {
619 c.rgba.r = png_palette[i].red;
620 c.rgba.g = png_palette[i].green;
621 c.rgba.b = png_palette[i].blue;
622 if (i < num_pal_trans)
623 c.rgba.a = png_pal_trans[i];
626 i_addcolors(im, &c, 1);
629 line = vline = mymalloc(width);
630 for (pass = 0; pass < number_passes; pass++) {
631 for (y = 0; y < height; y++) {
633 i_gpal(im, 0, width, y, line);
634 png_read_row(png_ptr,(png_bytep)line, NULL);
635 i_ppal(im, 0, width, y, line);
641 png_read_end(png_ptr, info_ptr);
646 struct png_text_name {
651 static const struct png_text_name
653 { "Author", "png_author" },
654 { "Comment", "i_comment" },
655 { "Copyright", "png_copyright" },
656 { "Creation Time", "png_creation_time" },
657 { "Description", "png_description" },
658 { "Disclaimer", "png_disclaimer" },
659 { "Software", "png_software" },
660 { "Source", "png_source" },
661 { "Title", "png_title" },
662 { "Warning", "png_warning" }
665 static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
667 static const char * const
669 "png_chroma_white_x",
670 "png_chroma_white_y",
673 "png_chroma_green_x",
674 "png_chroma_green_y",
679 static const int chroma_tag_count = sizeof(chroma_tags) / sizeof(*chroma_tags);
682 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr,
683 int bit_depth, int color_type) {
684 png_uint_32 xres, yres;
687 i_tags_set(&im->tags, "i_format", "png", -1);
688 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
689 mm_log((1,"pHYs (%u, %u) %d\n", (unsigned)xres, (unsigned)yres, unit_type));
690 if (unit_type == PNG_RESOLUTION_METER) {
691 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
692 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
695 i_tags_setn(&im->tags, "i_xres", xres);
696 i_tags_setn(&im->tags, "i_yres", yres);
697 i_tags_setn(&im->tags, "i_aspect_only", 1);
701 int interlace = png_get_interlace_type(png_ptr, info_ptr);
703 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
705 case PNG_INTERLACE_NONE:
706 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
709 case PNG_INTERLACE_ADAM7:
710 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
714 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
719 /* the various readers can call png_set_expand(), libpng will make
720 it's internal record of bit_depth at least 8 in that case */
721 i_tags_setn(&im->tags, "png_bits", bit_depth);
723 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
725 if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
726 i_tags_setn(&im->tags, "png_srgb_intent", intent);
730 /* Ignore these if there's an sRGB chunk, libpng simulates
731 their existence if there's an sRGB chunk, and the PNG spec says
732 that these are ignored if the sRGB is present, so ignore them.
737 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
738 i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
741 if (png_get_cHRM(png_ptr, info_ptr, chroma+0, chroma+1,
742 chroma+2, chroma+3, chroma+4, chroma+5,
743 chroma+6, chroma+7)) {
746 for (i = 0; i < chroma_tag_count; ++i)
747 i_tags_set_float2(&im->tags, chroma_tags[i], 0, chroma[i], 4);
755 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
757 int custom_index = 0;
758 for (i = 0; i < num_text; ++i) {
761 int compressed = text[i].compression == PNG_ITXT_COMPRESSION_zTXt
762 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt;
764 for (j = 0; j < text_tags_count; ++j) {
765 if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
767 i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
769 sprintf(tag_name, "%s_compressed", text_tags[j].tagname);
770 i_tags_setn(&im->tags, tag_name, 1);
779 sprintf(tag_name, "png_text%d_key", custom_index);
780 i_tags_set(&im->tags, tag_name, text[i].key, -1);
781 sprintf(tag_name, "png_text%d_text", custom_index);
782 i_tags_set(&im->tags, tag_name, text[i].text, -1);
783 sprintf(tag_name, "png_text%d_type", custom_index);
784 i_tags_set(&im->tags, tag_name,
785 (text[i].compression == PNG_TEXT_COMPRESSION_NONE
786 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
787 "text" : "itxt", -1);
789 sprintf(tag_name, "png_text%d_compressed", custom_index);
790 i_tags_setn(&im->tags, tag_name, 1);
801 if (png_get_tIME(png_ptr, info_ptr, &mod_time)) {
802 char time_formatted[80];
804 sprintf(time_formatted, "%d-%02d-%02dT%02d:%02d:%02d",
805 mod_time->year, mod_time->month, mod_time->day,
806 mod_time->hour, mod_time->minute, mod_time->second);
807 i_tags_set(&im->tags, "png_time", time_formatted, -1);
815 if (png_get_bKGD(png_ptr, info_ptr, &back)) {
816 switch (color_type) {
817 case PNG_COLOR_TYPE_GRAY:
818 case PNG_COLOR_TYPE_GRAY_ALPHA:
820 /* lib png stores the raw gray value rather than scaling it
821 to 16-bit (or 8), we use 8-bit color for i_background */
826 gray = back->gray >> 8;
832 gray = 0x11 * back->gray;
835 gray = 0x55 * back->gray;
838 gray = back->gray ? 0xFF : 0;
843 c.rgb.r = c.rgb.g = c.rgb.b = gray;
847 case PNG_COLOR_TYPE_RGB:
848 case PNG_COLOR_TYPE_RGB_ALPHA:
850 c.rgb.r = bit_depth == 16 ? (back->red >> 8) : back->red;
851 c.rgb.g = bit_depth == 16 ? (back->green >> 8) : back->green;
852 c.rgb.b = bit_depth == 16 ? (back->blue >> 8) : back->blue;
856 case PNG_COLOR_TYPE_PALETTE:
858 c.rgb.g = back->green;
859 c.rgb.b = back->blue;
864 i_tags_set_color(&im->tags, "i_background", 0, &c);
869 #define GET_STR_BUF_SIZE 40
872 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
874 int aspect_only, have_res = 1;
876 if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
877 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
878 ; /* nothing to do */
883 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
890 i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
893 png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
894 aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
899 if (i_tags_get_int(&im->tags, "png_srgb_intent", 0, &intent)) {
900 if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) {
901 i_push_error(0, "tag png_srgb_intent out of range");
904 png_set_sRGB(png_ptr, info_ptr, intent);
907 double chroma[8], gamma;
909 int found_chroma_count = 0;
911 for (i = 0; i < chroma_tag_count; ++i) {
912 if (i_tags_get_float(&im->tags, chroma_tags[i], 0, chroma+i))
913 ++found_chroma_count;
916 if (found_chroma_count) {
917 if (found_chroma_count != chroma_tag_count) {
918 i_push_error(0, "all png_chroma_* tags must be supplied or none");
922 png_set_cHRM(png_ptr, info_ptr, chroma[0], chroma[1], chroma[2],
923 chroma[3], chroma[4], chroma[5], chroma[6], chroma[7]);
926 if (i_tags_get_float(&im->tags, "png_gamma", 0, &gamma)) {
927 png_set_gAMA(png_ptr, info_ptr, gamma);
933 /* png_set_text() is sparsely documented, it isn't indicated whether
934 multiple calls add to or replace the lists of texts, and
935 whether the text/keyword data is copied or not.
937 Examining the linpng code reveals that png_set_text() adds to
938 the list and that the text is copied.
942 /* do our standard tags */
943 for (i = 0; i < text_tags_count; ++i) {
944 char buf[GET_STR_BUF_SIZE];
948 data = get_string2(&im->tags, text_tags[i].tagname, buf, &size);
951 int compression = size > 1000;
952 char compress_tag[40];
954 if (memchr(data, '\0', size)) {
955 i_push_errorf(0, "tag %s may not contain NUL characters", text_tags[i].tagname);
959 sprintf(compress_tag, "%s_compressed", text_tags[i].tagname);
960 i_tags_get_int(&im->tags, compress_tag, 0, &compression);
962 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
963 : PNG_TEXT_COMPRESSION_NONE;
964 text.key = (char *)text_tags[i].keyword;
965 text.text_length = size;
966 text.text = (char *)data;
967 #ifdef PNG_iTXt_SUPPORTED
968 text.itxt_length = 0;
970 text.lang_key = NULL;
973 png_set_text(png_ptr, info_ptr, &text, 1);
977 /* for non-standard tags ensure keywords are limited to 1 to 79
982 char key_buf[GET_STR_BUF_SIZE], value_buf[GET_STR_BUF_SIZE];
983 const char *key, *value;
984 size_t key_size, value_size;
986 sprintf(tag_name, "png_text%d_key", i);
987 key = get_string2(&im->tags, tag_name, key_buf, &key_size);
991 if (key_size < 1 || key_size > 79) {
992 i_push_errorf(0, "tag %s must be between 1 and 79 characters in length", tag_name);
996 if (key[0] == ' ' || key[key_size-1] == ' ') {
997 i_push_errorf(0, "tag %s may not contain leading or trailing spaces", tag_name);
1001 if (strstr(key, " ")) {
1002 i_push_errorf(0, "tag %s may not contain consecutive spaces", tag_name);
1006 for (k = 0; k < key_size; ++k) {
1007 if (key[k] < 32 || (key[k] > 126 && key[k] < 161)) {
1008 i_push_errorf(0, "tag %s may only contain Latin1 characters 32-126, 161-255", tag_name);
1014 sprintf(tag_name, "png_text%d_text", i);
1015 value = get_string2(&im->tags, tag_name, value_buf, &value_size);
1018 if (memchr(value, '\0', value_size)) {
1019 i_push_errorf(0, "tag %s may not contain NUL characters", tag_name);
1026 int compression = value_size > 1000;
1028 sprintf(tag_name, "png_text%d_compressed", i);
1029 i_tags_get_int(&im->tags, tag_name, 0, &compression);
1031 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1032 : PNG_TEXT_COMPRESSION_NONE;
1033 text.key = (char *)key;
1034 text.text_length = value_size;
1035 text.text = (char *)value;
1036 #ifdef PNG_iTXt_SUPPORTED
1037 text.itxt_length = 0;
1039 text.lang_key = NULL;
1042 png_set_text(png_ptr, info_ptr, &text, 1);
1045 i_push_errorf(0, "tag png_text%d_key found but not png_text%d_text", i, i);
1049 i_push_errorf(0, "tag png_text%d_text found but not png_text%d_key", i, i);
1060 char buf[GET_STR_BUF_SIZE];
1062 const char *timestr = get_string2(&im->tags, "png_time", buf, &time_size);
1065 int year, month, day, hour, minute, second;
1068 if (sscanf(timestr, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6) {
1069 /* rough validation */
1070 if (month < 1 || month > 12
1071 || day < 1 || day > 31
1072 || hour < 0 || hour > 23
1073 || minute < 0 || minute > 59
1074 || second < 0 || second > 60) {
1075 i_push_error(0, "invalid date/time for png_time");
1078 mod_time.year = year;
1079 mod_time.month = month;
1081 mod_time.hour = hour;
1082 mod_time.minute = minute;
1083 mod_time.second = second;
1085 png_set_tIME(png_ptr, info_ptr, &mod_time);
1088 i_push_error(0, "png_time must be formatted 'y-m-dTh:m:s'");
1095 /* no bKGD support yet, maybe later
1096 it may be simpler to do it in the individual writers
1104 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size) {
1107 if (i_tags_find(tags, name, 0, &index)) {
1108 const i_img_tag *entry = tags->tags + index;
1111 *size = entry->size;
1116 *size = sprintf(buf, "%d", entry->idata);
1125 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1126 unsigned char *data, *volatile vdata = NULL;
1129 if (setjmp(png_jmpbuf(png_ptr))) {
1136 png_write_info(png_ptr, info_ptr);
1138 vdata = data = mymalloc(im->xsize * im->channels);
1139 for (y = 0; y < im->ysize; y++) {
1140 i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
1141 png_write_row(png_ptr, (png_bytep)data);
1149 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1150 unsigned *data, *volatile vdata = NULL;
1151 unsigned char *tran_data, * volatile vtran_data = NULL;
1152 i_img_dim samples_per_row = im->xsize * im->channels;
1156 if (setjmp(png_jmpbuf(png_ptr))) {
1165 png_write_info(png_ptr, info_ptr);
1167 vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
1168 vtran_data = tran_data = mymalloc(samples_per_row * 2);
1169 for (y = 0; y < im->ysize; y++) {
1171 unsigned char *p = tran_data;
1172 i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
1173 for (i = 0; i < samples_per_row; ++i) {
1174 p[0] = data[i] >> 8;
1175 p[1] = data[i] & 0xff;
1178 png_write_row(png_ptr, (png_bytep)tran_data);
1187 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
1188 unsigned char *data, *volatile vdata = NULL;
1190 unsigned char pal_map[256];
1191 png_color pcolors[256];
1192 i_color colors[256];
1193 int count = i_colorcount(im);
1196 if (setjmp(png_jmpbuf(png_ptr))) {
1203 i_getcolors(im, 0, colors, count);
1204 if (im->channels < 3) {
1205 /* convert the greyscale palette to color */
1207 for (i = 0; i < count; ++i) {
1208 i_color *c = colors + i;
1209 c->channel[3] = c->channel[1];
1210 c->channel[2] = c->channel[1] = c->channel[0];
1214 if (i_img_has_alpha(im)) {
1216 int bottom_index = 0, top_index = count-1;
1218 /* fill out the palette map */
1219 for (i = 0; i < count; ++i)
1222 /* the PNG spec suggests sorting the palette by alpha, but that's
1223 unnecessary - all we want to do is move the opaque entries to
1225 while (bottom_index < top_index) {
1226 if (colors[bottom_index].rgba.a == 255) {
1227 pal_map[bottom_index] = top_index;
1228 pal_map[top_index--] = bottom_index;
1234 for (i = 0; i < count; ++i) {
1235 int srci = i_img_has_alpha(im) ? pal_map[i] : i;
1237 pcolors[i].red = colors[srci].rgb.r;
1238 pcolors[i].green = colors[srci].rgb.g;
1239 pcolors[i].blue = colors[srci].rgb.b;
1242 png_set_PLTE(png_ptr, info_ptr, pcolors, count);
1244 if (i_img_has_alpha(im)) {
1245 unsigned char trans[256];
1248 for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
1249 trans[i] = colors[pal_map[i]].rgba.a;
1251 png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
1254 png_write_info(png_ptr, info_ptr);
1256 png_set_packing(png_ptr);
1258 vdata = data = mymalloc(im->xsize);
1259 for (y = 0; y < im->ysize; y++) {
1260 i_gpal(im, 0, im->xsize, y, data);
1261 if (i_img_has_alpha(im)) {
1263 for (x = 0; x < im->xsize; ++x)
1264 data[x] = pal_map[data[x]];
1266 png_write_row(png_ptr, (png_bytep)data);
1274 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1275 unsigned char *data, *volatile vdata = NULL;
1278 if (setjmp(png_jmpbuf(png_ptr))) {
1285 png_write_info(png_ptr, info_ptr);
1287 png_set_packing(png_ptr);
1289 vdata = data = mymalloc(im->xsize);
1290 for (y = 0; y < im->ysize; y++) {
1291 i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
1292 png_write_row(png_ptr, (png_bytep)data);
1300 read_warn_handler(png_structp png_ptr, png_const_charp msg) {
1301 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
1305 mm_log((1, "PNG read warning '%s'\n", msg));
1307 /* in case this is part of an error report */
1308 i_push_error(0, msg);
1310 /* and save in the warnings so if we do manage to succeed, we
1311 * can save it as a tag
1313 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
1315 + strlen(msg) /* new text */
1317 workp = myrealloc(rs->warnings, new_size);
1321 strcat(workp, "\n");
1322 rs->warnings = workp;
1326 cleanup_read_state(i_png_read_statep rs) {
1328 myfree(rs->warnings);