/*****************************************************************
 * fltiff.c: FBM Release 1.2 24-Mar-93 Michael Mauldin
 *
 * Copyright (C) 1993 by Michael Mauldin.  Permission is granted
 * to use this file in whole or in part for any purpose, educational,
 * recreational or commercial, provided that this copyright notice
 * is retained unchanged.  This software is available to all free of
 * charge by anonymous FTP and in the UUNET archives.
 *
 * fltiff.c: 
 *
 * CONTENTS
 *	read_tiff (image, fname)
 *	write_tiff (image, wfile, graybits)
 *
 * EDITLOG
 *	LastEditDate = Mon Jun 25 00:18:04 1990 - Michael Mauldin
 *	LastFileName = /usr2/mlm/src/misc/fbm/fltiff.c
 *
 * HISTORY
 * 24-Mar-93  Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon
 *	Created from flsun.c and tiff2fbm.c, fbm2tiff.c
 *****************************************************************/

# include <stdio.h>
# include <math.h>
# include <ctype.h>
# include <stdio.h>
# include <ctype.h>
# ifdef BYTE
# undef BYTE
# endif
# include <sys/types.h>
# include <tiff.h>
# include <tiffio.h>
# include <tiffioP.h>
# include "fbm.h"

# define RED 0
# define GRN 1
# define BLU 2


# ifdef __STDC__
#   define FIELD(tif,f)	TIFFFieldSet(tif, FIELD_ ## f)
# else
    /* The following macro is taken from tiff_print.c */
#   define FIELD(tif,f)	TIFFFieldSet(tif, FIELD_/**/f)
# endif

#ifndef howmany
# define	howmany(x, y)	(((x)+((y)-1))/(y))
#endif
#define SCALE(x)        (((x)*((1L<<16)-1))/256)

/****************************************************************
 * fbmid:
 ****************************************************************/

#ifndef lint
static char *fbmid =
"$FBM fltiff.c <1.2> 24-Mar-93  (C) 1993 by Michael Mauldin, source \
code available free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif

/****************************************************************
 * write_tiff (image, fname, graybit)
 ****************************************************************/

write_tiff (image, fname, graybit)
FBM *image;
char *fname;
int graybit;
{ int width, height, rowlen, planes, plnlen, k;
  int failed = 0;

  u_short	config = PLANARCONFIG_CONTIG;
  u_short	compression = COMPRESSION_NONE;
  u_short	rowsperstrip = 0;

  u_char *bmp, *obm, *scanbuf;
  int row, linebytes;
  TIFF *out;


  /* Check for bad argument to reduced resolution argument */
  switch (graybit)
  { case 1: case 2: case 4:
		/* These are standard values */
		break;

    case 0: case 8:
		/* These mean no reduced resolution */
		graybit = 0; break;

    default:	fprintf (stderr,
		         "Error: graybit value must be 0, 1, 2, or 4\n");
		return (0);
  }

  width = image->hdr.cols;
  height = image->hdr.rows;
  rowlen = image->hdr.rowlen;
  planes = image->hdr.planes;
  plnlen = image->hdr.plnlen;

  /*-Start of Tiff writing code-------------------------------------*/
  if (!(out = TIFFOpen (fname, "w")))
  { return (0); }

  TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
  TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
  TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, graybit ? 1:image->hdr.planes);
  TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, graybit ? graybit:image->hdr.bits);
  TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
  TIFFSetField(out, TIFFTAG_PLANARCONFIG, config);

  if (image->hdr.clrlen > 0 && !graybit)
  { register u_short *red;
    register int i, j;
    int mapsize, cbytes, nclrs;

    mapsize = 1<<image->hdr.bits;
    if (image->hdr.clrlen > mapsize*3)
    { fprintf(stderr,
	      "stdin: Huh, %d colormap entries, should be %d?\n",
	      image->hdr.clrlen, mapsize*3);
      exit(-7);
    }
    
    cbytes = mapsize * 3 * sizeof (u_short);
    nclrs = image->hdr.clrlen/3;

    if ((red = (u_short *) malloc(cbytes)) == NULL)
    { perror ("colormap"); exit (-8); }

    /* XXX -- set pointers up before we step through arrays */ 
    TIFFSetField(out, TIFFTAG_COLORMAP,
		 red, red + mapsize, red + 2*mapsize);
    bmp = image->cm;

    for (j = 0; j < 3; j++)
    { for (i=0; i < nclrs; i++)	*red++ = SCALE(*bmp++);
      for (; i<mapsize; i++)	*red++ = 0;
    }

    TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
  }
  else
  { /* XXX this is bogus... */
    TIFFSetField(out, TIFFTAG_PHOTOMETRIC,
		 (image->hdr.planes == 3) && !graybit ? 
		 PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK);
  }

  TIFFSetField(out, TIFFTAG_COMPRESSION, compression);

  linebytes = (((image->hdr.bits * width+15) >> 3) &~ 1) * planes;

  if (TIFFScanlineSize(out) < linebytes)
      scanbuf = (u_char *)malloc(linebytes);
  else
      scanbuf = (u_char *)malloc(TIFFScanlineSize(out));

  if (scanbuf == NULL)
  { perror ("mallocing scanbuf"); exit (1); }

  if (rowsperstrip != (u_short)-1)
      rowsperstrip = (8*1024)/linebytes;
  TIFFSetField(out, TIFFTAG_ROWSPERSTRIP,
      rowsperstrip == 0 ? 1 : rowsperstrip);

  /* Handle bitmaps first */
  if (image->hdr.bits == 1 && image->hdr.physbits == 8)
  { int byte = 0, i;
    u_char *obm;
    
    for (row = 0; row < height; row++)
    { byte = 0;
      bmp = &image->bm[row*rowlen];
      obm = scanbuf;

      /* Write out each group of 8 bytes as one byte */
      for (i=0; i<width; i++)
      { byte = (byte << 1) | (*bmp++ ? 1 : 0);
	if ((i&7) == 7)
	{ *obm++ = byte; byte=0; }
      }

      /* Handle stragglers if width not multiple of 8 */
      if (i&7)
      { byte <<= (8 - (i&7));
	*obm = byte;
      }

      if (TIFFWriteScanline(out, scanbuf, row, 0) < 0)
      { failed++; break; }
    }
  }

  /* Handle reduced level grayscale */
  else if (graybit && image->hdr.bits == 8)
  { int byte, mask, spb, shift, i; /* spb: Samples per byte mask */
    u_char *obm;

    switch (graybit)
    { case 1:	mask = 0x80; spb = 7; shift = 7; break;
      case 2:	mask = 0xc0; spb = 3; shift = 6; break;
      case 4:	mask = 0xf0; spb = 1; shift = 4; break;
    }

    for (row = 0; row < height; row++)
    { byte = 0;
      bmp = &image->bm[row*rowlen];
      obm = scanbuf;

      /* Write out each group of 8 bytes as one byte */
      for (i=0; i<width; )
      { byte = (byte << graybit) | ((*bmp++ & mask) >> shift);

	if ((++i & spb) == 0)
	{ *obm++ = byte; byte=0; }
      }

      /* Handle stragglers if width not multiple of 8 */
      if (i&spb)
      { while (i++ & spb)
	{ byte <<= graybit; }
	*obm = byte;
      }

      if (TIFFWriteScanline(out, scanbuf, row, 0) < 0)
      { failed++; break; }
    }
  }
  
  /* Catch cases we cant handle */
  else if (image->hdr.physbits != 8)
  { fprintf (stderr, "Error: cannot handle %d physical bits per pixel\n",
	     image->hdr.physbits);
    (void) TIFFClose(out);
    return (0);
  }

  /* Handle 8bit grayscale or 24bit rgb */
  else
  { fprintf (stderr, "Writing %d bit output, height %d, width %d, ",
	      planes * image->hdr.physbits, height, width);
    fprintf (stderr, "planes %d, linebytes %d. plnlen %d, rowlen %d\n",
	      planes, linebytes, plnlen, rowlen);

    for (row = 0; row < height; row++)
    { for (k=0; k<planes; k++)
      { register int i, j, oi;

	for (i=0; i<width; i++)
	{ oi = i*planes + k;
	  if (oi >= linebytes)
	  { fprintf (stderr, "Overran scanbuf...\n");
	    abort();
	  }
	  scanbuf[oi] = image->bm[k*plnlen + row*rowlen + i];
	}
      }

      if (TIFFWriteScanline(out, scanbuf, row, 0) < 0)
      { failed++; break; }
    }
  }

  (void) TIFFClose(out);
  
  if (failed)
  { fprintf (stderr, "Could not write tiff file\n"); return (0); }


  return (1);
}

/****************************************************************
* read_tiff (image, rfile)
****************************************************************/

read_tiff (image, fname)
FBM *image;
char *fname;
{
  int userdir = 1, fw, fh, rowlen, plnlen, clrlen=0, colors=0;
  double aspect = 0.0;
  char *title = NULL, *credits = NULL;

  TIFF *tif;
  TIFFDirectory *td;
  u_char *scanbuf, *tail;
  register u_char *pp, *obm, *tmp;
  int bitspersample, scanline, samplesperbyte;
  register unsigned int mask;
  register int shift, nib;
  register int poff, k, planes;
  int i, j, w, h, dirnum = 0;

  /* Clear the memory pointers so alloc_fbm won't be confused */
  image->cm  = image->bm  = (unsigned char *) NULL;

  /* Now read in the TIFF format image */

  if ((tif = TIFFOpen(fname, "r")) == NULL)
  { return (0); }

  do
  { ++dirnum;

    if (userdir != dirnum) continue;

    td = &tif->tif_dir;
    bitspersample = td->td_bitspersample;
    planes = td->td_samplesperpixel;

    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
    { bitspersample *= planes; }

    scanline = howmany (bitspersample * td->td_imagewidth, 8);
    scanbuf = (u_char *) malloc (scanline);
    if (scanbuf == NULL)
    { printf ("No space for scanline buffer\n");
      continue;
    }

    w = td->td_imagewidth;
    h = td->td_imagelength;

    switch (bitspersample)
    { case 1:	samplesperbyte = 8; break;
      case 2:	samplesperbyte = 4; break;
      case 4:	samplesperbyte = 2; break;
      case 8:	samplesperbyte = 1; break;
      case 24:	samplesperbyte = 1; break;
      default:	fprintf (stderr, "I can't handle %d bits per sample\n",
			bitspersample);
		if (tif) TIFFClose (tif);
		return (0);
    }

    /* Now build FBM image header */
    
    if (bitspersample == 1)
    { rowlen = 16 * ((w+15)/16); }
    else
    { rowlen = 2 * ((w+1)/2); }

    plnlen = rowlen * h;

    /* Build header */
    image->hdr.title[0] = image->hdr.credits[0] = '\0';
    
    image->hdr.rows = h;
    image->hdr.cols = w;
    image->hdr.planes = planes;
    image->hdr.bits = bitspersample / planes;
    image->hdr.physbits = 8;
    image->hdr.rowlen = rowlen;
    image->hdr.plnlen = plnlen;
    colors = FIELD (tif,COLORMAP) ? (1 << td->td_bitspersample) : 0;
    image->hdr.clrlen = colors * 3;


    /* Determine aspect from X and Y resolution */
    if (FIELD (tif,RESOLUTION) && td->td_yresolution != 0)
    { image->hdr.aspect = td->td_xresolution / td->td_yresolution; }
    else
    { image->hdr.aspect = 1.0; }

    /* Extract Title and Credit information */
    if (FIELD (tif,DOCUMENTNAME))
    { strcpy (image->hdr.title, td->td_documentname); }
    else if (FIELD (tif,IMAGEDESCRIPTION))
    { strcpy (image->hdr.title, td->td_imagedescription); }
    else
    { strcpy (image->hdr.title, fname); }
    
    if (FIELD (tif,ARTIST))
    { strcpy (image->hdr.credits, td->td_artist); }
    else if (FIELD (tif,SOFTWARE))
    { strcpy (image->hdr.credits, td->td_software); }
    else if (FIELD (tif,MAKE))
    { strcpy (image->hdr.credits, td->td_make);
      if (FIELD (tif,MODEL))
      { strcat (image->hdr.credits, ", ");
        strcat (image->hdr.credits, td->td_model);
      }
    }

    fprintf (stderr, "Reading \"%s\" [%dx%d], %d bits, directory %d\n",
	     image->hdr.title, w, h, bitspersample, userdir);

    alloc_fbm (image);

    /* Read colormap: Note TIFF colors are 16 bit, FBM colors 8 bit */
    if (FIELD (tif,COLORMAP))
    { for (i=0; i<colors; i++)
      { image->cm[i]			= td->td_colormap[0][i] >> 8;
        image->cm[i + colors]		= td->td_colormap[1][i] >> 8;
        image->cm[i + colors + colors]	= td->td_colormap[2][i] >> 8;
      }
    }

    /* Check for multiplane images */
    if (td->td_planarconfig == PLANARCONFIG_SEPARATE &&
        planes > 1)
    { fprintf (stderr, "Multiplane images (%d) not yet implemented\n",
	       planes);
      if (tif) TIFFClose (tif);
      return (0);
    }

    else
    {
      /*
       * Read single plane images (for RGB color, values are stored in 
       * successive bytes)
       */
  
      /* Precompute mask and shift parameters */
      switch (samplesperbyte)
      { case 8:	mask = 0x01; shift = 1; break;
	case 4:	mask = 0x03; shift = 2; break;
	case 2:	mask = 0x0f; shift = 4; break;
	case 1:	mask = 0xff; shift = 8; break;
      }
  
      fprintf (stderr,
	  "Samples per byte %d, mask %02x, shift %d, planes %d, scanline %d\n", 
		samplesperbyte, mask, shift, planes, scanline);

      /* Each loop does one scan line */
      for (j=0; j<h; j++)
      { if (TIFFReadScanline (tif, scanbuf, j, 0) < 0)
	{ fprintf (stderr, "Error: only read %d lines\n", j); break; }
    
	/* If 0 is WHITE, then flip all bits in the scanline */
	if (td->td_photometric == PHOTOMETRIC_MINISWHITE)
	{ for (pp = scanbuf, i=scanline; --i >= 0; pp++)
	  { *pp = ~ *pp; }
	}
    
	pp = scanbuf;
	obm = &image->bm[j * rowlen];
	tmp = obm;
	tail = obm + w;
        nib = 8 - shift;
    
	if (samplesperbyte > 1)
	{ nib = 8 - shift;
	  for (; obm < tail; obm++)
	  { for (k=0, poff=0; k<planes; k++, poff += plnlen)
	    { obm[poff] = (*pp >> nib) & mask;
# ifdef DEBUG
	      if (j< 10 && (obm-tmp)< 18)
	      { fprintf (stderr, "<%2d,%2d>  obm[%d] = %d, nib %d, mask %d, pp %08x\n",
			(obm-tmp), j, poff, obm[poff], nib, mask, pp);
	      }
# endif
	      if ((nib -= shift) < 0) { pp++; nib = 8 - shift; }
	    }
	  }
	}
    
	else
	{ for (; obm < tail; obm++)
	  { for (k=0, poff=0; k<planes; k++, poff += plnlen)
	    { obm[poff] = *pp++; }
	  }
	}
      }
    }
  } while (TIFFReadDirectory (tif));


  /* ---- Close files and check to make sure we read an image ---- */
  if (tif) TIFFClose (tif);

  /* Check whether an image was read */
  if (userdir > dirnum)
  { if (dirnum > 0)
    { fprintf (stderr, "File %s has only %d directories, %d out of range\n",
		fname, dirnum, userdir);
    }
    else
    { fprintf (stderr, "File %s has no directories\n", fname); }
    
    return (0);
  }

  return (1);
}
