1 : #include <stdio.h>
2 : #include <math.h>
3 : #include <string.h>
4 : #include <stdlib.h>
5 : #include "gd.h"
6 :
7 : /* JCE: Arrange HAVE_LIBPNG so that it can be set in gd.h */
8 : #ifdef HAVE_LIBPNG
9 :
10 : #include "png.h" /* includes zlib.h and setjmp.h */
11 : #include "gdhelpers.h"
12 :
13 : #define TRUE 1
14 : #define FALSE 0
15 :
16 : /*---------------------------------------------------------------------------
17 :
18 : gd_png.c Copyright 1999 Greg Roelofs and Thomas Boutell
19 :
20 : The routines in this file, gdImagePng*() and gdImageCreateFromPng*(),
21 : are drop-in replacements for gdImageGif*() and gdImageCreateFromGif*(),
22 : except that these functions are noisier in the case of errors (comment
23 : out all fprintf() statements to disable that).
24 :
25 : GD 2.0 supports RGBA truecolor and will read and write truecolor PNGs.
26 : GD 2.0 supports 8 bits of color resolution per channel and
27 : 7 bits of alpha channel resolution. Images with more than 8 bits
28 : per channel are reduced to 8 bits. Images with an alpha channel are
29 : only able to resolve down to '1/128th opaque' instead of '1/256th',
30 : and this conversion is also automatic. I very much doubt you can see it.
31 : Both tRNS and true alpha are supported.
32 :
33 : Gamma is ignored, and there is no support for text annotations.
34 :
35 : Last updated: 9 February 2001
36 :
37 : ---------------------------------------------------------------------------*/
38 :
39 : #ifndef PNG_SETJMP_NOT_SUPPORTED
40 : typedef struct _jmpbuf_wrapper
41 : {
42 : jmp_buf jmpbuf;
43 : } jmpbuf_wrapper;
44 :
45 : static jmpbuf_wrapper gdPngJmpbufStruct;
46 :
47 : static void gdPngErrorHandler (png_structp png_ptr, png_const_charp msg)
48 2 : {
49 : jmpbuf_wrapper *jmpbuf_ptr;
50 :
51 : /* This function, aside from the extra step of retrieving the "error
52 : * pointer" (below) and the fact that it exists within the application
53 : * rather than within libpng, is essentially identical to libpng's
54 : * default error handler. The second point is critical: since both
55 : * setjmp() and longjmp() are called from the same code, they are
56 : * guaranteed to have compatible notions of how big a jmp_buf is,
57 : * regardless of whether _BSD_SOURCE or anything else has (or has not)
58 : * been defined.
59 : */
60 :
61 2 : php_gd_error_ex(E_WARNING, "gd-png: fatal libpng error: %s", msg);
62 :
63 2 : jmpbuf_ptr = png_get_error_ptr (png_ptr);
64 2 : if (jmpbuf_ptr == NULL) { /* we are completely hosed now */
65 0 : php_gd_error_ex(E_ERROR, "gd-png: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
66 : }
67 :
68 2 : longjmp (jmpbuf_ptr->jmpbuf, 1);
69 : }
70 : #endif
71 :
72 : static void gdPngReadData (png_structp png_ptr, png_bytep data, png_size_t length)
73 692 : {
74 : int check;
75 692 : check = gdGetBuf(data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
76 692 : if (check != length) {
77 2 : png_error(png_ptr, "Read Error: truncated data");
78 : }
79 690 : }
80 :
81 : static void gdPngWriteData (png_structp png_ptr, png_bytep data, png_size_t length)
82 1545 : {
83 1545 : gdPutBuf (data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
84 1545 : }
85 :
86 : static void gdPngFlushData (png_structp png_ptr)
87 0 : {
88 0 : }
89 :
90 : gdImagePtr gdImageCreateFromPng (FILE * inFile)
91 22 : {
92 : gdImagePtr im;
93 22 : gdIOCtx *in = gdNewFileCtx(inFile);
94 22 : im = gdImageCreateFromPngCtx(in);
95 22 : in->gd_free(in);
96 :
97 22 : return im;
98 : }
99 :
100 : gdImagePtr gdImageCreateFromPngPtr (int size, void *data)
101 0 : {
102 : gdImagePtr im;
103 0 : gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
104 0 : im = gdImageCreateFromPngCtx(in);
105 0 : in->gd_free(in);
106 0 : return im;
107 : }
108 :
109 : /* This routine is based in part on the Chapter 13 demo code in "PNG: The
110 : * Definitive Guide" (http://www.cdrom.com/pub/png/pngbook.html).
111 : */
112 : gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
113 24 : {
114 : png_byte sig[8];
115 : png_structp png_ptr;
116 : png_infop info_ptr;
117 : png_uint_32 width, height, rowbytes, w, h;
118 : int bit_depth, color_type, interlace_type;
119 : int num_palette, num_trans;
120 : png_colorp palette;
121 : png_color_16p trans_gray_rgb;
122 : png_color_16p trans_color_rgb;
123 : png_bytep trans;
124 24 : png_bytep image_data = NULL;
125 24 : png_bytepp row_pointers = NULL;
126 24 : gdImagePtr im = NULL;
127 24 : int i, j, *open = NULL;
128 24 : volatile int transparent = -1;
129 24 : volatile int palette_allocated = FALSE;
130 :
131 : /* Make sure the signature can't match by dumb luck -- TBB */
132 : /* GRR: isn't sizeof(infile) equal to the size of the pointer? */
133 24 : memset (sig, 0, sizeof(sig));
134 :
135 : /* first do a quick check that the file really is a PNG image; could
136 : * have used slightly more general png_sig_cmp() function instead
137 : */
138 24 : if (gdGetBuf(sig, 8, infile) < 8) {
139 1 : return NULL;
140 : }
141 :
142 23 : if (!png_check_sig (sig, 8)) { /* bad signature */
143 0 : return NULL;
144 : }
145 :
146 : #ifndef PNG_SETJMP_NOT_SUPPORTED
147 23 : png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &gdPngJmpbufStruct, gdPngErrorHandler, NULL);
148 : #else
149 : png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
150 : #endif
151 23 : if (png_ptr == NULL) {
152 0 : php_gd_error("gd-png error: cannot allocate libpng main struct");
153 0 : return NULL;
154 : }
155 :
156 23 : info_ptr = png_create_info_struct(png_ptr);
157 23 : if (info_ptr == NULL) {
158 0 : php_gd_error("gd-png error: cannot allocate libpng info struct");
159 0 : png_destroy_read_struct (&png_ptr, NULL, NULL);
160 :
161 0 : return NULL;
162 : }
163 :
164 : /* we could create a second info struct here (end_info), but it's only
165 : * useful if we want to keep pre- and post-IDAT chunk info separated
166 : * (mainly for PNG-aware image editors and converters)
167 : */
168 :
169 : /* setjmp() must be called in every non-callback function that calls a
170 : * PNG-reading libpng function
171 : */
172 : #ifndef PNG_SETJMP_NOT_SUPPORTED
173 23 : if (setjmp(gdPngJmpbufStruct.jmpbuf)) {
174 1 : php_gd_error("gd-png error: setjmp returns error condition");
175 1 : png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
176 :
177 1 : return NULL;
178 : }
179 : #endif
180 :
181 23 : png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */
182 :
183 23 : png_set_read_fn(png_ptr, (void *) infile, gdPngReadData);
184 23 : png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */
185 :
186 22 : png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
187 43 : if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) {
188 21 : im = gdImageCreateTrueColor((int) width, (int) height);
189 : } else {
190 1 : im = gdImageCreate((int) width, (int) height);
191 : }
192 22 : if (im == NULL) {
193 0 : php_gd_error("gd-png error: cannot allocate gdImage struct");
194 0 : png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
195 0 : gdFree(image_data);
196 0 : gdFree(row_pointers);
197 :
198 0 : return NULL;
199 : }
200 :
201 22 : if (bit_depth == 16) {
202 0 : png_set_strip_16(png_ptr);
203 22 : } else if (bit_depth < 8) {
204 1 : png_set_packing (png_ptr); /* expand to 1 byte per pixel */
205 : }
206 :
207 : /* setjmp() must be called in every non-callback function that calls a
208 : * PNG-reading libpng function
209 : */
210 : #ifndef PNG_SETJMP_NOT_SUPPORTED
211 22 : if (setjmp(gdPngJmpbufStruct.jmpbuf)) {
212 1 : php_gd_error("gd-png error: setjmp returns error condition");
213 1 : png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
214 1 : gdFree(image_data);
215 1 : gdFree(row_pointers);
216 1 : if (im) {
217 1 : gdImageDestroy(im);
218 : }
219 1 : return NULL;
220 : }
221 : #endif
222 :
223 :
224 22 : switch (color_type) {
225 : case PNG_COLOR_TYPE_PALETTE:
226 1 : png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
227 : #ifdef DEBUG
228 : php_gd_error("gd-png color_type is palette, colors: %d", num_palette);
229 : #endif /* DEBUG */
230 1 : if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
231 : /* gd 2.0: we support this rather thoroughly now. Grab the
232 : * first fully transparent entry, if any, as the value of
233 : * the simple-transparency index, mostly for backwards
234 : * binary compatibility. The alpha channel is where it's
235 : * really at these days.
236 : */
237 0 : int firstZero = 1;
238 0 : png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
239 0 : for (i = 0; i < num_trans; ++i) {
240 0 : im->alpha[i] = gdAlphaMax - (trans[i] >> 1);
241 0 : if ((trans[i] == 0) && (firstZero)) {
242 0 : transparent = i;
243 0 : firstZero = 0;
244 : }
245 : }
246 : }
247 1 : break;
248 : case PNG_COLOR_TYPE_GRAY:
249 : case PNG_COLOR_TYPE_GRAY_ALPHA:
250 : /* create a fake palette and check for single-shade transparency */
251 0 : if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL) {
252 0 : php_gd_error("gd-png error: cannot allocate gray palette");
253 0 : png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
254 :
255 0 : return NULL;
256 : }
257 0 : palette_allocated = TRUE;
258 0 : if (bit_depth < 8) {
259 0 : num_palette = 1 << bit_depth;
260 0 : for (i = 0; i < 256; ++i) {
261 0 : j = (255 * i) / (num_palette - 1);
262 0 : palette[i].red = palette[i].green = palette[i].blue = j;
263 : }
264 : } else {
265 0 : num_palette = 256;
266 0 : for (i = 0; i < 256; ++i) {
267 0 : palette[i].red = palette[i].green = palette[i].blue = i;
268 : }
269 : }
270 0 : if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
271 0 : png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb);
272 0 : if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */
273 0 : transparent = trans_gray_rgb->gray >> 8;
274 : } else {
275 0 : transparent = trans_gray_rgb->gray;
276 : }
277 : /* Note slight error in 16-bit case: up to 256 16-bit shades
278 : * may get mapped to a single 8-bit shade, and only one of them
279 : * is supposed to be transparent. IOW, both opaque pixels and
280 : * transparent pixels will be mapped into the transparent entry.
281 : * There is no particularly good way around this in the case
282 : * that all 256 8-bit shades are used, but one could write some
283 : * custom 16-bit code to handle the case where there are gdFree
284 : * palette entries. This error will be extremely rare in
285 : * general, though. (Quite possibly there is only one such
286 : * image in existence.)
287 : */
288 : }
289 0 : break;
290 :
291 : case PNG_COLOR_TYPE_RGB:
292 : case PNG_COLOR_TYPE_RGB_ALPHA:
293 : /* gd 2.0: we now support truecolor. See the comment above
294 : * for a rare situation in which the transparent pixel may not
295 : * work properly with 16-bit channels.
296 : */
297 21 : if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
298 0 : png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color_rgb);
299 0 : if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */
300 0 : transparent = gdTrueColor(trans_color_rgb->red >> 8,
301 : trans_color_rgb->green >> 8,
302 : trans_color_rgb->blue >> 8);
303 : } else {
304 0 : transparent = gdTrueColor(trans_color_rgb->red,
305 : trans_color_rgb->green,
306 : trans_color_rgb->blue);
307 : }
308 : }
309 : break;
310 : }
311 :
312 22 : png_read_update_info(png_ptr, info_ptr);
313 :
314 : /* allocate space for the PNG image data */
315 22 : rowbytes = png_get_rowbytes(png_ptr, info_ptr);
316 22 : image_data = (png_bytep) safe_emalloc(rowbytes, height, 0);
317 :
318 22 : row_pointers = (png_bytepp) safe_emalloc(height, sizeof(png_bytep), 0);
319 :
320 : /* set the individual row_pointers to point at the correct offsets */
321 3563 : for (h = 0; h < height; ++h) {
322 3541 : row_pointers[h] = image_data + h * rowbytes;
323 : }
324 :
325 22 : png_read_image(png_ptr, row_pointers); /* read whole image... */
326 21 : png_read_end(png_ptr, NULL); /* ...done! */
327 :
328 21 : if (!im->trueColor) {
329 1 : im->colorsTotal = num_palette;
330 : /* load the palette and mark all entries "open" (unused) for now */
331 1 : open = im->open;
332 3 : for (i = 0; i < num_palette; ++i) {
333 2 : im->red[i] = palette[i].red;
334 2 : im->green[i] = palette[i].green;
335 2 : im->blue[i] = palette[i].blue;
336 2 : open[i] = 1;
337 : }
338 255 : for (i = num_palette; i < gdMaxColors; ++i) {
339 254 : open[i] = 1;
340 : }
341 : }
342 : /* 2.0.12: Slaven Rezic: palette images are not the only images
343 : * with a simple transparent color setting.
344 : */
345 21 : im->transparent = transparent;
346 21 : im->interlace = (interlace_type == PNG_INTERLACE_ADAM7);
347 :
348 : /* can't nuke structs until done with palette */
349 21 : png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
350 21 : switch (color_type) {
351 : case PNG_COLOR_TYPE_RGB:
352 3005 : for (h = 0; h < height; h++) {
353 2986 : int boffset = 0;
354 589822 : for (w = 0; w < width; w++) {
355 586836 : register png_byte r = row_pointers[h][boffset++];
356 586836 : register png_byte g = row_pointers[h][boffset++];
357 586836 : register png_byte b = row_pointers[h][boffset++];
358 586836 : im->tpixels[h][w] = gdTrueColor (r, g, b);
359 : }
360 : }
361 19 : break;
362 :
363 : case PNG_COLOR_TYPE_RGB_ALPHA:
364 11 : for (h = 0; h < height; h++) {
365 10 : int boffset = 0;
366 110 : for (w = 0; w < width; w++) {
367 100 : register png_byte r = row_pointers[h][boffset++];
368 100 : register png_byte g = row_pointers[h][boffset++];
369 100 : register png_byte b = row_pointers[h][boffset++];
370 :
371 : /* gd has only 7 bits of alpha channel resolution, and
372 : * 127 is transparent, 0 opaque. A moment of convenience,
373 : * a lifetime of compatibility.
374 : */
375 :
376 100 : register png_byte a = gdAlphaMax - (row_pointers[h][boffset++] >> 1);
377 100 : im->tpixels[h][w] = gdTrueColorAlpha(r, g, b, a);
378 : }
379 : }
380 1 : break;
381 :
382 : default:
383 : /* Palette image, or something coerced to be one */
384 6 : for (h = 0; h < height; ++h) {
385 30 : for (w = 0; w < width; ++w) {
386 25 : register png_byte idx = row_pointers[h][w];
387 25 : im->pixels[h][w] = idx;
388 25 : open[idx] = 0;
389 : }
390 : }
391 : }
392 : #ifdef DEBUG
393 : if (!im->trueColor) {
394 : for (i = num_palette; i < gdMaxColors; ++i) {
395 : if (!open[i]) {
396 : php_gd_error("gd-png warning: image data references out-of-range color index (%d)", i);
397 : }
398 : }
399 : }
400 : #endif
401 :
402 21 : if (palette_allocated) {
403 0 : gdFree(palette);
404 : }
405 21 : gdFree(image_data);
406 21 : gdFree(row_pointers);
407 :
408 21 : return im;
409 : }
410 :
411 : void gdImagePngEx (gdImagePtr im, FILE * outFile, int level, int basefilter)
412 0 : {
413 0 : gdIOCtx *out = gdNewFileCtx(outFile);
414 0 : gdImagePngCtxEx(im, out, level, basefilter);
415 0 : out->gd_free(out);
416 0 : }
417 :
418 : void gdImagePng (gdImagePtr im, FILE * outFile)
419 0 : {
420 0 : gdIOCtx *out = gdNewFileCtx(outFile);
421 0 : gdImagePngCtxEx(im, out, -1, -1);
422 0 : out->gd_free(out);
423 0 : }
424 :
425 : void * gdImagePngPtr (gdImagePtr im, int *size)
426 0 : {
427 : void *rv;
428 0 : gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
429 0 : gdImagePngCtxEx(im, out, -1, -1);
430 0 : rv = gdDPExtractData(out, size);
431 0 : out->gd_free(out);
432 :
433 0 : return rv;
434 : }
435 :
436 : void * gdImagePngPtrEx (gdImagePtr im, int *size, int level, int basefilter)
437 0 : {
438 : void *rv;
439 0 : gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
440 0 : gdImagePngCtxEx(im, out, level, basefilter);
441 0 : rv = gdDPExtractData(out, size);
442 0 : out->gd_free(out);
443 0 : return rv;
444 : }
445 :
446 : void gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile)
447 0 : {
448 0 : gdImagePngCtxEx(im, outfile, -1, -1);
449 0 : }
450 :
451 : /* This routine is based in part on code from Dale Lutz (Safe Software Inc.)
452 : * and in part on demo code from Chapter 15 of "PNG: The Definitive Guide"
453 : * (http://www.cdrom.com/pub/png/pngbook.html).
454 : */
455 : void gdImagePngCtxEx (gdImagePtr im, gdIOCtx * outfile, int level, int basefilter)
456 59 : {
457 59 : int i, j, bit_depth = 0, interlace_type;
458 59 : int width = im->sx;
459 59 : int height = im->sy;
460 59 : int colors = im->colorsTotal;
461 59 : int *open = im->open;
462 : int mapping[gdMaxColors]; /* mapping[gd_index] == png_index */
463 : png_byte trans_values[256];
464 : png_color_16 trans_rgb_value;
465 : png_color palette[gdMaxColors];
466 : png_structp png_ptr;
467 : png_infop info_ptr;
468 59 : volatile int transparent = im->transparent;
469 59 : volatile int remap = FALSE;
470 :
471 : #ifndef PNG_SETJMP_NOT_SUPPORTED
472 59 : png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &gdPngJmpbufStruct, gdPngErrorHandler, NULL);
473 : #else
474 : png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
475 : #endif
476 59 : if (png_ptr == NULL) {
477 0 : php_gd_error("gd-png error: cannot allocate libpng main struct");
478 0 : return;
479 : }
480 :
481 59 : info_ptr = png_create_info_struct(png_ptr);
482 59 : if (info_ptr == NULL) {
483 0 : php_gd_error("gd-png error: cannot allocate libpng info struct");
484 0 : png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
485 :
486 0 : return;
487 : }
488 :
489 : #ifndef PNG_SETJMP_NOT_SUPPORTED
490 59 : if (setjmp (gdPngJmpbufStruct.jmpbuf)) {
491 0 : php_gd_error("gd-png error: setjmp returns error condition");
492 0 : png_destroy_write_struct (&png_ptr, &info_ptr);
493 :
494 0 : return;
495 : }
496 : #endif
497 :
498 59 : png_set_write_fn(png_ptr, (void *) outfile, gdPngWriteData, gdPngFlushData);
499 :
500 : /* This is best for palette images, and libpng defaults to it for
501 : * palette images anyway, so we don't need to do it explicitly.
502 : * What to ideally do for truecolor images depends, alas, on the image.
503 : * gd is intentionally imperfect and doesn't spend a lot of time
504 : * fussing with such things.
505 : */
506 :
507 : /* png_set_filter(png_ptr, 0, PNG_FILTER_NONE); */
508 :
509 : /* 2.0.12: this is finally a parameter */
510 59 : png_set_compression_level(png_ptr, level);
511 59 : if (basefilter >= 0) {
512 0 : png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, basefilter);
513 : }
514 :
515 : /* can set this to a smaller value without compromising compression if all
516 : * image data is 16K or less; will save some decoder memory [min == 8]
517 : */
518 :
519 : /* png_set_compression_window_bits(png_ptr, 15); */
520 :
521 59 : if (!im->trueColor) {
522 11 : if (transparent >= im->colorsTotal || (transparent >= 0 && open[transparent])) {
523 0 : transparent = -1;
524 : }
525 :
526 2827 : for (i = 0; i < gdMaxColors; ++i) {
527 2816 : mapping[i] = -1;
528 : }
529 :
530 : /* count actual number of colors used (colorsTotal == high-water mark) */
531 11 : colors = 0;
532 806 : for (i = 0; i < im->colorsTotal; ++i) {
533 795 : if (!open[i]) {
534 795 : mapping[i] = colors;
535 795 : ++colors;
536 : }
537 : }
538 11 : if (colors == 0) {
539 1 : php_gd_error("gd-png error: no colors in palette");
540 1 : goto bail;
541 : }
542 10 : if (colors < im->colorsTotal) {
543 0 : remap = TRUE;
544 : }
545 10 : if (colors <= 2) {
546 5 : bit_depth = 1;
547 5 : } else if (colors <= 4) {
548 0 : bit_depth = 2;
549 5 : } else if (colors <= 16) {
550 2 : bit_depth = 4;
551 : } else {
552 3 : bit_depth = 8;
553 : }
554 : }
555 :
556 58 : interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
557 :
558 58 : if (im->trueColor) {
559 48 : if (im->saveAlphaFlag) {
560 1 : png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, interlace_type,
561 : PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
562 : } else {
563 47 : png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, interlace_type,
564 : PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
565 : }
566 : } else {
567 10 : png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type,
568 : PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
569 : }
570 :
571 58 : if (im->trueColor && !im->saveAlphaFlag && (transparent >= 0)) {
572 : /* 2.0.9: fixed by Thomas Winzig */
573 1 : trans_rgb_value.red = gdTrueColorGetRed (im->transparent);
574 1 : trans_rgb_value.green = gdTrueColorGetGreen (im->transparent);
575 1 : trans_rgb_value.blue = gdTrueColorGetBlue (im->transparent);
576 1 : png_set_tRNS(png_ptr, info_ptr, 0, 0, &trans_rgb_value);
577 : }
578 :
579 58 : if (!im->trueColor) {
580 : /* Oy veh. Remap the PNG palette to put the entries with interesting alpha channel
581 : * values first. This minimizes the size of the tRNS chunk and thus the size
582 : * of the PNG file as a whole.
583 : */
584 :
585 10 : int tc = 0;
586 : int i;
587 : int j;
588 : int k;
589 :
590 805 : for (i = 0; (i < im->colorsTotal); i++) {
591 795 : if ((!im->open[i]) && (im->alpha[i] != gdAlphaOpaque)) {
592 0 : tc++;
593 : }
594 : }
595 10 : if (tc) {
596 : #if 0
597 : for (i = 0; (i < im->colorsTotal); i++) {
598 : trans_values[i] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
599 : }
600 : png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL);
601 : #endif
602 0 : if (!remap) {
603 0 : remap = TRUE;
604 : }
605 :
606 : /* (Semi-)transparent indexes come up from the bottom of the list of real colors; opaque
607 : * indexes come down from the top
608 : */
609 0 : j = 0;
610 0 : k = colors - 1;
611 :
612 0 : for (i = 0; i < im->colorsTotal; i++) {
613 0 : if (!im->open[i]) {
614 0 : if (im->alpha[i] != gdAlphaOpaque) {
615 : /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
616 0 : trans_values[j] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
617 0 : mapping[i] = j++;
618 : } else {
619 0 : mapping[i] = k--;
620 : }
621 : }
622 : }
623 0 : png_set_tRNS(png_ptr, info_ptr, trans_values, tc, NULL);
624 : }
625 : }
626 :
627 : /* convert palette to libpng layout */
628 58 : if (!im->trueColor) {
629 10 : if (remap) {
630 0 : for (i = 0; i < im->colorsTotal; ++i) {
631 0 : if (mapping[i] < 0) {
632 0 : continue;
633 : }
634 :
635 0 : palette[mapping[i]].red = im->red[i];
636 0 : palette[mapping[i]].green = im->green[i];
637 0 : palette[mapping[i]].blue = im->blue[i];
638 : }
639 : } else {
640 805 : for (i = 0; i < colors; ++i) {
641 795 : palette[i].red = im->red[i];
642 795 : palette[i].green = im->green[i];
643 795 : palette[i].blue = im->blue[i];
644 : }
645 : }
646 10 : png_set_PLTE(png_ptr, info_ptr, palette, colors);
647 : }
648 :
649 : /* write out the PNG header info (everything up to first IDAT) */
650 58 : png_write_info(png_ptr, info_ptr);
651 :
652 : /* make sure < 8-bit images are packed into pixels as tightly as possible */
653 58 : png_set_packing(png_ptr);
654 :
655 : /* This code allocates a set of row buffers and copies the gd image data
656 : * into them only in the case that remapping is necessary; in gd 1.3 and
657 : * later, the im->pixels array is laid out identically to libpng's row
658 : * pointers and can be passed to png_write_image() function directly.
659 : * The remapping case could be accomplished with less memory for non-
660 : * interlaced images, but interlacing causes some serious complications.
661 : */
662 :
663 58 : if (im->trueColor) {
664 : /* performance optimizations by Phong Tran */
665 48 : int channels = im->saveAlphaFlag ? 4 : 3;
666 : /* Our little 7-bit alpha channel trick costs us a bit here. */
667 : png_bytep *row_pointers;
668 : unsigned char* pOutputRow;
669 48 : int **ptpixels = im->tpixels;
670 : int *pThisRow;
671 : unsigned char a;
672 : int thisPixel;
673 : png_bytep *prow_pointers;
674 48 : int saveAlphaFlag = im->saveAlphaFlag;
675 :
676 48 : row_pointers = safe_emalloc(sizeof(png_bytep), height, 0);
677 48 : prow_pointers = row_pointers;
678 6119 : for (j = 0; j < height; ++j) {
679 6071 : *prow_pointers = (png_bytep) safe_emalloc(width, channels, 0);
680 6071 : pOutputRow = *prow_pointers++;
681 6071 : pThisRow = *ptpixels++;
682 1321482 : for (i = 0; i < width; ++i) {
683 1315411 : thisPixel = *pThisRow++;
684 1315411 : *pOutputRow++ = gdTrueColorGetRed(thisPixel);
685 1315411 : *pOutputRow++ = gdTrueColorGetGreen(thisPixel);
686 1315411 : *pOutputRow++ = gdTrueColorGetBlue(thisPixel);
687 1315411 : if (saveAlphaFlag) {
688 : /* convert the 7-bit alpha channel to an 8-bit alpha channel.
689 : * We do a little bit-flipping magic, repeating the MSB
690 : * as the LSB, to ensure that 0 maps to 0 and
691 : * 127 maps to 255. We also have to invert to match
692 : * PNG's convention in which 255 is opaque.
693 : */
694 100 : a = gdTrueColorGetAlpha(thisPixel);
695 : /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
696 100 : if (a == 127) {
697 0 : *pOutputRow++ = 0;
698 : } else {
699 100 : *pOutputRow++ = 255 - ((a << 1) + (a >> 6));
700 : }
701 :
702 : }
703 : }
704 : }
705 :
706 48 : png_write_image(png_ptr, row_pointers);
707 48 : png_write_end(png_ptr, info_ptr);
708 :
709 6119 : for (j = 0; j < height; ++j) {
710 6071 : gdFree(row_pointers[j]);
711 : }
712 :
713 48 : gdFree(row_pointers);
714 : } else {
715 10 : if (remap) {
716 : png_bytep *row_pointers;
717 0 : row_pointers = safe_emalloc(height, sizeof(png_bytep), 0);
718 0 : for (j = 0; j < height; ++j) {
719 0 : row_pointers[j] = (png_bytep) gdMalloc(width);
720 0 : for (i = 0; i < width; ++i) {
721 0 : row_pointers[j][i] = mapping[im->pixels[j][i]];
722 : }
723 : }
724 :
725 0 : png_write_image(png_ptr, row_pointers);
726 0 : png_write_end(png_ptr, info_ptr);
727 :
728 0 : for (j = 0; j < height; ++j) {
729 0 : gdFree(row_pointers[j]);
730 : }
731 :
732 0 : gdFree(row_pointers);
733 : } else {
734 10 : png_write_image(png_ptr, im->pixels);
735 10 : png_write_end(png_ptr, info_ptr);
736 : }
737 : }
738 : /* 1.6.3: maybe we should give that memory BACK! TBB */
739 59 : bail:
740 59 : png_destroy_write_struct(&png_ptr, &info_ptr);
741 : }
742 :
743 : #endif /* HAVE_LIBPNG */
|