
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "tiffio.h"

typedef struct
{
	char	sFLMId[5];
	char 	cTextEnd;
	long	lIconOffs;
	short	nImageWdt;
	short	nImageHgt;
	char	nYUVMode;
	short	nYUVLeng;
	char 	cYBits;
	char 	cUBits;
	char 	cVBits;
	char 	cCompMod;
	char 	cOldComp;
	short	nScrMod;
	short	nIconLenght;
	long	lTextOffs;
	short	nTextLen;
	short	nContr;
	short	nBright;
	short	nSatur;
	short	nHue;
	short	nRed;
	short	nGreen;
	short	nBlue;
	char	sRes[18];
	char	*data;
} FLM;
typedef	unsigned char u_char;
typedef	unsigned short u_short;
typedef	unsigned long u_long;

#define	streq(a,b)	(strcmp(a,b) == 0)

static	short config = PLANARCONFIG_CONTIG;
static	u_short compression = COMPRESSION_LZW;
static	double gval = 1.0;

static	void usage();
void flmclose(FLM *flm);
FLM *flmopen(char *name,char *mode);
static int convert2tiff(FLM *flm,TIFF *tiff);
static int convert2tiff_1(FLM *flm,TIFF *tiff);

int main(int argc, char **argv)
{
	FLM 	*flm;
	TIFF 	*tiff;

	argc--, argv++;
	if (argc < 2)
	{
		usage();
	}
	
	for (; argc > 2 && argv[0][0] == '-'; argc--, argv++) 
	{
		if (streq(argv[0], "-none")) 
		{
			compression = COMPRESSION_NONE;
			continue;
		}
		if (streq(argv[0], "-packbits")) 
		{
			compression = COMPRESSION_PACKBITS;
			continue;
		}
		if (streq(argv[0], "-lzw")) 
		{
			compression = COMPRESSION_LZW;
			continue;
		}
		if (streq(argv[0], "-gamma") && argc > 1) 
		{
			gval = atof(argv[1]);
			++argv;
			--argc;
			continue;
		}
		usage();
	}
	if(!(flm = flmopen(argv[0], "r")))
	{
		exit(-1);
	}
	
	if(!(tiff = TIFFOpen(argv[1], "w")))
	{
		exit(-2);
	}
	
	TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, flm->nImageWdt);
	TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, flm->nImageHgt);
	TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
	TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression);
	TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
	if(flm->nYUVMode == 1)
	{
		TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
		TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
	}
	else
	{
		TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3);
		TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
	}
	TIFFSetField(tiff, TIFFTAG_MINSAMPLEVALUE, (short) 0);
	TIFFSetField(tiff, TIFFTAG_MAXSAMPLEVALUE, (short) 255);
	TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, config);
	TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1L);

	printf("Converting Image of %d,%d with gamma of %f\n",flm->nImageWdt,flm->nImageHgt,gval);
	if(flm->nYUVMode == 1)
	{
		convert2tiff_1(flm,tiff);
	}
	else
	{
		convert2tiff(flm,tiff);
	}
	(void) flmclose(flm);
	(void) TIFFClose(tiff);
	exit(0);
}

void convertpixel(unsigned char *rgbdata, char *yuvdata,unsigned char *luma,char* chroma,unsigned char *gamma)
{
	unsigned int Y1,Y2;
	int CRR,CRG,CBG,CBB;
	
	Y1 = luma[(unsigned char) *yuvdata++];
	CBB =  CBG = chroma[(*yuvdata++)+128];
	Y2 = luma[(unsigned char) *yuvdata++];
	CRR =  CRG = chroma[(*yuvdata++)+128];
	CRG =  (CRG * 178) >> 8;

	CRR = (CRR * 350) >> 8;
	CBG = CRG + ((CBG * 86) >> 8);
	CBB = (CBB * 443) >> 8;

	
	rgbdata[0] = gamma[Y1 + CRR + 128];
	rgbdata[1] = gamma[Y1 - CBG + 128];
	rgbdata[2] = gamma[Y1 + CBB + 128]; 

	rgbdata[3] = gamma[Y2 + CRR + 128];
	rgbdata[4] = gamma[Y2 - CBG + 128];
	rgbdata[5] = gamma[Y2 + CBB + 128];
	
}

static int convert2tiff(FLM *flm,TIFF *tiff)
{
	unsigned char *buf = (unsigned char *)malloc(TIFFScanlineSize(tiff));
	char	*yuvdata,chroma[256];
	unsigned char *rgbdata;
	int i,x,y, stripsize;
	unsigned char luma[256],gamma[512];
	double b;
	
	/* Init luma lookup */
	for(i=0;i<16;i++)
	{
		luma[i] = 0;	
	}
	for(;i<235;i++)
	{
		luma[i] = i-16;
	}
	for(;i<256;i++)
	{
		luma[i] = 220;
	}
	/* Init chroma lookup */
	for(i=0;i<16;i++)
	{
		chroma[i] = -112;	
	}
	for(;i<240;i++)
	{
		chroma[i] = i-128;
	}
	for(;i<256;i++)
	{
		chroma[i] = 112;
	}
	for(i=0;i<128;i++)
	{
		gamma[i] = 0;
		gamma[i+256] = 255;
	}
	for(i=128;i<384;i++)
	{
		b = (double) (i-128) / 255.0;
		gamma[i] = (unsigned int) (pow(b,gval) * 255.0 + 0.5);
	}
	yuvdata = flm->data;
	stripsize = flm->nImageWdt * 3;
	buf = (unsigned char *)malloc(stripsize);
	for(y=0;y<flm->nImageHgt;y++)
	{
		rgbdata = buf;
		for(x=0;x<flm->nImageWdt;x+=2)
		{
			convertpixel(rgbdata, yuvdata,luma,chroma,gamma);
			yuvdata +=4;
			rgbdata +=6;
		}
		if (TIFFWriteEncodedStrip(tiff, y, buf, stripsize) < 0)
		{
			free(buf);
			return 0;
		}
	}	
	free(buf);
	return 1;
}

static int convert2tiff_1(FLM *flm,TIFF *tiff)
{
	unsigned char *buf = (unsigned char *)malloc(TIFFScanlineSize(tiff));
	unsigned char	*yuvdata;
	unsigned char *rgbdata;
	int i,x,y, stripsize;
	unsigned char luma[256],gamma[512];
	double b;
	
	/* Init luma lookup */
	for(i=0;i<16;i++)
	{
		luma[i] = 0;	
	}
	for(;i<235;i++)
	{
		luma[i] = ((i-16)*255)/219;
	}
	for(;i<256;i++)
	{
		luma[i] = 255;
	}
	for(i=0;i<128;i++)
	{
		gamma[i] = 0;
		gamma[i+256] = 255;
	}
	for(i=128;i<384;i++)
	{
		b = (double) (i-128) / 255.0;
		gamma[i] = (unsigned int) (pow(b,gval) * 255.0 + 0.5);
	}
	yuvdata = flm->data;
	stripsize = flm->nImageWdt;
	buf = (unsigned char *)malloc(stripsize);
	for(y=0;y<flm->nImageHgt;y++)
	{
		rgbdata = buf;
		for(x=0;x<flm->nImageWdt;x++)
		{
			*rgbdata++ = gamma[luma[*yuvdata++] + 128];
		}
		if (TIFFWriteEncodedStrip(tiff, y, buf, stripsize) < 0)
		{
			free(buf);
			return 0;
		}
	}	
	free(buf);
	return 1;
}


static void
usage()
{
	fprintf(stderr, "usage: flm2tiff [options] input output\n");
	fprintf(stderr, "where options are:\n");
	fprintf(stderr,
	    " -lzw\t\tcompress output with Lempel-Ziv & Welch encoding\n");
	fprintf(stderr,
	    " -packbits\tcompress output with packbits encoding\n");
	fprintf(stderr,
	    " -none\t\tuse no compression algorithm on output\n");
	fprintf(stderr, "\n");
	fprintf(stderr,
	    " -gamma #\tgamma correction for resulting RGB image (default = 1.5)\n");
	exit(-1);
}

FLM *flmopen(char *name,char *mode)
{
	FLM		*flm = (FLM*) malloc(sizeof(FLM));
	FILE 	*fp;
	unsigned char	buf[64];
	
	if(!(fp = fopen(name,mode)))
	{
		free(flm);
		return NULL;
	}
	/* Get Header data */
	if(!fread(buf,64,1,fp))
	{
		fclose(fp);
		free(flm);
		return NULL;
	}
	/* Decode Header */
	bcopy(buf,flm,5);
	flm->cTextEnd=buf[5];
	flm->lIconOffs=(buf[6]+(buf[7]<<8)+(buf[8]<<16)+(buf[9]<<24));
	flm->nImageWdt=(buf[10]+(buf[11]<<8));
	flm->nImageHgt=(buf[12]+(buf[13]<<8));
	flm->nYUVMode=buf[14];
	flm->nYUVLeng=(buf[15]+(buf[16]<<8));	
	flm->cYBits=buf[17];
	flm->cUBits=buf[18];
	flm->cVBits=buf[19];
	flm->cCompMod=buf[20];
	flm->cOldComp=buf[21];
	flm->nScrMod=(buf[22]+(buf[23]<<8));
	flm->nIconLenght=buf[24]+(buf[25]<<8);
	flm->lTextOffs=(buf[26]+(buf[27]<<8)+(buf[28]<<16)+(buf[29]<<24));
	flm->nTextLen=(buf[30]+(buf[31]<<8));
	flm->nContr=(buf[32]+(buf[33]<<8));	
	flm->nBright=(buf[34]+(buf[35]<<8));	
	flm->nSatur=(buf[36]+(buf[37]<<8));	
	flm->nHue=(buf[38]+(buf[39]<<8));	
	flm->nRed=(buf[40]+(buf[41]<<8));	
	flm->nGreen=(buf[42]+(buf[43]<<8));	
	flm->nBlue=(buf[44]+(buf[45]<<8));	
	/* Allocate data */
	flm->data = (char*) malloc(flm->nYUVLeng * flm->nImageHgt);
	/* Read data */
	if(!fread(flm->data,flm->nYUVLeng * flm->nImageHgt,1,fp))
	{
		fclose(fp);
		free(flm->data);
		free(flm);
		return NULL;
	}
	fclose(fp);
	return flm;
}

void flmclose(FLM *flm)
{
	free(flm->data);
	free(flm);
}


