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