PNG-rework: forward png flush output through to i_io_flush()
[imager.git] / PNG / impng.c
CommitLineData
1d7e3124 1#include "impng.h"
02d1d628 2#include "png.h"
21c6936b 3#include <stdlib.h>
02d1d628 4
02d1d628
AMH
5/* this is a way to get number of channels from color space
6 * Color code to channel number */
7
b33c08f8 8static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
02d1d628
AMH
9
10#define PNG_BYTES_TO_CHECK 4
cfb628e2
TC
11
12static i_img *
13read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
14
c631b2d5
TC
15static i_img *
16read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
17
963d3602
TC
18static i_img *
19read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
20
79b4c849
TC
21static i_img *
22read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
23
81089826
TC
24static int
25write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
26
d9610331
TC
27static int
28write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
29
21c6936b
TC
30static int
31write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
32
b8961d05
TC
33static int
34write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im);
21c6936b 35
647508aa
TC
36unsigned
37i_png_lib_version(void) {
38 return png_access_version_number();
39}
02d1d628 40
02d1d628 41static void
790923a4 42wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
f5b4354e 43 io_glue *ig = png_get_io_ptr(png_ptr);
38eab175 44 ssize_t rc = i_io_read(ig, data, length);
790923a4 45 if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
02d1d628
AMH
46}
47
02d1d628 48static void
790923a4 49wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
38eab175 50 ssize_t rc;
f5b4354e 51 io_glue *ig = png_get_io_ptr(png_ptr);
6d5c85a2 52 rc = i_io_write(ig, data, length);
790923a4 53 if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
02d1d628
AMH
54}
55
56static void
790923a4 57wiol_flush_data(png_structp png_ptr) {
6352c169
TC
58 io_glue *ig = png_get_io_ptr(png_ptr);
59 if (!i_io_flush(ig))
60 png_error(png_ptr, "Error flushing output");
02d1d628 61}
02d1d628 62
38eab175
TC
63static void
64error_handler(png_structp png_ptr, png_const_charp msg) {
65 mm_log((1, "PNG error: '%s'\n", msg));
66
67 i_push_error(0, msg);
68 longjmp(png_jmpbuf(png_ptr), 1);
69}
70
71/*
72
963d3602
TC
73 For writing a warning might have information about an error, so send
74 it to the error stack.
38eab175
TC
75
76*/
77static void
78write_warn_handler(png_structp png_ptr, png_const_charp msg) {
79 mm_log((1, "PNG write warning '%s'\n", msg));
80
81 i_push_error(0, msg);
82}
83
84#define PNG_DIM_MAX 0x7fffffffL
85
02d1d628 86undef_int
790923a4 87i_writepng_wiol(i_img *im, io_glue *ig) {
02d1d628 88 png_structp png_ptr;
a807aae6 89 png_infop info_ptr = NULL;
38eab175 90 i_img_dim width,height,y;
02d1d628 91 volatile int cspace,channels;
faa9b3e7
TC
92 double xres, yres;
93 int aspect_only, have_res;
34e56776
TC
94 unsigned char *data;
95 unsigned char * volatile vdata = NULL;
21c6936b 96 int bits;
b8961d05 97 int is_bilevel = 0, zero_is_white;
02d1d628 98
790923a4 99 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
8d14daab
TC
100
101 i_clear_error();
102
103 if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
104 i_push_error(0, "image too large for PNG");
105 return 0;
106 }
107
790923a4
AMH
108 height = im->ysize;
109 width = im->xsize;
02d1d628 110
38eab175
TC
111 /* if we ever have 64-bit i_img_dim
112 * the libpng docs state that png_set_user_limits() can be used to
113 * override the PNG_USER_*_MAX limits, but as implemented they
114 * don't. We check against the theoretical limit of PNG here, and
115 * try to override the limits below, in case the libpng
116 * implementation ever matches the documentation.
117 *
118 * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
647508aa 119 * fixed in libpng 1.5.3
34e56776 120 */
38eab175
TC
121 if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
122 i_push_error(0, "Image too large for PNG");
123 return 0;
124 }
125
02d1d628
AMH
126 channels=im->channels;
127
b8961d05
TC
128 if (i_img_is_monochrome(im, &zero_is_white)) {
129 is_bilevel = 1;
130 bits = 1;
131 cspace = PNG_COLOR_TYPE_GRAY;
132 mm_log((1, "i_writepng: bilevel output\n"));
133 }
134 else if (im->type == i_palette_type) {
21c6936b
TC
135 int colors = i_colorcount(im);
136
137 cspace = PNG_COLOR_TYPE_PALETTE;
138 bits = 1;
139 while ((1 << bits) < colors) {
140 bits += bits;
141 }
b8961d05 142 mm_log((1, "i_writepng: paletted output\n"));
21c6936b
TC
143 }
144 else {
145 switch (channels) {
146 case 1:
147 cspace = PNG_COLOR_TYPE_GRAY;
148 break;
149 case 2:
150 cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
151 break;
152 case 3:
153 cspace = PNG_COLOR_TYPE_RGB;
154 break;
155 case 4:
156 cspace = PNG_COLOR_TYPE_RGB_ALPHA;
157 break;
158 default:
159 fprintf(stderr, "Internal error, channels = %d\n", channels);
160 abort();
161 }
d9610331 162 bits = im->bits > 8 ? 16 : 8;
b8961d05 163 mm_log((1, "i_writepng: direct output\n"));
21c6936b 164 }
02d1d628 165
b8961d05 166 mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
02d1d628
AMH
167
168 /* Create and initialize the png_struct with the desired error handler
169 * functions. If you want to use the default stderr and longjump method,
170 * you can supply NULL for the last three parameters. We also check that
171 * the library version is compatible with the one used at compile time,
172 * in case we are using dynamically linked libraries. REQUIRED.
173 */
174
38eab175
TC
175 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
176 error_handler, write_warn_handler);
02d1d628 177
790923a4
AMH
178 if (png_ptr == NULL) return 0;
179
02d1d628
AMH
180
181 /* Allocate/initialize the image information data. REQUIRED */
182 info_ptr = png_create_info_struct(png_ptr);
183
184 if (info_ptr == NULL) {
a807aae6 185 png_destroy_write_struct(&png_ptr, &info_ptr);
790923a4 186 return 0;
02d1d628
AMH
187 }
188
189 /* Set error handling. REQUIRED if you aren't supplying your own
190 * error hadnling functions in the png_create_write_struct() call.
191 */
f5b4354e 192 if (setjmp(png_jmpbuf(png_ptr))) {
a807aae6 193 png_destroy_write_struct(&png_ptr, &info_ptr);
34e56776
TC
194 if (vdata)
195 myfree(vdata);
02d1d628
AMH
196 return(0);
197 }
198
790923a4 199 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
02d1d628
AMH
200
201 /* Set the image information here. Width and height are up to 2^31,
202 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
203 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
204 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
205 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
206 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
207 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
208 */
209
38eab175
TC
210 /* by default, libpng (not PNG) limits the image size to a maximum
211 * 1000000 pixels in each direction, but Imager doesn't.
212 * Configure libpng to avoid that limit.
213 */
214 png_set_user_limits(png_ptr, width, height);
215
21c6936b
TC
216 mm_log((1, ">png_set_IHDR\n"));
217 png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
02d1d628 218 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
21c6936b 219 mm_log((1, "<png_set_IHDR\n"));
02d1d628 220
faa9b3e7
TC
221 have_res = 1;
222 if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
223 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
224 ; /* nothing to do */
225 else
226 yres = xres;
227 }
228 else {
229 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
230 xres = yres;
231 else
232 have_res = 0;
233 }
234 if (have_res) {
235 aspect_only = 0;
236 i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
237 xres /= 0.0254;
238 yres /= 0.0254;
239 png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
240 aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
241 }
242
b8961d05
TC
243 if (is_bilevel) {
244 if (!write_bilevel(png_ptr, info_ptr, im)) {
245 png_destroy_write_struct(&png_ptr, &info_ptr);
246 return 0;
247 }
248 }
249 else if (im->type == i_palette_type) {
21c6936b
TC
250 if (!write_paletted(png_ptr, info_ptr, im, bits)) {
251 png_destroy_write_struct(&png_ptr, &info_ptr);
252 return 0;
253 }
254 }
d9610331
TC
255 else if (bits == 16) {
256 if (!write_direct16(png_ptr, info_ptr, im)) {
257 png_destroy_write_struct(&png_ptr, &info_ptr);
258 return 0;
259 }
260 }
261 else {
21c6936b
TC
262 if (!write_direct8(png_ptr, info_ptr, im)) {
263 png_destroy_write_struct(&png_ptr, &info_ptr);
264 return 0;
265 }
faa9b3e7 266 }
790923a4 267
02d1d628 268 png_write_end(png_ptr, info_ptr);
790923a4 269
a807aae6 270 png_destroy_write_struct(&png_ptr, &info_ptr);
02d1d628 271
6d5c85a2
TC
272 if (i_io_close(ig))
273 return 0;
10461f9a 274
02d1d628
AMH
275 return(1);
276}
277
38eab175 278static void
79b4c849 279get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth);
38eab175
TC
280
281typedef struct {
282 char *warnings;
283} i_png_read_state, *i_png_read_statep;
02d1d628 284
38eab175
TC
285static void
286read_warn_handler(png_structp, png_const_charp);
02d1d628 287
38eab175
TC
288static void
289cleanup_read_state(i_png_read_statep);
02d1d628
AMH
290
291i_img*
1d7e3124 292i_readpng_wiol(io_glue *ig) {
02576e8d 293 i_img *im = NULL;
02d1d628
AMH
294 png_structp png_ptr;
295 png_infop info_ptr;
790923a4 296 png_uint_32 width, height;
02d1d628 297 int bit_depth, color_type, interlace_type;
432d0ff0 298 int channels;
02d1d628 299 unsigned int sig_read;
38eab175 300 i_png_read_state rs;
02d1d628 301
38eab175 302 rs.warnings = NULL;
790923a4 303 sig_read = 0;
02d1d628 304
1d7e3124 305 mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
38eab175 306 i_clear_error();
02d1d628 307
38eab175
TC
308 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs,
309 error_handler, read_warn_handler);
310 if (!png_ptr) {
311 i_push_error(0, "Cannot create PNG read structure");
312 return NULL;
313 }
790923a4 314 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
02d1d628
AMH
315
316 info_ptr = png_create_info_struct(png_ptr);
317 if (info_ptr == NULL) {
318 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
38eab175 319 i_push_error(0, "Cannot create PNG info structure");
02d1d628
AMH
320 return NULL;
321 }
322
f5b4354e 323 if (setjmp(png_jmpbuf(png_ptr))) {
02576e8d 324 if (im) i_img_destroy(im);
790923a4 325 mm_log((1,"i_readpng_wiol: error.\n"));
02d1d628 326 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
38eab175 327 cleanup_read_state(&rs);
02d1d628
AMH
328 return NULL;
329 }
647508aa
TC
330
331 /* we do our own limit checks */
332 png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
790923a4 333
02d1d628
AMH
334 png_set_sig_bytes(png_ptr, sig_read);
335 png_read_info(png_ptr, info_ptr);
336 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
337
338 mm_log((1,
790923a4 339 "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
02d1d628
AMH
340 width,height,bit_depth,color_type,interlace_type));
341
342 CC2C[PNG_COLOR_TYPE_GRAY]=1;
343 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
344 CC2C[PNG_COLOR_TYPE_RGB]=3;
345 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
346 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
790923a4
AMH
347 channels = CC2C[color_type];
348
349 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
350
77157728
TC
351 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
352 mm_log((1, "i_readpnm: image size exceeds limits\n"));
353 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
354 return NULL;
355 }
356
963d3602
TC
357 if (color_type == PNG_COLOR_TYPE_PALETTE) {
358 im = read_paletted(png_ptr, info_ptr, channels, width, height);
359 }
79b4c849
TC
360 else if (color_type == PNG_COLOR_TYPE_GRAY
361 && bit_depth == 1
362 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
363 im = read_bilevel(png_ptr, info_ptr, width, height);
364 }
c631b2d5
TC
365 else if (bit_depth == 16) {
366 im = read_direct16(png_ptr, info_ptr, channels, width, height);
367 }
963d3602
TC
368 else {
369 im = read_direct8(png_ptr, info_ptr, channels, width, height);
370 }
cfb628e2
TC
371
372 if (im)
79b4c849 373 get_png_tags(im, png_ptr, info_ptr, bit_depth);
cfb628e2
TC
374
375 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
376
377 if (im) {
378 if (rs.warnings) {
379 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
380 }
381 }
382 cleanup_read_state(&rs);
383
384 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
385
386 return im;
387}
388
389static i_img *
390read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
391 i_img_dim width, i_img_dim height) {
392 i_img * volatile vim = NULL;
393 int color_type = png_get_color_type(png_ptr, info_ptr);
394 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
395 i_img_dim y;
396 int number_passes, pass;
397 i_img *im;
34e56776
TC
398 unsigned char *line;
399 unsigned char * volatile vline = NULL;
cfb628e2
TC
400
401 if (setjmp(png_jmpbuf(png_ptr))) {
402 if (vim) i_img_destroy(vim);
34e56776 403 if (vline) myfree(vline);
cfb628e2
TC
404
405 return NULL;
406 }
407
408 number_passes = png_set_interlace_handling(png_ptr);
409 mm_log((1,"number of passes=%d\n",number_passes));
410
02d1d628
AMH
411 png_set_strip_16(png_ptr);
412 png_set_packing(png_ptr);
6829f5f6 413
cfb628e2
TC
414 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
415 png_set_expand(png_ptr);
416
6829f5f6
AMH
417 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
418 channels++;
419 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
420 png_set_expand(png_ptr);
421 }
790923a4 422
02d1d628 423 png_read_update_info(png_ptr, info_ptr);
790923a4 424
cfb628e2 425 im = vim = i_img_8_new(width,height,channels);
352c64ed
TC
426 if (!im) {
427 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
428 return NULL;
429 }
02d1d628 430
34e56776 431 line = vline = mymalloc(channels * width);
cfb628e2
TC
432 for (pass = 0; pass < number_passes; pass++) {
433 for (y = 0; y < height; y++) {
34e56776
TC
434 if (pass > 0)
435 i_gsamp(im, 0, width, y, line, NULL, channels);
436 png_read_row(png_ptr,(png_bytep)line, NULL);
437 i_psamp(im, 0, width, y, line, NULL, channels);
cfb628e2 438 }
38eab175 439 }
34e56776
TC
440 myfree(line);
441 vline = NULL;
02d1d628 442
cfb628e2
TC
443 png_read_end(png_ptr, info_ptr);
444
790923a4
AMH
445 return im;
446}
faa9b3e7 447
c631b2d5
TC
448static i_img *
449read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
450 i_img_dim width, i_img_dim height) {
451 i_img * volatile vim = NULL;
c631b2d5
TC
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 unsigned *bits_line;
458 unsigned * volatile vbits_line = NULL;
459 size_t row_bytes;
460
461 if (setjmp(png_jmpbuf(png_ptr))) {
462 if (vim) i_img_destroy(vim);
463 if (vline) myfree(vline);
464 if (vbits_line) myfree(vbits_line);
465
466 return NULL;
467 }
468
469 number_passes = png_set_interlace_handling(png_ptr);
470 mm_log((1,"number of passes=%d\n",number_passes));
471
472 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
473 channels++;
474 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
475 png_set_expand(png_ptr);
476 }
477
478 png_read_update_info(png_ptr, info_ptr);
479
480 im = vim = i_img_16_new(width,height,channels);
481 if (!im) {
482 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
483 return NULL;
484 }
485
486 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
487 line = vline = mymalloc(row_bytes);
488 memset(line, 0, row_bytes);
489 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
490 for (pass = 0; pass < number_passes; pass++) {
491 for (y = 0; y < height; y++) {
492 if (pass > 0) {
493 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
494 for (x = 0; x < width * channels; ++x) {
495 line[x*2] = bits_line[x] >> 8;
496 line[x*2+1] = bits_line[x] & 0xff;
497 }
498 }
499 png_read_row(png_ptr,(png_bytep)line, NULL);
500 for (x = 0; x < width * channels; ++x)
501 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
502 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
503 }
504 }
505 myfree(line);
506 myfree(bits_line);
507 vline = NULL;
508 vbits_line = NULL;
509
510 png_read_end(png_ptr, info_ptr);
511
512 return im;
513}
514
79b4c849
TC
515static i_img *
516read_bilevel(png_structp png_ptr, png_infop info_ptr,
517 i_img_dim width, i_img_dim height) {
518 i_img * volatile vim = NULL;
519 i_img_dim x, y;
520 int number_passes, pass;
521 i_img *im;
522 unsigned char *line;
523 unsigned char * volatile vline = NULL;
524 i_color palette[2];
525
526 if (setjmp(png_jmpbuf(png_ptr))) {
527 if (vim) i_img_destroy(vim);
528 if (vline) myfree(vline);
529
530 return NULL;
531 }
532
533 number_passes = png_set_interlace_handling(png_ptr);
534 mm_log((1,"number of passes=%d\n",number_passes));
535
536 png_set_packing(png_ptr);
537
538 png_set_expand(png_ptr);
539
540 png_read_update_info(png_ptr, info_ptr);
541
542 im = vim = i_img_pal_new(width, height, 1, 256);
543 if (!im) {
544 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
545 return NULL;
546 }
547
548 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
549 palette[0].channel[3] = 0;
550 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
551 palette[1].channel[3] = 255;
552 i_addcolors(im, palette, 2);
553
554 line = vline = mymalloc(width);
555 memset(line, 0, width);
556 for (pass = 0; pass < number_passes; pass++) {
557 for (y = 0; y < height; y++) {
558 if (pass > 0) {
559 i_gpal(im, 0, width, y, line);
560 /* expand indexes back to 0/255 */
561 for (x = 0; x < width; ++x)
562 line[x] = line[x] ? 255 : 0;
563 }
564 png_read_row(png_ptr,(png_bytep)line, NULL);
565
566 /* back to palette indexes */
567 for (x = 0; x < width; ++x)
568 line[x] = line[x] ? 1 : 0;
569 i_ppal(im, 0, width, y, line);
570 }
571 }
572 myfree(line);
573 vline = NULL;
574
575 png_read_end(png_ptr, info_ptr);
576
577 return im;
578}
579
580/* FIXME: do we need to unscale palette color values from the
581 supplied alphas? */
963d3602
TC
582static i_img *
583read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
584 i_img_dim width, i_img_dim height) {
585 i_img * volatile vim = NULL;
586 int color_type = png_get_color_type(png_ptr, info_ptr);
587 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
588 i_img_dim y;
589 int number_passes, pass;
590 i_img *im;
591 unsigned char *line;
592 unsigned char * volatile vline = NULL;
593 int num_palette, i;
594 png_colorp png_palette;
595 png_bytep png_pal_trans;
596 png_color_16p png_color_trans;
597 int num_pal_trans;
598
599 if (setjmp(png_jmpbuf(png_ptr))) {
600 if (vim) i_img_destroy(vim);
601 if (vline) myfree(vline);
602
603 return NULL;
604 }
605
606 number_passes = png_set_interlace_handling(png_ptr);
607 mm_log((1,"number of passes=%d\n",number_passes));
608
609 png_set_strip_16(png_ptr);
610 png_set_packing(png_ptr);
611
612 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
613 png_set_expand(png_ptr);
614
615 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
616 i_push_error(0, "Paletted image with no PLTE chunk");
617 return NULL;
618 }
619
620 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
621 &png_color_trans)) {
622 channels++;
623 }
624 else {
625 num_pal_trans = 0;
626 }
627
628 png_read_update_info(png_ptr, info_ptr);
629
630 im = vim = i_img_pal_new(width, height, channels, 256);
631 if (!im) {
632 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
633 return NULL;
634 }
635
636 for (i = 0; i < num_palette; ++i) {
637 i_color c;
638
639 c.rgba.r = png_palette[i].red;
640 c.rgba.g = png_palette[i].green;
641 c.rgba.b = png_palette[i].blue;
642 if (i < num_pal_trans)
643 c.rgba.a = png_pal_trans[i];
644 else
645 c.rgba.a = 255;
646 i_addcolors(im, &c, 1);
647 }
648
649 line = vline = mymalloc(width);
650 for (pass = 0; pass < number_passes; pass++) {
651 for (y = 0; y < height; y++) {
652 if (pass > 0)
653 i_gpal(im, 0, width, y, line);
654 png_read_row(png_ptr,(png_bytep)line, NULL);
655 i_ppal(im, 0, width, y, line);
656 }
657 }
658 myfree(line);
659 vline = NULL;
660
661 png_read_end(png_ptr, info_ptr);
662
663 return im;
664}
665
b51bcea5
TC
666struct png_text_name {
667 const char *keyword;
668 const char *tagname;
669};
670
671static const struct png_text_name
672text_tags[] = {
673 { "Author", "png_author" },
674 { "Comment", "i_comment" },
675 { "Copyright", "png_copyright" },
676 { "Creation Time", "png_creation_time" },
677 { "Description", "png_description" },
678 { "Disclaimer", "png_disclaimer" },
679 { "Software", "png_software" },
680 { "Source", "png_source" },
681 { "Title", "png_title" },
682 { "Warning", "png_warning" }
683};
684
685static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
686
38eab175 687static void
79b4c849 688get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth) {
faa9b3e7
TC
689 png_uint_32 xres, yres;
690 int unit_type;
352c64ed 691
1d7e3124 692 i_tags_set(&im->tags, "i_format", "png", -1);
faa9b3e7
TC
693 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
694 mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
695 if (unit_type == PNG_RESOLUTION_METER) {
2e41e30b
TC
696 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
697 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
faa9b3e7
TC
698 }
699 else {
1d7e3124
TC
700 i_tags_setn(&im->tags, "i_xres", xres);
701 i_tags_setn(&im->tags, "i_yres", yres);
702 i_tags_setn(&im->tags, "i_aspect_only", 1);
faa9b3e7
TC
703 }
704 }
a4fa5d5e
TC
705 {
706 int interlace = png_get_interlace_type(png_ptr, info_ptr);
707
708 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
709 switch (interlace) {
710 case PNG_INTERLACE_NONE:
711 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
712 break;
713
714 case PNG_INTERLACE_ADAM7:
715 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
716 break;
717
718 default:
719 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
720 break;
721 }
00cff942
TC
722 }
723
79b4c849
TC
724 /* the various readers can call png_set_expand(), libpng will make
725 it's internal record of bit_depth at least 8 in that case */
726 i_tags_setn(&im->tags, "png_bits", bit_depth);
b51bcea5
TC
727
728
729 {
730 int intent;
731 if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
732 i_tags_setn(&im->tags, "png_srgb_intent", intent);
733 }
734 }
735 {
736 double gamma;
737 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
738 i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
739 }
740 }
741 {
742 double white_x, white_y;
743 double red_x, red_y;
744 double green_x, green_y;
745 double blue_x, blue_y;
746 if (png_get_cHRM(png_ptr, info_ptr, &white_x, &white_y,
747 &red_x, &red_y, &green_x, &green_y,
748 &blue_x, &blue_y)) {
749 i_tags_set_float2(&im->tags, "png_chroma_white_x", 0, white_x, 4);
750 i_tags_set_float2(&im->tags, "png_chroma_white_y", 0, white_y, 4);
751 i_tags_set_float2(&im->tags, "png_chroma_red_x", 0, red_x, 4);
752 i_tags_set_float2(&im->tags, "png_chroma_red_y", 0, red_y, 4);
753 i_tags_set_float2(&im->tags, "png_chroma_green_x", 0, green_x, 4);
754 i_tags_set_float2(&im->tags, "png_chroma_green_y", 0, green_y, 4);
755 i_tags_set_float2(&im->tags, "png_chroma_blue_x", 0, blue_x, 4);
756 i_tags_set_float2(&im->tags, "png_chroma_blue_y", 0, blue_y, 4);
757 }
758 }
759
760 {
761 int num_text;
762 png_text *text;
763
764 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
765 int i;
766 for (i = 0; i < num_text; ++i) {
767 int j;
768 char tag_name[50];
769 sprintf(tag_name, "png_text%d_key", i);
770 i_tags_set(&im->tags, tag_name, text[i].key, -1);
771 sprintf(tag_name, "png_text%d_text", i);
772 i_tags_set(&im->tags, tag_name, text[i].text, -1);
773 sprintf(tag_name, "png_text%d_type", i);
774 i_tags_set(&im->tags, tag_name,
775 (text[i].compression == PNG_TEXT_COMPRESSION_NONE
776 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
777 "text" : "itxt", -1);
778
779 for (j = 0; j < text_tags_count; ++j) {
780 if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
781 i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
782 break;
783 }
784 }
785 }
786 }
787 }
faa9b3e7 788}
38eab175 789
81089826
TC
790static int
791write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
792 unsigned char *data, *volatile vdata = NULL;
793 i_img_dim y;
794
795 if (setjmp(png_jmpbuf(png_ptr))) {
796 if (vdata)
797 myfree(vdata);
798
799 return 0;
800 }
801
21c6936b
TC
802 png_write_info(png_ptr, info_ptr);
803
81089826
TC
804 vdata = data = mymalloc(im->xsize * im->channels);
805 for (y = 0; y < im->ysize; y++) {
806 i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
807 png_write_row(png_ptr, (png_bytep)data);
808 }
809 myfree(data);
810
811 return 1;
812}
813
d9610331
TC
814static int
815write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
816 unsigned *data, *volatile vdata = NULL;
817 unsigned char *tran_data, * volatile vtran_data = NULL;
818 i_img_dim samples_per_row = im->xsize * im->channels;
819
820 i_img_dim y;
821
822 if (setjmp(png_jmpbuf(png_ptr))) {
823 if (vdata)
824 myfree(vdata);
825 if (vtran_data)
826 myfree(vtran_data);
827
828 return 0;
829 }
830
831 png_write_info(png_ptr, info_ptr);
832
833 vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
834 vtran_data = tran_data = mymalloc(samples_per_row * 2);
835 for (y = 0; y < im->ysize; y++) {
836 i_img_dim i;
837 unsigned char *p = tran_data;
838 i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
839 for (i = 0; i < samples_per_row; ++i) {
840 p[0] = data[i] >> 8;
841 p[1] = data[i] & 0xff;
842 p += 2;
843 }
844 png_write_row(png_ptr, (png_bytep)tran_data);
845 }
846 myfree(tran_data);
847 myfree(data);
848
849 return 1;
850}
851
21c6936b
TC
852static int
853write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
854 unsigned char *data, *volatile vdata = NULL;
855 i_img_dim y;
856 unsigned char pal_map[256];
857 png_color pcolors[256];
858 i_color colors[256];
859 int count = i_colorcount(im);
860 int i;
861
862 if (setjmp(png_jmpbuf(png_ptr))) {
863 if (vdata)
864 myfree(vdata);
865
866 return 0;
867 }
868
869 i_getcolors(im, 0, colors, count);
870 if (im->channels < 3) {
871 /* convert the greyscale palette to color */
872 int i;
873 for (i = 0; i < count; ++i) {
874 i_color *c = colors + i;
875 c->channel[3] = c->channel[1];
876 c->channel[2] = c->channel[1] = c->channel[0];
877 }
878 }
879
880 if (i_img_has_alpha(im)) {
881 int i;
882 int bottom_index = 0, top_index = count-1;
883
884 /* fill out the palette map */
885 for (i = 0; i < count; ++i)
886 pal_map[i] = i;
887
888 /* the PNG spec suggests sorting the palette by alpha, but that's
889 unnecessary - all we want to do is move the opaque entries to
890 the end */
891 while (bottom_index < top_index) {
892 if (colors[bottom_index].rgba.a == 255) {
893 pal_map[bottom_index] = top_index;
894 pal_map[top_index--] = bottom_index;
895 }
896 ++bottom_index;
897 }
898 }
899
900 for (i = 0; i < count; ++i) {
901 int srci = i_img_has_alpha(im) ? pal_map[i] : i;
902
903 pcolors[i].red = colors[srci].rgb.r;
904 pcolors[i].green = colors[srci].rgb.g;
905 pcolors[i].blue = colors[srci].rgb.b;
906 }
907
908 png_set_PLTE(png_ptr, info_ptr, pcolors, count);
909
910 if (i_img_has_alpha(im)) {
911 unsigned char trans[256];
912 int i;
913
914 for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
915 trans[i] = colors[pal_map[i]].rgba.a;
916 }
917 png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
918 }
919
920 png_write_info(png_ptr, info_ptr);
921
922 png_set_packing(png_ptr);
923
924 vdata = data = mymalloc(im->xsize);
925 for (y = 0; y < im->ysize; y++) {
926 i_gpal(im, 0, im->xsize, y, data);
927 if (i_img_has_alpha(im)) {
928 i_img_dim x;
929 for (x = 0; x < im->xsize; ++x)
930 data[x] = pal_map[data[x]];
931 }
932 png_write_row(png_ptr, (png_bytep)data);
933 }
934 myfree(data);
935
936 return 1;
937}
938
b8961d05
TC
939static int
940write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
941 unsigned char *data, *volatile vdata = NULL;
942 i_img_dim y;
21c6936b 943
b8961d05
TC
944 if (setjmp(png_jmpbuf(png_ptr))) {
945 if (vdata)
946 myfree(vdata);
947
948 return 0;
21c6936b 949 }
21c6936b 950
b8961d05
TC
951 png_write_info(png_ptr, info_ptr);
952
953 png_set_packing(png_ptr);
954
955 vdata = data = mymalloc(im->xsize);
956 for (y = 0; y < im->ysize; y++) {
957 i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
958 png_write_row(png_ptr, (png_bytep)data);
959 }
960 myfree(data);
961
962 return 1;
963}
21c6936b 964
38eab175
TC
965static void
966read_warn_handler(png_structp png_ptr, png_const_charp msg) {
967 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
968 char *workp;
969 size_t new_size;
970
971 mm_log((1, "PNG read warning '%s'\n", msg));
972
973 /* in case this is part of an error report */
974 i_push_error(0, msg);
975
976 /* and save in the warnings so if we do manage to succeed, we
977 * can save it as a tag
978 */
979 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
980 + 1 /* NUL */
981 + strlen(msg) /* new text */
982 + 1; /* newline */
983 workp = myrealloc(rs->warnings, new_size);
984 if (!rs->warnings)
985 *workp = '\0';
986 strcat(workp, msg);
987 strcat(workp, "\n");
988 rs->warnings = workp;
989}
990
991static void
992cleanup_read_state(i_png_read_statep rs) {
993 if (rs->warnings)
994 myfree(rs->warnings);
995}