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