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