/**********************************************************************
 *     EPSCAN, an EPSON ES-1200 scanner driver     
 *     Adam P. Jenkins <ajenkins@cs.umass.edu>
 *     
 *     This program is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License
 *     as published by the Free Software Foundation; either version 2
 *     of the License, or (at your option) any later version.
 *     
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *     
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/

// Adam P. Jenkins <ajenkins@cs.umass.edu>
// To use this code, call initTiffHandler().  This adds a handler for reading
// and writing TIFF files into a QImage, and QPixmap.


#include <qimage.h>

#include <tiffio.h>

#if 0
static void
tiffPutContig(TIFFRGBAImage* img, uint32* raster,
    uint32 x, uint32 y, uint32 w, uint32 h,
    int32 fromskew, int32 toskew,
    unsigned char* cp)
{
   uchar *p = (uchar *)raster;
   int tifscanline = TIFFScanlineSize(img->tif);
   int outscanline;
   
   switch (img->bitspersample) {
    case 1:
    case 8:
      outscanline = ((tifscanline + 3) / 4) * 4;
      break;
    case 4:
      outscanline = ((tifscanline*2 + 3) / 4) * 4;
      break;
    default:
      TIFFError(__FUNCTION__, "Invalid bpp for tiff image");
      return;
   }
   
   for (int i = 0; i < h; i++) {
      switch (img->bitspersample) {
       case 1:
       case 8:
	 memcpy(p, cp, w);
	 break;
       case 4:
	 for (int j = 0; j < w/2; j++) {
	    p[j*2]     = (cp[j] >> 4) & 0xf;
	    p[j*2 + 1] = cp[j] & 0xf;
	 }
	 break;
      }
      cp += tifscanline;
      p += outscanline;
   }
}
#endif

static void
read_tiff_image(QImageIO *iio)
{
  TIFF *inimg;
  QImage outimg;

  iio->setStatus(1);		// reset to 0 if all goes well
  
  inimg = TIFFOpen(iio->fileName(), "r");
  if (!inimg)	{		// failed to open file
     TIFFError(__FUNCTION__, "Failed to open tif file");
     return;
  }
  
  // read pixels.
  uint32
    width,
    height;

  TIFFGetField(inimg, TIFFTAG_IMAGEWIDTH, &width);
  TIFFGetField(inimg, TIFFTAG_IMAGELENGTH, &height);

  outimg.create(width, height,
		32,		// depth
		0,		// number of colors in palette
		QImage::IgnoreEndian);
  if (outimg.isNull()) {
     TIFFClose(inimg);
     return;
  }
  
  uchar *pixels = outimg.bits();
  TIFFRGBAImage img;
  char emsg[1024];

  if (TIFFRGBAImageBegin(&img, inimg, 1, emsg)) {
     img.orientation = ORIENTATION_BOTLEFT;
     if (TIFFRGBAImageGet(&img, (uint32 *)pixels, img.width, img.height))
       TIFFRGBAImageEnd(&img);
     else {
	TIFFError(__FUNCTION__, "Failed to read image");
	TIFFClose(inimg);
	return;
     }
  } else {
     TIFFError(__FUNCTION__, emsg);
     TIFFClose(inimg);
     return;
  }
  
  TIFFClose(inimg);
  iio->setStatus(0);		// ok
  iio->setImage(outimg);
}

static void
write_tiff_image(QImageIO *iio)
{
   QImage inimg = iio->image();
   TIFF *outimg;
   const int ROWSPERSTRIP = 1;

   iio->setStatus(1);		// we'll clear it to 0 if all goes well

   outimg = TIFFOpen(iio->fileName(), "w");
   if (!outimg) {
      TIFFError(__FUNCTION__, "Failed to open tiff image");
      return;
   }

   uint16
     bps,
     spp,
     photo;

   if (inimg.depth() == 32) {
      bps = 8;
      spp = 3;
      photo = PHOTOMETRIC_RGB;
   } else {			// depth == 8 or 1
      bps = inimg.depth();
      spp = 1;
      if (bps == 8)
	photo = PHOTOMETRIC_PALETTE;
      else {			// bps == 1
	 if (inimg.color(0) < inimg.color(1))
	   photo = PHOTOMETRIC_MINISBLACK;
	 else
	   photo = PHOTOMETRIC_MINISWHITE;
      }
   }
   
   TIFFSetField(outimg, TIFFTAG_BITSPERSAMPLE, bps);
   TIFFSetField(outimg, TIFFTAG_IMAGEWIDTH, inimg.width());
   TIFFSetField(outimg, TIFFTAG_IMAGELENGTH, inimg.height());
   TIFFSetField(outimg, TIFFTAG_SAMPLESPERPIXEL, spp);
   TIFFSetField(outimg, TIFFTAG_PHOTOMETRIC, photo);
   TIFFSetField(outimg, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
   TIFFSetField(outimg, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
   TIFFSetField(outimg, TIFFTAG_ROWSPERSTRIP, ROWSPERSTRIP);
   TIFFSetField(outimg, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
   
   if (photo == PHOTOMETRIC_PALETTE) {	// create colormap
      uint16 *r, *g, *b;
      r = new uint16[inimg.numColors()];
      g = new uint16[inimg.numColors()];
      b = new uint16[inimg.numColors()];
      QRgb *c = inimg.colorTable();

      for (int i = 0; i < inimg.numColors(); i++) {
	 r[i] = (qRed(c[i]) * 0xffff) / 0xff;
	 g[i] = (qGreen(c[i]) * 0xffff) / 0xff;
	 b[i] = (qBlue(c[i]) * 0xffff) / 0xff;
      }
      TIFFSetField(outimg, TIFFTAG_COLORMAP, r, g, b);
      delete [] r;
      delete [] g;
      delete [] b;
   }

   if (inimg.depth() == 1 && inimg.bitOrder() != QImage::BigEndian)
     inimg = inimg.convertBitOrder(QImage::BigEndian);

   uchar **line = inimg.jumpTable();
   
   switch (inimg.depth()) {
    case 1:
    case 8:
      for (int row = 0; row < inimg.height(); row++) {
	 if (TIFFWriteScanline(outimg, (tdata_t)line[row],
			       (uint32)row, 0) < 0) {
	    return;
	 }
      }
      break;
    case 32:
    {
       tdata_t buf = _TIFFmalloc(TIFFScanlineSize(outimg));
       uchar *bufp;
       uint32 *l;

       for (int row = 0; row < inimg.height(); row++) {
	  l = (uint32 *)(line[row]);
	  bufp = (uchar *)buf;
	  for (int p = 0; p < inimg.width(); p++) {
	     bufp[0] = qRed(l[p]);
	     bufp[1] = qGreen(l[p]);
	     bufp[2] = qBlue(l[p]);
	     bufp += 3;
	  }
	  if (TIFFWriteScanline(outimg, buf, (uint32)row, 0) < 0) {
	     _TIFFfree(buf);
	     return;
	  }
       }
       _TIFFfree(buf);
    }
   }

   TIFFClose(outimg);
   iio->setStatus(0);		// ok
}

void
initTiffHandler()
{
  // initialize load routines
  QImageIO::defineIOHandler("TIFF", "^[MI][MI]", 0,
			    read_tiff_image, write_tiff_image);
}
