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