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