/*****************************************************************
 * fbm2tiff.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
 *
 * Copyright (C) 1989,1990 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.
 *
 * fbm2tiff.c: 
 *	Convert an FBM format image to TIFF format.  Uses Sam Leffler's
 *	libtiff.a TIFF image library to write TIFF format.  See also,
 *	tiff2fbm for the opposite conversion.
 *
 * USAGE
 *	% fbm2tiff [ image ] > foo.tif
 *
 * EDITLOG
 *	LastEditDate = Mon Jun 25 00:03:33 1990 - Michael Mauldin
 *	LastFileName = /usr2/mlm/src/misc/fbm/fbm2tiff.c
 *
 * HISTORY
 * 25-Jun-90  Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon
 *	Package for Release 1.0
 *
 * 13-Jun-90  Michael Mauldin (mlm) at Carnegie-Mellon University
 *	Created.
 *****************************************************************/

# include <stdio.h>
# include <ctype.h>
# ifdef BYTE
# undef BYTE
# endif
# include <tiff.h>
# include <tiffio.h>
# include "fbm.h"

# 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

#define	howmany(x, y)	(((x)+((y)-1))/(y))
#define	streq(a,b)	(strcmp(a,b) == 0)
#define SCALE(x)        (((x)*((1L<<16)-1))/256)

# define USAGE \
"Usage: fbm2tiff [-N] [-<compression>] [-r<rowsperstrip>] foo.tif < foo.fbm\n\
       -l=lzw, -p=packbits, -n=none"

/****************************************************************
 * main
 ****************************************************************/

#ifndef lint
static char *fbmid =
"$FBM fbm2tiff.c <1.0> 25-Jun-90  (C) 1989,1990 by Michael Mauldin, source \
code available free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif

main (argc, argv)
char *argv[];
{ FBM image;
  int width, height, rowlen, planes, plnlen, k;
  int graybit=0;
  int failed = 0;

  u_short	config = PLANARCONFIG_CONTIG;
  u_short	compression = COMPRESSION_NONE;
  u_short	rowsperstrip = -1;

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

  /* Get the options */
  while (--argc > 0 && (*++argv)[0] == '-')
  { while (*++(*argv))
    { switch (**argv)
      { case 'n':	compression = COMPRESSION_NONE; break;
	case 'p':	compression = COMPRESSION_PACKBITS; break;
	case 'l':	compression = COMPRESSION_LZW; break;
	case 'r':	rowsperstrip = atoi (*argv+1); SKIPARG; break;
	case 'N':	graybit = 2; compression = COMPRESSION_NONE; break;
	case 'g':	graybit = atoi (*argv+1); SKIPARG; break;
	default:        fprintf (stderr, "%s\n", USAGE);
                        exit (1);
      }
    }
  }

  /* 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 vvalue must be 0, 1, 2, or 4\n");
		exit (1);
  }

  image.cm  = image.bm  = (unsigned char *) NULL;

  /* Now read in an FBM image and write a TIFF format file */
  if (!read_bitmap (&image, (char *) NULL))
  { exit (1); }
  else
  { width = image.hdr.cols;
    height = image.hdr.rows;
    rowlen = image.hdr.rowlen;
    planes = image.hdr.planes;
    plnlen = image.hdr.plnlen;

    fprintf (stderr, "input [%dx%d], %d colors, %d bits, %d physbits\n",
	     width, height, image.hdr.clrlen/3, 
	     image.hdr.bits, image.hdr.physbits);

    /*-Start of Tiff writing code-------------------------------------*/
    if (!(out = TIFFOpen(argv[0], "w")))
    { exit(-4); }

    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;

      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);
      }

      if ((red = (u_short *)malloc(mapsize * 3 * sizeof (u_short))) == 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 = image.hdr.clrlen; i-- > 0;)
        { *red++ = SCALE(*bmp++); }
        if ((i = image.hdr.clrlen/3) < mapsize)
        { i = mapsize - i;
          bzero(red, i*sizeof (u_short));
          red += i;
        }
      }
      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;

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

    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);
      exit (1);
    }

    /* Handle 8bit grayscale or 24bit rgb */
    else
    { for (row = 0; row < height; row++)
      { for (k=0; k<planes; k++)
        { bcopy (&image.bm[k*plnlen + row*rowlen], scanbuf + k*width, width); }

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

    (void) TIFFClose(out);
    
    if (failed)
    { exit (1); }
  }

  exit (0);
}
