/*
** pnmtotiff.c - converts a portable anymap to a Tagged Image File
**
** Derived by Jef Poskanzer from ras2tif.c, which is:
**
** Copyright (c) 1990 by Sun Microsystems, Inc.
**
** Author: Patrick J. Naughton
** naughton@wind.sun.com
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted,
** provided that the above copyright notice appear in all copies and that
** both that copyright notice and this permission notice appear in
** supporting documentation.
**
** This file is provided AS IS with no warranties of any kind.  The author
** shall have no liability with respect to the infringement of copyrights,
** trade secrets or any patents by this file or any part thereof.  In no
** event will the author be liable for any lost revenue or profits or
** other special, indirect and consequential damages.
*/

#include "pnm.h"
#ifdef VMS
#ifdef SYSV
#undef SYSV
#endif
#include <tiffioP.h>
#endif
#include <tiffio.h>

#include "ppmcmap.h"
#define MAXCOLORS 256

int
main( argc, argv )
    int argc;
    char* argv[];
    {
    int argn;
    char* inf = NULL;
    FILE* ifp;
    xel** xels;
    register xel* xP;
    colorhist_vector chv;
    colorhash_table cht;
    unsigned short red[MAXCOLORS], grn[MAXCOLORS], blu[MAXCOLORS];
    int cols, rows, format, row, colors, i;
    register int col;
    xelval maxval;
    int grayscale;
    TIFF* tif;
    long g3options;
    long rowsperstrip;
    unsigned short compression;
    unsigned short fillorder;
    short predictor;
    short photometric;
    short samplesperpixel;
    short bitspersample;
    int bytesperrow;
    unsigned char* buf;
    unsigned char* tP;
    char* usage = "[-none|-packbits|-lzw|-g3|-g4] [-msb2lsb|-lsb2msb] [-2d] [-fill] [-predictor n] [-rowsperstrip n] [pnmfile]";


    pnm_init( &argc, argv );

    argn = 1;
    compression = COMPRESSION_LZW;
    g3options = 0;
    fillorder = FILLORDER_MSB2LSB;
    predictor = 0;
    rowsperstrip = 0;

    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
	{
	if ( pm_keymatch( argv[argn], "-none", 2 ) )
	    compression = COMPRESSION_NONE;
	else if ( pm_keymatch( argv[argn], "-packbits", 3 ) )
	    compression = COMPRESSION_PACKBITS;
	else if ( pm_keymatch( argv[argn], "-lzw", 3 ) )
	    compression = COMPRESSION_LZW;
	else if ( pm_keymatch( argv[argn], "-g3", 3 ) )
	    compression = COMPRESSION_CCITTFAX3;
	else if ( pm_keymatch( argv[argn], "-g4", 3 ) )
	    compression = COMPRESSION_CCITTFAX4;
	else if ( pm_keymatch( argv[argn], "-msb2lsb", 3 ) )
	    fillorder = FILLORDER_MSB2LSB;
	else if ( pm_keymatch( argv[argn], "-lsb2msb", 3 ) )
	    fillorder = FILLORDER_LSB2MSB;
	else if ( pm_keymatch( argv[argn], "-2d", 2 ) )
	    g3options |= GROUP3OPT_2DENCODING;
	else if ( pm_keymatch( argv[argn], "-fill", 2 ) )
	    g3options |= GROUP3OPT_FILLBITS;
	else if ( pm_keymatch( argv[argn], "-predictor", 3) )
	    {
	    ++argn;
	    if ( argn == argc || sscanf( argv[argn], "%hd", &predictor ) != 1 )
		pm_usage( usage );
	    if ( predictor != 1 && predictor != 2 )
		pm_usage( usage );
	    }
	else if ( pm_keymatch( argv[argn], "-rowsperstrip", 2 ) )
	    {
	    ++argn;
	    if ( argn == argc ||
		 sscanf( argv[argn], "%ld", &rowsperstrip ) != 1 )
		pm_usage( usage );
	    if ( rowsperstrip < 1 )
		pm_usage( usage );
	    }
	else
	    pm_usage( usage );
	++argn;
	}

    if ( argn != argc )
	{
	inf = argv[argn];
	ifp = pm_openr( inf );
	++argn;
	}
    else
	{
	inf = "Standard Input";
	ifp = stdin;
	}

    if ( argn != argc )
	pm_usage( usage );

    xels = pnm_readpnm( ifp, &cols, &rows, &maxval, &format );
    pm_close( ifp );

    /* Check for grayscale. */
    switch ( PNM_FORMAT_TYPE(format) )
	{
	case PPM_TYPE:
	pm_message( "computing colormap..." );
	chv = ppm_computecolorhist( xels, cols, rows, MAXCOLORS, &colors );
	if ( chv == (colorhist_vector) 0 )
	    {
	    pm_message(
		"Too many colors - proceeding to write a 24-bit RGB file." );
	    pm_message(
		"If you want an 8-bit palette file, try doing a 'ppmquant %d'.",
		MAXCOLORS );
	    grayscale = 0;
	    }
	else
	    {
	    pm_message( "%d colors found", colors );
	    grayscale = 1;
	    for ( i = 0; i < colors; ++i )
		{
		register xelval r, g, b;

		r = PPM_GETR( chv[i].color );
		g = PPM_GETG( chv[i].color );
		b = PPM_GETB( chv[i].color );
		if ( r != g || g != b )
		    {
		    grayscale = 0;
		    break;
		    }
		}
	    }
	break;

	default:
	chv = (colorhist_vector) 0;
	grayscale = 1;
	break;
	}

    /* Open output file. */
    tif = TIFFFdOpen( 1, "Standard Output", "w" );
    if ( tif == NULL )
	pm_error( "error opening standard output as TIFF file" );

    /* Figure out TIFF parameters. */
    switch ( PNM_FORMAT_TYPE(format) )
	{
	case PPM_TYPE:
	if ( chv == (colorhist_vector) 0 )
	    {
	    samplesperpixel = 3;
	    bitspersample = 8;
	    photometric = PHOTOMETRIC_RGB;
	    bytesperrow = cols * 3;
	    }
	else if ( grayscale )
	    {
	    samplesperpixel = 1;
	    bitspersample = pm_maxvaltobits( maxval );
	    photometric = PHOTOMETRIC_MINISBLACK;
	    bytesperrow = ( cols + i - 1 ) / i;
	    }
	else
	    {
	    samplesperpixel = 1;
	    bitspersample = 8;
	    photometric = PHOTOMETRIC_PALETTE;
	    bytesperrow = cols;
	    }
	break;

	case PGM_TYPE:
	samplesperpixel = 1;
	bitspersample = pm_maxvaltobits( maxval );
	photometric = PHOTOMETRIC_MINISBLACK;
	i = 8 / bitspersample;
	bytesperrow = ( cols + i - 1 ) / i;
	break;

	default:
	samplesperpixel = 1;
	bitspersample = 1;
	photometric = PHOTOMETRIC_MINISBLACK;
	bytesperrow = ( cols + 7 ) / 8;
	break;
	}

    if ( rowsperstrip == 0 )
	rowsperstrip = ( 8 * 1024 ) / bytesperrow;
    buf = (unsigned char*) malloc( bytesperrow );
    if ( buf == (unsigned char*) 0 )
	pm_error( "can't allocate memory for row buffer" );

    /* Set TIFF parameters. */
    TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, cols );
    TIFFSetField( tif, TIFFTAG_IMAGELENGTH, rows );
    TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, bitspersample );
    TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT );
    TIFFSetField( tif, TIFFTAG_COMPRESSION, compression );
    if ( compression == COMPRESSION_CCITTFAX3 && g3options != 0 )
	TIFFSetField( tif, TIFFTAG_GROUP3OPTIONS, g3options );
    if ( compression == COMPRESSION_LZW && predictor != 0 )
	TIFFSetField( tif, TIFFTAG_PREDICTOR, predictor );
    TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, photometric );
    TIFFSetField( tif, TIFFTAG_FILLORDER, fillorder );
    TIFFSetField( tif, TIFFTAG_DOCUMENTNAME, inf );
    TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, "converted PNM file" );
    TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel );
    TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip );
    /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */
    TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );

#define TIFFMAXCOL 65535L  /* maximal color that can be stored in an uint16 */
    if ( chv == (colorhist_vector) 0 )
	cht = (colorhash_table) 0;
    else
	{
	/* Make TIFF colormap. */
	  /* change in color scale behaviour by CS */
	  if (maxval > TIFFMAXCOL) /* only scale if necessary */
	    for ( i = 0; i < colors; ++i )
	      {
		red[i] = (long) PPM_GETR( chv[i].color ) * 65535L / maxval;
		grn[i] = (long) PPM_GETG( chv[i].color ) * 65535L / maxval;
		blu[i] = (long) PPM_GETB( chv[i].color ) * 65535L / maxval;
	      }
	  else
	    for ( i = 0; i < colors; ++i )
	      {
		red[i] = (long) PPM_GETR( chv[i].color );
		grn[i] = (long) PPM_GETG( chv[i].color );
		blu[i] = (long) PPM_GETB( chv[i].color );
	      }
	  /* and also fill the remaining colors from color...(1<<bitspersample)
	     with zeroes; otherwise libtiff will later think this is a 16bit
	     lookup table if it contains random values > 256 (CS) */
	  {
	    long max;
	    max = 1L << bitspersample;
	    if (max > MAXCOLORS) max=MAXCOLORS;
	    for ( i = colors; i < max; ++i )
	      {
		red[i] = 0;
		grn[i] = 0;
		blu[i] = 0;
	      }
	  }

	TIFFSetField( tif, TIFFTAG_COLORMAP, red, grn, blu );

	/* Convert color vector to color hash table, for fast lookup. */
	cht = ppm_colorhisttocolorhash( chv, colors );
	ppm_freecolorhist( chv );
	}

    /* Now write the TIFF data. */
    for ( row = 0; row < rows; ++row )
	{
	if ( PNM_FORMAT_TYPE(format) == PPM_TYPE && ! grayscale )
	    {
	    if ( cht == (colorhash_table) 0 )
		{
		for ( col = 0, xP = xels[row], tP = buf;
		      col < cols; ++col, ++xP )
		    {
		    register unsigned char s;

		    s = PPM_GETR( *xP );
		    if ( maxval != 255 )
			s = (long) s * 255 / maxval;
		    *tP++ = s;
		    s = PPM_GETG( *xP );
		    if ( maxval != 255 )
			s = (long) s * 255 / maxval;
		    *tP++ = s;
		    s = PPM_GETB( *xP );
		    if ( maxval != 255 )
			s = (long) s * 255 / maxval;
		    *tP++ = s;
		    }
		}
	    else
		{
		for ( col = 0, xP = xels[row], tP = buf;
		      col < cols; ++col, ++xP )
		    {
		    register int s;

		    s = ppm_lookupcolor( cht, xP );
		    if ( s == -1 )
			pm_error(
			    "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
			    row, col, PPM_GETR( *xP ), PPM_GETG( *xP ),
			    PPM_GETB( *xP ) );
		    *tP++ = (unsigned char) s;
		    }
		}
	    }
	else
	    {
	    register xelval bigger_maxval;
	    register int bitshift;
	    register unsigned char byte;
	    register xelval s;

	    bigger_maxval = pm_bitstomaxval( bitspersample );
	    bitshift = 8 - bitspersample;
	    byte = 0;
	    for ( col = 0, xP = xels[row], tP = buf; col < cols; ++col, ++xP )
		{
		s = PNM_GET1( *xP );
		if ( maxval != bigger_maxval )
		    s = (long) s * bigger_maxval / maxval;
		byte |= s << bitshift;
		bitshift -= bitspersample;
		if ( bitshift < 0 )
		    {
		    *tP++ = byte;
		    bitshift = 8 - bitspersample;
		    byte = 0;
		    }
		}
	    if ( bitshift != 8 - bitspersample )
		*tP++ = byte;
	    }

	if ( TIFFWriteScanline( tif, buf, row, 0 ) < 0 )
	    pm_error( "failed a scanline write on row %d", row );
	}
    TIFFFlushData( tif );
    TIFFClose( tif );

    exit( 0 );
    }
