/*
 *   TIFF file routines for CUPS.
 *
 *   Copyright 2007-2011 by Apple Inc.
 *   Copyright 1993-2007 by Easy Software Products.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Apple Inc. and are protected by Federal copyright
 *   law.  Distribution and use rights are outlined in the file "COPYING"
 *   which should have been included with this file.
 *
 * Contents:
 *
 *   _cupsImageReadTIFF() - Read a TIFF image file.
 */

/*
 * Include necessary headers...
 */

#include "image-private.h"

#ifdef HAVE_LIBTIFF
#  include <tiff.h>	/* TIFF image definitions */
#  include <tiffio.h>
#  include <unistd.h>


/*
 * '_cupsImageReadTIFF()' - Read a TIFF image file.
 */

int					/* O - Read status */
_cupsImageReadTIFF(
    cups_image_t    *img,		/* IO - cupsImage */
    FILE            *fp,		/* I - cupsImage file */
    cups_icspace_t  primary,		/* I - Primary choice for colorspace */
    cups_icspace_t  secondary,		/* I - Secondary choice for colorspace */
    int             saturation,		/* I - Color saturation (%) */
    int             hue,		/* I - Color hue (degrees) */
    const cups_ib_t *lut)		/* I - Lookup table for gamma/brightness */
{
  TIFF		*tif;			/* TIFF file */
  uint32	width, height;		/* Size of image */
  uint16	photometric,		/* Colorspace */
		compression,		/* Type of compression */
		orientation,		/* Orientation */
		resunit,		/* Units for resolution */
		samples,		/* Number of samples/pixel */
		bits,			/* Number of bits/pixel */
		inkset,			/* Ink set for color separations */
		numinks;		/* Number of inks in set */
  float		xres,			/* Horizontal resolution */
		yres;			/* Vertical resolution */
  uint16	*redcmap,		/* Red colormap information */
		*greencmap,		/* Green colormap information */
		*bluecmap;		/* Blue colormap information */
  int		c,			/* Color index */
		num_colors,		/* Number of colors */
		bpp,			/* Bytes per pixel */
		x, y,			/* Current x & y */
		row,			/* Current row in image */
		xstart, ystart,		/* Starting x & y */
		xdir, ydir,		/* X & y direction */
		xcount, ycount,		/* X & Y counters */
		pstep,			/* Pixel step (= bpp or -2 * bpp) */
		scanwidth,		/* Width of scanline */
		r, g, b, k,		/* Red, green, blue, and black values */
		alpha;			/* cupsImage includes alpha? */
  cups_ib_t		*in,			/* Input buffer */
		*out,			/* Output buffer */
		*p,			/* Pointer into buffer */
		*scanline,		/* Scanline buffer */
		*scanptr,		/* Pointer into scanline buffer */
		bit,			/* Current bit */
		pixel,			/* Current pixel */
		zero,			/* Zero value (bitmaps) */
		one;			/* One value (bitmaps) */


 /*
  * Open the TIFF file and get the required parameters...
  */

  lseek(fileno(fp), 0, SEEK_SET); /* Work around "feature" in some stdio's */

  if ((tif = TIFFFdOpen(fileno(fp), "", "r")) == NULL)
  {
    fputs("DEBUG: TIFFFdOpen() failed!\n", stderr);
    fclose(fp);
    return (-1);
  }

  if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width))
  {
    fputs("DEBUG: No image width tag in the file!\n", stderr);
    TIFFClose(tif);
    fclose(fp);
    return (-1);
  }

  if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))
  {
    fputs("DEBUG: No image height tag in the file!\n", stderr);
    TIFFClose(tif);
    fclose(fp);
    return (-1);
  }

  if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
  {
    fputs("DEBUG: No photometric tag in the file!\n", stderr);
    TIFFClose(tif);
    fclose(fp);
    return (-1);
  }

  if (!TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression))
  {
    fputs("DEBUG: No compression tag in the file!\n", stderr);
    TIFFClose(tif);
    fclose(fp);
    return (-1);
  }

  if (!TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samples))
    samples = 1;

  if (!TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits))
    bits = 1;

 /*
  * Get the image orientation...
  */

  if (!TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation))
    orientation = 0;

 /*
  * Get the image resolution...
  */

  if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) &&
      TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) &&
      TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit))
  {
    if (resunit == RESUNIT_INCH)
    {
      img->xppi = xres;
      img->yppi = yres;
    }
    else if (resunit == RESUNIT_CENTIMETER)
    {
      img->xppi = xres * 2.54;
      img->yppi = yres * 2.54;
    }
    else
    {
      img->xppi = 128;
      img->yppi = 128;
    }

    if (img->xppi == 0 || img->yppi == 0)
    {
      fputs("DEBUG: Bad TIFF resolution.\n", stderr);
      img->xppi = img->yppi = 128;
    }

    fprintf(stderr, "DEBUG: TIFF resolution = %fx%f, units=%d\n",
            xres, yres, resunit);
    fprintf(stderr, "DEBUG: Stored resolution = %dx%d PPI\n",
            img->xppi, img->yppi);
  }

 /*
  * See if the image has an alpha channel...
  */

  if (samples == 2 || (samples == 4 && photometric == PHOTOMETRIC_RGB))
    alpha = 1;
  else
    alpha = 0;

 /*
  * Check the size of the image...
  */

  if (width == 0 || width > CUPS_IMAGE_MAX_WIDTH ||
      height == 0 || height > CUPS_IMAGE_MAX_HEIGHT ||
      (bits != 1 && bits != 2 && bits != 4 && bits != 8) ||
      samples < 1 || samples > 4)
  {
    fprintf(stderr, "DEBUG: Bad TIFF dimensions %ux%ux%ux%u!\n",
            (unsigned)width, (unsigned)height, (unsigned)bits,
	    (unsigned)samples);
    TIFFClose(tif);
    fclose(fp);
    return (1);
  }

 /*
  * Setup the image size and colorspace...
  */

  img->xsize = width;
  img->ysize = height;
  if (photometric == PHOTOMETRIC_MINISBLACK ||
      photometric == PHOTOMETRIC_MINISWHITE)
    img->colorspace = secondary;
  else if (photometric == PHOTOMETRIC_SEPARATED && primary == CUPS_IMAGE_RGB_CMYK)
    img->colorspace = CUPS_IMAGE_CMYK;
  else if (primary == CUPS_IMAGE_RGB_CMYK)
    img->colorspace = CUPS_IMAGE_RGB;
  else
    img->colorspace = primary;

  fprintf(stderr, "DEBUG: img->colorspace = %d\n", img->colorspace);

  bpp = cupsImageGetDepth(img);

  cupsImageSetMaxTiles(img, 0);

 /*
  * Set the X & Y start and direction according to the image orientation...
  */

  switch (orientation)
  {
    case ORIENTATION_TOPRIGHT :
        fputs("DEBUG: orientation = top-right\n", stderr);
        break;
    case ORIENTATION_RIGHTTOP :
        fputs("DEBUG: orientation = right-top\n", stderr);
        break;
    default :
    case ORIENTATION_TOPLEFT :
        fputs("DEBUG: orientation = top-left\n", stderr);
        break;
    case ORIENTATION_LEFTTOP :
        fputs("DEBUG: orientation = left-top\n", stderr);
        break;
    case ORIENTATION_BOTLEFT :
        fputs("DEBUG: orientation = bottom-left\n", stderr);
        break;
    case ORIENTATION_LEFTBOT :
        fputs("DEBUG: orientation = left-bottom\n", stderr);
        break;
    case ORIENTATION_BOTRIGHT :
        fputs("DEBUG: orientation = bottom-right\n", stderr);
        break;
    case ORIENTATION_RIGHTBOT :
        fputs("DEBUG: orientation = right-bottom\n", stderr);
        break;
  }

  switch (orientation)
  {
    case ORIENTATION_TOPRIGHT :
    case ORIENTATION_RIGHTTOP :
        xstart = img->xsize - 1;
        xdir   = -1;
        ystart = 0;
        ydir   = 1;
        break;
    default :
    case ORIENTATION_TOPLEFT :
    case ORIENTATION_LEFTTOP :
        xstart = 0;
        xdir   = 1;
        ystart = 0;
        ydir   = 1;
        break;
    case ORIENTATION_BOTLEFT :
    case ORIENTATION_LEFTBOT :
        xstart = 0;
        xdir   = 1;
        ystart = img->ysize - 1;
        ydir   = -1;
        break;
    case ORIENTATION_BOTRIGHT :
    case ORIENTATION_RIGHTBOT :
        xstart = img->xsize - 1;
        xdir   = -1;
        ystart = img->ysize - 1;
        ydir   = -1;
        break;
  }

 /*
  * Allocate a scanline buffer...
  */

  scanwidth = TIFFScanlineSize(tif);
  scanline  = _TIFFmalloc(scanwidth);

 /*
  * Allocate input and output buffers...
  */

  if (orientation < ORIENTATION_LEFTTOP)
  {
    if (samples > 1 || photometric == PHOTOMETRIC_PALETTE)
      pstep = xdir * 3;
    else
      pstep = xdir;

    in  = malloc(img->xsize * 3 + 3);
    out = malloc(img->xsize * bpp);
  }
  else
  {
    if (samples > 1 || photometric == PHOTOMETRIC_PALETTE)
      pstep = ydir * 3;
    else
      pstep = ydir;

    in  = malloc(img->ysize * 3 + 3);
    out = malloc(img->ysize * bpp);
  }

 /*
  * Read the image.  This is greatly complicated by the fact that TIFF
  * supports literally hundreds of different colorspaces and orientations,
  * each which must be handled separately...
  */

  fprintf(stderr, "DEBUG: photometric = %d\n", photometric);
  fprintf(stderr, "DEBUG: compression = %d\n", compression);

  switch (photometric)
  {
    case PHOTOMETRIC_MINISWHITE :
    case PHOTOMETRIC_MINISBLACK :
        if (photometric == PHOTOMETRIC_MINISWHITE)
        {
          zero = 255;
          one  = 0;
        }
        else
        {
          zero = 0;
          one  = 255;
        }

        if (orientation < ORIENTATION_LEFTTOP)
        {
         /*
          * Row major order...
          */

          for (y = ystart, ycount = img->ysize, row = 0;
               ycount > 0;
               ycount --, y += ydir, row ++)
          {
            if (bits == 1)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline, p = in + xstart, bit = 128;
                   xcount > 0;
                   xcount --, p += pstep)
              {
        	if (*scanptr & bit)
                  *p = one;
                else
                  *p = zero;

        	if (bit > 1)
                  bit >>= 1;
        	else
        	{
                  bit = 128;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 2)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline, p = in + xstart, bit = 0xc0;
                   xcount > 0;
                   xcount --, p += pstep)
              {
                pixel = *scanptr & bit;
                while (pixel > 3)
                  pixel >>= 2;
                *p = (255 * pixel / 3) ^ zero;

        	if (bit > 3)
                  bit >>= 2;
        	else
        	{
                  bit = 0xc0;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 4)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline, p = in + xstart, bit = 0xf0;
                   xcount > 0;
                   xcount --, p += pstep)
              {
                if (bit == 0xf0)
                {
                  *p = (255 * ((*scanptr & 0xf0) >> 4) / 15) ^ zero;
                  bit = 0x0f;
                }
                else
        	{
                  *p = (255 * (*scanptr & 0x0f) / 15) ^ zero;
                  bit = 0xf0;
                  scanptr ++;
        	}
              }
            }
            else if (xdir < 0 || zero || alpha)
            {
              TIFFReadScanline(tif, scanline, row, 0);

              if (alpha)
	      {
        	if (zero)
        	{
                  for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
                       xcount > 0;
                       xcount --, p += pstep, scanptr += 2)
                    *p = (scanptr[1] * (255 - scanptr[0]) +
		          (255 - scanptr[1]) * 255) / 255;
        	}
        	else
        	{
                  for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
                       xcount > 0;
                       xcount --, p += pstep, scanptr += 2)
                    *p = (scanptr[1] * scanptr[0] +
		          (255 - scanptr[1]) * 255) / 255;
        	}
	      }
	      else
	      {
        	if (zero)
        	{
                  for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
                       xcount > 0;
                       xcount --, p += pstep, scanptr ++)
                    *p = 255 - *scanptr;
        	}
        	else
        	{
                  for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
                       xcount > 0;
                       xcount --, p += pstep, scanptr ++)
                    *p = *scanptr;
        	}
              }
            }
            else
              TIFFReadScanline(tif, in, row, 0);

            if (img->colorspace == CUPS_IMAGE_WHITE)
	    {
	      if (lut)
	        cupsImageLut(in, img->xsize, lut);

              _cupsImagePutRow(img, 0, y, img->xsize, in);
	    }
            else
            {
	      switch (img->colorspace)
	      {
		default :
		    break;

		case CUPS_IMAGE_RGB :
		    cupsImageWhiteToRGB(in, out, img->xsize);
		    break;
		case CUPS_IMAGE_BLACK :
		    cupsImageWhiteToBlack(in, out, img->xsize);
		    break;
		case CUPS_IMAGE_CMY :
		    cupsImageWhiteToCMY(in, out, img->xsize);
		    break;
		case CUPS_IMAGE_CMYK :
		    cupsImageWhiteToCMYK(in, out, img->xsize);
		    break;
	      }

	      if (lut)
	        cupsImageLut(out, img->xsize * bpp, lut);

              _cupsImagePutRow(img, 0, y, img->xsize, out);
	    }
          }
        }
        else
        {
         /*
          * Column major order...
          */

          for (x = xstart, xcount = img->xsize, row = 0;
               xcount > 0;
               xcount --, x += xdir, row ++)
          {
            if (bits == 1)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline, p = in + ystart, bit = 128;
                   ycount > 0;
                   ycount --, p += ydir)
              {
        	if (*scanptr & bit)
                  *p = one;
                else
                  *p = zero;

        	if (bit > 1)
                  bit >>= 1;
        	else
        	{
                  bit = 128;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 2)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline, p = in + ystart, bit = 0xc0;
                   ycount > 0;
                   ycount --, p += ydir)
              {
                pixel = *scanptr & 0xc0;
                while (pixel > 3)
                  pixel >>= 2;

                *p = (255 * pixel / 3) ^ zero;

        	if (bit > 3)
                  bit >>= 2;
        	else
        	{
                  bit = 0xc0;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 4)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline, p = in + ystart, bit = 0xf0;
                   ycount > 0;
                   ycount --, p += ydir)
              {
        	if (bit == 0xf0)
        	{
                  *p = (255 * ((*scanptr & 0xf0) >> 4) / 15) ^ zero;
                  bit = 0x0f;
                }
        	else
        	{
                  *p = (255 * (*scanptr & 0x0f) / 15) ^ zero;
                  bit = 0xf0;
                  scanptr ++;
        	}
              }
            }
            else if (ydir < 0 || zero || alpha)
            {
              TIFFReadScanline(tif, scanline, row, 0);

              if (alpha)
	      {
		if (zero)
        	{
                  for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
                       ycount > 0;
                       ycount --, p += ydir, scanptr += 2)
                    *p = (scanptr[1] * (255 - scanptr[0]) +
		          (255 - scanptr[1]) * 255) / 255;
        	}
        	else
        	{
                  for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
                       ycount > 0;
                       ycount --, p += ydir, scanptr += 2)
                    *p = (scanptr[1] * scanptr[0] +
		          (255 - scanptr[1]) * 255) / 255;
        	}
              }
	      else
	      {
		if (zero)
        	{
                  for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
                       ycount > 0;
                       ycount --, p += ydir, scanptr ++)
                    *p = 255 - *scanptr;
        	}
        	else
        	{
                  for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
                       ycount > 0;
                       ycount --, p += ydir, scanptr ++)
                    *p = *scanptr;
        	}
	      }
            }
            else
              TIFFReadScanline(tif, in, row, 0);

            if (img->colorspace == CUPS_IMAGE_WHITE)
	    {
	      if (lut)
	        cupsImageLut(in, img->ysize, lut);

              _cupsImagePutCol(img, x, 0, img->ysize, in);
	    }
            else
            {
	      switch (img->colorspace)
	      {
		default :
		    break;

		case CUPS_IMAGE_RGB :
		    cupsImageWhiteToRGB(in, out, img->ysize);
		    break;
		case CUPS_IMAGE_BLACK :
		    cupsImageWhiteToBlack(in, out, img->ysize);
		    break;
		case CUPS_IMAGE_CMY :
		    cupsImageWhiteToCMY(in, out, img->ysize);
		    break;
		case CUPS_IMAGE_CMYK :
		    cupsImageWhiteToCMYK(in, out, img->ysize);
		    break;
	      }

	      if (lut)
	        cupsImageLut(out, img->ysize * bpp, lut);

              _cupsImagePutCol(img, x, 0, img->ysize, out);
	    }
          }
        }
        break;

    case PHOTOMETRIC_PALETTE :
	if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap))
	{
	  _TIFFfree(scanline);
	  free(in);
	  free(out);

	  TIFFClose(tif);
	  fputs("DEBUG: No colormap tag in the file!\n", stderr);
	  fclose(fp);
	  return (-1);
	}

        num_colors = 1 << bits;

        for (c = 0; c < num_colors; c ++)
	{
	  redcmap[c]   >>= 8;
	  greencmap[c] >>= 8;
	  bluecmap[c]  >>= 8;
	}

        if (orientation < ORIENTATION_LEFTTOP)
        {
         /*
          * Row major order...
          */

          for (y = ystart, ycount = img->ysize, row = 0;
               ycount > 0;
               ycount --, y += ydir, row ++)
          {
            if (bits == 1)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline,
	               p = in + xstart * 3, bit = 128;
                   xcount > 0;
                   xcount --, p += pstep)
              {
        	if (*scanptr & bit)
		{
                  p[0] = redcmap[1];
                  p[1] = greencmap[1];
                  p[2] = bluecmap[1];
		}
                else
		{
                  p[0] = redcmap[0];
                  p[1] = greencmap[0];
                  p[2] = bluecmap[0];
		}

        	if (bit > 1)
                  bit >>= 1;
        	else
        	{
                  bit = 128;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 2)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline,
	               p = in + xstart * 3, bit = 0xc0;
                   xcount > 0;
                   xcount --, p += pstep)
              {
                pixel = *scanptr & bit;
                while (pixel > 3)
                  pixel >>= 2;

                p[0] = redcmap[pixel];
                p[1] = greencmap[pixel];
                p[2] = bluecmap[pixel];

        	if (bit > 3)
                  bit >>= 2;
        	else
        	{
                  bit = 0xc0;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 4)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline,
	               p = in + 3 * xstart, bit = 0xf0;
                   xcount > 0;
                   xcount --, p += pstep)
              {
                if (bit == 0xf0)
                {
		  pixel = (*scanptr & 0xf0) >> 4;
                  p[0]  = redcmap[pixel];
                  p[1]  = greencmap[pixel];
                  p[2]  = bluecmap[pixel];
                  bit   = 0x0f;
                }
                else
        	{
		  pixel = *scanptr++ & 0x0f;
                  p[0]  = redcmap[pixel];
                  p[1]  = greencmap[pixel];
                  p[2]  = bluecmap[pixel];
                  bit   = 0xf0;
        	}
              }
            }
            else
            {
              TIFFReadScanline(tif, scanline, row, 0);

              for (xcount = img->xsize, p = in + 3 * xstart, scanptr = scanline;
                   xcount > 0;
                   xcount --, p += pstep)
              {
	        p[0] = redcmap[*scanptr];
	        p[1] = greencmap[*scanptr];
	        p[2] = bluecmap[*scanptr++];
	      }
            }

	    switch (img->colorspace)
	    {
	      default :
		  break;

	      case CUPS_IMAGE_WHITE :
		  cupsImageRGBToWhite(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_RGB :
		  cupsImageRGBToRGB(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_BLACK :
		  cupsImageRGBToBlack(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_CMY :
		  cupsImageRGBToCMY(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_CMYK :
		  cupsImageRGBToCMYK(in, out, img->xsize);
		  break;
	    }

	    if (lut)
	      cupsImageLut(out, img->xsize * bpp, lut);

            _cupsImagePutRow(img, 0, y, img->xsize, out);
          }
        }
        else
        {
         /*
          * Column major order...
          */

          for (x = xstart, xcount = img->xsize, row = 0;
               xcount > 0;
               xcount --, x += xdir, row ++)
          {
            if (bits == 1)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline,
	               p = in + 3 * ystart, bit = 128;
                   ycount > 0;
                   ycount --, p += ydir)
              {
        	if (*scanptr & bit)
		{
                  p[0] = redcmap[1];
                  p[1] = greencmap[1];
                  p[2] = bluecmap[1];
		}
                else
		{
                  p[0] = redcmap[0];
                  p[1] = greencmap[0];
                  p[2] = bluecmap[0];
		}

        	if (bit > 1)
                  bit >>= 1;
        	else
        	{
                  bit = 128;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 2)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline,
	               p = in + 3 * ystart, bit = 0xc0;
                   ycount > 0;
                   ycount --, p += ydir)
              {
                pixel = *scanptr & 0xc0;
                while (pixel > 3)
                  pixel >>= 2;

                p[0] = redcmap[pixel];
                p[1] = greencmap[pixel];
                p[2] = bluecmap[pixel];

        	if (bit > 3)
                  bit >>= 2;
        	else
        	{
                  bit = 0xc0;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 4)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline,
	               p = in + 3 * ystart, bit = 0xf0;
                   ycount > 0;
                   ycount --, p += ydir)
              {
                if (bit == 0xf0)
                {
		  pixel = (*scanptr & 0xf0) >> 4;
                  p[0]  = redcmap[pixel];
                  p[1]  = greencmap[pixel];
                  p[2]  = bluecmap[pixel];
                  bit   = 0x0f;
                }
                else
        	{
		  pixel = *scanptr++ & 0x0f;
                  p[0]  = redcmap[pixel];
                  p[1]  = greencmap[pixel];
                  p[2]  = bluecmap[pixel];
                  bit   = 0xf0;
        	}
              }
            }
            else
            {
              TIFFReadScanline(tif, scanline, row, 0);

              for (ycount = img->ysize, p = in + 3 * ystart, scanptr = scanline;
                   ycount > 0;
                   ycount --, p += ydir)
              {
	        p[0] = redcmap[*scanptr];
	        p[1] = greencmap[*scanptr];
	        p[2] = bluecmap[*scanptr++];
	      }
            }

	    switch (img->colorspace)
	    {
	      default :
		  break;

	      case CUPS_IMAGE_WHITE :
		  cupsImageRGBToWhite(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_RGB :
		  cupsImageRGBToRGB(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_BLACK :
		  cupsImageRGBToBlack(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_CMY :
		  cupsImageRGBToCMY(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_CMYK :
		  cupsImageRGBToCMYK(in, out, img->ysize);
		  break;
	    }

	    if (lut)
	      cupsImageLut(out, img->ysize * bpp, lut);

            _cupsImagePutCol(img, x, 0, img->ysize, out);
	  }
        }
        break;

    case PHOTOMETRIC_RGB :
        if (orientation < ORIENTATION_LEFTTOP)
        {
         /*
          * Row major order...
          */

          for (y = ystart, ycount = img->ysize, row = 0;
               ycount > 0;
               ycount --, y += ydir, row ++)
          {
            if (bits == 1)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3, bit = 0xf0;
                   xcount > 0;
                   xcount --, p += pstep)
              {
        	if (*scanptr & bit & 0x88)
                  p[0] = 255;
                else
                  p[0] = 0;

        	if (*scanptr & bit & 0x44)
                  p[1] = 255;
                else
                  p[1] = 0;

        	if (*scanptr & bit & 0x22)
                  p[2] = 255;
                else
                  p[2] = 0;

        	if (bit == 0xf0)
                  bit = 0x0f;
        	else
        	{
                  bit = 0xf0;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 2)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
                   xcount > 0;
                   xcount --, p += pstep, scanptr ++)
              {
                pixel = *scanptr >> 2;
                p[0] = 255 * (pixel & 3) / 3;
                pixel >>= 2;
                p[1] = 255 * (pixel & 3) / 3;
                pixel >>= 2;
                p[2] = 255 * (pixel & 3) / 3;
              }
            }
            else if (bits == 4)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
                   xcount > 0;
                   xcount -= 2, p += 2 * pstep, scanptr += 3)
              {
                pixel = scanptr[0];
                p[1] = 255 * (pixel & 15) / 15;
                pixel >>= 4;
                p[0] = 255 * (pixel & 15) / 15;
                pixel = scanptr[1];
                p[2] = 255 * ((pixel >> 4) & 15) / 15;

                if (xcount > 1)
                {
                  p[pstep + 0] = 255 * (pixel & 15) / 15;
                  pixel = scanptr[2];
                  p[pstep + 2] = 255 * (pixel & 15) / 15;
                  pixel >>= 4;
                  p[pstep + 1] = 255 * (pixel & 15) / 15;
                }
              }
            }
            else if (xdir < 0 || alpha)
            {
              TIFFReadScanline(tif, scanline, row, 0);

              if (alpha)
	      {
        	for (xcount = img->xsize, p = in + xstart * 3, scanptr = scanline;
                     xcount > 0;
                     xcount --, p += pstep, scanptr += 4)
        	{
                  p[0] = (scanptr[0] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
                  p[1] = (scanptr[1] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
                  p[2] = (scanptr[2] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
        	}
              }
	      else
              {
	      	for (xcount = img->xsize, p = in + xstart * 3, scanptr = scanline;
                     xcount > 0;
                     xcount --, p += pstep, scanptr += 3)
        	{
                  p[0] = scanptr[0];
                  p[1] = scanptr[1];
                  p[2] = scanptr[2];
        	}
	      }
            }
            else
              TIFFReadScanline(tif, in, row, 0);

            if ((saturation != 100 || hue != 0) && bpp > 1)
              cupsImageRGBAdjust(in, img->xsize, saturation, hue);

	    switch (img->colorspace)
	    {
	      default :
		  break;

	      case CUPS_IMAGE_WHITE :
		  cupsImageRGBToWhite(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_RGB :
		  cupsImageRGBToRGB(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_BLACK :
		  cupsImageRGBToBlack(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_CMY :
		  cupsImageRGBToCMY(in, out, img->xsize);
		  break;
	      case CUPS_IMAGE_CMYK :
		  cupsImageRGBToCMYK(in, out, img->xsize);
		  break;
	    }

	    if (lut)
	      cupsImageLut(out, img->xsize * bpp, lut);

            _cupsImagePutRow(img, 0, y, img->xsize, out);
          }
        }
        else
        {
         /*
          * Column major order...
          */

          for (x = xstart, xcount = img->xsize, row = 0;
               xcount > 0;
               xcount --, x += xdir, row ++)
          {
            if (bits == 1)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline, p = in + ystart * 3, bit = 0xf0;
                   ycount > 0;
                   ycount --, p += pstep)
              {
        	if (*scanptr & bit & 0x88)
                  p[0] = 255;
                else
                  p[0] = 0;

        	if (*scanptr & bit & 0x44)
                  p[1] = 255;
                else
                  p[1] = 0;

        	if (*scanptr & bit & 0x22)
                  p[2] = 255;
                else
                  p[2] = 0;

        	if (bit == 0xf0)
                  bit = 0x0f;
        	else
        	{
                  bit = 0xf0;
                  scanptr ++;
        	}
              }
            }
            else if (bits == 2)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline, p = in + ystart * 3;
                   ycount > 0;
                   ycount --, p += pstep, scanptr ++)
              {
                pixel = *scanptr >> 2;
                p[0] = 255 * (pixel & 3) / 3;
                pixel >>= 2;
                p[1] = 255 * (pixel & 3) / 3;
                pixel >>= 2;
                p[2] = 255 * (pixel & 3) / 3;
              }
            }
            else if (bits == 4)
            {
              TIFFReadScanline(tif, scanline, row, 0);
              for (ycount = img->ysize, scanptr = scanline, p = in + ystart * 3;
                   ycount > 0;
                   ycount -= 2, p += 2 * pstep, scanptr += 3)
              {
                pixel = scanptr[0];
                p[1] = 255 * (pixel & 15) / 15;
                pixel >>= 4;
                p[0] = 255 * (pixel & 15) / 15;
                pixel = scanptr[1];
                p[2] = 255 * ((pixel >> 4) & 15) / 15;

                if (ycount > 1)
                {
                  p[pstep + 0] = 255 * (pixel & 15) / 15;
                  pixel = scanptr[2];
                  p[pstep + 2] = 255 * (pixel & 15) / 15;
                  pixel >>= 4;
                  p[pstep + 1] = 255 * (pixel & 15) / 15;
                }
              }
            }
            else if (ydir < 0 || alpha)
            {
              TIFFReadScanline(tif, scanline, row, 0);

              if (alpha)
	      {
		for (ycount = img->ysize, p = in + ystart * 3, scanptr = scanline;
                     ycount > 0;
                     ycount --, p += pstep, scanptr += 4)
        	{
                  p[0] = (scanptr[0] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
                  p[1] = (scanptr[1] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
                  p[2] = (scanptr[2] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
        	}
              }
	      else
	      {
		for (ycount = img->ysize, p = in + ystart * 3, scanptr = scanline;
                     ycount > 0;
                     ycount --, p += pstep, scanptr += 3)
        	{
                  p[0] = scanptr[0];
                  p[1] = scanptr[1];
                  p[2] = scanptr[2];
        	}
	      }
            }
            else
              TIFFReadScanline(tif, in, row, 0);

            if ((saturation != 100 || hue != 0) && bpp > 1)
              cupsImageRGBAdjust(in, img->ysize, saturation, hue);

	    switch (img->colorspace)
	    {
	      default :
		  break;

	      case CUPS_IMAGE_WHITE :
		  cupsImageRGBToWhite(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_RGB :
		  cupsImageRGBToRGB(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_BLACK :
		  cupsImageRGBToBlack(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_CMY :
		  cupsImageRGBToCMY(in, out, img->ysize);
		  break;
	      case CUPS_IMAGE_CMYK :
		  cupsImageRGBToCMYK(in, out, img->ysize);
		  break;
	    }

	    if (lut)
	      cupsImageLut(out, img->ysize * bpp, lut);

            _cupsImagePutCol(img, x, 0, img->ysize, out);
          }
        }
        break;

    case PHOTOMETRIC_SEPARATED :
        inkset  = INKSET_CMYK;
        numinks = 4;

#ifdef TIFFTAG_NUMBEROFINKS
        if (!TIFFGetField(tif, TIFFTAG_INKSET, &inkset) &&
	    !TIFFGetField(tif, TIFFTAG_NUMBEROFINKS, &numinks))
#else
        if (!TIFFGetField(tif, TIFFTAG_INKSET, &inkset))
#endif /* TIFFTAG_NUMBEROFINKS */
	{
          fputs("WARNING: No inkset or number-of-inks tag in the file!\n", stderr);
	}

	if (inkset == INKSET_CMYK || numinks == 4)
	{
          if (orientation < ORIENTATION_LEFTTOP)
          {
           /*
            * Row major order...
            */

            for (y = ystart, ycount = img->ysize, row = 0;
        	 ycount > 0;
        	 ycount --, y += ydir, row ++)
            {
              if (bits == 1)
              {
        	TIFFReadScanline(tif, scanline, row, 0);
        	for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3, bit = 0xf0;
                     xcount > 0;
                     xcount --, p += pstep)
        	{
        	  if (*scanptr & bit & 0x11)
        	  {
                    p[0] = 0;
                    p[1] = 0;
                    p[2] = 0;
                  }
                  else
                  {
        	    if (*scanptr & bit & 0x88)
                      p[0] = 0;
                    else
                      p[0] = 255;

        	    if (*scanptr & bit & 0x44)
                      p[1] = 0;
                    else
                      p[1] = 255;

        	    if (*scanptr & bit & 0x22)
                      p[2] = 0;
                    else
                      p[2] = 255;
                  }

        	  if (bit == 0xf0)
                    bit = 0x0f;
        	  else
        	  {
                    bit = 0xf0;
                    scanptr ++;
        	  }
        	}
              }
              else if (bits == 2)
              {
        	TIFFReadScanline(tif, scanline, row, 0);
        	for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
                     xcount > 0;
                     xcount --, p += pstep, scanptr ++)
        	{
        	  pixel = *scanptr;
        	  k     = 255 * (pixel & 3) / 3;
        	  if (k == 255)
        	  {
        	    p[0] = 0;
        	    p[1] = 0;
        	    p[2] = 0;
        	  }
        	  else
        	  {
                    pixel >>= 2;
                    b = 255 - 255 * (pixel & 3) / 3 - k;
                    if (b < 0)
                      p[2] = 0;
                    else if (b < 256)
                      p[2] = b;
                    else
                      p[2] = 255;

                    pixel >>= 2;
                    g = 255 - 255 * (pixel & 3) / 3 - k;
                    if (g < 0)
                      p[1] = 0;
                    else if (g < 256)
                      p[1] = g;
                    else
                      p[1] = 255;

                    pixel >>= 2;
                    r = 255 - 255 * (pixel & 3) / 3 - k;
                    if (r < 0)
                      p[0] = 0;
                    else if (r < 256)
                      p[0] = r;
                    else
                      p[0] = 255;
                  }
        	}
              }
              else if (bits == 4)
              {
        	TIFFReadScanline(tif, scanline, row, 0);
        	for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
                     xcount > 0;
                     xcount --, p += pstep, scanptr += 2)
        	{
        	  pixel = scanptr[1];
        	  k     = 255 * (pixel & 15) / 15;
        	  if (k == 255)
        	  {
        	    p[0] = 0;
        	    p[1] = 0;
        	    p[2] = 0;
        	  }
        	  else
        	  {
                    pixel >>= 4;
                    b = 255 - 255 * (pixel & 15) / 15 - k;
                    if (b < 0)
                      p[2] = 0;
                    else if (b < 256)
                      p[2] = b;
                    else
                      p[2] = 255;

                    pixel = scanptr[0];
                    g = 255 - 255 * (pixel & 15) / 15 - k;
                    if (g < 0)
                      p[1] = 0;
                    else if (g < 256)
                      p[1] = g;
                    else
                      p[1] = 255;

                    pixel >>= 4;
                    r = 255 - 255 * (pixel & 15) / 15 - k;
                    if (r < 0)
                      p[0] = 0;
                    else if (r < 256)
                      p[0] = r;
                    else
                      p[0] = 255;
                  }
        	}
              }
              else if (img->colorspace == CUPS_IMAGE_CMYK)
	      {
	        TIFFReadScanline(tif, scanline, row, 0);
		_cupsImagePutRow(img, 0, y, img->xsize, scanline);
	      }
	      else
              {
        	TIFFReadScanline(tif, scanline, row, 0);

        	for (xcount = img->xsize, p = in + xstart * 3, scanptr = scanline;
                     xcount > 0;
                     xcount --, p += pstep, scanptr += 4)
        	{
        	  k = scanptr[3];
        	  if (k == 255)
        	  {
        	    p[0] = 0;
        	    p[1] = 0;
        	    p[2] = 0;
        	  }
        	  else
        	  {
                    r = 255 - scanptr[0] - k;
                    if (r < 0)
                      p[0] = 0;
                    else if (r < 256)
                      p[0] = r;
                    else
                      p[0] = 255;

                    g = 255 - scanptr[1] - k;
                    if (g < 0)
                      p[1] = 0;
                    else if (g < 256)
                      p[1] = g;
                    else
                      p[1] = 255;

                    b = 255 - scanptr[2] - k;
                    if (b < 0)
                      p[2] = 0;
                    else if (b < 256)
                      p[2] = b;
                    else
                      p[2] = 255;
        	  }
        	}
              }

              if ((saturation != 100 || hue != 0) && bpp > 1)
        	cupsImageRGBAdjust(in, img->xsize, saturation, hue);

	      switch (img->colorspace)
	      {
		default :
		    break;

		case CUPS_IMAGE_WHITE :
		    cupsImageRGBToWhite(in, out, img->xsize);
		    break;
		case CUPS_IMAGE_RGB :
		    cupsImageRGBToRGB(in, out, img->xsize);
		    break;
		case CUPS_IMAGE_BLACK :
		    cupsImageRGBToBlack(in, out, img->xsize);
		    break;
		case CUPS_IMAGE_CMY :
		    cupsImageRGBToCMY(in, out, img->xsize);
		    break;
		case CUPS_IMAGE_CMYK :
		    cupsImageRGBToCMYK(in, out, img->xsize);
		    break;
	      }

	      if (lut)
	        cupsImageLut(out, img->xsize * 3, lut);

              _cupsImagePutRow(img, 0, y, img->xsize, out);
            }
          }
          else
          {
           /*
            * Column major order...
            */

            for (x = xstart, xcount = img->xsize, row = 0;
        	 xcount > 0;
        	 xcount --, x += xdir, row ++)
            {
              if (bits == 1)
              {
        	TIFFReadScanline(tif, scanline, row, 0);
        	for (ycount = img->ysize, scanptr = scanline, p = in + xstart * 3, bit = 0xf0;
                     ycount > 0;
                     ycount --, p += pstep)
        	{
        	  if (*scanptr & bit & 0x11)
        	  {
                    p[0] = 0;
                    p[1] = 0;
                    p[2] = 0;
                  }
                  else
                  {
        	    if (*scanptr & bit & 0x88)
                      p[0] = 0;
                    else
                      p[0] = 255;

        	    if (*scanptr & bit & 0x44)
                      p[1] = 0;
                    else
                      p[1] = 255;

        	    if (*scanptr & bit & 0x22)
                      p[2] = 0;
                    else
                      p[2] = 255;
                  }

        	  if (bit == 0xf0)
                    bit = 0x0f;
        	  else
        	  {
                    bit = 0xf0;
                    scanptr ++;
        	  }
        	}
              }
              else if (bits == 2)
              {
        	TIFFReadScanline(tif, scanline, row, 0);
        	for (ycount = img->ysize, scanptr = scanline, p = in + xstart * 3;
                     ycount > 0;
                     ycount --, p += pstep, scanptr ++)
        	{
        	  pixel = *scanptr;
        	  k     = 255 * (pixel & 3) / 3;
        	  if (k == 255)
        	  {
        	    p[0] = 0;
        	    p[1] = 0;
        	    p[2] = 0;
        	  }
        	  else
        	  {
                    pixel >>= 2;
                    b = 255 - 255 * (pixel & 3) / 3 - k;
                    if (b < 0)
                      p[2] = 0;
                    else if (b < 256)
                      p[2] = b;
                    else
                      p[2] = 255;

                    pixel >>= 2;
                    g = 255 - 255 * (pixel & 3) / 3 - k;
                    if (g < 0)
                      p[1] = 0;
                    else if (g < 256)
                      p[1] = g;
                    else
                      p[1] = 255;

                    pixel >>= 2;
                    r = 255 - 255 * (pixel & 3) / 3 - k;
                    if (r < 0)
                      p[0] = 0;
                    else if (r < 256)
                      p[0] = r;
                    else
                      p[0] = 255;
                  }
        	}
              }
              else if (bits == 4)
              {
        	TIFFReadScanline(tif, scanline, row, 0);
        	for (ycount = img->ysize, scanptr = scanline, p = in + xstart * 3;
                     ycount > 0;
                     ycount --, p += pstep, scanptr += 2)
        	{
        	  pixel = scanptr[1];
        	  k     = 255 * (pixel & 15) / 15;
        	  if (k == 255)
        	  {
        	    p[0] = 0;
        	    p[1] = 0;
        	    p[2] = 0;
        	  }
        	  else
        	  {
                    pixel >>= 4;
                    b = 255 - 255 * (pixel & 15) / 15 - k;
                    if (b < 0)
                      p[2] = 0;
                    else if (b < 256)
                      p[2] = b;
                    else
                      p[2] = 255;

                    pixel = scanptr[0];
                    g = 255 - 255 * (pixel & 15) / 15 - k;
                    if (g < 0)
                      p[1] = 0;
                    else if (g < 256)
                      p[1] = g;
                    else
                      p[1] = 255;

                    pixel >>= 4;
                    r = 255 - 255 * (pixel & 15) / 15 - k;
                    if (r < 0)
                      p[0] = 0;
                    else if (r < 256)
                      p[0] = r;
                    else
                      p[0] = 255;
                  }
        	}
              }
              else if (img->colorspace == CUPS_IMAGE_CMYK)
	      {
	        TIFFReadScanline(tif, scanline, row, 0);
		_cupsImagePutCol(img, x, 0, img->ysize, scanline);
	      }
              else
              {
        	TIFFReadScanline(tif, scanline, row, 0);

        	for (ycount = img->ysize, p = in + xstart * 3, scanptr = scanline;
                     ycount > 0;
                     ycount --, p += pstep, scanptr += 4)
        	{
        	  k = scanptr[3];
        	  if (k == 255)
        	  {
        	    p[0] = 0;
        	    p[1] = 0;
        	    p[2] = 0;
        	  }
        	  else
        	  {
                    r = 255 - scanptr[0] - k;
                    if (r < 0)
                      p[0] = 0;
                    else if (r < 256)
                      p[0] = r;
                    else
                      p[0] = 255;

                    g = 255 - scanptr[1] - k;
                    if (g < 0)
                      p[1] = 0;
                    else if (g < 256)
                      p[1] = g;
                    else
                      p[1] = 255;

                    b = 255 - scanptr[2] - k;
                    if (b < 0)
                      p[2] = 0;
                    else if (b < 256)
                      p[2] = b;
                    else
                      p[2] = 255;
        	  }
        	}
              }

              if ((saturation != 100 || hue != 0) && bpp > 1)
        	cupsImageRGBAdjust(in, img->ysize, saturation, hue);

	      switch (img->colorspace)
	      {
		default :
		    break;

		case CUPS_IMAGE_WHITE :
		    cupsImageRGBToWhite(in, out, img->ysize);
		    break;
		case CUPS_IMAGE_RGB :
		    cupsImageRGBToRGB(in, out, img->ysize);
		    break;
		case CUPS_IMAGE_BLACK :
		    cupsImageRGBToBlack(in, out, img->ysize);
		    break;
		case CUPS_IMAGE_CMY :
		    cupsImageRGBToCMY(in, out, img->ysize);
		    break;
		case CUPS_IMAGE_CMYK :
		    cupsImageRGBToCMYK(in, out, img->ysize);
		    break;
	      }

	      if (lut)
	        cupsImageLut(out, img->ysize * bpp, lut);

              _cupsImagePutCol(img, x, 0, img->ysize, out);
            }
          }

          break;
	}

    default :
	_TIFFfree(scanline);
	free(in);
	free(out);

	TIFFClose(tif);
	fputs("DEBUG: Unknown TIFF photometric value!\n", stderr);
	return (-1);
  }

 /*
  * Free temporary buffers, close the TIFF file, and return.
  */

  _TIFFfree(scanline);
  free(in);
  free(out);

  TIFFClose(tif);
  return (0);
}
#endif /* HAVE_LIBTIFF */

