Blame src/gd_png.c

Packit ed3af9
/* $Id$ */
Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#include "config.h"
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#include <stdio.h>
Packit ed3af9
#include <math.h>
Packit ed3af9
#include <string.h>
Packit ed3af9
#include <stdlib.h>
Packit ed3af9
#include "gd.h"
Packit ed3af9
#include "gd_errors.h"
Packit ed3af9
Packit ed3af9
/* JCE: Arrange HAVE_LIBPNG so that it can be set in gd.h */
Packit ed3af9
#ifdef HAVE_LIBPNG
Packit ed3af9
Packit ed3af9
#include "gdhelpers.h"
Packit ed3af9
#include "png.h"		/* includes zlib.h and setjmp.h */
Packit ed3af9
Packit ed3af9
#define TRUE 1
Packit ed3af9
#define FALSE 0
Packit ed3af9
Packit ed3af9
/*---------------------------------------------------------------------------
Packit ed3af9
Packit ed3af9
  gd_png.c                 Copyright 1999 Greg Roelofs and Thomas Boutell
Packit ed3af9
Packit ed3af9
  The routines in this file, gdImagePng*() and gdImageCreateFromPng*(),
Packit ed3af9
  are drop-in replacements for gdImageGif*() and gdImageCreateFromGif*(),
Packit ed3af9
  except that these functions are noisier in the case of errors (comment
Packit ed3af9
  out all fprintf() statements to disable that).
Packit ed3af9
Packit ed3af9
  GD 2.0 supports RGBA truecolor and will read and write truecolor PNGs.
Packit ed3af9
  GD 2.0 supports 8 bits of color resolution per channel and
Packit ed3af9
  7 bits of alpha channel resolution. Images with more than 8 bits
Packit ed3af9
  per channel are reduced to 8 bits. Images with an alpha channel are
Packit ed3af9
  only able to resolve down to '1/128th opaque' instead of '1/256th',
Packit ed3af9
  and this conversion is also automatic. I very much doubt you can see it.
Packit ed3af9
  Both tRNS and true alpha are supported.
Packit ed3af9
Packit ed3af9
  Gamma is ignored, and there is no support for text annotations.
Packit ed3af9
Packit ed3af9
  Last updated:  9 February 2001
Packit ed3af9
Packit ed3af9
  ---------------------------------------------------------------------------*/
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * File: PNG IO
Packit ed3af9
 *
Packit ed3af9
 * Read and write PNG images.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
typedef struct _jmpbuf_wrapper {
Packit ed3af9
	jmp_buf jmpbuf;
Packit ed3af9
}
Packit ed3af9
jmpbuf_wrapper;
Packit ed3af9
Packit ed3af9
static void
Packit ed3af9
gdPngErrorHandler (png_structp png_ptr, png_const_charp msg)
Packit ed3af9
{
Packit ed3af9
	jmpbuf_wrapper *jmpbuf_ptr;
Packit ed3af9
Packit ed3af9
	/* This function, aside from the extra step of retrieving the "error
Packit ed3af9
	 * pointer" (below) and the fact that it exists within the application
Packit ed3af9
	 * rather than within libpng, is essentially identical to libpng's
Packit ed3af9
	 * default error handler.  The second point is critical:  since both
Packit ed3af9
	 * setjmp() and longjmp() are called from the same code, they are
Packit ed3af9
	 * guaranteed to have compatible notions of how big a jmp_buf is,
Packit ed3af9
	 * regardless of whether _BSD_SOURCE or anything else has (or has not)
Packit ed3af9
	 * been defined. */
Packit ed3af9
Packit ed3af9
	gd_error_ex(GD_WARNING, "gd-png: fatal libpng error: %s\n", msg);
Packit ed3af9
Packit ed3af9
	jmpbuf_ptr = png_get_error_ptr (png_ptr);
Packit ed3af9
	if (jmpbuf_ptr == NULL) {				/* we are completely hosed now */
Packit ed3af9
		gd_error_ex(GD_ERROR, "gd-png: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n");
Packit ed3af9
		exit (99);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	longjmp (jmpbuf_ptr->jmpbuf, 1);
Packit ed3af9
}
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
static void
Packit ed3af9
gdPngReadData (png_structp png_ptr, png_bytep data, png_size_t length)
Packit ed3af9
{
Packit ed3af9
	int check;
Packit ed3af9
	check = gdGetBuf (data, length, (gdIOCtx *) png_get_io_ptr (png_ptr));
Packit ed3af9
	if (check != (int)length) {
Packit ed3af9
		png_error(png_ptr, "Read Error: truncated data");
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static void
Packit ed3af9
gdPngWriteData (png_structp png_ptr, png_bytep data, png_size_t length)
Packit ed3af9
{
Packit ed3af9
	gdPutBuf (data, length, (gdIOCtx *) png_get_io_ptr (png_ptr));
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static void
Packit ed3af9
gdPngFlushData (png_structp png_ptr)
Packit ed3af9
{
Packit ed3af9
	(void)png_ptr;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromPng
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromPng> is called to load images from PNG format
Packit ed3af9
    files. Invoke <gdImageCreateFromPng> with an already opened
Packit ed3af9
    pointer to a FILE containing the desired
Packit ed3af9
    image. <gdImageCreateFromPng> returns a <gdImagePtr> to the new
Packit ed3af9
    image, or NULL if unable to load the image (most often because the
Packit ed3af9
    file is corrupt or does not contain a PNG
Packit ed3af9
    image). <gdImageCreateFromPng> does not close the file. You can
Packit ed3af9
    inspect the sx and sy members of the image to determine its
Packit ed3af9
    size. The image must eventually be destroyed using
Packit ed3af9
    gdImageDestroy().
Packit ed3af9
Packit ed3af9
    If the PNG image being loaded is a truecolor image, the resulting
Packit ed3af9
    gdImagePtr will refer to a truecolor image. If the PNG image being
Packit ed3af9
    loaded is a palette or grayscale image, the resulting gdImagePtr
Packit ed3af9
    will refer to a palette image. gd retains only 8 bits of
Packit ed3af9
    resolution for each of the red, green and blue channels, and only
Packit ed3af9
    7 bits of resolution for the alpha channel. The former restriction
Packit ed3af9
    affects only a handful of very rare 48-bit color and 16-bit
Packit ed3af9
    grayscale PNG images. The second restriction affects all
Packit ed3af9
    semitransparent PNG images, but the difference is essentially
Packit ed3af9
    invisible to the eye. 7 bits of alpha channel resolution is, in
Packit ed3af9
    practice, quite a lot.
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromPngPtr> creates an image from PNG data (i.e. the
Packit ed3af9
    contents of a PNG file) already in memory.
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromPngCtx> reads in an image using the functions in
Packit ed3af9
    a <gdIOCtx> struct.
Packit ed3af9
Packit ed3af9
    <gdImageCreateFromPngSource> is similar to
Packit ed3af9
    <gdImageCreateFromPngCtx> but uses the old <gdSource> interface.
Packit ed3af9
    It is *obsolete*.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    infile - The input FILE pointer.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    A pointer to the new image or NULL if an error occurred.
Packit ed3af9
Packit ed3af9
  Example:
Packit ed3af9
    (start code)
Packit ed3af9
Packit ed3af9
    gdImagePtr im;
Packit ed3af9
    ... inside a function ...
Packit ed3af9
    FILE *in;
Packit ed3af9
    in = fopen("mypng.png", "rb");
Packit ed3af9
    im = gdImageCreateFromPng(in);
Packit ed3af9
    fclose(in);
Packit ed3af9
    // ... Use the image ...
Packit ed3af9
    gdImageDestroy(im);
Packit ed3af9
Packit ed3af9
    (end code)
Packit ed3af9
 */
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromPng (FILE * inFile)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr im;
Packit ed3af9
	gdIOCtx *in = gdNewFileCtx (inFile);
Packit ed3af9
	if (in == NULL) return NULL;
Packit ed3af9
	im = gdImageCreateFromPngCtx (in);
Packit ed3af9
	in->gd_free (in);
Packit ed3af9
	return im;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromPngPtr
Packit ed3af9
Packit ed3af9
  See <gdImageCreateFromPng>.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromPngPtr (int size, void *data)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr im;
Packit ed3af9
	gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0);
Packit ed3af9
	if(!in)
Packit ed3af9
		return 0;
Packit ed3af9
	im = gdImageCreateFromPngCtx (in);
Packit ed3af9
	in->gd_free (in);
Packit ed3af9
	return im;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
/* This routine is based in part on the Chapter 13 demo code in
Packit ed3af9
 * "PNG: The Definitive Guide" (http://www.libpng.org/pub/png/book/).
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageCreateFromPngCtx
Packit ed3af9
Packit ed3af9
  See <gdImageCreateFromPng>.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCreateFromPngCtx (gdIOCtx * infile)
Packit ed3af9
{
Packit ed3af9
	png_byte sig[8];
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	jmpbuf_wrapper jbw;
Packit ed3af9
#endif
Packit ed3af9
	png_structp png_ptr;
Packit ed3af9
	png_infop info_ptr;
Packit ed3af9
	png_uint_32 width, height, rowbytes, w, h, res_x, res_y;
Packit ed3af9
	int bit_depth, color_type, interlace_type, unit_type;
Packit ed3af9
	int num_palette = 0, num_trans;
Packit ed3af9
	png_colorp palette;
Packit ed3af9
	png_color_16p trans_gray_rgb;
Packit ed3af9
	png_color_16p trans_color_rgb;
Packit ed3af9
	png_bytep trans;
Packit ed3af9
	png_bytep image_data = NULL;
Packit ed3af9
	png_bytepp row_pointers = NULL;
Packit ed3af9
	gdImagePtr im = NULL;
Packit ed3af9
	int i, j, *open = NULL;
Packit ed3af9
	volatile int transparent = -1;
Packit ed3af9
	volatile int palette_allocated = FALSE;
Packit ed3af9
Packit ed3af9
	/* Make sure the signature can't match by dumb luck -- TBB */
Packit ed3af9
	/* GRR: isn't sizeof(infile) equal to the size of the pointer? */
Packit ed3af9
	memset (sig, 0, sizeof (sig));
Packit ed3af9
Packit ed3af9
	/* first do a quick check that the file really is a PNG image; could
Packit ed3af9
	 * have used slightly more general png_sig_cmp() function instead */
Packit ed3af9
	if (gdGetBuf (sig, 8, infile) < 8) {
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (png_sig_cmp(sig, 0, 8) != 0) { /* bad signature */
Packit ed3af9
		return NULL;		/* bad signature */
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, NULL);
Packit ed3af9
#else
Packit ed3af9
	png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
Packit ed3af9
#endif
Packit ed3af9
	if (png_ptr == NULL) {
Packit ed3af9
		gd_error("gd-png error: cannot allocate libpng main struct\n");
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	info_ptr = png_create_info_struct (png_ptr);
Packit ed3af9
	if (info_ptr == NULL) {
Packit ed3af9
		gd_error("gd-png error: cannot allocate libpng info struct\n");
Packit ed3af9
		png_destroy_read_struct (&png_ptr, NULL, NULL);
Packit ed3af9
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* we could create a second info struct here (end_info), but it's only
Packit ed3af9
	 * useful if we want to keep pre- and post-IDAT chunk info separated
Packit ed3af9
	 * (mainly for PNG-aware image editors and converters)
Packit ed3af9
	 */
Packit ed3af9
Packit ed3af9
	/* setjmp() must be called in every non-callback function that calls a
Packit ed3af9
	 * PNG-reading libpng function.  We must reset it everytime we get a
Packit ed3af9
	 * new allocation that we save in a stack variable.
Packit ed3af9
	 */
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	if (setjmp(jbw.jmpbuf)) {
Packit ed3af9
		gd_error("gd-png error: setjmp returns error condition 1\n");
Packit ed3af9
		png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
Packit ed3af9
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	png_set_sig_bytes (png_ptr, 8);	/* we already read the 8 signature bytes */
Packit ed3af9
Packit ed3af9
	png_set_read_fn (png_ptr, (void *) infile, gdPngReadData);
Packit ed3af9
	png_read_info (png_ptr, info_ptr);	/* read all PNG info up to image data */
Packit ed3af9
Packit ed3af9
	png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
Packit ed3af9
	if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
Packit ed3af9
	        || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
Packit ed3af9
		im = gdImageCreateTrueColor ((int) width, (int) height);
Packit ed3af9
	} else {
Packit ed3af9
		im = gdImageCreate ((int) width, (int) height);
Packit ed3af9
	}
Packit ed3af9
	if (im == NULL) {
Packit ed3af9
		gd_error("gd-png error: cannot allocate gdImage struct\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (bit_depth == 16) {
Packit ed3af9
		png_set_strip_16 (png_ptr);
Packit ed3af9
	} else if (bit_depth < 8) {
Packit ed3af9
		png_set_packing (png_ptr);	/* expand to 1 byte per pixel */
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* setjmp() must be called in every non-callback function that calls a
Packit ed3af9
	 * PNG-reading libpng function.  We must reset it everytime we get a
Packit ed3af9
	 * new allocation that we save in a stack variable.
Packit ed3af9
	 */
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	if (setjmp(jbw.jmpbuf)) {
Packit ed3af9
		gd_error("gd-png error: setjmp returns error condition 2\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#ifdef PNG_pHYs_SUPPORTED
Packit ed3af9
	/* check if the resolution is specified */
Packit ed3af9
	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) {
Packit ed3af9
		if (png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type)) {
Packit ed3af9
			switch (unit_type) {
Packit ed3af9
			case PNG_RESOLUTION_METER:
Packit ed3af9
				im->res_x = DPM2DPI(res_x);
Packit ed3af9
				im->res_y = DPM2DPI(res_y);
Packit ed3af9
				break;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	switch (color_type) {
Packit ed3af9
	case PNG_COLOR_TYPE_PALETTE:
Packit ed3af9
		png_get_PLTE (png_ptr, info_ptr, &palette, &num_palette);
Packit ed3af9
#ifdef DEBUG
Packit ed3af9
		gd_error("gd-png color_type is palette, colors: %d\n", num_palette);
Packit ed3af9
#endif /* DEBUG */
Packit ed3af9
		if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
Packit ed3af9
			/* gd 2.0: we support this rather thoroughly now. Grab the
Packit ed3af9
			 * first fully transparent entry, if any, as the value of
Packit ed3af9
			 * the simple-transparency index, mostly for backwards
Packit ed3af9
			 * binary compatibility. The alpha channel is where it's
Packit ed3af9
			 * really at these days.
Packit ed3af9
			 */
Packit ed3af9
			int firstZero = 1;
Packit ed3af9
			png_get_tRNS (png_ptr, info_ptr, &trans, &num_trans, NULL);
Packit ed3af9
			for (i = 0; i < num_trans; ++i) {
Packit ed3af9
				im->alpha[i] = gdAlphaMax - (trans[i] >> 1);
Packit ed3af9
				if ((trans[i] == 0) && (firstZero)) {
Packit ed3af9
					/* 2.0.5: long-forgotten patch from Wez Furlong */
Packit ed3af9
					transparent = i;
Packit ed3af9
					firstZero = 0;
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case PNG_COLOR_TYPE_GRAY:
Packit ed3af9
		/* create a fake palette and check for single-shade transparency */
Packit ed3af9
		if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL) {
Packit ed3af9
			gd_error("gd-png error: cannot allocate gray palette\n");
Packit ed3af9
			goto error;
Packit ed3af9
		}
Packit ed3af9
		palette_allocated = TRUE;
Packit ed3af9
		if (bit_depth < 8) {
Packit ed3af9
			num_palette = 1 << bit_depth;
Packit ed3af9
			for (i = 0; i < 256; ++i) {
Packit ed3af9
				j = (255 * i) / (num_palette - 1);
Packit ed3af9
				palette[i].red = palette[i].green = palette[i].blue = j;
Packit ed3af9
			}
Packit ed3af9
		} else {
Packit ed3af9
			num_palette = 256;
Packit ed3af9
			for (i = 0; i < 256; ++i) {
Packit ed3af9
				palette[i].red = palette[i].green = palette[i].blue = i;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
Packit ed3af9
			png_get_tRNS (png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb);
Packit ed3af9
			if (bit_depth == 16) {	/* png_set_strip_16() not yet in effect */
Packit ed3af9
				transparent = trans_gray_rgb->gray >> 8;
Packit ed3af9
			} else {
Packit ed3af9
				transparent = trans_gray_rgb->gray;
Packit ed3af9
			}
Packit ed3af9
			/* Note slight error in 16-bit case:  up to 256 16-bit shades
Packit ed3af9
			 * may get mapped to a single 8-bit shade, and only one of them
Packit ed3af9
			 * is supposed to be transparent.  IOW, both opaque pixels and
Packit ed3af9
			 * transparent pixels will be mapped into the transparent entry.
Packit ed3af9
			 * There is no particularly good way around this in the case
Packit ed3af9
			 * that all 256 8-bit shades are used, but one could write some
Packit ed3af9
			 * custom 16-bit code to handle the case where there are gdFree
Packit ed3af9
			 * palette entries.  This error will be extremely rare in
Packit ed3af9
			 * general, though.  (Quite possibly there is only one such
Packit ed3af9
			 * image in existence.) */
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case PNG_COLOR_TYPE_GRAY_ALPHA:
Packit ed3af9
		png_set_gray_to_rgb(png_ptr);
Packit ed3af9
Packit ed3af9
	case PNG_COLOR_TYPE_RGB:
Packit ed3af9
	case PNG_COLOR_TYPE_RGB_ALPHA:
Packit ed3af9
		/* gd 2.0: we now support truecolor. See the comment above
Packit ed3af9
		   for a rare situation in which the transparent pixel may not
Packit ed3af9
		   work properly with 16-bit channels. */
Packit ed3af9
		if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
Packit ed3af9
			png_get_tRNS (png_ptr, info_ptr, NULL, NULL, &trans_color_rgb);
Packit ed3af9
			if (bit_depth == 16) {	/* png_set_strip_16() not yet in effect */
Packit ed3af9
				transparent = gdTrueColor (trans_color_rgb->red >> 8,
Packit ed3af9
				                           trans_color_rgb->green >> 8,
Packit ed3af9
				                           trans_color_rgb->blue >> 8);
Packit ed3af9
			} else {
Packit ed3af9
				transparent = gdTrueColor (trans_color_rgb->red,
Packit ed3af9
				                           trans_color_rgb->green,
Packit ed3af9
				                           trans_color_rgb->blue);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
	default:
Packit ed3af9
		gd_error("gd-png color_type is unknown: %d\n", color_type);
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	png_read_update_info (png_ptr, info_ptr);
Packit ed3af9
Packit ed3af9
	/* allocate space for the PNG image data */
Packit ed3af9
	rowbytes = png_get_rowbytes (png_ptr, info_ptr);
Packit ed3af9
	if (overflow2(rowbytes, height))
Packit ed3af9
		goto error;
Packit ed3af9
	image_data = (png_bytep) gdMalloc (rowbytes * height);
Packit ed3af9
	if (!image_data) {
Packit ed3af9
		gd_error("gd-png error: cannot allocate image data\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
	if (overflow2(height, sizeof (png_bytep)))
Packit ed3af9
		goto error;
Packit ed3af9
Packit ed3af9
	row_pointers = (png_bytepp) gdMalloc (height * sizeof (png_bytep));
Packit ed3af9
	if (!row_pointers) {
Packit ed3af9
		gd_error("gd-png error: cannot allocate row pointers\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* setjmp() must be called in every non-callback function that calls a
Packit ed3af9
	 * PNG-reading libpng function.  We must reset it everytime we get a
Packit ed3af9
	 * new allocation that we save in a stack variable.
Packit ed3af9
	 */
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	if (setjmp(jbw.jmpbuf)) {
Packit ed3af9
		gd_error("gd-png error: setjmp returns error condition 3\n");
Packit ed3af9
		goto error;
Packit ed3af9
	}
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	/* set the individual row_pointers to point at the correct offsets */
Packit ed3af9
	for (h = 0; h < height; ++h) {
Packit ed3af9
		row_pointers[h] = image_data + h * rowbytes;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	png_read_image (png_ptr, row_pointers);	/* read whole image... */
Packit ed3af9
	png_read_end (png_ptr, NULL);	/* ...done! */
Packit ed3af9
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		im->colorsTotal = num_palette;
Packit ed3af9
		/* load the palette and mark all entries "open" (unused) for now */
Packit ed3af9
		open = im->open;
Packit ed3af9
		for (i = 0; i < num_palette; ++i) {
Packit ed3af9
			im->red[i] = palette[i].red;
Packit ed3af9
			im->green[i] = palette[i].green;
Packit ed3af9
			im->blue[i] = palette[i].blue;
Packit ed3af9
			open[i] = 1;
Packit ed3af9
		}
Packit ed3af9
		for (i = num_palette; i < gdMaxColors; ++i) {
Packit ed3af9
			open[i] = 1;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	/* 2.0.12: Slaven Rezic: palette images are not the only images
Packit ed3af9
	   with a simple transparent color setting */
Packit ed3af9
	im->transparent = transparent;
Packit ed3af9
	im->interlace = (interlace_type == PNG_INTERLACE_ADAM7);
Packit ed3af9
Packit ed3af9
	/* can't nuke structs until done with palette */
Packit ed3af9
	png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
Packit ed3af9
	switch (color_type) {
Packit ed3af9
	case PNG_COLOR_TYPE_RGB:
Packit ed3af9
		for (h = 0; h < height; h++) {
Packit ed3af9
			int boffset = 0;
Packit ed3af9
			for (w = 0; w < width; w++) {
Packit ed3af9
				register png_byte r = row_pointers[h][boffset++];
Packit ed3af9
				register png_byte g = row_pointers[h][boffset++];
Packit ed3af9
				register png_byte b = row_pointers[h][boffset++];
Packit ed3af9
				im->tpixels[h][w] = gdTrueColor (r, g, b);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case PNG_COLOR_TYPE_GRAY_ALPHA:
Packit ed3af9
	case PNG_COLOR_TYPE_RGB_ALPHA:
Packit ed3af9
		for (h = 0; h < height; h++) {
Packit ed3af9
			int boffset = 0;
Packit ed3af9
			for (w = 0; w < width; w++) {
Packit ed3af9
				register png_byte r = row_pointers[h][boffset++];
Packit ed3af9
				register png_byte g = row_pointers[h][boffset++];
Packit ed3af9
				register png_byte b = row_pointers[h][boffset++];
Packit ed3af9
Packit ed3af9
				/* gd has only 7 bits of alpha channel resolution, and
Packit ed3af9
				 * 127 is transparent, 0 opaque. A moment of convenience,
Packit ed3af9
				 *  a lifetime of compatibility.
Packit ed3af9
				 */
Packit ed3af9
Packit ed3af9
				register png_byte a = gdAlphaMax - (row_pointers[h][boffset++] >> 1);
Packit ed3af9
				im->tpixels[h][w] = gdTrueColorAlpha(r, g, b, a);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		break;
Packit ed3af9
	default:
Packit ed3af9
		if (!im->trueColor) {
Packit ed3af9
			/* Palette image, or something coerced to be one */
Packit ed3af9
			for (h = 0; h < height; ++h) {
Packit ed3af9
				for (w = 0; w < width; ++w) {
Packit ed3af9
					register png_byte idx = row_pointers[h][w];
Packit ed3af9
					im->pixels[h][w] = idx;
Packit ed3af9
					open[idx] = 0;
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
#ifdef DEBUG
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		for (i = num_palette; i < gdMaxColors; ++i) {
Packit ed3af9
			if (!open[i]) {
Packit ed3af9
				fprintf (stderr,
Packit ed3af9
				         "gd-png warning: image data references out-of-range"
Packit ed3af9
				         " color index (%d)\n", i);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
 done:
Packit ed3af9
	if (palette_allocated) {
Packit ed3af9
		gdFree (palette);
Packit ed3af9
	}
Packit ed3af9
	if (image_data)
Packit ed3af9
		gdFree(image_data);
Packit ed3af9
	if (row_pointers)
Packit ed3af9
		gdFree(row_pointers);
Packit ed3af9
Packit ed3af9
	return im;
Packit ed3af9
Packit ed3af9
 error:
Packit ed3af9
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
Packit ed3af9
	if (im) {
Packit ed3af9
		gdImageDestroy(im);
Packit ed3af9
		im = NULL;
Packit ed3af9
	}
Packit ed3af9
	goto done;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImagePngEx
Packit ed3af9
Packit ed3af9
    <gdImagePngEx> outputs the specified image to the specified file in
Packit ed3af9
    PNG format. The file must be open for writing. Under MSDOS and all
Packit ed3af9
    versions of Windows, it is important to use "wb" as opposed to
Packit ed3af9
    simply "w" as the mode when opening the file, and under Unix there
Packit ed3af9
    is no penalty for doing so. <gdImagePngEx> does not close the file;
Packit ed3af9
    your code must do so.
Packit ed3af9
Packit ed3af9
    In addition, <gdImagePngEx> allows the level of compression to be
Packit ed3af9
    specified. A compression level of 0 means "no compression." A
Packit ed3af9
    compression level of 1 means "compressed, but as quickly as
Packit ed3af9
    possible." A compression level of 9 means "compressed as much as
Packit ed3af9
    possible to produce the smallest possible file." A compression level
Packit ed3af9
    of -1 will use the default compression level at the time zlib was
Packit ed3af9
    compiled on your system.
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImagePng> is equivalent to calling <gdImagePngEx> with
Packit ed3af9
    compression of -1.
Packit ed3af9
Packit ed3af9
    <gdImagePngCtx> and <gdImagePngCtxEx> write via a <gdIOCtx>
Packit ed3af9
    instead of a file handle.
Packit ed3af9
Packit ed3af9
    <gdImagePngPtr> and <gdImagePngPtrEx> store the image file to
Packit ed3af9
    memory.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - the image to write
Packit ed3af9
    outFile - the output FILE* object.
Packit ed3af9
    level   - compression level: 0 -> none, 1-9 -> level, -1 -> default
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
  Example:
Packit ed3af9
    (start code)
Packit ed3af9
Packit ed3af9
    gdImagePtr im;
Packit ed3af9
    int black, white;
Packit ed3af9
    FILE *out;
Packit ed3af9
     
Packit ed3af9
    im = gdImageCreate(100, 100);              // Create the image
Packit ed3af9
    white = gdImageColorAllocate(im, 255, 255, 255); // Alloc background
Packit ed3af9
    black = gdImageColorAllocate(im, 0, 0, 0); // Allocate drawing color
Packit ed3af9
    gdImageRectangle(im, 0, 0, 99, 99, black); // Draw rectangle
Packit ed3af9
    out = fopen("rect.png", "wb");             // Open output file (binary)
Packit ed3af9
    gdImagePngEx(im, out, 9);                  // Write PNG, max compression
Packit ed3af9
    fclose(out);                               // Close file
Packit ed3af9
    gdImageDestroy(im);                        // Destroy image
Packit ed3af9
Packit ed3af9
    (end code)
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImagePngEx (gdImagePtr im, FILE * outFile, int level)
Packit ed3af9
{
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx (outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImagePngCtxEx (im, out, level);
Packit ed3af9
	out->gd_free (out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImagePng
Packit ed3af9
Packit ed3af9
    Equivalent to calling <gdImagePngEx> with compression of -1.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - the image to save.
Packit ed3af9
    outFile - the output FILE*.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImagePng (gdImagePtr im, FILE * outFile)
Packit ed3af9
{
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx (outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImagePngCtxEx (im, out, -1);
Packit ed3af9
	out->gd_free (out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int _gdImagePngCtxEx(gdImagePtr im, gdIOCtx * outfile, int level);
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImagePngPtr
Packit ed3af9
Packit ed3af9
    Equivalent to calling <gdImagePngPtrEx> with compression of -1.
Packit ed3af9
Packit ed3af9
    See <gdImagePngEx> for more information.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - the image to save.
Packit ed3af9
    size    - Output: size in bytes of the result.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    A pointer to memory containing the image data or NULL on error.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void *) gdImagePngPtr (gdImagePtr im, int *size)
Packit ed3af9
{
Packit ed3af9
	void *rv;
Packit ed3af9
	gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
Packit ed3af9
	if (out == NULL) return NULL;
Packit ed3af9
	if (!_gdImagePngCtxEx (im, out, -1)) {
Packit ed3af9
		rv = gdDPExtractData (out, size);
Packit ed3af9
	} else {
Packit ed3af9
		rv = NULL;
Packit ed3af9
	}
Packit ed3af9
	out->gd_free (out);
Packit ed3af9
	return rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImagePngPtrEx
Packit ed3af9
Packit ed3af9
    Identical to <gdImagePngEx> except that it returns a pointer to a
Packit ed3af9
    memory area with the PNG data. This memory must be freed by the
Packit ed3af9
    caller when it is no longer needed. **The caller must invoke
Packit ed3af9
    gdFree(), not free()**
Packit ed3af9
Packit ed3af9
    The 'size' parameter receives the total size of the block of
Packit ed3af9
    memory.
Packit ed3af9
Packit ed3af9
    See <gdImagePngEx> for more information.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - the image to save.
Packit ed3af9
    size    - Output: size in bytes of the result.
Packit ed3af9
    level   - compression level: 0 -> none, 1-9 -> level, -1 -> default
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    A pointer to memory containing the image data or NULL on error.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void *) gdImagePngPtrEx (gdImagePtr im, int *size, int level)
Packit ed3af9
{
Packit ed3af9
	void *rv;
Packit ed3af9
	gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
Packit ed3af9
	if (out == NULL) return NULL;
Packit ed3af9
	if (!_gdImagePngCtxEx (im, out, level)) {
Packit ed3af9
		rv = gdDPExtractData (out, size);
Packit ed3af9
	} else {
Packit ed3af9
		rv = NULL;
Packit ed3af9
	}
Packit ed3af9
	out->gd_free (out);
Packit ed3af9
	return rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImagePngCtx
Packit ed3af9
Packit ed3af9
    Equivalent to calling <gdImagePngCtxEx> with compression of -1.
Packit ed3af9
    See <gdImagePngEx> for more information.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - the image to save.
Packit ed3af9
    outfile - the <gdIOCtx> to write to.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile)
Packit ed3af9
{
Packit ed3af9
	/* 2.0.13: 'return' here was an error, thanks to Kevin Smith */
Packit ed3af9
	gdImagePngCtxEx (im, outfile, -1);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImagePngCtxEx
Packit ed3af9
Packit ed3af9
    Outputs the given image as PNG data, but using a <gdIOCtx> instead
Packit ed3af9
    of a file.  See <gdIamgePnEx>.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - the image to save.
Packit ed3af9
    outfile - the <gdIOCtx> to write to.
Packit ed3af9
    level   - compression level: 0 -> none, 1-9 -> level, -1 -> default
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImagePngCtxEx (gdImagePtr im, gdIOCtx * outfile, int level)
Packit ed3af9
{
Packit ed3af9
	_gdImagePngCtxEx(im, outfile, level);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* This routine is based in part on code from Dale Lutz (Safe Software Inc.)
Packit ed3af9
 *  and in part on demo code from Chapter 15 of "PNG: The Definitive Guide"
Packit ed3af9
 *  (http://www.libpng.org/pub/png/book/).
Packit ed3af9
 */
Packit ed3af9
/* returns 0 on success, 1 on failure */
Packit ed3af9
static int _gdImagePngCtxEx(gdImagePtr im, gdIOCtx * outfile, int level)
Packit ed3af9
{
Packit ed3af9
	int i, j, bit_depth = 0, interlace_type;
Packit ed3af9
	int width = im->sx;
Packit ed3af9
	int height = im->sy;
Packit ed3af9
	int colors = im->colorsTotal;
Packit ed3af9
	int *open = im->open;
Packit ed3af9
	int mapping[gdMaxColors];	/* mapping[gd_index] == png_index */
Packit ed3af9
	png_byte trans_values[256];
Packit ed3af9
	png_color_16 trans_rgb_value;
Packit ed3af9
	png_color palette[gdMaxColors];
Packit ed3af9
	png_structp png_ptr;
Packit ed3af9
	png_infop info_ptr;
Packit ed3af9
	volatile int transparent = im->transparent;
Packit ed3af9
	volatile int remap = FALSE;
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	jmpbuf_wrapper jbw;
Packit ed3af9
#endif
Packit ed3af9
	int ret = 0;
Packit ed3af9
Packit ed3af9
	/* width or height of value 0 is invalid in IHDR;
Packit ed3af9
	   see http://www.w3.org/TR/PNG-Chunks.html */
Packit ed3af9
	if (width == 0 || height ==0) return 1;
Packit ed3af9
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
Packit ed3af9
	                                   &jbw, gdPngErrorHandler,
Packit ed3af9
	                                   NULL);
Packit ed3af9
#else
Packit ed3af9
	png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
Packit ed3af9
#endif
Packit ed3af9
	if (png_ptr == NULL) {
Packit ed3af9
		gd_error("gd-png error: cannot allocate libpng main struct\n");
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	info_ptr = png_create_info_struct (png_ptr);
Packit ed3af9
	if (info_ptr == NULL) {
Packit ed3af9
		gd_error("gd-png error: cannot allocate libpng info struct\n");
Packit ed3af9
		png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
#ifdef PNG_SETJMP_SUPPORTED
Packit ed3af9
	if (setjmp(jbw.jmpbuf)) {
Packit ed3af9
		gd_error("gd-png error: setjmp returns error condition\n");
Packit ed3af9
		png_destroy_write_struct (&png_ptr, &info_ptr);
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	png_set_write_fn (png_ptr, (void *) outfile, gdPngWriteData,
Packit ed3af9
	                  gdPngFlushData);
Packit ed3af9
Packit ed3af9
	/* This is best for palette images, and libpng defaults to it for
Packit ed3af9
	   palette images anyway, so we don't need to do it explicitly.
Packit ed3af9
	   What to ideally do for truecolor images depends, alas, on the image.
Packit ed3af9
	   gd is intentionally imperfect and doesn't spend a lot of time
Packit ed3af9
	   fussing with such things. */
Packit ed3af9
Packit ed3af9
	/* Faster if this is uncommented, but may produce larger truecolor files.
Packit ed3af9
	   Wait for gdImagePngCtxEx. */
Packit ed3af9
#if 0
Packit ed3af9
	png_set_filter (png_ptr, 0, PNG_FILTER_NONE);
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	/* 2.0.12: this is finally a parameter */
Packit ed3af9
	png_set_compression_level (png_ptr, level);
Packit ed3af9
Packit ed3af9
#ifdef PNG_pHYs_SUPPORTED
Packit ed3af9
	/* 2.1.0: specify the resolution */
Packit ed3af9
	png_set_pHYs(png_ptr, info_ptr, DPI2DPM(im->res_x), DPI2DPM(im->res_y),
Packit ed3af9
	             PNG_RESOLUTION_METER);
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
	/* can set this to a smaller value without compromising compression if all
Packit ed3af9
	 * image data is 16K or less; will save some decoder memory [min == 8] */
Packit ed3af9
	/*  png_set_compression_window_bits(png_ptr, 15);  */
Packit ed3af9
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		if (transparent >= im->colorsTotal ||
Packit ed3af9
		        (transparent >= 0 && open[transparent]))
Packit ed3af9
			transparent = -1;
Packit ed3af9
	}
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		for (i = 0; i < gdMaxColors; ++i)
Packit ed3af9
			mapping[i] = -1;
Packit ed3af9
	}
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		/* count actual number of colors used (colorsTotal == high-water mark) */
Packit ed3af9
		colors = 0;
Packit ed3af9
		for (i = 0; i < im->colorsTotal; ++i) {
Packit ed3af9
			if (!open[i]) {
Packit ed3af9
				mapping[i] = colors;
Packit ed3af9
				++colors;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		if (colors == 0) {
Packit ed3af9
			gd_error("gd-png error: no colors in palette\n");
Packit ed3af9
			ret = 1;
Packit ed3af9
			goto bail;
Packit ed3af9
		}
Packit ed3af9
		if (colors < im->colorsTotal) {
Packit ed3af9
			remap = TRUE;
Packit ed3af9
		}
Packit ed3af9
		if (colors <= 2)
Packit ed3af9
			bit_depth = 1;
Packit ed3af9
		else if (colors <= 4)
Packit ed3af9
			bit_depth = 2;
Packit ed3af9
		else if (colors <= 16)
Packit ed3af9
			bit_depth = 4;
Packit ed3af9
		else
Packit ed3af9
			bit_depth = 8;
Packit ed3af9
	}
Packit ed3af9
	interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
Packit ed3af9
Packit ed3af9
	if (im->trueColor) {
Packit ed3af9
		if (im->saveAlphaFlag) {
Packit ed3af9
			png_set_IHDR (png_ptr, info_ptr, width, height, 8,
Packit ed3af9
			              PNG_COLOR_TYPE_RGB_ALPHA, interlace_type,
Packit ed3af9
			              PNG_COMPRESSION_TYPE_DEFAULT,
Packit ed3af9
			              PNG_FILTER_TYPE_DEFAULT);
Packit ed3af9
		} else {
Packit ed3af9
			png_set_IHDR (png_ptr, info_ptr, width, height, 8,
Packit ed3af9
			              PNG_COLOR_TYPE_RGB, interlace_type,
Packit ed3af9
			              PNG_COMPRESSION_TYPE_DEFAULT,
Packit ed3af9
			              PNG_FILTER_TYPE_DEFAULT);
Packit ed3af9
		}
Packit ed3af9
	} else {
Packit ed3af9
		png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth,
Packit ed3af9
		              PNG_COLOR_TYPE_PALETTE, interlace_type,
Packit ed3af9
		              PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
Packit ed3af9
	}
Packit ed3af9
	if (im->trueColor && (!im->saveAlphaFlag) && (transparent >= 0)) {
Packit ed3af9
		/* 2.0.9: fixed by Thomas Winzig */
Packit ed3af9
		trans_rgb_value.red = gdTrueColorGetRed (im->transparent);
Packit ed3af9
		trans_rgb_value.green = gdTrueColorGetGreen (im->transparent);
Packit ed3af9
		trans_rgb_value.blue = gdTrueColorGetBlue (im->transparent);
Packit ed3af9
		png_set_tRNS (png_ptr, info_ptr, 0, 0, &trans_rgb_value);
Packit ed3af9
	}
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		/* Oy veh. Remap the PNG palette to put the
Packit ed3af9
		   entries with interesting alpha channel
Packit ed3af9
		   values first. This minimizes the size
Packit ed3af9
		   of the tRNS chunk and thus the size
Packit ed3af9
		   of the PNG file as a whole. */
Packit ed3af9
		int tc = 0;
Packit ed3af9
		int i;
Packit ed3af9
		int j;
Packit ed3af9
		int k;
Packit ed3af9
		for (i = 0; (i < im->colorsTotal); i++) {
Packit ed3af9
			if ((!im->open[i]) && (im->alpha[i] != gdAlphaOpaque)) {
Packit ed3af9
				tc++;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
		if (tc) {
Packit ed3af9
#if 0
Packit ed3af9
			for (i = 0; (i < im->colorsTotal); i++) {
Packit ed3af9
				trans_values[i] = 255 -
Packit ed3af9
				                  ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
Packit ed3af9
			}
Packit ed3af9
			png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL);
Packit ed3af9
#endif
Packit ed3af9
			if (!remap) {
Packit ed3af9
				remap = TRUE;
Packit ed3af9
			}
Packit ed3af9
			/* (Semi-)transparent indexes come up from the bottom
Packit ed3af9
			   of the list of real colors; opaque
Packit ed3af9
			   indexes come down from the top */
Packit ed3af9
			j = 0;
Packit ed3af9
			k = colors - 1;
Packit ed3af9
			for (i = 0; (i < im->colorsTotal); i++) {
Packit ed3af9
				if (!im->open[i]) {
Packit ed3af9
					if (im->alpha[i] != gdAlphaOpaque) {
Packit ed3af9
						/* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
Packit ed3af9
						trans_values[j] = 255 -
Packit ed3af9
						                  ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
Packit ed3af9
						mapping[i] = j++;
Packit ed3af9
					} else {
Packit ed3af9
						mapping[i] = k--;
Packit ed3af9
					}
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
			png_set_tRNS (png_ptr, info_ptr, trans_values, tc, NULL);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* convert palette to libpng layout */
Packit ed3af9
	if (!im->trueColor) {
Packit ed3af9
		if (remap)
Packit ed3af9
			for (i = 0; i < im->colorsTotal; ++i) {
Packit ed3af9
				if (mapping[i] < 0)
Packit ed3af9
					continue;
Packit ed3af9
				palette[mapping[i]].red = im->red[i];
Packit ed3af9
				palette[mapping[i]].green = im->green[i];
Packit ed3af9
				palette[mapping[i]].blue = im->blue[i];
Packit ed3af9
			}
Packit ed3af9
		else
Packit ed3af9
			for (i = 0; i < colors; ++i) {
Packit ed3af9
				palette[i].red = im->red[i];
Packit ed3af9
				palette[i].green = im->green[i];
Packit ed3af9
				palette[i].blue = im->blue[i];
Packit ed3af9
			}
Packit ed3af9
		png_set_PLTE (png_ptr, info_ptr, palette, colors);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* write out the PNG header info (everything up to first IDAT) */
Packit ed3af9
	png_write_info (png_ptr, info_ptr);
Packit ed3af9
Packit ed3af9
	/* make sure < 8-bit images are packed into pixels as tightly as possible */
Packit ed3af9
	png_set_packing (png_ptr);
Packit ed3af9
Packit ed3af9
	/* This code allocates a set of row buffers and copies the gd image data
Packit ed3af9
	 * into them only in the case that remapping is necessary; in gd 1.3 and
Packit ed3af9
	 * later, the im->pixels array is laid out identically to libpng's row
Packit ed3af9
	 * pointers and can be passed to png_write_image() function directly.
Packit ed3af9
	 * The remapping case could be accomplished with less memory for non-
Packit ed3af9
	 * interlaced images, but interlacing causes some serious complications. */
Packit ed3af9
	if (im->trueColor) {
Packit ed3af9
		/* performance optimizations by Phong Tran */
Packit ed3af9
		int channels = im->saveAlphaFlag ? 4 : 3;
Packit ed3af9
		/* Our little 7-bit alpha channel trick costs us a bit here. */
Packit ed3af9
		png_bytep *row_pointers;
Packit ed3af9
		unsigned char *pOutputRow;
Packit ed3af9
		int **ptpixels = im->tpixels;
Packit ed3af9
		int *pThisRow;
Packit ed3af9
		unsigned char a;
Packit ed3af9
		int thisPixel;
Packit ed3af9
		png_bytep *prow_pointers;
Packit ed3af9
		int saveAlphaFlag = im->saveAlphaFlag;
Packit ed3af9
		if (overflow2(sizeof (png_bytep), height)) {
Packit ed3af9
			ret = 1;
Packit ed3af9
			goto bail;
Packit ed3af9
		}
Packit ed3af9
		row_pointers = gdMalloc (sizeof (png_bytep) * height);
Packit ed3af9
		if (row_pointers == NULL) {
Packit ed3af9
			gd_error("gd-png error: unable to allocate row_pointers\n");
Packit ed3af9
			ret = 1;
Packit ed3af9
			goto bail;
Packit ed3af9
		}
Packit ed3af9
		prow_pointers = row_pointers;
Packit ed3af9
		for (j = 0; j < height; ++j) {
Packit ed3af9
			if (overflow2(width, channels) || ((*prow_pointers =
Packit ed3af9
			                                        (png_bytep) gdMalloc (width * channels)) == NULL)) {
Packit ed3af9
				gd_error("gd-png error: unable to allocate rows\n");
Packit ed3af9
				for (i = 0; i < j; ++i)
Packit ed3af9
					gdFree (row_pointers[i]);
Packit ed3af9
				/* 2.0.29: memory leak TBB */
Packit ed3af9
				gdFree(row_pointers);
Packit ed3af9
				ret = 1;
Packit ed3af9
				goto bail;
Packit ed3af9
			}
Packit ed3af9
			pOutputRow = *prow_pointers++;
Packit ed3af9
			pThisRow = *ptpixels++;
Packit ed3af9
			for (i = 0; i < width; ++i) {
Packit ed3af9
				thisPixel = *pThisRow++;
Packit ed3af9
				*pOutputRow++ = gdTrueColorGetRed (thisPixel);
Packit ed3af9
				*pOutputRow++ = gdTrueColorGetGreen (thisPixel);
Packit ed3af9
				*pOutputRow++ = gdTrueColorGetBlue (thisPixel);
Packit ed3af9
Packit ed3af9
				if (saveAlphaFlag) {
Packit ed3af9
					/* convert the 7-bit alpha channel to an 8-bit alpha channel.
Packit ed3af9
					   We do a little bit-flipping magic, repeating the MSB
Packit ed3af9
					   as the LSB, to ensure that 0 maps to 0 and
Packit ed3af9
					   127 maps to 255. We also have to invert to match
Packit ed3af9
					   PNG's convention in which 255 is opaque. */
Packit ed3af9
					a = gdTrueColorGetAlpha (thisPixel);
Packit ed3af9
					/* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
Packit ed3af9
					*pOutputRow++ = 255 - ((a << 1) + (a >> 6));
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		png_write_image (png_ptr, row_pointers);
Packit ed3af9
		png_write_end (png_ptr, info_ptr);
Packit ed3af9
Packit ed3af9
		for (j = 0; j < height; ++j)
Packit ed3af9
			gdFree (row_pointers[j]);
Packit ed3af9
		gdFree (row_pointers);
Packit ed3af9
	} else {
Packit ed3af9
		if (remap) {
Packit ed3af9
			png_bytep *row_pointers;
Packit ed3af9
			if (overflow2(sizeof (png_bytep), height)) {
Packit ed3af9
				ret = 1;
Packit ed3af9
				goto bail;
Packit ed3af9
			}
Packit ed3af9
			row_pointers = gdMalloc (sizeof (png_bytep) * height);
Packit ed3af9
			if (row_pointers == NULL) {
Packit ed3af9
				gd_error("gd-png error: unable to allocate row_pointers\n");
Packit ed3af9
				ret = 1;
Packit ed3af9
				goto bail;
Packit ed3af9
			}
Packit ed3af9
			for (j = 0; j < height; ++j) {
Packit ed3af9
				if ((row_pointers[j] = (png_bytep) gdMalloc (width)) == NULL) {
Packit ed3af9
					gd_error("gd-png error: unable to allocate rows\n");
Packit ed3af9
					for (i = 0; i < j; ++i)
Packit ed3af9
						gdFree (row_pointers[i]);
Packit ed3af9
					/* TBB: memory leak */
Packit ed3af9
					gdFree (row_pointers);
Packit ed3af9
					ret = 1;
Packit ed3af9
					goto bail;
Packit ed3af9
				}
Packit ed3af9
				for (i = 0; i < width; ++i)
Packit ed3af9
					row_pointers[j][i] = mapping[im->pixels[j][i]];
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			png_write_image (png_ptr, row_pointers);
Packit ed3af9
			png_write_end (png_ptr, info_ptr);
Packit ed3af9
Packit ed3af9
			for (j = 0; j < height; ++j)
Packit ed3af9
				gdFree (row_pointers[j]);
Packit ed3af9
			gdFree (row_pointers);
Packit ed3af9
		} else {
Packit ed3af9
			png_write_image (png_ptr, im->pixels);
Packit ed3af9
			png_write_end (png_ptr, info_ptr);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	/* 1.6.3: maybe we should give that memory BACK! TBB */
Packit ed3af9
bail:
Packit ed3af9
	png_destroy_write_struct (&png_ptr, &info_ptr);
Packit ed3af9
	return ret;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
#endif /* HAVE_LIBPNG */