/*
Copyright (C) 1997-2001 Id Software, Inc.

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 (at your option) 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// draw.c

#include "sw_local.h"


//=============================================================================


/*
=============
Draw_GetPicSize
=============
*/
void Draw_GetPicSize( int *w, int *h, qhandle_t hPic ) {
	image_t *gl;

	if( !( gl = R_ImageForHandle( hPic ) ) ) {
		*w = *h = -1;
		return;
	}

	*w = gl->width;
	*h = gl->height;
}

#define DOSTEP do {																		\
			byte	*_alphaTable, *_oneMinusAlphaTable;									\
			_alphaTable = r_alphaTable[alphaTable[3][src[3]]];							\
			_oneMinusAlphaTable = r_alphaTable[255 - alphaTable[3][src[3]]];			\
			dst[2] = _alphaTable[alphaTable[0][src[0]]] + _oneMinusAlphaTable[dst[2]];	\
			dst[1] = _alphaTable[alphaTable[1][src[1]]] + _oneMinusAlphaTable[dst[1]];	\
			dst[0] = _alphaTable[alphaTable[2][src[2]]] + _oneMinusAlphaTable[dst[0]];	\
			src += 4; dst += 4;															\
		} while( 0 )

#define DOSTRETCH do {																	\
			byte	*_alphaTable, *_oneMinusAlphaTable;									\
			byte *p = &src[ ( u >> 16 ) * 4 ];											\
			_alphaTable = r_alphaTable[alphaTable[3][p[3]]];							\
			_oneMinusAlphaTable = r_alphaTable[255 - alphaTable[3][p[3]]];				\
			dst[2] = _alphaTable[alphaTable[0][p[0]]] + _oneMinusAlphaTable[dst[2]];	\
			dst[1] = _alphaTable[alphaTable[1][p[1]]] + _oneMinusAlphaTable[dst[1]];	\
			dst[0] = _alphaTable[alphaTable[2][p[2]]] + _oneMinusAlphaTable[dst[0]];	\
			u += ustep; dst += 4;														\
		} while( 0 )

#define ROW1 do {	\
		DOSTEP;		\
	} while( --count );

#define ROW4 do {	\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
					\
		count -= 4;	\
	} while( count );

#define ROW8 do {	\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
		DOSTEP;		\
					\
		count -= 8;	\
	} while( count );

#define STRETCH1 do {	\
		DOSTRETCH;		\
	} while( --count );

#define STRETCH4 do {	\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
						\
		count -= 4;		\
	} while( count );

#define STRETCH8 do {	\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
		DOSTRETCH;		\
						\
		count -= 8;		\
	} while( count );

/*
=============
Draw_TransformedPic
=============
*/
void Draw_TransformedPic( int x, int y, int w, int h, float s1, float t1, float s2, float t2, const color_t color, image_t *pic ) {
	byte			*srcpixels, *dstpixels, *dst, *src;
	int			i;
	uint32				u, v, ustep, vstep;
	int				height;
	int				skip;
	int				xx, yy, ww, hh;
	byte	*alphaTable[4];//, *oneMinusAlphaTable;
	int	count;
	int	pitch;
//	color_t _color;

	if( x < 0 || x + w > vid.width || y + h > vid.height ) {
		return; /* totally offscreen */

	}

	height = h;

	if( y < 0 ) {
		skip = -y;
		height += y;
		y = 0;
	} else {
		skip = 0;
	}

	if(!color)color=colorWhite;
	alphaTable[0] = r_alphaTable[color[0]];
	alphaTable[1] = r_alphaTable[color[1]];
	alphaTable[2] = r_alphaTable[color[2]];
	alphaTable[3] = r_alphaTable[color[3]];


	xx = pic->width * s1;
	yy = pic->height * t1;
	ww = pic->width * ( s2 - s1 );
	hh = pic->height * ( t2 - t1 );

/*
=============
Draw_TransformedPic
=============
*/
void Draw_TransformedPic( int x, int y, int w, int h, float s1, float t1, float s2, float t2, image_t *pic ) {
	byte			*dest, *source, *pixels;
	int				v, u, sv;
	int				height;
	int				f, fstep;
	int				skip;
	int				xx, yy, ww, hh;
	byte			tbyte;

	if( x < 0 || x + w > vid.width || y + h > vid.height ) {
		return;
	}

	height = h;
	if (y < 0)
	{
		skip = -y;
		height += y;
		y = 0;
	}
	else
		skip = 0;

	xx = pic->width * s1;
	yy = pic->height * t1;
	ww = pic->width * ( s2 - s1 );
	hh = pic->height * ( t2 - t1 );

	pixels = pic->pixels[0] + yy * pic->width + xx;
	dest = vid.buffer + y * vid.rowbytes + x;

	for( v=0 ; v<height ; v++, dest+=vid.rowbytes ) {
		sv = ( skip + v ) * hh / h;
		source = pixels + sv * pic->width;

		if( w == ww ) {
			if( w & 7 ) {	
				// general
				for( u=0 ; u<w ; u++ ) {
					if( ( tbyte = source[u] ) != TRANSPARENT_COLOR ) {
						dest[u] = tbyte;
					}
				}
			} else {
				// unwound
				for( u=0 ; u<w ; u+=8 ) {

#define COPY_PIXEL( n )			if( ( tbyte = source[u+(n)] ) != TRANSPARENT_COLOR ) { dest[u+(n)] = tbyte; }

					COPY_PIXEL( 0 );
					COPY_PIXEL( 1 );
					COPY_PIXEL( 2 );
					COPY_PIXEL( 3 );
					COPY_PIXEL( 4 );
					COPY_PIXEL( 5 );
					COPY_PIXEL( 6 );
					COPY_PIXEL( 7 );

#undef COPY_PIXEL

				}
			}
		} else {
			f = 0;
			fstep = ww * 0x10000 / w;
			if( w & 3 ) {	
				// general
				for( u=0 ; u<w ; u++ ) {
					if( ( tbyte = source[f>>16] ) != TRANSPARENT_COLOR ) {
						dest[u] = tbyte;
					}
					f += fstep;
				}
			} else {
				// unwound
				for( u=0 ; u<w ; u+=4 ) {

#define COPY_PIXEL( n )			if( ( tbyte = source[f>>16] ) != TRANSPARENT_COLOR ) { dest[u+(n)] = tbyte; } f += fstep;

					COPY_PIXEL( 0 );
					COPY_PIXEL( 1 );
					COPY_PIXEL( 2 );
					COPY_PIXEL( 3 );

#undef COPY_PIXEL

				}
			}
		}
	}
}



/*
=============
Draw_StaticPic
=============
*/
static void Draw_StaticPic( int x, int y, image_t *pic ) {
	byte			*dest, *source;
	int				v, u;
	int				tbyte;
	int				width;
	int				height;


	if ((x < 0) ||
		(x >= vid.width) ||
		(y >= vid.height))
		return;

	width = pic->width;
	height = pic->height;
	source = pic->pixels[0];
	if (y < 0)
	{
		height += y;
		source += pic->width*-y;
		y = 0;
	}

	if( width > vid.width - x ) {
		width = vid.width - x;
	}

	if( height > vid.height - y ) {
		height = vid.height - y;
	}

	dest = vid.buffer + y * vid.rowbytes + x;

	if( !pic->transparent ) {
		for( v=0 ; v<height ; v++ ) {
			memcpy( dest, source, width );
			dest += vid.rowbytes;
			source += pic->width;
		}
	} else {
		if( width & 7 ) {	
			// general
			for( v=0 ; v<height ; v++ ) {
				for( u=0 ; u<width ; u++ )
					if( ( tbyte = source[u] ) != TRANSPARENT_COLOR )
						dest[u] = tbyte;

				dest += vid.rowbytes;
				source += pic->width;
			}
		} else {	
			// unwound
			for( v=0 ; v<height ; v++ ) {
				for( u=0 ; u<width ; u+=8 ) {

#define COPY_PIXEL( n )			if ( ( tbyte = source[u+(n)] ) != TRANSPARENT_COLOR ) { dest[u+(n)] = tbyte; }

				COPY_PIXEL( 0 );
				COPY_PIXEL( 1 );
				COPY_PIXEL( 2 );
				COPY_PIXEL( 3 );
				COPY_PIXEL( 4 );
				COPY_PIXEL( 5 );
				COPY_PIXEL( 6 );
				COPY_PIXEL( 7 );

#undef COPY_PIXEL

				}
				dest += vid.rowbytes;
				source += pic->width;
			}
		}
	}
}

/*
=============
Draw_TransformedPic
=============
*/
void Draw_TransformedPicFilled( int x, int y, int w, int h, float s1, float t1, float s2, float t2, int colorIndex, image_t *pic ) {
	byte			*dest, *source, *pixels;
	int				v, u, sv;
	int				height;
	int				f, fstep;
	int				skip;
	int				xx, yy, ww, hh;

	if( x < 0 || x + w > vid.width || y + h > vid.height ) {
		return;
	}

	height = h;
	if (y < 0)
	{
		skip = -y;
		height += y;
		y = 0;
	}
	else
		skip = 0;

	xx = pic->width * s1;
	yy = pic->height * t1;
	ww = pic->width * ( s2 - s1 );
	hh = pic->height * ( t2 - t1 );

	pixels = pic->pixels[0] + yy * pic->width + xx;
	dest = vid.buffer + y * vid.rowbytes + x;

	for( v=0 ; v<height ; v++, dest+=vid.rowbytes ) {
		sv = ( skip + v ) * hh / h;
		source = pixels + sv * pic->width;

		if( w == ww ) {
			if( w & 7 ) {	
				// general
				for( u=0 ; u<w ; u++ ) {
					if( source[u] != TRANSPARENT_COLOR ) {
						dest[u] = colorIndex;
					}
				}
			} else {
				// unwound
				for( u=0 ; u<w ; u+=8 ) {

#define COPY_PIXEL( n )			if( source[u+(n)] != TRANSPARENT_COLOR ) { dest[u+(n)] = colorIndex; }

					COPY_PIXEL( 0 );
					COPY_PIXEL( 1 );
					COPY_PIXEL( 2 );
					COPY_PIXEL( 3 );
					COPY_PIXEL( 4 );
					COPY_PIXEL( 5 );
					COPY_PIXEL( 6 );
					COPY_PIXEL( 7 );

#undef COPY_PIXEL

				}
			}
		} else {
			f = 0;
			fstep = ww * 0x10000 / w;
			if( w & 3 ) {	
				// general
				for( u=0 ; u<w ; u++ ) {
					if( source[f>>16] != TRANSPARENT_COLOR ) {
						dest[u] = colorIndex;
					}
					f += fstep;
				}
			} else {
				// unwound
				for( u=0 ; u<w ; u+=4 ) {

#define COPY_PIXEL( n )			if( source[f>>16] != TRANSPARENT_COLOR ) { dest[u+(n)] = colorIndex; } f += fstep;

					COPY_PIXEL( 0 );
					COPY_PIXEL( 1 );
					COPY_PIXEL( 2 );
					COPY_PIXEL( 3 );

#undef COPY_PIXEL

				}
			}
		}
	}
}

/*
=============
Draw_StaticPic
=============
*/
static void Draw_StaticPicFilled( int x, int y, int colorIndex, image_t *pic ) {
	byte			*dest, *source;
	int				v, u;
	int				width;
	int				height;


	if ((x < 0) ||
		(x >= vid.width) ||
		(y >= vid.height))
		return;

	width = pic->width;
	height = pic->height;
	source = pic->pixels[0];
	if (y < 0)
	{
		height += y;
		source += pic->width*-y;
		y = 0;
	}

	if( width > vid.width - x ) {
		width = vid.width - x;
	}

	if( height > vid.height - y ) {
		height = vid.height - y;
	}

	dest = vid.buffer + y * vid.rowbytes + x;

	if( width & 7 ) {	
		// general
		for( v=0 ; v<height ; v++ ) {
			for( u=0 ; u<width ; u++ )
				if( source[u] != TRANSPARENT_COLOR )
					dest[u] = colorIndex;

			dest += vid.rowbytes;
			source += pic->width;
		}
	} else {	
		// unwound
		for( v=0 ; v<height ; v++ ) {
			for( u=0 ; u<width ; u+=8 ) {

#define COPY_PIXEL( n )			if ( source[u+(n)] != TRANSPARENT_COLOR ) { dest[u+(n)] = colorIndex; }

			COPY_PIXEL( 0 );
			COPY_PIXEL( 1 );
			COPY_PIXEL( 2 );
			COPY_PIXEL( 3 );
			COPY_PIXEL( 4 );
			COPY_PIXEL( 5 );
			COPY_PIXEL( 6 );
			COPY_PIXEL( 7 );

#undef COPY_PIXEL

			}
			dest += vid.rowbytes;
			source += pic->width;
		}
	}
	
}

/*
=============
Draw_StretchPic
=============
*/
void Draw_StretchPic( int x, int y, int w, int h, float s1, float t1, float s2, float t2, const color_t color, qhandle_t hPic ) {
	image_t *image;

	if( !( image = R_ImageForHandle( hPic ) ) ) {
		return;
	}


	if( color && image->transparent && ( color[0] != 255 || color[1] != 255 || color[2] != 255 ) ) {
		Draw_TransformedPicFilled( x, y, w, h, s1, t1, s2, t2, R_IndexForColor( color ), image );
	} else {
		Draw_TransformedPic( x, y, w, h, s1, t1, s2, t2, image );
	}
	

	//Draw_TransformedPic( x, y, w, h, s1, t1, s2, t2, image );
}

/*
=============
Draw_StretchPic
=============
*/
void Draw_Pic( int x, int y, const color_t color, qhandle_t hPic ) {
	image_t *image;

	if( !( image = R_ImageForHandle( hPic ) ) ) {
		return;
	}


	if( color && image->transparent && ( color[0] != 255 || color[1] != 255 || color[2] != 255 ) ) {
		Draw_StaticPicFilled( x, y, R_IndexForColor( color ), image );
	} else {
		Draw_StaticPic( x, y, image );
	}

	//Draw_StaticPic( x, y, image );

}




/*
=============
Draw_StretchRaw
=============
*/
void Draw_StretchRaw( int x, int y, int w, int h, int cols, int rows, const byte *data ) {
	image_t	pic;

	pic.pixels[0] = (byte *)data;
	pic.width = cols;
	pic.height = rows;
	Draw_TransformedPic( x, y, w, h, 0.0f, 0.0f, 1.0f, 1.0f, &pic );
}

/*
===============
Draw_String
===============
*/
void Draw_String( int x, int y, int cw, int ch, const color_t color, int flags, int maxChars, const char *string, qhandle_t hImage ) {
	float fcol, frow;
	byte c;
	qboolean italic;
	image_t *image;
	/*color_t	temp;*/

	if( !( image = R_ImageForHandle( hImage ) ) ) {
		return;
	}

	italic = ( flags & UI_ITALIC ) != 0;


	while( *string ) {
		if( !( flags & UI_IGNOREESCAPES ) ) {
			if( Q_IsColorEscape( string ) ) {
				string++;
			} else if( Q_IsColorString( string ) ) {
				string++;
				switch( c = *string++ ) {
				case COLOR_ITALIC:
					italic = qtrue;
					break;
				case COLOR_RESET:
					italic = qfalse;
					break;
				default:
#if 0						
					if( !( flags & UI_IGNORECOLOR ) ) {
						VectorCopy( colorTable[ ColorIndex( c ) ], temp ); /* don't copy alpha */
						temp[3] = color ? color[3] : 0xff;
					}
#endif					
					break;
				}
				continue;
			}
		}

		if( !maxChars-- ) {
			break;
		}

		if( !( c = *string++ ) ) {
			break;
		}

		if( c == 32 ) {
			x += cw;	/* optimized case */
			continue;
		}

		frow = ( c >> 4 ) * 0.0625f;
		fcol = ( c & 15 ) * 0.0625f;
		
		Draw_TransformedPic( x, y, cw, ch, fcol, frow, fcol + 0.0625f, frow + 0.0625f, image );

		
		x += cw;

	}


}

/*
=============
Draw_TileClear

This repeats a 64*64 tile graphic to fill the screen around a sized down
refresh window.
=============
*/
void Draw_TileClear( int x, int y, int w, int h, qhandle_t hPic ) {
	int			i, j;
	byte		*psrc;
	byte		*pdest;
	image_t		*pic;
	int			x2;

	if (x < 0)
	{
		w += x;
		x = 0;
	}
	if (y < 0)
	{
		h += y;
		y = 0;
	}
	if (x + w > vid.width)
		w = vid.width - x;
	if (y + h > vid.height)
		h = vid.height - y;
	if (w <= 0 || h <= 0)
		return;

	if( !( pic = R_ImageForHandle( hPic ) ) ) {
		return;
	}
	x2 = x + w;
	pdest = vid.buffer + y*vid.rowbytes;
	for (i=0 ; i<h ; i++, pdest += vid.rowbytes)
	{
		psrc = pic->pixels[0] + pic->width * ((i+y)&63);
		for (j=x ; j<x2 ; j++)
			pdest[j] = psrc[j&63];
	}
}


/*
=============
Draw_Fill

Fills a box of pixels with a single color
=============
*/
void Draw_Fill (int x, int y, int w, int h, int c)
{
	byte			*dest;
	int				u, v;

	if (x+w > vid.width)
		w = vid.width - x;
	if (y+h > vid.height)
		h = vid.height - y;
	if (x < 0)
	{
		w += x;
		x = 0;
	}
	if (y < 0)
	{
		h += y;
		y = 0;
	}
	if (w < 0 || h < 0)
		return;
	dest = vid.buffer + y*vid.rowbytes + x;
	for (v=0 ; v<h ; v++, dest += vid.rowbytes)
		for (u=0 ; u<w ; u++)
			dest[u] = c;
}

static byte Blend33( int pcolor, int dstcolor ) {
	return vid.alphamap[pcolor + dstcolor*256];
}

static byte Blend66( int pcolor, int dstcolor ) {
	return vid.alphamap[pcolor*256+dstcolor];
}

void Draw_FillEx( int x, int y, int w, int h, const color_t color ) {
	int colorIndex;
	byte			*dest;
	int				u, v;
	byte (*blendfunc)( int, int );

	colorIndex = color ? R_IndexForColor( color ) : 0xD7;

	blendfunc = NULL;
	if( color[3] < 172 ) {
		blendfunc = color[3] > 84 ? Blend66 : Blend33;
	}

	if (x+w > vid.width)
		w = vid.width - x;
	if (y+h > vid.height)
		h = vid.height - y;
	if (x < 0)
	{
		w += x;
		x = 0;
	}
	if (y < 0)
	{
		h += y;
		y = 0;
	}
	if (w < 0 || h < 0)
		return;
	dest = vid.buffer + y*vid.rowbytes + x;
	for (v=0 ; v<h ; v++, dest += vid.rowbytes)
		for (u=0 ; u<w ; u++)
			dest[u] = blendfunc ? blendfunc( colorIndex, dest[u] ) : colorIndex;
	
}


//=============================================================================

/*
================
Draw_FadeScreen

================
*/
void Draw_FadeScreen (void)
{
	int			x,y;
	byte		*pbuf;
	int	t;

	for (y=0 ; y<vid.height ; y++)
	{
		pbuf = (byte *)(vid.buffer + vid.rowbytes*y);
		t = (y & 1) << 1;

		for (x=0 ; x<vid.width ; x++)
		{
			if ((x & 3) != t)
				pbuf[x] = 0;
		}
	}
}
