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