PNG re-work: test results of new and old read handlers
[imager.git] / PNG / impng.c
CommitLineData
1d7e3124 1#include "impng.h"
02d1d628
AMH
2#include "png.h"
3
02d1d628
AMH
4/* this is a way to get number of channels from color space
5 * Color code to channel number */
6
b33c08f8 7static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
02d1d628
AMH
8
9#define PNG_BYTES_TO_CHECK 4
cfb628e2
TC
10
11static i_img *
12read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
13
c631b2d5
TC
14static i_img *
15read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
16
963d3602
TC
17static i_img *
18read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
19
79b4c849
TC
20static i_img *
21read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
22
647508aa
TC
23unsigned
24i_png_lib_version(void) {
25 return png_access_version_number();
26}
02d1d628 27
02d1d628 28static void
790923a4 29wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
f5b4354e 30 io_glue *ig = png_get_io_ptr(png_ptr);
38eab175 31 ssize_t rc = i_io_read(ig, data, length);
790923a4 32 if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
02d1d628
AMH
33}
34
02d1d628 35static void
790923a4 36wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
38eab175 37 ssize_t rc;
f5b4354e 38 io_glue *ig = png_get_io_ptr(png_ptr);
6d5c85a2 39 rc = i_io_write(ig, data, length);
790923a4 40 if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
02d1d628
AMH
41}
42
43static void
790923a4
AMH
44wiol_flush_data(png_structp png_ptr) {
45 /* XXX : This needs to be added to the io layer */
02d1d628 46}
02d1d628 47
38eab175
TC
48static void
49error_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
963d3602
TC
58 For writing a warning might have information about an error, so send
59 it to the error stack.
38eab175
TC
60
61*/
62static void
63write_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
02d1d628 71undef_int
790923a4 72i_writepng_wiol(i_img *im, io_glue *ig) {
02d1d628 73 png_structp png_ptr;
a807aae6 74 png_infop info_ptr = NULL;
38eab175 75 i_img_dim width,height,y;
02d1d628 76 volatile int cspace,channels;
faa9b3e7
TC
77 double xres, yres;
78 int aspect_only, have_res;
34e56776
TC
79 unsigned char *data;
80 unsigned char * volatile vdata = NULL;
02d1d628 81
790923a4 82 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
8d14daab
TC
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
790923a4
AMH
91 height = im->ysize;
92 width = im->xsize;
02d1d628 93
38eab175
TC
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
647508aa 102 * fixed in libpng 1.5.3
34e56776 103 */
38eab175
TC
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
02d1d628
AMH
109 channels=im->channels;
110
790923a4 111 if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; }
02d1d628
AMH
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
790923a4 117 channels = im->channels;
02d1d628
AMH
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
38eab175
TC
126 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
127 error_handler, write_warn_handler);
02d1d628 128
790923a4
AMH
129 if (png_ptr == NULL) return 0;
130
02d1d628
AMH
131
132 /* Allocate/initialize the image information data. REQUIRED */
133 info_ptr = png_create_info_struct(png_ptr);
134
135 if (info_ptr == NULL) {
a807aae6 136 png_destroy_write_struct(&png_ptr, &info_ptr);
790923a4 137 return 0;
02d1d628
AMH
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 */
f5b4354e 143 if (setjmp(png_jmpbuf(png_ptr))) {
a807aae6 144 png_destroy_write_struct(&png_ptr, &info_ptr);
34e56776
TC
145 if (vdata)
146 myfree(vdata);
02d1d628
AMH
147 return(0);
148 }
149
790923a4 150 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
02d1d628
AMH
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
38eab175
TC
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
02d1d628
AMH
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
faa9b3e7
TC
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
02d1d628 192 png_write_info(png_ptr, info_ptr);
790923a4 193
34e56776
TC
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);
faa9b3e7 198 }
34e56776 199 myfree(data);
790923a4 200
02d1d628 201 png_write_end(png_ptr, info_ptr);
790923a4 202
a807aae6 203 png_destroy_write_struct(&png_ptr, &info_ptr);
02d1d628 204
6d5c85a2
TC
205 if (i_io_close(ig))
206 return 0;
10461f9a 207
02d1d628
AMH
208 return(1);
209}
210
38eab175 211static void
79b4c849 212get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth);
38eab175
TC
213
214typedef struct {
215 char *warnings;
216} i_png_read_state, *i_png_read_statep;
02d1d628 217
38eab175
TC
218static void
219read_warn_handler(png_structp, png_const_charp);
02d1d628 220
38eab175
TC
221static void
222cleanup_read_state(i_png_read_statep);
02d1d628
AMH
223
224i_img*
1d7e3124 225i_readpng_wiol(io_glue *ig) {
02576e8d 226 i_img *im = NULL;
02d1d628
AMH
227 png_structp png_ptr;
228 png_infop info_ptr;
790923a4 229 png_uint_32 width, height;
02d1d628 230 int bit_depth, color_type, interlace_type;
790923a4
AMH
231 int number_passes,y;
232 int channels,pass;
02d1d628 233 unsigned int sig_read;
38eab175 234 i_png_read_state rs;
647508aa
TC
235 i_img_dim wmax, hmax;
236 size_t bytes;
02d1d628 237
38eab175 238 rs.warnings = NULL;
790923a4 239 sig_read = 0;
02d1d628 240
1d7e3124 241 mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
38eab175 242 i_clear_error();
02d1d628 243
38eab175
TC
244 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs,
245 error_handler, read_warn_handler);
246 if (!png_ptr) {
247 i_push_error(0, "Cannot create PNG read structure");
248 return NULL;
249 }
790923a4 250 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
02d1d628
AMH
251
252 info_ptr = png_create_info_struct(png_ptr);
253 if (info_ptr == NULL) {
254 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
38eab175 255 i_push_error(0, "Cannot create PNG info structure");
02d1d628
AMH
256 return NULL;
257 }
258
f5b4354e 259 if (setjmp(png_jmpbuf(png_ptr))) {
02576e8d 260 if (im) i_img_destroy(im);
790923a4 261 mm_log((1,"i_readpng_wiol: error.\n"));
02d1d628 262 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
38eab175 263 cleanup_read_state(&rs);
02d1d628
AMH
264 return NULL;
265 }
647508aa
TC
266
267 /* we do our own limit checks */
268 png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
790923a4 269
02d1d628
AMH
270 png_set_sig_bytes(png_ptr, sig_read);
271 png_read_info(png_ptr, info_ptr);
272 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
273
274 mm_log((1,
790923a4 275 "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
02d1d628
AMH
276 width,height,bit_depth,color_type,interlace_type));
277
278 CC2C[PNG_COLOR_TYPE_GRAY]=1;
279 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
280 CC2C[PNG_COLOR_TYPE_RGB]=3;
281 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
282 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
790923a4
AMH
283 channels = CC2C[color_type];
284
285 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
286
77157728
TC
287 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
288 mm_log((1, "i_readpnm: image size exceeds limits\n"));
289 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
290 return NULL;
291 }
292
963d3602
TC
293 if (color_type == PNG_COLOR_TYPE_PALETTE) {
294 im = read_paletted(png_ptr, info_ptr, channels, width, height);
295 }
79b4c849
TC
296 else if (color_type == PNG_COLOR_TYPE_GRAY
297 && bit_depth == 1
298 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
299 im = read_bilevel(png_ptr, info_ptr, width, height);
300 }
c631b2d5
TC
301 else if (bit_depth == 16) {
302 im = read_direct16(png_ptr, info_ptr, channels, width, height);
303 }
963d3602
TC
304 else {
305 im = read_direct8(png_ptr, info_ptr, channels, width, height);
306 }
cfb628e2
TC
307
308 if (im)
79b4c849 309 get_png_tags(im, png_ptr, info_ptr, bit_depth);
cfb628e2
TC
310
311 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
312
313 if (im) {
314 if (rs.warnings) {
315 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
316 }
317 }
318 cleanup_read_state(&rs);
319
320 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
321
322 return im;
323}
324
325static i_img *
326read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
327 i_img_dim width, i_img_dim height) {
328 i_img * volatile vim = NULL;
329 int color_type = png_get_color_type(png_ptr, info_ptr);
330 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
331 i_img_dim y;
332 int number_passes, pass;
333 i_img *im;
34e56776
TC
334 unsigned char *line;
335 unsigned char * volatile vline = NULL;
cfb628e2
TC
336
337 if (setjmp(png_jmpbuf(png_ptr))) {
338 if (vim) i_img_destroy(vim);
34e56776 339 if (vline) myfree(vline);
cfb628e2
TC
340
341 return NULL;
342 }
343
344 number_passes = png_set_interlace_handling(png_ptr);
345 mm_log((1,"number of passes=%d\n",number_passes));
346
02d1d628
AMH
347 png_set_strip_16(png_ptr);
348 png_set_packing(png_ptr);
6829f5f6 349
cfb628e2
TC
350 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
351 png_set_expand(png_ptr);
352
6829f5f6
AMH
353 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
354 channels++;
355 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
356 png_set_expand(png_ptr);
357 }
790923a4 358
02d1d628 359 png_read_update_info(png_ptr, info_ptr);
790923a4 360
cfb628e2 361 im = vim = i_img_8_new(width,height,channels);
352c64ed
TC
362 if (!im) {
363 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
364 return NULL;
365 }
02d1d628 366
34e56776 367 line = vline = mymalloc(channels * width);
cfb628e2
TC
368 for (pass = 0; pass < number_passes; pass++) {
369 for (y = 0; y < height; y++) {
34e56776
TC
370 if (pass > 0)
371 i_gsamp(im, 0, width, y, line, NULL, channels);
372 png_read_row(png_ptr,(png_bytep)line, NULL);
373 i_psamp(im, 0, width, y, line, NULL, channels);
cfb628e2 374 }
38eab175 375 }
34e56776
TC
376 myfree(line);
377 vline = NULL;
02d1d628 378
cfb628e2
TC
379 png_read_end(png_ptr, info_ptr);
380
790923a4
AMH
381 return im;
382}
faa9b3e7 383
c631b2d5
TC
384static i_img *
385read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
386 i_img_dim width, i_img_dim height) {
387 i_img * volatile vim = NULL;
388 int color_type = png_get_color_type(png_ptr, info_ptr);
389 i_img_dim x, y;
390 int number_passes, pass;
391 i_img *im;
392 unsigned char *line;
393 unsigned char * volatile vline = NULL;
394 unsigned *bits_line;
395 unsigned * volatile vbits_line = NULL;
396 size_t row_bytes;
397
398 if (setjmp(png_jmpbuf(png_ptr))) {
399 if (vim) i_img_destroy(vim);
400 if (vline) myfree(vline);
401 if (vbits_line) myfree(vbits_line);
402
403 return NULL;
404 }
405
406 number_passes = png_set_interlace_handling(png_ptr);
407 mm_log((1,"number of passes=%d\n",number_passes));
408
409 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
410 channels++;
411 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
412 png_set_expand(png_ptr);
413 }
414
415 png_read_update_info(png_ptr, info_ptr);
416
417 im = vim = i_img_16_new(width,height,channels);
418 if (!im) {
419 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
420 return NULL;
421 }
422
423 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
424 line = vline = mymalloc(row_bytes);
425 memset(line, 0, row_bytes);
426 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
427 for (pass = 0; pass < number_passes; pass++) {
428 for (y = 0; y < height; y++) {
429 if (pass > 0) {
430 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
431 for (x = 0; x < width * channels; ++x) {
432 line[x*2] = bits_line[x] >> 8;
433 line[x*2+1] = bits_line[x] & 0xff;
434 }
435 }
436 png_read_row(png_ptr,(png_bytep)line, NULL);
437 for (x = 0; x < width * channels; ++x)
438 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
439 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
440 }
441 }
442 myfree(line);
443 myfree(bits_line);
444 vline = NULL;
445 vbits_line = NULL;
446
447 png_read_end(png_ptr, info_ptr);
448
449 return im;
450}
451
79b4c849
TC
452static i_img *
453read_bilevel(png_structp png_ptr, png_infop info_ptr,
454 i_img_dim width, i_img_dim height) {
455 i_img * volatile vim = NULL;
456 i_img_dim x, y;
457 int number_passes, pass;
458 i_img *im;
459 unsigned char *line;
460 unsigned char * volatile vline = NULL;
461 i_color palette[2];
462
463 if (setjmp(png_jmpbuf(png_ptr))) {
464 if (vim) i_img_destroy(vim);
465 if (vline) myfree(vline);
466
467 return NULL;
468 }
469
470 number_passes = png_set_interlace_handling(png_ptr);
471 mm_log((1,"number of passes=%d\n",number_passes));
472
473 png_set_packing(png_ptr);
474
475 png_set_expand(png_ptr);
476
477 png_read_update_info(png_ptr, info_ptr);
478
479 im = vim = i_img_pal_new(width, height, 1, 256);
480 if (!im) {
481 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
482 return NULL;
483 }
484
485 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
486 palette[0].channel[3] = 0;
487 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
488 palette[1].channel[3] = 255;
489 i_addcolors(im, palette, 2);
490
491 line = vline = mymalloc(width);
492 memset(line, 0, width);
493 for (pass = 0; pass < number_passes; pass++) {
494 for (y = 0; y < height; y++) {
495 if (pass > 0) {
496 i_gpal(im, 0, width, y, line);
497 /* expand indexes back to 0/255 */
498 for (x = 0; x < width; ++x)
499 line[x] = line[x] ? 255 : 0;
500 }
501 png_read_row(png_ptr,(png_bytep)line, NULL);
502
503 /* back to palette indexes */
504 for (x = 0; x < width; ++x)
505 line[x] = line[x] ? 1 : 0;
506 i_ppal(im, 0, width, y, line);
507 }
508 }
509 myfree(line);
510 vline = NULL;
511
512 png_read_end(png_ptr, info_ptr);
513
514 return im;
515}
516
517/* FIXME: do we need to unscale palette color values from the
518 supplied alphas? */
963d3602
TC
519static i_img *
520read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
521 i_img_dim width, i_img_dim height) {
522 i_img * volatile vim = NULL;
523 int color_type = png_get_color_type(png_ptr, info_ptr);
524 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
525 i_img_dim y;
526 int number_passes, pass;
527 i_img *im;
528 unsigned char *line;
529 unsigned char * volatile vline = NULL;
530 int num_palette, i;
531 png_colorp png_palette;
532 png_bytep png_pal_trans;
533 png_color_16p png_color_trans;
534 int num_pal_trans;
535
536 if (setjmp(png_jmpbuf(png_ptr))) {
537 if (vim) i_img_destroy(vim);
538 if (vline) myfree(vline);
539
540 return NULL;
541 }
542
543 number_passes = png_set_interlace_handling(png_ptr);
544 mm_log((1,"number of passes=%d\n",number_passes));
545
546 png_set_strip_16(png_ptr);
547 png_set_packing(png_ptr);
548
549 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
550 png_set_expand(png_ptr);
551
552 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
553 i_push_error(0, "Paletted image with no PLTE chunk");
554 return NULL;
555 }
556
557 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
558 &png_color_trans)) {
559 channels++;
560 }
561 else {
562 num_pal_trans = 0;
563 }
564
565 png_read_update_info(png_ptr, info_ptr);
566
567 im = vim = i_img_pal_new(width, height, channels, 256);
568 if (!im) {
569 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
570 return NULL;
571 }
572
573 for (i = 0; i < num_palette; ++i) {
574 i_color c;
575
576 c.rgba.r = png_palette[i].red;
577 c.rgba.g = png_palette[i].green;
578 c.rgba.b = png_palette[i].blue;
579 if (i < num_pal_trans)
580 c.rgba.a = png_pal_trans[i];
581 else
582 c.rgba.a = 255;
583 i_addcolors(im, &c, 1);
584 }
585
586 line = vline = mymalloc(width);
587 for (pass = 0; pass < number_passes; pass++) {
588 for (y = 0; y < height; y++) {
589 if (pass > 0)
590 i_gpal(im, 0, width, y, line);
591 png_read_row(png_ptr,(png_bytep)line, NULL);
592 i_ppal(im, 0, width, y, line);
593 }
594 }
595 myfree(line);
596 vline = NULL;
597
598 png_read_end(png_ptr, info_ptr);
599
600 return im;
601}
602
38eab175 603static void
79b4c849 604get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth) {
faa9b3e7
TC
605 png_uint_32 xres, yres;
606 int unit_type;
352c64ed 607
1d7e3124 608 i_tags_set(&im->tags, "i_format", "png", -1);
faa9b3e7
TC
609 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
610 mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
611 if (unit_type == PNG_RESOLUTION_METER) {
2e41e30b
TC
612 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
613 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
faa9b3e7
TC
614 }
615 else {
1d7e3124
TC
616 i_tags_setn(&im->tags, "i_xres", xres);
617 i_tags_setn(&im->tags, "i_yres", yres);
618 i_tags_setn(&im->tags, "i_aspect_only", 1);
faa9b3e7
TC
619 }
620 }
a4fa5d5e
TC
621 {
622 int interlace = png_get_interlace_type(png_ptr, info_ptr);
623
624 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
625 switch (interlace) {
626 case PNG_INTERLACE_NONE:
627 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
628 break;
629
630 case PNG_INTERLACE_ADAM7:
631 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
632 break;
633
634 default:
635 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
636 break;
637 }
00cff942
TC
638 }
639
79b4c849
TC
640 /* the various readers can call png_set_expand(), libpng will make
641 it's internal record of bit_depth at least 8 in that case */
642 i_tags_setn(&im->tags, "png_bits", bit_depth);
faa9b3e7 643}
38eab175
TC
644
645static void
646read_warn_handler(png_structp png_ptr, png_const_charp msg) {
647 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
648 char *workp;
649 size_t new_size;
650
651 mm_log((1, "PNG read warning '%s'\n", msg));
652
653 /* in case this is part of an error report */
654 i_push_error(0, msg);
655
656 /* and save in the warnings so if we do manage to succeed, we
657 * can save it as a tag
658 */
659 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
660 + 1 /* NUL */
661 + strlen(msg) /* new text */
662 + 1; /* newline */
663 workp = myrealloc(rs->warnings, new_size);
664 if (!rs->warnings)
665 *workp = '\0';
666 strcat(workp, msg);
667 strcat(workp, "\n");
668 rs->warnings = workp;
669}
670
671static void
672cleanup_read_state(i_png_read_statep rs) {
673 if (rs->warnings)
674 myfree(rs->warnings);
675}