/* 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 ---------------------------------------------------------------------------- */ /** * File: TIFF IO * * Read and write TIFF images. * * There is only most basic support for the TIFF format available for now; * for instance, multiple pages are not yet supported. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "gd.h" #include "gd_errors.h" #include "gd_intern.h" #include "gdfonts.h" #include #include #include #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 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_t extraSamples[1]; uint16_t *colorMapRed = NULL; uint16_t *colorMapGreen = NULL; uint16_t *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); TIFFSetField(tiff, TIFFTAG_XRESOLUTION, (float)image->res_x); TIFFSetField(tiff, TIFFTAG_YRESOLUTION, (float)image->res_y); /* build the color map for 8 bit images */ if(bitDepth != 24) { colorMapRed = (uint16_t *) gdMalloc(3 * (1 << bitsPerSample)); if (!colorMapRed) { gdFree(th); return; } colorMapGreen = (uint16_t *) gdMalloc(3 * (1 << bitsPerSample)); if (!colorMapGreen) { gdFree(colorMapRed); gdFree(th); return; } colorMapBlue = (uint16_t *) gdMalloc(3 * (1 << bitsPerSample)); if (!colorMapBlue) { gdFree(colorMapRed); gdFree(colorMapGreen); gdFree(th); 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)) { if (colorMapRed) gdFree(colorMapRed); if (colorMapGreen) gdFree(colorMapGreen); if (colorMapBlue) gdFree(colorMapBlue); gdFree(th); return; } if(!(scan = (char *)gdMalloc(width * samplesPerPixel))) { if (colorMapRed) gdFree(colorMapRed); if (colorMapGreen) gdFree(colorMapGreen); if (colorMapBlue) gdFree(colorMapBlue); gdFree(th); 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) { if (colorMapRed) gdFree(colorMapRed); if (colorMapGreen) gdFree(colorMapGreen); if (colorMapBlue) gdFree(colorMapBlue); gdFree(th); /* 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); } } /* Function: gdImageTiffCtx Write the gd image as a tiff file. Parameters: 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(int n, uint16_t *r, uint16_t *g, uint16_t *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_t *redcmap, *greencmap, *bluecmap; uint16_t 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_t 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< 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_t 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;) { register unsigned char curr = *src++; register unsigned char mask; if (photometric == PHOTOMETRIC_MINISWHITE) { curr = ~curr; } for (mask = 0x80; mask != 0 && x < startx + width; x++, mask >>= 1) { gdImageSetPixel(im, x, y, ((curr & mask) != 0)?0:1); } } } } static void readTiff8bit (const unsigned char *src, gdImagePtr im, uint16_t 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_t bps, uint16_t photometric, char has_alpha, char is_bw, int extra) { uint16_t planar; int im_width, im_height; int tile_width, tile_height; int x, y, height, width; unsigned char *buffer; int success = GD_SUCCESS; if (!TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar)) { planar = PLANARCONFIG_CONTIG; } if (TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &im_width) == 0 || TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &im_height) == 0 || TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width) == 0 || TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height) == 0) { return FALSE; } 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) { if (TIFFReadTile(tif, buffer, x, y, 0, 0) < 0) { success = GD_FAILURE; goto end; } 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 */ } } } end: gdFree(buffer); return success; } static int createFromTiffLines(TIFF *tif, gdImagePtr im, uint16_t bps, uint16_t photometric, char has_alpha, char is_bw, int extra) { uint16_t planar; uint32_t im_height, im_width, y; unsigned char *buffer; int success = GD_SUCCESS; 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) < 0) { gd_error("Error while reading scanline %i", y); success = GD_FAILURE; 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) < 0) { gd_error("Error while reading scanline %i", y); success = GD_FAILURE; 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 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_t *buffer; uint32_t rgba; int success; buffer = (uint32_t *) gdCalloc(sizeof(uint32_t), width * height); if (!buffer) { return GD_FAILURE; } /* 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); success = TIFFReadRGBAImage(tif, width, height, buffer, 1); if (success) { 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 success; } /* Function: gdImageCreateFromTiffCtx Create a gdImage from a TIFF file input from an gdIOCtx. */ BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile) { TIFF *tif; tiff_handle *th; uint16_t bps, spp, photometric; uint16_t orientation; int width, height; uint16_t extra, *extra_types; uint16_t planar; char has_alpha, is_bw, is_gray; char force_rgba = FALSE; char save_transparent; int image_type; int ret; float res_float; 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"); gdFree(th); 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 != 1 /*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_t 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; } /* Force rgba if image has 1bps, but is not bw */ if (bps == 1 && !is_bw) { force_rgba = TRUE; } 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_XRESOLUTION, &res_float)) { im->res_x = (unsigned int)res_float; //truncate } if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &res_float)) { im->res_y = (unsigned int)res_float; //truncate } 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); gdFree(th); return im; } /* Function: 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; } /* Function: gdImageCreateFromTiffPtr */ 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; } /* Function: 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); } /* Function: 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; } #else static void _noTiffError(void) { gd_error("TIFF image support has been disabled\n"); } BGD_DECLARE(void) gdImageTiffCtx(gdImagePtr image, gdIOCtx *out) { ARG_NOT_USED(image); ARG_NOT_USED(out); _noTiffError(); } BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile) { ARG_NOT_USED(infile); _noTiffError(); return NULL; } BGD_DECLARE(gdImagePtr) gdImageCreateFromTiff(FILE *inFile) { ARG_NOT_USED(inFile); _noTiffError(); return NULL; } BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffPtr(int size, void *data) { ARG_NOT_USED(size); ARG_NOT_USED(data); _noTiffError(); return NULL; } BGD_DECLARE(void) gdImageTiff(gdImagePtr im, FILE *outFile) { ARG_NOT_USED(im); ARG_NOT_USED(outFile); _noTiffError(); } BGD_DECLARE(void *) gdImageTiffPtr(gdImagePtr im, int *size) { ARG_NOT_USED(im); ARG_NOT_USED(size); _noTiffError(); return NULL; } #endif