update Changes
[imager.git] / PNG / impng.c
CommitLineData
1d7e3124 1#include "impng.h"
02d1d628 2#include "png.h"
21c6936b 3#include <stdlib.h>
57520a19 4#include <string.h>
02d1d628 5
02d1d628
AMH
6/* this is a way to get number of channels from color space
7 * Color code to channel number */
8
b33c08f8 9static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
02d1d628
AMH
10
11#define PNG_BYTES_TO_CHECK 4
cfb628e2
TC
12
13static i_img *
14read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
15
c631b2d5
TC
16static i_img *
17read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
18
963d3602
TC
19static i_img *
20read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
21
79b4c849
TC
22static i_img *
23read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
24
81089826
TC
25static int
26write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
27
d9610331
TC
28static int
29write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
30
21c6936b
TC
31static int
32write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
33
b8961d05
TC
34static int
35write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im);
21c6936b 36
d0f15206
TC
37static void
38get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth, int color_type);
39
40static int
41set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
42
43static const char *
44get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size);
45
647508aa
TC
46unsigned
47i_png_lib_version(void) {
48 return png_access_version_number();
49}
02d1d628 50
02d1d628 51static void
790923a4 52wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
f5b4354e 53 io_glue *ig = png_get_io_ptr(png_ptr);
38eab175 54 ssize_t rc = i_io_read(ig, data, length);
790923a4 55 if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
02d1d628
AMH
56}
57
02d1d628 58static void
790923a4 59wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
38eab175 60 ssize_t rc;
f5b4354e 61 io_glue *ig = png_get_io_ptr(png_ptr);
6d5c85a2 62 rc = i_io_write(ig, data, length);
790923a4 63 if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
02d1d628
AMH
64}
65
66static void
790923a4 67wiol_flush_data(png_structp png_ptr) {
6352c169
TC
68 io_glue *ig = png_get_io_ptr(png_ptr);
69 if (!i_io_flush(ig))
70 png_error(png_ptr, "Error flushing output");
02d1d628 71}
02d1d628 72
38eab175
TC
73static void
74error_handler(png_structp png_ptr, png_const_charp msg) {
75 mm_log((1, "PNG error: '%s'\n", msg));
76
77 i_push_error(0, msg);
78 longjmp(png_jmpbuf(png_ptr), 1);
79}
80
81/*
82
963d3602
TC
83 For writing a warning might have information about an error, so send
84 it to the error stack.
38eab175
TC
85
86*/
87static void
88write_warn_handler(png_structp png_ptr, png_const_charp msg) {
89 mm_log((1, "PNG write warning '%s'\n", msg));
90
91 i_push_error(0, msg);
92}
93
94#define PNG_DIM_MAX 0x7fffffffL
95
02d1d628 96undef_int
790923a4 97i_writepng_wiol(i_img *im, io_glue *ig) {
02d1d628 98 png_structp png_ptr;
a807aae6 99 png_infop info_ptr = NULL;
0a53238e 100 i_img_dim width,height;
02d1d628 101 volatile int cspace,channels;
21c6936b 102 int bits;
b8961d05 103 int is_bilevel = 0, zero_is_white;
02d1d628 104
790923a4 105 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
8d14daab
TC
106
107 i_clear_error();
108
109 if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) {
110 i_push_error(0, "image too large for PNG");
111 return 0;
112 }
113
790923a4
AMH
114 height = im->ysize;
115 width = im->xsize;
02d1d628 116
38eab175
TC
117 /* if we ever have 64-bit i_img_dim
118 * the libpng docs state that png_set_user_limits() can be used to
119 * override the PNG_USER_*_MAX limits, but as implemented they
120 * don't. We check against the theoretical limit of PNG here, and
121 * try to override the limits below, in case the libpng
122 * implementation ever matches the documentation.
123 *
124 * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624
647508aa 125 * fixed in libpng 1.5.3
34e56776 126 */
38eab175
TC
127 if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) {
128 i_push_error(0, "Image too large for PNG");
129 return 0;
130 }
131
02d1d628
AMH
132 channels=im->channels;
133
b8961d05
TC
134 if (i_img_is_monochrome(im, &zero_is_white)) {
135 is_bilevel = 1;
136 bits = 1;
137 cspace = PNG_COLOR_TYPE_GRAY;
138 mm_log((1, "i_writepng: bilevel output\n"));
139 }
140 else if (im->type == i_palette_type) {
21c6936b
TC
141 int colors = i_colorcount(im);
142
143 cspace = PNG_COLOR_TYPE_PALETTE;
144 bits = 1;
145 while ((1 << bits) < colors) {
146 bits += bits;
147 }
b8961d05 148 mm_log((1, "i_writepng: paletted output\n"));
21c6936b
TC
149 }
150 else {
151 switch (channels) {
152 case 1:
153 cspace = PNG_COLOR_TYPE_GRAY;
154 break;
155 case 2:
156 cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
157 break;
158 case 3:
159 cspace = PNG_COLOR_TYPE_RGB;
160 break;
161 case 4:
162 cspace = PNG_COLOR_TYPE_RGB_ALPHA;
163 break;
164 default:
165 fprintf(stderr, "Internal error, channels = %d\n", channels);
166 abort();
167 }
d9610331 168 bits = im->bits > 8 ? 16 : 8;
b8961d05 169 mm_log((1, "i_writepng: direct output\n"));
21c6936b 170 }
02d1d628 171
b8961d05 172 mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
02d1d628
AMH
173
174 /* Create and initialize the png_struct with the desired error handler
175 * functions. If you want to use the default stderr and longjump method,
176 * you can supply NULL for the last three parameters. We also check that
177 * the library version is compatible with the one used at compile time,
178 * in case we are using dynamically linked libraries. REQUIRED.
179 */
180
38eab175
TC
181 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
182 error_handler, write_warn_handler);
02d1d628 183
790923a4
AMH
184 if (png_ptr == NULL) return 0;
185
02d1d628
AMH
186
187 /* Allocate/initialize the image information data. REQUIRED */
188 info_ptr = png_create_info_struct(png_ptr);
189
190 if (info_ptr == NULL) {
a807aae6 191 png_destroy_write_struct(&png_ptr, &info_ptr);
790923a4 192 return 0;
02d1d628
AMH
193 }
194
195 /* Set error handling. REQUIRED if you aren't supplying your own
196 * error hadnling functions in the png_create_write_struct() call.
197 */
f5b4354e 198 if (setjmp(png_jmpbuf(png_ptr))) {
a807aae6 199 png_destroy_write_struct(&png_ptr, &info_ptr);
02d1d628
AMH
200 return(0);
201 }
202
790923a4 203 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
02d1d628
AMH
204
205 /* Set the image information here. Width and height are up to 2^31,
206 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
207 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
208 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
209 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
210 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
211 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
212 */
213
38eab175
TC
214 /* by default, libpng (not PNG) limits the image size to a maximum
215 * 1000000 pixels in each direction, but Imager doesn't.
216 * Configure libpng to avoid that limit.
217 */
218 png_set_user_limits(png_ptr, width, height);
219
21c6936b 220 png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
02d1d628
AMH
221 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
222
d0f15206
TC
223 if (!set_png_tags(im, png_ptr, info_ptr)) {
224 png_destroy_write_struct(&png_ptr, &info_ptr);
225 return 0;
faa9b3e7
TC
226 }
227
b8961d05
TC
228 if (is_bilevel) {
229 if (!write_bilevel(png_ptr, info_ptr, im)) {
230 png_destroy_write_struct(&png_ptr, &info_ptr);
231 return 0;
232 }
233 }
234 else if (im->type == i_palette_type) {
21c6936b
TC
235 if (!write_paletted(png_ptr, info_ptr, im, bits)) {
236 png_destroy_write_struct(&png_ptr, &info_ptr);
237 return 0;
238 }
239 }
d9610331
TC
240 else if (bits == 16) {
241 if (!write_direct16(png_ptr, info_ptr, im)) {
242 png_destroy_write_struct(&png_ptr, &info_ptr);
243 return 0;
244 }
245 }
246 else {
21c6936b
TC
247 if (!write_direct8(png_ptr, info_ptr, im)) {
248 png_destroy_write_struct(&png_ptr, &info_ptr);
249 return 0;
250 }
faa9b3e7 251 }
790923a4 252
02d1d628 253 png_write_end(png_ptr, info_ptr);
790923a4 254
a807aae6 255 png_destroy_write_struct(&png_ptr, &info_ptr);
02d1d628 256
6d5c85a2
TC
257 if (i_io_close(ig))
258 return 0;
10461f9a 259
02d1d628
AMH
260 return(1);
261}
262
38eab175
TC
263typedef struct {
264 char *warnings;
265} i_png_read_state, *i_png_read_statep;
02d1d628 266
38eab175
TC
267static void
268read_warn_handler(png_structp, png_const_charp);
02d1d628 269
38eab175
TC
270static void
271cleanup_read_state(i_png_read_statep);
02d1d628
AMH
272
273i_img*
57520a19 274i_readpng_wiol(io_glue *ig, int flags) {
02576e8d 275 i_img *im = NULL;
02d1d628
AMH
276 png_structp png_ptr;
277 png_infop info_ptr;
790923a4 278 png_uint_32 width, height;
02d1d628 279 int bit_depth, color_type, interlace_type;
432d0ff0 280 int channels;
02d1d628 281 unsigned int sig_read;
38eab175 282 i_png_read_state rs;
02d1d628 283
38eab175 284 rs.warnings = NULL;
790923a4 285 sig_read = 0;
02d1d628 286
1d7e3124 287 mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
38eab175 288 i_clear_error();
02d1d628 289
38eab175
TC
290 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs,
291 error_handler, read_warn_handler);
292 if (!png_ptr) {
293 i_push_error(0, "Cannot create PNG read structure");
294 return NULL;
295 }
790923a4 296 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
57520a19
TC
297
298#if PNG_LIBPNG_VER >= 10400
299 png_set_benign_errors(png_ptr, (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) ? 1 : 0);
300#else
301 if (flags & IMPNG_READ_IGNORE_BENIGN_ERRORS) {
302 i_push_error(0, "libpng too old to ignore benign errors");
303 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
304 return NULL;
305 }
306#endif
307
02d1d628
AMH
308 info_ptr = png_create_info_struct(png_ptr);
309 if (info_ptr == NULL) {
310 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
38eab175 311 i_push_error(0, "Cannot create PNG info structure");
02d1d628
AMH
312 return NULL;
313 }
314
f5b4354e 315 if (setjmp(png_jmpbuf(png_ptr))) {
02576e8d 316 if (im) i_img_destroy(im);
790923a4 317 mm_log((1,"i_readpng_wiol: error.\n"));
02d1d628 318 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
38eab175 319 cleanup_read_state(&rs);
02d1d628
AMH
320 return NULL;
321 }
57520a19 322
647508aa
TC
323 /* we do our own limit checks */
324 png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX);
790923a4 325
02d1d628
AMH
326 png_set_sig_bytes(png_ptr, sig_read);
327 png_read_info(png_ptr, info_ptr);
328 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
329
5e84d110
TC
330 mm_log((1, "png_get_IHDR results: width %u, height %u, bit_depth %d, color_type %d, interlace_type %d\n",
331 (unsigned)width, (unsigned)height, bit_depth,color_type,interlace_type));
02d1d628
AMH
332
333 CC2C[PNG_COLOR_TYPE_GRAY]=1;
334 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
335 CC2C[PNG_COLOR_TYPE_RGB]=3;
336 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
337 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
790923a4
AMH
338 channels = CC2C[color_type];
339
340 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
341
77157728
TC
342 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
343 mm_log((1, "i_readpnm: image size exceeds limits\n"));
344 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
345 return NULL;
346 }
347
963d3602
TC
348 if (color_type == PNG_COLOR_TYPE_PALETTE) {
349 im = read_paletted(png_ptr, info_ptr, channels, width, height);
350 }
79b4c849
TC
351 else if (color_type == PNG_COLOR_TYPE_GRAY
352 && bit_depth == 1
353 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
354 im = read_bilevel(png_ptr, info_ptr, width, height);
355 }
c631b2d5
TC
356 else if (bit_depth == 16) {
357 im = read_direct16(png_ptr, info_ptr, channels, width, height);
358 }
963d3602
TC
359 else {
360 im = read_direct8(png_ptr, info_ptr, channels, width, height);
361 }
cfb628e2
TC
362
363 if (im)
6fa6c8ee 364 get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
cfb628e2
TC
365
366 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
367
368 if (im) {
369 if (rs.warnings) {
370 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
371 }
372 }
373 cleanup_read_state(&rs);
374
375 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
376
377 return im;
378}
379
380static i_img *
381read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
382 i_img_dim width, i_img_dim height) {
383 i_img * volatile vim = NULL;
384 int color_type = png_get_color_type(png_ptr, info_ptr);
385 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
386 i_img_dim y;
387 int number_passes, pass;
388 i_img *im;
34e56776
TC
389 unsigned char *line;
390 unsigned char * volatile vline = NULL;
cfb628e2
TC
391
392 if (setjmp(png_jmpbuf(png_ptr))) {
393 if (vim) i_img_destroy(vim);
34e56776 394 if (vline) myfree(vline);
cfb628e2
TC
395
396 return NULL;
397 }
398
399 number_passes = png_set_interlace_handling(png_ptr);
400 mm_log((1,"number of passes=%d\n",number_passes));
401
02d1d628
AMH
402 png_set_strip_16(png_ptr);
403 png_set_packing(png_ptr);
6829f5f6 404
cfb628e2
TC
405 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
406 png_set_expand(png_ptr);
407
6829f5f6
AMH
408 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
409 channels++;
410 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
411 png_set_expand(png_ptr);
412 }
790923a4 413
02d1d628 414 png_read_update_info(png_ptr, info_ptr);
790923a4 415
cfb628e2 416 im = vim = i_img_8_new(width,height,channels);
352c64ed
TC
417 if (!im) {
418 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
419 return NULL;
420 }
02d1d628 421
34e56776 422 line = vline = mymalloc(channels * width);
cfb628e2
TC
423 for (pass = 0; pass < number_passes; pass++) {
424 for (y = 0; y < height; y++) {
34e56776
TC
425 if (pass > 0)
426 i_gsamp(im, 0, width, y, line, NULL, channels);
427 png_read_row(png_ptr,(png_bytep)line, NULL);
428 i_psamp(im, 0, width, y, line, NULL, channels);
cfb628e2 429 }
38eab175 430 }
34e56776
TC
431 myfree(line);
432 vline = NULL;
02d1d628 433
cfb628e2
TC
434 png_read_end(png_ptr, info_ptr);
435
790923a4
AMH
436 return im;
437}
faa9b3e7 438
c631b2d5
TC
439static i_img *
440read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
441 i_img_dim width, i_img_dim height) {
442 i_img * volatile vim = NULL;
c631b2d5
TC
443 i_img_dim x, y;
444 int number_passes, pass;
445 i_img *im;
446 unsigned char *line;
447 unsigned char * volatile vline = NULL;
448 unsigned *bits_line;
449 unsigned * volatile vbits_line = NULL;
450 size_t row_bytes;
451
452 if (setjmp(png_jmpbuf(png_ptr))) {
453 if (vim) i_img_destroy(vim);
454 if (vline) myfree(vline);
455 if (vbits_line) myfree(vbits_line);
456
457 return NULL;
458 }
459
460 number_passes = png_set_interlace_handling(png_ptr);
461 mm_log((1,"number of passes=%d\n",number_passes));
462
463 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
464 channels++;
465 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
466 png_set_expand(png_ptr);
467 }
468
469 png_read_update_info(png_ptr, info_ptr);
470
471 im = vim = i_img_16_new(width,height,channels);
472 if (!im) {
473 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
474 return NULL;
475 }
476
477 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
478 line = vline = mymalloc(row_bytes);
479 memset(line, 0, row_bytes);
480 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
481 for (pass = 0; pass < number_passes; pass++) {
482 for (y = 0; y < height; y++) {
483 if (pass > 0) {
484 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
485 for (x = 0; x < width * channels; ++x) {
486 line[x*2] = bits_line[x] >> 8;
487 line[x*2+1] = bits_line[x] & 0xff;
488 }
489 }
490 png_read_row(png_ptr,(png_bytep)line, NULL);
491 for (x = 0; x < width * channels; ++x)
492 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
493 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
494 }
495 }
496 myfree(line);
497 myfree(bits_line);
498 vline = NULL;
499 vbits_line = NULL;
500
501 png_read_end(png_ptr, info_ptr);
502
503 return im;
504}
505
79b4c849
TC
506static i_img *
507read_bilevel(png_structp png_ptr, png_infop info_ptr,
508 i_img_dim width, i_img_dim height) {
509 i_img * volatile vim = NULL;
510 i_img_dim x, y;
511 int number_passes, pass;
512 i_img *im;
513 unsigned char *line;
514 unsigned char * volatile vline = NULL;
515 i_color palette[2];
516
517 if (setjmp(png_jmpbuf(png_ptr))) {
518 if (vim) i_img_destroy(vim);
519 if (vline) myfree(vline);
520
521 return NULL;
522 }
523
524 number_passes = png_set_interlace_handling(png_ptr);
525 mm_log((1,"number of passes=%d\n",number_passes));
526
527 png_set_packing(png_ptr);
528
529 png_set_expand(png_ptr);
530
531 png_read_update_info(png_ptr, info_ptr);
532
533 im = vim = i_img_pal_new(width, height, 1, 256);
534 if (!im) {
535 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
536 return NULL;
537 }
538
539 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
540 palette[0].channel[3] = 0;
541 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
542 palette[1].channel[3] = 255;
543 i_addcolors(im, palette, 2);
544
545 line = vline = mymalloc(width);
546 memset(line, 0, width);
547 for (pass = 0; pass < number_passes; pass++) {
548 for (y = 0; y < height; y++) {
549 if (pass > 0) {
550 i_gpal(im, 0, width, y, line);
551 /* expand indexes back to 0/255 */
552 for (x = 0; x < width; ++x)
553 line[x] = line[x] ? 255 : 0;
554 }
555 png_read_row(png_ptr,(png_bytep)line, NULL);
556
557 /* back to palette indexes */
558 for (x = 0; x < width; ++x)
559 line[x] = line[x] ? 1 : 0;
560 i_ppal(im, 0, width, y, line);
561 }
562 }
563 myfree(line);
564 vline = NULL;
565
566 png_read_end(png_ptr, info_ptr);
567
568 return im;
569}
570
571/* FIXME: do we need to unscale palette color values from the
572 supplied alphas? */
963d3602
TC
573static i_img *
574read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
575 i_img_dim width, i_img_dim height) {
576 i_img * volatile vim = NULL;
577 int color_type = png_get_color_type(png_ptr, info_ptr);
578 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
579 i_img_dim y;
580 int number_passes, pass;
581 i_img *im;
582 unsigned char *line;
583 unsigned char * volatile vline = NULL;
584 int num_palette, i;
585 png_colorp png_palette;
586 png_bytep png_pal_trans;
587 png_color_16p png_color_trans;
588 int num_pal_trans;
589
590 if (setjmp(png_jmpbuf(png_ptr))) {
591 if (vim) i_img_destroy(vim);
592 if (vline) myfree(vline);
593
594 return NULL;
595 }
596
597 number_passes = png_set_interlace_handling(png_ptr);
598 mm_log((1,"number of passes=%d\n",number_passes));
599
600 png_set_strip_16(png_ptr);
601 png_set_packing(png_ptr);
602
603 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
604 png_set_expand(png_ptr);
605
606 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
607 i_push_error(0, "Paletted image with no PLTE chunk");
608 return NULL;
609 }
610
611 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
612 &png_color_trans)) {
613 channels++;
614 }
615 else {
616 num_pal_trans = 0;
617 }
618
619 png_read_update_info(png_ptr, info_ptr);
620
621 im = vim = i_img_pal_new(width, height, channels, 256);
622 if (!im) {
623 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
624 return NULL;
625 }
626
627 for (i = 0; i < num_palette; ++i) {
628 i_color c;
629
630 c.rgba.r = png_palette[i].red;
631 c.rgba.g = png_palette[i].green;
632 c.rgba.b = png_palette[i].blue;
633 if (i < num_pal_trans)
634 c.rgba.a = png_pal_trans[i];
635 else
636 c.rgba.a = 255;
637 i_addcolors(im, &c, 1);
638 }
639
640 line = vline = mymalloc(width);
641 for (pass = 0; pass < number_passes; pass++) {
642 for (y = 0; y < height; y++) {
643 if (pass > 0)
644 i_gpal(im, 0, width, y, line);
645 png_read_row(png_ptr,(png_bytep)line, NULL);
646 i_ppal(im, 0, width, y, line);
647 }
648 }
649 myfree(line);
650 vline = NULL;
651
652 png_read_end(png_ptr, info_ptr);
653
654 return im;
655}
656
b51bcea5
TC
657struct png_text_name {
658 const char *keyword;
659 const char *tagname;
660};
661
662static const struct png_text_name
663text_tags[] = {
664 { "Author", "png_author" },
665 { "Comment", "i_comment" },
666 { "Copyright", "png_copyright" },
667 { "Creation Time", "png_creation_time" },
668 { "Description", "png_description" },
669 { "Disclaimer", "png_disclaimer" },
670 { "Software", "png_software" },
671 { "Source", "png_source" },
672 { "Title", "png_title" },
673 { "Warning", "png_warning" }
674};
675
676static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
677
d0f15206
TC
678static const char * const
679chroma_tags[] = {
680 "png_chroma_white_x",
681 "png_chroma_white_y",
682 "png_chroma_red_x",
683 "png_chroma_red_y",
684 "png_chroma_green_x",
685 "png_chroma_green_y",
686 "png_chroma_blue_x",
687 "png_chroma_blue_y"
688};
689
690static const int chroma_tag_count = sizeof(chroma_tags) / sizeof(*chroma_tags);
691
38eab175 692static void
6fa6c8ee
TC
693get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr,
694 int bit_depth, int color_type) {
faa9b3e7
TC
695 png_uint_32 xres, yres;
696 int unit_type;
352c64ed 697
1d7e3124 698 i_tags_set(&im->tags, "i_format", "png", -1);
faa9b3e7 699 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
5e84d110 700 mm_log((1,"pHYs (%u, %u) %d\n", (unsigned)xres, (unsigned)yres, unit_type));
faa9b3e7 701 if (unit_type == PNG_RESOLUTION_METER) {
2e41e30b
TC
702 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
703 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
faa9b3e7
TC
704 }
705 else {
1d7e3124
TC
706 i_tags_setn(&im->tags, "i_xres", xres);
707 i_tags_setn(&im->tags, "i_yres", yres);
708 i_tags_setn(&im->tags, "i_aspect_only", 1);
faa9b3e7
TC
709 }
710 }
a4fa5d5e
TC
711 {
712 int interlace = png_get_interlace_type(png_ptr, info_ptr);
713
714 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
715 switch (interlace) {
716 case PNG_INTERLACE_NONE:
717 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
718 break;
719
720 case PNG_INTERLACE_ADAM7:
721 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
722 break;
723
724 default:
725 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
726 break;
727 }
00cff942
TC
728 }
729
79b4c849
TC
730 /* the various readers can call png_set_expand(), libpng will make
731 it's internal record of bit_depth at least 8 in that case */
732 i_tags_setn(&im->tags, "png_bits", bit_depth);
b51bcea5 733
86464bbf 734 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
b51bcea5
TC
735 int intent;
736 if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
737 i_tags_setn(&im->tags, "png_srgb_intent", intent);
738 }
739 }
86464bbf
TC
740 else {
741 /* Ignore these if there's an sRGB chunk, libpng simulates
742 their existence if there's an sRGB chunk, and the PNG spec says
743 that these are ignored if the sRGB is present, so ignore them.
744 */
b51bcea5 745 double gamma;
d0f15206 746 double chroma[8];
86464bbf
TC
747
748 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
749 i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
750 }
751
d0f15206
TC
752 if (png_get_cHRM(png_ptr, info_ptr, chroma+0, chroma+1,
753 chroma+2, chroma+3, chroma+4, chroma+5,
754 chroma+6, chroma+7)) {
755 int i;
756
757 for (i = 0; i < chroma_tag_count; ++i)
758 i_tags_set_float2(&im->tags, chroma_tags[i], 0, chroma[i], 4);
b51bcea5
TC
759 }
760 }
761
762 {
763 int num_text;
764 png_text *text;
765
766 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
767 int i;
d0f15206 768 int custom_index = 0;
b51bcea5
TC
769 for (i = 0; i < num_text; ++i) {
770 int j;
86464bbf 771 int found = 0;
d0f15206
TC
772 int compressed = text[i].compression == PNG_ITXT_COMPRESSION_zTXt
773 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt;
b51bcea5
TC
774
775 for (j = 0; j < text_tags_count; ++j) {
776 if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
d0f15206 777 char tag_name[50];
b51bcea5 778 i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
d0f15206
TC
779 if (compressed) {
780 sprintf(tag_name, "%s_compressed", text_tags[j].tagname);
781 i_tags_setn(&im->tags, tag_name, 1);
782 }
86464bbf 783 found = 1;
b51bcea5
TC
784 break;
785 }
786 }
86464bbf
TC
787
788 if (!found) {
789 char tag_name[50];
d0f15206 790 sprintf(tag_name, "png_text%d_key", custom_index);
86464bbf 791 i_tags_set(&im->tags, tag_name, text[i].key, -1);
d0f15206 792 sprintf(tag_name, "png_text%d_text", custom_index);
86464bbf 793 i_tags_set(&im->tags, tag_name, text[i].text, -1);
d0f15206 794 sprintf(tag_name, "png_text%d_type", custom_index);
86464bbf
TC
795 i_tags_set(&im->tags, tag_name,
796 (text[i].compression == PNG_TEXT_COMPRESSION_NONE
797 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
798 "text" : "itxt", -1);
d0f15206
TC
799 if (compressed) {
800 sprintf(tag_name, "png_text%d_compressed", custom_index);
801 i_tags_setn(&im->tags, tag_name, 1);
802 }
803 ++custom_index;
86464bbf 804 }
b51bcea5
TC
805 }
806 }
807 }
86464bbf
TC
808
809 {
810 png_time *mod_time;
811
812 if (png_get_tIME(png_ptr, info_ptr, &mod_time)) {
813 char time_formatted[80];
814
815 sprintf(time_formatted, "%d-%02d-%02dT%02d:%02d:%02d",
816 mod_time->year, mod_time->month, mod_time->day,
817 mod_time->hour, mod_time->minute, mod_time->second);
818 i_tags_set(&im->tags, "png_time", time_formatted, -1);
819 }
820 }
6fa6c8ee
TC
821
822 {
823 png_color_16 *back;
824 i_color c;
825
826 if (png_get_bKGD(png_ptr, info_ptr, &back)) {
827 switch (color_type) {
828 case PNG_COLOR_TYPE_GRAY:
829 case PNG_COLOR_TYPE_GRAY_ALPHA:
830 {
831 /* lib png stores the raw gray value rather than scaling it
832 to 16-bit (or 8), we use 8-bit color for i_background */
833
834 int gray;
835 switch (bit_depth) {
836 case 16:
837 gray = back->gray >> 8;
838 break;
839 case 8:
840 gray = back->gray;
841 break;
842 case 4:
843 gray = 0x11 * back->gray;
844 break;
845 case 2:
846 gray = 0x55 * back->gray;
847 break;
848 case 1:
849 gray = back->gray ? 0xFF : 0;
850 break;
851 default:
852 gray = 0;
853 }
854 c.rgb.r = c.rgb.g = c.rgb.b = gray;
855 break;
856 }
857
858 case PNG_COLOR_TYPE_RGB:
859 case PNG_COLOR_TYPE_RGB_ALPHA:
860 {
861 c.rgb.r = bit_depth == 16 ? (back->red >> 8) : back->red;
862 c.rgb.g = bit_depth == 16 ? (back->green >> 8) : back->green;
863 c.rgb.b = bit_depth == 16 ? (back->blue >> 8) : back->blue;
864 break;
865 }
866
867 case PNG_COLOR_TYPE_PALETTE:
868 c.rgb.r = back->red;
869 c.rgb.g = back->green;
870 c.rgb.b = back->blue;
871 break;
872 }
873
874 c.rgba.a = 255;
875 i_tags_set_color(&im->tags, "i_background", 0, &c);
876 }
877 }
faa9b3e7 878}
38eab175 879
d0f15206
TC
880#define GET_STR_BUF_SIZE 40
881
882static int
883set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
884 double xres, yres;
885 int aspect_only, have_res = 1;
886
887 if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
888 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
889 ; /* nothing to do */
890 else
891 yres = xres;
892 }
893 else {
894 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
895 xres = yres;
896 else
897 have_res = 0;
898 }
899 if (have_res) {
900 aspect_only = 0;
901 i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
902 xres /= 0.0254;
903 yres /= 0.0254;
904 png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
905 aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
906 }
907
908 {
909 int intent;
910 if (i_tags_get_int(&im->tags, "png_srgb_intent", 0, &intent)) {
911 if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) {
912 i_push_error(0, "tag png_srgb_intent out of range");
913 return 0;
914 }
915 png_set_sRGB(png_ptr, info_ptr, intent);
916 }
917 else {
918 double chroma[8], gamma;
919 int i;
920 int found_chroma_count = 0;
921
922 for (i = 0; i < chroma_tag_count; ++i) {
923 if (i_tags_get_float(&im->tags, chroma_tags[i], 0, chroma+i))
924 ++found_chroma_count;
925 }
926
927 if (found_chroma_count) {
928 if (found_chroma_count != chroma_tag_count) {
929 i_push_error(0, "all png_chroma_* tags must be supplied or none");
930 return 0;
931 }
932
933 png_set_cHRM(png_ptr, info_ptr, chroma[0], chroma[1], chroma[2],
934 chroma[3], chroma[4], chroma[5], chroma[6], chroma[7]);
935 }
936
937 if (i_tags_get_float(&im->tags, "png_gamma", 0, &gamma)) {
938 png_set_gAMA(png_ptr, info_ptr, gamma);
939 }
940 }
941 }
942
943 {
944 /* png_set_text() is sparsely documented, it isn't indicated whether
945 multiple calls add to or replace the lists of texts, and
946 whether the text/keyword data is copied or not.
947
948 Examining the linpng code reveals that png_set_text() adds to
949 the list and that the text is copied.
950 */
951 int i;
952
953 /* do our standard tags */
954 for (i = 0; i < text_tags_count; ++i) {
955 char buf[GET_STR_BUF_SIZE];
956 size_t size;
957 const char *data;
958
959 data = get_string2(&im->tags, text_tags[i].tagname, buf, &size);
960 if (data) {
961 png_text text;
962 int compression = size > 1000;
963 char compress_tag[40];
964
965 if (memchr(data, '\0', size)) {
966 i_push_errorf(0, "tag %s may not contain NUL characters", text_tags[i].tagname);
967 return 0;
968 }
969
970 sprintf(compress_tag, "%s_compressed", text_tags[i].tagname);
971 i_tags_get_int(&im->tags, compress_tag, 0, &compression);
972
973 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
974 : PNG_TEXT_COMPRESSION_NONE;
975 text.key = (char *)text_tags[i].keyword;
976 text.text_length = size;
977 text.text = (char *)data;
978#ifdef PNG_iTXt_SUPPORTED
979 text.itxt_length = 0;
980 text.lang = NULL;
981 text.lang_key = NULL;
982#endif
983
984 png_set_text(png_ptr, info_ptr, &text, 1);
985 }
986 }
987
988 /* for non-standard tags ensure keywords are limited to 1 to 79
989 characters */
990 i = 0;
991 while (1) {
992 char tag_name[50];
993 char key_buf[GET_STR_BUF_SIZE], value_buf[GET_STR_BUF_SIZE];
994 const char *key, *value;
995 size_t key_size, value_size;
996
997 sprintf(tag_name, "png_text%d_key", i);
998 key = get_string2(&im->tags, tag_name, key_buf, &key_size);
999
1000 if (key) {
1001 size_t k;
1002 if (key_size < 1 || key_size > 79) {
1003 i_push_errorf(0, "tag %s must be between 1 and 79 characters in length", tag_name);
1004 return 0;
1005 }
1006
1007 if (key[0] == ' ' || key[key_size-1] == ' ') {
1008 i_push_errorf(0, "tag %s may not contain leading or trailing spaces", tag_name);
1009 return 0;
1010 }
1011
1012 if (strstr(key, " ")) {
1013 i_push_errorf(0, "tag %s may not contain consecutive spaces", tag_name);
1014 return 0;
1015 }
1016
1017 for (k = 0; k < key_size; ++k) {
0a53238e 1018 if (key[k] < 32 || (key[k] > 126 && key[k] < 161)) {
d0f15206
TC
1019 i_push_errorf(0, "tag %s may only contain Latin1 characters 32-126, 161-255", tag_name);
1020 return 0;
1021 }
1022 }
1023 }
1024
1025 sprintf(tag_name, "png_text%d_text", i);
1026 value = get_string2(&im->tags, tag_name, value_buf, &value_size);
1027
1028 if (value) {
1029 if (memchr(value, '\0', value_size)) {
1030 i_push_errorf(0, "tag %s may not contain NUL characters", tag_name);
1031 return 0;
1032 }
1033 }
1034
1035 if (key && value) {
1036 png_text text;
1037 int compression = value_size > 1000;
1038
1039 sprintf(tag_name, "png_text%d_compressed", i);
1040 i_tags_get_int(&im->tags, tag_name, 0, &compression);
1041
1042 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1043 : PNG_TEXT_COMPRESSION_NONE;
1044 text.key = (char *)key;
1045 text.text_length = value_size;
1046 text.text = (char *)value;
1047#ifdef PNG_iTXt_SUPPORTED
1048 text.itxt_length = 0;
1049 text.lang = NULL;
1050 text.lang_key = NULL;
1051#endif
1052
1053 png_set_text(png_ptr, info_ptr, &text, 1);
1054 }
1055 else if (key) {
1056 i_push_errorf(0, "tag png_text%d_key found but not png_text%d_text", i, i);
1057 return 0;
1058 }
1059 else if (value) {
1060 i_push_errorf(0, "tag png_text%d_text found but not png_text%d_key", i, i);
1061 return 0;
1062 }
1063 else {
1064 break;
1065 }
1066 ++i;
1067 }
1068 }
1069
1070 {
1071 char buf[GET_STR_BUF_SIZE];
1072 size_t time_size;
1073 const char *timestr = get_string2(&im->tags, "png_time", buf, &time_size);
1074
1075 if (timestr) {
1076 int year, month, day, hour, minute, second;
1077 png_time mod_time;
1078
1079 if (sscanf(timestr, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6) {
1080 /* rough validation */
1081 if (month < 1 || month > 12
1082 || day < 1 || day > 31
1083 || hour < 0 || hour > 23
1084 || minute < 0 || minute > 59
1085 || second < 0 || second > 60) {
1086 i_push_error(0, "invalid date/time for png_time");
1087 return 0;
1088 }
1089 mod_time.year = year;
1090 mod_time.month = month;
1091 mod_time.day = day;
1092 mod_time.hour = hour;
1093 mod_time.minute = minute;
1094 mod_time.second = second;
1095
1096 png_set_tIME(png_ptr, info_ptr, &mod_time);
1097 }
1098 else {
1099 i_push_error(0, "png_time must be formatted 'y-m-dTh:m:s'");
1100 return 0;
1101 }
1102 }
1103 }
1104
1105 {
1106 /* no bKGD support yet, maybe later
1107 it may be simpler to do it in the individual writers
1108 */
1109 }
1110
1111 return 1;
1112}
1113
1114static const char *
1115get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size) {
1116 int index;
1117
1118 if (i_tags_find(tags, name, 0, &index)) {
1119 const i_img_tag *entry = tags->tags + index;
1120
1121 if (entry->data) {
1122 *size = entry->size;
1123
1124 return entry->data;
1125 }
1126 else {
1127 *size = sprintf(buf, "%d", entry->idata);
1128
1129 return buf;
1130 }
1131 }
1132 return NULL;
1133}
1134
81089826
TC
1135static int
1136write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1137 unsigned char *data, *volatile vdata = NULL;
1138 i_img_dim y;
1139
1140 if (setjmp(png_jmpbuf(png_ptr))) {
1141 if (vdata)
1142 myfree(vdata);
1143
1144 return 0;
1145 }
1146
21c6936b
TC
1147 png_write_info(png_ptr, info_ptr);
1148
81089826
TC
1149 vdata = data = mymalloc(im->xsize * im->channels);
1150 for (y = 0; y < im->ysize; y++) {
1151 i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
1152 png_write_row(png_ptr, (png_bytep)data);
1153 }
1154 myfree(data);
1155
1156 return 1;
1157}
1158
d9610331
TC
1159static int
1160write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1161 unsigned *data, *volatile vdata = NULL;
1162 unsigned char *tran_data, * volatile vtran_data = NULL;
1163 i_img_dim samples_per_row = im->xsize * im->channels;
1164
1165 i_img_dim y;
1166
1167 if (setjmp(png_jmpbuf(png_ptr))) {
1168 if (vdata)
1169 myfree(vdata);
1170 if (vtran_data)
1171 myfree(vtran_data);
1172
1173 return 0;
1174 }
1175
1176 png_write_info(png_ptr, info_ptr);
1177
1178 vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
1179 vtran_data = tran_data = mymalloc(samples_per_row * 2);
1180 for (y = 0; y < im->ysize; y++) {
1181 i_img_dim i;
1182 unsigned char *p = tran_data;
1183 i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
1184 for (i = 0; i < samples_per_row; ++i) {
1185 p[0] = data[i] >> 8;
1186 p[1] = data[i] & 0xff;
1187 p += 2;
1188 }
1189 png_write_row(png_ptr, (png_bytep)tran_data);
1190 }
1191 myfree(tran_data);
1192 myfree(data);
1193
1194 return 1;
1195}
1196
21c6936b
TC
1197static int
1198write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
1199 unsigned char *data, *volatile vdata = NULL;
1200 i_img_dim y;
1201 unsigned char pal_map[256];
1202 png_color pcolors[256];
1203 i_color colors[256];
1204 int count = i_colorcount(im);
1205 int i;
1206
1207 if (setjmp(png_jmpbuf(png_ptr))) {
1208 if (vdata)
1209 myfree(vdata);
1210
1211 return 0;
1212 }
1213
1214 i_getcolors(im, 0, colors, count);
1215 if (im->channels < 3) {
1216 /* convert the greyscale palette to color */
1217 int i;
1218 for (i = 0; i < count; ++i) {
1219 i_color *c = colors + i;
1220 c->channel[3] = c->channel[1];
1221 c->channel[2] = c->channel[1] = c->channel[0];
1222 }
1223 }
1224
1225 if (i_img_has_alpha(im)) {
1226 int i;
1227 int bottom_index = 0, top_index = count-1;
1228
1229 /* fill out the palette map */
1230 for (i = 0; i < count; ++i)
1231 pal_map[i] = i;
1232
1233 /* the PNG spec suggests sorting the palette by alpha, but that's
1234 unnecessary - all we want to do is move the opaque entries to
1235 the end */
1236 while (bottom_index < top_index) {
1237 if (colors[bottom_index].rgba.a == 255) {
1238 pal_map[bottom_index] = top_index;
1239 pal_map[top_index--] = bottom_index;
1240 }
1241 ++bottom_index;
1242 }
1243 }
1244
1245 for (i = 0; i < count; ++i) {
1246 int srci = i_img_has_alpha(im) ? pal_map[i] : i;
1247
1248 pcolors[i].red = colors[srci].rgb.r;
1249 pcolors[i].green = colors[srci].rgb.g;
1250 pcolors[i].blue = colors[srci].rgb.b;
1251 }
1252
1253 png_set_PLTE(png_ptr, info_ptr, pcolors, count);
1254
1255 if (i_img_has_alpha(im)) {
1256 unsigned char trans[256];
1257 int i;
1258
1259 for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
1260 trans[i] = colors[pal_map[i]].rgba.a;
1261 }
1262 png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
1263 }
1264
1265 png_write_info(png_ptr, info_ptr);
1266
1267 png_set_packing(png_ptr);
1268
1269 vdata = data = mymalloc(im->xsize);
1270 for (y = 0; y < im->ysize; y++) {
1271 i_gpal(im, 0, im->xsize, y, data);
1272 if (i_img_has_alpha(im)) {
1273 i_img_dim x;
1274 for (x = 0; x < im->xsize; ++x)
1275 data[x] = pal_map[data[x]];
1276 }
1277 png_write_row(png_ptr, (png_bytep)data);
1278 }
1279 myfree(data);
1280
1281 return 1;
1282}
1283
b8961d05
TC
1284static int
1285write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1286 unsigned char *data, *volatile vdata = NULL;
1287 i_img_dim y;
21c6936b 1288
b8961d05
TC
1289 if (setjmp(png_jmpbuf(png_ptr))) {
1290 if (vdata)
1291 myfree(vdata);
1292
1293 return 0;
21c6936b 1294 }
21c6936b 1295
b8961d05
TC
1296 png_write_info(png_ptr, info_ptr);
1297
1298 png_set_packing(png_ptr);
1299
1300 vdata = data = mymalloc(im->xsize);
1301 for (y = 0; y < im->ysize; y++) {
1302 i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
1303 png_write_row(png_ptr, (png_bytep)data);
1304 }
1305 myfree(data);
1306
1307 return 1;
1308}
21c6936b 1309
38eab175
TC
1310static void
1311read_warn_handler(png_structp png_ptr, png_const_charp msg) {
1312 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
1313 char *workp;
1314 size_t new_size;
1315
1316 mm_log((1, "PNG read warning '%s'\n", msg));
1317
1318 /* in case this is part of an error report */
1319 i_push_error(0, msg);
1320
1321 /* and save in the warnings so if we do manage to succeed, we
1322 * can save it as a tag
1323 */
1324 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
1325 + 1 /* NUL */
1326 + strlen(msg) /* new text */
1327 + 1; /* newline */
1328 workp = myrealloc(rs->warnings, new_size);
1329 if (!rs->warnings)
1330 *workp = '\0';
1331 strcat(workp, msg);
1332 strcat(workp, "\n");
1333 rs->warnings = workp;
1334}
1335
1336static void
1337cleanup_read_state(i_png_read_statep rs) {
1338 if (rs->warnings)
1339 myfree(rs->warnings);
1340}