// TSMorph - Amiga Morphing program
// Copyright (C) © 1993  Topicsave Limited

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// mpaddock@cix.compulink.co.uk

// This is routines to save chunky 24 bit RGB in various formats
// B&W 16 grey scale
// B&W 256 grey scale
// HAM6 with fixed palette (but could easily used variable palette)
// HAM6 with fixed palette (but could easily used variable palette)
// DCTV

// include headers
#include <proto/iffparse.h>
#include <graphics/view.h>
#include <libraries/dctv.h>
#include <clib/dctv_protos.h>
#include <pragmas/dctv_pragmas.h>
extern struct Library *DCTVBase;

// IFF header
#include "iffp/ILBMapp.h"

// Following explains(?) the palette choices

//   0 = x00 = b00000000
//  85 = x55 = b01010101
// 170 = xaa = b10101010
// 255 = xff = b11111111

//   0 = x00 = o000 = b00000000
//  36 = x24 = o111 = b00100100
//  73 = x49 = o222 = b01001001
// 109 = x6d = o333 = b01101101
// 146 = x92 = o444 = b10010010
// 182 = xb6 = o555 = b10110110
// 219 = xdb = o666 = b11011011
// 255 = xff = o777 = b11111111

//   0 = x00 = b00000000
//  17 = x11 = b00010001
//  34 = x22 = b00100010
//  51 = x33 = b00110011
//  68 = x44 = b01000100
//  85 = x55 = b01010101
// 102 = x66 = b01100110
// 119 = x77 = b01110111
// 136 = x88 = b10001000
// 153 = x99 = b10011001
// 170 = xaa = b10101010
// 187 = xbb = b10111011
// 204 = xcc = b11001100
// 221 = xdd = b11011101
// 238 = xee = b11101110
// 255 = xff = b11111111 

// 16 grey shade palette
UBYTE BW16_Palette[48] = {
	0,0,0,
	17,17,17,
	34,34,34,
	51,51,51,
	68,68,68,
	85,85,85,
	102,102,102,
	119,119,119,
	136,136,136,
	153,153,153,
	170,170,170,
	187,187,187,
	204,204,204,
	221,221,221,
	238,238,238,
	255,255,255
};

// 256 grey shade palette
UBYTE BW256_Palette[768] = {
	0,0,0,
	1,1,1,
	2,2,2,
	3,3,3,
	4,4,4,
	5,5,5,
	6,6,6,
	7,7,7,
	8,8,8,
	9,9,9,
	10,10,10,
	11,11,11,
	12,12,12,
	13,13,13,
	14,14,14,
	15,15,15,
	16,16,16,
	17,17,17,
	18,18,18,
	19,19,19,
	20,20,20,
	21,21,21,
	22,22,22,
	23,23,23,
	24,24,24,
	25,25,25,
	26,26,26,
	27,27,27,
	28,28,28,
	29,29,29,
	30,30,30,
	31,31,31,
	32,32,32,
	33,33,33,
	34,34,34,
	35,35,35,
	36,36,36,
	37,37,37,
	38,38,38,
	39,39,39,
	40,40,40,
	41,41,41,
	42,42,42,
	43,43,43,
	44,44,44,
	45,45,45,
	46,46,46,
	47,47,47,
	48,48,48,
	49,49,49,
	50,50,50,
	51,51,51,
	52,52,52,
	53,53,53,
	54,54,54,
	55,55,55,
	56,56,56,
	57,57,57,
	58,58,58,
	59,59,59,
	60,60,60,
	61,61,61,
	62,62,62,
	63,63,63,
	64,64,64,
	65,65,65,
	66,66,66,
	67,67,67,
	68,68,68,
	69,69,69,
	70,70,70,
	71,71,71,
	72,72,72,
	73,73,73,
	74,74,74,
	75,75,75,
	76,76,76,
	77,77,77,
	78,78,78,
	79,79,79,
	80,80,80,
	81,81,81,
	82,82,82,
	83,83,83,
	84,84,84,
	85,85,85,
	86,86,86,
	87,87,87,
	88,88,88,
	89,89,89,
	90,90,90,
	91,91,91,
	92,92,92,
	93,93,93,
	94,94,94,
	95,95,95,
	96,96,96,
	97,97,97,
	98,98,98,
	99,99,99,
	100,100,100,
	101,101,101,
	102,102,102,
	103,103,103,
	104,104,104,
	105,105,105,
	106,106,106,
	107,107,107,
	108,108,108,
	109,109,109,
	110,110,110,
	111,111,111,
	112,112,112,
	113,113,113,
	114,114,114,
	115,115,115,
	116,116,116,
	117,117,117,
	118,118,118,
	119,119,119,
	120,120,120,
	121,121,121,
	122,122,122,
	123,123,123,
	124,124,124,
	125,125,125,
	126,126,126,
	127,127,127,
	128,128,128,
	129,129,129,
	130,130,130,
	131,131,131,
	132,132,132,
	133,133,133,
	134,134,134,
	135,135,135,
	136,136,136,
	137,137,137,
	138,138,138,
	139,139,139,
	140,140,140,
	141,141,141,
	142,142,142,
	143,143,143,
	144,144,144,
	145,145,145,
	146,146,146,
	147,147,147,
	148,148,148,
	149,149,149,
	150,150,150,
	151,151,151,
	152,152,152,
	153,153,153,
	154,154,154,
	155,155,155,
	156,156,156,
	157,157,157,
	158,158,158,
	159,159,159,
	160,160,160,
	161,161,161,
	162,162,162,
	163,163,163,
	164,164,164,
	165,165,165,
	166,166,166,
	167,167,167,
	168,168,168,
	169,169,169,
	170,170,170,
	171,171,171,
	172,172,172,
	173,173,173,
	174,174,174,
	175,175,175,
	176,176,176,
	177,177,177,
	178,178,178,
	179,179,179,
	180,180,180,
	181,181,181,
	182,182,182,
	183,183,183,
	184,184,184,
	185,185,185,
	186,186,186,
	187,187,187,
	188,188,188,
	189,189,189,
	190,190,190,
	191,191,191,
	192,192,192,
	193,193,193,
	194,194,194,
	195,195,195,
	196,196,196,
	197,197,197,
	198,198,198,
	199,199,199,
	200,200,200,
	201,201,201,
	202,202,202,
	203,203,203,
	204,204,204,
	205,205,205,
	206,206,206,
	207,207,207,
	208,208,208,
	209,209,209,
	210,210,210,
	211,211,211,
	212,212,212,
	213,213,213,
	214,214,214,
	215,215,215,
	216,216,216,
	217,217,217,
	218,218,218,
	219,219,219,
	220,220,220,
	221,221,221,
	222,222,222,
	223,223,223,
	224,224,224,
	225,225,225,
	226,226,226,
	227,227,227,
	228,228,228,
	229,229,229,
	230,230,230,
	231,231,231,
	232,232,232,
	233,233,233,
	234,234,234,
	235,235,235,
	236,236,236,
	237,237,237,
	238,238,238,
	239,239,239,
	240,240,240,
	241,241,241,
	242,242,242,
	243,243,243,
	244,244,244,
	245,245,245,
	246,246,246,
	247,247,247,
	248,248,248,
	249,249,249,
	250,250,250,
	251,251,251,
	252,252,252,
	253,253,253,
	254,254,254,
	255,255,255,
};

// HAM6 base palette
// note 3 extra entries based on changing r,g or b using HAM stuff
UBYTE HAM6_Palette[57] = {
	0,0,0,
	0,0,170,
	0,85,0,
	0,85,170,
	0,170,0,
	0,170,170,
	0,255,0,
	0,255,170,
	170,0,0,
	170,0,170,
	170,85,0,
	170,85,170,
	170,170,0,
	170,170,170,
	170,255,0,
	170,255,170,
	0,0,0,	// r
	0,0,0,	// g
	0,0,0,	// b
};

// HAM8 base palette
// note 3 extra entries based on changing r,g or b using HAM stuff
UBYTE HAM8_Palette[201] = {
	0,0,0,
	0,0,170,
	0,36,0,
	0,36,170,
	0,73,0,
	0,73,170,
	0,109,0,
	0,109,170,
	0,146,0,
	0,146,170,
	0,182,0,
	0,182,170,
	0,219,0,
	0,219,170,
	0,255,0,
	0,255,170,
	85,0,0,
	85,0,170,
	85,36,0,
	85,36,170,
	85,73,0,
	85,73,170,
	85,109,0,
	85,109,170,
	85,146,0,
	85,146,170,
	85,182,0,
	85,182,170,
	85,219,0,
	85,219,170,
	85,255,0,
	85,255,170,
	170,0,0,
	170,0,170,
	170,36,0,
	170,36,170,
	170,73,0,
	170,73,170,
	170,109,0,
	170,109,170,
	170,146,0,
	170,146,170,
	170,182,0,
	170,182,170,
	170,219,0,
	170,219,170,
	170,255,0,
	170,255,170,
	255,0,0,
	255,0,170,
	255,36,0,
	255,36,170,
	255,73,0,
	255,73,170,
	255,109,0,
	255,109,170,
	255,146,0,
	255,146,170,
	255,182,0,
	255,182,170,
	255,219,0,
	255,219,170,
	255,255,0,
	255,255,170,
	0,0,0,	// r
	0,0,0,	// g
	0,0,0,	// b
};

/* save 16 grey scale
 * FileName : file to save
 * width    : width of image
 * height   : height of image
 * swidth   : widht of image when in full words
 * pwidth   : page width
 * pheight  : page height
 * pmode    : camg chunk
 * red      : red
 * green    : green
 * blue     : blue chunky bit map
 * rp       : a work rast port
 * trp      : another work rast port
 */
BOOL
SaveBW16(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp) {
	UBYTE *r,*g,*b,*old;
	UWORD x,y;
	BOOL OkFlag;
	struct ILBMInfo *ilbm;
	// allocate IFF stuff
	if (ilbm = (struct ILBMInfo *)AllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
		if (ilbm->ParseInfo.iff = AllocIFF()) {
			r = red;
			g = green;
			b = blue;
			old = red;
			// for each line
			for (y=0;
				  y<height;
				  y++) {
				// for each column
				for (x=0;
					  x < width;
					  x++) {
					// convert rgb to 16 grey scale
					*old++ = ((30 * *r++) + (59 * *g++) + (11 * *b++)) / 1594;
				}
				r += (swidth - width);
				g += (swidth - width);
				b += (swidth - width);
				old += (swidth - width);
			}
			// convert chunky to planar
			WritePixelArray8(rp,0,0,width-1,height-1,red,trp);
			// and save IFF
			OkFlag = !saveilbm(ilbm, rp->BitMap, pmode,
									 width,  height, pwidth, pheight,
									 BW16_Palette, 16, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 FileName);
			// Close everything down cleanly
			FreeIFF(ilbm->ParseInfo.iff);
		}
		else {
			OkFlag = FALSE;
		}
		FreeMem(ilbm,sizeof(struct ILBMInfo));
	}
	else {
		OkFlag = FALSE;
	}
	return OkFlag;
}

/* save 256 grey scale
 * see SaveBW16()
 */
BOOL
SaveBW256(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp) {
	UBYTE *r,*g,*b,*old;
	UWORD x,y;
	BOOL OkFlag;
	struct ILBMInfo *ilbm;
	if (ilbm = (struct ILBMInfo *)AllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
		if (ilbm->ParseInfo.iff = AllocIFF()) {
			r = red;
			g = green;
			b = blue;
			old = red;
			for (y=0;
				  y<height;
				  y++) {
				for (x=0;
					  x < width;
					  x++) {
					// convert rgb to 256 grey scale
					*old = ((30 * *r++) + (59 * *g++) + (11 * *b++)) / 100;
				}
				r += (swidth - width);
				g += (swidth - width);
				b += (swidth - width);
				old += (swidth - width);
			}
			WritePixelArray8(rp,0,0,width-1,height-1,red,trp);
			OkFlag = !saveilbm(ilbm, rp->BitMap, pmode,
									 width,  height, pwidth, pheight,
									 BW256_Palette, 256, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 FileName);
			// Close everything down cleanly
			FreeIFF(ilbm->ParseInfo.iff);
		}
		else {
			OkFlag = FALSE;
		}
		FreeMem(ilbm,sizeof(struct ILBMInfo));
	}
	else {
		OkFlag = FALSE;
	}
	return OkFlag;
}

/* save HAM6
 * see SaveBW16()
 */
BOOL
SaveHAM6(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp) {
	UBYTE *r,*g,*b,*old,*p;
	UWORD x,y;
	ULONG maxdiff;
	ULONG diff;
	LONG t;
	UWORD k;
	UWORD index;
	BOOL OkFlag;
	struct ILBMInfo *ilbm;
	UBYTE lr,lg,lb;
	if (ilbm = (struct ILBMInfo *)AllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
		if (ilbm->ParseInfo.iff = AllocIFF()) {
			r = red;
			g = green;
			b = blue;
			old = red;
			for (y=0;
				  y<height;
				  y++) {
				lr = 0;
				lg = 0;
				lb = 0;
				for (x=0;
					  x < width;
					  x++) {
					// colour if we use HAM to change R or G or B
					HAM6_Palette[16*3] = *r;
					HAM6_Palette[16*3+1] = lg;
					HAM6_Palette[16*3+2] = lb;
					HAM6_Palette[17*3] = lr;
					HAM6_Palette[17*3+1] = *g;
					HAM6_Palette[17*3+2] = lb;
					HAM6_Palette[18*3] = lr;
					HAM6_Palette[18*3+1] = lg;
					HAM6_Palette[18*3+2] = *b;
					// Find closest color
					maxdiff = 0x7FFFFFFF;
					p = HAM6_Palette;
					for (k = 0;
						  k < 19;
						  ++k) {
						t = *r - *p++;
						diff = t*t*3;
						t = *g - *p++;
						diff += (t*t*6);
						t = *b - *p++;
						diff += (t*t);
						if (diff < maxdiff) {
							maxdiff = diff;
							index = k;
						}
					}
					lr = HAM6_Palette[index*3];
					lg = HAM6_Palette[index*3+1];
					lb = HAM6_Palette[index*3+2];
					// Set HAM bits if required
					if (index == 16) {
						*old++ = (*r>>4) | 0x20;
					}
					else {
						if (index == 17) {
							*old++ = (*g>>4) | 0x30;
						}
						else {
							if (index == 18) {
								*old++ = (*b>>4) | 0x10;
							}
							else {
								*old++ = index;
							}
						}
					}
					r++;
					g++;
					b++;
				}
				r += (swidth - width);
				g += (swidth - width);
				b += (swidth - width);
				old += (swidth - width);
			}
			WritePixelArray8(rp,0,0,width-1,height-1,red,trp);
			OkFlag = !saveilbm(ilbm, rp->BitMap, pmode | HAM,
									 width,  height, pwidth, pheight,
									 HAM6_Palette, 16, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 FileName);
			// Close everything down cleanly
			FreeIFF(ilbm->ParseInfo.iff);
		}
		else {
			OkFlag = FALSE;
		}
		FreeMem(ilbm,sizeof(struct ILBMInfo));
	}
	else {
		OkFlag = FALSE;
	}
	return OkFlag;
}

/* save HAM8
 * see SaveHAM6()
 */
BOOL SaveHAM8(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp) {
	UBYTE *r,*g,*b,*old,*p;
	UWORD x,y;
	ULONG maxdiff;
	ULONG diff;
	LONG t;
	UWORD k;
	UWORD index;
	BOOL OkFlag;
	struct ILBMInfo *ilbm;
	UBYTE lr,lg,lb;
	if (ilbm = (struct ILBMInfo *)AllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
		if (ilbm->ParseInfo.iff = AllocIFF()) {
			r = red;
			g = green;
			b = blue;
			old = red;
			for (y=0;
				  y<height;
				  y++) {
				lr = 0;
				lg = 0;
				lb = 0;
				for (x=0;
					  x < width;
					  x++) {
					HAM8_Palette[64*3] = *r;
					HAM8_Palette[64*3+1] = lg;
					HAM8_Palette[64*3+2] = lb;
					HAM8_Palette[65*3] = lr;
					HAM8_Palette[65*3+1] = *g;
					HAM8_Palette[65*3+2] = lb;
					HAM8_Palette[66*3] = lr;
					HAM8_Palette[66*3+1] = lg;
					HAM8_Palette[66*3+2] = *b;
					// Find closest color
					maxdiff = 0x7FFFFFFF;
					p = HAM8_Palette;
					for (k = 0;
						  k < 67;
						  ++k) {
						t = *r - *p++;
						diff = t*t*3;
						t = *g - *p++;
						diff += (t*t*6);
						t = *b - *p++;;
						diff += (t*t);
						if (diff < maxdiff) {
							maxdiff = diff;
							index = k;
						}
					}
					lr = HAM8_Palette[index*3];
					lg = HAM8_Palette[index*3+1];
					lb = HAM8_Palette[index*3+2];
					if (index == 64) {
						*old++ = (*r>>2) | 0x80;
					}
					else {
						if (index == 65) {
							*old++ = (*g>>2) | 0xc0;
						}
						else {
							if (index == 66) {
								*old++ = (*b>>2) | 0x40;
							}
							else {
								*old++ = index;
							}
						}
					}
					r++;
					g++;
					b++;
				}
				r += (swidth - width);
				g += (swidth - width);
				b += (swidth - width);
				old += (swidth - width);
			}
			WritePixelArray8(rp,0,0,width-1,height-1,red,trp);
			OkFlag = !saveilbm(ilbm, rp->BitMap, pmode | HAM,
									 width,  height, pwidth, pheight,
									 HAM8_Palette, 64, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 FileName);
			// Close everything down cleanly
			FreeIFF(ilbm->ParseInfo.iff);
		}
		else {
			OkFlag = FALSE;
		}
		FreeMem(ilbm,sizeof(struct ILBMInfo));
	}
	else {
		OkFlag = FALSE;
	}
	return OkFlag;
}

/* save DCTV
 * see SaveBW16()
 */
BOOL SaveDCTV(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp) {
	UWORD i;
	BOOL OkFlag;
	struct ILBMInfo *ilbm;
	struct DCTVCvtHandle *chandle;

	if (ilbm = (struct ILBMInfo *)AllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
		if (ilbm->ParseInfo.iff = AllocIFF()) {
			// Allocate DCTV stuff
			if (chandle = AllocDCTVCvtTags(rp->BitMap,
											DCTVCVTA_Type, DCTVCVTT_RGBtoDCTV,
											DCTVCVTA_Flags,((pmode|LACE)?DCTVCVTF_Lace:0)|
																DCTVCVTF_Filter|
																DCTVCVTF_CustomRGBBuf,
											TAG_END)) {
				chandle->Red = red;
				chandle->Green = green;
				chandle->Blue = blue;
				// Convert each line
				while (chandle->DstLineNum < chandle->Height) {
					i = chandle->SrcLineNum;
					CvtDCTVLine(chandle);
					if (i != chandle->SrcLineNum) {
						chandle->Red += swidth;
						chandle->Green += swidth;
						chandle->Blue += swidth;
					}
				}
				OkFlag = !saveilbm(ilbm, rp->BitMap, pmode,
									 width,  height, pwidth, pheight,
									 chandle->ColorTable, 1L << rp->BitMap->Depth, 4,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 FileName);
				// Free DCTV stuff
				FreeDCTVCvt(chandle);
			}
			else {
				OkFlag = FALSE;
			}
			// Close everything down cleanly
			FreeIFF(ilbm->ParseInfo.iff);
		}
		else {
			OkFlag = FALSE;
		}
		FreeMem(ilbm,sizeof(struct ILBMInfo));
	}
	else {
		OkFlag = FALSE;
	}
	return OkFlag;
}
