PNG re-work: remove unused variables
[imager.git] / PNG / impng.c
1 #include "impng.h"
2 #include "png.h"
3
4 /* this is a way to get number of channels from color space 
5  * Color code to channel number */
6
7 static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
8
9 #define PNG_BYTES_TO_CHECK 4
10
11 static i_img *
12 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
13
14 static i_img *
15 read_direct16(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_paletted(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_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
22
23 unsigned
24 i_png_lib_version(void) {
25   return png_access_version_number();
26 }
27
28 static void
29 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
30   io_glue *ig = png_get_io_ptr(png_ptr);
31   ssize_t rc = i_io_read(ig, data, length);
32   if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
33 }
34
35 static void
36 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
37   ssize_t rc;
38   io_glue *ig = png_get_io_ptr(png_ptr);
39   rc = i_io_write(ig, data, length);
40   if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
41 }
42
43 static void
44 wiol_flush_data(png_structp png_ptr) {
45   /* XXX : This needs to be added to the io layer */
46 }
47
48 static void
49 error_handler(png_structp png_ptr, png_const_charp msg) {
50   mm_log((1, "PNG error: '%s'\n", msg));
51
52   i_push_error(0, msg);
53   longjmp(png_jmpbuf(png_ptr), 1);
54 }
55
56 /*
57
58   For writing a warning might have information about an error, so send
59   it to the error stack.
60
61 */
62 static void
63 write_warn_handler(png_structp png_ptr, png_const_charp msg) {
64   mm_log((1, "PNG write warning '%s'\n", msg));
65
66   i_push_error(0, msg);
67 }
68
69 #define PNG_DIM_MAX 0x7fffffffL
70
71 undef_int
72 i_writepng_wiol(i_img *im, io_glue *ig) {
73   png_structp png_ptr;
74   png_infop info_ptr = NULL;
75   i_img_dim width,height,y;
76   volatile int cspace,channels;
77   double xres, yres;
78   int aspect_only, have_res;
79   unsigned char *data;
80   unsigned char * volatile vdata = NULL;
81
82   mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
83
84   i_clear_error();
85
86   if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
87     i_push_error(0, "image too large for PNG");
88     return 0;
89   }
90
91   height = im->ysize;
92   width  = im->xsize;
93
94   /* if we ever have 64-bit i_img_dim
95    * the libpng docs state that png_set_user_limits() can be used to
96    * override the PNG_USER_*_MAX limits, but as implemented they
97    * don't.  We check against the theoretical limit of PNG here, and
98    * try to override the limits below, in case the libpng
99    * implementation ever matches the documentation.
100    *
101    * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
102    * fixed in libpng 1.5.3
103    */
104   if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
105     i_push_error(0, "Image too large for PNG");
106     return 0;
107   }
108
109   channels=im->channels;
110
111   if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; }
112   else { cspace=PNG_COLOR_TYPE_GRAY; channels--; }
113   
114   if (channels) cspace|=PNG_COLOR_MASK_ALPHA;
115   mm_log((1,"cspace=%d\n",cspace));
116
117   channels = im->channels;
118
119   /* Create and initialize the png_struct with the desired error handler
120    * functions.  If you want to use the default stderr and longjump method,
121    * you can supply NULL for the last three parameters.  We also check that
122    * the library version is compatible with the one used at compile time,
123    * in case we are using dynamically linked libraries.  REQUIRED.
124    */
125   
126   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
127                                     error_handler, write_warn_handler);
128   
129   if (png_ptr == NULL) return 0;
130
131   
132   /* Allocate/initialize the image information data.  REQUIRED */
133   info_ptr = png_create_info_struct(png_ptr);
134
135   if (info_ptr == NULL) {
136     png_destroy_write_struct(&png_ptr, &info_ptr);
137     return 0;
138   }
139   
140   /* Set error handling.  REQUIRED if you aren't supplying your own
141    * error hadnling functions in the png_create_write_struct() call.
142    */
143   if (setjmp(png_jmpbuf(png_ptr))) {
144     png_destroy_write_struct(&png_ptr, &info_ptr);
145     if (vdata)
146       myfree(vdata);
147     return(0);
148   }
149   
150   png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
151
152   /* Set the image information here.  Width and height are up to 2^31,
153    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
154    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
155    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
156    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
157    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
158    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
159    */
160
161   /* by default, libpng (not PNG) limits the image size to a maximum
162    * 1000000 pixels in each direction, but Imager doesn't.
163    * Configure libpng to avoid that limit.
164    */
165   png_set_user_limits(png_ptr, width, height);
166
167   png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
168                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
169
170   have_res = 1;
171   if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
172     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
173       ; /* nothing to do */
174     else
175       yres = xres;
176   }
177   else {
178     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
179       xres = yres;
180     else
181       have_res = 0;
182   }
183   if (have_res) {
184     aspect_only = 0;
185     i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
186     xres /= 0.0254;
187     yres /= 0.0254;
188     png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, 
189                  aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
190   }
191
192   png_write_info(png_ptr, info_ptr);
193
194   vdata = data = mymalloc(im->xsize * im->channels);
195   for (y = 0; y < height; y++) {
196     i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
197     png_write_row(png_ptr, (png_bytep)data);
198   }
199   myfree(data);
200
201   png_write_end(png_ptr, info_ptr);
202
203   png_destroy_write_struct(&png_ptr, &info_ptr);
204
205   if (i_io_close(ig))
206     return 0;
207
208   return(1);
209 }
210
211 static void 
212 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth);
213
214 typedef struct {
215   char *warnings;
216 } i_png_read_state, *i_png_read_statep;
217
218 static void
219 read_warn_handler(png_structp, png_const_charp);
220
221 static void
222 cleanup_read_state(i_png_read_statep);
223
224 i_img*
225 i_readpng_wiol(io_glue *ig) {
226   i_img *im = NULL;
227   png_structp png_ptr;
228   png_infop info_ptr;
229   png_uint_32 width, height;
230   int bit_depth, color_type, interlace_type;
231   int channels;
232   unsigned int sig_read;
233   i_png_read_state rs;
234
235   rs.warnings = NULL;
236   sig_read  = 0;
237
238   mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
239   i_clear_error();
240
241   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs, 
242                                    error_handler, read_warn_handler);
243   if (!png_ptr) {
244     i_push_error(0, "Cannot create PNG read structure");
245     return NULL;
246   }
247   png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
248   
249   info_ptr = png_create_info_struct(png_ptr);
250   if (info_ptr == NULL) {
251     png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
252     i_push_error(0, "Cannot create PNG info structure");
253     return NULL;
254   }
255   
256   if (setjmp(png_jmpbuf(png_ptr))) {
257     if (im) i_img_destroy(im);
258     mm_log((1,"i_readpng_wiol: error.\n"));
259     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
260     cleanup_read_state(&rs);
261     return NULL;
262   }
263   
264   /* we do our own limit checks */
265   png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
266
267   png_set_sig_bytes(png_ptr, sig_read);
268   png_read_info(png_ptr, info_ptr);
269   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
270   
271   mm_log((1,
272           "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
273           width,height,bit_depth,color_type,interlace_type));
274   
275   CC2C[PNG_COLOR_TYPE_GRAY]=1;
276   CC2C[PNG_COLOR_TYPE_PALETTE]=3;
277   CC2C[PNG_COLOR_TYPE_RGB]=3;
278   CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
279   CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
280   channels = CC2C[color_type];
281
282   mm_log((1,"i_readpng_wiol: channels %d\n",channels));
283
284   if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
285     mm_log((1, "i_readpnm: image size exceeds limits\n"));
286     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
287     return NULL;
288   }
289
290   if (color_type == PNG_COLOR_TYPE_PALETTE) {
291     im = read_paletted(png_ptr, info_ptr, channels, width, height);
292   }
293   else if (color_type == PNG_COLOR_TYPE_GRAY
294            && bit_depth == 1
295            && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
296     im = read_bilevel(png_ptr, info_ptr, width, height);
297   }
298   else if (bit_depth == 16) {
299     im = read_direct16(png_ptr, info_ptr, channels, width, height);
300   }
301   else {
302     im = read_direct8(png_ptr, info_ptr, channels, width, height);
303   }
304
305   if (im)
306     get_png_tags(im, png_ptr, info_ptr, bit_depth);
307
308   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
309
310   if (im) {
311     if (rs.warnings) {
312       i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
313     }
314   }
315   cleanup_read_state(&rs);
316   
317   mm_log((1,"(%p) <- i_readpng_wiol\n", im));  
318   
319   return im;
320 }
321
322 static i_img *
323 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
324              i_img_dim width, i_img_dim height) {
325   i_img * volatile vim = NULL;
326   int color_type = png_get_color_type(png_ptr, info_ptr);
327   int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
328   i_img_dim y;
329   int number_passes, pass;
330   i_img *im;
331   unsigned char *line;
332   unsigned char * volatile vline = NULL;
333
334   if (setjmp(png_jmpbuf(png_ptr))) {
335     if (vim) i_img_destroy(vim);
336     if (vline) myfree(vline);
337
338     return NULL;
339   }
340
341   number_passes = png_set_interlace_handling(png_ptr);
342   mm_log((1,"number of passes=%d\n",number_passes));
343
344   png_set_strip_16(png_ptr);
345   png_set_packing(png_ptr);
346
347   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
348     png_set_expand(png_ptr);
349     
350   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
351     channels++;
352     mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
353     png_set_expand(png_ptr);
354   }
355   
356   png_read_update_info(png_ptr, info_ptr);
357   
358   im = vim = i_img_8_new(width,height,channels);
359   if (!im) {
360     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
361     return NULL;
362   }
363   
364   line = vline = mymalloc(channels * width);
365   for (pass = 0; pass < number_passes; pass++) {
366     for (y = 0; y < height; y++) {
367       if (pass > 0)
368         i_gsamp(im, 0, width, y, line, NULL, channels);
369       png_read_row(png_ptr,(png_bytep)line, NULL);
370       i_psamp(im, 0, width, y, line, NULL, channels);
371     }
372   }
373   myfree(line);
374   vline = NULL;
375   
376   png_read_end(png_ptr, info_ptr); 
377
378   return im;
379 }
380
381 static i_img *
382 read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
383              i_img_dim width, i_img_dim height) {
384   i_img * volatile vim = NULL;
385   i_img_dim x, y;
386   int number_passes, pass;
387   i_img *im;
388   unsigned char *line;
389   unsigned char * volatile vline = NULL;
390   unsigned *bits_line;
391   unsigned * volatile vbits_line = NULL;
392   size_t row_bytes;
393
394   if (setjmp(png_jmpbuf(png_ptr))) {
395     if (vim) i_img_destroy(vim);
396     if (vline) myfree(vline);
397     if (vbits_line) myfree(vbits_line);
398
399     return NULL;
400   }
401
402   number_passes = png_set_interlace_handling(png_ptr);
403   mm_log((1,"number of passes=%d\n",number_passes));
404
405   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
406     channels++;
407     mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
408     png_set_expand(png_ptr);
409   }
410   
411   png_read_update_info(png_ptr, info_ptr);
412   
413   im = vim = i_img_16_new(width,height,channels);
414   if (!im) {
415     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
416     return NULL;
417   }
418   
419   row_bytes = png_get_rowbytes(png_ptr, info_ptr);
420   line = vline = mymalloc(row_bytes);
421   memset(line, 0, row_bytes);
422   bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
423   for (pass = 0; pass < number_passes; pass++) {
424     for (y = 0; y < height; y++) {
425       if (pass > 0) {
426         i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
427         for (x = 0; x < width * channels; ++x) {
428           line[x*2] = bits_line[x] >> 8;
429           line[x*2+1] = bits_line[x] & 0xff;
430         }
431       }
432       png_read_row(png_ptr,(png_bytep)line, NULL);
433       for (x = 0; x < width * channels; ++x)
434         bits_line[x] = (line[x*2] << 8) + line[x*2+1];
435       i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
436     }
437   }
438   myfree(line);
439   myfree(bits_line);
440   vline = NULL;
441   vbits_line = NULL;
442   
443   png_read_end(png_ptr, info_ptr); 
444
445   return im;
446 }
447
448 static i_img *
449 read_bilevel(png_structp png_ptr, png_infop info_ptr,
450              i_img_dim width, i_img_dim height) {
451   i_img * volatile vim = NULL;
452   i_img_dim x, y;
453   int number_passes, pass;
454   i_img *im;
455   unsigned char *line;
456   unsigned char * volatile vline = NULL;
457   i_color palette[2];
458
459   if (setjmp(png_jmpbuf(png_ptr))) {
460     if (vim) i_img_destroy(vim);
461     if (vline) myfree(vline);
462
463     return NULL;
464   }
465
466   number_passes = png_set_interlace_handling(png_ptr);
467   mm_log((1,"number of passes=%d\n",number_passes));
468
469   png_set_packing(png_ptr);
470
471   png_set_expand(png_ptr);  
472   
473   png_read_update_info(png_ptr, info_ptr);
474   
475   im = vim = i_img_pal_new(width, height, 1, 256);
476   if (!im) {
477     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
478     return NULL;
479   }
480
481   palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] = 
482     palette[0].channel[3] = 0;
483   palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] = 
484     palette[1].channel[3] = 255;
485   i_addcolors(im, palette, 2);
486   
487   line = vline = mymalloc(width);
488   memset(line, 0, width);
489   for (pass = 0; pass < number_passes; pass++) {
490     for (y = 0; y < height; y++) {
491       if (pass > 0) {
492         i_gpal(im, 0, width, y, line);
493         /* expand indexes back to 0/255 */
494         for (x = 0; x < width; ++x)
495           line[x] = line[x] ? 255 : 0;
496       }
497       png_read_row(png_ptr,(png_bytep)line, NULL);
498
499       /* back to palette indexes */
500       for (x = 0; x < width; ++x)
501         line[x] = line[x] ? 1 : 0;
502       i_ppal(im, 0, width, y, line);
503     }
504   }
505   myfree(line);
506   vline = NULL;
507   
508   png_read_end(png_ptr, info_ptr); 
509
510   return im;
511 }
512
513 /* FIXME: do we need to unscale palette color values from the 
514    supplied alphas? */
515 static i_img *
516 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
517               i_img_dim width, i_img_dim height) {
518   i_img * volatile vim = NULL;
519   int color_type = png_get_color_type(png_ptr, info_ptr);
520   int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
521   i_img_dim y;
522   int number_passes, pass;
523   i_img *im;
524   unsigned char *line;
525   unsigned char * volatile vline = NULL;
526   int num_palette, i;
527   png_colorp png_palette;
528   png_bytep png_pal_trans;
529   png_color_16p png_color_trans;
530   int num_pal_trans;
531
532   if (setjmp(png_jmpbuf(png_ptr))) {
533     if (vim) i_img_destroy(vim);
534     if (vline) myfree(vline);
535
536     return NULL;
537   }
538
539   number_passes = png_set_interlace_handling(png_ptr);
540   mm_log((1,"number of passes=%d\n",number_passes));
541
542   png_set_strip_16(png_ptr);
543   png_set_packing(png_ptr);
544
545   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
546     png_set_expand(png_ptr);
547     
548   if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
549     i_push_error(0, "Paletted image with no PLTE chunk");
550     return NULL;
551   }
552
553   if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
554                    &png_color_trans)) {
555     channels++;
556   }
557   else {
558     num_pal_trans = 0;
559   }
560   
561   png_read_update_info(png_ptr, info_ptr);
562   
563   im = vim = i_img_pal_new(width, height, channels, 256);
564   if (!im) {
565     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
566     return NULL;
567   }
568
569   for (i = 0; i < num_palette; ++i) {
570     i_color c;
571
572     c.rgba.r = png_palette[i].red;
573     c.rgba.g = png_palette[i].green;
574     c.rgba.b = png_palette[i].blue;
575     if (i < num_pal_trans)
576       c.rgba.a = png_pal_trans[i];
577     else
578       c.rgba.a = 255;
579     i_addcolors(im, &c, 1);
580   }
581
582   line = vline = mymalloc(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       png_read_row(png_ptr,(png_bytep)line, NULL);
588       i_ppal(im, 0, width, y, line);
589     }
590   }
591   myfree(line);
592   vline = NULL;
593   
594   png_read_end(png_ptr, info_ptr); 
595
596   return im;
597 }
598
599 static void
600 get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth) {
601   png_uint_32 xres, yres;
602   int unit_type;
603
604   i_tags_set(&im->tags, "i_format", "png", -1);
605   if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
606     mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
607     if (unit_type == PNG_RESOLUTION_METER) {
608       i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
609       i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
610     }
611     else {
612       i_tags_setn(&im->tags, "i_xres", xres);
613       i_tags_setn(&im->tags, "i_yres", yres);
614       i_tags_setn(&im->tags, "i_aspect_only", 1);
615     }
616   }
617   {
618     int interlace = png_get_interlace_type(png_ptr, info_ptr);
619
620     i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
621     switch (interlace) {
622     case PNG_INTERLACE_NONE:
623       i_tags_set(&im->tags, "png_interlace_name", "none", -1);
624       break;
625       
626     case PNG_INTERLACE_ADAM7:
627       i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
628       break;
629       
630     default:
631       i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
632       break;
633     }
634   }
635
636   /* the various readers can call png_set_expand(), libpng will make
637      it's internal record of bit_depth at least 8 in that case */
638   i_tags_setn(&im->tags, "png_bits", bit_depth);
639 }
640
641 static void
642 read_warn_handler(png_structp png_ptr, png_const_charp msg) {
643   i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
644   char *workp;
645   size_t new_size;
646
647   mm_log((1, "PNG read warning '%s'\n", msg));
648
649   /* in case this is part of an error report */
650   i_push_error(0, msg);
651   
652   /* and save in the warnings so if we do manage to succeed, we 
653    * can save it as a tag
654    */
655   new_size = (rs->warnings ? strlen(rs->warnings) : 0)
656     + 1 /* NUL */
657     + strlen(msg) /* new text */
658     + 1; /* newline */
659   workp = myrealloc(rs->warnings, new_size);
660   if (!rs->warnings)
661     *workp = '\0';
662   strcat(workp, msg);
663   strcat(workp, "\n");
664   rs->warnings = workp;
665 }
666
667 static void
668 cleanup_read_state(i_png_read_statep rs) {
669   if (rs->warnings)
670     myfree(rs->warnings);
671 }