]> git.imager.perl.org - imager.git/blob - PNG/impng.c
Devel::CheckLib: handle the gcc-4 symlink on Cygwin
[imager.git] / PNG / impng.c
1 #include "impng.h"
2 #include "png.h"
3 #include <stdlib.h>
4 #include <string.h>
5
6 /* this is a way to get number of channels from color space 
7  * Color code to channel number */
8
9 static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
10
11 #define PNG_BYTES_TO_CHECK 4
12
13 static i_img *
14 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
15
16 static i_img *
17 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
18
19 static i_img *
20 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
21
22 static i_img *
23 read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
24
25 static int
26 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
27
28 static int
29 write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
30
31 static int
32 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
33
34 static int
35 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im);
36
37 static void 
38 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth, int color_type);
39
40 static int
41 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
42
43 static const char *
44 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size);
45
46 unsigned
47 i_png_lib_version(void) {
48   return png_access_version_number();
49 }
50
51 static char const * const
52 features[] =
53   {
54 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
55     "benign-errors",
56 #endif
57 #ifdef PNG_READ_SUPPORTED
58     "read",
59 #endif
60 #ifdef PNG_WRITE_SUPPORTED
61     "write",
62 #endif
63 #ifdef PNG_MNG_FEATURES_SUPPORTED
64     "mng-features",
65 #endif
66 #ifdef PNG_CHECK_cHRM_SUPPORTED
67     "check-cHRM",
68 #endif
69 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
70     "user-limits",
71 #endif
72     NULL
73   };
74
75 const char * const *
76 i_png_features(void) {
77   return features;
78 }
79
80 static 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.");
85 }
86
87 static void
88 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
89   ssize_t rc;
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.");
93 }
94
95 static void
96 wiol_flush_data(png_structp png_ptr) {
97   io_glue *ig = png_get_io_ptr(png_ptr);
98   if (!i_io_flush(ig))
99     png_error(png_ptr, "Error flushing output");
100 }
101
102 static void
103 error_handler(png_structp png_ptr, png_const_charp msg) {
104   mm_log((1, "PNG error: '%s'\n", msg));
105
106   i_push_error(0, msg);
107   longjmp(png_jmpbuf(png_ptr), 1);
108 }
109
110 /*
111
112   For writing a warning might have information about an error, so send
113   it to the error stack.
114
115 */
116 static void
117 write_warn_handler(png_structp png_ptr, png_const_charp msg) {
118   mm_log((1, "PNG write warning '%s'\n", msg));
119
120   i_push_error(0, msg);
121 }
122
123 #define PNG_DIM_MAX 0x7fffffffL
124
125 undef_int
126 i_writepng_wiol(i_img *im, io_glue *ig) {
127   png_structp png_ptr;
128   png_infop info_ptr = NULL;
129   i_img_dim width,height;
130   volatile int cspace,channels;
131   int bits;
132   int is_bilevel = 0, zero_is_white;
133
134   mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
135
136   i_clear_error();
137
138   if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
139     i_push_error(0, "image too large for PNG");
140     return 0;
141   }
142
143   height = im->ysize;
144   width  = im->xsize;
145
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.
152    *
153    * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
154    * fixed in libpng 1.5.3
155    */
156   if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
157     i_push_error(0, "Image too large for PNG");
158     return 0;
159   }
160
161   channels=im->channels;
162
163   if (i_img_is_monochrome(im, &zero_is_white)) {
164     is_bilevel = 1;
165     bits = 1;
166     cspace = PNG_COLOR_TYPE_GRAY;
167     mm_log((1, "i_writepng: bilevel output\n"));
168   }
169   else if (im->type == i_palette_type) {
170     int colors = i_colorcount(im);
171
172     cspace = PNG_COLOR_TYPE_PALETTE;
173     bits = 1;
174     while ((1 << bits) < colors) {
175       bits += bits;
176     }
177     mm_log((1, "i_writepng: paletted output\n"));
178   }
179   else {
180     switch (channels) {
181     case 1:
182       cspace = PNG_COLOR_TYPE_GRAY;
183       break;
184     case 2:
185       cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
186       break;
187     case 3:
188       cspace = PNG_COLOR_TYPE_RGB;
189       break;
190     case 4:
191       cspace = PNG_COLOR_TYPE_RGB_ALPHA;
192       break;
193     default:
194       fprintf(stderr, "Internal error, channels = %d\n", channels);
195       abort();
196     }
197     bits = im->bits > 8 ? 16 : 8;
198     mm_log((1, "i_writepng: direct output\n"));
199   }
200
201   mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
202
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.
208    */
209   
210   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
211                                     error_handler, write_warn_handler);
212   
213   if (png_ptr == NULL) return 0;
214
215   
216   /* Allocate/initialize the image information data.  REQUIRED */
217   info_ptr = png_create_info_struct(png_ptr);
218
219   if (info_ptr == NULL) {
220     png_destroy_write_struct(&png_ptr, &info_ptr);
221     return 0;
222   }
223   
224   /* Set error handling.  REQUIRED if you aren't supplying your own
225    * error hadnling functions in the png_create_write_struct() call.
226    */
227   if (setjmp(png_jmpbuf(png_ptr))) {
228     png_destroy_write_struct(&png_ptr, &info_ptr);
229     return(0);
230   }
231   
232   png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
233
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
241    */
242
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.
246    */
247   png_set_user_limits(png_ptr, width, height);
248
249   png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
250                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
251
252   if (!set_png_tags(im, png_ptr, info_ptr)) {
253     png_destroy_write_struct(&png_ptr, &info_ptr);
254     return 0;
255   }
256
257   if (is_bilevel) {
258     if (!write_bilevel(png_ptr, info_ptr, im)) {
259       png_destroy_write_struct(&png_ptr, &info_ptr);
260       return 0;
261     }
262   }
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);
266       return 0;
267     }
268   }
269   else if (bits == 16) {
270     if (!write_direct16(png_ptr, info_ptr, im)) {
271       png_destroy_write_struct(&png_ptr, &info_ptr);
272       return 0;
273     }
274   }
275   else {
276     if (!write_direct8(png_ptr, info_ptr, im)) {
277       png_destroy_write_struct(&png_ptr, &info_ptr);
278       return 0;
279     }
280   }
281
282   png_write_end(png_ptr, info_ptr);
283
284   png_destroy_write_struct(&png_ptr, &info_ptr);
285
286   if (i_io_close(ig))
287     return 0;
288
289   return(1);
290 }
291
292 typedef struct {
293   char *warnings;
294 } i_png_read_state, *i_png_read_statep;
295
296 static void
297 read_warn_handler(png_structp, png_const_charp);
298
299 static void
300 cleanup_read_state(i_png_read_statep);
301
302 i_img*
303 i_readpng_wiol(io_glue *ig, int flags) {
304   i_img *im = NULL;
305   png_structp png_ptr;
306   png_infop info_ptr;
307   png_uint_32 width, height;
308   int bit_depth, color_type, interlace_type;
309   int channels;
310   unsigned int sig_read;
311   i_png_read_state rs;
312
313   rs.warnings = NULL;
314   sig_read  = 0;
315
316   mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
317   i_clear_error();
318
319   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs, 
320                                    error_handler, read_warn_handler);
321   if (!png_ptr) {
322     i_push_error(0, "Cannot create PNG read structure");
323     return NULL;
324   }
325   png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
326
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);
333     return NULL;
334   }
335 #else
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);
339     return NULL;
340   }
341 #endif
342
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");
347     return NULL;
348   }
349   
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);
355     return NULL;
356   }
357
358   /* we do our own limit checks */
359   png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
360
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);
364   
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));
367   
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];
374
375   mm_log((1,"i_readpng_wiol: channels %d\n",channels));
376
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);
380     return NULL;
381   }
382
383   if (color_type == PNG_COLOR_TYPE_PALETTE) {
384     im = read_paletted(png_ptr, info_ptr, channels, width, height);
385   }
386   else if (color_type == PNG_COLOR_TYPE_GRAY
387            && bit_depth == 1
388            && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
389     im = read_bilevel(png_ptr, info_ptr, width, height);
390   }
391   else if (bit_depth == 16) {
392     im = read_direct16(png_ptr, info_ptr, channels, width, height);
393   }
394   else {
395     im = read_direct8(png_ptr, info_ptr, channels, width, height);
396   }
397
398   if (im)
399     get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
400
401   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
402
403   if (im) {
404     if (rs.warnings) {
405       i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
406     }
407   }
408   cleanup_read_state(&rs);
409   
410   mm_log((1,"(%p) <- i_readpng_wiol\n", im));  
411   
412   return im;
413 }
414
415 static i_img *
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);
421   i_img_dim y;
422   int number_passes, pass;
423   i_img *im;
424   unsigned char *line;
425   unsigned char * volatile vline = NULL;
426
427   if (setjmp(png_jmpbuf(png_ptr))) {
428     if (vim) i_img_destroy(vim);
429     if (vline) myfree(vline);
430
431     return NULL;
432   }
433
434   number_passes = png_set_interlace_handling(png_ptr);
435   mm_log((1,"number of passes=%d\n",number_passes));
436
437   png_set_strip_16(png_ptr);
438   png_set_packing(png_ptr);
439
440   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
441     png_set_expand(png_ptr);
442     
443   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
444     channels++;
445     mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
446     png_set_expand(png_ptr);
447   }
448   
449   png_read_update_info(png_ptr, info_ptr);
450   
451   im = vim = i_img_8_new(width,height,channels);
452   if (!im) {
453     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
454     return NULL;
455   }
456   
457   line = vline = mymalloc(channels * width);
458   for (pass = 0; pass < number_passes; pass++) {
459     for (y = 0; y < height; y++) {
460       if (pass > 0)
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);
464     }
465   }
466   myfree(line);
467   vline = NULL;
468   
469   png_read_end(png_ptr, info_ptr); 
470
471   return im;
472 }
473
474 static i_img *
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;
478   i_img_dim x, y;
479   int number_passes, pass;
480   i_img *im;
481   unsigned char *line;
482   unsigned char * volatile vline = NULL;
483   unsigned *bits_line;
484   unsigned * volatile vbits_line = NULL;
485   size_t row_bytes;
486
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);
491
492     return NULL;
493   }
494
495   number_passes = png_set_interlace_handling(png_ptr);
496   mm_log((1,"number of passes=%d\n",number_passes));
497
498   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
499     channels++;
500     mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
501     png_set_expand(png_ptr);
502   }
503   
504   png_read_update_info(png_ptr, info_ptr);
505   
506   im = vim = i_img_16_new(width,height,channels);
507   if (!im) {
508     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
509     return NULL;
510   }
511   
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++) {
518       if (pass > 0) {
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;
523         }
524       }
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);
529     }
530   }
531   myfree(line);
532   myfree(bits_line);
533   vline = NULL;
534   vbits_line = NULL;
535   
536   png_read_end(png_ptr, info_ptr); 
537
538   return im;
539 }
540
541 static i_img *
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;
545   i_img_dim x, y;
546   int number_passes, pass;
547   i_img *im;
548   unsigned char *line;
549   unsigned char * volatile vline = NULL;
550   i_color palette[2];
551
552   if (setjmp(png_jmpbuf(png_ptr))) {
553     if (vim) i_img_destroy(vim);
554     if (vline) myfree(vline);
555
556     return NULL;
557   }
558
559   number_passes = png_set_interlace_handling(png_ptr);
560   mm_log((1,"number of passes=%d\n",number_passes));
561
562   png_set_packing(png_ptr);
563
564   png_set_expand(png_ptr);  
565   
566   png_read_update_info(png_ptr, info_ptr);
567   
568   im = vim = i_img_pal_new(width, height, 1, 256);
569   if (!im) {
570     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
571     return NULL;
572   }
573
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);
579   
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++) {
584       if (pass > 0) {
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;
589       }
590       png_read_row(png_ptr,(png_bytep)line, NULL);
591
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);
596     }
597   }
598   myfree(line);
599   vline = NULL;
600   
601   png_read_end(png_ptr, info_ptr); 
602
603   return im;
604 }
605
606 /* FIXME: do we need to unscale palette color values from the 
607    supplied alphas? */
608 static i_img *
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);
614   i_img_dim y;
615   int number_passes, pass;
616   i_img *im;
617   unsigned char *line;
618   unsigned char * volatile vline = NULL;
619   int num_palette, i;
620   png_colorp png_palette;
621   png_bytep png_pal_trans;
622   png_color_16p png_color_trans;
623   int num_pal_trans;
624
625   if (setjmp(png_jmpbuf(png_ptr))) {
626     if (vim) i_img_destroy(vim);
627     if (vline) myfree(vline);
628
629     return NULL;
630   }
631
632   number_passes = png_set_interlace_handling(png_ptr);
633   mm_log((1,"number of passes=%d\n",number_passes));
634
635   png_set_strip_16(png_ptr);
636   png_set_packing(png_ptr);
637
638   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
639     png_set_expand(png_ptr);
640     
641   if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
642     i_push_error(0, "Paletted image with no PLTE chunk");
643     return NULL;
644   }
645
646   if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
647                    &png_color_trans)) {
648     channels++;
649   }
650   else {
651     num_pal_trans = 0;
652   }
653   
654   png_read_update_info(png_ptr, info_ptr);
655   
656   im = vim = i_img_pal_new(width, height, channels, 256);
657   if (!im) {
658     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
659     return NULL;
660   }
661
662   for (i = 0; i < num_palette; ++i) {
663     i_color c;
664
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];
670     else
671       c.rgba.a = 255;
672     i_addcolors(im, &c, 1);
673   }
674
675   line = vline = mymalloc(width);
676   for (pass = 0; pass < number_passes; pass++) {
677     for (y = 0; y < height; y++) {
678       if (pass > 0)
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);
682     }
683   }
684   myfree(line);
685   vline = NULL;
686   
687   png_read_end(png_ptr, info_ptr); 
688
689   return im;
690 }
691
692 struct png_text_name {
693   const char *keyword;
694   const char *tagname;
695 };
696
697 static const struct png_text_name
698 text_tags[] = {
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" }
709 };
710
711 static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
712
713 static const char * const
714 chroma_tags[] = {
715   "png_chroma_white_x",
716   "png_chroma_white_y",
717   "png_chroma_red_x",
718   "png_chroma_red_y",
719   "png_chroma_green_x",
720   "png_chroma_green_y",
721   "png_chroma_blue_x",
722   "png_chroma_blue_y"
723 };
724
725 static const int chroma_tag_count = sizeof(chroma_tags) / sizeof(*chroma_tags);
726
727 static void
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;
731   int unit_type;
732
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);
739     }
740     else {
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);
744     }
745   }
746   {
747     int interlace = png_get_interlace_type(png_ptr, info_ptr);
748
749     i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
750     switch (interlace) {
751     case PNG_INTERLACE_NONE:
752       i_tags_set(&im->tags, "png_interlace_name", "none", -1);
753       break;
754       
755     case PNG_INTERLACE_ADAM7:
756       i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
757       break;
758       
759     default:
760       i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
761       break;
762     }
763   }
764
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);
768   
769   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
770     int intent;
771     if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
772       i_tags_setn(&im->tags, "png_srgb_intent", intent);
773     }
774   }
775   else {
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.
779     */
780     double gamma;
781     double chroma[8];
782
783     if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
784       i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
785     }
786
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)) {
790       int i;
791
792       for (i = 0; i < chroma_tag_count; ++i)
793         i_tags_set_float2(&im->tags, chroma_tags[i], 0, chroma[i], 4);
794     }
795   }
796
797   {
798     int num_text;
799     png_text *text;
800
801     if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
802       int i;
803       int custom_index = 0;
804       for (i = 0; i < num_text; ++i) {
805         int j;
806         int found = 0;
807         int compressed = text[i].compression == PNG_ITXT_COMPRESSION_zTXt
808           || text[i].compression == PNG_TEXT_COMPRESSION_zTXt;
809
810         for (j = 0; j < text_tags_count; ++j) {
811           if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
812             char tag_name[50];
813             i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
814             if (compressed) {
815               sprintf(tag_name, "%s_compressed", text_tags[j].tagname);
816               i_tags_setn(&im->tags, tag_name, 1);
817             }
818             found = 1;
819             break;
820           }
821         }
822
823         if (!found) {
824           char tag_name[50];
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);
834           if (compressed) {
835             sprintf(tag_name, "png_text%d_compressed", custom_index);
836             i_tags_setn(&im->tags, tag_name, 1);
837           }
838           ++custom_index;
839         }
840       }
841     }
842   }
843
844   {
845     png_time *mod_time;
846
847     if (png_get_tIME(png_ptr, info_ptr, &mod_time)) {
848       char time_formatted[80];
849
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);
854     }
855   }
856
857   {
858     png_color_16 *back;
859     i_color c;
860
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:
865         {
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 */
868
869           int gray;
870           switch (bit_depth) {
871           case 16:
872             gray = back->gray >> 8;
873             break;
874           case 8:
875             gray = back->gray;
876             break;
877           case 4:
878             gray = 0x11 * back->gray;
879             break;
880           case 2:
881             gray = 0x55 * back->gray;
882             break;
883           case 1:
884             gray = back->gray ? 0xFF : 0;
885             break;
886           default:
887             gray = 0;
888           }
889           c.rgb.r = c.rgb.g = c.rgb.b = gray;
890           break;
891         }
892
893       case PNG_COLOR_TYPE_RGB:
894       case PNG_COLOR_TYPE_RGB_ALPHA:
895         {
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;
899           break;
900         }
901
902       case PNG_COLOR_TYPE_PALETTE:
903         c.rgb.r = back->red;
904         c.rgb.g = back->green;
905         c.rgb.b = back->blue;
906         break;
907       }
908
909       c.rgba.a = 255;
910       i_tags_set_color(&im->tags, "i_background", 0, &c);
911     }
912   }
913 }
914
915 #define GET_STR_BUF_SIZE 40
916
917 static int
918 set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
919   double xres, yres;
920   int aspect_only, have_res = 1;
921
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 */
925     else
926       yres = xres;
927   }
928   else {
929     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
930       xres = yres;
931     else
932       have_res = 0;
933   }
934   if (have_res) {
935     aspect_only = 0;
936     i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
937     xres /= 0.0254;
938     yres /= 0.0254;
939     png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, 
940                  aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
941   }
942
943   {
944     int intent;
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");
948         return 0;
949       }
950       png_set_sRGB(png_ptr, info_ptr, intent);
951     }
952     else {
953       double chroma[8], gamma;
954       int i;
955       int found_chroma_count = 0;
956
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;
960       }
961
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");
965           return 0;
966         }
967
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]);
970       }
971
972       if (i_tags_get_float(&im->tags, "png_gamma", 0, &gamma)) {
973         png_set_gAMA(png_ptr, info_ptr, gamma);
974       }
975     }
976   }
977
978   {
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.
982
983        Examining the linpng code reveals that png_set_text() adds to
984        the list and that the text is copied.
985     */
986     int i;
987
988     /* do our standard tags */
989     for (i = 0; i < text_tags_count; ++i) {
990       char buf[GET_STR_BUF_SIZE];
991       size_t size;
992       const char *data;
993       
994       data = get_string2(&im->tags, text_tags[i].tagname, buf, &size);
995       if (data) {
996         png_text text;
997         int compression = size > 1000;
998         char compress_tag[40];
999
1000         if (memchr(data, '\0',  size)) {
1001           i_push_errorf(0, "tag %s may not contain NUL characters", text_tags[i].tagname);
1002           return 0;
1003         }
1004       
1005         sprintf(compress_tag, "%s_compressed", text_tags[i].tagname);
1006         i_tags_get_int(&im->tags, compress_tag, 0, &compression);
1007         
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;
1015         text.lang = NULL;
1016         text.lang_key = NULL;
1017 #endif
1018
1019         png_set_text(png_ptr, info_ptr, &text, 1);
1020       }
1021     }
1022
1023     /* for non-standard tags ensure keywords are limited to 1 to 79
1024        characters */
1025     i = 0;
1026     while (1) {
1027       char tag_name[50];
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;
1031
1032       sprintf(tag_name, "png_text%d_key", i);
1033       key = get_string2(&im->tags, tag_name, key_buf, &key_size);
1034       
1035       if (key) {
1036         size_t k;
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);
1039           return 0;
1040         }
1041
1042         if (key[0] == ' ' || key[key_size-1] == ' ') {
1043           i_push_errorf(0, "tag %s may not contain leading or trailing spaces", tag_name);
1044           return 0;
1045         }
1046
1047         if (strstr(key, "  ")) {
1048           i_push_errorf(0, "tag %s may not contain consecutive spaces", tag_name);
1049           return 0;
1050         }
1051
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);
1055             return 0;
1056           }
1057         }
1058       }
1059
1060       sprintf(tag_name, "png_text%d_text", i);
1061       value = get_string2(&im->tags, tag_name, value_buf, &value_size);
1062
1063       if (value) {
1064         if (memchr(value, '\0', value_size)) {
1065           i_push_errorf(0, "tag %s may not contain NUL characters", tag_name);
1066           return 0;
1067         }
1068       }
1069
1070       if (key && value) {
1071         png_text text;
1072         int compression = value_size > 1000;
1073
1074         sprintf(tag_name, "png_text%d_compressed", i);
1075         i_tags_get_int(&im->tags, tag_name, 0, &compression);
1076
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;
1084         text.lang = NULL;
1085         text.lang_key = NULL;
1086 #endif
1087
1088         png_set_text(png_ptr, info_ptr, &text, 1);
1089       }
1090       else if (key) {
1091         i_push_errorf(0, "tag png_text%d_key found but not png_text%d_text", i, i);
1092         return 0;
1093       }
1094       else if (value) {
1095         i_push_errorf(0, "tag png_text%d_text found but not png_text%d_key", i, i);
1096         return 0;
1097       }
1098       else {
1099         break;
1100       }
1101       ++i;
1102     }
1103   }
1104
1105   {
1106     char buf[GET_STR_BUF_SIZE];
1107     size_t time_size;
1108     const char *timestr = get_string2(&im->tags, "png_time", buf, &time_size);
1109
1110     if (timestr) {
1111       int year, month, day, hour, minute, second;
1112       png_time mod_time;
1113
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");
1122           return 0;
1123         }
1124         mod_time.year = year;
1125         mod_time.month = month;
1126         mod_time.day = day;
1127         mod_time.hour = hour;
1128         mod_time.minute = minute;
1129         mod_time.second = second;
1130
1131         png_set_tIME(png_ptr, info_ptr, &mod_time);
1132       }
1133       else {
1134         i_push_error(0, "png_time must be formatted 'y-m-dTh:m:s'");
1135         return 0;
1136       }
1137     }
1138   }
1139
1140   {
1141     /* no bKGD support yet, maybe later
1142        it may be simpler to do it in the individual writers
1143      */
1144   }
1145
1146   return 1;
1147 }
1148
1149 static const char *
1150 get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size) {
1151   int index;
1152
1153   if (i_tags_find(tags, name, 0, &index)) {
1154     const i_img_tag *entry = tags->tags + index;
1155     
1156     if (entry->data) {
1157       *size = entry->size;
1158
1159       return entry->data;
1160     }
1161     else {
1162       *size = sprintf(buf, "%d", entry->idata);
1163
1164       return buf;
1165     }
1166   }
1167   return NULL;
1168 }
1169
1170 static int
1171 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1172   unsigned char *data, *volatile vdata = NULL;
1173   i_img_dim y;
1174
1175   if (setjmp(png_jmpbuf(png_ptr))) {
1176     if (vdata)
1177       myfree(vdata);
1178
1179     return 0;
1180   }
1181
1182   png_write_info(png_ptr, info_ptr);
1183
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);
1188   }
1189   myfree(data);
1190
1191   return 1;
1192 }
1193
1194 static int
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;
1199   
1200   i_img_dim y;
1201
1202   if (setjmp(png_jmpbuf(png_ptr))) {
1203     if (vdata)
1204       myfree(vdata);
1205     if (vtran_data)
1206       myfree(vtran_data);
1207
1208     return 0;
1209   }
1210
1211   png_write_info(png_ptr, info_ptr);
1212
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++) {
1216     i_img_dim i;
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;
1222       p += 2;
1223     }
1224     png_write_row(png_ptr, (png_bytep)tran_data);
1225   }
1226   myfree(tran_data);
1227   myfree(data);
1228
1229   return 1;
1230 }
1231
1232 static int
1233 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
1234   unsigned char *data, *volatile vdata = NULL;
1235   i_img_dim y;
1236   unsigned char pal_map[256];
1237   png_color pcolors[256];
1238   i_color colors[256];
1239   int count = i_colorcount(im);
1240   int i;
1241
1242   if (setjmp(png_jmpbuf(png_ptr))) {
1243     if (vdata)
1244       myfree(vdata);
1245
1246     return 0;
1247   }
1248
1249   i_getcolors(im, 0, colors, count);
1250   if (im->channels < 3) {
1251     /* convert the greyscale palette to color */
1252     int i;
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];
1257     }
1258   }
1259
1260   if (i_img_has_alpha(im)) {
1261     int i;
1262     int bottom_index = 0, top_index = count-1;
1263
1264     /* fill out the palette map */
1265     for (i = 0; i < count; ++i)
1266       pal_map[i] = i;
1267
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
1270        the end */
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;
1275       }
1276       ++bottom_index;
1277     }
1278   }
1279
1280   for (i = 0; i < count; ++i) {
1281     int srci = i_img_has_alpha(im) ? pal_map[i] : i;
1282
1283     pcolors[i].red = colors[srci].rgb.r;
1284     pcolors[i].green = colors[srci].rgb.g;
1285     pcolors[i].blue = colors[srci].rgb.b;
1286   }
1287
1288   png_set_PLTE(png_ptr, info_ptr, pcolors, count);
1289
1290   if (i_img_has_alpha(im)) {
1291     unsigned char trans[256];
1292     int i;
1293
1294     for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
1295       trans[i] = colors[pal_map[i]].rgba.a;
1296     }
1297     png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
1298   }
1299
1300   png_write_info(png_ptr, info_ptr);
1301
1302   png_set_packing(png_ptr);
1303
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)) {
1308       i_img_dim x;
1309       for (x = 0; x < im->xsize; ++x)
1310         data[x] = pal_map[data[x]];
1311     }
1312     png_write_row(png_ptr, (png_bytep)data);
1313   }
1314   myfree(data);
1315
1316   return 1;
1317 }
1318
1319 static int
1320 write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1321   unsigned char *data, *volatile vdata = NULL;
1322   i_img_dim y;
1323
1324   if (setjmp(png_jmpbuf(png_ptr))) {
1325     if (vdata)
1326       myfree(vdata);
1327
1328     return 0;
1329   }
1330
1331   png_write_info(png_ptr, info_ptr);
1332
1333   png_set_packing(png_ptr);
1334
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);
1339   }
1340   myfree(data);
1341
1342   return 1;
1343 }
1344
1345 static void
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);
1348   char *workp;
1349   size_t new_size;
1350
1351   mm_log((1, "PNG read warning '%s'\n", msg));
1352
1353   /* in case this is part of an error report */
1354   i_push_error(0, msg);
1355   
1356   /* and save in the warnings so if we do manage to succeed, we 
1357    * can save it as a tag
1358    */
1359   new_size = (rs->warnings ? strlen(rs->warnings) : 0)
1360     + 1 /* NUL */
1361     + strlen(msg) /* new text */
1362     + 1; /* newline */
1363   workp = myrealloc(rs->warnings, new_size);
1364   if (!rs->warnings)
1365     *workp = '\0';
1366   strcat(workp, msg);
1367   strcat(workp, "\n");
1368   rs->warnings = workp;
1369 }
1370
1371 static void
1372 cleanup_read_state(i_png_read_statep rs) {
1373   if (rs->warnings)
1374     myfree(rs->warnings);
1375 }