Commit | Line | Data |
---|---|---|
1d7e3124 | 1 | #include "impng.h" |
02d1d628 AMH |
2 | #include "png.h" |
3 | ||
02d1d628 AMH |
4 | /* this is a way to get number of channels from color space |
5 | * Color code to channel number */ | |
6 | ||
b33c08f8 | 7 | static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA]; |
02d1d628 AMH |
8 | |
9 | #define PNG_BYTES_TO_CHECK 4 | |
cfb628e2 TC |
10 | |
11 | static i_img * | |
12 | read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height); | |
13 | ||
c631b2d5 TC |
14 | static i_img * |
15 | read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height); | |
16 | ||
963d3602 TC |
17 | static i_img * |
18 | read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height); | |
19 | ||
79b4c849 TC |
20 | static i_img * |
21 | read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height); | |
22 | ||
647508aa TC |
23 | unsigned |
24 | i_png_lib_version(void) { | |
25 | return png_access_version_number(); | |
26 | } | |
02d1d628 | 27 | |
02d1d628 | 28 | static void |
790923a4 | 29 | wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { |
f5b4354e | 30 | io_glue *ig = png_get_io_ptr(png_ptr); |
38eab175 | 31 | ssize_t rc = i_io_read(ig, data, length); |
790923a4 | 32 | if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source."); |
02d1d628 AMH |
33 | } |
34 | ||
02d1d628 | 35 | static void |
790923a4 | 36 | wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { |
38eab175 | 37 | ssize_t rc; |
f5b4354e | 38 | io_glue *ig = png_get_io_ptr(png_ptr); |
6d5c85a2 | 39 | rc = i_io_write(ig, data, length); |
790923a4 | 40 | if (rc != length) png_error(png_ptr, "Write error on an iolayer source."); |
02d1d628 AMH |
41 | } |
42 | ||
43 | static void | |
790923a4 AMH |
44 | wiol_flush_data(png_structp png_ptr) { |
45 | /* XXX : This needs to be added to the io layer */ | |
02d1d628 | 46 | } |
02d1d628 | 47 | |
38eab175 TC |
48 | static void |
49 | error_handler(png_structp png_ptr, png_const_charp msg) { | |
50 | mm_log((1, "PNG error: '%s'\n", msg)); | |
51 | ||
52 | i_push_error(0, msg); | |
53 | longjmp(png_jmpbuf(png_ptr), 1); | |
54 | } | |
55 | ||
56 | /* | |
57 | ||
963d3602 TC |
58 | For writing a warning might have information about an error, so send |
59 | it to the error stack. | |
38eab175 TC |
60 | |
61 | */ | |
62 | static void | |
63 | write_warn_handler(png_structp png_ptr, png_const_charp msg) { | |
64 | mm_log((1, "PNG write warning '%s'\n", msg)); | |
65 | ||
66 | i_push_error(0, msg); | |
67 | } | |
68 | ||
69 | #define PNG_DIM_MAX 0x7fffffffL | |
70 | ||
02d1d628 | 71 | undef_int |
790923a4 | 72 | i_writepng_wiol(i_img *im, io_glue *ig) { |
02d1d628 | 73 | png_structp png_ptr; |
a807aae6 | 74 | png_infop info_ptr = NULL; |
38eab175 | 75 | i_img_dim width,height,y; |
02d1d628 | 76 | volatile int cspace,channels; |
faa9b3e7 TC |
77 | double xres, yres; |
78 | int aspect_only, have_res; | |
34e56776 TC |
79 | unsigned char *data; |
80 | unsigned char * volatile vdata = NULL; | |
02d1d628 | 81 | |
790923a4 | 82 | mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig)); |
8d14daab TC |
83 | |
84 | i_clear_error(); | |
85 | ||
86 | if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) { | |
87 | i_push_error(0, "image too large for PNG"); | |
88 | return 0; | |
89 | } | |
90 | ||
790923a4 AMH |
91 | height = im->ysize; |
92 | width = im->xsize; | |
02d1d628 | 93 | |
38eab175 TC |
94 | /* if we ever have 64-bit i_img_dim |
95 | * the libpng docs state that png_set_user_limits() can be used to | |
96 | * override the PNG_USER_*_MAX limits, but as implemented they | |
97 | * don't. We check against the theoretical limit of PNG here, and | |
98 | * try to override the limits below, in case the libpng | |
99 | * implementation ever matches the documentation. | |
100 | * | |
101 | * https://sourceforge.net/tracker/?func=detail&atid=105624&aid=3314943&group_id=5624 | |
647508aa | 102 | * fixed in libpng 1.5.3 |
34e56776 | 103 | */ |
38eab175 TC |
104 | if (width > PNG_DIM_MAX || height > PNG_DIM_MAX) { |
105 | i_push_error(0, "Image too large for PNG"); | |
106 | return 0; | |
107 | } | |
108 | ||
02d1d628 AMH |
109 | channels=im->channels; |
110 | ||
790923a4 | 111 | if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; } |
02d1d628 AMH |
112 | else { cspace=PNG_COLOR_TYPE_GRAY; channels--; } |
113 | ||
114 | if (channels) cspace|=PNG_COLOR_MASK_ALPHA; | |
115 | mm_log((1,"cspace=%d\n",cspace)); | |
116 | ||
790923a4 | 117 | channels = im->channels; |
02d1d628 AMH |
118 | |
119 | /* Create and initialize the png_struct with the desired error handler | |
120 | * functions. If you want to use the default stderr and longjump method, | |
121 | * you can supply NULL for the last three parameters. We also check that | |
122 | * the library version is compatible with the one used at compile time, | |
123 | * in case we are using dynamically linked libraries. REQUIRED. | |
124 | */ | |
125 | ||
38eab175 TC |
126 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, |
127 | error_handler, write_warn_handler); | |
02d1d628 | 128 | |
790923a4 AMH |
129 | if (png_ptr == NULL) return 0; |
130 | ||
02d1d628 AMH |
131 | |
132 | /* Allocate/initialize the image information data. REQUIRED */ | |
133 | info_ptr = png_create_info_struct(png_ptr); | |
134 | ||
135 | if (info_ptr == NULL) { | |
a807aae6 | 136 | png_destroy_write_struct(&png_ptr, &info_ptr); |
790923a4 | 137 | return 0; |
02d1d628 AMH |
138 | } |
139 | ||
140 | /* Set error handling. REQUIRED if you aren't supplying your own | |
141 | * error hadnling functions in the png_create_write_struct() call. | |
142 | */ | |
f5b4354e | 143 | if (setjmp(png_jmpbuf(png_ptr))) { |
a807aae6 | 144 | png_destroy_write_struct(&png_ptr, &info_ptr); |
34e56776 TC |
145 | if (vdata) |
146 | myfree(vdata); | |
02d1d628 AMH |
147 | return(0); |
148 | } | |
149 | ||
790923a4 | 150 | png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data); |
02d1d628 AMH |
151 | |
152 | /* Set the image information here. Width and height are up to 2^31, | |
153 | * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on | |
154 | * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, | |
155 | * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, | |
156 | * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or | |
157 | * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST | |
158 | * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED | |
159 | */ | |
160 | ||
38eab175 TC |
161 | /* by default, libpng (not PNG) limits the image size to a maximum |
162 | * 1000000 pixels in each direction, but Imager doesn't. | |
163 | * Configure libpng to avoid that limit. | |
164 | */ | |
165 | png_set_user_limits(png_ptr, width, height); | |
166 | ||
02d1d628 AMH |
167 | png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace, |
168 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
169 | ||
faa9b3e7 TC |
170 | have_res = 1; |
171 | if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) { | |
172 | if (i_tags_get_float(&im->tags, "i_yres", 0, &yres)) | |
173 | ; /* nothing to do */ | |
174 | else | |
175 | yres = xres; | |
176 | } | |
177 | else { | |
178 | if (i_tags_get_float(&im->tags, "i_yres", 0, &yres)) | |
179 | xres = yres; | |
180 | else | |
181 | have_res = 0; | |
182 | } | |
183 | if (have_res) { | |
184 | aspect_only = 0; | |
185 | i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only); | |
186 | xres /= 0.0254; | |
187 | yres /= 0.0254; | |
188 | png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, | |
189 | aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER); | |
190 | } | |
191 | ||
02d1d628 | 192 | png_write_info(png_ptr, info_ptr); |
790923a4 | 193 | |
34e56776 TC |
194 | vdata = data = mymalloc(im->xsize * im->channels); |
195 | for (y = 0; y < height; y++) { | |
196 | i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels); | |
197 | png_write_row(png_ptr, (png_bytep)data); | |
faa9b3e7 | 198 | } |
34e56776 | 199 | myfree(data); |
790923a4 | 200 | |
02d1d628 | 201 | png_write_end(png_ptr, info_ptr); |
790923a4 | 202 | |
a807aae6 | 203 | png_destroy_write_struct(&png_ptr, &info_ptr); |
02d1d628 | 204 | |
6d5c85a2 TC |
205 | if (i_io_close(ig)) |
206 | return 0; | |
10461f9a | 207 | |
02d1d628 AMH |
208 | return(1); |
209 | } | |
210 | ||
38eab175 | 211 | static void |
79b4c849 | 212 | get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth); |
38eab175 TC |
213 | |
214 | typedef struct { | |
215 | char *warnings; | |
216 | } i_png_read_state, *i_png_read_statep; | |
02d1d628 | 217 | |
38eab175 TC |
218 | static void |
219 | read_warn_handler(png_structp, png_const_charp); | |
02d1d628 | 220 | |
38eab175 TC |
221 | static void |
222 | cleanup_read_state(i_png_read_statep); | |
02d1d628 AMH |
223 | |
224 | i_img* | |
1d7e3124 | 225 | i_readpng_wiol(io_glue *ig) { |
02576e8d | 226 | i_img *im = NULL; |
02d1d628 AMH |
227 | png_structp png_ptr; |
228 | png_infop info_ptr; | |
790923a4 | 229 | png_uint_32 width, height; |
02d1d628 | 230 | int bit_depth, color_type, interlace_type; |
790923a4 AMH |
231 | int number_passes,y; |
232 | int channels,pass; | |
02d1d628 | 233 | unsigned int sig_read; |
38eab175 | 234 | i_png_read_state rs; |
647508aa TC |
235 | i_img_dim wmax, hmax; |
236 | size_t bytes; | |
02d1d628 | 237 | |
38eab175 | 238 | rs.warnings = NULL; |
790923a4 | 239 | sig_read = 0; |
02d1d628 | 240 | |
1d7e3124 | 241 | mm_log((1,"i_readpng_wiol(ig %p)\n", ig)); |
38eab175 | 242 | i_clear_error(); |
02d1d628 | 243 | |
38eab175 TC |
244 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &rs, |
245 | error_handler, read_warn_handler); | |
246 | if (!png_ptr) { | |
247 | i_push_error(0, "Cannot create PNG read structure"); | |
248 | return NULL; | |
249 | } | |
790923a4 | 250 | png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data); |
02d1d628 AMH |
251 | |
252 | info_ptr = png_create_info_struct(png_ptr); | |
253 | if (info_ptr == NULL) { | |
254 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); | |
38eab175 | 255 | i_push_error(0, "Cannot create PNG info structure"); |
02d1d628 AMH |
256 | return NULL; |
257 | } | |
258 | ||
f5b4354e | 259 | if (setjmp(png_jmpbuf(png_ptr))) { |
02576e8d | 260 | if (im) i_img_destroy(im); |
790923a4 | 261 | mm_log((1,"i_readpng_wiol: error.\n")); |
02d1d628 | 262 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
38eab175 | 263 | cleanup_read_state(&rs); |
02d1d628 AMH |
264 | return NULL; |
265 | } | |
647508aa TC |
266 | |
267 | /* we do our own limit checks */ | |
268 | png_set_user_limits(png_ptr, PNG_DIM_MAX, PNG_DIM_MAX); | |
790923a4 | 269 | |
02d1d628 AMH |
270 | png_set_sig_bytes(png_ptr, sig_read); |
271 | png_read_info(png_ptr, info_ptr); | |
272 | png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); | |
273 | ||
274 | mm_log((1, | |
790923a4 | 275 | "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n", |
02d1d628 AMH |
276 | width,height,bit_depth,color_type,interlace_type)); |
277 | ||
278 | CC2C[PNG_COLOR_TYPE_GRAY]=1; | |
279 | CC2C[PNG_COLOR_TYPE_PALETTE]=3; | |
280 | CC2C[PNG_COLOR_TYPE_RGB]=3; | |
281 | CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4; | |
282 | CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2; | |
790923a4 AMH |
283 | channels = CC2C[color_type]; |
284 | ||
285 | mm_log((1,"i_readpng_wiol: channels %d\n",channels)); | |
286 | ||
77157728 TC |
287 | if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) { |
288 | mm_log((1, "i_readpnm: image size exceeds limits\n")); | |
289 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
290 | return NULL; | |
291 | } | |
292 | ||
963d3602 TC |
293 | if (color_type == PNG_COLOR_TYPE_PALETTE) { |
294 | im = read_paletted(png_ptr, info_ptr, channels, width, height); | |
295 | } | |
79b4c849 TC |
296 | else if (color_type == PNG_COLOR_TYPE_GRAY |
297 | && bit_depth == 1 | |
298 | && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | |
299 | im = read_bilevel(png_ptr, info_ptr, width, height); | |
300 | } | |
c631b2d5 TC |
301 | else if (bit_depth == 16) { |
302 | im = read_direct16(png_ptr, info_ptr, channels, width, height); | |
303 | } | |
963d3602 TC |
304 | else { |
305 | im = read_direct8(png_ptr, info_ptr, channels, width, height); | |
306 | } | |
cfb628e2 TC |
307 | |
308 | if (im) | |
79b4c849 | 309 | get_png_tags(im, png_ptr, info_ptr, bit_depth); |
cfb628e2 TC |
310 | |
311 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
312 | ||
313 | if (im) { | |
314 | if (rs.warnings) { | |
315 | i_tags_set(&im->tags, "png_warnings", rs.warnings, -1); | |
316 | } | |
317 | } | |
318 | cleanup_read_state(&rs); | |
319 | ||
320 | mm_log((1,"(%p) <- i_readpng_wiol\n", im)); | |
321 | ||
322 | return im; | |
323 | } | |
324 | ||
325 | static i_img * | |
326 | read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, | |
327 | i_img_dim width, i_img_dim height) { | |
328 | i_img * volatile vim = NULL; | |
329 | int color_type = png_get_color_type(png_ptr, info_ptr); | |
330 | int bit_depth = png_get_bit_depth(png_ptr, info_ptr); | |
331 | i_img_dim y; | |
332 | int number_passes, pass; | |
333 | i_img *im; | |
34e56776 TC |
334 | unsigned char *line; |
335 | unsigned char * volatile vline = NULL; | |
cfb628e2 TC |
336 | |
337 | if (setjmp(png_jmpbuf(png_ptr))) { | |
338 | if (vim) i_img_destroy(vim); | |
34e56776 | 339 | if (vline) myfree(vline); |
cfb628e2 TC |
340 | |
341 | return NULL; | |
342 | } | |
343 | ||
344 | number_passes = png_set_interlace_handling(png_ptr); | |
345 | mm_log((1,"number of passes=%d\n",number_passes)); | |
346 | ||
02d1d628 AMH |
347 | png_set_strip_16(png_ptr); |
348 | png_set_packing(png_ptr); | |
6829f5f6 | 349 | |
cfb628e2 TC |
350 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) |
351 | png_set_expand(png_ptr); | |
352 | ||
6829f5f6 AMH |
353 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
354 | channels++; | |
355 | mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels)); | |
356 | png_set_expand(png_ptr); | |
357 | } | |
790923a4 | 358 | |
02d1d628 | 359 | png_read_update_info(png_ptr, info_ptr); |
790923a4 | 360 | |
cfb628e2 | 361 | im = vim = i_img_8_new(width,height,channels); |
352c64ed TC |
362 | if (!im) { |
363 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
364 | return NULL; | |
365 | } | |
02d1d628 | 366 | |
34e56776 | 367 | line = vline = mymalloc(channels * width); |
cfb628e2 TC |
368 | for (pass = 0; pass < number_passes; pass++) { |
369 | for (y = 0; y < height; y++) { | |
34e56776 TC |
370 | if (pass > 0) |
371 | i_gsamp(im, 0, width, y, line, NULL, channels); | |
372 | png_read_row(png_ptr,(png_bytep)line, NULL); | |
373 | i_psamp(im, 0, width, y, line, NULL, channels); | |
cfb628e2 | 374 | } |
38eab175 | 375 | } |
34e56776 TC |
376 | myfree(line); |
377 | vline = NULL; | |
02d1d628 | 378 | |
cfb628e2 TC |
379 | png_read_end(png_ptr, info_ptr); |
380 | ||
790923a4 AMH |
381 | return im; |
382 | } | |
faa9b3e7 | 383 | |
c631b2d5 TC |
384 | static i_img * |
385 | read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, | |
386 | i_img_dim width, i_img_dim height) { | |
387 | i_img * volatile vim = NULL; | |
388 | int color_type = png_get_color_type(png_ptr, info_ptr); | |
389 | i_img_dim x, y; | |
390 | int number_passes, pass; | |
391 | i_img *im; | |
392 | unsigned char *line; | |
393 | unsigned char * volatile vline = NULL; | |
394 | unsigned *bits_line; | |
395 | unsigned * volatile vbits_line = NULL; | |
396 | size_t row_bytes; | |
397 | ||
398 | if (setjmp(png_jmpbuf(png_ptr))) { | |
399 | if (vim) i_img_destroy(vim); | |
400 | if (vline) myfree(vline); | |
401 | if (vbits_line) myfree(vbits_line); | |
402 | ||
403 | return NULL; | |
404 | } | |
405 | ||
406 | number_passes = png_set_interlace_handling(png_ptr); | |
407 | mm_log((1,"number of passes=%d\n",number_passes)); | |
408 | ||
409 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | |
410 | channels++; | |
411 | mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels)); | |
412 | png_set_expand(png_ptr); | |
413 | } | |
414 | ||
415 | png_read_update_info(png_ptr, info_ptr); | |
416 | ||
417 | im = vim = i_img_16_new(width,height,channels); | |
418 | if (!im) { | |
419 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
420 | return NULL; | |
421 | } | |
422 | ||
423 | row_bytes = png_get_rowbytes(png_ptr, info_ptr); | |
424 | line = vline = mymalloc(row_bytes); | |
425 | memset(line, 0, row_bytes); | |
426 | bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels); | |
427 | for (pass = 0; pass < number_passes; pass++) { | |
428 | for (y = 0; y < height; y++) { | |
429 | if (pass > 0) { | |
430 | i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16); | |
431 | for (x = 0; x < width * channels; ++x) { | |
432 | line[x*2] = bits_line[x] >> 8; | |
433 | line[x*2+1] = bits_line[x] & 0xff; | |
434 | } | |
435 | } | |
436 | png_read_row(png_ptr,(png_bytep)line, NULL); | |
437 | for (x = 0; x < width * channels; ++x) | |
438 | bits_line[x] = (line[x*2] << 8) + line[x*2+1]; | |
439 | i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16); | |
440 | } | |
441 | } | |
442 | myfree(line); | |
443 | myfree(bits_line); | |
444 | vline = NULL; | |
445 | vbits_line = NULL; | |
446 | ||
447 | png_read_end(png_ptr, info_ptr); | |
448 | ||
449 | return im; | |
450 | } | |
451 | ||
79b4c849 TC |
452 | static i_img * |
453 | read_bilevel(png_structp png_ptr, png_infop info_ptr, | |
454 | i_img_dim width, i_img_dim height) { | |
455 | i_img * volatile vim = NULL; | |
456 | i_img_dim x, y; | |
457 | int number_passes, pass; | |
458 | i_img *im; | |
459 | unsigned char *line; | |
460 | unsigned char * volatile vline = NULL; | |
461 | i_color palette[2]; | |
462 | ||
463 | if (setjmp(png_jmpbuf(png_ptr))) { | |
464 | if (vim) i_img_destroy(vim); | |
465 | if (vline) myfree(vline); | |
466 | ||
467 | return NULL; | |
468 | } | |
469 | ||
470 | number_passes = png_set_interlace_handling(png_ptr); | |
471 | mm_log((1,"number of passes=%d\n",number_passes)); | |
472 | ||
473 | png_set_packing(png_ptr); | |
474 | ||
475 | png_set_expand(png_ptr); | |
476 | ||
477 | png_read_update_info(png_ptr, info_ptr); | |
478 | ||
479 | im = vim = i_img_pal_new(width, height, 1, 256); | |
480 | if (!im) { | |
481 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
482 | return NULL; | |
483 | } | |
484 | ||
485 | palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] = | |
486 | palette[0].channel[3] = 0; | |
487 | palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] = | |
488 | palette[1].channel[3] = 255; | |
489 | i_addcolors(im, palette, 2); | |
490 | ||
491 | line = vline = mymalloc(width); | |
492 | memset(line, 0, width); | |
493 | for (pass = 0; pass < number_passes; pass++) { | |
494 | for (y = 0; y < height; y++) { | |
495 | if (pass > 0) { | |
496 | i_gpal(im, 0, width, y, line); | |
497 | /* expand indexes back to 0/255 */ | |
498 | for (x = 0; x < width; ++x) | |
499 | line[x] = line[x] ? 255 : 0; | |
500 | } | |
501 | png_read_row(png_ptr,(png_bytep)line, NULL); | |
502 | ||
503 | /* back to palette indexes */ | |
504 | for (x = 0; x < width; ++x) | |
505 | line[x] = line[x] ? 1 : 0; | |
506 | i_ppal(im, 0, width, y, line); | |
507 | } | |
508 | } | |
509 | myfree(line); | |
510 | vline = NULL; | |
511 | ||
512 | png_read_end(png_ptr, info_ptr); | |
513 | ||
514 | return im; | |
515 | } | |
516 | ||
517 | /* FIXME: do we need to unscale palette color values from the | |
518 | supplied alphas? */ | |
963d3602 TC |
519 | static i_img * |
520 | read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, | |
521 | i_img_dim width, i_img_dim height) { | |
522 | i_img * volatile vim = NULL; | |
523 | int color_type = png_get_color_type(png_ptr, info_ptr); | |
524 | int bit_depth = png_get_bit_depth(png_ptr, info_ptr); | |
525 | i_img_dim y; | |
526 | int number_passes, pass; | |
527 | i_img *im; | |
528 | unsigned char *line; | |
529 | unsigned char * volatile vline = NULL; | |
530 | int num_palette, i; | |
531 | png_colorp png_palette; | |
532 | png_bytep png_pal_trans; | |
533 | png_color_16p png_color_trans; | |
534 | int num_pal_trans; | |
535 | ||
536 | if (setjmp(png_jmpbuf(png_ptr))) { | |
537 | if (vim) i_img_destroy(vim); | |
538 | if (vline) myfree(vline); | |
539 | ||
540 | return NULL; | |
541 | } | |
542 | ||
543 | number_passes = png_set_interlace_handling(png_ptr); | |
544 | mm_log((1,"number of passes=%d\n",number_passes)); | |
545 | ||
546 | png_set_strip_16(png_ptr); | |
547 | png_set_packing(png_ptr); | |
548 | ||
549 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) | |
550 | png_set_expand(png_ptr); | |
551 | ||
552 | if (!png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette)) { | |
553 | i_push_error(0, "Paletted image with no PLTE chunk"); | |
554 | return NULL; | |
555 | } | |
556 | ||
557 | if (png_get_tRNS(png_ptr, info_ptr, &png_pal_trans, &num_pal_trans, | |
558 | &png_color_trans)) { | |
559 | channels++; | |
560 | } | |
561 | else { | |
562 | num_pal_trans = 0; | |
563 | } | |
564 | ||
565 | png_read_update_info(png_ptr, info_ptr); | |
566 | ||
567 | im = vim = i_img_pal_new(width, height, channels, 256); | |
568 | if (!im) { | |
569 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
570 | return NULL; | |
571 | } | |
572 | ||
573 | for (i = 0; i < num_palette; ++i) { | |
574 | i_color c; | |
575 | ||
576 | c.rgba.r = png_palette[i].red; | |
577 | c.rgba.g = png_palette[i].green; | |
578 | c.rgba.b = png_palette[i].blue; | |
579 | if (i < num_pal_trans) | |
580 | c.rgba.a = png_pal_trans[i]; | |
581 | else | |
582 | c.rgba.a = 255; | |
583 | i_addcolors(im, &c, 1); | |
584 | } | |
585 | ||
586 | line = vline = mymalloc(width); | |
587 | for (pass = 0; pass < number_passes; pass++) { | |
588 | for (y = 0; y < height; y++) { | |
589 | if (pass > 0) | |
590 | i_gpal(im, 0, width, y, line); | |
591 | png_read_row(png_ptr,(png_bytep)line, NULL); | |
592 | i_ppal(im, 0, width, y, line); | |
593 | } | |
594 | } | |
595 | myfree(line); | |
596 | vline = NULL; | |
597 | ||
598 | png_read_end(png_ptr, info_ptr); | |
599 | ||
600 | return im; | |
601 | } | |
602 | ||
38eab175 | 603 | static void |
79b4c849 | 604 | get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth) { |
faa9b3e7 TC |
605 | png_uint_32 xres, yres; |
606 | int unit_type; | |
352c64ed | 607 | |
1d7e3124 | 608 | i_tags_set(&im->tags, "i_format", "png", -1); |
faa9b3e7 TC |
609 | if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) { |
610 | mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type)); | |
611 | if (unit_type == PNG_RESOLUTION_METER) { | |
2e41e30b TC |
612 | i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5); |
613 | i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5); | |
faa9b3e7 TC |
614 | } |
615 | else { | |
1d7e3124 TC |
616 | i_tags_setn(&im->tags, "i_xres", xres); |
617 | i_tags_setn(&im->tags, "i_yres", yres); | |
618 | i_tags_setn(&im->tags, "i_aspect_only", 1); | |
faa9b3e7 TC |
619 | } |
620 | } | |
a4fa5d5e TC |
621 | { |
622 | int interlace = png_get_interlace_type(png_ptr, info_ptr); | |
623 | ||
624 | i_tags_setn(&im->tags, "png_interlace", interlace != PNG_INTERLACE_NONE); | |
625 | switch (interlace) { | |
626 | case PNG_INTERLACE_NONE: | |
627 | i_tags_set(&im->tags, "png_interlace_name", "none", -1); | |
628 | break; | |
629 | ||
630 | case PNG_INTERLACE_ADAM7: | |
631 | i_tags_set(&im->tags, "png_interlace_name", "adam7", -1); | |
632 | break; | |
633 | ||
634 | default: | |
635 | i_tags_set(&im->tags, "png_interlace_name", "unknown", -1); | |
636 | break; | |
637 | } | |
00cff942 TC |
638 | } |
639 | ||
79b4c849 TC |
640 | /* the various readers can call png_set_expand(), libpng will make |
641 | it's internal record of bit_depth at least 8 in that case */ | |
642 | i_tags_setn(&im->tags, "png_bits", bit_depth); | |
faa9b3e7 | 643 | } |
38eab175 TC |
644 | |
645 | static void | |
646 | read_warn_handler(png_structp png_ptr, png_const_charp msg) { | |
647 | i_png_read_statep rs = (i_png_read_statep)png_get_error_ptr(png_ptr); | |
648 | char *workp; | |
649 | size_t new_size; | |
650 | ||
651 | mm_log((1, "PNG read warning '%s'\n", msg)); | |
652 | ||
653 | /* in case this is part of an error report */ | |
654 | i_push_error(0, msg); | |
655 | ||
656 | /* and save in the warnings so if we do manage to succeed, we | |
657 | * can save it as a tag | |
658 | */ | |
659 | new_size = (rs->warnings ? strlen(rs->warnings) : 0) | |
660 | + 1 /* NUL */ | |
661 | + strlen(msg) /* new text */ | |
662 | + 1; /* newline */ | |
663 | workp = myrealloc(rs->warnings, new_size); | |
664 | if (!rs->warnings) | |
665 | *workp = '\0'; | |
666 | strcat(workp, msg); | |
667 | strcat(workp, "\n"); | |
668 | rs->warnings = workp; | |
669 | } | |
670 | ||
671 | static void | |
672 | cleanup_read_state(i_png_read_statep rs) { | |
673 | if (rs->warnings) | |
674 | myfree(rs->warnings); | |
675 | } |