/*
**	MandelSquare - AmigaDOS 2.0/3.0 Mandelbrot set explorer
**
**	Palette.c, Colour palette management routines
**
**	Copyright © 1991-1992 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

	/* A palette template, to be filled in later. */

struct Palette
{
	APTR			Colours;
	LONG			NumColours;
	struct ColourRecord *	Record;
};

	/* A 32 bit colour triplet. */

struct ColourTriplet
{
	ULONG			Red,
				Green,
				Blue;
};

	/* A set of colour palette entries, suitable for submitting
	 * to LoadRGB32().
	 */

struct ColourRecord
{
		/* Number of colour registers to set. */

	WORD			NumColours;

		/* Offset of the first colour to set. */

	WORD			FirstColour;

		/* Space for at least one colour triplet. Note:
		 * this entry is bound to grow.
		 */

	struct ColourTriplet	Triplets[1];

		/* The trick: the last word will serve as an array
		 * terminator as the record is bound to grow in
		 * triplets only.
		 */

	WORD			Terminator;
};

	/* Global flag. */

extern BYTE		Is39;

	/* Exported routines. */

VOID			FreePalette(struct Palette *Palette);
struct Palette *	AllocPalette(LONG NumColours,BYTE TrueColour);
struct Palette *	GetPalette(struct Screen *Screen,struct Palette *Palette);
VOID			LoadPalette(struct Screen *Screen,struct Palette *Palette,LONG NumColours);
LONG			GetPaletteSize(struct Palette *Palette);
BYTE			GetPaletteTriplet(struct Palette *Palette,UBYTE *Triplet,LONG Index);
BYTE			SetPaletteTriplet(struct Palette *Palette,UBYTE R,UBYTE G,UBYTE B,LONG Index);
ULONG			GetPaletteEntry(struct Palette *Palette,LONG Index);
BYTE			SetPaletteEntry(struct Palette *Palette,ULONG Entry,LONG Index);

	/* FreePalette(struct Palette *Palette):
	 *
	 *	Free palette memory.
	 */

VOID
FreePalette(struct Palette *Palette)
{
	if(Palette)
	{
		if(Palette -> Colours)
			FreeVec(Palette -> Colours);

		if(Palette -> Record)
			FreeVec(Palette -> Record);

		FreeVec(Palette);
	}
}

	/* AllocPalette(LONG NumColours,BYTE TrueColour):
	 *
	 *	Allocate palette memory.
	 */

struct Palette *
AllocPalette(LONG NumColours,BYTE TrueColour)
{
	if(NumColours)
	{
		struct Palette *Palette;

			/* Allocate the template. */

		if(Palette = (struct Palette *)AllocVec(sizeof(struct Palette),MEMF_ANY | MEMF_CLEAR))
		{
				/* Pick up the number of colours. */

			Palette -> NumColours = NumColours;

				/* Are we to use a true colour palette (24 bits)? */

			if(Is39 && TrueColour)
			{
					/* Allocate 24 colour palette. */

				if(Palette -> Colours = AllocVec(sizeof(ULONG) * Palette -> NumColours,MEMF_ANY | MEMF_CLEAR))
				{
						/* Allocate colour records. */

					if(Palette -> Record = (struct ColourRecord *)AllocVec(sizeof(struct ColourRecord) + ((NumColours - 1) * sizeof(struct ColourTriplet)),MEMF_ANY | MEMF_CLEAR))
						return(Palette);
					else
						FreeVec(Palette -> Colours);
				}
				else
					return(Palette);
			}
			else
			{
					/* Allocate standard palette. */

				if(Palette -> Colours = AllocVec(sizeof(UWORD) * Palette -> NumColours,MEMF_ANY | MEMF_CLEAR))
					return(Palette);
			}

			FreeVec(Palette);
		}
	}

	return(NULL);
}

	/* GetPalette(struct Screen *Screen,struct Palette *Palette):
	 *
	 *	Get the screen colours palette.
	 */

struct Palette *
GetPalette(struct Screen *Screen,struct Palette *Palette)
{
	if(Screen)
	{
		LONG i;

			/* No palette given? Allocate a new one... */

		if(!Palette)
		{
			if(!(Palette = AllocPalette(Screen -> ViewPort . ColorMap -> Count,Screen -> ViewPort . ColorMap -> Count == 256)))
				return(NULL);
		}

			/* v39 true colour palette? */

		if(Palette -> Record)
		{
			ULONG	*Colours = Palette -> Colours,
				 Triplet[3];

				/* Run down the table... */

			for(i = 0 ; i < Palette -> NumColours ; i++)
			{
					/* Query the colour table entry. */

				GetRGB32(Screen -> ViewPort . ColorMap,i,1,Triplet);

					/* Only eight colours so far. */

				Triplet[0] >>= 24;
				Triplet[1] >>= 24;
				Triplet[2] >>= 24;

					/* Build new entry. */

				Colours[i] = (Triplet[0] << 16) | (Triplet[1] << 8) | Triplet[2];
			}
		}
		else
		{
			UWORD *Colours = Palette -> Colours;

				/* Fill the table. */

			for(i = 0 ; i < Palette -> NumColours ; i++)
				Colours[i] = GetRGB4(Screen -> ViewPort . ColorMap,i);
		}

		return(Palette);
	}

	return(NULL);
}

	/* LoadPalette(struct Screen *Screen,struct Palette *Palette,LONG NumColours):
	 *
	 *	Load the screen palette.
	 */

VOID
LoadPalette(struct Screen *Screen,struct Palette *Palette,LONG NumColours)
{
	if(Screen && Palette)
	{
			/* Set up number of colours if invalid. */

		if(NumColours > Palette -> NumColours || !NumColours)
			NumColours = Palette -> NumColours;

			/* Not too much, please. */

		if(NumColours > Screen -> ViewPort . ColorMap -> Count)
			NumColours = Screen -> ViewPort . ColorMap -> Count;

			/* v39 true colour palette? */

		if(Palette -> Record)
		{
			ULONG	*Colours = Palette -> Colours;
			LONG	 i;

				/* Set up the colour records. */

			for(i = 0 ; i < NumColours ; i++)
			{
				Palette -> Record -> Triplets[i] . Red		= (0xFFFFFFFF / 255) * ((Colours[i] >> 16) & 0xFF);
				Palette -> Record -> Triplets[i] . Green	= (0xFFFFFFFF / 255) * ((Colours[i] >>  8) & 0xFF);
				Palette -> Record -> Triplets[i] . Blue		= (0xFFFFFFFF / 255) * ((Colours[i]      ) & 0xFF);
			}

				/* Fill in the number of colours to display. */

			Palette -> Record -> NumColours = NumColours;

				/* Load the palette. */

			LoadRGB32(&Screen -> ViewPort,(ULONG *)Palette -> Record);
		}
		else
			LoadRGB4(&Screen -> ViewPort,Palette -> Colours,NumColours);
	}
}

	/* GetPaletteSize(struct Palette *Palette):
	 *
	 *	Query the size of the palette.
	 */

LONG
GetPaletteSize(struct Palette *Palette)
{
	if(Palette)
		return(Palette -> NumColours);
	else
		return(0);
}

	/* GetPaletteTriplet(struct Palette *Palette,UBYTE *Triplet,LONG Index):
	 *
	 *	Fill in a single colour triplet.
	 */

BYTE
GetPaletteTriplet(struct Palette *Palette,UBYTE *Triplet,LONG Index)
{
	if(Palette)
	{
		if(Index <= Palette -> NumColours)
		{
			if(Palette -> Record)
			{
				ULONG *Colours = Palette -> Colours;

				Triplet[0] = (Colours[Index] >> 16) & 0xFF;
				Triplet[1] = (Colours[Index] >>  8) & 0xFF;
				Triplet[2] = (Colours[Index]      ) & 0xFF;
			}
			else
			{
				UWORD *Colours = Palette -> Colours;

				Triplet[0] = (Colours[Index] >> 8) & 0xF;
				Triplet[1] = (Colours[Index] >> 4) & 0xF;
				Triplet[2] = (Colours[Index]     ) & 0xF;
			}

			return(TRUE);
		}
	}

	return(FALSE);
}

	/* SetPaletteTriplet(struct Palette *Palette,UBYTE R,UBYTE G,UBYTE B,LONG Index):
	 *
	 *	Set a single colour triplet.
	 */

BYTE
SetPaletteTriplet(struct Palette *Palette,UBYTE R,UBYTE G,UBYTE B,LONG Index)
{
	if(Palette)
	{
		if(Index <= Palette -> NumColours)
		{
			if(Palette -> Record)
			{
				ULONG *Colours = Palette -> Colours;

				Colours[Index] = (R << 16) | (G << 8) | B;
			}
			else
			{
				UWORD *Colours = Palette -> Colours;

				Colours[Index] = (R << 8) | (G << 4) | B;
			}

			return(TRUE);
		}
	}

	return(FALSE);
}

	/* GetPaletteEntry(struct Palette *Palette,LONG Index):
	 *
	 *	Query a packed colour table entry.
	 */

ULONG
GetPaletteEntry(struct Palette *Palette,LONG Index)
{
	if(Palette)
	{
		if(Index <= Palette -> NumColours)
		{
			if(Palette -> Record)
			{
				ULONG *Colours = Palette -> Colours;

				return(Colours[Index]);
			}
			else
			{
				UWORD *Colours = Palette -> Colours;

				return((ULONG)Colours[Index]);
			}
		}
	}

	return(0);
}

	/* SetPaletteEntry(struct Palette *Palette,ULONG Entry,LONG Index):
	 *
	 *	Set a packed colour table entry.
	 */

BYTE
SetPaletteEntry(struct Palette *Palette,ULONG Entry,LONG Index)
{
	if(Palette)
	{
		if(Index <= Palette -> NumColours)
		{
			if(Palette -> Record)
			{
				ULONG *Colours = Palette -> Colours;

				Colours[Index] = Entry;
			}
			else
			{
				UWORD *Colours = Palette -> Colours;

				Colours[Index] = Entry;
			}

			return(TRUE);
		}
	}

	return(FALSE);
}
