summaryrefslogtreecommitdiff
path: root/Build/source/libs/gd/libgd-2.1.0/src/gd_tiff.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/gd/libgd-2.1.0/src/gd_tiff.c')
-rw-r--r--Build/source/libs/gd/libgd-2.1.0/src/gd_tiff.c1011
1 files changed, 1011 insertions, 0 deletions
diff --git a/Build/source/libs/gd/libgd-2.1.0/src/gd_tiff.c b/Build/source/libs/gd/libgd-2.1.0/src/gd_tiff.c
new file mode 100644
index 00000000000..ec6dd113074
--- /dev/null
+++ b/Build/source/libs/gd/libgd-2.1.0/src/gd_tiff.c
@@ -0,0 +1,1011 @@
+/*
+ TIFF - Tagged Image File Format Encapsulation for GD Library
+
+ gd_tiff.c
+ Copyright (C) Pierre-A. Joye, M. Retallack
+
+ ---------------------------------------------------------------------------
+ **
+ ** Permission to use, copy, modify, and distribute this software and its
+ ** documentation for any purpose and without fee is hereby granted, provided
+ ** that the above copyright notice appear in all copies and that both that
+ ** copyright notice and this permission notice appear in supporting
+ ** documentation. This software is provided "as is" without express or
+ ** implied warranty.
+ **
+ ---------------------------------------------------------------------------
+ Ctx code written by M. Retallack
+
+ Todo:
+
+ If we fail - cleanup
+ Writer: Use gd error function, overflow check may not be necessary as
+ we write our own data (check already done)
+
+ Implement 2 color black/white saving using group4 fax compression
+ Implement function to specify encoding to use when writing tiff data
+
+ ----------------------------------------------------------------------------
+ */
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gd.h"
+#include "gd_errors.h"
+#include "gdfonts.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "gdhelpers.h"
+
+#ifdef HAVE_LIBTIFF
+
+#include "tiff.h"
+#include "tiffio.h"
+
+#define GD_SUCCESS 1
+#define GD_FAILURE 0
+
+#define TRUE 1
+#define FALSE 0
+
+/* I define those here until the new formats
+ * are commited. We can then rely on the global
+ * def
+ */
+#define GD_PALETTE 1
+#define GD_TRUECOLOR 2
+#define GD_GRAY 3
+#define GD_INDEXED 4
+#define GD_RGB 5
+
+#define MIN(a,b) (a < b) ? a : b;
+#define MAX(a,b) (a > b) ? a : b;
+
+
+typedef struct tiff_handle {
+ int size;
+ int pos;
+ gdIOCtx *ctx;
+ int written;
+}
+tiff_handle;
+
+/*
+ Functions for reading, writing and seeking in gdIOCtx
+ This allows for non-file i/o operations with no
+ explicit use of libtiff fileio wrapper functions
+
+ Note: because libtiff requires random access, but gdIOCtx
+ only supports streams, all writes are buffered
+ into memory and written out on close, also all
+ reads are done from a memory mapped version of the
+ tiff (assuming one already exists)
+*/
+
+tiff_handle * new_tiff_handle(gdIOCtx *g)
+{
+ tiff_handle * t;
+
+ if (!g) {
+ gd_error("Cannot create a new tiff handle, missing Ctx argument");
+ return NULL;
+ }
+
+ t = (tiff_handle *) gdMalloc(sizeof(tiff_handle));
+ if (!t) {
+ gd_error("Failed to allocate a new tiff handle");
+ return NULL;
+ }
+
+ t->size = 0;
+ t->pos = 0;
+ t->ctx = g;
+ t->written = 0;
+
+ return t;
+}
+
+/* TIFFReadWriteProc tiff_readproc - Will use gdIOCtx procs to read required
+ (previously written) TIFF file content */
+static tsize_t tiff_readproc(thandle_t clientdata, tdata_t data, tsize_t size)
+{
+ tiff_handle *th = (tiff_handle *)clientdata;
+ gdIOCtx *ctx = th->ctx;
+
+ size = (ctx->getBuf)(ctx, data, size);
+
+ return size;
+}
+
+/* TIFFReadWriteProc tiff_writeproc - Will use gdIOCtx procs to write out
+ TIFF data */
+static tsize_t tiff_writeproc(thandle_t clientdata, tdata_t data, tsize_t size)
+{
+ tiff_handle *th = (tiff_handle *)clientdata;
+ gdIOCtx *ctx = th->ctx;
+
+ size = (ctx->putBuf)(ctx, data, size);
+ if(size + th->pos>th->size) {
+ th->size = size + th->pos;
+ th->pos += size;
+ }
+
+ return size;
+}
+
+/* TIFFSeekProc tiff_seekproc
+ * used to move around the partially written TIFF */
+static toff_t tiff_seekproc(thandle_t clientdata, toff_t offset, int from)
+{
+ tiff_handle *th = (tiff_handle *)clientdata;
+ gdIOCtx *ctx = th->ctx;
+ int result;
+
+ switch(from) {
+ default:
+ case SEEK_SET:
+ /* just use offset */
+ break;
+
+ case SEEK_END:
+ /* invert offset, so that it is from start, not end as supplied */
+ offset = th->size + offset;
+ break;
+
+ case SEEK_CUR:
+ /* add current position to translate it to 'from start',
+ * not from durrent as supplied
+ */
+ offset += th->pos;
+ break;
+ }
+
+ /* now, move pos in both io context and buf */
+ if((result = (ctx->seek)(ctx, offset))) {
+ th->pos = offset;
+ }
+
+ return result ? offset : (toff_t)-1;
+}
+
+/* TIFFCloseProc tiff_closeproc - used to finally close the TIFF file */
+static int tiff_closeproc(thandle_t clientdata)
+{
+ (void)clientdata;
+ /*tiff_handle *th = (tiff_handle *)clientdata;
+ gdIOCtx *ctx = th->ctx;
+
+ (ctx->gd_free)(ctx);*/
+
+ return 0;
+}
+
+/* TIFFSizeProc tiff_sizeproc */
+static toff_t tiff_sizeproc(thandle_t clientdata)
+{
+ tiff_handle *th = (tiff_handle *)clientdata;
+ return th->size;
+}
+
+/* TIFFMapFileProc tiff_mapproc() */
+static int tiff_mapproc(thandle_t h, tdata_t *d, toff_t *o)
+{
+ (void)h;
+ (void)d;
+ (void)o;
+ return 0;
+}
+
+/* TIFFUnmapFileProc tiff_unmapproc */
+static void tiff_unmapproc(thandle_t h, tdata_t d, toff_t o)
+{
+ (void)h;
+ (void)d;
+ (void)o;
+}
+
+
+/* tiffWriter
+ * ----------
+ * Write the gd image as a tiff file (called by gdImageTiffCtx)
+ * Parameters are:
+ * image: gd image structure;
+ * out: the stream where to write
+ * bitDepth: depth in bits of each pixel
+ */
+void tiffWriter(gdImagePtr image, gdIOCtx *out, int bitDepth)
+{
+ int x, y;
+ int i;
+ int r, g, b, a;
+ TIFF *tiff;
+ int width, height;
+ int color;
+ char *scan;
+ int samplesPerPixel = 3;
+ int bitsPerSample;
+ int transparentColorR = -1;
+ int transparentColorG = -1;
+ int transparentColorB = -1;
+ uint16 extraSamples[1];
+ uint16 *colorMapRed = NULL;
+ uint16 *colorMapGreen = NULL;
+ uint16 *colorMapBlue = NULL;
+
+ tiff_handle *th;
+
+ th = new_tiff_handle(out);
+ if (!th) {
+ return;
+ }
+ extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
+
+ /* read in the width/height of gd image */
+ width = gdImageSX(image);
+ height = gdImageSY(image);
+
+ /* reset clip region to whole image */
+ gdImageSetClip(image, 0, 0, width, height);
+
+ /* handle old-style single-colour mapping to 100% transparency */
+ if(image->transparent != -1) {
+ /* set our 100% transparent colour value */
+ transparentColorR = gdImageRed(image, image->transparent);
+ transparentColorG = gdImageGreen(image, image->transparent);
+ transparentColorB = gdImageBlue(image, image->transparent);
+ }
+
+ /* Open tiff file writing routines, but use special read/write/seek
+ * functions so that tiff lib writes correct bits of tiff content to
+ * correct areas of file opened and modifieable by the gdIOCtx functions
+ */
+ tiff = TIFFClientOpen("", "w", th, tiff_readproc,
+ tiff_writeproc,
+ tiff_seekproc,
+ tiff_closeproc,
+ tiff_sizeproc,
+ tiff_mapproc,
+ tiff_unmapproc);
+
+ TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE);
+ TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC,
+ (bitDepth == 24) ? PHOTOMETRIC_RGB : PHOTOMETRIC_PALETTE);
+
+ bitsPerSample = (bitDepth == 24 || bitDepth == 8) ? 8 : 1;
+ TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bitsPerSample);
+
+ /* build the color map for 8 bit images */
+ if(bitDepth != 24) {
+ colorMapRed = (uint16 *) gdMalloc(3 * (1 << bitsPerSample));
+ if (!colorMapRed) {
+ return;
+ }
+ colorMapGreen = (uint16 *) gdMalloc(3 * (1 << bitsPerSample));
+ if (!colorMapGreen) {
+ gdFree(colorMapRed);
+ return;
+ }
+ colorMapBlue = (uint16 *) gdMalloc(3 * (1 << bitsPerSample));
+ if (!colorMapBlue) {
+ gdFree(colorMapRed);
+ gdFree(colorMapGreen);
+ return;
+ }
+
+ for(i = 0; i < image->colorsTotal; i++) {
+ colorMapRed[i] = gdImageRed(image,i) + (gdImageRed(image,i) * 256);
+ colorMapGreen[i] = gdImageGreen(image,i)+(gdImageGreen(image,i)*256);
+ colorMapBlue[i] = gdImageBlue(image,i) + (gdImageBlue(image,i)*256);
+ }
+
+ TIFFSetField(tiff, TIFFTAG_COLORMAP, colorMapRed, colorMapGreen,
+ colorMapBlue);
+ samplesPerPixel = 1;
+ }
+
+ /* here, we check if the 'save alpha' flag is set on the source gd image */
+ if ((bitDepth == 24) &&
+ (image->saveAlphaFlag || image->transparent != -1)) {
+ /* so, we need to store the alpha values too!
+ * Also, tell TIFF what the extra sample means (associated alpha) */
+ samplesPerPixel = 4;
+ TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel);
+ TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, extraSamples);
+ } else {
+ TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel);
+ }
+
+ TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1);
+
+ if(overflow2(width, samplesPerPixel)) {
+ return;
+ }
+
+ if(!(scan = (char *)gdMalloc(width * samplesPerPixel))) {
+ return;
+ }
+
+ /* loop through y-coords, and x-coords */
+ for(y = 0; y < height; y++) {
+ for(x = 0; x < width; x++) {
+ /* generate scan line for writing to tiff */
+ color = gdImageGetPixel(image, x, y);
+
+ a = (127 - gdImageAlpha(image, color)) * 2;
+ a = (a == 0xfe) ? 0xff : a & 0xff;
+ b = gdImageBlue(image, color);
+ g = gdImageGreen(image, color);
+ r = gdImageRed(image, color);
+
+ /* if this pixel has the same RGB as the transparent colour,
+ * then set alpha fully transparent */
+ if (transparentColorR == r &&
+ transparentColorG == g &&
+ transparentColorB == b) {
+ a = 0x00;
+ }
+
+ if(bitDepth != 24) {
+ /* write out 1 or 8 bit value in 1 byte
+ * (currently treats 1bit as 8bit) */
+ scan[(x * samplesPerPixel) + 0] = color;
+ } else {
+ /* write out 24 bit value in 3 (or 4 if transparent) bytes */
+ if(image->saveAlphaFlag || image->transparent != -1) {
+ scan[(x * samplesPerPixel) + 3] = a;
+ }
+
+ scan[(x * samplesPerPixel) + 2] = b;
+ scan[(x * samplesPerPixel) + 1] = g;
+ scan[(x * samplesPerPixel) + 0] = r;
+ }
+ }
+
+ /* Write the scan line to the tiff */
+ if(TIFFWriteEncodedStrip(tiff, y, scan, width * samplesPerPixel) == -1) {
+ /* error handler here */
+ gd_error("Could not create TIFF\n");
+ return;
+ }
+ }
+
+ /* now cloase and free up resources */
+ TIFFClose(tiff);
+ gdFree(scan);
+ gdFree(th);
+
+ if(bitDepth != 24) {
+ gdFree(colorMapRed);
+ gdFree(colorMapGreen);
+ gdFree(colorMapBlue);
+ }
+}
+
+/*
+ gdImageTiffCtx
+ --------------
+ Write the gd image as a tiff file
+ Parameters are:
+ image: gd image structure;
+ out: the stream where to write
+ */
+BGD_DECLARE(void) gdImageTiffCtx(gdImagePtr image, gdIOCtx *out)
+{
+ int clipx1P, clipy1P, clipx2P, clipy2P;
+ int bitDepth = 24;
+
+ /* First, switch off clipping, or we'll not get all the image! */
+ gdImageGetClip(image, &clipx1P, &clipy1P, &clipx2P, &clipy2P);
+
+ /* use the appropriate routine depending on the bit depth of the image */
+ if(image->trueColor) {
+ bitDepth = 24;
+ } else if(image->colorsTotal == 2) {
+ bitDepth = 1;
+ } else {
+ bitDepth = 8;
+ }
+
+ tiffWriter(image, out, bitDepth);
+
+ /* reset clipping area to the gd image's original values */
+ gdImageSetClip(image, clipx1P, clipy1P, clipx2P, clipy2P);
+}
+
+/* Check if we are really in 8bit mode */
+static int checkColorMap(n, r, g, b)
+int n;
+uint16 *r, *g, *b;
+{
+ while (n-- > 0)
+ if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
+ return (16);
+ return (8);
+}
+
+
+/* Read and convert a TIFF colormap */
+static int readTiffColorMap(gdImagePtr im, TIFF *tif, char is_bw, int photometric)
+{
+ uint16 *redcmap, *greencmap, *bluecmap;
+ uint16 bps;
+ int i;
+
+ if (is_bw) {
+ if (photometric == PHOTOMETRIC_MINISWHITE) {
+ gdImageColorAllocate(im, 255,255,255);
+ gdImageColorAllocate(im, 0, 0, 0);
+ } else {
+ gdImageColorAllocate(im, 0, 0, 0);
+ gdImageColorAllocate(im, 255,255,255);
+ }
+ } else {
+ uint16 min_sample_val, max_sample_val;
+
+ if (!TIFFGetField(tif, TIFFTAG_MINSAMPLEVALUE, &min_sample_val)) {
+ min_sample_val = 0;
+ }
+ if (!TIFFGetField(tif, TIFFTAG_MAXSAMPLEVALUE, &max_sample_val)) {
+ max_sample_val = 255;
+ }
+
+ if (photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE) {
+ /* TODO: use TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE */
+ /* Gray level palette */
+ for (i=min_sample_val; i <= max_sample_val; i++) {
+ gdImageColorAllocate(im, i,i,i);
+ }
+ return GD_SUCCESS;
+
+ } else if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap)) {
+ gd_error("Cannot read the color map");
+ return GD_FAILURE;
+ }
+
+ TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
+
+#define CVT(x) (((x) * 255) / ((1L<<16)-1))
+ if (checkColorMap(1<<bps, redcmap, greencmap, bluecmap) == 16) {
+ for (i = (1<<bps)-1; i > 0; i--) {
+ redcmap[i] = CVT(redcmap[i]);
+ greencmap[i] = CVT(greencmap[i]);
+ bluecmap[i] = CVT(bluecmap[i]);
+ }
+ }
+ for (i = 0; i < 256; i++) {
+ gdImageColorAllocate(im, redcmap[i], greencmap[i], bluecmap[i]);
+ }
+#undef CVT
+ }
+ return GD_SUCCESS;
+}
+
+static void readTiffBw (const unsigned char *src,
+ gdImagePtr im,
+ uint16 photometric,
+ int startx,
+ int starty,
+ int width,
+ int height,
+ char has_alpha,
+ int extra,
+ int align)
+{
+ int x = startx, y = starty;
+
+ (void)has_alpha;
+ (void)extra;
+ (void)align;
+
+ for (y = starty; y < starty + height; y++) {
+ for (x = startx; x < startx + width; x++) {
+ register unsigned char curr = *src++;
+ register unsigned char mask;
+
+ if (photometric == PHOTOMETRIC_MINISWHITE) {
+ curr = ~curr;
+ }
+ for (mask = 0x80; mask != 0 && x < startx + width; mask >>= 1) {
+ gdImageSetPixel(im, x, y, ((curr & mask) != 0)?0:1);
+ }
+ }
+ }
+}
+
+static void readTiff8bit (const unsigned char *src,
+ gdImagePtr im,
+ uint16 photometric,
+ int startx,
+ int starty,
+ int width,
+ int height,
+ char has_alpha,
+ int extra,
+ int align)
+{
+ int red, green, blue, alpha;
+ int x, y;
+
+ (void)extra;
+ (void)align;
+
+ switch (photometric) {
+ case PHOTOMETRIC_PALETTE:
+ /* Palette has no alpha (see TIFF specs for more details */
+ for (y = starty; y < starty + height; y++) {
+ for (x = startx; x < startx + width; x++) {
+ gdImageSetPixel(im, x, y,*(src++));
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_RGB:
+ if (has_alpha) {
+ gdImageAlphaBlending(im, 0);
+ gdImageSaveAlpha(im, 1);
+
+ for (y = starty; y < starty + height; y++) {
+ for (x = startx; x < startx + width; x++) {
+ red = *src++;
+ green = *src++;
+ blue = *src++;
+ alpha = *src++;
+ red = MIN (red, alpha);
+ blue = MIN (blue, alpha);
+ green = MIN (green, alpha);
+
+ if (alpha) {
+ gdImageSetPixel(im, x, y, gdTrueColorAlpha(red * 255 / alpha, green * 255 / alpha, blue * 255 /alpha, gdAlphaMax - (alpha >> 1)));
+ } else {
+ gdImageSetPixel(im, x, y, gdTrueColorAlpha(red, green, blue, gdAlphaMax - (alpha >> 1)));
+ }
+ }
+ }
+
+ } else {
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ register unsigned char r = *src++;
+ register unsigned char g = *src++;
+ register unsigned char b = *src++;
+
+ gdImageSetPixel(im, x, y, gdTrueColor(r, g, b));
+ }
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_MINISWHITE:
+ if (has_alpha) {
+ /* We don't process the extra yet */
+ } else {
+ for (y = starty; y < starty + height; y++) {
+ for (x = startx; x < startx + width; x++) {
+ gdImageSetPixel(im, x, y, ~(*src++));
+ }
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_MINISBLACK:
+ if (has_alpha) {
+ /* We don't process the extra yet */
+ } else {
+ for (y = starty; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ gdImageSetPixel(im, x, y, *src++);
+ }
+ }
+ }
+ break;
+ }
+}
+
+static int createFromTiffTiles(TIFF *tif, gdImagePtr im, uint16 bps, uint16 photometric,
+ char has_alpha, char is_bw, int extra)
+{
+ uint16 planar;
+ int im_width, im_height;
+ int tile_width, tile_height;
+ int x, y, height, width;
+ unsigned char *buffer;
+
+ if (!TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar)) {
+ planar = PLANARCONFIG_CONTIG;
+ }
+ TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &im_width);
+ TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &im_height);
+ TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height);
+
+ buffer = (unsigned char *) gdMalloc (TIFFTileSize (tif));
+ if (!buffer) {
+ return FALSE;
+ }
+
+ for (y = 0; y < im_height; y += tile_height) {
+ for (x = 0; x < im_width; x += tile_width) {
+ TIFFReadTile(tif, buffer, x, y, 0, 0);
+ width = MIN(im_width - x, tile_width);
+ height = MIN(im_height - y, tile_height);
+ if (bps == 16) {
+ } else if (bps == 8) {
+ readTiff8bit(buffer, im, photometric, x, y, width, height, has_alpha, extra, 0);
+ } else if (is_bw) {
+ readTiffBw(buffer, im, photometric, x, y, width, height, has_alpha, extra, 0);
+ } else {
+ /* TODO: implement some default reader or detect this case earlier use force_rgb */
+ }
+ }
+ }
+ return TRUE;
+}
+
+static int createFromTiffLines(TIFF *tif, gdImagePtr im, uint16 bps, uint16 photometric,
+ char has_alpha, char is_bw, int extra)
+{
+ uint16 planar;
+ uint32 im_height, im_width, y;
+
+ unsigned char *buffer;
+
+ if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar)) {
+ planar = PLANARCONFIG_CONTIG;
+ }
+
+ if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &im_height)) {
+ gd_error("Can't fetch TIFF height\n");
+ return FALSE;
+ }
+
+ if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &im_width)) {
+ gd_error("Can't fetch TIFF width \n");
+ return FALSE;
+ }
+
+ buffer = (unsigned char *)gdMalloc(im_width * 4);
+ if (!buffer) {
+ return GD_FAILURE;
+ }
+ if (planar == PLANARCONFIG_CONTIG) {
+ switch (bps) {
+ case 16:
+ /* TODO
+ * or simply use force_rgba
+ */
+ break;
+
+ case 8:
+ for (y = 0; y < im_height; y++ ) {
+ if (!TIFFReadScanline (tif, buffer, y, 0)) {
+ gd_error("Error while reading scanline %i", y);
+ break;
+ }
+ /* reading one line at a time */
+ readTiff8bit(buffer, im, photometric, 0, y, im_width, 1, has_alpha, extra, 0);
+ }
+ break;
+
+ default:
+ if (is_bw) {
+ for (y = 0; y < im_height; y++ ) {
+ if (!TIFFReadScanline (tif, buffer, y, 0)) {
+ gd_error("Error while reading scanline %i", y);
+ break;
+ }
+ /* reading one line at a time */
+ readTiffBw(buffer, im, photometric, 0, y, im_width, 1, has_alpha, extra, 0);
+ }
+ } else {
+ /* TODO: implement some default reader or detect this case earlier > force_rgb */
+ }
+ break;
+ }
+ } else {
+ /* TODO: implement a reader for separate panes. We detect this case earlier for now and use force_rgb */
+ }
+
+ gdFree(buffer);
+ return GD_SUCCESS;
+}
+
+static int createFromTiffRgba(TIFF * tif, gdImagePtr im)
+{
+ int a;
+ int x, y;
+ int alphaBlendingFlag = 0;
+ int color;
+ int width = im->sx;
+ int height = im->sy;
+ uint32 *buffer;
+ uint32 rgba;
+
+ /* switch off colour merging on target gd image just while we write out
+ * content - we want to preserve the alpha data until the user chooses
+ * what to do with the image */
+ alphaBlendingFlag = im->alphaBlendingFlag;
+ gdImageAlphaBlending(im, 0);
+
+ buffer = (uint32 *) gdCalloc(sizeof(uint32), width * height);
+ if (!buffer) {
+ return GD_FAILURE;
+ }
+
+ TIFFReadRGBAImage(tif, width, height, buffer, 0);
+
+ for(y = 0; y < height; y++) {
+ for(x = 0; x < width; x++) {
+ /* if it doesn't already exist, allocate a new colour,
+ * else use existing one */
+ rgba = buffer[(y * width + x)];
+ a = (0xff - TIFFGetA(rgba)) / 2;
+ color = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a);
+
+ /* set pixel colour to this colour */
+ gdImageSetPixel(im, x, height - y - 1, color);
+ }
+ }
+
+ gdFree(buffer);
+
+ /* now reset colour merge for alpha blending routines */
+ gdImageAlphaBlending(im, alphaBlendingFlag);
+ return GD_SUCCESS;
+}
+
+/* gdImageCreateFromTiffCtx
+** ------------------------
+** Create a gdImage from a TIFF file input from an gdIOCtx
+ */
+BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile)
+{
+ TIFF *tif;
+ tiff_handle *th;
+
+ uint16 bps, spp, photometric;
+ uint16 orientation;
+ int width, height;
+ uint16 extra, *extra_types;
+ uint16 planar;
+ char has_alpha, is_bw, is_gray;
+ char force_rgba = FALSE;
+ char save_transparent;
+ int image_type;
+ int ret;
+
+ gdImagePtr im = NULL;
+
+ th = new_tiff_handle(infile);
+ if (!th) {
+ return NULL;
+ }
+
+ tif = TIFFClientOpen("", "rb", th, tiff_readproc,
+ tiff_writeproc,
+ tiff_seekproc,
+ tiff_closeproc,
+ tiff_sizeproc,
+ tiff_mapproc,
+ tiff_unmapproc);
+
+ if (!tif) {
+ gd_error("Cannot open TIFF image");
+ return NULL;
+ }
+
+ if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width)) {
+ gd_error("TIFF error, Cannot read image width");
+ goto error;
+ }
+
+ if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) {
+ gd_error("TIFF error, Cannot read image width");
+ goto error;
+ }
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bps);
+
+ /* Unsupported bps, force to RGBA */
+ if (bps > 8 && bps != 16) {
+ force_rgba = TRUE;
+ }
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
+
+ if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) {
+ extra = 0;
+ }
+
+ if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photometric)) {
+ uint16 compression;
+ if (TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression) &&
+ (compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4 ||
+ compression == COMPRESSION_CCITTRLE ||
+ compression == COMPRESSION_CCITTRLEW)) {
+ gd_error("Could not get photometric. "
+ "Image is CCITT compressed, assuming min-is-white");
+ photometric = PHOTOMETRIC_MINISWHITE;
+ } else {
+ gd_error("Could not get photometric. "
+ "Assuming min-is-black");
+
+ photometric = PHOTOMETRIC_MINISBLACK;
+ }
+ }
+ save_transparent = FALSE;
+
+ /* test if the extrasample represents an associated alpha channel... */
+ if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) {
+ has_alpha = TRUE;
+ save_transparent = FALSE;
+ --extra;
+ } else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA)) {
+ has_alpha = TRUE;
+ save_transparent = TRUE;
+ --extra;
+ } else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED)) {
+ /* assuming unassociated alpha if unspecified */
+ gd_error("alpha channel type not defined, assuming alpha is not premultiplied");
+ has_alpha = TRUE;
+ save_transparent = TRUE;
+ --extra;
+ } else {
+ has_alpha = FALSE;
+ }
+
+ if (photometric == PHOTOMETRIC_RGB && spp > 3 + extra) {
+ has_alpha = TRUE;
+ extra = spp - 4;
+ } else if (photometric != PHOTOMETRIC_RGB && spp > 1 + extra) {
+ has_alpha = TRUE;
+ extra = spp - 2;
+ }
+
+ is_bw = FALSE;
+ is_gray = FALSE;
+
+ switch (photometric) {
+ case PHOTOMETRIC_MINISBLACK:
+ case PHOTOMETRIC_MINISWHITE:
+ if (!has_alpha && bps == 1 && spp == 1) {
+ image_type = GD_INDEXED;
+ is_bw = TRUE;
+ } else {
+ image_type = GD_GRAY;
+ }
+ break;
+
+ case PHOTOMETRIC_RGB:
+ image_type = GD_RGB;
+ break;
+
+ case PHOTOMETRIC_PALETTE:
+ image_type = GD_INDEXED;
+ break;
+
+ default:
+ force_rgba = TRUE;
+ break;
+ }
+
+ if (!TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar)) {
+ planar = PLANARCONFIG_CONTIG;
+ }
+
+ /* Force rgba if image plans are not contiguous */
+ if (force_rgba || planar != PLANARCONFIG_CONTIG) {
+ image_type = GD_RGB;
+ }
+
+ if (!force_rgba &&
+ (image_type == GD_PALETTE || image_type == GD_INDEXED || image_type == GD_GRAY)) {
+ im = gdImageCreate(width, height);
+ if (!im) goto error;
+ readTiffColorMap(im, tif, is_bw, photometric);
+ } else {
+ im = gdImageCreateTrueColor(width, height);
+ if (!im) goto error;
+ }
+
+#ifdef DEBUG
+ printf("force rgba: %i\n", force_rgba);
+ printf("has_alpha: %i\n", has_alpha);
+ printf("save trans: %i\n", save_transparent);
+ printf("is_bw: %i\n", is_bw);
+ printf("is_gray: %i\n", is_gray);
+ printf("type: %i\n", image_type);
+#else
+ (void)is_gray;
+ (void)save_transparent;
+#endif
+
+ if (force_rgba) {
+ ret = createFromTiffRgba(tif, im);
+ } else if (TIFFIsTiled(tif)) {
+ ret = createFromTiffTiles(tif, im, bps, photometric, has_alpha, is_bw, extra);
+ } else {
+ ret = createFromTiffLines(tif, im, bps, photometric, has_alpha, is_bw, extra);
+ }
+
+ if (!ret) {
+ gdImageDestroy(im);
+ im = NULL;
+ goto error;
+ }
+
+ if (TIFFGetField (tif, TIFFTAG_ORIENTATION, &orientation)) {
+ switch (orientation) {
+ case ORIENTATION_TOPLEFT:
+ case ORIENTATION_TOPRIGHT:
+ case ORIENTATION_BOTRIGHT:
+ case ORIENTATION_BOTLEFT:
+ break;
+
+ default:
+ gd_error("Orientation %d not handled yet!", orientation);
+ break;
+ }
+ }
+error:
+ TIFFClose(tif);
+ return im;
+}
+
+/* gdImageCreateFromTIFF
+** ---------------------
+ */
+BGD_DECLARE(gdImagePtr) gdImageCreateFromTiff(FILE *inFile)
+{
+ gdImagePtr im;
+ gdIOCtx *in = gdNewFileCtx(inFile);
+ if (in == NULL) return NULL;
+ im = gdImageCreateFromTiffCtx(in);
+ in->gd_free(in);
+ return im;
+}
+
+BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffPtr(int size, void *data)
+{
+ gdImagePtr im;
+ gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0);
+ if (in == NULL) return NULL;
+ im = gdImageCreateFromTiffCtx(in);
+ in->gd_free(in);
+ return im;
+}
+
+/* gdImageTIFF
+** -----------
+ */
+BGD_DECLARE(void) gdImageTiff(gdImagePtr im, FILE *outFile)
+{
+ gdIOCtx *out = gdNewFileCtx(outFile);
+ if (out == NULL) return;
+ gdImageTiffCtx(im, out); /* what's an fg again? */
+ out->gd_free(out);
+}
+
+/* gdImageTIFFPtr
+** --------------
+ */
+BGD_DECLARE(void *) gdImageTiffPtr(gdImagePtr im, int *size)
+{
+ void *rv;
+ gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
+ if (out == NULL) return NULL;
+ gdImageTiffCtx(im, out); /* what's an fg again? */
+ rv = gdDPExtractData(out, size);
+ out->gd_free(out);
+ return rv;
+}
+
+#endif