note .dSYM clean up change
[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
319 mm_log((1,
790923a4 320 "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
02d1d628
AMH
321 width,height,bit_depth,color_type,interlace_type));
322
323 CC2C[PNG_COLOR_TYPE_GRAY]=1;
324 CC2C[PNG_COLOR_TYPE_PALETTE]=3;
325 CC2C[PNG_COLOR_TYPE_RGB]=3;
326 CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
327 CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
790923a4
AMH
328 channels = CC2C[color_type];
329
330 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
331
77157728
TC
332 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
333 mm_log((1, "i_readpnm: image size exceeds limits\n"));
334 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
335 return NULL;
336 }
337
963d3602
TC
338 if (color_type == PNG_COLOR_TYPE_PALETTE) {
339 im = read_paletted(png_ptr, info_ptr, channels, width, height);
340 }
79b4c849
TC
341 else if (color_type == PNG_COLOR_TYPE_GRAY
342 && bit_depth == 1
343 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
344 im = read_bilevel(png_ptr, info_ptr, width, height);
345 }
c631b2d5
TC
346 else if (bit_depth == 16) {
347 im = read_direct16(png_ptr, info_ptr, channels, width, height);
348 }
963d3602
TC
349 else {
350 im = read_direct8(png_ptr, info_ptr, channels, width, height);
351 }
cfb628e2
TC
352
353 if (im)
6fa6c8ee 354 get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
cfb628e2
TC
355
356 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
357
358 if (im) {
359 if (rs.warnings) {
360 i_tags_set(&im->tags, "png_warnings", rs.warnings, -1);
361 }
362 }
363 cleanup_read_state(&rs);
364
365 mm_log((1,"(%p) <- i_readpng_wiol\n", im));
366
367 return im;
368}
369
370static i_img *
371read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
372 i_img_dim width, i_img_dim height) {
373 i_img * volatile vim = NULL;
374 int color_type = png_get_color_type(png_ptr, info_ptr);
375 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
376 i_img_dim y;
377 int number_passes, pass;
378 i_img *im;
34e56776
TC
379 unsigned char *line;
380 unsigned char * volatile vline = NULL;
cfb628e2
TC
381
382 if (setjmp(png_jmpbuf(png_ptr))) {
383 if (vim) i_img_destroy(vim);
34e56776 384 if (vline) myfree(vline);
cfb628e2
TC
385
386 return NULL;
387 }
388
389 number_passes = png_set_interlace_handling(png_ptr);
390 mm_log((1,"number of passes=%d\n",number_passes));
391
02d1d628
AMH
392 png_set_strip_16(png_ptr);
393 png_set_packing(png_ptr);
6829f5f6 394
cfb628e2
TC
395 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
396 png_set_expand(png_ptr);
397
6829f5f6
AMH
398 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
399 channels++;
400 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
401 png_set_expand(png_ptr);
402 }
790923a4 403
02d1d628 404 png_read_update_info(png_ptr, info_ptr);
790923a4 405
cfb628e2 406 im = vim = i_img_8_new(width,height,channels);
352c64ed
TC
407 if (!im) {
408 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
409 return NULL;
410 }
02d1d628 411
34e56776 412 line = vline = mymalloc(channels * width);
cfb628e2
TC
413 for (pass = 0; pass < number_passes; pass++) {
414 for (y = 0; y < height; y++) {
34e56776
TC
415 if (pass > 0)
416 i_gsamp(im, 0, width, y, line, NULL, channels);
417 png_read_row(png_ptr,(png_bytep)line, NULL);
418 i_psamp(im, 0, width, y, line, NULL, channels);
cfb628e2 419 }
38eab175 420 }
34e56776
TC
421 myfree(line);
422 vline = NULL;
02d1d628 423
cfb628e2
TC
424 png_read_end(png_ptr, info_ptr);
425
790923a4
AMH
426 return im;
427}
faa9b3e7 428
c631b2d5
TC
429static i_img *
430read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
431 i_img_dim width, i_img_dim height) {
432 i_img * volatile vim = NULL;
c631b2d5
TC
433 i_img_dim x, y;
434 int number_passes, pass;
435 i_img *im;
436 unsigned char *line;
437 unsigned char * volatile vline = NULL;
438 unsigned *bits_line;
439 unsigned * volatile vbits_line = NULL;
440 size_t row_bytes;
441
442 if (setjmp(png_jmpbuf(png_ptr))) {
443 if (vim) i_img_destroy(vim);
444 if (vline) myfree(vline);
445 if (vbits_line) myfree(vbits_line);
446
447 return NULL;
448 }
449
450 number_passes = png_set_interlace_handling(png_ptr);
451 mm_log((1,"number of passes=%d\n",number_passes));
452
453 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
454 channels++;
455 mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
456 png_set_expand(png_ptr);
457 }
458
459 png_read_update_info(png_ptr, info_ptr);
460
461 im = vim = i_img_16_new(width,height,channels);
462 if (!im) {
463 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
464 return NULL;
465 }
466
467 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
468 line = vline = mymalloc(row_bytes);
469 memset(line, 0, row_bytes);
470 bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
471 for (pass = 0; pass < number_passes; pass++) {
472 for (y = 0; y < height; y++) {
473 if (pass > 0) {
474 i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
475 for (x = 0; x < width * channels; ++x) {
476 line[x*2] = bits_line[x] >> 8;
477 line[x*2+1] = bits_line[x] & 0xff;
478 }
479 }
480 png_read_row(png_ptr,(png_bytep)line, NULL);
481 for (x = 0; x < width * channels; ++x)
482 bits_line[x] = (line[x*2] << 8) + line[x*2+1];
483 i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
484 }
485 }
486 myfree(line);
487 myfree(bits_line);
488 vline = NULL;
489 vbits_line = NULL;
490
491 png_read_end(png_ptr, info_ptr);
492
493 return im;
494}
495
79b4c849
TC
496static i_img *
497read_bilevel(png_structp png_ptr, png_infop info_ptr,
498 i_img_dim width, i_img_dim height) {
499 i_img * volatile vim = NULL;
500 i_img_dim x, y;
501 int number_passes, pass;
502 i_img *im;
503 unsigned char *line;
504 unsigned char * volatile vline = NULL;
505 i_color palette[2];
506
507 if (setjmp(png_jmpbuf(png_ptr))) {
508 if (vim) i_img_destroy(vim);
509 if (vline) myfree(vline);
510
511 return NULL;
512 }
513
514 number_passes = png_set_interlace_handling(png_ptr);
515 mm_log((1,"number of passes=%d\n",number_passes));
516
517 png_set_packing(png_ptr);
518
519 png_set_expand(png_ptr);
520
521 png_read_update_info(png_ptr, info_ptr);
522
523 im = vim = i_img_pal_new(width, height, 1, 256);
524 if (!im) {
525 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
526 return NULL;
527 }
528
529 palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] =
530 palette[0].channel[3] = 0;
531 palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] =
532 palette[1].channel[3] = 255;
533 i_addcolors(im, palette, 2);
534
535 line = vline = mymalloc(width);
536 memset(line, 0, width);
537 for (pass = 0; pass < number_passes; pass++) {
538 for (y = 0; y < height; y++) {
539 if (pass > 0) {
540 i_gpal(im, 0, width, y, line);
541 /* expand indexes back to 0/255 */
542 for (x = 0; x < width; ++x)
543 line[x] = line[x] ? 255 : 0;
544 }
545 png_read_row(png_ptr,(png_bytep)line, NULL);
546
547 /* back to palette indexes */
548 for (x = 0; x < width; ++x)
549 line[x] = line[x] ? 1 : 0;
550 i_ppal(im, 0, width, y, line);
551 }
552 }
553 myfree(line);
554 vline = NULL;
555
556 png_read_end(png_ptr, info_ptr);
557
558 return im;
559}
560
561/* FIXME: do we need to unscale palette color values from the
562 supplied alphas? */
963d3602
TC
563static i_img *
564read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
565 i_img_dim width, i_img_dim height) {
566 i_img * volatile vim = NULL;
567 int color_type = png_get_color_type(png_ptr, info_ptr);
568 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
569 i_img_dim y;
570 int number_passes, pass;
571 i_img *im;
572 unsigned char *line;
573 unsigned char * volatile vline = NULL;
574 int num_palette, i;
575 png_colorp png_palette;
576 png_bytep png_pal_trans;
577 png_color_16p png_color_trans;
578 int num_pal_trans;
579
580 if (setjmp(png_jmpbuf(png_ptr))) {
581 if (vim) i_img_destroy(vim);
582 if (vline) myfree(vline);
583
584 return NULL;
585 }
586
587 number_passes = png_set_interlace_handling(png_ptr);
588 mm_log((1,"number of passes=%d\n",number_passes));
589
590 png_set_strip_16(png_ptr);
591 png_set_packing(png_ptr);
592
593 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
594 png_set_expand(png_ptr);
595
596 if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) {
597 i_push_error(0, "Paletted image with no PLTE chunk");
598 return NULL;
599 }
600
601 if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans,
602 &png_color_trans)) {
603 channels++;
604 }
605 else {
606 num_pal_trans = 0;
607 }
608
609 png_read_update_info(png_ptr, info_ptr);
610
611 im = vim = i_img_pal_new(width, height, channels, 256);
612 if (!im) {
613 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
614 return NULL;
615 }
616
617 for (i = 0; i < num_palette; ++i) {
618 i_color c;
619
620 c.rgba.r = png_palette[i].red;
621 c.rgba.g = png_palette[i].green;
622 c.rgba.b = png_palette[i].blue;
623 if (i < num_pal_trans)
624 c.rgba.a = png_pal_trans[i];
625 else
626 c.rgba.a = 255;
627 i_addcolors(im, &c, 1);
628 }
629
630 line = vline = mymalloc(width);
631 for (pass = 0; pass < number_passes; pass++) {
632 for (y = 0; y < height; y++) {
633 if (pass > 0)
634 i_gpal(im, 0, width, y, line);
635 png_read_row(png_ptr,(png_bytep)line, NULL);
636 i_ppal(im, 0, width, y, line);
637 }
638 }
639 myfree(line);
640 vline = NULL;
641
642 png_read_end(png_ptr, info_ptr);
643
644 return im;
645}
646
b51bcea5
TC
647struct png_text_name {
648 const char *keyword;
649 const char *tagname;
650};
651
652static const struct png_text_name
653text_tags[] = {
654 { "Author", "png_author" },
655 { "Comment", "i_comment" },
656 { "Copyright", "png_copyright" },
657 { "Creation Time", "png_creation_time" },
658 { "Description", "png_description" },
659 { "Disclaimer", "png_disclaimer" },
660 { "Software", "png_software" },
661 { "Source", "png_source" },
662 { "Title", "png_title" },
663 { "Warning", "png_warning" }
664};
665
666static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
667
d0f15206
TC
668static const char * const
669chroma_tags[] = {
670 "png_chroma_white_x",
671 "png_chroma_white_y",
672 "png_chroma_red_x",
673 "png_chroma_red_y",
674 "png_chroma_green_x",
675 "png_chroma_green_y",
676 "png_chroma_blue_x",
677 "png_chroma_blue_y"
678};
679
680static const int chroma_tag_count = sizeof(chroma_tags) / sizeof(*chroma_tags);
681
38eab175 682static void
6fa6c8ee
TC
683get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr,
684 int bit_depth, int color_type) {
faa9b3e7
TC
685 png_uint_32 xres, yres;
686 int unit_type;
352c64ed 687
1d7e3124 688 i_tags_set(&im->tags, "i_format", "png", -1);
faa9b3e7
TC
689 if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
690 mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
691 if (unit_type == PNG_RESOLUTION_METER) {
2e41e30b
TC
692 i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
693 i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
faa9b3e7
TC
694 }
695 else {
1d7e3124
TC
696 i_tags_setn(&im->tags, "i_xres", xres);
697 i_tags_setn(&im->tags, "i_yres", yres);
698 i_tags_setn(&im->tags, "i_aspect_only", 1);
faa9b3e7
TC
699 }
700 }
a4fa5d5e
TC
701 {
702 int interlace = png_get_interlace_type(png_ptr, info_ptr);
703
704 i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE);
705 switch (interlace) {
706 case PNG_INTERLACE_NONE:
707 i_tags_set(&im->tags, "png_interlace_name", "none", -1);
708 break;
709
710 case PNG_INTERLACE_ADAM7:
711 i_tags_set(&im->tags, "png_interlace_name", "adam7", -1);
712 break;
713
714 default:
715 i_tags_set(&im->tags, "png_interlace_name", "unknown", -1);
716 break;
717 }
00cff942
TC
718 }
719
79b4c849
TC
720 /* the various readers can call png_set_expand(), libpng will make
721 it's internal record of bit_depth at least 8 in that case */
722 i_tags_setn(&im->tags, "png_bits", bit_depth);
b51bcea5 723
86464bbf 724 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
b51bcea5
TC
725 int intent;
726 if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
727 i_tags_setn(&im->tags, "png_srgb_intent", intent);
728 }
729 }
86464bbf
TC
730 else {
731 /* Ignore these if there's an sRGB chunk, libpng simulates
732 their existence if there's an sRGB chunk, and the PNG spec says
733 that these are ignored if the sRGB is present, so ignore them.
734 */
b51bcea5 735 double gamma;
d0f15206 736 double chroma[8];
86464bbf
TC
737
738 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
739 i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
740 }
741
d0f15206
TC
742 if (png_get_cHRM(png_ptr, info_ptr, chroma+0, chroma+1,
743 chroma+2, chroma+3, chroma+4, chroma+5,
744 chroma+6, chroma+7)) {
745 int i;
746
747 for (i = 0; i < chroma_tag_count; ++i)
748 i_tags_set_float2(&im->tags, chroma_tags[i], 0, chroma[i], 4);
b51bcea5
TC
749 }
750 }
751
752 {
753 int num_text;
754 png_text *text;
755
756 if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
757 int i;
d0f15206 758 int custom_index = 0;
b51bcea5
TC
759 for (i = 0; i < num_text; ++i) {
760 int j;
86464bbf 761 int found = 0;
d0f15206
TC
762 int compressed = text[i].compression == PNG_ITXT_COMPRESSION_zTXt
763 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt;
b51bcea5
TC
764
765 for (j = 0; j < text_tags_count; ++j) {
766 if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
d0f15206 767 char tag_name[50];
b51bcea5 768 i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
d0f15206
TC
769 if (compressed) {
770 sprintf(tag_name, "%s_compressed", text_tags[j].tagname);
771 i_tags_setn(&im->tags, tag_name, 1);
772 }
86464bbf 773 found = 1;
b51bcea5
TC
774 break;
775 }
776 }
86464bbf
TC
777
778 if (!found) {
779 char tag_name[50];
d0f15206 780 sprintf(tag_name, "png_text%d_key", custom_index);
86464bbf 781 i_tags_set(&im->tags, tag_name, text[i].key, -1);
d0f15206 782 sprintf(tag_name, "png_text%d_text", custom_index);
86464bbf 783 i_tags_set(&im->tags, tag_name, text[i].text, -1);
d0f15206 784 sprintf(tag_name, "png_text%d_type", custom_index);
86464bbf
TC
785 i_tags_set(&im->tags, tag_name,
786 (text[i].compression == PNG_TEXT_COMPRESSION_NONE
787 || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
788 "text" : "itxt", -1);
d0f15206
TC
789 if (compressed) {
790 sprintf(tag_name, "png_text%d_compressed", custom_index);
791 i_tags_setn(&im->tags, tag_name, 1);
792 }
793 ++custom_index;
86464bbf 794 }
b51bcea5
TC
795 }
796 }
797 }
86464bbf
TC
798
799 {
800 png_time *mod_time;
801
802 if (png_get_tIME(png_ptr, info_ptr, &mod_time)) {
803 char time_formatted[80];
804
805 sprintf(time_formatted, "%d-%02d-%02dT%02d:%02d:%02d",
806 mod_time->year, mod_time->month, mod_time->day,
807 mod_time->hour, mod_time->minute, mod_time->second);
808 i_tags_set(&im->tags, "png_time", time_formatted, -1);
809 }
810 }
6fa6c8ee
TC
811
812 {
813 png_color_16 *back;
814 i_color c;
815
816 if (png_get_bKGD(png_ptr, info_ptr, &back)) {
817 switch (color_type) {
818 case PNG_COLOR_TYPE_GRAY:
819 case PNG_COLOR_TYPE_GRAY_ALPHA:
820 {
821 /* lib png stores the raw gray value rather than scaling it
822 to 16-bit (or 8), we use 8-bit color for i_background */
823
824 int gray;
825 switch (bit_depth) {
826 case 16:
827 gray = back->gray >> 8;
828 break;
829 case 8:
830 gray = back->gray;
831 break;
832 case 4:
833 gray = 0x11 * back->gray;
834 break;
835 case 2:
836 gray = 0x55 * back->gray;
837 break;
838 case 1:
839 gray = back->gray ? 0xFF : 0;
840 break;
841 default:
842 gray = 0;
843 }
844 c.rgb.r = c.rgb.g = c.rgb.b = gray;
845 break;
846 }
847
848 case PNG_COLOR_TYPE_RGB:
849 case PNG_COLOR_TYPE_RGB_ALPHA:
850 {
851 c.rgb.r = bit_depth == 16 ? (back->red >> 8) : back->red;
852 c.rgb.g = bit_depth == 16 ? (back->green >> 8) : back->green;
853 c.rgb.b = bit_depth == 16 ? (back->blue >> 8) : back->blue;
854 break;
855 }
856
857 case PNG_COLOR_TYPE_PALETTE:
858 c.rgb.r = back->red;
859 c.rgb.g = back->green;
860 c.rgb.b = back->blue;
861 break;
862 }
863
864 c.rgba.a = 255;
865 i_tags_set_color(&im->tags, "i_background", 0, &c);
866 }
867 }
faa9b3e7 868}
38eab175 869
d0f15206
TC
870#define GET_STR_BUF_SIZE 40
871
872static int
873set_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
874 double xres, yres;
875 int aspect_only, have_res = 1;
876
877 if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
878 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
879 ; /* nothing to do */
880 else
881 yres = xres;
882 }
883 else {
884 if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
885 xres = yres;
886 else
887 have_res = 0;
888 }
889 if (have_res) {
890 aspect_only = 0;
891 i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
892 xres /= 0.0254;
893 yres /= 0.0254;
894 png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
895 aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
896 }
897
898 {
899 int intent;
900 if (i_tags_get_int(&im->tags, "png_srgb_intent", 0, &intent)) {
901 if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) {
902 i_push_error(0, "tag png_srgb_intent out of range");
903 return 0;
904 }
905 png_set_sRGB(png_ptr, info_ptr, intent);
906 }
907 else {
908 double chroma[8], gamma;
909 int i;
910 int found_chroma_count = 0;
911
912 for (i = 0; i < chroma_tag_count; ++i) {
913 if (i_tags_get_float(&im->tags, chroma_tags[i], 0, chroma+i))
914 ++found_chroma_count;
915 }
916
917 if (found_chroma_count) {
918 if (found_chroma_count != chroma_tag_count) {
919 i_push_error(0, "all png_chroma_* tags must be supplied or none");
920 return 0;
921 }
922
923 png_set_cHRM(png_ptr, info_ptr, chroma[0], chroma[1], chroma[2],
924 chroma[3], chroma[4], chroma[5], chroma[6], chroma[7]);
925 }
926
927 if (i_tags_get_float(&im->tags, "png_gamma", 0, &gamma)) {
928 png_set_gAMA(png_ptr, info_ptr, gamma);
929 }
930 }
931 }
932
933 {
934 /* png_set_text() is sparsely documented, it isn't indicated whether
935 multiple calls add to or replace the lists of texts, and
936 whether the text/keyword data is copied or not.
937
938 Examining the linpng code reveals that png_set_text() adds to
939 the list and that the text is copied.
940 */
941 int i;
942
943 /* do our standard tags */
944 for (i = 0; i < text_tags_count; ++i) {
945 char buf[GET_STR_BUF_SIZE];
946 size_t size;
947 const char *data;
948
949 data = get_string2(&im->tags, text_tags[i].tagname, buf, &size);
950 if (data) {
951 png_text text;
952 int compression = size > 1000;
953 char compress_tag[40];
954
955 if (memchr(data, '\0', size)) {
956 i_push_errorf(0, "tag %s may not contain NUL characters", text_tags[i].tagname);
957 return 0;
958 }
959
960 sprintf(compress_tag, "%s_compressed", text_tags[i].tagname);
961 i_tags_get_int(&im->tags, compress_tag, 0, &compression);
962
963 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
964 : PNG_TEXT_COMPRESSION_NONE;
965 text.key = (char *)text_tags[i].keyword;
966 text.text_length = size;
967 text.text = (char *)data;
968#ifdef PNG_iTXt_SUPPORTED
969 text.itxt_length = 0;
970 text.lang = NULL;
971 text.lang_key = NULL;
972#endif
973
974 png_set_text(png_ptr, info_ptr, &text, 1);
975 }
976 }
977
978 /* for non-standard tags ensure keywords are limited to 1 to 79
979 characters */
980 i = 0;
981 while (1) {
982 char tag_name[50];
983 char key_buf[GET_STR_BUF_SIZE], value_buf[GET_STR_BUF_SIZE];
984 const char *key, *value;
985 size_t key_size, value_size;
986
987 sprintf(tag_name, "png_text%d_key", i);
988 key = get_string2(&im->tags, tag_name, key_buf, &key_size);
989
990 if (key) {
991 size_t k;
992 if (key_size < 1 || key_size > 79) {
993 i_push_errorf(0, "tag %s must be between 1 and 79 characters in length", tag_name);
994 return 0;
995 }
996
997 if (key[0] == ' ' || key[key_size-1] == ' ') {
998 i_push_errorf(0, "tag %s may not contain leading or trailing spaces", tag_name);
999 return 0;
1000 }
1001
1002 if (strstr(key, " ")) {
1003 i_push_errorf(0, "tag %s may not contain consecutive spaces", tag_name);
1004 return 0;
1005 }
1006
1007 for (k = 0; k < key_size; ++k) {
0a53238e 1008 if (key[k] < 32 || (key[k] > 126 && key[k] < 161)) {
d0f15206
TC
1009 i_push_errorf(0, "tag %s may only contain Latin1 characters 32-126, 161-255", tag_name);
1010 return 0;
1011 }
1012 }
1013 }
1014
1015 sprintf(tag_name, "png_text%d_text", i);
1016 value = get_string2(&im->tags, tag_name, value_buf, &value_size);
1017
1018 if (value) {
1019 if (memchr(value, '\0', value_size)) {
1020 i_push_errorf(0, "tag %s may not contain NUL characters", tag_name);
1021 return 0;
1022 }
1023 }
1024
1025 if (key && value) {
1026 png_text text;
1027 int compression = value_size > 1000;
1028
1029 sprintf(tag_name, "png_text%d_compressed", i);
1030 i_tags_get_int(&im->tags, tag_name, 0, &compression);
1031
1032 text.compression = compression ? PNG_TEXT_COMPRESSION_zTXt
1033 : PNG_TEXT_COMPRESSION_NONE;
1034 text.key = (char *)key;
1035 text.text_length = value_size;
1036 text.text = (char *)value;
1037#ifdef PNG_iTXt_SUPPORTED
1038 text.itxt_length = 0;
1039 text.lang = NULL;
1040 text.lang_key = NULL;
1041#endif
1042
1043 png_set_text(png_ptr, info_ptr, &text, 1);
1044 }
1045 else if (key) {
1046 i_push_errorf(0, "tag png_text%d_key found but not png_text%d_text", i, i);
1047 return 0;
1048 }
1049 else if (value) {
1050 i_push_errorf(0, "tag png_text%d_text found but not png_text%d_key", i, i);
1051 return 0;
1052 }
1053 else {
1054 break;
1055 }
1056 ++i;
1057 }
1058 }
1059
1060 {
1061 char buf[GET_STR_BUF_SIZE];
1062 size_t time_size;
1063 const char *timestr = get_string2(&im->tags, "png_time", buf, &time_size);
1064
1065 if (timestr) {
1066 int year, month, day, hour, minute, second;
1067 png_time mod_time;
1068
1069 if (sscanf(timestr, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6) {
1070 /* rough validation */
1071 if (month < 1 || month > 12
1072 || day < 1 || day > 31
1073 || hour < 0 || hour > 23
1074 || minute < 0 || minute > 59
1075 || second < 0 || second > 60) {
1076 i_push_error(0, "invalid date/time for png_time");
1077 return 0;
1078 }
1079 mod_time.year = year;
1080 mod_time.month = month;
1081 mod_time.day = day;
1082 mod_time.hour = hour;
1083 mod_time.minute = minute;
1084 mod_time.second = second;
1085
1086 png_set_tIME(png_ptr, info_ptr, &mod_time);
1087 }
1088 else {
1089 i_push_error(0, "png_time must be formatted 'y-m-dTh:m:s'");
1090 return 0;
1091 }
1092 }
1093 }
1094
1095 {
1096 /* no bKGD support yet, maybe later
1097 it may be simpler to do it in the individual writers
1098 */
1099 }
1100
1101 return 1;
1102}
1103
1104static const char *
1105get_string2(i_img_tags *tags, const char *name, char *buf, size_t *size) {
1106 int index;
1107
1108 if (i_tags_find(tags, name, 0, &index)) {
1109 const i_img_tag *entry = tags->tags + index;
1110
1111 if (entry->data) {
1112 *size = entry->size;
1113
1114 return entry->data;
1115 }
1116 else {
1117 *size = sprintf(buf, "%d", entry->idata);
1118
1119 return buf;
1120 }
1121 }
1122 return NULL;
1123}
1124
81089826
TC
1125static int
1126write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1127 unsigned char *data, *volatile vdata = NULL;
1128 i_img_dim y;
1129
1130 if (setjmp(png_jmpbuf(png_ptr))) {
1131 if (vdata)
1132 myfree(vdata);
1133
1134 return 0;
1135 }
1136
21c6936b
TC
1137 png_write_info(png_ptr, info_ptr);
1138
81089826
TC
1139 vdata = data = mymalloc(im->xsize * im->channels);
1140 for (y = 0; y < im->ysize; y++) {
1141 i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
1142 png_write_row(png_ptr, (png_bytep)data);
1143 }
1144 myfree(data);
1145
1146 return 1;
1147}
1148
d9610331
TC
1149static int
1150write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1151 unsigned *data, *volatile vdata = NULL;
1152 unsigned char *tran_data, * volatile vtran_data = NULL;
1153 i_img_dim samples_per_row = im->xsize * im->channels;
1154
1155 i_img_dim y;
1156
1157 if (setjmp(png_jmpbuf(png_ptr))) {
1158 if (vdata)
1159 myfree(vdata);
1160 if (vtran_data)
1161 myfree(vtran_data);
1162
1163 return 0;
1164 }
1165
1166 png_write_info(png_ptr, info_ptr);
1167
1168 vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
1169 vtran_data = tran_data = mymalloc(samples_per_row * 2);
1170 for (y = 0; y < im->ysize; y++) {
1171 i_img_dim i;
1172 unsigned char *p = tran_data;
1173 i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
1174 for (i = 0; i < samples_per_row; ++i) {
1175 p[0] = data[i] >> 8;
1176 p[1] = data[i] & 0xff;
1177 p += 2;
1178 }
1179 png_write_row(png_ptr, (png_bytep)tran_data);
1180 }
1181 myfree(tran_data);
1182 myfree(data);
1183
1184 return 1;
1185}
1186
21c6936b
TC
1187static int
1188write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
1189 unsigned char *data, *volatile vdata = NULL;
1190 i_img_dim y;
1191 unsigned char pal_map[256];
1192 png_color pcolors[256];
1193 i_color colors[256];
1194 int count = i_colorcount(im);
1195 int i;
1196
1197 if (setjmp(png_jmpbuf(png_ptr))) {
1198 if (vdata)
1199 myfree(vdata);
1200
1201 return 0;
1202 }
1203
1204 i_getcolors(im, 0, colors, count);
1205 if (im->channels < 3) {
1206 /* convert the greyscale palette to color */
1207 int i;
1208 for (i = 0; i < count; ++i) {
1209 i_color *c = colors + i;
1210 c->channel[3] = c->channel[1];
1211 c->channel[2] = c->channel[1] = c->channel[0];
1212 }
1213 }
1214
1215 if (i_img_has_alpha(im)) {
1216 int i;
1217 int bottom_index = 0, top_index = count-1;
1218
1219 /* fill out the palette map */
1220 for (i = 0; i < count; ++i)
1221 pal_map[i] = i;
1222
1223 /* the PNG spec suggests sorting the palette by alpha, but that's
1224 unnecessary - all we want to do is move the opaque entries to
1225 the end */
1226 while (bottom_index < top_index) {
1227 if (colors[bottom_index].rgba.a == 255) {
1228 pal_map[bottom_index] = top_index;
1229 pal_map[top_index--] = bottom_index;
1230 }
1231 ++bottom_index;
1232 }
1233 }
1234
1235 for (i = 0; i < count; ++i) {
1236 int srci = i_img_has_alpha(im) ? pal_map[i] : i;
1237
1238 pcolors[i].red = colors[srci].rgb.r;
1239 pcolors[i].green = colors[srci].rgb.g;
1240 pcolors[i].blue = colors[srci].rgb.b;
1241 }
1242
1243 png_set_PLTE(png_ptr, info_ptr, pcolors, count);
1244
1245 if (i_img_has_alpha(im)) {
1246 unsigned char trans[256];
1247 int i;
1248
1249 for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
1250 trans[i] = colors[pal_map[i]].rgba.a;
1251 }
1252 png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
1253 }
1254
1255 png_write_info(png_ptr, info_ptr);
1256
1257 png_set_packing(png_ptr);
1258
1259 vdata = data = mymalloc(im->xsize);
1260 for (y = 0; y < im->ysize; y++) {
1261 i_gpal(im, 0, im->xsize, y, data);
1262 if (i_img_has_alpha(im)) {
1263 i_img_dim x;
1264 for (x = 0; x < im->xsize; ++x)
1265 data[x] = pal_map[data[x]];
1266 }
1267 png_write_row(png_ptr, (png_bytep)data);
1268 }
1269 myfree(data);
1270
1271 return 1;
1272}
1273
b8961d05
TC
1274static int
1275write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
1276 unsigned char *data, *volatile vdata = NULL;
1277 i_img_dim y;
21c6936b 1278
b8961d05
TC
1279 if (setjmp(png_jmpbuf(png_ptr))) {
1280 if (vdata)
1281 myfree(vdata);
1282
1283 return 0;
21c6936b 1284 }
21c6936b 1285
b8961d05
TC
1286 png_write_info(png_ptr, info_ptr);
1287
1288 png_set_packing(png_ptr);
1289
1290 vdata = data = mymalloc(im->xsize);
1291 for (y = 0; y < im->ysize; y++) {
1292 i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
1293 png_write_row(png_ptr, (png_bytep)data);
1294 }
1295 myfree(data);
1296
1297 return 1;
1298}
21c6936b 1299
38eab175
TC
1300static void
1301read_warn_handler(png_structp png_ptr, png_const_charp msg) {
1302 i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr);
1303 char *workp;
1304 size_t new_size;
1305
1306 mm_log((1, "PNG read warning '%s'\n", msg));
1307
1308 /* in case this is part of an error report */
1309 i_push_error(0, msg);
1310
1311 /* and save in the warnings so if we do manage to succeed, we
1312 * can save it as a tag
1313 */
1314 new_size = (rs->warnings ? strlen(rs->warnings) : 0)
1315 + 1 /* NUL */
1316 + strlen(msg) /* new text */
1317 + 1; /* newline */
1318 workp = myrealloc(rs->warnings, new_size);
1319 if (!rs->warnings)
1320 *workp = '\0';
1321 strcat(workp, msg);
1322 strcat(workp, "\n");
1323 rs->warnings = workp;
1324}
1325
1326static void
1327cleanup_read_state(i_png_read_statep rs) {
1328 if (rs->warnings)
1329 myfree(rs->warnings);
1330}