//contains routines for blending images (as well as blitting 32bit to 8bit type stuff)


#include "quakedef.h"
#include "d_local.h"
#include "r_local.h"

void MakeVideoPalette(void);
void MakeSwizzledPalette(void);

int *srctable;
int *dsttable;
qbyte *pal555to8;

int swzpal[TRANS_LEVELS][256];

#define palette host_basepal
#define _abs(x) ((x)*(x))

void D_InitTrans(void)
{
	// create pal555to8 and swizzled palette 
	MakeVideoPalette();
	MakeSwizzledPalette();

	srctable = swzpal[0];
	dsttable = swzpal[TRANS_MAX];
}

#if 0
#define Trans(p, p2)	(t_curlookupp[p][p2])
#else
// TODO: INLINE THESE FUNCTIONS
qbyte FASTCALL Trans(qbyte p, qbyte p2)
{	
	int x;

	x = (srctable[p] + dsttable[p2]) | 0x01F07C1F;
	return pal555to8[x & (x >> 15)];

}
#endif

qbyte FASTCALL AddBlend(qbyte p, qbyte p2)
{
	int x, y;

	x = (srctable[p] + dsttable[p2]);
	y = x & 0x40100400; // overflow bits
	x = (x | 0x01F07C1F) & 0x3FFFFFFF;
	y = y - (y >> 5);
	x = x | y;
	return pal555to8[x & (x >> 15)];
}

/*
void Set_TransLevelI(int level)
{
	t_curtable = level/(100.0f/(t_numtables-1));
	t_curlookupp = t_lookup[t_curtable];
}
*/

void D_SetTransLevel(float level, blendmode_t blend)	//MUST be between 0 and 1
{
	int ilvl;

	// cap and set level
	ilvl = (bound(0, level, 1) * (TRANS_MAX + 0.99));

	// set blending tables
	switch (blend)
	{
	case BM_ADD:
		dsttable = swzpal[ilvl];
		srctable = swzpal[TRANS_MAX];
		break;
	default:
		dsttable = swzpal[ilvl];
		srctable = swzpal[TRANS_MAX - ilvl];
	}
}


#define _abs(x) ((x)*(x))
qbyte FindIndexFromRGB(int red, int green, int blue)
{
	int i, best=15;
	int bestdif=256*256*256, curdif;
	extern qbyte *host_basepal;
	qbyte *pa;

	pa = host_basepal;
	for (i = 0; i < 256; i++, pa+=3)
	{
		curdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]);
		if (curdif < bestdif)
		{
			if (curdif<1)
				return i;
			bestdif = curdif;
			best = i;
		}
	}
	return best;
}

#define FindPalette(r,g,b) pal555to8[((r&0xF8)>>3)|((g&0xF8)<<2)|((b&0xF8)<<7)]
qbyte GetPalette(int red, int green, int blue)
{
	if (pal555to8)	//fast precalculated method
		return FindPalette(red,green,blue);
	else	//slow, horrible method.
		return FindIndexFromRGB(red, green, blue);
}

void MakeVideoPalette(void)
{
	FILE *f;
	int r, g, b;

	// allocate memory
	if (!pal555to8)
		pal555to8 = BZ_Malloc(PAL555_SIZE);
	// pal555to8 = Hunk_AllocName(PAL555_SIZE, "RGB data");

	// load in previously created table
	COM_FOpenFile ("pal555.pal", &f);
	if (f)
	{
		fread(pal555to8, 1, PAL555_SIZE, f);	//cached
		fclose(f);
		return;
	}

	// create palette conversion table
	for (b = 0; b < 32; b++)
		for (g = 0; g < 32; g++)
			for (r = 0; r < 32; r++)
				pal555to8[r | (g << 5) | (b << 10)] =
					FindIndexFromRGB(r<<3|r>>2, g<<3|g>>2, b<<3|b>>2);

	// write palette conversion table
	if (r_palconvwrite.value)
		COM_WriteFile("pal555.pal", pal555to8, PAL555_SIZE);
}

void MakeSwizzledPalette(void)
{
	int idx, lvl;
	qbyte *pa;

	// create swizzled palettes
	for (lvl = 0; lvl < TRANS_LEVELS; lvl++)
	{
		pa = host_basepal;
		for (idx = 0; idx < 256; idx++)
		{
			// create a b10r10g10 table for each alpha level
			// may need some hacking due to the tendancy of
			// identity merges becoming darker
			swzpal[lvl][idx]  = ( (pa[0] * lvl) >> 4 ) << 10; // red
			swzpal[lvl][idx] |= ( (pa[1] * lvl) >> 4 );       // green
			swzpal[lvl][idx] |= ( (pa[2] * lvl) >> 4 ) << 20; // blue
			swzpal[lvl][idx]  = swzpal[lvl][idx] & 0x3feffbff;
			pa += 3;
		}	
	}
}

void MediaSW_ShowFrame8bit(qbyte *framedata, int inwidth, int inheight, qbyte *palette)
{
	int y, x;

	D_EnableBackBufferAccess ();	// of all overlay stuff if drawing directly
	if (r_pixbytes == 1)
	{
		qbyte *dest, *src;
		int lines=vid.conheight;
		int v;
		int f, fstep;

		dest = vid.conbuffer;

		for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
		{
			v = (vid.conheight - lines + y)*inheight/vid.conheight;
			src = framedata + v*inwidth;
			{
				f = 0;
				fstep = (inwidth<<16)/vid.conwidth;
				for (x=0 ; x<vid.conwidth ; x+=4)
				{
					dest[x] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
					f += fstep;
					dest[x+1] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
					f += fstep;
					dest[x+2] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
					f += fstep;
					dest[x+3] = FindPalette(palette[src[(f>>16)]*3], palette[src[(f>>16)]*3+1], palette[src[(f>>16)]*3+2]);
					f += fstep;
				}
			}
		}
	}
	else if (r_pixbytes == 2)
	{
		/*	this still expects 32bit input
extern int redbits, redshift;
extern int greenbits, greenshift;
extern int bluebits, blueshift;

		unsigned short *dest;
		qbyte *src;
		int lines=vid.conheight;
		int v;
		int f, fstep;

		dest = (unsigned short *)vid.conbuffer;

		for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
		{
			v = (vid.conheight - lines + y)*inheight/vid.conheight;
			src = framedata + v*inwidth*4;
			{
				f = 0;
				fstep = ((inwidth)*0x10000)/vid.conwidth;
				for (x=0 ; x<vid.conwidth; x++)	//sw 32 bit rendering is bgrx
				{
					dest[x] = (((src[(f>>16)*4]*(1<<redbits))/256)<<redshift) + (((src[(f>>16)*4+1]*(1<<greenbits))/256)<<greenshift) + (((src[(f>>16)*4+2]*(1<<bluebits))/256)<<blueshift);
					f += fstep;
				}
			}
		}
		*/
	}
	else if (r_pixbytes == 4)
	{
		qbyte *dest, *src;
		int lines=vid.conheight;
		int v;
		int f, fstep;

		dest = vid.conbuffer;

		for (y=0 ; y<lines ; y++, dest += vid.conrowbytes*4)
		{
			v = (vid.conheight - lines + y)*inheight/vid.conheight;
			src = framedata + v*inwidth;
			{
				f = 0;
				fstep = ((inwidth)*0x10000)/vid.conwidth;
				for (x=0 ; x<vid.conwidth*4 ; x+=4)	//sw 32 bit rendering is bgrx
				{
					dest[x] = palette[src[(f>>16)]*3+2];
					dest[x+1] = palette[src[(f>>16)]*3+1];
					dest[x+2] = palette[src[(f>>16)]*3];
					f += fstep;
				}
			}
		}
	}
	else
		Sys_Error("24 bit rendering?");

	D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in

	SCR_SetUpToDrawConsole();
	if  (scr_con_current)
	SCR_DrawConsole (false);

	M_Draw(0);
}

void MediaSW_ShowFrameRGBA_32(qbyte *framedata, int inwidth, int inheight)	//top down
{
	int y, x;

		D_EnableBackBufferAccess ();	// of all overlay stuff if drawing directly
		if (r_pixbytes == 1)
		{
			qbyte *dest, *src;
			int lines=vid.conheight;
			int v;
			int f, fstep;

			dest = vid.conbuffer;

				for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
				{
					v = (vid.conheight - lines + y)*inheight/vid.conheight;
					src = framedata + v*inwidth*4;
					{
						f = 0;
						fstep = ((inwidth)*0x10000)/vid.conwidth;
						for (x=0 ; x<vid.conwidth ; x+=4)
						{
							dest[x] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
							f += fstep;
							dest[x+1] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
							f += fstep;
							dest[x+2] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
							f += fstep;
							dest[x+3] = FindPalette(src[(f>>16)*4], src[(f>>16)*4+1], src[(f>>16)*4+2]);
							f += fstep;
						}
					}
				}
		}
		else if (r_pixbytes == 2)
		{
extern int redbits, redshift;
extern int greenbits, greenshift;
extern int bluebits, blueshift;

				unsigned short *dest;
				qbyte *src;
				int lines=vid.conheight;
				int v;
				int f, fstep;

				dest = (unsigned short *)vid.conbuffer;

				for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
				{
					v = (vid.conheight - lines + y)*inheight/vid.conheight;
					src = framedata + v*inwidth*4;
					{
						f = 0;
						fstep = ((inwidth)*0x10000)/vid.conwidth;
						for (x=0 ; x<vid.conwidth; x++)	//sw 32 bit rendering is bgrx
						{
							dest[x] = (((src[(f>>16)*4]*(1<<redbits))/256)<<redshift) + (((src[(f>>16)*4+1]*(1<<greenbits))/256)<<greenshift) + (((src[(f>>16)*4+2]*(1<<bluebits))/256)<<blueshift);
							f += fstep;
						}
					}
				}
		}
		else if (r_pixbytes == 4)
		{
				qbyte *dest, *src;
				int lines=vid.conheight;
				int v;
				int f, fstep;

				dest = vid.conbuffer;

				for (y=0 ; y<lines ; y++, dest += vid.conrowbytes*4)
				{
					v = (vid.conheight - lines + y)*inheight/vid.conheight;
					src = framedata + v*inwidth*4;
					{
						f = 0;
						fstep = ((inwidth)*0x10000)/vid.conwidth;
						for (x=0 ; x<vid.conwidth*4 ; x+=4)	//sw 32 bit rendering is bgrx
						{
							dest[x] = src[(f>>16)*4+2];
							dest[x+1] = src[(f>>16)*4+1];
							dest[x+2] = src[(f>>16)*4];
							f += fstep;
						}
					}
				}
		}
		else
			Sys_Error("24 bit rendering?");

		D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in

	SCR_SetUpToDrawConsole();
	if  (scr_con_current)
	SCR_DrawConsole (false);
}

void MediaSW_ShowFrameBGR_24_Flip(qbyte *framedata, int inwidth, int inheight)	//input is bottom up...
{
	int y, x;

		D_EnableBackBufferAccess ();	// of all overlay stuff if drawing directly
		if (r_pixbytes == 1)
		{
			qbyte *dest, *src;
			int lines=vid.conheight;
			int v;
			int f, fstep;

			dest = vid.conbuffer;

				for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
				{
					v = (lines - y)*inheight/vid.conheight;
					src = framedata + v*inwidth*3;
					{
						f = 0;
						fstep = ((inwidth)*0x10000)/vid.conwidth;
						for (x=0 ; x<vid.conwidth ; x+=4)
						{
							dest[x] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
							f += fstep;
							dest[x+1] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
							f += fstep;
							dest[x+2] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
							f += fstep;
							dest[x+3] = FindPalette(src[(f>>16)*3+2], src[(f>>16)*3+1], src[(f>>16)*3]);
							f += fstep;
						}
					}
				}
		}
		else if (r_pixbytes == 2)
		{
extern int redbits, redshift;
extern int greenbits, greenshift;
extern int bluebits, blueshift;

				unsigned short *dest;
				qbyte *src;
				int lines=vid.conheight;
				int v;
				int f, fstep;

				dest = (unsigned short *)vid.conbuffer;

				for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
				{
					v = (lines - y)*inheight/vid.conheight;
					src = framedata + v*inwidth*3;
					{
						f = 0;
						fstep = ((inwidth)*0x10000)/vid.conwidth;
						for (x=0 ; x<vid.conwidth; x++)	//sw 32 bit rendering is bgrx
						{
							dest[x] = (((src[(f>>16)*3+2]*(1<<redbits))/256)<<redshift) + (((src[(f>>16)*3+1]*(1<<greenbits))/256)<<greenshift) + (((src[(f>>16)*3+0]*(1<<bluebits))/256)<<blueshift);
							f += fstep;
						}
					}
				}
		}
		else if (r_pixbytes == 4)
		{
				unsigned int *dest;
				qbyte *src;
				int lines=vid.conheight;
				int v;
				int f, fstep;

				dest = (unsigned int *)vid.conbuffer;

				for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
				{
					v = (lines - y)*inheight/vid.conheight;
					src = framedata + v*inwidth*3;
					{
						f = 0;
						fstep = ((inwidth)*0x10000)/vid.conwidth;
						for (x=0 ; x<vid.conwidth ; x++)	//sw 32 bit rendering is bgrx
						{
							*(dest+x) = *(int *)(src + (f>>16)*3);
							f += fstep;
						}
					}
				}
		}
		else
			Sys_Error("24 bit rendering?");

		D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in

	SCR_SetUpToDrawConsole();
	if  (scr_con_current)
	SCR_DrawConsole (false);
}