Blame src/gd_png.c

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