/* +-------------------------------------------------------------------+ */
/* | Copyright 1993, David Koblas (koblas@netcom.com)		       | */
/* |								       | */
/* | Permission to use, copy, modify, and to distribute this software  | */
/* | and its documentation for any purpose is hereby granted without   | */
/* | fee, provided that the above copyright notice appear in all       | */
/* | copies and that both that copyright notice and this permission    | */
/* | notice appear in supporting documentation.	 There is no	       | */
/* | representations about the suitability of this software for	       | */
/* | any purpose.  this software is provided "as is" without express   | */
/* | or implied warranty.					       | */
/* |								       | */
/* +-------------------------------------------------------------------+ */

/* $Id: writeTIFF.c,v 1.5 1996/05/28 09:19:16 torsten Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <tiffio.h>
#include "image.h"

#undef howmany
#define	howmany(x, y)	(((x)+((y)-1))/(y))
#define	streq(a,b)	(strcmp(a,b) == 0)

int WriteTIFF(char *file, Image * image)
{
    TIFF *out;
    int samplesPP, bitsPP;
    int x, y;
    unsigned char *tbuf = NULL;
    unsigned char *buf = NULL, *bp;
    unsigned char *ucp = (unsigned char *) image->data;
    unsigned short photometric;

    if (image->isGrey) {
	photometric = PHOTOMETRIC_MINISBLACK;
	samplesPP = 1;
	bitsPP = image->isBW ? 1 : 8;
	if (bitsPP == 1)
	    buf = (unsigned char *) malloc(sizeof(char) * image->width / 8 + 4);
    } else {
	Image *cmapImage;   /* GRR 960525 */

	/* try compressing image to a colormap, but don't force compression */
	if (!image->maskData)   /* can TIFF store alpha channel with palette? */
	    cmapImage = ImageCompress(image, 256, 1);
	if (cmapImage)
	    image = cmapImage;  /* original was deleted in ImageCompress() */

	if (image->cmapSize == 0 || image->cmapSize > 256) {
	    photometric = PHOTOMETRIC_RGB;
	    samplesPP = 3;
	    bitsPP = 8;
	} else {
	    photometric = PHOTOMETRIC_PALETTE;
	    samplesPP = 1;
	    bitsPP = 8;
	}
    }

    if ((out = TIFFOpen(file, "w")) == NULL)
	return 1;

    TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (unsigned long) image->width);
    TIFFSetField(out, TIFFTAG_IMAGELENGTH, (unsigned long) image->height);
    TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
    TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, samplesPP);
    TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitsPP);
    TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
    TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
    TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, 1);
    TIFFSetField(out, TIFFTAG_SOFTWARE, "XPaint 2.1");

    if (photometric == PHOTOMETRIC_PALETTE) {
	unsigned short *r, *g, *b;
	int i;

	r = (unsigned short *) malloc(sizeof(unsigned short) * 3 * image->cmapSize);
	g = r + image->cmapSize;
	b = g + image->cmapSize;
	for (i = 0; i < image->cmapSize; i++) {
	    r[i] = image->cmapData[i * 3 + 0] * 256;
	    g[i] = image->cmapData[i * 3 + 1] * 256;
	    b[i] = image->cmapData[i * 3 + 2] * 256;
	}
	TIFFSetField(out, TIFFTAG_COLORMAP, r, g, b);

	free(r);
    }
    /*
    **	Here is where we are lucky that TIFF and 
    **	  the Image format are the same.
    **	[ except for large colormap images ]
     */
    if (image->cmapSize > 256)
	tbuf = (unsigned char *) malloc(image->width * sizeof(char) * 3);
    for (y = 0; y < image->height; y++) {
	if (bitsPP == 8) {
	    if (image->cmapSize <= 256) {
		TIFFWriteScanline(out, ucp, y, 0);
		ucp += image->width * samplesPP;
	    } else {
		unsigned char *p, *tp = tbuf;

		for (x = 0; x < image->width; x++) {
		    p = ImagePixel(image, x, y);
		    *tp++ = *p++;
		    *tp++ = *p++;
		    *tp++ = *p++;
		}
		TIFFWriteScanline(out, tbuf, y, 0);
	    }
	} else {
	    int shift = 7;

	    bp = buf;
	    *bp = 0;
	    for (x = 0; x < image->width; x++, ucp++) {
		*bp |= (*ucp > 128 ? 1 : 0) << shift--;
		if (shift < 0) {
		    shift = 7;
		    bp++;
		    *bp = 0;
		}
	    }
	    TIFFWriteScanline(out, buf, y, 0);
	}
    }

    if (image->maskData != NULL) {
	TIFFWriteDirectory(out);

	TIFFSetDirectory(out, 1);

	samplesPP = 1;
	bitsPP = 1;
	TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (unsigned long) image->width);
	TIFFSetField(out, TIFFTAG_IMAGELENGTH, (unsigned long) image->height);
	TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
	TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, samplesPP);
	TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitsPP);
	TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
	TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MASK);
	TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
	TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, 1);
	TIFFSetField(out, TIFFTAG_SOFTWARE, "XPaint");

	ucp = image->maskData;

	if (buf == NULL)
	    buf = (unsigned char *) malloc(sizeof(char) * image->width / 8 + 4);
	for (y = 0; y < image->height; y++) {
	    int shift = 7;

	    bp = buf;
	    *bp = 0;
	    for (x = 0; x < image->width; x++, ucp++) {
		*bp |= (*ucp ? 1 : 0) << shift--;
		if (shift < 0) {
		    shift = 7;
		    bp++;
		    *bp = 0;
		}
	    }
	    TIFFWriteScanline(out, buf, y, 0);
	}
    }
    (void) TIFFClose(out);

    if (buf != NULL)
	free(buf);
    if (tbuf != NULL)
	free(tbuf);

    return 0;
}
