// MPImage - Amiga Image Conversion
// Copyright (C) © 1996 Mark John Paddock
//
// 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.

// mark@topic.demon.co.uk
// mpaddock@cix.compulink.co.uk

#include "mpimage.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[16][3] = {
	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[256][3] = {
	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
static UBYTE HAM6_Palette[19][3] = {
	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,
	255,255,255,
	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[67][3] = {
	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,255,
	0,0,0,	// r
	0,0,0,	// g
	0,0,0,	// b
};

UBYTE MyPalette[256][3];

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

BOOL __asm __saveds SaveMPImageA(register __a0 const UBYTE *file,
								register __a1 UBYTE *red, register __a2 UBYTE *green,register __a3 UBYTE *blue,
								register __d0 UWORD width, register __d1 UWORD height,
								register __a5 struct TagItem *TagList);

extern UWORD MedianCut(UWORD Hist[],UBYTE ColMap[][3], int maxcubes, UWORD HistPtr[]);
extern UWORD MedHam6(UWORD Hist[],UBYTE ColMap[][3], int maxcubes, UWORD HistPtr[]);

static ULONG LoadPalette(UBYTE *filename);
static BOOL ComputePalette(UWORD Depth,UWORD width,UWORD height,UBYTE *red,UBYTE *green,UBYTE *blue);
static BOOL ComputePal12(UWORD Depth,UWORD width,UWORD height,UBYTE *red,UBYTE *green,UBYTE *blue);
static BOOL ComputePalEHB(UWORD width,UWORD height,UBYTE *red,UBYTE *green,UBYTE *blue);
static ULONG GetMode(UWORD height, UWORD width, UWORD depth, ULONG camg, ULONG flags, const UBYTE * filename);
static BOOL SaveBW16(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UBYTE palette[][3], ULONG camg, UBYTE *GreyMap, BOOL Linear);
static BOOL SaveBW256(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UBYTE palette[][3], ULONG camg, UBYTE *GreyMap, BOOL Linear);
static BOOL SaveHAM6(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UBYTE palette[][3], ULONG camg);
static BOOL SaveHAM8(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UBYTE palette[][3], ULONG camg);
static BOOL Save24(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg);
static BOOL SavePPM(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue,UWORD jpeg);
static BOOL SaveColour(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg, ULONG numcols);
static BOOL SaveColour12(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg, ULONG numcols);
static BOOL SaveEHB(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg);
static BOOL SaveDCTV(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg,BOOL dctv4);
static LONG mysaveilbm(struct ILBMInfo *ilbm,
		struct BitMap *bitmap, ULONG modeid,
		WORD width, WORD height, WORD pagewidth, WORD pageheight,
		UBYTE colortable[][3], UWORD count, UWORD bitspergun,
                WORD masking, WORD transparentColor,
		struct Chunk *chunklist1, struct Chunk *chunklist2,
		UBYTE *filename,int Depth);

// Properties for IFF read
static LONG props[] = { ID_ILBM,	ID_BMHD,
						ID_ILBM,	ID_CAMG,
						ID_ILBM, ID_CMAP,
						TAG_DONE };
static LONG stops[] = {	ID_ILBM,	ID_BODY,
						TAG_DONE };
static LONG nowt[] = { TAG_DONE };

/****** MPImage.library/SaveMPImageA *****************************************
*
*   NAME   
*  SaveMPImageA -- Save an image in various formats. (V3)
*  SaveMPIMage -- Varargs version of SaveMPImageA (V3)
*
*   SYNOPSIS
*  succ = SaveMPImageA( file,red,green,blue,width,height,taglist)
*  D0                   A0   A1  A2    A3   D0    D1     A5
*
*  BOOL SaveMPImageA( UBYTE *,UBYTE *,UBYTE *,UBYTE *,
*                                   UWORD,UWORD,struct TagItem *);
*
*  succ = SaveMPImage( file,red,green,blue,width,height,Tag1, ...)
*
*  BOOL SaveMPImage( UBYTE *,UBYTE *,UBYTE *,UBYTE *,
*                                   UWORD,UWORD,ULONG,...);
*
*   FUNCTION
*  Saves/displays an image held in chunky buffers.
*
*   INPUTS
*  file    - filename to save file as. If NULL or "" then image is
*            displayed on a custom screen.
*  red     - red chunky input.
*  green   - green chunky input.
*  blue    - blue chunky input.
*  width   - width of chunky buffers
*  height  - height of chunky buffers.
*  taglist - pointer to TagItem array.
*
*  Tags are:
*
*  MPIS_MODE   - Data is ULONG CAMG of output IFF file/screen EHB and HAM
*                will be added if required. If not supplied then
*                MPIS_MODENAME will be used. If that is not supplied then
*                a CAMG mode will be generated.
*  MPIS_MODENAME - Data is char * mode name of CAMG of output. Invalid
*                  names are ignored.
*  MPIS_FORMAT - Data is char * specifying output format.
*                Default is MPI_BW16. Values are:
*                MPI_BW16   - "BW16"   - 16 colour ILBM grey scale.
*                MPI_BW256  - "BW256"  - 256 colour ILBM grey scale.
*                MPI_HAM6   - "HAM6"   - HAM6 with fixed (internal) palette.
*                MPI_HAM6P  - "HAM6P"  - HAM6 with generated or supplied
*                                        palette - see MPIS_PALETTE.
*                MPI_HAM8   - "HAM8"   - HAM8 with fixed (internal) palette.
*                MPI_HAM8P  - "HAM8P"  - HAM8 with generated or supplied
*                                        palette - see MPIS_PALETTE.
*                MPI_ILBM24 - "ILBM24" - 24 bit ILBM.
*                MPI_PPM    - "PPM"    - P6 (or P5 if red,green and blue are
*                                        the same).
*                MPI_COLOUR - "COLOUR" - ILBM with generated or supplied
*                                        palette - see MPIS_PALETTE.
*                MPI_EHB    - "EHB"    - EHB with generated or supplied
*                                        palette - see MPIS_PALETTE.
*                MPI_JPEG   - "JPEG"   - JPEG see NOTES.
*                MPI_PNM    - "PNG"    - PNG see NOTES.
*                MPI_DCTV3  - "DCTV3"  - DCTV 3 bit plane format
*                MPI_DCTV4  - "DCTV4"  - DCTV 4 bit plane format
*  MPIS_PALETTE - Data is char * name of ILBM to load palette from.
*  MPIS_COLOURS - Data is ULONG number of colours for MPI_COLOUR.
*                 Default is is number of colours in MPIS_PALETTE if
*                 supplied, otherwise 16. If greater than that from
*                 MPIS_PALETTE then ignored.
*  MPIS_12BIT   - Data is BOOL. If TRUE then use faster 12bit colour palette
*                 generating algorithm for MPI_COLOUR and MPI_EHB. Default
*                 is FALSE to use 18bit algorithm.
*  MPIS_LINEAR  - Data is BOOL. If TRUE then for BW16/BW256 use linear
*                 (not colour based) mapping. (V5.0)
*  MPIS_GREYMAP - Data is UBYTE *. For BW16/BW256 palette map. Must point
*                 to at least 16 of value 0 to 15 for BW16, 256 bytes of
*                 0 to 255 for BW255. (V5.0)
*                 Format is e.g. 0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1
*                   Colour 0 will be black
*                   Colour 1 will be white
*                   etc.
*                 This is the opposite to which you may expect!
*
*   RESULT
*  error - 1 for success, 0 for failure.
*          Use MPImageErrorMessage() to get error.
*
*   EXAMPLE
*
*   NOTES
*  If MPIS_FORMAT is BW16 or BW256 and red,green and blue are the same then
*  a more efficent algorithm is used.
*
*  When displaying on screen click in the top left and press a key to exit.
*
*  If file format is JPEG and env/mpimage/cjpeg is set (e.g. cjpeg "%s" "%s")
*  then cjpeg is used.
*
*  If file format is PNG and env/mpimage/pnmtopng is set
*  (e.g. 'pnmtopng "%s" >"%s"') then pnmtopng is used.
*
*   BUGS
*  Does not work for images > 1024 wide (except PPM/JPEG/PNG).
*  The palette file requires a body which is loaded and then discarded.
*
*  Waits 20 seconds for cjpeg/pnmtopng to start then aborts. This check is
*  not fool proof and the PIPE:xxx can be left hanging.
*
*  Prior to version 4.3 fails to determine a screen mode correctly.
*
*   SEE ALSO
*  MPImageErrorMessage().
*
*****************************************************************************
*
*/
BOOL __asm __saveds
SaveMPImageA(register __a0 const UBYTE *file,
						register __a1 UBYTE *red, register __a2 UBYTE *green,register __a3 UBYTE *blue,
						register __d0 UWORD width, register __d1 UWORD height,
						register __a5 struct TagItem *TagList) {
	struct TagItem *ti;
	ULONG camg = (ULONG)INVALID_ID;
	UBYTE *format = NULL;
	UBYTE *palette = NULL;
	ULONG numcols = 0;
	ULONG numcols1;
	BOOL bit12 = FALSE;
	char *modename;
	ULONG id = (ULONG)INVALID_ID;
	struct NameInfo buff;
	BOOL ret;
	BOOL Linear = FALSE;
	UBYTE *GreyMap = NULL;

	if (ti = FindTagItem(MPIS_MODE,TagList)) {
		camg = ti->ti_Data;
	}
	else {
		if (ti = FindTagItem(MPIS_MODENAME,TagList)) {
			modename = (char *)(ti->ti_Data);
			if (modename && *modename) {
				id = NextDisplayInfo(id);
				while ((id != INVALID_ID) && (camg == INVALID_ID)) {
					if (GetDisplayInfoData(NULL,(UBYTE *)&buff,sizeof(struct NameInfo),DTAG_NAME,id)) {
						if (!Stricmp(buff.Name,modename)) {
							camg = id;
						}
					}
					id = NextDisplayInfo(id);
				}
			}
		}
	}
	if (ti = FindTagItem(MPIS_FORMAT,TagList)) {
		format = (UBYTE *)(ti->ti_Data);
	}
	if (!format) {
		format = MPI_BW16;
	}
	if (ti = FindTagItem(MPIS_COLOURS,TagList)) {
		numcols = ti->ti_Data;
	}
	if (ti = FindTagItem(MPIS_12BIT,TagList)) {
		bit12 = ti->ti_Data;
	}
	if (ti = FindTagItem(MPIS_LINEAR,TagList)) {
		Linear = ti->ti_Data;
	}
	if (ti = FindTagItem(MPIS_GREYMAP,TagList)) {
		GreyMap = (UBYTE *)(ti->ti_Data);
	}
	if (ti = FindTagItem(MPIS_PALETTE,TagList)) {
		palette = (UBYTE *)(ti->ti_Data);
	}
	if (palette) {
		if (!(numcols1 = LoadPalette(palette))) {
			return FALSE;
		}
		if (numcols > numcols1) {
			numcols = numcols1;
		}
	}
	if (numcols > 256) {
		numcols = 256;
	}
	if (!numcols) {
		numcols = 16;
	}
	if (numcols < 2) {
		numcols = 2;
	}
	if (!SetupScreen()) {
		OpenProgressWindow();
	}
	if (!Stricmp(format,MPI_BW16)) {
		if (GreyMap) {
			int i;
			for (i = 0; i < 16; ++i) {
				MyPalette[GreyMap[i]][0]=i + (i<<4);
				MyPalette[GreyMap[i]][1]=i + (i<<4);
				MyPalette[GreyMap[i]][2]=i + (i<<4);
			}
			ret =	SaveBW16(file,width,height, red, green, blue, MyPalette, camg, GreyMap, Linear);
		}
		else {
			ret = SaveBW16(file,width,height, red, green, blue, BW16_Palette, camg, DefGreyMap, Linear);
		}
	}
	else if (!Stricmp(format,MPI_BW256)) {
		if (GreyMap) {
			int i;
			for (i = 0; i < 256; ++i) {
				MyPalette[GreyMap[i]][0]=i;
				MyPalette[GreyMap[i]][1]=i;
				MyPalette[GreyMap[i]][2]=i;
			}
			ret =	SaveBW256(file,width,height, red, green, blue, MyPalette, camg, GreyMap, Linear);
		}
		else {
			ret =	SaveBW256(file,width,height, red, green, blue, BW256_Palette, camg, DefGreyMap, Linear);
		}
	}
	else if (!Stricmp(format,MPI_HAM6)) {
		ret =	SaveHAM6(file,width,height, red, green, blue, HAM6_Palette, camg);
	}
	else if (!Stricmp(format,MPI_HAM8)) {
		ret =	SaveHAM8(file,width,height, red, green, blue, HAM8_Palette, camg);
	}
	else if (!Stricmp(format,MPI_ILBM24)) {
		ret =	Save24(file,width,height, red, green, blue, camg);
	}
	else if (!Stricmp(format,MPI_PPM)) {
		ret =	SavePPM(file,width,height, red, green, blue, 0);
	}
	else if (!Stricmp(format,MPI_HAM6P)) {
		if (palette || ComputePal12(16,width,height,red,green,blue)) {
			ret =	SaveHAM6(file,width,height, red, green, blue, MyPalette, camg);
		}
		else {
			ret = FALSE;
		}
	}
	else if (!Stricmp(format,MPI_HAM8P)) {
		if (palette || ComputePalette(64,width,height,red,green,blue)) {
			ret =	SaveHAM8(file,width,height, red, green, blue, MyPalette, camg);
		}
		else {
			ret = FALSE;
		}
	}
	else if (!Stricmp(format,MPI_COLOUR) && !bit12) {
		if (palette || ComputePalette(numcols,width,height,red,green,blue)) {
			ret =	SaveColour(file,width,height, red, green, blue, camg, numcols);
		}
		else {
			ret = FALSE;
		}
	}
	else if (!Stricmp(format,MPI_COLOUR) && bit12) {
		if (palette || ComputePal12(numcols,width,height,red,green,blue)) {
			ret =	SaveColour12(file,width,height, red, green, blue, camg, numcols);
		}
		else {
			ret = FALSE;
		}
	}
	else if (!Stricmp(format,MPI_EHB)) {
		if (palette || ComputePalEHB(width,height,red,green,blue)) {
			ret =	SaveEHB(file,width,height, red, green, blue, camg);
		}
		else {
			ret = FALSE;
		}
	}
	else if (!Stricmp(format,MPI_JPEG)) {
		ret =	SavePPM(file,width,height, red, green, blue, 1);
	}
	else if (!Stricmp(format,MPI_PNG)) {
		ret =	SavePPM(file,width,height, red, green, blue, 2);
	}
	else if (!Stricmp(format,MPI_DCTV3)) {
		ret =	SaveDCTV(file,width,height, red, green, blue, camg, FALSE);
	}
	else if (!Stricmp(format,MPI_DCTV4)) {
		ret =	SaveDCTV(file,width,height, red, green, blue, camg, TRUE);
	}
	else {
		sprintf(ErrorMessage,"Invalid Save format '%s'",format);
		ret =  FALSE;
	}
	CloseProgressWindow();
	CloseDownScreen();
	return ret;
}

// returns 0 for error - message already set up
// otherwise returns number of colours in palette
static ULONG
LoadPalette(UBYTE *filename) {
	struct ILBMInfo ilbm = {0};		// ILBM stuff
	int i;
	ULONG numcols;

	AddMessage("Loading Palette");
	if (ilbm.ParseInfo.iff = AllocIFF()) {
		ilbm.ParseInfo.propchks = props;
		ilbm.ParseInfo.collectchks = nowt;
		ilbm.ParseInfo.stopchks = stops;
		if (loadbrush(&ilbm,(char *)filename)) {
			closeifile(&(ilbm.ParseInfo));
			unloadbrush(&ilbm);
			FreeIFF(ilbm.ParseInfo.iff);
			return 0;
		}
		closeifile(&(ilbm.ParseInfo));
		numcols = ilbm.ncolors;
		AddMessage("Geting Colours");
		for (i=0; i < numcols; ++i) {
			MyPalette[i][0]=(ilbm.colortable32[i].r)>>24;
			MyPalette[i][1]=(ilbm.colortable32[i].g)>>24;
			MyPalette[i][2]=(ilbm.colortable32[i].b)>>24;
		}
		unloadbrush(&ilbm);
		FreeIFF(ilbm.ParseInfo.iff);
		return numcols;
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate IFF structure");
		return 0;
	}
}

#define HSIZE     32768          /* size of image histogram    */
/* Macros for converting between (r,g,b)-colors and 15-bit     */
/* colors follow.                                              */
#define RGB(r,g,b) (UWORD)(((b)&~7)<<7)|(((g)&~7)<<2)|((r)>>3)
static BOOL
ComputePalette(UWORD Depth,UWORD width,UWORD height,UBYTE *red,UBYTE *green,UBYTE *blue) {
	UWORD *hist;
	UBYTE *r=red,*g=green,*b=blue;
	int i,j;
	if (hist = AllocVec(sizeof(UWORD)*HSIZE*2,MEMF_CLEAR)) {
		AddMessage("Creating Histogram");
		SetMax(height);
		memset(MyPalette,0,256*3);
		for (i = 0; i < height; ++i) {
			SetCur(i);
			for (j = 0; j < width; ++j) {
				hist[RGB(*r++,*g++,*b++)] += 1;
			}
		}
		AddMessage("Creating Palette");
		MedianCut(hist,MyPalette,Depth,&(hist[HSIZE]));
		FreeVec(hist);
		return TRUE;
	}
	else {
		strcpy(ErrorMessage,"Out of Memory");
		return FALSE;
	}
}

#define HSIZE6    4096           /* size of image histogram    */
/* Macros for converting between (r,g,b)-colors and 12-bit     */
/* colors follow.                                              */
#define RGB6(r,g,b) (UWORD)((((b)&~15)<<4)|((g)&~15)|((r)>>4))
#define RED6(x)     ((((x)&15)<<4)|((x)&15))
#define GREEN6(x)   (((((x)>>4)&15)<<4)|((((x)>>4)&15)))
#define BLUE6(x)    (((((x)>>8)&15)<<4)|((((x)>>8)&15)))
static BOOL
ComputePal12(UWORD Depth, UWORD width,UWORD height,UBYTE *red,UBYTE *green,UBYTE *blue) {
	UWORD *hist;
	UBYTE *r=red,*g=green,*b=blue;
	int i,j;
	if (hist = AllocVec(sizeof(UWORD)*HSIZE6*2,MEMF_CLEAR)) {
		AddMessage("Creating Histogram");
		SetMax(height);
		memset(MyPalette,0,256*3);
		for (i = 0; i < height; ++i) {
			SetCur(i);
			for (j = 0; j < width; ++j) {
				hist[RGB6(*r++,*g++,*b++)] += 1;
			}
		}
		AddMessage("Creating Palette");
		MedHam6(hist,MyPalette,Depth,&(hist[HSIZE6]));
		FreeVec(hist);
		return TRUE;
	}
	else {
		strcpy(ErrorMessage,"Out of Memory");
		return FALSE;
	}
}

static BOOL
ComputePalEHB(UWORD width,UWORD height,UBYTE *red,UBYTE *green,UBYTE *blue) {
	UWORD *hist;
	UBYTE *r=red,*g=green,*b=blue;
	int i,j,k;
	long maxdiff,diff,t;

	if (hist = AllocVec(sizeof(UWORD)*HSIZE6*4,MEMF_CLEAR)) {
		AddMessage("Creating Histogram");
		memset(MyPalette,0,256*3);
		for (i = 0; i < height; ++i) {
			for (j = 0; j < width; ++j) {
				if ((*r > 127) || (*g > 127) || (*b > 127)) {
					hist[RGB6(*r++,*g++,*b++)] += 1;
				}
				else {
					hist[RGB6(*r++,*g++,*b++)+(HSIZE6*2)] += 1;
				}
			}
		}
		// Compute top 32 colours and bottom 32 colours
		AddMessage("Creating High Palette");
		MedHam6(hist,MyPalette,32,&(hist[HSIZE6]));
		AddMessage("Creating Low Palette");
		MedHam6(&(hist[HSIZE6*2]),(UBYTE (*)[3])&(MyPalette[32][0]),32,&(hist[HSIZE6*3]));
		AddMessage("Merging Palettes");
		for (i = 0; i < 32; i++) {
			maxdiff = 0x7fffffff;
			for (j = 32; (j < 64) && maxdiff; ++j) {
				t = (int)MyPalette[i][0] - ((int)MyPalette[j][0]*2);
				diff = t*t * 2;
				if (diff < maxdiff) {
					t = (int)MyPalette[i][1] - ((int)MyPalette[j][1]*2);
					diff += t*t * 4;
					if (diff < maxdiff) {
						t = (int)MyPalette[i][2] - ((int)MyPalette[j][2]*2);
						diff += t*t;
						if (diff < maxdiff) {
							maxdiff = diff;
							k = j;
						}
					}
				}
			}
			for (j = 0; j < 3; ++j) {
				t = (MyPalette[i][j] + (MyPalette[k][j]*2)) / 2;
				if (t > 255) {
					MyPalette[i][k] = 255;
				}
				else {
					MyPalette[i][k] = t;
				}
			}
		}
		FreeVec(hist);
		return TRUE;
	}
	else {
		strcpy(ErrorMessage,"Out of Memory");
		return FALSE;
	}
}

// Hack a sort of modeid (caller must merge in HAM itself...)
static ULONG
GetMode(UWORD height, UWORD width,UWORD depth, ULONG camg, ULONG flags, const UBYTE *filename) {
	ULONG mode=0;
	if (camg != INVALID_ID) {
		return camg;
	}
	AddMessage("Creating CAMG");
	if (!filename) {
		camg = BestModeID(BIDTAG_DIPFMustHave,		flags,
								BIDTAG_DIPFMustNotHave,	SPECIAL_FLAGS & (~flags),
								BIDTAG_NominalWidth,		width,
								BIDTAG_NominalHeight,	height,
								BIDTAG_Depth,				depth,
								TAG_END);
		if (camg != INVALID_ID) {
			return camg;
		}
	}
	if (depth == 24) {	// 24 bit so can set both hires and lace
		if (width > 400) {	// more than lores overscan
			mode |= HIRES;
		}
		if (height > 300) {	// more than nolace overscan
			mode |= LACE;
		}
	}
	else {
		if (depth > 6) {	// Must be AGA only so can have hires
			if (width > 400) {	// more than lores overscan
				mode |= HIRES;
			}
			if (height > 300) {	// more than nolace overscan
				mode |= LACE;
			}
		}
		else {
			if (depth > 4) {	// May be HAM6 or 32 colour or EHB - don't set HIRES for non-AGA
				if (height > 300) {
					mode |= LACE;
				}
			}
			else {	// 16 or less colours
				if (width > 400) {	// more than lores overscan
					mode |= HIRES;
				}
				if (height > 300) {	// more than nolace overscan
					mode |= LACE;
				}
			}
		}
	}
	if (flags & DIPF_IS_HAM) {
		return (mode | HAM);
	}
	if (flags & DIPF_IS_EXTRAHALFBRITE) {
		return (mode | EXTRA_HALFBRITE);
	}
	return mode;
}

/* save 16 grey scale
 * FileName : file to save
 * width    : width of image
 * height   : height of image
 * red      : red
 * green    : green
 * blue     : blue chunky bit map
 */
static BOOL
SaveBW16(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UBYTE palette[][3], ULONG camg, UBYTE *GreyMap, BOOL Linear) {
	UBYTE *r,*g,*b;
	UBYTE *old;
	UWORD x,y;
	BOOL OkFlag;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	struct c2pStruct c2p;
	int *word,*w;
	int diff,cur,new,car;

	AddMessage("Saving BW16");
	// allocate IFF stuff
	if ((word = AllocVec((sizeof(int))*(width+1),MEMF_CLEAR)) &&
		 (bitmap = MyAllocBitMap(width,height,4,FileName))) {
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			if ((red == green) && (red == blue)) {	// grey scale input
				r = red;
				old = red;
				// for each line
				AddMessage("Grey input");
				SetMax(height);
				for (y=0;
					  y<height;
					  y++) {
					SetCur(y);
					w = word;	// Carrys from previous row
					diff = 0;	// Carry from last column
					car = *w;	// Carry from last row
					// for each column
					for (x=0;
						  x < width;
						  x++) {
						// convert rgb to 16 grey scale
						cur = *r++ + diff + car;
						new = cur / 17;
						if (new > 15) {
							*old++ = GreyMap[15];
							diff = cur - 15;
						}
						else {
							if (new < 0) {
								*old++ = GreyMap[0];
								diff = cur;
							}
							else {
								*old++ = GreyMap[new];
								diff = cur - (new * 17);
							}
						}
						*w++ = (diff * 3)/8;	//	3/8 to pixel below
						car = *w;				// carry to next pixel
						*w = diff / 4;			// 2/8 to pixel below right
						diff = (diff * 3)/8;	// 3/8 to pixel right;
					}
				}
			}
			else {	// not greyscale input
				r = red;
				g = green;
				b = blue;
				old = red;
				AddMessage("Colour Input");
				if (Linear) {
					AddMessage("Linear");
				}
				SetMax(height);
				// for each line
				for (y=0;
					  y<height;
					  y++) {
					SetCur(y);
					w = word;	// Carrys from previous row
					diff = 0;	// Carry from last column
					car = *w;	// Carry from last row
					// for each column
					if (Linear) {
						for (x=0;
							  x < width;
							  x++) {
							// convert rgb to 16 grey scale
							cur = ((int)*r++ + (int)*g++ + (int)*b++)/3 + diff + car;
							new = cur / 17;
							if (new > 15) {
								*old++ = GreyMap[15];
								diff = cur - 15;
							}
							else {
								if (new < 0) {
									*old++ = GreyMap[0];
									diff = cur;
								}
								else {
									*old++ = GreyMap[new];
									diff = cur - (new * 17);
								}
							}
							*w++ = (diff * 3)/8;	//	3/8 to pixel below
							car = *w;				// carry to next pixel
							*w = diff / 4;			// 2/8 to pixel below right
							diff = (diff * 3)/8;	// 3/8 to pixel right;
						}
					}
					else {
						for (x=0;
							  x < width;
							  x++) {
							// convert rgb to 16 grey scale
							cur = (30 * (int)*r++) + (59 * (int)*g++) + (11 * (int)*b++) + diff + car + 100;
							new = cur / 1700;
							if (new > 15) {
								*old++ = GreyMap[15];
								diff = cur - (15 * 1700);
							}
							else {
								if (new < 0) {
									*old++ = GreyMap[0];
									diff = cur;
								}
								else {
									*old++ = GreyMap[new];
									diff = cur - (new * 1700);
								}
							}
							*w++ = (diff * 3)/8;	//	3/8 to pixel below
							car = *w;				// carry to next pixel
							*w = diff / 4;			// 2/8 to pixel below right
							diff = (diff * 3)/8;	// 3/8 to pixel right;
						}
					}
				}
			}
			// convert chunky to planar
			c2p.bmap = (struct BitMap *)bitmap;
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			// and save IFF
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap, GetMode(height,width,4,camg,0,FileName),
									 width,  height, width, height,
									 palette, 16, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklisMP */
									 (UBYTE *)FileName,4);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			OkFlag = FALSE;
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	if (word) {
		FreeVec(word);
	}
	return OkFlag;
}

/* save 256 grey scale
 * see SaveBW16()
 */
static BOOL
SaveBW256(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UBYTE palette[][3], ULONG camg, UBYTE *GreyMap, BOOL Linear) {
	UBYTE *r,*g,*b;
	UBYTE *old;
	UWORD x,y;
	BOOL OkFlag;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	struct c2pStruct c2p;
	int *word,*w;
	int diff,cur,new,car;

	AddMessage("Saving BW256");
	// allocate IFF stuff
	if ((word = AllocVec((sizeof(int))*(width+1),MEMF_CLEAR)) &&
		 (bitmap = MyAllocBitMap(width,height,8,FileName))) {
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			if ((red == green) && (red == blue)) {	// grey scale input
				r = red;
				old = red;
				AddMessage("Grey input");
				SetMax(height);
				for (y=0;
					  y<height;
					  y++) {
					SetCur(y);
					w = word;	// Carrys from previous row
					diff = 0;	// Carry from last column
					car = *w;	// Carry from last row
					for (x=0;
						  x < width;
						  x++) {
						// convert rgb to 256 grey scale
						cur = (*r++ * 256) + diff + car;
						new = cur / 256;
						if (new > 255) {
							*old++ = GreyMap[255];
							diff = cur - (255 * 256);
						}
						else {
							if (new < 0) {
								*old++ = GreyMap[0];
								diff = cur;
							}
							else {
								*old++ = GreyMap[new];
								diff = cur - (new * 256);
							}
						}
						*w++ = (diff * 3)/8;	//	3/8 to pixel below
						car = *w;				// carry to next pixel
						*w = diff / 4;			// 2/8 to pixel below right
						diff = (diff * 3)/8;	// 3/8 to pixel right;
					}
				}
			}
			else {
				r = red;
				g = green;
				b = blue;
				old = red;
				AddMessage("Colour Input");
				if (Linear) {
					AddMessage("Linear");
				}
				SetMax(height);
				for (y=0;
					  y<height;
					  y++) {
					SetCur(y);
					w = word;	// Carrys from previous row
					diff = 0;	// Carry from last column
					car = *w;	// Carry from last row
					if (Linear) {
						for (x=0;
							  x < width;
							  x++) {
							// convert rgb to 256 grey scale
							cur = (int)*r++ + (int)*g++ + (int)*b++ + diff + car;
							new = cur / 3;
							if (new > 255) {
								*old++ = GreyMap[255];
								diff = cur - (255 * 3);
							}
							else {
								if (new < 0) {
									*old++ = GreyMap[0];
									diff = cur;
								}
								else {
									*old++ = GreyMap[new];
									diff = cur - (new * 3);
								}
							}
							*w++ = (diff * 3)/8;	//	3/8 to pixel below
							car = *w;				// carry to next pixel
							*w = diff / 4;			// 2/8 to pixel below right
							diff = (diff * 3)/8;	// 3/8 to pixel right;
						}
					}
					else {
						for (x=0;
							  x < width;
							  x++) {
							// convert rgb to 256 grey scale
							cur = (30 * (int)*r++) + (59 * (int)*g++) + (11 * (int)*b++) + diff + car;
							new = cur / 100;
							if (new > 255) {
								*old++ = GreyMap[255];
								diff = cur - (255 * 100);
							}
							else {
								if (new < 0) {
									*old++ = GreyMap[0];
									diff = cur;
								}
								else {
									*old++ = GreyMap[new];
									diff = cur - (new * 100);
								}
							}
							*w++ = (diff * 3)/8;	//	3/8 to pixel below
							car = *w;				// carry to next pixel
							*w = diff / 4;			// 2/8 to pixel below right
							diff = (diff * 3)/8;	// 3/8 to pixel right;
						}
					}
				}
			}
			c2p.bmap = (struct BitMap *)bitmap;
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap, GetMode(height,width,8,camg,0,FileName),
									 width,  height, width, height,
									 palette, 256, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 (UBYTE *)FileName,8);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			OkFlag = FALSE;
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	if (word) {
		FreeVec(word);
	}
	return OkFlag;
}

/* save HAM6
 * see SaveBW16()
 */
static BOOL
SaveHAM6(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue,UBYTE palette[][3], ULONG camg) {
	UBYTE *r,*g,*b,*p;
	UBYTE rr,gg,bb;
	UBYTE *old;
	UWORD x,y;
	ULONG maxdiff;
	ULONG diff;
	LONG t;
	UWORD k;
	UWORD index;
	BOOL OkFlag;
	UBYTE lr,lg,lb;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	struct c2pStruct c2p;
	int *word = NULL,*wr,*wg,*wb;
	int diffr,curr,newr,carr;
	int diffg,curg,newg,carg;
	int diffb,curb,newb,carb;
	UBYTE *InvMap;
	ULONG *Diffs = NULL;
	int i,ir,ig,ib;

	AddMessage("Saving HAM6");
	if ((InvMap = AllocVec(4906,MEMF_ANY)) &&
		 (Diffs = AllocVec(4906*sizeof(ULONG),MEMF_ANY)) &&
		 (word = AllocVec((sizeof(int))*(width+1)*3,MEMF_CLEAR)) &&
		 (bitmap = MyAllocBitMap(width,height,6,FileName))) {
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			AddMessage("Computing Map");
			// Compute inverse color map
			for (i = 0; i < 4096; ++i) {
				ir = RED6(i);
				ig = GREEN6(i);
				ib = BLUE6(i);
				// Find closest color
				maxdiff = 0x7FFFFFFF;
				p = &(palette[0][0]);
				for (k = 0;
					  (k < 16) && maxdiff;
					  ++k) {
					t = ir - (int)*p++;
					diff = t*t * 2;
					if (diff < maxdiff) {
						t = ig - (int)*p++;
						diff += t*t * 4;
						if (diff < maxdiff) {
							t = ib - (int)*p++;
							diff += t*t;
							if (diff < maxdiff) {
								maxdiff = diff;
								index = k;
							}
						}
						else {
							++p;
						}
					}
					else {
						++p;
						++p;
					}
				}
				InvMap[i] = index;
				Diffs[i] = maxdiff;
			}
			r = red;
			g = green;
			b = blue;
			old = red;
			AddMessage("Remapping");
			SetMax(height);
			for (y=0;
				  y<height;
				  y++) {
				SetCur(y);
				lr = palette[0][0];
				lg = palette[0][1];
				lb = palette[0][2];
				wr = word;	// Carrys from previous row
				diffr = 0;	// Carry from last column
				carr = *wr;	// Carry from last row
				wg = word+(width+1);
				diffg = 0;
				carg = *wg;
				wb = word+(2*(width+1));
				diffb = 0;
				carb = *wb;
				for (x=0;
					  x < width;
					  x++) {
					curr = ((int)*r * 256) + diffr + carr;
					newr = curr / 256;
					if (newr > 255) {
						rr = 255;
					}
					else {
						if (newr < 0) {
							rr = 0;
						}
						else {
							rr = newr;
						}
					}
					curg = ((int)*g * 256) + diffg + carg;
					newg = curg / 256;
					if (newg > 255) {
						gg = 255;
					}
					else {
						if (newg < 0) {
							gg = 0;
						}
						else {
							gg = newg;
						}
					}
					curb = ((int)*b * 256) + diffb + carb;
					newb = curb / 256;
					if (newb > 255) {
						bb = 255;
					}
					else {
						if (newb < 0) {
							bb = 0;
						}
						else {
							bb = newb;
						}
					}
					// colour if we use HAM to change R or G or B
					palette[16][0] = rr;
					palette[16][1] = lg;
					palette[16][2] = lb;
					palette[17][0] = lr;
					palette[17][1] = gg;
					palette[17][2] = lb;
					palette[18][0] = lr;
					palette[18][1] = lg;
					palette[18][2] = bb;
					// Find closest color
					index = InvMap[RGB6(rr,gg,bb)];
					maxdiff = Diffs[RGB6(rr,gg,bb)];
					p = &(palette[18][2]);
					for (k = 0;
						  (k < 3) && maxdiff;
						  ++k) {
						t = (int)bb - (int)*p--;
						diff = t*t;
						if (diff < maxdiff) {
							t = (int)gg - (int)*p--;
							diff += t*t * 4;
							if (diff < maxdiff) {
								t = (int)rr - (int)*p--;
								diff += t*t * 2;
								if (diff < maxdiff) {
									maxdiff = diff;
									index = 18 - k;
								}
							}
							else {
								--p;
							}
						}
						else {
							--p;
							--p;
						}
					}
					lr = palette[index][0];
					lg = palette[index][1];
					lb = palette[index][2];
					// FS dither
					diffr = curr - (lr * 256);
					*wr++ = (diffr * 3)/8;	//	3/8 to pixel below
					carr = *wr;					// carry to next pixel
					*wr = diffr / 4;			// 2/8 to pixel below right
					diffr = (diffr * 3)/8;	// 3/8 to pixel right;
					diffg = curg - (lg * 256);
					*wg++ = (diffg * 3)/8;
					carg = *wg;
					*wg = diffg / 4;
					diffg = (diffg * 3)/8;
					diffb = curb - (lb * 256);
					*wb++ = (diffb * 3)/8;
					carb = *wb;
					*wb = diffb / 4;
					diffb = (diffb * 3)/8;
					// Set HAM bits if required
					if (index == 16) {
						*old++ = (rr>>4) | 0x20;
					}
					else {
						if (index == 17) {
							*old++ = (gg>>4) | 0x30;
						}
						else {
							if (index == 18) {
								*old++ = (bb>>4) | 0x10;
							}
							else {
								*old++ = index;
							}
						}
					}
					r++;
					g++;
					b++;
				}
			}
			c2p.bmap = (struct BitMap *)bitmap;
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap, GetMode(height,width,6,camg,DIPF_IS_HAM,FileName),
									 width,  height, width, height,
									 palette, 16, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 (UBYTE *)FileName,6);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
			OkFlag = FALSE;
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	if (word) {
		FreeVec(word);
	}
	if (InvMap) {
		FreeVec(InvMap);
	}
	if (Diffs) {
		FreeVec(Diffs);
	}
	return OkFlag;
}

/* save HAM8
 * see SaveHAM6()
 */
static BOOL
SaveHAM8(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UBYTE palette[][3], ULONG camg) {
	UBYTE *r,*g,*b,*p;
	UBYTE rr,gg,bb;
	UBYTE *old;
	UWORD x,y;
	ULONG maxdiff;
	ULONG diff;
	LONG t;
	UWORD k;
	UWORD index;
	BOOL OkFlag;
	UBYTE lr,lg,lb;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	struct c2pStruct c2p;
	int *word,*wr,*wg,*wb;
	int diffr,curr,newr,carr;
	int diffg,curg,newg,carg;
	int diffb,curb,newb,carb;

	AddMessage("Saving HAM8");
	if ((word = AllocVec((sizeof(int))*(width+1)*3,MEMF_CLEAR)) &&
		 (bitmap = MyAllocBitMap(width,height,8,FileName))) {
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			r = red;
			g = green;
			b = blue;
			old = red;
			AddMessage("Remapping");
			SetMax(height);
			for (y=0;
				  y<height;
				  y++) {
				SetCur(y);
				lr = palette[0][0];
				lg = palette[0][1];
				lb = palette[0][2];
				wr = word;	// Carrys from previous row
				diffr = 0;	// Carry from last column
				carr = *wr;	// Carry from last row
				wg = word+(width+1);
				diffg = 0;
				carg = *wg;
				wb = word+(2*(width+1));
				diffb = 0;
				carb = *wb;
				for (x=0;
					  x < width;
					  x++) {
					curr = ((int)*r * 256) + diffr + carr;
					newr = curr / 256;
					if (newr > 255) {
						rr = 255;
					}
					else {
						if (newr < 0) {
							rr = 0;
						}
						else {
							rr = newr;
						}
					}
					curg = ((int)*g * 256) + diffg + carg;
					newg = curg / 256;
					if (newg > 255) {
						gg = 255;
					}
					else {
						if (newg < 0) {
							gg = 0;
						}
						else {
							gg = newg;
						}
					}
					curb = ((int)*b * 256) + diffb + carb;
					newb = curb / 256;
					if (newb > 255) {
						bb = 255;
					}
					else {
						if (newb < 0) {
							bb = 0;
						}
						else {
							bb = newb;
						}
					}
					palette[64][0] = rr;
					palette[64][1] = lg;
					palette[64][2] = lb;
					palette[65][0] = lr;
					palette[65][1] = gg;
					palette[65][2] = lb;
					palette[66][0] = lr;
					palette[66][1] = lg;
					palette[66][2] = bb;
					// Find closest color
					maxdiff = 0x7FFFFFFF;
					p = &(palette[66][2]);
					for (k = 0;
						  (k < 67) && maxdiff;
						  ++k) {
						t = (int)bb - (int)*p--;
						diff = t*t;
						if (diff < maxdiff) {
							t = (int)gg - (int)*p--;
							diff += t*t * 4;
							if (diff < maxdiff) {
								t = (int)rr - (int)*p--;
								diff += t*t * 2;
								if (diff < maxdiff) {
									maxdiff = diff;
									index = 66 - k;
								}
							}
							else {
								--p;
							}
						}
						else {
							--p;
							--p;
						}
					}
					lr = palette[index][0];
					lg = palette[index][1];
					lb = palette[index][2];
					// FS dither
					diffr = curr - (lr * 256);
					*wr++ = (diffr * 3)/8;	//	3/8 to pixel below
					carr = *wr;					// carry to next pixel
					*wr = diffr / 4;			// 2/8 to pixel below right
					diffr = (diffr * 3)/8;	// 3/8 to pixel right;
					diffg = curg - (lg * 256);
					*wg++ = (diffg * 3)/8;
					carg = *wg;
					*wg = diffg / 4;
					diffg = (diffg * 3)/8;
					diffb = curb - (lb * 256);
					*wb++ = (diffb * 3)/8;
					carb = *wb;
					*wb = diffb / 4;
					diffb = (diffb * 3)/8;
					if (index == 64) {
						*old++ = (rr>>2) | 0x80;
					}
					else {
						if (index == 65) {
							*old++ = (gg>>2) | 0xc0;
						}
						else {
							if (index == 66) {
								*old++ = (bb>>2) | 0x40;
							}
							else {
								*old++ = index;
							}
						}
					}
					r++;
					g++;
					b++;
				}
			}
			c2p.bmap = (struct BitMap *)bitmap;
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap, GetMode(height,width,8,camg,DIPF_IS_HAM,FileName),
									 width,  height, width, height,
									 palette, 64, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 (UBYTE *)FileName,8);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
			OkFlag = FALSE;
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	if (word) {
		FreeVec(word);
	}
	return OkFlag;
}

/* save 24
 * see Save24()
 */
static BOOL
Save24(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg) {
	struct ILBMInfo 	ilbm = {0};		// IFF stuff to save
	BOOL OkFlag;
	UBYTE *Planes[24];
	struct MyBitMap *bitmap;
	int i;
	struct c2pStruct c2p;

	AddMessage("Saving ILBM24");
	// Set up BitMap
	if (bitmap = MyAllocBitMap(width,height,24,FileName)) {
		for (i=0; i<24; i++) {
			Planes[i] = bitmap->BitMap.Planes[i];
		}
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			// i.e. 24 bit ILBM?
			// so convert chunky to planar
			bitmap->BitMap.Depth = 8;
			c2p.bmap = &(bitmap->BitMap);
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			bitmap->BitMap.Planes[0] = Planes[8];
			bitmap->BitMap.Planes[1] = Planes[9];
			bitmap->BitMap.Planes[2] = Planes[10];
			bitmap->BitMap.Planes[3] = Planes[11];
			bitmap->BitMap.Planes[4] = Planes[12];
			bitmap->BitMap.Planes[5] = Planes[13];
			bitmap->BitMap.Planes[6] = Planes[14];
			bitmap->BitMap.Planes[7] = Planes[15];
			c2p.chunkybuffer = green;
			ChunkyToPlanar(&c2p);
			bitmap->BitMap.Planes[0] = Planes[16];
			bitmap->BitMap.Planes[1] = Planes[17];
			bitmap->BitMap.Planes[2] = Planes[18];
			bitmap->BitMap.Planes[3] = Planes[19];
			bitmap->BitMap.Planes[4] = Planes[20];
			bitmap->BitMap.Planes[5] = Planes[21];
			bitmap->BitMap.Planes[6] = Planes[22];
			bitmap->BitMap.Planes[7] = Planes[23];
			c2p.chunkybuffer = blue;
			ChunkyToPlanar(&c2p);
			InitBitMap((struct BitMap *)bitmap,24,width,height);
			for (i=0; i<24; i++) {
				bitmap->BitMap.Planes[i] = Planes[i];
			}
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap, GetMode(height,width,24,camg,0,FileName),
									 width,  height, width, height,
									 NULL, 0, 0,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 (UBYTE *)FileName,24);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
			OkFlag = FALSE;
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	return OkFlag;
}

static BOOL
SavePPM(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, UWORD jpeg) {
	int x,y;
	int i,j;
	UBYTE *arrayr,*arrayg,*arrayb;
	BPTR				fh;			// File handle for PPM
	BOOL OkFlag = TRUE;
	UBYTE buffer[32];
	char jpegbuf[120];
	char jpegpip[32];
	char jpegcom[256];
	char *jfilename;

	if (!jpeg) {
		AddMessage("Saving PPM");
		jfilename = FileName;
	}
	else {
		if (jpeg == 1) {
			AddMessage("Saving JPEG");
		}
		else {
			AddMessage("Saving PNG");
		}
		if (((jpeg == 1) && (GetVar("mpimage/cjpeg",jpegbuf,119,0) > 0)) ||
			 ((jpeg == 2) && (GetVar("mpimage/pnmtopng",jpegbuf,119,0) > 0))) {
			if (fh = Open(FileName,MODE_NEWFILE)) {
				Close(fh);
			}
			else {
				strcpy(ErrorMessage,"Can't open output file");
				return FALSE;
			}
			sprintf(jpegpip,"%s%ld","PIPE:",red);
			AddMessage(jpegpip);
			sprintf(jpegcom,jpegbuf,jpegpip,FileName);
			SystemTags(jpegcom,
					SYS_Input,	0,
					SYS_Output,	0,
					SYS_Asynch,	TRUE,
					NP_StackSize,	16000,
					NP_Name,		(jpeg == 1) ? "MPImage CJPEG task" : "MPImage PNMTOPNG task",
					TAG_END);
			jfilename = jpegpip;
			j = 0;
			for (i = 0;
				  (i < 20) && (!j);
				  ++i) {
				BYTE pri;
				// Try to write to file - if this works then wait again
				pri = SetTaskPri(FindTask(0),21);
				if (fh = Open(FileName,MODE_NEWFILE)) {
					Close(fh);
					if (jpeg == 1) {
						AddMessage("Waiting for cjpeg");
					}
					else {
						AddMessage("Waiting for pnmtopng");
					}
					Delay(50*1);
				}
				else {
					j = 1;
				}
				SetTaskPri(FindTask(0),pri);
			}
			if (!j) {
				if (jpeg == 1) {
					strcpy(ErrorMessage,"cjpeg failed to start");
				}
				else {
					strcpy(ErrorMessage,"pnmtopng failed to start");
				}
				return FALSE;
			}
		}
		else {
			if (jpeg == 1) {
				strcpy(ErrorMessage,"Can't GetVar mpimage/cjpeg");
			}
			else {
				strcpy(ErrorMessage,"Can't GetVar mpimage/pnmtopng");
			}
			return FALSE;
		}
	}
	if (fh = Open((UBYTE *)jfilename,MODE_NEWFILE)) {
		if ((red == green) && (red == blue)) {	// grey scale input
			AddMessage("Saving P5");
			sprintf(buffer,"P5\n%ld %ld\n255\n",width,height);
			FPuts(fh,buffer);
			if (FWrite(fh,red,width,height) != height) {
				OkFlag = FALSE;
				strcpy(ErrorMessage,"Can't write to output file");
			}
		}
		else {
			AddMessage("Saving P6");
			sprintf(buffer,"P6\n%ld %ld\n255\n",width,height);
			FPuts(fh,buffer);
			arrayr=red;
			arrayg=green;
			arrayb=blue;
			SetMax(height);
			// loop thru lines
			for (y=0;
				  (y<height) && OkFlag;
					  y++) {
				SetCur(y);
				// Loop thru columns
				for (x=0;
					  (x < width) && OkFlag;
					  x++) {
					if (FPutC(fh,*arrayr++) == EOF) {
						OkFlag = FALSE;
					}
					else {
						if (FPutC(fh,*arrayg++) == EOF) {
							OkFlag = FALSE;
						}
						else {
							if (FPutC(fh,*arrayb++) == EOF) {
								OkFlag = FALSE;
							}
						}
					}
					if (!OkFlag) {
						strcpy(ErrorMessage,"Can't write to output file");
					}
				}
			}
		}
		if (!Close(fh)) {
			strcpy(ErrorMessage,"Can't close output file");
			OkFlag = FALSE;
		}
	}
	else {
		strcpy(ErrorMessage,"Can't open output file");
		OkFlag = FALSE;
	}
	return OkFlag;
}

static BOOL
SaveColour(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg, ULONG numcols) {
	UBYTE *r,*g,*b,*p;
	UBYTE rr,gg,bb;
	UBYTE *old;
	UWORD x,y;
	ULONG maxdiff;
	ULONG diff;
	LONG t;
	UWORD k;
	UWORD index;
	BOOL OkFlag;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	struct c2pStruct c2p;
	int *word,*wr,*wg,*wb;
	int diffr,curr,newr,carr;
	int diffg,curg,newg,carg;
	int diffb,curb,newb,carb;
	int Depth;

	AddMessage("Saving COLOUR");
	if (numcols > 128) {
		Depth = 8;
	}
	else {
		if (numcols > 64) {
			Depth = 7;
		}
		else {
			if (numcols > 32) {
				Depth = 6;
			}
			else {
				if (numcols > 16) {
					Depth = 5;
				}
				else {
					if (numcols > 8) {
						Depth = 4;
					}
					else {
						if (numcols > 4) {
							Depth = 3;
						}
						else {
							if (numcols > 2) {
								Depth = 2;
							}
							else {
								Depth = 1;
							}
						}
					}
				}
			}
		}
	}
	if ((word = AllocVec((sizeof(int))*(width+1)*3,MEMF_CLEAR)) &&
		 (bitmap = MyAllocBitMap(width,height,Depth,FileName))) {
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			r = red;
			g = green;
			b = blue;
			old = red;
			AddMessage("Remapping");
			SetMax(height);
			for (y=0;
				  y<height;
				  y++) {
				SetCur(y);
				wr = word;	// Carrys from previous row
				diffr = 0;	// Carry from last column
				carr = *wr;	// Carry from last row
				wg = word+(width+1);
				diffg = 0;
				carg = *wg;
				wb = word+(2*(width+1));
				diffb = 0;
				carb = *wb;
				for (x=0;
					  x < width;
					  x++) {
					curr = ((int)*r * 256) + diffr + carr;
					newr = curr / 256;
					if (newr > 255) {
						rr = 255;
					}
					else {
						if (newr < 0) {
							rr = 0;
						}
						else {
							rr = newr;
						}
					}
					curg = ((int)*g * 256) + diffg + carg;
					newg = curg / 256;
					if (newg > 255) {
						gg = 255;
					}
					else {
						if (newg < 0) {
							gg = 0;
						}
						else {
							gg = newg;
						}
					}
					curb = ((int)*b * 256) + diffb + carb;
					newb = curb / 256;
					if (newb > 255) {
						bb = 255;
					}
					else {
						if (newb < 0) {
							bb = 0;
						}
						else {
							bb = newb;
						}
					}
					// Find closest color
					maxdiff = 0x7FFFFFFF;
					p = &(MyPalette[0][0]);
					for (k = 0;
						  (k < numcols) && maxdiff;
						  ++k) {
						t = (int)rr - (int)*p++;
						diff = t*t * 2;
						if (diff < maxdiff) {
							t = (int)gg - (int)*p++;
							diff += t*t * 4;
							if (diff < maxdiff) {
								t = (int)bb - (int)*p++;
								diff += t*t;
								if (diff < maxdiff) {
									maxdiff = diff;
									index = k;
								}
							}
							else {
								++p;
							}
						}
						else {
							++p;
							++p;
						}
					}
					// FS dither
					diffr = curr - (MyPalette[index][0] * 256);
					*wr++ = (diffr * 3)/8;	//	3/8 to pixel below
					carr = *wr;					// carry to next pixel
					*wr = diffr / 4;			// 2/8 to pixel below right
					diffr = (diffr * 3)/8;	// 3/8 to pixel right;
					diffg = curg - (MyPalette[index][1] * 256);
					*wg++ = (diffg * 3)/8;
					carg = *wg;
					*wg = diffg / 4;
					diffg = (diffg * 3)/8;
					diffb = curb - (MyPalette[index][2] * 256);
					*wb++ = (diffb * 3)/8;
					carb = *wb;
					*wb = diffb / 4;
					diffb = (diffb * 3)/8;
					*old++ = index;
					r++;
					g++;
					b++;
				}
			}
			c2p.bmap = (struct BitMap *)bitmap;
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap,GetMode(height,width,Depth,camg,0,FileName),
									 width,  height, width, height,
									 MyPalette, numcols, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 (UBYTE *)FileName,Depth);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
			OkFlag = FALSE;
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	if (word) {
		FreeVec(word);
	}
	return OkFlag;
}

static BOOL
SaveColour12(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg, ULONG numcols) {
	UBYTE *r,*g,*b,*p;
	UBYTE rr,gg,bb;
	UBYTE *old;
	UWORD x,y;
	ULONG maxdiff;
	ULONG diff;
	LONG t;
	UWORD k;
	UWORD index;
	BOOL OkFlag;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	struct c2pStruct c2p;
	int *word = NULL,*wr,*wg,*wb;
	int diffr,curr,newr,carr;
	int diffg,curg,newg,carg;
	int diffb,curb,newb,carb;
	UBYTE *InvMap;
	int i,ir,ig,ib;
	int Depth;

	AddMessage("Saving 12bit COLOUR");
	if (numcols > 128) {
		Depth = 8;
	}
	else {
		if (numcols > 64) {
			Depth = 7;
		}
		else {
			if (numcols > 32) {
				Depth = 6;
			}
			else {
				if (numcols > 16) {
					Depth = 5;
				}
				else {
					if (numcols > 8) {
						Depth = 4;
					}
					else {
						if (numcols > 4) {
							Depth = 3;
						}
						else {
							if (numcols > 2) {
								Depth = 2;
							}
							else {
								Depth = 1;
							}
						}
					}
				}
			}
		}
	}

	if ((InvMap = AllocVec(4906,MEMF_ANY)) &&
		 (word = AllocVec((sizeof(int))*(width+1)*3,MEMF_CLEAR)) &&
		 (bitmap = MyAllocBitMap(width,height,Depth,FileName))) {
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			AddMessage("Computing Map");
			// Compute inverse color map
			for (i = 0; i < 4096; ++i) {
				ir = RED6(i);
				ig = GREEN6(i);
				ib = BLUE6(i);
				// Find closest color
				maxdiff = 0x7FFFFFFF;
				p = &(MyPalette[0][0]);
				for (k = 0;
					  (k < numcols) && maxdiff;
					  ++k) {
					t = ir - (int)*p++;
					diff = t*t * 2;
					if (diff < maxdiff) {
						t = ig - (int)*p++;
						diff += t*t * 4;
						if (diff < maxdiff) {
							t = ib - (int)*p++;
							diff += t*t;
							if (diff < maxdiff) {
								maxdiff = diff;
								index = k;
							}
						}
						else {
							++p;
						}
					}
					else {
						++p;
						++p;
					}
				}
				InvMap[i] = index;
			}
			r = red;
			g = green;
			b = blue;
			old = red;
			AddMessage("Remapping");
			SetMax(height);
			for (y=0;
				  y<height;
				  y++) {
				SetCur(y);
				wr = word;	// Carrys from previous row
				diffr = 0;	// Carry from last column
				carr = *wr;	// Carry from last row
				wg = word+(width+1);
				diffg = 0;
				carg = *wg;
				wb = word+(2*(width+1));
				diffb = 0;
				carb = *wb;
				for (x=0;
					  x < width;
					  x++) {
					curr = ((int)*r * 256) + diffr + carr;
					newr = curr / 256;
					if (newr > 255) {
						rr = 255;
					}
					else {
						if (newr < 0) {
							rr = 0;
						}
						else {
							rr = newr;
						}
					}
					curg = ((int)*g * 256) + diffg + carg;
					newg = curg / 256;
					if (newg > 255) {
						gg = 255;
					}
					else {
						if (newg < 0) {
							gg = 0;
						}
						else {
							gg = newg;
						}
					}
					curb = ((int)*b * 256) + diffb + carb;
					newb = curb / 256;
					if (newb > 255) {
						bb = 255;
					}
					else {
						if (newb < 0) {
							bb = 0;
						}
						else {
							bb = newb;
						}
					}
					// Find closest color
					index = InvMap[RGB6(rr,gg,bb)];
					// FS dither
					diffr = curr - (MyPalette[index][0] * 256);
					*wr++ = (diffr * 3)/8;	//	3/8 to pixel below
					carr = *wr;					// carry to next pixel
					*wr = diffr / 4;			// 2/8 to pixel below right
					diffr = (diffr * 3)/8;	// 3/8 to pixel right;
					diffg = curg - (MyPalette[index][1] * 256);
					*wg++ = (diffg * 3)/8;
					carg = *wg;
					*wg = diffg / 4;
					diffg = (diffg * 3)/8;
					diffb = curb - (MyPalette[index][2] * 256);
					*wb++ = (diffb * 3)/8;
					carb = *wb;
					*wb = diffb / 4;
					diffb = (diffb * 3)/8;
					*old++ = index;
					r++;
					g++;
					b++;
				}
			}
			c2p.bmap = (struct BitMap *)bitmap;
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap, GetMode(height,width,Depth,camg,0,FileName),
									 width,  height, width, height,
									 MyPalette, numcols, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 (UBYTE *)FileName,Depth);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
			OkFlag = FALSE;
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	if (word) {
		FreeVec(word);
	}
	if (InvMap) {
		FreeVec(InvMap);
	}
	return OkFlag;
}

static BOOL
SaveEHB(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg) {
	UBYTE *r,*g,*b,*p;
	UBYTE rr,gg,bb;
	UBYTE *old;
	UWORD x,y;
	ULONG maxdiff;
	ULONG diff;
	LONG t;
	UWORD k;
	UWORD index;
	BOOL OkFlag;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	struct c2pStruct c2p;
	int *word = NULL,*wr,*wg,*wb;
	int diffr,curr,newr,carr;
	int diffg,curg,newg,carg;
	int diffb,curb,newb,carb;
	UBYTE *InvMap;
	int i,ir,ig,ib,j;

	AddMessage("Saving EHB");
	for (i = 0; i < 32; ++i) {
		for (j = 0; j < 3; ++j) {
			MyPalette[i+32][j] = (MyPalette[i][j])>>1;
		}
	}
	if ((InvMap = AllocVec(4906,MEMF_ANY)) &&
		 (word = AllocVec((sizeof(int))*(width+1)*3,MEMF_CLEAR)) &&
		 (bitmap = MyAllocBitMap(width,height,6,FileName))) {
		if (ilbm.ParseInfo.iff = AllocIFF()) {
			AddMessage("Computing Map");
			// Compute inverse color map
			for (i = 0; i < 4096; ++i) {
				ir = RED6(i);
				ig = GREEN6(i);
				ib = BLUE6(i);
				// Find closest color
				maxdiff = 0x7FFFFFFF;
				p = &(MyPalette[0][0]);
				for (k = 0;
					  (k < 64) && maxdiff;
					  ++k) {
					t = ir - (int)*p++;
					diff = t*t * 2;
					if (diff < maxdiff) {
						t = ig - (int)*p++;
						diff += t*t * 4;
						if (diff < maxdiff) {
							t = ib - (int)*p++;
							diff += t*t;
							if (diff < maxdiff) {
								maxdiff = diff;
								index = k;
							}
						}
						else {
							++p;
						}
					}
					else {
						++p;
						++p;
					}
				}
				InvMap[i] = index;
			}
			r = red;
			g = green;
			b = blue;
			old = red;
			AddMessage("Remapping");
			SetMax(height);
			for (y=0;
				  y<height;
				  y++) {
				SetCur(y);
				wr = word;	// Carrys from previous row
				diffr = 0;	// Carry from last column
				carr = *wr;	// Carry from last row
				wg = word+(width+1);
				diffg = 0;
				carg = *wg;
				wb = word+(2*(width+1));
				diffb = 0;
				carb = *wb;
				for (x=0;
					  x < width;
					  x++) {
					curr = ((int)*r * 256) + diffr + carr;
					newr = curr / 256;
					if (newr > 255) {
						rr = 255;
					}
					else {
						if (newr < 0) {
							rr = 0;
						}
						else {
							rr = newr;
						}
					}
					curg = ((int)*g * 256) + diffg + carg;
					newg = curg / 256;
					if (newg > 255) {
						gg = 255;
					}
					else {
						if (newg < 0) {
							gg = 0;
						}
						else {
							gg = newg;
						}
					}
					curb = ((int)*b * 256) + diffb + carb;
					newb = curb / 256;
					if (newb > 255) {
						bb = 255;
					}
					else {
						if (newb < 0) {
							bb = 0;
						}
						else {
							bb = newb;
						}
					}
					// Find closest color
					index = InvMap[RGB6(rr,gg,bb)];
					// FS dither
					diffr = curr - (MyPalette[index][0] * 256);
					*wr++ = (diffr * 3)/8;	//	3/8 to pixel below
					carr = *wr;					// carry to next pixel
					*wr = diffr / 4;			// 2/8 to pixel below right
					diffr = (diffr * 3)/8;	// 3/8 to pixel right;
					diffg = curg - (MyPalette[index][1] * 256);
					*wg++ = (diffg * 3)/8;
					carg = *wg;
					*wg = diffg / 4;
					diffg = (diffg * 3)/8;
					diffb = curb - (MyPalette[index][2] * 256);
					*wb++ = (diffb * 3)/8;
					carb = *wb;
					*wb = diffb / 4;
					diffb = (diffb * 3)/8;
					*old++ = index;
					r++;
					g++;
					b++;
				}
			}
			c2p.bmap = (struct BitMap *)bitmap;
			c2p.startX = 0;
			c2p.startY = 0;
			c2p.width = width;
			c2p.height = height;
			c2p.chunkybuffer = red;
			ChunkyToPlanar(&c2p);
			AddMessage("Saving ILBM");
			OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap, GetMode(height,width,6,camg,DIPF_IS_EXTRAHALFBRITE,FileName),
									 width,  height, width, height,
									 MyPalette, 32, 8,	/* colortable */
									 mskNone, 0,	/* masking, transparent */
									 NULL, NULL, 	/* chunklists */
									 (UBYTE *)FileName,6);
			// Close everything down cleanly
			FreeIFF(ilbm.ParseInfo.iff);
		}
		else {
			strcpy(ErrorMessage,"Can't Allocate IFF structure");
			OkFlag = FALSE;
		}
		MyFreeBitMap(bitmap,FileName);
	}
	else {
		strcpy(ErrorMessage,"Can't Allocate BitMap");
		OkFlag = FALSE;
	}
	if (word) {
		FreeVec(word);
	}
	if (InvMap) {
		FreeVec(InvMap);
	}
	return OkFlag;
}

// Note returns 0 for success!
static LONG
mysaveilbm(struct ILBMInfo *ilbm,
		struct BitMap *bitmap, ULONG modeid,
		WORD width, WORD height, WORD pagewidth, WORD pageheight,
		UBYTE col[][3], UWORD count, UWORD bitspergun,
                WORD masking, WORD transparentColor,
		struct Chunk *chunklist1, struct Chunk *chunklist2,
		UBYTE *filename, int Depth) {
	if (filename && *filename) {
		return saveilbm(ilbm,bitmap,modeid,width,height,pagewidth,pageheight,col,count,bitspergun,
					masking,transparentColor,chunklist1,chunklist2,filename);
	}
	else {
		struct Screen* Screen;
		struct Rectangle Rectangle;
		struct Window *Window;
		int l,temp;
		int i;
		struct Message *msg;
		ULONG error;
		LONG okflag=1;
		AddMessage("Displaying on screen");
		if (QueryOverscan(modeid, &Rectangle, OSCAN_MAX)) {
			if ((temp = (Rectangle.MaxX - Rectangle.MinX + 1)) > width) {
				l = (temp-width)/2 + Rectangle.MinX;
				Rectangle.MinX = l;
				Rectangle.MaxX = l + width - 1;
			}
			else {
				l = Rectangle.MinX;
			}
			if (Screen = OpenScreenTags(NULL,
							SA_Left,			l,
							SA_Top,			Rectangle.MinY,
							SA_Width,		width,
							SA_Height,		height,
							SA_Depth,		Depth,
							SA_ShowTitle,	FALSE,
							SA_Quiet,		TRUE,
							SA_Type,			CUSTOMSCREEN,
							SA_DisplayID,	modeid,
							SA_DClip,		&Rectangle,
							SA_AutoScroll,	TRUE,
							SA_ErrorCode,	&error,
							SA_BitMap,		bitmap,
//							SA_BackFill,	LAYERS_NOBACKFILL,
							TAG_END)) {
				if (8 == bitspergun) {
					for (i = 0; i < count; ++i) {
						SetRGB32(&(Screen->ViewPort),i,(col[i][0])<<24,(col[i][1])<<24,(col[i][2])<<24);
					}
				}
				else {
					LoadRGB4(&(Screen->ViewPort),(UWORD *)col,count);
				}
				if (Window = OpenWindowTags(NULL,
								WA_Left,				0,
								WA_Top,				Screen->BarHeight+1,
								WA_Width,			width,
								WA_Height,			height - Screen->BarHeight - 1,
								WA_IDCMP,			IDCMP_VANILLAKEY,
								WA_CustomScreen,	Screen,
								WA_Backdrop,		TRUE,
								WA_Borderless,		TRUE,
								WA_NoCareRefresh,	TRUE,
								WA_Activate,		TRUE,
								WA_RMBTrap,			TRUE,
								WA_BackFill,		LAYERS_NOBACKFILL,
								TAG_END)) {
					AddMessage("Waiting for Key");
					WaitPort(Window->UserPort);
					while (msg = GetMsg(Window->UserPort)) {
						ReplyMsg(msg);
					}
					okflag = 0;
					CloseWindow(Window);
				}
				else {
					strcpy(ErrorMessage,"Error opening Window");
				}
				CloseScreen(Screen);
			}
			else {
				strcpy(ErrorMessage,"Can't open Screen\n");
				switch(error) {
				case OSERR_NOMONITOR:
					strcat(ErrorMessage,"Monitor not available");
					break;
				case OSERR_NOCHIPS:
					strcat(ErrorMessage,"Custom chips too old");
					break;
				case OSERR_NOMEM:
					strcat(ErrorMessage,"Out of memory");
					break;
				case OSERR_NOCHIPMEM:
					strcat(ErrorMessage,"Out of Chip memory");
					break;
				case OSERR_UNKNOWNMODE:
					strcat(ErrorMessage,"Unknown Mode");
					break;
				case OSERR_TOODEEP:
					strcat(ErrorMessage,"Too Deep");
					break;
				default:
					strcat(ErrorMessage,"Unknown error?");
					break;
				}
			}
		}
		else {
			strcpy(ErrorMessage,"Can't QueryOverscan");
		}
		return okflag;
	}
}

static BOOL SaveDCTV(const UBYTE *FileName,USHORT width,USHORT height,
				  UBYTE *red, UBYTE *green, UBYTE *blue, ULONG camg,BOOL dctv4) {
	int i;
	BOOL OkFlag;
	struct ILBMInfo ilbm = {0};
	struct MyBitMap *bitmap;
	int Depth;
	struct DCTVCvtHandle *chandle;
	ULONG newcamg;

	if (dctv4) {
		AddMessage("Saving DCTV4");
	}
	else {
		AddMessage("Saving DCTV3");
	}
	if (dctv4) {
		Depth = 4;
	}
	else {
		Depth = 3;
	}
	if (height > 300) {
		newcamg = HIRES|LACE;
	}
	else {
		newcamg = HIRES;
	}
	if (DCTVBase) {
		if (bitmap = MyAllocBitMap(width,height,Depth,FileName)) {
			if (ilbm.ParseInfo.iff = AllocIFF()) {
				if (chandle = AllocDCTVCvtTags(bitmap,
													DCTVCVTA_Type, DCTVCVTT_RGBtoDCTV,
													DCTVCVTA_Flags,((newcamg&LACE)?DCTVCVTF_Lace:0)|
																		DCTVCVTF_Filter|
																		DCTVCVTF_CustomRGBBuf,
													TAG_END)) {
					chandle->Red = red;
					chandle->Green = green;
					chandle->Blue = blue;
					// Convert each line
					SetMax(chandle->Height);
					while (chandle->DstLineNum < chandle->Height) {
						i = chandle->SrcLineNum;
						SetCur(i);
						CvtDCTVLine(chandle);
						if (i != chandle->SrcLineNum) {
							chandle->Red += width;
							chandle->Green += width;
							chandle->Blue += width;
						}
					}
					AddMessage("Saving ILBM");
					OkFlag = !mysaveilbm(&ilbm, (struct BitMap *)bitmap,newcamg,
											 width,  height, width, height,
											 (UBYTE (*)[3])(chandle->ColorTable), dctv4?16:8, 4,	/* colortable */
											 mskNone, 0,	/* masking, transparent */
											 NULL, NULL, 	/* chunklists */
											 (UBYTE *)FileName,Depth);
					// Free DCTV stuff
					FreeDCTVCvt(chandle);
				}
				else {
					strcpy(ErrorMessage,"Can't allocate DCTV buffer");
					OkFlag = FALSE;
				}
				// Close everything down cleanly
				FreeIFF(ilbm.ParseInfo.iff);
			}
			else {
				strcpy(ErrorMessage,"Can't Allocate IFF structure");
				OkFlag = FALSE;
			}
			MyFreeBitMap(bitmap,FileName);
		}
		else {
			strcpy(ErrorMessage,"Can't Allocate BitMap");
			OkFlag = FALSE;
		}
	}
	else {
		strcpy(ErrorMessage,"Can't open dctv.library(3)");
		OkFlag = FALSE;
	}
	return OkFlag;
}