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