]> git.saurik.com Git - wxWidgets.git/blobdiff - src/tiff/tif_getimage.c
i18n files are installed as part of wxBase (and should be ideally part of separate...
[wxWidgets.git] / src / tiff / tif_getimage.c
index ed081d65071cc327a69540d892ef24c101970a06..f69fbc17aae896dec77bab44940382cba49905e7 100644 (file)
@@ -42,6 +42,28 @@ static       int pickTileSeparateCase(TIFFRGBAImage*);
 
 static const char photoTag[] = "PhotometricInterpretation";
 
+/* 
+ * Helper constants used in Orientation tag handling
+ */
+#define FLIP_VERTICALLY 0x01
+#define FLIP_HORIZONTALLY 0x02
+
+/*
+ * Color conversion constants. We will define display types here.
+ */
+
+TIFFDisplay display_sRGB = {
+       {                       /* XYZ -> luminance matrix */
+               {  3.2410F, -1.5374F, -0.4986F },
+               {  -0.9692F, 1.8760F, 0.0416F },
+               {  0.0556F, -0.2040F, 1.0570F }
+       },      
+       100.0F, 100.0F, 100.0F, /* Light o/p for reference white */
+       255, 255, 255,          /* Pixel values for ref. white */
+       1.0F, 1.0F, 1.0F,       /* Residual light o/p for black pixel */
+       2.4F, 2.4F, 2.4F,       /* Gamma values for the three guns */
+};
+
 /*
  * Check the image to see if TIFFReadRGBAImage can deal with it.
  * 1/0 is returned according to whether or not the image can
@@ -55,6 +77,10 @@ TIFFRGBAImageOK(TIFF* tif, char emsg[1024])
     uint16 photometric;
     int colorchannels;
 
+    if (!tif->tif_decodestatus) {
+       sprintf(emsg, "Sorry, requested compression method is not configured");
+       return (0);
+    }
     switch (td->td_bitspersample) {
     case 1: case 2: case 4:
     case 8: case 16:
@@ -82,13 +108,22 @@ TIFFRGBAImageOK(TIFF* tif, char emsg[1024])
     case PHOTOMETRIC_MINISWHITE:
     case PHOTOMETRIC_MINISBLACK:
     case PHOTOMETRIC_PALETTE:
-       if (td->td_planarconfig == PLANARCONFIG_CONTIG && td->td_samplesperpixel != 1) {
+       if (td->td_planarconfig == PLANARCONFIG_CONTIG 
+            && td->td_samplesperpixel != 1
+            && td->td_bitspersample < 8 ) {
            sprintf(emsg,
-               "Sorry, can not handle contiguous data with %s=%d, and %s=%d",
-               photoTag, photometric,
-               "Samples/pixel", td->td_samplesperpixel);
+                    "Sorry, can not handle contiguous data with %s=%d, "
+                    "and %s=%d and Bits/Sample=%d",
+                    photoTag, photometric,
+                    "Samples/pixel", td->td_samplesperpixel,
+                    td->td_bitspersample);
            return (0);
        }
+        /*
+        ** We should likely validate that any extra samples are either
+        ** to be ignored, or are alpha, and if alpha we should try to use
+        ** them.  But for now we won't bother with this. 
+        */
        break;
     case PHOTOMETRIC_YCBCR:
        if (td->td_planarconfig != PLANARCONFIG_CONTIG) {
@@ -104,20 +139,18 @@ TIFFRGBAImageOK(TIFF* tif, char emsg[1024])
            return (0);
        }
        break;
-#ifdef CMYK_SUPPORT
     case PHOTOMETRIC_SEPARATED:
        if (td->td_inkset != INKSET_CMYK) {
            sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
                "InkSet", td->td_inkset);
            return (0);
        }
-       if (td->td_samplesperpixel != 4) {
+       if (td->td_samplesperpixel < 4) {
            sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
                "Samples/pixel", td->td_samplesperpixel);
            return (0);
        }
        break;
-#endif
     case PHOTOMETRIC_LOGL:
        if (td->td_compression != COMPRESSION_SGILOG) {
            sprintf(emsg, "Sorry, LogL data must have %s=%d",
@@ -138,6 +171,8 @@ TIFFRGBAImageOK(TIFF* tif, char emsg[1024])
            return (0);
        }
        break;
+    case PHOTOMETRIC_CIELAB:
+       break;
     default:
        sprintf(emsg, "Sorry, can not handle image with %s=%d",
            photoTag, photometric);
@@ -149,20 +184,22 @@ TIFFRGBAImageOK(TIFF* tif, char emsg[1024])
 void
 TIFFRGBAImageEnd(TIFFRGBAImage* img)
 {
-    if (img->Map)
-       _TIFFfree(img->Map), img->Map = NULL;
-    if (img->BWmap)
-       _TIFFfree(img->BWmap), img->BWmap = NULL;
-    if (img->PALmap)
-       _TIFFfree(img->PALmap), img->PALmap = NULL;
-    if (img->ycbcr)
-       _TIFFfree(img->ycbcr), img->ycbcr = NULL;
-
-    if( img->redcmap ) {
-        _TIFFfree( img->redcmap );
-        _TIFFfree( img->greencmap );
-        _TIFFfree( img->bluecmap );
-    }
+       if (img->Map)
+               _TIFFfree(img->Map), img->Map = NULL;
+       if (img->BWmap)
+               _TIFFfree(img->BWmap), img->BWmap = NULL;
+       if (img->PALmap)
+               _TIFFfree(img->PALmap), img->PALmap = NULL;
+       if (img->ycbcr)
+               _TIFFfree(img->ycbcr), img->ycbcr = NULL;
+       if (img->cielab)
+               _TIFFfree(img->cielab), img->cielab = NULL;
+
+       if( img->redcmap ) {
+               _TIFFfree( img->redcmap );
+               _TIFFfree( img->greencmap );
+               _TIFFfree( img->bluecmap );
+       }
 }
 
 static int
@@ -193,6 +230,7 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
     img->redcmap = NULL;
     img->greencmap = NULL;
     img->bluecmap = NULL;
+    img->req_orientation = ORIENTATION_BOTLEFT;            /* It is the default */
     
     img->tif = tif;
     img->stoponerr = stop;
@@ -202,7 +240,7 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
     case 8: case 16:
        break;
     default:
-       sprintf(emsg, "Sorry, can not image with %d-bit samples",
+       sprintf(emsg, "Sorry, can not handle images with %d-bit samples",
            img->bitspersample);
        return (0);
     }
@@ -211,12 +249,32 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
     TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
        &extrasamples, &sampleinfo);
     if (extrasamples == 1)
+    {
        switch (sampleinfo[0]) {
+       case EXTRASAMPLE_UNSPECIFIED:   /* Workaround for some images without */
+               if (img->samplesperpixel == 4)  /* correct info about alpha channel */
+                       img->alpha = EXTRASAMPLE_ASSOCALPHA;
+               break;
        case EXTRASAMPLE_ASSOCALPHA:    /* data is pre-multiplied */
        case EXTRASAMPLE_UNASSALPHA:    /* data is not pre-multiplied */
-           img->alpha = sampleinfo[0];
-           break;
+               img->alpha = sampleinfo[0];
+               break;
        }
+    }
+
+#if DEFAULT_EXTRASAMPLE_AS_ALPHA == 1
+    if( !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric))
+        img->photometric = PHOTOMETRIC_MINISWHITE;
+
+    if( extrasamples == 0 
+        && img->samplesperpixel == 4 
+        && img->photometric == PHOTOMETRIC_RGB )
+    {
+        img->alpha = EXTRASAMPLE_ASSOCALPHA;
+        extrasamples = 1;
+    }
+#endif
+
     colorchannels = img->samplesperpixel - extrasamples;
     TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress);
     TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig);
@@ -261,11 +319,15 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
        /* fall thru... */
     case PHOTOMETRIC_MINISWHITE:
     case PHOTOMETRIC_MINISBLACK:
-       if (planarconfig == PLANARCONFIG_CONTIG && img->samplesperpixel != 1) {
+       if (planarconfig == PLANARCONFIG_CONTIG 
+            && img->samplesperpixel != 1
+            && img->bitspersample < 8 ) {
            sprintf(emsg,
-               "Sorry, can not handle contiguous data with %s=%d, and %s=%d",
-               photoTag, img->photometric,
-               "Samples/pixel", img->samplesperpixel);
+                    "Sorry, can not handle contiguous data with %s=%d, "
+                    "and %s=%d and Bits/Sample=%d",
+                    photoTag, img->photometric,
+                    "Samples/pixel", img->samplesperpixel,
+                    img->bitspersample);
            return (0);
        }
        break;
@@ -276,12 +338,20 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
            return (0);
        }
        /* It would probably be nice to have a reality check here. */
-       if (compress == COMPRESSION_JPEG && planarconfig == PLANARCONFIG_CONTIG) {
+       if (planarconfig == PLANARCONFIG_CONTIG)
            /* can rely on libjpeg to convert to RGB */
            /* XXX should restore current state on exit */
-           TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
-           img->photometric = PHOTOMETRIC_RGB;
-       }
+           switch (compress) {
+               case COMPRESSION_OJPEG:
+               case COMPRESSION_JPEG:
+                   TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
+                   img->photometric = PHOTOMETRIC_RGB;
+                    break;
+
+                default:
+                    /* do nothing */;
+                    break;
+           }
        break;
     case PHOTOMETRIC_RGB: 
        if (colorchannels < 3) {
@@ -298,7 +368,7 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
                "InkSet", inkset);
            return (0);
        }
-       if (img->samplesperpixel != 4) {
+       if (img->samplesperpixel < 4) {
            sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
                "Samples/pixel", img->samplesperpixel);
            return (0);
@@ -330,6 +400,8 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
        img->photometric = PHOTOMETRIC_RGB;             /* little white lie */
        img->bitspersample = 8;
        break;
+    case PHOTOMETRIC_CIELAB:
+       break;
     default:
        sprintf(emsg, "Sorry, can not handle image with %s=%d",
            photoTag, img->photometric);
@@ -339,6 +411,7 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
     img->BWmap = NULL;
     img->PALmap = NULL;
     img->ycbcr = NULL;
+    img->cielab = NULL;
     TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width);
     TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height);
     TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation);
@@ -346,12 +419,11 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
        !(planarconfig == PLANARCONFIG_SEPARATE && colorchannels > 1);
     if (img->isContig) {
        img->get = TIFFIsTiled(tif) ? gtTileContig : gtStripContig;
-       (void) pickTileContigCase(img);
+       return pickTileContigCase(img);
     } else {
        img->get = TIFFIsTiled(tif) ? gtTileSeparate : gtStripSeparate;
-       (void) pickTileSeparateCase(img);
+       return pickTileSeparateCase(img);
     }
-    return (1);
 }
 
 int
@@ -370,17 +442,21 @@ TIFFRGBAImageGet(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
 }
 
 /*
- * Read the specified image into an ABGR-format raster.
+ * Read the specified image into an ABGR-format rastertaking in account
+ * specified orientation.
  */
 int
-TIFFReadRGBAImage(TIFF* tif,
-    uint32 rwidth, uint32 rheight, uint32* raster, int stop)
+TIFFReadRGBAImageOriented(TIFF* tif,
+                         uint32 rwidth, uint32 rheight, uint32* raster,
+                         int orientation, int stop)
 {
     char emsg[1024];
     TIFFRGBAImage img;
     int ok;
 
-    if (TIFFRGBAImageBegin(&img, tif, stop, emsg)) {
+    if (TIFFRGBAImageOK(tif, emsg) &&
+       TIFFRGBAImageBegin(&img, tif, stop, emsg)) {
+       img.req_orientation = orientation;
        /* XXX verify rwidth and rheight against width and height */
        ok = TIFFRGBAImageGet(&img, raster+(rheight-img.height)*rwidth,
            rwidth, img.height);
@@ -392,34 +468,77 @@ TIFFReadRGBAImage(TIFF* tif,
     return (ok);
 }
 
-static uint32
-setorientation(TIFFRGBAImage* img, uint32 h)
+/*
+ * Read the specified image into an ABGR-format raster. Use bottom left
+ * origin for raster by default.
+ */
+int
+TIFFReadRGBAImage(TIFF* tif,
+                 uint32 rwidth, uint32 rheight, uint32* raster, int stop)
 {
-    TIFF* tif = img->tif;
-    uint32 y;
-
-    switch (img->orientation) {
-    case ORIENTATION_BOTRIGHT:
-    case ORIENTATION_RIGHTBOT: /* XXX */
-    case ORIENTATION_LEFTBOT:  /* XXX */
-       TIFFWarning(TIFFFileName(tif), "using bottom-left orientation");
-       img->orientation = ORIENTATION_BOTLEFT;
-       /* fall thru... */
-    case ORIENTATION_BOTLEFT:
-       y = 0;
-       break;
-    case ORIENTATION_TOPRIGHT:
-    case ORIENTATION_RIGHTTOP: /* XXX */
-    case ORIENTATION_LEFTTOP:  /* XXX */
-    default:
-       TIFFWarning(TIFFFileName(tif), "using top-left orientation");
-       img->orientation = ORIENTATION_TOPLEFT;
-       /* fall thru... */
-    case ORIENTATION_TOPLEFT:
-       y = h-1;
-       break;
-    }
-    return (y);
+       return TIFFReadRGBAImageOriented(tif, rwidth, rheight, raster,
+                                        ORIENTATION_BOTLEFT, stop);
+}
+
+static int 
+setorientation(TIFFRGBAImage* img)
+{
+       switch (img->orientation) {
+               case ORIENTATION_TOPLEFT:
+               case ORIENTATION_LEFTTOP:
+                       if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+                           img->req_orientation == ORIENTATION_RIGHTTOP)
+                               return FLIP_HORIZONTALLY;
+                       else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+                           img->req_orientation == ORIENTATION_RIGHTBOT)
+                               return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+                       else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+                           img->req_orientation == ORIENTATION_LEFTBOT)
+                               return FLIP_VERTICALLY;
+                       else
+                               return 0;
+               case ORIENTATION_TOPRIGHT:
+               case ORIENTATION_RIGHTTOP:
+                       if (img->req_orientation == ORIENTATION_TOPLEFT ||
+                           img->req_orientation == ORIENTATION_LEFTTOP)
+                               return FLIP_HORIZONTALLY;
+                       else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+                           img->req_orientation == ORIENTATION_RIGHTBOT)
+                               return FLIP_VERTICALLY;
+                       else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+                           img->req_orientation == ORIENTATION_LEFTBOT)
+                               return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+                       else
+                               return 0;
+               case ORIENTATION_BOTRIGHT:
+               case ORIENTATION_RIGHTBOT:
+                       if (img->req_orientation == ORIENTATION_TOPLEFT ||
+                           img->req_orientation == ORIENTATION_LEFTTOP)
+                               return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+                       else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+                           img->req_orientation == ORIENTATION_RIGHTTOP)
+                               return FLIP_VERTICALLY;
+                       else if (img->req_orientation == ORIENTATION_BOTLEFT ||
+                           img->req_orientation == ORIENTATION_LEFTBOT)
+                               return FLIP_HORIZONTALLY;
+                       else
+                               return 0;
+               case ORIENTATION_BOTLEFT:
+               case ORIENTATION_LEFTBOT:
+                       if (img->req_orientation == ORIENTATION_TOPLEFT ||
+                           img->req_orientation == ORIENTATION_LEFTTOP)
+                               return FLIP_VERTICALLY;
+                       else if (img->req_orientation == ORIENTATION_TOPRIGHT ||
+                           img->req_orientation == ORIENTATION_RIGHTTOP)
+                               return FLIP_HORIZONTALLY | FLIP_VERTICALLY;
+                       else if (img->req_orientation == ORIENTATION_BOTRIGHT ||
+                           img->req_orientation == ORIENTATION_RIGHTBOT)
+                               return FLIP_HORIZONTALLY;
+                       else
+                               return 0;
+               default:        /* NOTREACHED */
+                       return 0;
+       }
 }
 
 /*
@@ -433,47 +552,86 @@ gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
 {
     TIFF* tif = img->tif;
     tileContigRoutine put = img->put.contig;
-    uint16 orientation;
-    uint32 col, row, y;
+    uint32 col, row, y, rowstoread;
+    uint32 pos;
     uint32 tw, th;
     u_char* buf;
     int32 fromskew, toskew;
     uint32 nrow;
+    int ret = 1, flip;
 
     buf = (u_char*) _TIFFmalloc(TIFFTileSize(tif));
     if (buf == 0) {
        TIFFError(TIFFFileName(tif), "No space for tile buffer");
        return (0);
     }
+    _TIFFmemset(buf, 0, TIFFTileSize(tif));
     TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
     TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
-    y = setorientation(img, h);
-    orientation = img->orientation;
-    toskew = -(int32) (orientation == ORIENTATION_TOPLEFT ? tw+w : tw-w);
-    for (row = 0; row < h; row += th) {
-       nrow = (row + th > h ? h - row : th);
-       for (col = 0; col < w; col += tw) {
-           if (TIFFReadTile(tif, buf, col+img->col_offset,
+
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY) {
+           y = h - 1;
+           toskew = -(int32)(tw + w);
+    }
+    else {
+           y = 0;
+           toskew = -(int32)(tw - w);
+    }
+     
+    for (row = 0; row < h; row += nrow)
+    {
+        rowstoread = th - (row + img->row_offset) % th;
+       nrow = (row + rowstoread > h ? h - row : rowstoread);
+       for (col = 0; col < w; col += tw) 
+        {
+            if (TIFFReadTile(tif, buf, col+img->col_offset,
                              row+img->row_offset, 0, 0) < 0 && img->stoponerr)
-               break;
-           if (col + tw > w) {
-               /*
-                * Tile is clipped horizontally.  Calculate
-                * visible portion and skewing factors.
-                */
-               uint32 npix = w - col;
-               fromskew = tw - npix;
-               (*put)(img, raster+y*w+col, col, y,
-                   npix, nrow, fromskew, toskew + fromskew, buf);
-           } else {
-               (*put)(img, raster+y*w+col, col, y, tw, nrow, 0, toskew, buf);
-           }
-       }
-       y += (orientation == ORIENTATION_TOPLEFT ?
-           -(int32) nrow : (int32) nrow);
+            {
+                ret = 0;
+                break;
+            }
+           
+            pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif);
+
+           if (col + tw > w) 
+            {
+                /*
+                 * Tile is clipped horizontally.  Calculate
+                 * visible portion and skewing factors.
+                 */
+                uint32 npix = w - col;
+                fromskew = tw - npix;
+                (*put)(img, raster+y*w+col, col, y,
+                       npix, nrow, fromskew, toskew + fromskew, buf + pos);
+            }
+            else 
+            {
+                (*put)(img, raster+y*w+col, col, y, tw, nrow, 0, toskew, buf + pos);
+            }
+        }
+
+        y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow);
     }
     _TIFFfree(buf);
-    return (1);
+
+    if (flip & FLIP_HORIZONTALLY) {
+           uint32 line;
+
+           for (line = 0; line < h; line++) {
+                   uint32 *left = raster + (line * w);
+                   uint32 *right = left + w - 1;
+                   
+                   while ( left < right ) {
+                           uint32 temp = *left;
+                           *left = *right;
+                           *right = temp;
+                           left++, right--;
+                   }
+           }
+    }
+
+    return (ret);
 }
 
 /*
@@ -487,8 +645,8 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
 {
     TIFF* tif = img->tif;
     tileSeparateRoutine put = img->put.separate;
-    uint16 orientation;
-    uint32 col, row, y;
+    uint32 col, row, y, rowstoread;
+    uint32 pos;
     uint32 tw, th;
     u_char* buf;
     u_char* r;
@@ -499,6 +657,7 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
     int32 fromskew, toskew;
     int alpha = img->alpha;
     uint32 nrow;
+    int ret = 1, flip;
 
     tilesize = TIFFTileSize(tif);
     buf = (u_char*) _TIFFmalloc(4*tilesize);
@@ -506,6 +665,7 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
        TIFFError(TIFFFileName(tif), "No space for tile buffer");
        return (0);
     }
+    _TIFFmemset(buf, 0, 4*tilesize);
     r = buf;
     g = r + tilesize;
     b = g + tilesize;
@@ -514,43 +674,88 @@ gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
        memset(a, 0xff, tilesize);
     TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
     TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
-    y = setorientation(img, h);
-    orientation = img->orientation;
-    toskew = -(int32) (orientation == ORIENTATION_TOPLEFT ? tw+w : tw-w);
-    for (row = 0; row < h; row += th) {
-       nrow = (row + th > h ? h - row : th);
-       for (col = 0; col < w; col += tw) {
-           if (TIFFReadTile(tif, r, col+img->col_offset,
+
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY) {
+           y = h - 1;
+           toskew = -(int32)(tw + w);
+    }
+    else {
+           y = 0;
+           toskew = -(int32)(tw - w);
+    }
+
+    for (row = 0; row < h; row += nrow) 
+    {
+        rowstoread = th - (row + img->row_offset) % th;
+       nrow = (row + rowstoread > h ? h - row : rowstoread);
+        for (col = 0; col < w; col += tw) 
+        {
+            if (TIFFReadTile(tif, r, col+img->col_offset,
                              row+img->row_offset,0,0) < 0 && img->stoponerr)
-               break;
-           if (TIFFReadTile(tif, g, col+img->col_offset,
+            {
+                ret = 0;
+                break;
+            }
+            if (TIFFReadTile(tif, g, col+img->col_offset,
                              row+img->row_offset,0,1) < 0 && img->stoponerr)
-               break;
-           if (TIFFReadTile(tif, b, col+img->col_offset,
+            {
+                ret = 0;
+                break;
+            }
+            if (TIFFReadTile(tif, b, col+img->col_offset,
                              row+img->row_offset,0,2) < 0 && img->stoponerr)
-               break;
-           if (alpha && TIFFReadTile(tif,a,col+img->col_offset,
-                               row+img->row_offset,0,3) < 0 && img->stoponerr)
-               break;
-           if (col + tw > w) {
-               /*
-                * Tile is clipped horizontally.  Calculate
-                * visible portion and skewing factors.
-                */
-               uint32 npix = w - col;
-               fromskew = tw - npix;
-               (*put)(img, raster+y*w+col, col, y,
-                   npix, nrow, fromskew, toskew + fromskew, r, g, b, a);
-           } else {
-               (*put)(img, raster+y*w+col, col, y,
-                   tw, nrow, 0, toskew, r, g, b, a);
+            {
+                ret = 0;
+                break;
+            }
+            if (alpha && TIFFReadTile(tif,a,col+img->col_offset,
+                                      row+img->row_offset,0,3) < 0 && img->stoponerr)
+            {
+                ret = 0;
+                break;
+            }
+
+            pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif);
+
+            if (col + tw > w) 
+            {
+                /*
+                 * Tile is clipped horizontally.  Calculate
+                 * visible portion and skewing factors.
+                 */
+                uint32 npix = w - col;
+                fromskew = tw - npix;
+                (*put)(img, raster+y*w+col, col, y,
+                       npix, nrow, fromskew, toskew + fromskew, 
+                       r + pos, g + pos, b + pos, a + pos);
+            } else {
+                (*put)(img, raster+y*w+col, col, y,
+                       tw, nrow, 0, toskew, r + pos, g + pos, b + pos, a + pos);
+            }
+        }
+
+        y += (flip & FLIP_VERTICALLY ?-(int32) nrow : (int32) nrow);
+    }
+
+    if (flip & FLIP_HORIZONTALLY) {
+           uint32 line;
+
+           for (line = 0; line < h; line++) {
+                   uint32 *left = raster + (line * w);
+                   uint32 *right = left + w - 1;
+                   
+                   while ( left < right ) {
+                           uint32 temp = *left;
+                           *left = *right;
+                           *right = temp;
+                           left++, right--;
+                   }
            }
-       }
-       y += (orientation == ORIENTATION_TOPLEFT ?
-           -(int32) nrow : (int32) nrow);
     }
+
     _TIFFfree(buf);
-    return (1);
+    return (ret);
 }
 
 /*
@@ -564,38 +769,71 @@ gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
 {
     TIFF* tif = img->tif;
     tileContigRoutine put = img->put.contig;
-    uint16 orientation;
-    uint32 row, y, nrow;
+    uint32 row, y, nrow, rowstoread;
+    uint32 pos;
     u_char* buf;
     uint32 rowsperstrip;
     uint32 imagewidth = img->width;
     tsize_t scanline;
     int32 fromskew, toskew;
+    int ret = 1, flip;
 
     buf = (u_char*) _TIFFmalloc(TIFFStripSize(tif));
     if (buf == 0) {
        TIFFError(TIFFFileName(tif), "No space for strip buffer");
        return (0);
     }
-    y = setorientation(img, h);
-    orientation = img->orientation;
-    toskew = -(int32) (orientation == ORIENTATION_TOPLEFT ? w+w : w-w);
+    _TIFFmemset(buf, 0, TIFFStripSize(tif));
+
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY) {
+           y = h - 1;
+           toskew = -(int32)(w + w);
+    } else {
+           y = 0;
+           toskew = -(int32)(w - w);
+    }
+
     TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
     scanline = TIFFScanlineSize(tif);
     fromskew = (w < imagewidth ? imagewidth - w : 0);
-    for (row = 0; row < h; row += rowsperstrip) {
-       nrow = (row + rowsperstrip > h ? h - row : rowsperstrip);
-       if (TIFFReadEncodedStrip(tif,
+    for (row = 0; row < h; row += nrow) 
+    {
+        rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
+        nrow = (row + rowstoread > h ? h - row : rowstoread);
+        if (TIFFReadEncodedStrip(tif,
                                  TIFFComputeStrip(tif,row+img->row_offset, 0),
-                                 buf, nrow*scanline) < 0
+                                 buf, 
+                                 ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0
             && img->stoponerr)
-               break;
-       (*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, buf);
-       y += (orientation == ORIENTATION_TOPLEFT ?
-           -(int32) nrow : (int32) nrow);
+        {
+            ret = 0;
+            break;
+        }
+
+        pos = ((row + img->row_offset) % rowsperstrip) * scanline;
+        (*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, buf + pos);
+        y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow);
+    }
+
+    if (flip & FLIP_HORIZONTALLY) {
+           uint32 line;
+
+           for (line = 0; line < h; line++) {
+                   uint32 *left = raster + (line * w);
+                   uint32 *right = left + w - 1;
+                   
+                   while ( left < right ) {
+                           uint32 temp = *left;
+                           *left = *right;
+                           *right = temp;
+                           left++, right--;
+                   }
+           }
     }
+
     _TIFFfree(buf);
-    return (1);
+    return (ret);
 }
 
 /*
@@ -609,16 +847,17 @@ gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
 {
     TIFF* tif = img->tif;
     tileSeparateRoutine put = img->put.separate;
-    uint16 orientation;
     u_char *buf;
     u_char *r, *g, *b, *a;
-    uint32 row, y, nrow;
+    uint32 row, y, nrow, rowstoread;
+    uint32 pos;
     tsize_t scanline;
     uint32 rowsperstrip, offset_row;
     uint32 imagewidth = img->width;
     tsize_t stripsize;
     int32 fromskew, toskew;
     int alpha = img->alpha;
+    int        ret = 1, flip;
 
     stripsize = TIFFStripSize(tif);
     r = buf = (u_char *)_TIFFmalloc(4*stripsize);
@@ -626,39 +865,85 @@ gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h)
        TIFFError(TIFFFileName(tif), "No space for tile buffer");
        return (0);
     }
+    _TIFFmemset(buf, 0, 4*stripsize);
     g = r + stripsize;
     b = g + stripsize;
     a = b + stripsize;
     if (!alpha)
        memset(a, 0xff, stripsize);
-    y = setorientation(img, h);
-    orientation = img->orientation;
-    toskew = -(int32) (orientation == ORIENTATION_TOPLEFT ? w+w : w-w);
+
+    flip = setorientation(img);
+    if (flip & FLIP_VERTICALLY) {
+           y = h - 1;
+           toskew = -(int32)(w + w);
+    }
+    else {
+           y = 0;
+           toskew = -(int32)(w - w);
+    }
+
     TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
     scanline = TIFFScanlineSize(tif);
     fromskew = (w < imagewidth ? imagewidth - w : 0);
-    for (row = 0; row < h; row += rowsperstrip) {
-       nrow = (row + rowsperstrip > h ? h - row : rowsperstrip);
+    for (row = 0; row < h; row += nrow) 
+    {
+        rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;            
+        nrow = (row + rowstoread > h ? h - row : rowstoread);
         offset_row = row + img->row_offset;
-       if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
-           r, nrow*scanline) < 0 && img->stoponerr)
-           break;
-       if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 1),
-           g, nrow*scanline) < 0 && img->stoponerr)
-           break;
-       if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 2),
-           b, nrow*scanline) < 0 && img->stoponerr)
-           break;
-       if (alpha &&
-           (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 3),
-           a, nrow*scanline) < 0 && img->stoponerr))
-           break;
-       (*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, r, g, b, a);
-       y += (orientation == ORIENTATION_TOPLEFT ?
-           -(int32) nrow : (int32) nrow);
+       if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0),
+                                 r, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0 
+            && img->stoponerr)
+        {
+            ret = 0;
+            break;
+        }
+        if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 1),
+                                 g, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0 
+            && img->stoponerr)
+        {
+            ret = 0;
+            break;
+        }
+        if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 2),
+                                 b, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0 
+            && img->stoponerr)
+        {
+            ret = 0;
+            break;
+        }
+        if (alpha &&
+            (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 3),
+                                  a, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) < 0 
+             && img->stoponerr))
+        {
+            ret = 0;
+            break;
+        }
+
+        pos = ((row + img->row_offset) % rowsperstrip) * scanline;
+        (*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, r + pos, g + pos, 
+               b + pos, a + pos);
+        y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow);
+    }
+
+    if (flip & FLIP_HORIZONTALLY) {
+           uint32 line;
+
+           for (line = 0; line < h; line++) {
+                   uint32 *left = raster + (line * w);
+                   uint32 *right = left + w - 1;
+                   
+                   while ( left < right ) {
+                           uint32 temp = *left;
+                           *left = *right;
+                           *right = temp;
+                           left++, right--;
+                   }
+           }
     }
+
     _TIFFfree(buf);
-    return (1);
+    return (ret);
 }
 
 /*
@@ -747,10 +1032,15 @@ static void name(\
 DECLAREContigPutFunc(put8bitcmaptile)
 {
     uint32** PALmap = img->PALmap;
+    int samplesperpixel = img->samplesperpixel;
 
-    (void) x; (void) y;
+    (void) y;
     while (h-- > 0) {
-       UNROLL8(w, NOP, *cp++ = PALmap[*pp++][0]);
+       for (x = w; x-- > 0;)
+        {
+           *cp++ = PALmap[*pp][0];
+            pp += samplesperpixel;
+        }
        cp += toskew;
        pp += fromskew;
     }
@@ -812,12 +1102,41 @@ DECLAREContigPutFunc(put1bitcmaptile)
  */
 DECLAREContigPutFunc(putgreytile)
 {
+    int samplesperpixel = img->samplesperpixel;
     uint32** BWmap = img->BWmap;
 
     (void) y;
     while (h-- > 0) {
        for (x = w; x-- > 0;)
-           *cp++ = BWmap[*pp++][0];
+        {
+           *cp++ = BWmap[*pp][0];
+            pp += samplesperpixel;
+        }
+       cp += toskew;
+       pp += fromskew;
+    }
+}
+
+/*
+ * 16-bit greyscale => colormap/RGB
+ */
+DECLAREContigPutFunc(put16bitbwtile)
+{
+    int samplesperpixel = img->samplesperpixel;
+    uint32** BWmap = img->BWmap;
+
+    (void) y;
+    while (h-- > 0) {
+        uint16 *wp = (uint16 *) pp;
+
+       for (x = w; x-- > 0;)
+        {
+            /* use high order byte of 16bit value */
+
+           *cp++ = BWmap[*wp >> 8][0];
+            pp += 2 * samplesperpixel;
+            wp += samplesperpixel;
+        }
        cp += toskew;
        pp += fromskew;
     }
@@ -1226,44 +1545,89 @@ DECLARESepPutFunc(putRGBUAseparate16bittile)
 }
 
 /*
- * YCbCr -> RGB conversion and packing routines.  The colorspace
- * conversion algorithm comes from the IJG v5a code; see below
- * for more information on how it works.
+ * 8-bit packed CIE L*a*b 1976 samples => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitCIELab)
+{
+       float X, Y, Z;
+       uint32 r, g, b;
+       (void) y;
+       fromskew *= 3;
+       while (h-- > 0) {
+               for (x = w; x-- > 0;) {
+                       TIFFCIELabToXYZ(img->cielab,
+                                       (u_char)pp[0],
+                                       (signed char)pp[1],
+                                       (signed char)pp[2],
+                                       &X, &Y, &Z);
+                       TIFFXYZToRGB(img->cielab, X, Y, Z, &r, &g, &b);
+                       *cp++ = PACK(r, g, b);
+                       pp += 3;
+               }
+               cp += toskew;
+               pp += fromskew;
+       }
+}
+
+/*
+ * YCbCr -> RGB conversion and packing routines.
  */
 
-#define        YCbCrtoRGB(dst, yc) {                                           \
-    int Y = (yc);                                                      \
-    dst = PACK(                                                                \
-       clamptab[Y+Crrtab[Cr]],                                         \
-       clamptab[Y + (int)((Cbgtab[Cb]+Crgtab[Cr])>>16)],               \
-       clamptab[Y+Cbbtab[Cb]]);                                        \
+#define        YCbCrtoRGB(dst, Y) {                                            \
+       uint32 r, g, b;                                                 \
+       TIFFYCbCrtoRGB(img->ycbcr, (Y), Cb, Cr, &r, &g, &b);            \
+       dst = PACK(r, g, b);                                            \
 }
-#define        YCbCrSetup                                                      \
-    TIFFYCbCrToRGB* ycbcr = img->ycbcr;                                        \
-    int* Crrtab = ycbcr->Cr_r_tab;                                     \
-    int* Cbbtab = ycbcr->Cb_b_tab;                                     \
-    int32* Crgtab = ycbcr->Cr_g_tab;                                   \
-    int32* Cbgtab = ycbcr->Cb_g_tab;                                   \
-    TIFFRGBValue* clamptab = ycbcr->clamptab
 
 /*
- * 8-bit packed YCbCr samples w/ 4,4 subsampling => RGB
+ * 8-bit packed YCbCr samples => RGB 
+ * This function is generic for different sampling sizes, 
+ * and can handle blocks sizes that aren't multiples of the
+ * sampling size.  However, it is substantially less optimized
+ * than the specific sampling cases.  It is used as a fallback
+ * for difficult blocks.
  */
-DECLAREContigPutFunc(putcontig8bitYCbCr44tile)
+#ifdef notdef
+static void putcontig8bitYCbCrGenericTile( 
+    TIFFRGBAImage* img, 
+    uint32* cp, 
+    uint32 x, uint32 y, 
+    uint32 w, uint32 h, 
+    int32 fromskew, int32 toskew, 
+    u_char* pp,
+    int h_group, 
+    int v_group )
+
 {
-    YCbCrSetup;
     uint32* cp1 = cp+w+toskew;
     uint32* cp2 = cp1+w+toskew;
     uint32* cp3 = cp2+w+toskew;
     int32 incr = 3*w+4*toskew;
+    int32   Cb, Cr;
+    int     group_size = v_group * h_group + 2;
 
     (void) y;
-    /* XXX adjust fromskew */
+    fromskew = (fromskew * group_size) / h_group;
+
+    for( yy = 0; yy < h; yy++ )
+    {
+        u_char *pp_line;
+        int     y_line_group = yy / v_group;
+        int     y_remainder = yy - y_line_group * v_group;
+
+        pp_line = pp + v_line_group * 
+
+        
+        for( xx = 0; xx < w; xx++ )
+        {
+            Cb = pp
+        }
+    }
     for (; h >= 4; h -= 4) {
        x = w>>2;
        do {
-           int Cb = pp[16];
-           int Cr = pp[17];
+           Cb = pp[16];
+           Cr = pp[17];
 
            YCbCrtoRGB(cp [0], pp[ 0]);
            YCbCrtoRGB(cp [1], pp[ 1]);
@@ -1289,38 +1653,180 @@ DECLAREContigPutFunc(putcontig8bitYCbCr44tile)
        pp += fromskew;
     }
 }
+#endif
+
+/*
+ * 8-bit packed YCbCr samples w/ 4,4 subsampling => RGB
+ */
+DECLAREContigPutFunc(putcontig8bitYCbCr44tile)
+{
+    uint32* cp1 = cp+w+toskew;
+    uint32* cp2 = cp1+w+toskew;
+    uint32* cp3 = cp2+w+toskew;
+    int32 incr = 3*w+4*toskew;
+
+    (void) y;
+    /* adjust fromskew */
+    fromskew = (fromskew * 18) / 4;
+    if ((h & 3) == 0 && (w & 3) == 0) {                                        
+        for (; h >= 4; h -= 4) {
+            x = w>>2;
+            do {
+                int32 Cb = pp[16];
+                int32 Cr = pp[17];
+
+                YCbCrtoRGB(cp [0], pp[ 0]);
+                YCbCrtoRGB(cp [1], pp[ 1]);
+                YCbCrtoRGB(cp [2], pp[ 2]);
+                YCbCrtoRGB(cp [3], pp[ 3]);
+                YCbCrtoRGB(cp1[0], pp[ 4]);
+                YCbCrtoRGB(cp1[1], pp[ 5]);
+                YCbCrtoRGB(cp1[2], pp[ 6]);
+                YCbCrtoRGB(cp1[3], pp[ 7]);
+                YCbCrtoRGB(cp2[0], pp[ 8]);
+                YCbCrtoRGB(cp2[1], pp[ 9]);
+                YCbCrtoRGB(cp2[2], pp[10]);
+                YCbCrtoRGB(cp2[3], pp[11]);
+                YCbCrtoRGB(cp3[0], pp[12]);
+                YCbCrtoRGB(cp3[1], pp[13]);
+                YCbCrtoRGB(cp3[2], pp[14]);
+                YCbCrtoRGB(cp3[3], pp[15]);
+
+                cp += 4, cp1 += 4, cp2 += 4, cp3 += 4;
+                pp += 18;
+            } while (--x);
+            cp += incr, cp1 += incr, cp2 += incr, cp3 += incr;
+            pp += fromskew;
+        }
+    } else {
+        while (h > 0) {
+            for (x = w; x > 0;) {
+                int32 Cb = pp[16];
+                int32 Cr = pp[17];
+                switch (x) {
+                default:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[3], pp[15]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[3], pp[11]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 3:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[2], pp[14]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[2], pp[10]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 2:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[1], pp[13]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[1], pp[ 9]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 1:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp3[0], pp[12]); /* FALLTHROUGH */
+                    case 3:  YCbCrtoRGB(cp2[0], pp[ 8]); /* FALLTHROUGH */
+                    case 2:  YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                }
+                if (x < 4) {
+                    cp += x; cp1 += x; cp2 += x; cp3 += x;
+                    x = 0;
+                }
+                else {
+                    cp += 4; cp1 += 4; cp2 += 4; cp3 += 4;
+                    x -= 4;
+                }
+                pp += 18;
+            }
+            if (h <= 4)
+                break;
+            h -= 4;
+            cp += incr, cp1 += incr, cp2 += incr, cp3 += incr;
+            pp += fromskew;
+        }
+    }
+}
 
 /*
  * 8-bit packed YCbCr samples w/ 4,2 subsampling => RGB
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr42tile)
 {
-    YCbCrSetup;
     uint32* cp1 = cp+w+toskew;
     int32 incr = 2*toskew+w;
 
     (void) y;
-    /* XXX adjust fromskew */
-    for (; h >= 2; h -= 2) {
-       x = w>>2;
-       do {
-           int Cb = pp[8];
-           int Cr = pp[9];
-
-           YCbCrtoRGB(cp [0], pp[0]);
-           YCbCrtoRGB(cp [1], pp[1]);
-           YCbCrtoRGB(cp [2], pp[2]);
-           YCbCrtoRGB(cp [3], pp[3]);
-           YCbCrtoRGB(cp1[0], pp[4]);
-           YCbCrtoRGB(cp1[1], pp[5]);
-           YCbCrtoRGB(cp1[2], pp[6]);
-           YCbCrtoRGB(cp1[3], pp[7]);
-
-           cp += 4, cp1 += 4;
-           pp += 10;
-       } while (--x);
-       cp += incr, cp1 += incr;
-       pp += fromskew;
+    fromskew = (fromskew * 10) / 4;
+    if ((h & 3) == 0 && (w & 1) == 0) {
+        for (; h >= 2; h -= 2) {
+            x = w>>2;
+            do {
+                int32 Cb = pp[8];
+                int32 Cr = pp[9];
+                
+                YCbCrtoRGB(cp [0], pp[0]);
+                YCbCrtoRGB(cp [1], pp[1]);
+                YCbCrtoRGB(cp [2], pp[2]);
+                YCbCrtoRGB(cp [3], pp[3]);
+                YCbCrtoRGB(cp1[0], pp[4]);
+                YCbCrtoRGB(cp1[1], pp[5]);
+                YCbCrtoRGB(cp1[2], pp[6]);
+                YCbCrtoRGB(cp1[3], pp[7]);
+                
+                cp += 4, cp1 += 4;
+                pp += 10;
+            } while (--x);
+            cp += incr, cp1 += incr;
+            pp += fromskew;
+        }
+    } else {
+        while (h > 0) {
+            for (x = w; x > 0;) {
+                int32 Cb = pp[8];
+                int32 Cr = pp[9];
+                switch (x) {
+                default:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 3:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 2:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 1:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                }
+                if (x < 4) {
+                    cp += x; cp1 += x;
+                    x = 0;
+                }
+                else {
+                    cp += 4; cp1 += 4;
+                    x -= 4;
+                }
+                pp += 10;
+            }
+            if (h <= 2)
+                break;
+            h -= 2;
+            cp += incr, cp1 += incr;
+            pp += fromskew;
+        }
     }
 }
 
@@ -1329,15 +1835,13 @@ DECLAREContigPutFunc(putcontig8bitYCbCr42tile)
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr41tile)
 {
-    YCbCrSetup;
-
     (void) y;
     /* XXX adjust fromskew */
     do {
        x = w>>2;
        do {
-           int Cb = pp[4];
-           int Cr = pp[5];
+           int32 Cb = pp[4];
+           int32 Cr = pp[5];
 
            YCbCrtoRGB(cp [0], pp[0]);
            YCbCrtoRGB(cp [1], pp[1]);
@@ -1347,9 +1851,27 @@ DECLAREContigPutFunc(putcontig8bitYCbCr41tile)
            cp += 4;
            pp += 6;
        } while (--x);
+
+        if( (w&3) != 0 )
+        {
+           int32 Cb = pp[4];
+           int32 Cr = pp[5];
+
+            switch( (w&3) ) {
+              case 3: YCbCrtoRGB(cp [2], pp[2]);
+              case 2: YCbCrtoRGB(cp [1], pp[1]);
+              case 1: YCbCrtoRGB(cp [0], pp[0]);
+              case 0: break;
+            }
+
+            cp += (w&3);
+            pp += 6;
+        }
+
        cp += toskew;
        pp += fromskew;
     } while (--h);
+
 }
 
 /*
@@ -1357,28 +1879,62 @@ DECLAREContigPutFunc(putcontig8bitYCbCr41tile)
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr22tile)
 {
-    YCbCrSetup;
     uint32* cp1 = cp+w+toskew;
     int32 incr = 2*toskew+w;
 
     (void) y;
-    /* XXX adjust fromskew */
-    for (; h >= 2; h -= 2) {
-       x = w>>1;
-       do {
-           int Cb = pp[4];
-           int Cr = pp[5];
-
-           YCbCrtoRGB(cp [0], pp[0]);
-           YCbCrtoRGB(cp [1], pp[1]);
-           YCbCrtoRGB(cp1[0], pp[2]);
-           YCbCrtoRGB(cp1[1], pp[3]);
-
-           cp += 2, cp1 += 2;
-           pp += 6;
-       } while (--x);
-       cp += incr, cp1 += incr;
-       pp += fromskew;
+    fromskew = (fromskew * 6) / 2;
+    if ((h & 1) == 0 && (w & 1) == 0) {
+        for (; h >= 2; h -= 2) {
+            x = w>>1;
+            do {
+                int32 Cb = pp[4];
+                int32 Cr = pp[5];
+
+                YCbCrtoRGB(cp [0], pp[0]);
+                YCbCrtoRGB(cp [1], pp[1]);
+                YCbCrtoRGB(cp1[0], pp[2]);
+                YCbCrtoRGB(cp1[1], pp[3]);
+
+                cp += 2, cp1 += 2;
+                pp += 6;
+            } while (--x);
+            cp += incr, cp1 += incr;
+            pp += fromskew;
+        }
+    } else {
+        while (h > 0) {
+            for (x = w; x > 0;) {
+                int32 Cb = pp[4];
+                int32 Cr = pp[5];
+                switch (x) {
+                default:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[1], pp[ 3]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                case 1:
+                    switch (h) {
+                    default: YCbCrtoRGB(cp1[0], pp[ 2]); /* FALLTHROUGH */
+                    case 1:  YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */
+                    }                                    /* FALLTHROUGH */
+                }
+                if (x < 2) {
+                    cp += x; cp1 += x;
+                    x = 0;
+                }
+                else {
+                    cp += 2; cp1 += 2;
+                    x -= 2;
+                }
+                pp += 6;
+            }
+            if (h <= 2)
+                break;
+            h -= 2;
+            cp += incr, cp1 += incr;
+            pp += fromskew;
+        }
     }
 }
 
@@ -1387,22 +1943,32 @@ DECLAREContigPutFunc(putcontig8bitYCbCr22tile)
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr21tile)
 {
-    YCbCrSetup;
-
     (void) y;
-    /* XXX adjust fromskew */
+    fromskew = (fromskew * 4) / 2;
     do {
        x = w>>1;
        do {
-           int Cb = pp[2];
-           int Cr = pp[3];
+           int32 Cb = pp[2];
+           int32 Cr = pp[3];
 
-           YCbCrtoRGB(cp[0], pp[0]);
+           YCbCrtoRGB(cp[0], pp[0]); 
            YCbCrtoRGB(cp[1], pp[1]);
 
            cp += 2;
            pp += 4;
        } while (--x);
+
+        if( (w&1) != 0 )
+        {
+           int32 Cb = pp[2];
+           int32 Cr = pp[3];
+            
+            YCbCrtoRGB(cp [0], pp[0]);
+
+           cp += 1;
+           pp += 4;
+        }
+
        cp += toskew;
        pp += fromskew;
     } while (--h);
@@ -1413,15 +1979,13 @@ DECLAREContigPutFunc(putcontig8bitYCbCr21tile)
  */
 DECLAREContigPutFunc(putcontig8bitYCbCr11tile)
 {
-    YCbCrSetup;
-
     (void) y;
-    /* XXX adjust fromskew */
+    fromskew *= 3;
     do {
-       x = w>>1;
+        x = w; /* was x = w>>1; patched 2000/09/25 warmerda@home.com */ 
        do {
-           int Cb = pp[1];
-           int Cr = pp[2];
+           int32 Cb = pp[1];
+           int32 Cr = pp[2];
 
            YCbCrtoRGB(*cp++, pp[0]);
 
@@ -1431,125 +1995,87 @@ DECLAREContigPutFunc(putcontig8bitYCbCr11tile)
        pp += fromskew;
     } while (--h);
 }
-#undef YCbCrSetup
 #undef YCbCrtoRGB
 
-#define        LumaRed                 coeffs[0]
-#define        LumaGreen               coeffs[1]
-#define        LumaBlue                coeffs[2]
-#define        SHIFT                   16
-#define        FIX(x)                  ((int32)((x) * (1L<<SHIFT) + 0.5))
-#define        ONE_HALF                ((int32)(1<<(SHIFT-1)))
-
-/*
- * Initialize the YCbCr->RGB conversion tables.  The conversion
- * is done according to the 6.0 spec:
- *
- *    R = Y + Cr*(2 - 2*LumaRed)
- *    B = Y + Cb*(2 - 2*LumaBlue)
- *    G =   Y
- *        - LumaBlue*Cb*(2-2*LumaBlue)/LumaGreen
- *        - LumaRed*Cr*(2-2*LumaRed)/LumaGreen
- *
- * To avoid floating point arithmetic the fractional constants that
- * come out of the equations are represented as fixed point values
- * in the range 0...2^16.  We also eliminate multiplications by
- * pre-calculating possible values indexed by Cb and Cr (this code
- * assumes conversion is being done for 8-bit samples).
- */
-static void
-TIFFYCbCrToRGBInit(TIFFYCbCrToRGB* ycbcr, TIFF* tif)
+static tileContigRoutine
+initYCbCrConversion(TIFFRGBAImage* img)
 {
-    TIFFRGBValue* clamptab;
-    float* coeffs;
-    int i;
+       static char module[] = "initCIELabConversion";
 
-    clamptab = (TIFFRGBValue*)(
-       (tidata_t) ycbcr+TIFFroundup(sizeof (TIFFYCbCrToRGB), sizeof (long)));
-    _TIFFmemset(clamptab, 0, 256);             /* v < 0 => 0 */
-    ycbcr->clamptab = (clamptab += 256);
-    for (i = 0; i < 256; i++)
-       clamptab[i] = i;
-    _TIFFmemset(clamptab+256, 255, 2*256);     /* v > 255 => 255 */
-    TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRCOEFFICIENTS, &coeffs);
-    _TIFFmemcpy(ycbcr->coeffs, coeffs, 3*sizeof (float));
-    { float f1 = 2-2*LumaRed;          int32 D1 = FIX(f1);
-      float f2 = LumaRed*f1/LumaGreen; int32 D2 = -FIX(f2);
-      float f3 = 2-2*LumaBlue;         int32 D3 = FIX(f3);
-      float f4 = LumaBlue*f3/LumaGreen;        int32 D4 = -FIX(f4);
-      int x;
-
-      ycbcr->Cr_r_tab = (int*) (clamptab + 3*256);
-      ycbcr->Cb_b_tab = ycbcr->Cr_r_tab + 256;
-      ycbcr->Cr_g_tab = (int32*) (ycbcr->Cb_b_tab + 256);
-      ycbcr->Cb_g_tab = ycbcr->Cr_g_tab + 256;
-      /*
-       * i is the actual input pixel value in the range 0..255
-       * Cb and Cr values are in the range -128..127 (actually
-       * they are in a range defined by the ReferenceBlackWhite
-       * tag) so there is some range shifting to do here when
-       * constructing tables indexed by the raw pixel data.
-       *
-       * XXX handle ReferenceBlackWhite correctly to calculate
-       *     Cb/Cr values to use in constructing the tables.
-       */
-      for (i = 0, x = -128; i < 256; i++, x++) {
-         ycbcr->Cr_r_tab[i] = (int)((D1*x + ONE_HALF)>>SHIFT);
-         ycbcr->Cb_b_tab[i] = (int)((D3*x + ONE_HALF)>>SHIFT);
-         ycbcr->Cr_g_tab[i] = D2*x;
-         ycbcr->Cb_g_tab[i] = D4*x + ONE_HALF;
-      }
-    }
+       float *luma, *refBlackWhite;
+       uint16 hs, vs;
+
+       if (img->ycbcr == NULL) {
+           img->ycbcr = (TIFFYCbCrToRGB*) _TIFFmalloc(
+                   TIFFroundup(sizeof (TIFFYCbCrToRGB), sizeof (long))
+                   + 4*256*sizeof (TIFFRGBValue)
+                   + 2*256*sizeof (int)
+                   + 3*256*sizeof (int32)
+           );
+           if (img->ycbcr == NULL) {
+                   TIFFError(module,
+                             "No space for YCbCr->RGB conversion state");
+                   return (NULL);
+           }
+       }
+
+       TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRCOEFFICIENTS, &luma);
+       TIFFGetFieldDefaulted(img->tif, TIFFTAG_REFERENCEBLACKWHITE,
+                             &refBlackWhite);
+       if (TIFFYCbCrToRGBInit(img->ycbcr, luma, refBlackWhite) < 0)
+               return NULL;
+
+       /*
+        * The 6.0 spec says that subsampling must be
+        * one of 1, 2, or 4, and that vertical subsampling
+        * must always be <= horizontal subsampling; so
+        * there are only a few possibilities and we just
+        * enumerate the cases.
+        */
+       TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &hs, &vs);
+       switch ((hs<<4)|vs) {
+               case 0x44: return (&putcontig8bitYCbCr44tile);
+               case 0x42: return (&putcontig8bitYCbCr42tile);
+               case 0x41: return (&putcontig8bitYCbCr41tile);
+               case 0x22: return (&putcontig8bitYCbCr22tile);
+               case 0x21: return (&putcontig8bitYCbCr21tile);
+               case 0x11: return (&putcontig8bitYCbCr11tile);
+       }
+
+       return (NULL);
 }
-#undef SHIFT
-#undef ONE_HALF
-#undef FIX
-#undef LumaBlue
-#undef LumaGreen
-#undef LumaRed
 
 static tileContigRoutine
-initYCbCrConversion(TIFFRGBAImage* img)
+initCIELabConversion(TIFFRGBAImage* img)
 {
-    uint16 hs, vs;
-
-    if (img->ycbcr == NULL) {
-       img->ycbcr = (TIFFYCbCrToRGB*) _TIFFmalloc(
-             TIFFroundup(sizeof (TIFFYCbCrToRGB), sizeof (long))
-           + 4*256*sizeof (TIFFRGBValue)
-           + 2*256*sizeof (int)
-           + 2*256*sizeof (int32)
-       );
-       if (img->ycbcr == NULL) {
-           TIFFError(TIFFFileName(img->tif),
-               "No space for YCbCr->RGB conversion state");
-           return (NULL);
+       static char module[] = "initCIELabConversion";
+
+       float   *whitePoint;
+       float   refWhite[3];
+
+       if (!img->cielab) {
+               img->cielab = (TIFFCIELabToRGB *)
+                       _TIFFmalloc(sizeof(TIFFCIELabToRGB));
+               if (!img->cielab) {
+                       TIFFError(module,
+                           "No space for CIE L*a*b*->RGB conversion state.");
+                       return NULL;
+               }
        }
-       TIFFYCbCrToRGBInit(img->ycbcr, img->tif);
-    } else {
-       float* coeffs;
 
-       TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRCOEFFICIENTS, &coeffs);
-       if (_TIFFmemcmp(coeffs, img->ycbcr->coeffs, 3*sizeof (float)) != 0)
-           TIFFYCbCrToRGBInit(img->ycbcr, img->tif);
-    }
-    /*
-     * The 6.0 spec says that subsampling must be
-     * one of 1, 2, or 4, and that vertical subsampling
-     * must always be <= horizontal subsampling; so
-     * there are only a few possibilities and we just
-     * enumerate the cases.
-     */
-    TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &hs, &vs);
-    switch ((hs<<4)|vs) {
-    case 0x44: return (putcontig8bitYCbCr44tile);
-    case 0x42: return (putcontig8bitYCbCr42tile);
-    case 0x41: return (putcontig8bitYCbCr41tile);
-    case 0x22: return (putcontig8bitYCbCr22tile);
-    case 0x21: return (putcontig8bitYCbCr21tile);
-    case 0x11: return (putcontig8bitYCbCr11tile);
-    }
-    return (NULL);
+       TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint);
+       refWhite[1] = 100.0F;
+       refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1];
+       refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1])
+                     / whitePoint[1] * refWhite[1];
+       if (TIFFCIELabToRGBInit(img->cielab, &display_sRGB, refWhite) < 0) {
+               TIFFError(module,
+                   "Failed to initialize CIE L*a*b*->RGB conversion state.");
+               _TIFFfree(img->cielab);
+               return NULL;
+       }
+
+       return &putcontig8bitCIELab;
 }
 
 /*
@@ -1568,6 +2094,9 @@ makebwmap(TIFFRGBAImage* img)
     int i;
     uint32* p;
 
+    if( nsamples == 0 )
+        nsamples = 1;
+
     img->BWmap = (uint32**) _TIFFmalloc(
        256*sizeof (uint32 *)+(256*nsamples*sizeof(uint32)));
     if (img->BWmap == NULL) {
@@ -1601,6 +2130,7 @@ makebwmap(TIFFRGBAImage* img)
            GREY(i&0xf);
            break;
        case 8:
+        case 16:
            GREY(i);
            break;
        }
@@ -1620,6 +2150,11 @@ setupMap(TIFFRGBAImage* img)
     int32 x, range;
 
     range = (int32)((1L<<img->bitspersample)-1);
+    
+    /* treat 16 bit the same as eight bit */
+    if( img->bitspersample == 16 )
+        range = (int32) 255;
+
     img->Map = (TIFFRGBValue*) _TIFFmalloc((range+1) * sizeof (TIFFRGBValue));
     if (img->Map == NULL) {
        TIFFError(TIFFFileName(img->tif),
@@ -1628,12 +2163,12 @@ setupMap(TIFFRGBAImage* img)
     }
     if (img->photometric == PHOTOMETRIC_MINISWHITE) {
        for (x = 0; x <= range; x++)
-           img->Map[x] = ((range - x) * 255) / range;
+           img->Map[x] = (TIFFRGBValue) (((range - x) * 255) / range);
     } else {
        for (x = 0; x <= range; x++)
-           img->Map[x] = (x * 255) / range;
+           img->Map[x] = (TIFFRGBValue) ((x * 255) / range);
     }
-    if (img->bitspersample <= 8 &&
+    if (img->bitspersample <= 16 &&
        (img->photometric == PHOTOMETRIC_MINISBLACK ||
         img->photometric == PHOTOMETRIC_MINISWHITE)) {
        /*
@@ -1707,7 +2242,7 @@ makecmap(TIFFRGBAImage* img)
     for (i = 0; i < 256; i++) {
        TIFFRGBValue c;
        img->PALmap[i] = p;
-#define        CMAP(x) c = x; *p++ = PACK(r[c]&0xff, g[c]&0xff, b[c]&0xff);
+#define        CMAP(x) c = (TIFFRGBValue) x; *p++ = PACK(r[c]&0xff, g[c]&0xff, b[c]&0xff);
        switch (bitspersample) {
        case 1:
            CMAP(i>>7);
@@ -1830,16 +2365,21 @@ pickTileContigCase(TIFFRGBAImage* img)
        case PHOTOMETRIC_MINISWHITE:
        case PHOTOMETRIC_MINISBLACK:
            switch (img->bitspersample) {
-           case 8:     put = putgreytile; break;
-           case 4: put = put4bitbwtile; break;
-           case 2: put = put2bitbwtile; break;
-           case 1: put = put1bitbwtile; break;
+            case 16: put = put16bitbwtile; break;
+           case 8:  put = putgreytile; break;
+           case 4:  put = put4bitbwtile; break;
+           case 2:  put = put2bitbwtile; break;
+           case 1:  put = put1bitbwtile; break;
            }
            break;
        case PHOTOMETRIC_YCBCR:
            if (img->bitspersample == 8)
                put = initYCbCrConversion(img);
            break;
+       case PHOTOMETRIC_CIELAB:
+           if (img->bitspersample == 8)
+               put = initCIELabConversion(img);
+           break;
        }
     }
     return ((img->put.contig = put) != 0);
@@ -1919,7 +2459,7 @@ TIFFReadRGBAStrip(TIFF* tif, uint32 row, uint32 * raster )
        return (0);
     }
 
-    if (TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
+    if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
 
         img.row_offset = row;
         img.col_offset = 0;
@@ -1955,7 +2495,7 @@ TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster)
     int        ok;
     uint32     tile_xsize, tile_ysize;
     uint32     read_xsize, read_ysize;
-    int                i_row;
+    uint32     i_row;
 
     /*
      * Verify that our request is legal - on a tile file, and on a
@@ -1983,9 +2523,10 @@ TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster)
      * Setup the RGBA reader.
      */
     
-    if ( !TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
-       TIFFError(TIFFFileName(tif), emsg);
-        return( 0 );
+    if (!TIFFRGBAImageOK(tif, emsg) 
+       || !TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
+           TIFFError(TIFFFileName(tif), emsg);
+           return( 0 );
     }
 
     /*
@@ -2027,20 +2568,18 @@ TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster)
     if( read_xsize == tile_xsize && read_ysize == tile_ysize )
         return( ok );
 
-    for( i_row = 0; i_row < read_ysize; i_row++ )
-    {
-        _TIFFmemcpy( raster + (tile_ysize - i_row - 1) * tile_xsize,
-                     raster + (read_ysize - i_row - 1) * read_xsize,
-                     read_xsize * sizeof(uint32) );
+    for( i_row = 0; i_row < read_ysize; i_row++ ) {
+        memmove( raster + (tile_ysize - i_row - 1) * tile_xsize,
+                 raster + (read_ysize - i_row - 1) * read_xsize,
+                 read_xsize * sizeof(uint32) );
         _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize+read_xsize,
                      0, sizeof(uint32) * (tile_xsize - read_xsize) );
     }
 
-    for( i_row = read_ysize; i_row < tile_ysize; i_row++ )
-    {
+    for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) {
         _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize,
                      0, sizeof(uint32) * tile_xsize );
     }
-    
+
     return (ok);
 }