xref: /PHP-5.5/ext/gd/libgd/gd_png.c (revision a9913603)
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