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