/***************************************************************************
 *
 * colour.c
 *
 ***************************************************************************
 *
 * RGB/YUV colour manipulation
 *
 ***************************************************************************/

/*
 * includes
 */

#include "generic.h"
#include "colour.h"

/*
 * defines
 */

/*
 * typedefs
 */

/*
 * structures
 */

/*
 * globals
 */

int		ditherMode=0;
unsigned long	ditherSeed=0;
unsigned char   randomBuf[250];
unsigned int	randomIndex;

unsigned char	rBlock[256];
unsigned char	gBlock[256];
unsigned char	bBlock[256];

unsigned short	rgb16Block[256];

signed char	yBig[256];
signed char	uBig[256];
signed char	vBig[256];

signed char	yBlock00[64];
signed char	yBlock10[64];
signed char	yBlock01[64];
signed char	yBlock11[64];
signed char	uBlock[64];
signed char	vBlock[64];

/*
 * function prototypes
 */

unsigned char GetRandom(void);
void InitRandom(int);

/*
 * code
 */


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void RGBtoYUV(int r,int g,int b,int *y,int *u,int *v)
{
	*y=((19596*r) + (38470*g) + (7470*b))/65536;
	*u=((32768*r) + (-27439*g) + (-5328*b))/65536;
	*v=((-11059*r) + (-21710*g) + (32768*b))/65536;

	*y-=128;

	if(*u<-128) *u=-128;
	if(*v<-128) *v=-128;
	if(*u>127) *u=127;
	if(*v>127) *v=127;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void YUVtoRGB(int y,int u,int v,int *r,int *g,int *b)
{
	y+=128;

	*r=((65536*y) + (91881*u) + (0*v))/65536;
	*g=((65536*y) + (-46802*u) + (-22551*v))/65536;
	*b=((65536*y) + (0*u) + (116129*v))/65536;

	if(*r < 0) *r=0;
	if(*g < 0) *g=0;
	if(*b < 0) *b=0;
	if(*r > 255) *r=255;
	if(*g > 255) *g=255;
	if(*b > 255) *b=255;
}

/***************************************************************************
 *
 * void SplitMacroBlock(void)
 *
 ***************************************************************************
 *
 * Split a raw 16x16 RGB block into 6 sub-blocks (4Y,U,V)
 *
 ***************************************************************************/

void SplitMacroBlock(void)
{
	int i,j,k;
	int y,u,v;

	/*
	 * convert all pixels...
	 */

	for(i=0;i<16;i++)
	{
		for(j=0;j<16;j++)
		{
			k=(i*16)+j;
			RGBtoYUV(rBlock[k],gBlock[k],bBlock[k],&y,&u,&v);
			yBig[k]=y;
			uBig[k]=u;
			vBig[k]=v;
		}
	}

	/*
	 * sub sample U and V
	 */

	for(i=0;i<16;i+=2)
	{
		for(j=0;j<16;j+=2)
		{
			k=(i*16)+j;
			u=(int)uBig[k]+(int)uBig[k+1]+(int)uBig[k+16]+(int)uBig[k+17];
			u/=4;
			uBlock[(i/2)*8 + (j/2)]=u;
			v=(int)vBig[k]+(int)vBig[k+1]+(int)vBig[k+16]+(int)vBig[k+17];
			v/=4;
			vBlock[(i/2)*8 + (j/2)]=v;
		}
	}

	/*
	 * copy Y into 4 quarter blocks
	 */

	for(i=0;i<8;i++)
	{
		memcpy(&yBlock00[i*8],&yBig[i*16],8);
		memcpy(&yBlock10[i*8],&yBig[i*16+8],8);
		memcpy(&yBlock01[i*8],&yBig[(i+8)*16],8);
		memcpy(&yBlock11[i*8],&yBig[(i+8)*16+8],8);
	}
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CombineMacroBlock16(unsigned short *dest,int mod)
{
	int i,j,k,l,r,g,b;
	int rErr,gErr,bErr;

	/*
	 * copy 4Y blocks into yBig
	 */

	for(i=0;i<8;i++)
	{
		memcpy(&yBig[i*16],      &yBlock00[i*8],8);
		memcpy(&yBig[i*16+8],    &yBlock10[i*8],8);
		memcpy(&yBig[(i+8)*16],  &yBlock01[i*8],8);
		memcpy(&yBig[(i+8)*16+8],&yBlock11[i*8],8);
	}

	/*
	 * enlarge U and V blocks
	 */

	for(i=0;i<8;i++)
	{
		for(j=0;j<8;j++)
		{
			k=((i*2)*16)+(j*2);
			l=(i*8+j);
			uBig[k]=uBlock[l];
			uBig[k+1]=uBlock[l];
			uBig[k+16]=uBlock[l];
			uBig[k+17]=uBlock[l];
			vBig[k]=vBlock[l];
			vBig[k+1]=vBlock[l];
			vBig[k+16]=vBlock[l];
			vBig[k+17]=vBlock[l];
		}
	}

	/*
	 * do conversion
	 */

	rErr=0; gErr=0; bErr=0;

	k=0;
	for(i=0;i<16;i++)
	{
		for(j=0;j<16;j++)
		{
			YUVtoRGB(yBig[k],uBig[k],vBig[k],&r,&g,&b);
			r+=rErr; g+=gErr; b+=bErr;

			if(ditherMode & DITHER_RANDOM)
			{
				r+=GetRandom()&3;
				g+=GetRandom()&3;
				b+=GetRandom()&3;
			}

			if(r>255) r=255;
			if(g>255) g=255;
			if(b>255) b=255;

			rErr=(r & 7);
			gErr=(g & 7);
			bErr=(b & 7);

			*(dest++)=((r>>3)<<11) | ((g>>3)<<6) | ((b>>3)<<1);
			k++;
		}
		dest+=(mod-16);
	}
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CombineMacroBlock24(unsigned char *dest,int mod)
{
	int i,j,k,l,r,g,b;

	/*
	 * copy 4Y blocks into yBig
	 */

	for(i=0;i<8;i++)
	{
		memcpy(&yBig[i*16],      &yBlock00[i*8],8);
		memcpy(&yBig[i*16+8],    &yBlock10[i*8],8);
		memcpy(&yBig[(i+8)*16],  &yBlock01[i*8],8);
		memcpy(&yBig[(i+8)*16+8],&yBlock11[i*8],8);
	}

	/*
	 * enlarge U and V blocks
	 */

	for(i=0;i<8;i++)
	{
		for(j=0;j<8;j++)
		{
			k=((i*2)*16)+(j*2);
			l=(i*8+j);
			uBig[k]=uBlock[l];
			uBig[k+1]=uBlock[l];
			uBig[k+16]=uBlock[l];
			uBig[k+17]=uBlock[l];
			vBig[k]=vBlock[l];
			vBig[k+1]=vBlock[l];
			vBig[k+16]=vBlock[l];
			vBig[k+17]=vBlock[l];
		}
	}

	/*
	 * do conversion
	 */

	k=0;
	for(i=0;i<16;i++)
	{
		for(j=0;j<16;j++)
		{
			YUVtoRGB(yBig[k],uBig[k],vBig[k],&r,&g,&b);
			*(dest++)=r;
			*(dest++)=g;
			*(dest++)=b;
			k++;
		}
		dest+=(mod-48);
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void CombineMacroBlock(void)
{
	int i,j,k,l,r,g,b;

	/*
	 * copy 4Y blocks into yBig
	 */

	for(i=0;i<8;i++)
	{
		memcpy(&yBig[i*16],      &yBlock00[i*8],8);
		memcpy(&yBig[i*16+8],    &yBlock10[i*8],8);
		memcpy(&yBig[(i+8)*16],  &yBlock01[i*8],8);
		memcpy(&yBig[(i+8)*16+8],&yBlock11[i*8],8);
	}

	/*
	 * enlarge U and V blocks
	 */

	for(i=0;i<8;i++)
	{
		for(j=0;j<8;j++)
		{
			k=((i*2)*16)+(j*2);
			l=(i*8+j);
			uBig[k]=uBlock[l];
			uBig[k+1]=uBlock[l];
			uBig[k+16]=uBlock[l];
			uBig[k+17]=uBlock[l];
			vBig[k]=vBlock[l];
			vBig[k+1]=vBlock[l];
			vBig[k+16]=vBlock[l];
			vBig[k+17]=vBlock[l];
		}
	}

	/*
	 * do conversion
	 */

	for(i=0;i<256;i++)
	{
		YUVtoRGB(yBig[i],uBig[i],vBig[i],&r,&g,&b);
		rBlock[i]=r;
		gBlock[i]=g;
		bBlock[i]=b;
	}
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void InitRandom(int seed)
{
	unsigned char	*rPtr;
	unsigned int	i;
	unsigned char	msk,msb;

	randomIndex = 0;

	/* Fill buffer with random 8-bit values. */

	rPtr = &randomBuf[0];

	for (i = 250; i != 0; i -= 1)
	{
		*rPtr++ = i;
	}

	/* Take precausions to avoid degeneration into non-random */
	/* behaviour. */
	/* - Turn off bits left of diagonal. */
	/* - Turn on diagonal bit. */

	msb = 0x80u;
	msk = 0xFFu;

	rPtr = &randomBuf[14];

	do
	{
		rPtr[0] &= msk;
		rPtr[0] |= msb;
		rPtr    += 11;
		msk >>= 1;
		msb >>= 1;
	} while (msb != 0);

	/* All done. */
	return;
}

unsigned char GetRandom(void)
{
	unsigned int	i;
	unsigned char	irnd;

	/* */

	if (randomIndex >= 147) {
		i = randomIndex - 147;
		} else {
		i = randomIndex + 103;
		}

	irnd = randomBuf [randomIndex] ^ randomBuf[i];

	randomBuf[randomIndex] = irnd;

	if (randomIndex >= 249) {
		randomIndex  = 0;
		} else {
		randomIndex += 1;
		}

	/* All done. */

	return (irnd);
}



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void SetDither(int mode)
{
	ditherMode=mode;
	InitRandom(4528009);
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

