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