cvs: gd /libgd/src gd_tiff.c

[email protected] ("Pierre-Alain Joye") Sun, 07 Oct 2007 14:18:00 -0000
Newsgroups php.gd.cvs
Message-ID <cvspajoye1191766680@cvsserver>
pajoye		Sun Oct  7 14:18:00 2007 UTC

  Modified files:              
    /gd/libgd/src	gd_tiff.c 
  Log:
  - #121, rewamp it almost completely
   - add support for 1..8bit, 24 and 32bit pixels
   - replace black&white and gray support
   - add default rgba import for unknown/unsupported format
   - detect which mode is better to be used (save us double or triple alloc
     of a complete image buffer)
   - fix alpha support (extra alpha in extra planes are not supported)
pajoye-20071007141800.txt (text/plain, 28.4 KB)
http://cvs.php.net/viewvc.cgi/gd/libgd/src/gd_tiff.c?r1=1.9&r2=1.10&diff_format=u
Index: gd/libgd/src/gd_tiff.c
diff -u gd/libgd/src/gd_tiff.c:1.9 gd/libgd/src/gd_tiff.c:1.10
--- gd/libgd/src/gd_tiff.c:1.9	Sat Oct  6 10:52:09 2007
+++ gd/libgd/src/gd_tiff.c	Sun Oct  7 14:18:00 2007
@@ -29,7 +29,7 @@
 
    ----------------------------------------------------------------------------
  */
-/* $Id: gd_tiff.c,v 1.9 2007/10/06 10:52:09 nlopess Exp $ */
+/* $Id: gd_tiff.c,v 1.10 2007/10/07 14:18:00 pajoye Exp $ */
 
 #ifdef HAVE_CONFIG_H
 	#include "config.h"
@@ -40,13 +40,32 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
-#include <math.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;
@@ -68,7 +87,7 @@
          tiff (assuming one already exists)
 */
 
-static tiff_handle *new_tiff_handle(tiff_handle *t, gdIOCtx *g)
+tiff_handle *new_tiff_handle(tiff_handle *t, gdIOCtx *g)
 {
 	t = (tiff_handle *) gdMalloc(sizeof(tiff_handle));
 	if(!t || !g) {
@@ -85,7 +104,7 @@
 
 /* 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)
+tsize_t tiff_readproc(thandle_t clientdata, tdata_t data, tsize_t size)
 {
 	tiff_handle *th = (tiff_handle *)clientdata;
 	gdIOCtx *ctx = th->ctx;
@@ -97,7 +116,7 @@
 
 /* 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)
+tsize_t tiff_writeproc(thandle_t clientdata, tdata_t data, tsize_t size)
 {
 	tiff_handle *th = (tiff_handle *)clientdata;
 	gdIOCtx *ctx = th->ctx;
@@ -113,7 +132,7 @@
 
 /* TIFFSeekProc tiff_seekproc
  * used to move around the partially written TIFF */
-static toff_t tiff_seekproc(thandle_t clientdata, toff_t offset, int from)
+toff_t tiff_seekproc(thandle_t clientdata, toff_t offset, int from)
 {
 	tiff_handle *th = (tiff_handle *)clientdata;
 	gdIOCtx *ctx = th->ctx;
@@ -147,7 +166,7 @@
 }
 
 /* TIFFCloseProc tiff_closeproc - used to finally close the TIFF file */
-static int tiff_closeproc(thandle_t clientdata)
+int tiff_closeproc(thandle_t clientdata)
 {
 	tiff_handle *th = (tiff_handle *)clientdata;
 	gdIOCtx *ctx = th->ctx;
@@ -158,20 +177,20 @@
 }
 
 /* TIFFSizeProc tiff_sizeproc */
-static toff_t tiff_sizeproc(thandle_t clientdata)
+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)
+int tiff_mapproc(thandle_t h, tdata_t *d, toff_t *o)
 {
 	return 0;
 }
 
 /* TIFFUnmapFileProc tiff_unmapproc */
-static void tiff_unmapproc(thandle_t h, tdata_t d, toff_t o)
+void tiff_unmapproc(thandle_t h, tdata_t d, toff_t o)
 {
 
 }
@@ -191,8 +210,7 @@
 	int i;
 	int r, g, b, a;
 	TIFF *tiff;
-	int width;
-	int height;
+	int width, height;
 	int colour;
 	char *scan;
 	int rowsperstrip;
@@ -249,15 +267,24 @@
 
 	/* build the color map for 8 bit images */
 	if(bitDepth != 24) {
-		/* no overflow check is necessary because bitsPerSample is at most 8 and the following doesn't overflow */
-		colorMapRed = (uint16 *) gdMalloc(3 * (1 << bitsPerSample));
+		/*TODO: Add checking */
+		colorMapRed   = (uint16 *) gdMalloc(3 * (1 << bitsPerSample));
+		if (!colorMapRed) {
+			return;
+		}
 		colorMapGreen = (uint16 *) gdMalloc(3 * (1 << bitsPerSample));
-		colorMapBlue = (uint16 *) gdMalloc(3 * (1 << bitsPerSample));
+		if (!colorMapGreen) {
+			return;
+		}
+		colorMapBlue  = (uint16 *) gdMalloc(3 *  (1 << bitsPerSample));
+		if (!colorMapBlue) {
+			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);
+			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,
@@ -344,12 +371,13 @@
 	}
 }
 
-/*  gdImageTiffCtx
-**  --------------
-**  Write the gd image as a tiff file
-**  Parameters are:
-**  image:  gd image structure;
-**  out:    the stream where to write
+/*  
+  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)
 {
@@ -374,398 +402,353 @@
 	gdImageSetClip(image, clipx1P, clipy1P, clipx2P, clipy2P);
 }
 
-
-/* readColorMap
-** ------------
-** Used by tiff reading routines to generate a map between color indices
-** and their actual RGB color values.
-** return value is a chunk of memory containing 
- */
-
-typedef struct
+/* Check if we are really in 8bit mode */
+static int checkColorMap(n, r, g, b)
+	int n;
+	uint16 *r, *g, *b;
 {
-	uint16 *red;
-	uint16 *green;
-	uint16 *blue;
+	while (n-- > 0)
+		if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
+			return (16);
+	return (8);
 }
-RgbContext;
 
-static void readColorMap(TIFF *tiff, RgbContext ctx)
-{
-	uint32 dataSize;
 
-	TIFFGetField(tiff, TIFFTAG_COLORMAP, &ctx.red, &ctx.green, &ctx.blue);
-}
-
-/* getRed, getGreen, getBlue
-** -------------------------
-** gets the red value for specified index from specified color map data 
-** from source tiff file 
- */
-BGD_DECLARE(uint16) getColor(TIFF *tiff, RgbContext ctx, int index, char color)
+/* Read and convert a TIFF colormap */
+static int readTiffColorMap(gdImagePtr im, TIFF *tif, char is_bw, int photometric)
 {
-	uint16 bitsPerSample;
-	uint16 val;
-	uint32 offset;
+	uint16 *redcmap, *greencmap, *bluecmap;
+	uint16 bps;
+	int i;
 
-	TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
-	offset = 1 << bitsPerSample;
+	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(color == 0) {
-		val = ctx.red[index];
-	}
-	if(color == 1) {
-		val = ctx.green[index];
-	}
-	if(color == 2) {
-		val = ctx.blue[index];
-	}
+		if (!TIFFGetField(tif, TIFFTAG_MINSAMPLEVALUE, &min_sample_val)) {
+			min_sample_val = 0;
+		}
+		if (!TIFFGetField(tif, TIFFTAG_MAXSAMPLEVALUE, &max_sample_val)) {
+			max_sample_val = 255;
+		}
 
-	val = (val >> 8);
+		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;
 
-	return val;
-}
+		} else if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap)) {
+			fprintf(stderr, "Cannot read the color map");
+			return GD_FAILURE;
+		}
 
-#define getRed(t,c,i) getColor(t,c,i,0)
-#define getGreen(t,c,i) getColor(t,c,i,1)
-#define getBlue(t,c,i) getColor(t,c,i,2)
+		TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
 
-/* createFromTiffCtx1bit
-** ------------------------
-** Create a gdImage from a TIFF file input from an gdIOCtx
-** called by gdImageCreateFromTiffCtx() when bitsPerPixel==1
- */
-BGD_DECLARE(gdImagePtr) createFromTiffCtx1bit(TIFF *tiff, int width, int height)
-{
-	gdImagePtr im = NULL;
-	int x, y;
-	int tileX, tileY, tileMaxX, tileMaxY;
-	int i, j, k;
-	int colour;
-	char *scan;
-	int compression;
-	ttile_t numberOfTiles = 0;
-	int tileNum;
-	uint32 black, white;
-	unsigned long long *bitmapAsVal;
-	unsigned long long bitToTest;
-	unsigned long long bitTestResult;
-	unsigned char byteVal;
-	unsigned char byteBitToTest;
-	unsigned char byteBitTestResult;
-	uint32 tileSize;
-	RgbContext ctx;
-
-	TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression);
- 
-	if(!(im = gdImageCreate(width, height))) {
-		TIFFClose(tiff);
-		return NULL;
+#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;
+}
 
-	/* allocate both black and white for this image */
-	white = gdImageColorAllocate(im, 0xff, 0xff, 0xff); 
-	black = gdImageColorAllocate(im, 0x00, 0x00, 0x00);
-
-	numberOfTiles = TIFFNumberOfTiles(tiff);
-
-	if(TIFFIsTiled(tiff)) {
-		TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileMaxX);
-		TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tileMaxY);
+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)
+{
+	unsigned char *buffer;
+	int x = startx, y = starty;
+	int src_x, src_y;
+	int k;
+
+	for (src_y = 0; src_y < height; ++src_y) {
+		k = 0;
+		while (k < width) {
+			register unsigned char curr = *src++;
+			register unsigned char mask;
 
-		tileSize = TIFFTileSize(tiff);
-		if(!(scan = (char *) gdMalloc(tileSize))) {
-			return 0;
+			if (photometric == PHOTOMETRIC_MINISWHITE) {
+				curr = ~curr;
+			}
+			for (mask = 0x80; mask != 0 && k < width; mask >>= 1){
+				if((curr & mask) != 0) {
+					gdImageSetPixel(im, x, y, 0);
+				} else {
+					gdImageSetPixel(im, x, y, 1);
+				}
+				++x;
+				++k;
+			}
+			++src_x;
 		}
+		y++;
+	}
+}
 
-		tileX = tileY = 0;
-
-		for(tileNum = 0; tileNum < numberOfTiles; tileNum++) {
-			TIFFReadEncodedTile(tiff, tileNum, scan, tileSize); 
-
-			/* setup coords for this pixel */
-			x = tileX;
-			y = tileY;
-
-			for(i=0; i < tileSize; i += sizeof(unsigned long long)) {
-				/* check if the next sizeof(unsigned long long) bytes are zero,
-				 * if they are, move along */
-				bitmapAsVal = (unsigned long long *)&scan[i];
-				if(bitmapAsVal[0] != 0ull) {
-					/* if this chunk has some black bits, write them out,
-					 * one bit at a time */
-					bitToTest = 1ull;
-
-					for(j = 0; j < sizeof(unsigned long long) * 8; j++) {
-						bitTestResult = bitmapAsVal[0] & bitToTest;
-
-						/* if the bit at offset j is white, skip it */
-						if(bitTestResult != 0ull) {
-							gdImageSetPixel(im, x, y, black);
-						}
+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)
+{
+	unsigned char *dst;
+	int    gray, red, green, blue, alpha;
+	int    x, y, i;
+
+	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;
 
-						/* move onto next pixel in tile's gd target area */
-						x++;
-						if(x >= (tileX + tileMaxX)) {
-							x = tileX;
-							y++;
+		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)));
 						}
-
-						bitToTest *= 2;
 					}
-				} else {
-					/* entire section was blank - move x on by section length */
-					x += sizeof(unsigned long long) * 8;
-					while(x >= (tileX + tileMaxX)) {
-						x -= tileMaxX;
-						y++;
+				}
+
+			} else {
+				for (y = 0; y < height; y++) {
+					for (x = 0; x < width; x++) {
+						gdImageSetPixel(im, x, y, gdTrueColor(*src++, *src++, *src++));
 					}
 				}
 			}
-      
-			/* move to the next tile offset */
-			tileX += tileMaxX;
-			if(tileX > width) {
-				tileX = 0;
-				tileY += tileMaxY;
-			}
-		}
-
-		gdFree(scan);
+			break;
 
-	} else {
-		/* image is not tiled - assume stripped (scanline) format */
-		printf("is a 1bit scanline tiff!\n");
-
-		if(!(scan = (char *) gdMalloc(TIFFScanlineSize(tiff)))) {
-			return 0;
-		}
-
-		y = x = 0;
-
-		for(i = 0; i < height; i++) {
-			/* looping on number of scanlines - read source scanline */
-			TIFFReadScanline(tiff, scan, i, 0);
-
-			for(j = 0; j < TIFFScanlineSize(tiff); j++) {
-				/* now, loop on content of scanline */
-				byteVal = scan[j];
-				byteBitToTest = 0x01;
-
-				if(byteVal == 0) {
-					x += 8;
-					continue;
+		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;
 
-				/* now, loop through this byte, setting all bits that are not
-				 * the background color */
-				for(k = 0; k < 8; k++) {
-					byteBitTestResult = (byteVal & byteBitToTest);
-
-					if(byteBitTestResult) {
-						gdImageSetPixel(im, x, y, black);
+		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++);
 					}
-
-					x++;
-					byteBitToTest *= 2;
 				}
 			}
-
-			y++;
-			x = 0;
-		}
-
-		gdFree(scan);
+			break;
 	}
-
-	return im;
 }
 
-/* createFromTiffCtx8bit
-** ------------------------
-** Create a gdImage from a TIFF file input from an gdIOCtx
-** called by gdImageCreateFromTiffCtx() when bitsPerPixel==8
- */
-BGD_DECLARE(gdImagePtr) createFromTiffCtx8bit(TIFF *tiff, int width, int height)
+static int createFromTiffTiles(TIFF *tif, gdImagePtr im, uint16 bps, uint16 photometric,
+		char has_alpha, char is_bw, int extra)
 {
-	gdImagePtr im = NULL;
-	int x, y;
-	int i, j;
-	int colorIndex;
-	int colour;
-	char *scan;
-	uint32 numberOfTiles = 0;
-	uint32 tileSize;
-	int tileX, tileY;
-	int tileMaxX, tileMaxY;
-	int tileNum;
-	uint32 color;
-	char *colorMap;
-	RgbContext ctx;
-
-	if(!(im = gdImageCreate(width, height))) {
-		TIFFClose(tiff);
-		return NULL;
+	uint16  planar;
+	int im_width, im_height;
+	int tile_width, tile_height;
+	int  x, y, height, width;
+	unsigned char *buffer;
+	double one_row;
+	int    i;
+
+	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);
+
+	one_row = (double) tile_height / (double) im_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 */
+			}
+		}
 	}
+}
 
-	readColorMap(tiff, ctx);
-
-	/* set the background of the image to the first color in the color map */
-	gdImageColorAllocate(im, getRed(tiff, ctx, 0),
-							getGreen(tiff, ctx, 0),
-							getBlue(tiff, ctx, 0));
-
-	numberOfTiles = TIFFNumberOfTiles(tiff);
-
-	if(TIFFIsTiled(tiff)) {
-		printf("is a 8bit tiled tiff! (%d tiles)\n",numberOfTiles);
-		TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileMaxX);
-		TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tileMaxY);
-
-		tileSize = TIFFTileSize(tiff);
-		if(!(scan = (char*) gdMalloc(tileSize))) {
-			return 0;
-		}
-
-		tileX = tileY = 0;
-
-		for(tileNum = 0; tileNum < numberOfTiles; tileNum++) {
-			TIFFReadEncodedTile(tiff, tileNum, scan, tileSize);
-
-			/* setup coords for this pixel */
-			x = tileX;
-			y = tileY;
-
-			for(i = 0; i < tileSize; i++) {
-				/* check if the next byte is zero */
-				/* if it is - background already set so move along */
-				colorIndex = scan[i];
-				if(colorIndex != 0x0) {
-					color = gdImageColorResolve(im,
-											getRed(tiff, ctx, colorIndex),
-											getGreen(tiff, ctx, colorIndex),
-											getBlue(tiff, ctx, colorIndex));
-
-					gdImageSetPixel(im, x, y, color);
-
-					/* move onto next pixel in tile's gd target area */
-					x++;
-					if(x >= (tileX + tileMaxX)) {
-						x = tileX;
-						y++;
-					}
-				} else {
-					/* move x on to next pixel offset */
-					x++;
-					if(x >= (tileX + tileMaxX)) {
-						x = tileMaxX;
-						y++;
-					}
-				}
-			}
+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, im_line_size;
+	int y, i;
 
-			/* move to the next tile offset */
-			tileX += tileMaxX;
-			if(tileX > width) {
-				tileX = 0;
-				tileY += tileMaxY;
-			}
-		}
+	unsigned char *buffer;
 
-		gdFree(scan);
+	if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar)) {
+		planar = PLANARCONFIG_CONTIG;
+	}
 
-	} else {
-		if(!(scan = (char *)gdMalloc(TIFFScanlineSize(tiff)))) {
-			return 0;
-		}
+	if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &im_height)) {
+		fprintf(stderr, "Can't fetch TIFF height");
+		return FALSE;
+	}
 
-		y = x = 0;
+	if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &im_width)) {
+		fprintf(stderr, "Can't fetch TUFF width", y);
+		return FALSE;
+	}
 
-		/* image is not tiled - assume scanline based images */
-		for(i = 0; i < height; i++) {
-			/* looping on number of scanlines - read source scanline */
-			TIFFReadScanline(tiff, scan, i, 0);
+	im_line_size = TIFFScanlineSize(tif);
 
-			for(j = 0; j < TIFFScanlineSize(tiff); j++) {
-				colorIndex = scan[j];
-				color = gdImageColorResolve(im, getRed(tiff, ctx, colorIndex),
-												getGreen(tiff, ctx, colorIndex),
-												getBlue(tiff, ctx, colorIndex));
+	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;
 
-				/*printf("%2x  %2x %2x %2x\n",colorIndex,getRed(tiff,colorIndex),getGreen(tiff,colorIndex),getBlue(tiff,colorIndex));*/
+			case 8:
+					for (y = 0; y < im_height; y++ ) {
+						if (!TIFFReadScanline (tif, buffer, y, 0)) {
+							fprintf(stderr, "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;
 
-				gdImageSetPixel(im,x,y,color);
-				x++;
-			}
-			
-			y++;
-			x = 0;
+			default:
+				if (is_bw) {
+					for (y = 0; y < im_height; y++ ) {
+						if (!TIFFReadScanline (tif, buffer, y, 0)) {
+							fprintf(stderr, "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;
 		}
-
-		gdFree(scan);
+	} else {
+			/* TODO: implement a reader for separate panes. We detect this case earlier for now and use force_rgb */
 	}
 
-	return im;
+error:
+	gdFree(buffer);
+	return GD_SUCCESS;
 }
 
-/* createFromTiffCtx32bit
-** ------------------------
-** Create a 32 bit gdImage from a TIFF file input from an gdIOCtx
-** - called by gdImageCreateFromTiffCtx() when bitsPerPixel>8bit
-** This is essentially a fall-back routine; it's very slow but will 
-** handle any format of tiff that libtiff supports.
- */
-BGD_DECLARE(gdImagePtr) createFromTiffCtx32bit(TIFF *tiff, int width,int height)
+static int createFromTiffRgba(TIFF * tif, gdImagePtr im) 
 {
-	gdImagePtr im = NULL;
 	int r, g, b, a;
 	int x, y;
 	int alphaBlendingFlag = 0;
 	int colour;
-	uint32 *scan;
+	int width = im->sx;
+	int height = im->sy;
+	uint32 *buffer;
 	uint32 rgba;
 
-	printf("32 bits\n");
-
-	if(!(im = gdImageCreateTrueColor(width, height))) {
-		TIFFClose(tiff);
-		return NULL;
-	}
-
 	/* 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);
 
-	/* loop through y-coords, and x-coords */
-	scan = calloc(sizeof(uint32), width * height);
-	TIFFReadRGBAImage(tiff, width, height, scan, 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 = scan[((y * width) + x)];
+			rgba = buffer[(y * width + x)];
 			a = (0xff - TIFFGetA(rgba)) / 2;
-
-			b = TIFFGetB(rgba);
-			g = TIFFGetG(rgba);
-			r = TIFFGetR(rgba);
-
-			colour = gdTrueColorAlpha(r, g, b, a);
+			colour = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a);
 
 			/* set pixel colour to this colour */
 			gdImageSetPixel(im, x, height - y - 1, colour);
 		}
 	}
 
-	gdFree(scan);
+	gdFree(buffer);
 
 	/* now reset colour merge for alpha blending routines */
 	gdImageAlphaBlending(im, alphaBlendingFlag);
-
-	return im;
+	return GD_SUCCESS;
 }
 
 /* gdImageCreateFromTiffCtx
@@ -774,19 +757,27 @@
  */
 BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile)
 {
-	int width;
-	int height;
-	int compression;
-	uint16 samplesPerPixel;
-	uint16 bitsPerSample[4];
-	int bitsPerPixel;
-	TIFF *tiff;
-	gdImagePtr im;
-
+	TIFF *tif;
 	tiff_handle *th;
-	th = new_tiff_handle(th, infile);
 
-	tiff = TIFFClientOpen("", "rb", th, tiff_readproc,
+	uint16 bps, spp, photometric;
+	uint16 orientation;
+	int width, height;
+	uint16 extra, *extra_types;
+	uint16 planar;
+	float layer_offset_y_float, layer_offset_x_float;
+	int		layer_offset_y, layer_offset_x;
+	char	has_alpha, is_bw, is_gray;
+	char	flip_horizontal, flip_vertical;
+	char	force_rgba = FALSE;
+	char	save_transparent;
+	int		image_type;
+	int   ret;
+
+	gdImagePtr im = NULL;
+
+	th = new_tiff_handle(th, infile);
+	tif = TIFFClientOpen("", "rb", th, tiff_readproc,
 										tiff_writeproc,
 										tiff_seekproc,
 										tiff_closeproc,
@@ -794,24 +785,179 @@
 										tiff_mapproc,
 										tiff_unmapproc);
 
+	if (!tif) {
+		fprintf(stderr, "Cannot open TIFF image");
+		return NULL;
+	}
 
-	TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
-	TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
-	TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, bitsPerSample);
-	TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
-	bitsPerPixel = bitsPerSample[0] * samplesPerPixel;
-
-	if(bitsPerPixel == 1) {
-		im = createFromTiffCtx1bit(tiff, width, height);
-	} else if(bitsPerPixel == 8) {
-		im = createFromTiffCtx8bit(tiff, width, height);
+	if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width)) {
+		fprintf(stderr, "TIFF error, Cannot read image width");
+		goto error;
+	}
+
+	if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) {
+		fprintf(stderr, "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))
+		{
+			fprintf(stderr, "Could not get photometric. "
+					"Image is CCITT compressed, assuming min-is-white");
+			photometric = PHOTOMETRIC_MINISWHITE;
+		} else {
+			fprintf(stderr, "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 */
+		fprintf(stderr, "alpha channel type not defined, assuming alpha is not premultiplied");
+		has_alpha = TRUE;
+		save_transparent = TRUE;
+		--extra;
 	} else {
-		im = createFromTiffCtx32bit(tiff, width, height);
+		has_alpha = FALSE;
 	}
 
-	TIFFClose(tiff);
-	gdFree(th);
+	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);
+		readTiffColorMap(im, tif, is_bw, photometric);
+	} else {
+		im = gdImageCreateTrueColor(width, height);
+	}
+
+#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);
+#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:
+				flip_horizontal = FALSE;
+				flip_vertical   = FALSE;
+				break;
+
+			case ORIENTATION_TOPRIGHT:
+				flip_horizontal = TRUE;
+				flip_vertical   = FALSE;
+				break;
+
+			case ORIENTATION_BOTRIGHT:
+				flip_horizontal = TRUE;
+				flip_vertical   = TRUE;
+				break;
+
+			case ORIENTATION_BOTLEFT:
+				flip_horizontal = FALSE;
+				flip_vertical   = TRUE;
+				break;
+
+			default:
+				flip_horizontal = FALSE;
+				flip_vertical   = FALSE;
+				fprintf (stderr, "Orientation %d not handled yet!", orientation);
+				break;
+		}
+	}
+error:
+	TIFFClose(tif);
 	return im;
 }