"Fossies" - the Fresh Open Source Software Archive

Member "libgd-2.3.3/src/gd_tiff.c" (11 Sep 2021, 27878 Bytes) of package /linux/www/libgd-2.3.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2    TIFF - Tagged Image File Format Encapsulation for GD Library
    3 
    4    gd_tiff.c
    5    Copyright (C) Pierre-A. Joye, M. Retallack
    6 
    7    ---------------------------------------------------------------------------
    8    **
    9    ** Permission to use, copy, modify, and distribute this software and its
   10    ** documentation for any purpose and without fee is hereby granted, provided
   11    ** that the above copyright notice appear in all copies and that both that
   12    ** copyright notice and this permission notice appear in supporting
   13    ** documentation.  This software is provided "as is" without express or
   14    ** implied warranty.
   15    **
   16    ---------------------------------------------------------------------------
   17    Ctx code written by M. Retallack
   18 
   19    Todo:
   20 
   21    If we fail - cleanup
   22    Writer: Use gd error function, overflow check may not be necessary as
   23      we write our own data (check already done)
   24 
   25    Implement 2 color black/white saving using group4 fax compression
   26    Implement function to specify encoding to use when writing tiff data
   27 
   28    ----------------------------------------------------------------------------
   29  */
   30 
   31 /**
   32  * File: TIFF IO
   33  *
   34  * Read and write TIFF images.
   35  *
   36  * There is only most basic support for the TIFF format available for now;
   37  * for instance, multiple pages are not yet supported.
   38  */
   39 
   40 #ifdef HAVE_CONFIG_H
   41 #   include "config.h"
   42 #endif
   43 
   44 #include "gd.h"
   45 #include "gd_errors.h"
   46 #include "gd_intern.h"
   47 #include "gdfonts.h"
   48 #include <stdio.h>
   49 #include <stdlib.h>
   50 #include <limits.h>
   51 
   52 #include "gdhelpers.h"
   53 
   54 #ifdef HAVE_LIBTIFF
   55 
   56 #include "tiff.h"
   57 #include "tiffio.h"
   58 
   59 #define GD_SUCCESS 1
   60 #define GD_FAILURE 0
   61 
   62 #define TRUE 1
   63 #define FALSE 0
   64 
   65 /* I define those here until the new formats
   66  * are commited. We can then rely on the global
   67  * def
   68  */
   69 #define GD_PALETTE 1
   70 #define GD_TRUECOLOR 2
   71 #define GD_GRAY 3
   72 #define GD_INDEXED 4
   73 #define GD_RGB 5
   74 
   75 typedef struct tiff_handle {
   76     int size;
   77     int pos;
   78     gdIOCtx *ctx;
   79     int written;
   80 }
   81 tiff_handle;
   82 
   83 /*
   84    Functions for reading, writing and seeking in gdIOCtx
   85    This allows for non-file i/o operations with no
   86    explicit use of libtiff fileio wrapper functions
   87 
   88    Note: because libtiff requires random access, but gdIOCtx
   89          only supports streams, all writes are buffered
   90          into memory and written out on close, also all
   91          reads are done from a memory mapped version of the
   92          tiff (assuming one already exists)
   93 */
   94 
   95 tiff_handle * new_tiff_handle(gdIOCtx *g)
   96 {
   97     tiff_handle * t;
   98 
   99     if (!g) {
  100         gd_error("Cannot create a new tiff handle, missing Ctx argument");
  101         return NULL;
  102     }
  103 
  104     t = (tiff_handle *) gdMalloc(sizeof(tiff_handle));
  105     if (!t) {
  106         gd_error("Failed to allocate a new tiff handle");
  107         return NULL;
  108     }
  109 
  110     t->size = 0;
  111     t->pos = 0;
  112     t->ctx = g;
  113     t->written = 0;
  114 
  115     return t;
  116 }
  117 
  118 /* TIFFReadWriteProc tiff_readproc - Will use gdIOCtx procs to read required
  119    (previously written) TIFF file content */
  120 static tsize_t tiff_readproc(thandle_t clientdata, tdata_t data, tsize_t size)
  121 {
  122     tiff_handle *th = (tiff_handle *)clientdata;
  123     gdIOCtx *ctx = th->ctx;
  124 
  125     size = (ctx->getBuf)(ctx, data, size);
  126 
  127     return size;
  128 }
  129 
  130 /* TIFFReadWriteProc tiff_writeproc - Will use gdIOCtx procs to write out
  131    TIFF data */
  132 static tsize_t tiff_writeproc(thandle_t clientdata, tdata_t data, tsize_t size)
  133 {
  134     tiff_handle *th = (tiff_handle *)clientdata;
  135     gdIOCtx *ctx = th->ctx;
  136 
  137     size = (ctx->putBuf)(ctx, data, size);
  138     if(size + th->pos>th->size) {
  139         th->size = size + th->pos;
  140         th->pos += size;
  141     }
  142 
  143     return size;
  144 }
  145 
  146 /* TIFFSeekProc tiff_seekproc
  147  * used to move around the partially written TIFF */
  148 static toff_t tiff_seekproc(thandle_t clientdata, toff_t offset, int from)
  149 {
  150     tiff_handle *th = (tiff_handle *)clientdata;
  151     gdIOCtx *ctx = th->ctx;
  152     int result;
  153 
  154     switch(from) {
  155     default:
  156     case SEEK_SET:
  157         /* just use offset */
  158         break;
  159 
  160     case SEEK_END:
  161         /* invert offset, so that it is from start, not end as supplied */
  162         offset = th->size + offset;
  163         break;
  164 
  165     case SEEK_CUR:
  166         /* add current position to translate it to 'from start',
  167          * not from durrent as supplied
  168          */
  169         offset += th->pos;
  170         break;
  171     }
  172 
  173     /* now, move pos in both io context and buf */
  174     if((result = (ctx->seek)(ctx, offset))) {
  175         th->pos = offset;
  176     }
  177 
  178     return result ? offset : (toff_t)-1;
  179 }
  180 
  181 /* TIFFCloseProc tiff_closeproc - used to finally close the TIFF file */
  182 static int tiff_closeproc(thandle_t clientdata)
  183 {
  184     (void)clientdata;
  185     /*tiff_handle *th = (tiff_handle *)clientdata;
  186     gdIOCtx *ctx = th->ctx;
  187 
  188     (ctx->gd_free)(ctx);*/
  189 
  190     return 0;
  191 }
  192 
  193 /* TIFFSizeProc tiff_sizeproc */
  194 static toff_t tiff_sizeproc(thandle_t clientdata)
  195 {
  196     tiff_handle *th = (tiff_handle *)clientdata;
  197     return th->size;
  198 }
  199 
  200 /* TIFFMapFileProc tiff_mapproc() */
  201 static int tiff_mapproc(thandle_t h, tdata_t *d, toff_t *o)
  202 {
  203     (void)h;
  204     (void)d;
  205     (void)o;
  206     return 0;
  207 }
  208 
  209 /* TIFFUnmapFileProc tiff_unmapproc */
  210 static void tiff_unmapproc(thandle_t h, tdata_t d, toff_t o)
  211 {
  212     (void)h;
  213     (void)d;
  214     (void)o;
  215 }
  216 
  217 
  218 /*  tiffWriter
  219  *  ----------
  220  *  Write the gd image as a tiff file (called by gdImageTiffCtx)
  221  *  Parameters are:
  222  *  image:    gd image structure;
  223  *  out:      the stream where to write
  224  *  bitDepth: depth in bits of each pixel
  225  */
  226 void tiffWriter(gdImagePtr image, gdIOCtx *out, int bitDepth)
  227 {
  228     int x, y;
  229     int i;
  230     int r, g, b, a;
  231     TIFF *tiff;
  232     int width, height;
  233     int color;
  234     char *scan;
  235     int samplesPerPixel = 3;
  236     int bitsPerSample;
  237     int transparentColorR = -1;
  238     int transparentColorG = -1;
  239     int transparentColorB = -1;
  240     uint16_t extraSamples[1];
  241     uint16_t *colorMapRed = NULL;
  242     uint16_t *colorMapGreen = NULL;
  243     uint16_t *colorMapBlue = NULL;
  244 
  245     tiff_handle *th;
  246 
  247     th = new_tiff_handle(out);
  248     if (!th) {
  249         return;
  250     }
  251     extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
  252 
  253     /* read in the width/height of gd image */
  254     width = gdImageSX(image);
  255     height = gdImageSY(image);
  256 
  257     /* reset clip region to whole image */
  258     gdImageSetClip(image, 0, 0, width, height);
  259 
  260     /* handle old-style single-colour mapping to 100% transparency */
  261     if(image->transparent != -1) {
  262         /* set our 100% transparent colour value */
  263         transparentColorR = gdImageRed(image, image->transparent);
  264         transparentColorG = gdImageGreen(image, image->transparent);
  265         transparentColorB = gdImageBlue(image, image->transparent);
  266     }
  267 
  268     /* Open tiff file writing routines, but use special read/write/seek
  269      * functions so that tiff lib writes correct bits of tiff content to
  270      * correct areas of file opened and modifieable by the gdIOCtx functions
  271      */
  272     tiff = TIFFClientOpen("", "w", th,  tiff_readproc,
  273                   tiff_writeproc,
  274                   tiff_seekproc,
  275                   tiff_closeproc,
  276                   tiff_sizeproc,
  277                   tiff_mapproc,
  278                   tiff_unmapproc);
  279 
  280     TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width);
  281     TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height);
  282     TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE);
  283     TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  284     TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC,
  285              (bitDepth == 24) ? PHOTOMETRIC_RGB : PHOTOMETRIC_PALETTE);
  286 
  287     bitsPerSample = (bitDepth == 24 || bitDepth == 8) ? 8 : 1;
  288     TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bitsPerSample);
  289 
  290     TIFFSetField(tiff, TIFFTAG_XRESOLUTION, (float)image->res_x);
  291     TIFFSetField(tiff, TIFFTAG_YRESOLUTION, (float)image->res_y);
  292 
  293     /* build the color map for 8 bit images */
  294     if(bitDepth != 24) {
  295         colorMapRed   = (uint16_t *) gdMalloc(3 * (1 << bitsPerSample));
  296         if (!colorMapRed) {
  297             gdFree(th);
  298             return;
  299         }
  300         colorMapGreen = (uint16_t *) gdMalloc(3 * (1 << bitsPerSample));
  301         if (!colorMapGreen) {
  302             gdFree(colorMapRed);
  303             gdFree(th);
  304             return;
  305         }
  306         colorMapBlue  = (uint16_t *) gdMalloc(3 *  (1 << bitsPerSample));
  307         if (!colorMapBlue) {
  308             gdFree(colorMapRed);
  309             gdFree(colorMapGreen);
  310             gdFree(th);
  311             return;
  312         }
  313 
  314         for(i = 0; i < image->colorsTotal; i++) {
  315             colorMapRed[i]   = gdImageRed(image,i) + (gdImageRed(image,i) * 256);
  316             colorMapGreen[i] = gdImageGreen(image,i)+(gdImageGreen(image,i)*256);
  317             colorMapBlue[i]  = gdImageBlue(image,i) + (gdImageBlue(image,i)*256);
  318         }
  319 
  320         TIFFSetField(tiff, TIFFTAG_COLORMAP, colorMapRed, colorMapGreen,
  321                  colorMapBlue);
  322         samplesPerPixel = 1;
  323     }
  324 
  325     /* here, we check if the 'save alpha' flag is set on the source gd image */
  326     if ((bitDepth == 24) &&
  327         (image->saveAlphaFlag || image->transparent != -1)) {
  328         /* so, we need to store the alpha values too!
  329          * Also, tell TIFF what the extra sample means (associated alpha) */
  330         samplesPerPixel = 4;
  331         TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel);
  332         TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, extraSamples);
  333     } else {
  334         TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel);
  335     }
  336 
  337     TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1);
  338 
  339     if(overflow2(width, samplesPerPixel)) {
  340         if (colorMapRed)   gdFree(colorMapRed);
  341         if (colorMapGreen) gdFree(colorMapGreen);
  342         if (colorMapBlue)  gdFree(colorMapBlue);
  343         gdFree(th);
  344         return;
  345     }
  346 
  347     if(!(scan = (char *)gdMalloc(width * samplesPerPixel))) {
  348         if (colorMapRed)   gdFree(colorMapRed);
  349         if (colorMapGreen) gdFree(colorMapGreen);
  350         if (colorMapBlue)  gdFree(colorMapBlue);
  351         gdFree(th);
  352         return;
  353     }
  354 
  355     /* loop through y-coords, and x-coords */
  356     for(y = 0; y < height; y++) {
  357         for(x = 0; x < width; x++) {
  358             /* generate scan line for writing to tiff */
  359             color = gdImageGetPixel(image, x, y);
  360 
  361             a = (127 - gdImageAlpha(image, color)) * 2;
  362             a = (a == 0xfe) ? 0xff : a & 0xff;
  363             b = gdImageBlue(image, color);
  364             g = gdImageGreen(image, color);
  365             r = gdImageRed(image, color);
  366 
  367             /* if this pixel has the same RGB as the transparent colour,
  368              * then set alpha fully transparent */
  369             if (transparentColorR == r &&
  370                 transparentColorG == g &&
  371                 transparentColorB == b) {
  372                 a = 0x00;
  373             }
  374 
  375             if(bitDepth != 24) {
  376                 /* write out 1 or 8 bit value in 1 byte
  377                  * (currently treats 1bit as 8bit) */
  378                 scan[(x * samplesPerPixel) + 0] = color;
  379             } else {
  380                 /* write out 24 bit value in 3 (or 4 if transparent) bytes */
  381                 if(image->saveAlphaFlag || image->transparent != -1) {
  382                     scan[(x * samplesPerPixel) + 3] = a;
  383                 }
  384 
  385                 scan[(x * samplesPerPixel) + 2] = b;
  386                 scan[(x * samplesPerPixel) + 1] = g;
  387                 scan[(x * samplesPerPixel) + 0] = r;
  388             }
  389         }
  390 
  391         /* Write the scan line to the tiff */
  392         if(TIFFWriteEncodedStrip(tiff, y, scan, width * samplesPerPixel) == -1) {
  393             if (colorMapRed)   gdFree(colorMapRed);
  394             if (colorMapGreen) gdFree(colorMapGreen);
  395             if (colorMapBlue)  gdFree(colorMapBlue);
  396             gdFree(th);
  397             /* error handler here */
  398             gd_error("Could not create TIFF\n");
  399             return;
  400         }
  401     }
  402 
  403     /* now cloase and free up resources */
  404     TIFFClose(tiff);
  405     gdFree(scan);
  406     gdFree(th);
  407 
  408     if(bitDepth != 24) {
  409         gdFree(colorMapRed);
  410         gdFree(colorMapGreen);
  411         gdFree(colorMapBlue);
  412     }
  413 }
  414 
  415 /*
  416     Function: gdImageTiffCtx
  417 
  418     Write the gd image as a tiff file.
  419 
  420     Parameters:
  421 
  422         image - gd image structure;
  423         out   - the stream where to write
  424 */
  425 BGD_DECLARE(void) gdImageTiffCtx(gdImagePtr image, gdIOCtx *out)
  426 {
  427     int clipx1P, clipy1P, clipx2P, clipy2P;
  428     int bitDepth = 24;
  429 
  430     /* First, switch off clipping, or we'll not get all the image! */
  431     gdImageGetClip(image, &clipx1P, &clipy1P, &clipx2P, &clipy2P);
  432 
  433     /* use the appropriate routine depending on the bit depth of the image */
  434     if(image->trueColor) {
  435         bitDepth = 24;
  436     } else if(image->colorsTotal == 2) {
  437         bitDepth = 1;
  438     } else {
  439         bitDepth = 8;
  440     }
  441 
  442     tiffWriter(image, out, bitDepth);
  443 
  444     /* reset clipping area to the gd image's original values */
  445     gdImageSetClip(image, clipx1P, clipy1P, clipx2P, clipy2P);
  446 }
  447 
  448 /* Check if we are really in 8bit mode */
  449 static int checkColorMap(n, r, g, b)
  450 int n;
  451 uint16_t *r, *g, *b;
  452 {
  453     while (n-- > 0)
  454         if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
  455             return (16);
  456     return (8);
  457 }
  458 
  459 
  460 /* Read and convert a TIFF colormap */
  461 static int readTiffColorMap(gdImagePtr im, TIFF *tif, char is_bw, int photometric)
  462 {
  463     uint16_t *redcmap, *greencmap, *bluecmap;
  464     uint16_t bps;
  465     int i;
  466 
  467     if (is_bw) {
  468         if (photometric == PHOTOMETRIC_MINISWHITE) {
  469             gdImageColorAllocate(im, 255,255,255);
  470             gdImageColorAllocate(im, 0, 0, 0);
  471         } else {
  472             gdImageColorAllocate(im, 0, 0, 0);
  473             gdImageColorAllocate(im, 255,255,255);
  474         }
  475     } else {
  476         uint16_t min_sample_val, max_sample_val;
  477 
  478         if (!TIFFGetField(tif, TIFFTAG_MINSAMPLEVALUE, &min_sample_val)) {
  479             min_sample_val = 0;
  480         }
  481         if (!TIFFGetField(tif, TIFFTAG_MAXSAMPLEVALUE, &max_sample_val)) {
  482             max_sample_val = 255;
  483         }
  484 
  485         if (photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE) {
  486             /* TODO: use TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE */
  487             /* Gray level palette */
  488             for (i=min_sample_val; i <= max_sample_val; i++) {
  489                 gdImageColorAllocate(im, i,i,i);
  490             }
  491             return GD_SUCCESS;
  492 
  493         } else if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap)) {
  494             gd_error("Cannot read the color map");
  495             return GD_FAILURE;
  496         }
  497 
  498         TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
  499 
  500 #define CVT(x)      (((x) * 255) / ((1L<<16)-1))
  501         if (checkColorMap(1<<bps, redcmap, greencmap, bluecmap) == 16) {
  502             for (i = (1<<bps)-1; i > 0; i--) {
  503                 redcmap[i] = CVT(redcmap[i]);
  504                 greencmap[i] = CVT(greencmap[i]);
  505                 bluecmap[i] = CVT(bluecmap[i]);
  506             }
  507         }
  508         for (i = 0; i < 256; i++) {
  509             gdImageColorAllocate(im, redcmap[i], greencmap[i], bluecmap[i]);
  510         }
  511 #undef CVT
  512     }
  513     return GD_SUCCESS;
  514 }
  515 
  516 static void readTiffBw (const unsigned char *src,
  517             gdImagePtr im,
  518             uint16_t       photometric,
  519             int          startx,
  520             int          starty,
  521             int          width,
  522             int          height,
  523             char         has_alpha,
  524             int          extra,
  525             int          align)
  526 {
  527     int x = startx, y = starty;
  528 
  529     (void)has_alpha;
  530     (void)extra;
  531     (void)align;
  532 
  533     for (y = starty; y < starty + height; y++) {
  534         for (x = startx; x < startx + width;) {
  535             register unsigned char curr = *src++;
  536             register unsigned char mask;
  537 
  538             if (photometric == PHOTOMETRIC_MINISWHITE) {
  539                 curr = ~curr;
  540             }
  541             for (mask = 0x80; mask != 0 && x < startx + width; x++, mask >>= 1) {
  542                 gdImageSetPixel(im, x, y, ((curr & mask) != 0)?0:1);
  543             }
  544         }
  545     }
  546 }
  547 
  548 static void readTiff8bit (const unsigned char *src,
  549                           gdImagePtr im,
  550                           uint16_t       photometric,
  551                           int          startx,
  552                           int          starty,
  553                           int          width,
  554                           int          height,
  555                           char         has_alpha,
  556                           int          extra,
  557                           int          align)
  558 {
  559     int    red, green, blue, alpha;
  560     int    x, y;
  561 
  562     (void)extra;
  563     (void)align;
  564 
  565     switch (photometric) {
  566     case PHOTOMETRIC_PALETTE:
  567         /* Palette has no alpha (see TIFF specs for more details */
  568         for (y = starty; y < starty + height; y++) {
  569             for (x = startx; x < startx + width; x++) {
  570                 gdImageSetPixel(im, x, y,*(src++));
  571             }
  572         }
  573         break;
  574 
  575     case PHOTOMETRIC_RGB:
  576         if (has_alpha) {
  577             gdImageAlphaBlending(im, 0);
  578             gdImageSaveAlpha(im, 1);
  579 
  580             for (y = starty; y < starty + height; y++) {
  581                 for (x = startx; x < startx + width; x++) {
  582                     red   = *src++;
  583                     green = *src++;
  584                     blue  = *src++;
  585                     alpha = *src++;
  586                     red   = MIN (red, alpha);
  587                     blue  = MIN (blue, alpha);
  588                     green = MIN (green, alpha);
  589 
  590                     if (alpha) {
  591                         gdImageSetPixel(im, x, y, gdTrueColorAlpha(red * 255 / alpha, green * 255 / alpha, blue * 255 /alpha, gdAlphaMax - (alpha >> 1)));
  592                     } else {
  593                         gdImageSetPixel(im, x, y, gdTrueColorAlpha(red, green, blue, gdAlphaMax - (alpha >> 1)));
  594                     }
  595                 }
  596             }
  597 
  598         } else {
  599             for (y = 0; y < height; y++) {
  600                 for (x = 0; x < width; x++) {
  601                     register unsigned char r = *src++;
  602                     register unsigned char g = *src++;
  603                     register unsigned char b = *src++;
  604 
  605                     gdImageSetPixel(im, x, y, gdTrueColor(r, g, b));
  606                 }
  607             }
  608         }
  609         break;
  610 
  611     case PHOTOMETRIC_MINISWHITE:
  612         if (has_alpha) {
  613             /* We don't process the extra yet */
  614         } else {
  615             for (y = starty; y < starty + height; y++) {
  616                 for (x = startx; x < startx + width; x++) {
  617                     gdImageSetPixel(im, x, y, ~(*src++));
  618                 }
  619             }
  620         }
  621         break;
  622 
  623     case PHOTOMETRIC_MINISBLACK:
  624         if (has_alpha) {
  625             /* We don't process the extra yet */
  626         } else {
  627             for (y = starty; y < height; y++) {
  628                 for (x = 0; x < width; x++) {
  629                     gdImageSetPixel(im, x, y, *src++);
  630                 }
  631             }
  632         }
  633         break;
  634     }
  635 }
  636 
  637 static int createFromTiffTiles(TIFF *tif, gdImagePtr im, uint16_t bps, uint16_t photometric,
  638                                char has_alpha, char is_bw, int extra)
  639 {
  640     uint16_t  planar;
  641     int im_width, im_height;
  642     int tile_width, tile_height;
  643     int  x, y, height, width;
  644     unsigned char *buffer;
  645     int success = GD_SUCCESS;
  646 
  647     if (!TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar)) {
  648         planar = PLANARCONFIG_CONTIG;
  649     }
  650     if (TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &im_width) == 0 ||
  651         TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &im_height) == 0 ||
  652         TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width) ==  0 ||
  653         TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height) == 0) {
  654         return FALSE;
  655     }
  656 
  657     buffer = (unsigned char *) gdMalloc (TIFFTileSize (tif));
  658     if (!buffer) {
  659         return FALSE;
  660     }
  661 
  662     for (y = 0; y < im_height; y += tile_height) {
  663         for (x = 0; x < im_width; x += tile_width) {
  664             if (TIFFReadTile(tif, buffer, x, y, 0, 0) < 0) {
  665                 success = GD_FAILURE;
  666                 goto end;
  667             }
  668             width = MIN(im_width - x, tile_width);
  669             height = MIN(im_height - y, tile_height);
  670             if (bps == 16) {
  671             } else if (bps == 8) {
  672                 readTiff8bit(buffer, im, photometric, x, y, width, height, has_alpha, extra, 0);
  673             } else if (is_bw) {
  674                 readTiffBw(buffer, im, photometric, x, y, width, height, has_alpha, extra, 0);
  675             } else {
  676                 /* TODO: implement some default reader or detect this case earlier use force_rgb */
  677             }
  678         }
  679     }
  680 end:
  681     gdFree(buffer);
  682     return success;
  683 }
  684 
  685 static int createFromTiffLines(TIFF *tif, gdImagePtr im, uint16_t bps, uint16_t photometric,
  686                                char has_alpha, char is_bw, int extra)
  687 {
  688     uint16_t  planar;
  689     uint32_t im_height, im_width, y;
  690 
  691     unsigned char *buffer;
  692     int success = GD_SUCCESS;
  693 
  694     if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar)) {
  695         planar = PLANARCONFIG_CONTIG;
  696     }
  697 
  698     if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &im_height)) {
  699         gd_error("Can't fetch TIFF height\n");
  700         return FALSE;
  701     }
  702 
  703     if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &im_width)) {
  704         gd_error("Can't fetch TIFF width \n");
  705         return FALSE;
  706     }
  707 
  708     buffer = (unsigned char *)gdMalloc(im_width * 4);
  709     if (!buffer) {
  710         return GD_FAILURE;
  711     }
  712     if (planar == PLANARCONFIG_CONTIG) {
  713         switch (bps) {
  714         case 16:
  715             /* TODO
  716              * or simply use force_rgba
  717              */
  718             break;
  719 
  720         case 8:
  721             for (y = 0; y < im_height; y++ ) {
  722                 if (TIFFReadScanline (tif, buffer, y, 0) < 0) {
  723                     gd_error("Error while reading scanline %i", y);
  724                     success = GD_FAILURE;
  725                     break;
  726                 }
  727                 /* reading one line at a time */
  728                 readTiff8bit(buffer, im, photometric, 0, y, im_width, 1, has_alpha, extra, 0);
  729             }
  730             break;
  731 
  732         default:
  733             if (is_bw) {
  734                 for (y = 0; y < im_height; y++ ) {
  735                     if (TIFFReadScanline (tif, buffer, y, 0) < 0) {
  736                         gd_error("Error while reading scanline %i", y);
  737                         success = GD_FAILURE;
  738                         break;
  739                     }
  740                     /* reading one line at a time */
  741                     readTiffBw(buffer, im, photometric, 0, y, im_width, 1, has_alpha, extra, 0);
  742                 }
  743             } else {
  744                 /* TODO: implement some default reader or detect this case earlier > force_rgb */
  745             }
  746             break;
  747         }
  748     } else {
  749         /* TODO: implement a reader for separate panes. We detect this case earlier for now and use force_rgb */
  750     }
  751 
  752     gdFree(buffer);
  753     return success;
  754 }
  755 
  756 static int createFromTiffRgba(TIFF * tif, gdImagePtr im)
  757 {
  758     int a;
  759     int x, y;
  760     int alphaBlendingFlag = 0;
  761     int color;
  762     int width = im->sx;
  763     int height = im->sy;
  764     uint32_t *buffer;
  765     uint32_t rgba;
  766     int success;
  767 
  768     buffer = (uint32_t *) gdCalloc(sizeof(uint32_t), width * height);
  769     if (!buffer) {
  770         return GD_FAILURE;
  771     }
  772 
  773     /* switch off colour merging on target gd image just while we write out
  774      * content - we want to preserve the alpha data until the user chooses
  775      * what to do with the image */
  776     alphaBlendingFlag = im->alphaBlendingFlag;
  777     gdImageAlphaBlending(im, 0);
  778 
  779     success = TIFFReadRGBAImage(tif, width, height, buffer, 1);
  780 
  781     if (success) {
  782         for(y = 0; y < height; y++) {
  783             for(x = 0; x < width; x++) {
  784                 /* if it doesn't already exist, allocate a new colour,
  785                  * else use existing one */
  786                 rgba = buffer[(y * width + x)];
  787                 a = (0xff - TIFFGetA(rgba)) / 2;
  788                 color = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a);
  789 
  790                 /* set pixel colour to this colour */
  791                 gdImageSetPixel(im, x, height - y - 1, color);
  792             }
  793         }
  794     }
  795 
  796     gdFree(buffer);
  797 
  798     /* now reset colour merge for alpha blending routines */
  799     gdImageAlphaBlending(im, alphaBlendingFlag);
  800     return success;
  801 }
  802 
  803 /*
  804     Function: gdImageCreateFromTiffCtx
  805 
  806     Create a gdImage from a TIFF file input from an gdIOCtx.
  807 */
  808 BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile)
  809 {
  810     TIFF *tif;
  811     tiff_handle *th;
  812 
  813     uint16_t bps, spp, photometric;
  814     uint16_t orientation;
  815     int width, height;
  816     uint16_t extra, *extra_types;
  817     uint16_t planar;
  818     char    has_alpha, is_bw, is_gray;
  819     char    force_rgba = FALSE;
  820     char    save_transparent;
  821     int     image_type;
  822     int   ret;
  823     float res_float;
  824 
  825     gdImagePtr im = NULL;
  826 
  827     th = new_tiff_handle(infile);
  828     if (!th) {
  829         return NULL;
  830     }
  831 
  832     tif = TIFFClientOpen("", "rb", th, tiff_readproc,
  833                          tiff_writeproc,
  834                          tiff_seekproc,
  835                          tiff_closeproc,
  836                          tiff_sizeproc,
  837                          tiff_mapproc,
  838                          tiff_unmapproc);
  839 
  840     if (!tif) {
  841         gd_error("Cannot open TIFF image");
  842         gdFree(th);
  843         return NULL;
  844     }
  845 
  846     if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width)) {
  847         gd_error("TIFF error, Cannot read image width");
  848         goto error;
  849     }
  850 
  851     if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) {
  852         gd_error("TIFF error, Cannot read image width");
  853         goto error;
  854     }
  855 
  856     TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bps);
  857 
  858     /* Unsupported bps, force to RGBA */
  859     if (bps != 1 /*bps > 8 && bps != 16*/) {
  860         force_rgba = TRUE;
  861     }
  862 
  863     TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
  864 
  865     if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) {
  866         extra = 0;
  867     }
  868 
  869     if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photometric)) {
  870         uint16_t compression;
  871         if (TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression) &&
  872                 (compression == COMPRESSION_CCITTFAX3 ||
  873                  compression == COMPRESSION_CCITTFAX4 ||
  874                  compression == COMPRESSION_CCITTRLE ||
  875                  compression == COMPRESSION_CCITTRLEW)) {
  876             gd_error("Could not get photometric. "
  877                     "Image is CCITT compressed, assuming min-is-white");
  878             photometric = PHOTOMETRIC_MINISWHITE;
  879         } else {
  880             gd_error("Could not get photometric. "
  881                     "Assuming min-is-black");
  882 
  883             photometric = PHOTOMETRIC_MINISBLACK;
  884         }
  885     }
  886     save_transparent = FALSE;
  887 
  888     /* test if the extrasample represents an associated alpha channel... */
  889     if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) {
  890         has_alpha = TRUE;
  891         save_transparent = FALSE;
  892         --extra;
  893     } else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA)) {
  894         has_alpha = TRUE;
  895         save_transparent = TRUE;
  896         --extra;
  897     } else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED)) {
  898         /* assuming unassociated alpha if unspecified */
  899         gd_error("alpha channel type not defined, assuming alpha is not premultiplied");
  900         has_alpha = TRUE;
  901         save_transparent = TRUE;
  902         --extra;
  903     } else {
  904         has_alpha = FALSE;
  905     }
  906 
  907     if (photometric == PHOTOMETRIC_RGB && spp > 3 + extra) {
  908         has_alpha = TRUE;
  909         extra = spp - 4;
  910     } else if (photometric != PHOTOMETRIC_RGB && spp > 1 + extra) {
  911         has_alpha = TRUE;
  912         extra = spp - 2;
  913     }
  914 
  915     is_bw = FALSE;
  916     is_gray = FALSE;
  917 
  918     switch (photometric) {
  919     case PHOTOMETRIC_MINISBLACK:
  920     case PHOTOMETRIC_MINISWHITE:
  921         if (!has_alpha && bps == 1 && spp == 1) {
  922             image_type = GD_INDEXED;
  923             is_bw = TRUE;
  924         } else {
  925             image_type = GD_GRAY;
  926         }
  927         break;
  928 
  929     case PHOTOMETRIC_RGB:
  930         image_type = GD_RGB;
  931         break;
  932 
  933     case PHOTOMETRIC_PALETTE:
  934         image_type = GD_INDEXED;
  935         break;
  936 
  937     default:
  938         force_rgba = TRUE;
  939         break;
  940     }
  941 
  942     /* Force rgba if image has 1bps, but is not bw */
  943     if (bps == 1 && !is_bw) {
  944         force_rgba = TRUE;
  945     }
  946 
  947     if (!TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar)) {
  948         planar = PLANARCONFIG_CONTIG;
  949     }
  950 
  951     /* Force rgba if image plans are not contiguous */
  952     if (force_rgba || planar != PLANARCONFIG_CONTIG) {
  953         image_type = GD_RGB;
  954     }
  955 
  956     if (!force_rgba &&
  957             (image_type == GD_PALETTE || image_type == GD_INDEXED || image_type == GD_GRAY)) {
  958         im = gdImageCreate(width, height);
  959         if (!im) goto error;
  960         readTiffColorMap(im, tif, is_bw, photometric);
  961     } else {
  962         im = gdImageCreateTrueColor(width, height);
  963         if (!im) goto error;
  964     }
  965 
  966 #ifdef DEBUG
  967     printf("force rgba: %i\n", force_rgba);
  968     printf("has_alpha: %i\n", has_alpha);
  969     printf("save trans: %i\n", save_transparent);
  970     printf("is_bw: %i\n", is_bw);
  971     printf("is_gray: %i\n", is_gray);
  972     printf("type: %i\n", image_type);
  973 #else
  974     (void)is_gray;
  975     (void)save_transparent;
  976 #endif
  977 
  978     if (force_rgba) {
  979         ret = createFromTiffRgba(tif, im);
  980     } else if (TIFFIsTiled(tif)) {
  981         ret = createFromTiffTiles(tif, im, bps, photometric, has_alpha, is_bw, extra);
  982     } else {
  983         ret = createFromTiffLines(tif, im, bps, photometric, has_alpha, is_bw, extra);
  984     }
  985 
  986     if (!ret) {
  987         gdImageDestroy(im);
  988         im = NULL;
  989         goto error;
  990     }
  991 
  992     if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &res_float)) {
  993         im->res_x = (unsigned int)res_float;  //truncate
  994     }
  995     if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &res_float)) {
  996         im->res_y = (unsigned int)res_float;  //truncate
  997     }
  998 
  999     if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation)) {
 1000         switch (orientation) {
 1001         case ORIENTATION_TOPLEFT:
 1002         case ORIENTATION_TOPRIGHT:
 1003         case ORIENTATION_BOTRIGHT:
 1004         case ORIENTATION_BOTLEFT:
 1005             break;
 1006 
 1007         default:
 1008             gd_error("Orientation %d not handled yet!", orientation);
 1009             break;
 1010         }
 1011     }
 1012 error:
 1013     TIFFClose(tif);
 1014     gdFree(th);
 1015     return im;
 1016 }
 1017 
 1018 /*
 1019     Function: gdImageCreateFromTIFF
 1020 */
 1021 BGD_DECLARE(gdImagePtr) gdImageCreateFromTiff(FILE *inFile)
 1022 {
 1023     gdImagePtr im;
 1024     gdIOCtx *in = gdNewFileCtx(inFile);
 1025     if (in == NULL) return NULL;
 1026     im = gdImageCreateFromTiffCtx(in);
 1027     in->gd_free(in);
 1028     return im;
 1029 }
 1030 
 1031 /*
 1032     Function: gdImageCreateFromTiffPtr
 1033 */
 1034 BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffPtr(int size, void *data)
 1035 {
 1036     gdImagePtr im;
 1037     gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0);
 1038     if (in == NULL) return NULL;
 1039     im = gdImageCreateFromTiffCtx(in);
 1040     in->gd_free(in);
 1041     return im;
 1042 }
 1043 
 1044 /*
 1045     Function: gdImageTiff
 1046 */
 1047 BGD_DECLARE(void) gdImageTiff(gdImagePtr im, FILE *outFile)
 1048 {
 1049     gdIOCtx *out = gdNewFileCtx(outFile);
 1050     if (out == NULL) return;
 1051     gdImageTiffCtx(im, out); /* what's an fg again? */
 1052     out->gd_free(out);
 1053 }
 1054 
 1055 /*
 1056     Function: gdImageTiffPtr
 1057 */
 1058 BGD_DECLARE(void *) gdImageTiffPtr(gdImagePtr im, int *size)
 1059 {
 1060     void *rv;
 1061     gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
 1062     if (out == NULL) return NULL;
 1063     gdImageTiffCtx(im, out); /* what's an fg again? */
 1064     rv = gdDPExtractData(out, size);
 1065     out->gd_free(out);
 1066     return rv;
 1067 }
 1068 
 1069 #else
 1070 
 1071 static void _noTiffError(void)
 1072 {
 1073     gd_error("TIFF image support has been disabled\n");
 1074 }
 1075 
 1076 BGD_DECLARE(void) gdImageTiffCtx(gdImagePtr image, gdIOCtx *out)
 1077 {
 1078     ARG_NOT_USED(image);
 1079     ARG_NOT_USED(out);
 1080     _noTiffError();
 1081 }
 1082 
 1083 BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile)
 1084 {
 1085     ARG_NOT_USED(infile);
 1086     _noTiffError();
 1087     return NULL;
 1088 }
 1089 
 1090 BGD_DECLARE(gdImagePtr) gdImageCreateFromTiff(FILE *inFile)
 1091 {
 1092     ARG_NOT_USED(inFile);
 1093     _noTiffError();
 1094     return NULL;
 1095 }
 1096 
 1097 BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffPtr(int size, void *data)
 1098 {
 1099     ARG_NOT_USED(size);
 1100     ARG_NOT_USED(data);
 1101     _noTiffError();
 1102     return NULL;
 1103 }
 1104 
 1105 BGD_DECLARE(void) gdImageTiff(gdImagePtr im, FILE *outFile)
 1106 {
 1107     ARG_NOT_USED(im);
 1108     ARG_NOT_USED(outFile);
 1109     _noTiffError();
 1110 }
 1111 
 1112 BGD_DECLARE(void *) gdImageTiffPtr(gdImagePtr im, int *size)
 1113 {
 1114     ARG_NOT_USED(im);
 1115     ARG_NOT_USED(size);
 1116     _noTiffError();
 1117     return NULL;
 1118 }
 1119 
 1120 #endif