//-----------------------------------------------------------------------------
//
//  $Logfile:: /Source/r_draw.c                                               $
// $Revision:: 3                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 8/8/01 4:42 PM                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
//

#include "r_local.h"


image_t *draw_chars;	// 8*8 graphic characters

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

/*
==============
PicImage
==============
*/
#define MAX_PICIMAGES 64
#define MAX_PICIMAGE_LENGTH ( 64 - ( sizeof( image_t * ) ) )

typedef struct
{
	char name[ MAX_PICIMAGE_LENGTH ];
	image_t *image;
} picimage_t;

static picimage_t picimages[ MAX_PICIMAGES ];
static int numpicimages = 0;
static int picimagesptr = 0;

static void AddPicImage (const char * name, image_t * image)
{
	if ( strlen( name ) > MAX_PICIMAGE_LENGTH )
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "AddPicImage: image name too long %s\n", name );
		return;
	}

	strcpy( picimages[ picimagesptr ].name, name );
	picimages[ picimagesptr ].image = image;
	picimagesptr++;
	if ( picimagesptr >= MAX_PICIMAGES )
		picimagesptr = 0;
	if ( numpicimages < MAX_PICIMAGES )
		numpicimages++;
}

static image_t * FindPicImage (const char * name)
{
	int i;

	for( i = 0; i < numpicimages; i++ )
	{
		if ( !Q_stricmp( name, picimages[ i ].name ) )
		{
			return picimages[ i ].image;
		}
	}

	return NULL;
}

static image_t *PicImage (const char * name)
{
	image_t *image;

	image = FindPicImage( name );
	if ( image )
	{
		return image;
	}

	image = R_FindImage( name, it_pic, false );
	AddPicImage( name, image );

	return image;
}

/*
================
Draw_FindPic
================
*/
image_t *Draw_FindPic (const char *name)
{
	image_t *image;
	char fullname[MAX_QPATH];

	if (name[0] != '/' && name[0] != '\\')
	{
		Com_sprintf ( fullname, sizeof(fullname), "pics/%s", name );
		COM_DefaultExtension( fullname, ".tga" );
		image = PicImage ( fullname );
	}
	else
		image = PicImage ( name+1 );

	return image;
}

/*
===============
Draw_InitLocal
===============
*/
void Draw_InitLocal (void)
{
	numpicimages = 0;
	picimagesptr = 0;
	draw_chars = R_FindImage ( r_conchars->string, it_pic, true );
	if (!draw_chars)
	{
		ri.Sys_Error ( ERR_FATAL,"Draw_InitLocal: Could not load %s",r_conchars->string );
	}
}

/*
================
Draw_Char16bit

Draws one 8*8 graphics character, 16 bit
It can be clipped to the top of the screen to allow the console to be
smoothly scrolled off.
================
*/
static void Draw_Char16bit (int x, int y, int num)
{
	unsigned short *dest;
	unsigned short  *source;
	int drawline;
	int row, col;

	num &= 255;

	if (num == 32 || num == 32+128)
		return;

	if (y <= -8)
		return; // totally off screen

	if ( y >= vid.height )
		return;

	if ( !draw_chars )
		return;

	row = num>>4;
	col = num&15;
	source = (unsigned short *)draw_chars->pixels[0] + (row<<10) + (col<<3);

	if (y < 0)
	{	// clipped
		drawline = 8 + y;
		source -= 128*y;
		y = 0;
	}
	else if ( y > vid.height - 8 )
	{
		drawline = vid.height - y;
	}
	else
		drawline = 8;

	dest = (unsigned short * )vid.buffer + y*vid.rowbytes + x;

	while (drawline--)
	{
		if (source[0] != TRANSPARENT_COLOR16)
			dest[0] = source[0];
		if (source[1] != TRANSPARENT_COLOR16)
			dest[1] = source[1];
		if (source[2] != TRANSPARENT_COLOR16)
			dest[2] = source[2];
		if (source[3] != TRANSPARENT_COLOR16)
			dest[3] = source[3];
		if (source[4] != TRANSPARENT_COLOR16)
			dest[4] = source[4];
		if (source[5] != TRANSPARENT_COLOR16)
			dest[5] = source[5];
		if (source[6] != TRANSPARENT_COLOR16)
			dest[6] = source[6];
		if (source[7] != TRANSPARENT_COLOR16)
			dest[7] = source[7];
		source += 128;
		dest += vid.rowbytes;
	}
}

/*
================
Draw_Char

Draws one 8*8 graphics character
It can be clipped to the top of the screen to allow the console to be
smoothly scrolled off.
================
*/
void Draw_Char (int x, int y, int num)
{
	byte *dest;
	byte *source;
	int drawline;
	int row, col;

	if ( !draw_chars )
		return;

	if ( vid.bitdepth > 8 )
	{
		Draw_Char16bit( x, y, num );
		return;
	}

	num &= 255;

	if (num == 32 || num == 32+128)
		return;

	if (y <= -8)
		return; // totally off screen

	if ( y >= vid.height )
		return;

#ifdef PARANOID
	if (y > vid.height - 8 || x < 0 || x > vid.width - 8)
		ri.Sys_Error ( ERR_FATAL,"Con_DrawCharacter: (%i, %i)", x, y );
	if (num < 0 || num > 255)
		ri.Sys_Error ( ERR_FATAL,"Con_DrawCharacter: char %i", num );
#endif

	row = num>>4;
	col = num&15;
	source = draw_chars->pixels[0] + (row<<10) + (col<<3);

	if (y < 0)
	{	// clipped
		drawline = 8 + y;
		source -= 128*y;
		y = 0;
	}
	else if ( y > vid.height - 8 )
	{
		drawline = vid.height - y;
	}
	else
		drawline = 8;

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

	while (drawline--)
	{
		if (source[0] != TRANSPARENT_COLOR)
			dest[0] = source[0];
		if (source[1] != TRANSPARENT_COLOR)
			dest[1] = source[1];
		if (source[2] != TRANSPARENT_COLOR)
			dest[2] = source[2];
		if (source[3] != TRANSPARENT_COLOR)
			dest[3] = source[3];
		if (source[4] != TRANSPARENT_COLOR)
			dest[4] = source[4];
		if (source[5] != TRANSPARENT_COLOR)
			dest[5] = source[5];
		if (source[6] != TRANSPARENT_COLOR)
			dest[6] = source[6];
		if (source[7] != TRANSPARENT_COLOR)
			dest[7] = source[7];
		source += 128;
		dest += vid.rowbytes;
	}
}

/*
=============
Draw_GetPicSize
=============
*/
void Draw_GetPicSize (int *w, int *h, const char *pic)
{
	image_t *gl;

	gl = Draw_FindPic ( pic );
	if (!gl)
	{
		*w = *h = -1;
		return;
	}
	*w = gl->width;
	*h = gl->height;
}

/*
=============
Draw_StretchPicImplementation
=============
*/
static void Draw_StretchPicImplementation (int x, int y, int w, int h, int c, float a, image_t *pic)
{
	byte *dest, *source;
	int v, u, sv;
	int height;
	int f, fstep;
	int skip;

	if ( x < 0 || ( x + w > vid.width ) )
		return;         // totally off screen
	if ( y <= -h )
		return;         // totally off screen
	if ( ( y + h > vid.height ) )
		y = vid.height - h;

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

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

	if (pic->transparent)
	{
		if (c >= 0)
		{
			if (a < 1.0f)
			{
				if (a > 0.66f)
				{
					for (v=0; v<height; v++, dest += vid.rowbytes)
					{
						sv = (skip + v)*pic->height/h;
						source = pic->pixels[0] + sv*pic->width;
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u++, f += fstep)
						{
							if (source[f>>16] != TRANSPARENT_COLOR)
								dest[u] = c;
						}
					}
				}
				else if (a > 0.33f)
				{
					byte * alphamap;
					alphamap = vid.alphamap + (c*255);
					for (v=0; v<height; v++, dest += vid.rowbytes)
					{
						sv = (skip + v)*pic->height/h;
						source = pic->pixels[0] + sv*pic->width;
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u++, f += fstep)
						{
							if (source[f>>16] != TRANSPARENT_COLOR)
								dest[u] = alphamap[ dest[u] ];
						}
					}
				}
				else
				{
					byte * alphamap;
					alphamap = vid.alphamap + c;
					for (v=0; v<height; v++, dest += vid.rowbytes)
					{
						sv = (skip + v)*pic->height/h;
						source = pic->pixels[0] + sv*pic->width;
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u++, f += fstep)
						{
							if (source[f>>16] != TRANSPARENT_COLOR)
								dest[u] = alphamap[ dest[u]*256 ];
						}
					}
				}
			}
			else
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					sv = (skip + v)*pic->height/h;
					source = pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u+=4)
					{
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u] = c;
						f += fstep;
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u+1] = c;
						f += fstep;
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u+2] = c;
						f += fstep;
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u+3] = c;
						f += fstep;
					}
				}
			}
		}
		else
		{
			if (a < 1.0f)
			{
				if (a > 0.66f)
				{
					for (v=0; v<height; v++, dest += vid.rowbytes)
					{
						sv = (skip + v)*pic->height/h;
						source = pic->pixels[0] + sv*pic->width;
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u++, f += fstep)
						{
							if (source[f>>16] != TRANSPARENT_COLOR)
								dest[u] = source[f>>16];
						}
					}
				}
				else if (a > 0.33f)
				{
					for (v=0; v<height; v++, dest += vid.rowbytes)
					{
						sv = (skip + v)*pic->height/h;
						source = pic->pixels[0] + sv*pic->width;
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u++, f += fstep)
						{
							if (source[f>>16] != TRANSPARENT_COLOR)
								dest[u] = vid.alphamap[ dest[u] + ( source[f>>16] * 256 ) ];
						}
					}
				}
				else
				{
					for (v=0; v<height; v++, dest += vid.rowbytes)
					{
						sv = (skip + v)*pic->height/h;
						source = pic->pixels[0] + sv*pic->width;
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u++, f += fstep)
						{
							if (source[f>>16] != TRANSPARENT_COLOR)
								dest[u] = vid.alphamap[ source[f>>16] + ( dest[u] * 256 ) ];
						}
					}
				}
			}
			else
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					sv = (skip + v)*pic->height/h;
					source = pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u+=4)
					{
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u] = source[f>>16];
						f += fstep;
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u+1] = source[f>>16];
						f += fstep;
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u+2] = source[f>>16];
						f += fstep;
						if (source[f>>16] != TRANSPARENT_COLOR)
							dest[u+3] = source[f>>16];
						f += fstep;
					}
				}
			}
		}
	}
	else
	{
		if (a < 1)
		{
			if (a > 0.66f)
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					sv = (skip + v)*pic->height/h;
					source = pic->pixels[0] + sv*pic->width;
					if (w == pic->width)
						memcpy( dest, source, w );
					else
					{
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u+=4)
						{
							dest[u] = source[f>>16];
							f += fstep;
							dest[u+1] = source[f>>16];
							f += fstep;
							dest[u+2] = source[f>>16];
							f += fstep;
							dest[u+3] = source[f>>16];
							f += fstep;
						}
					}
				}
			}
			else if (a > 0.33f)
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					sv = (skip + v)*pic->height/h;
					source = pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u++, f += fstep)
					{
						dest[u] = vid.alphamap[ ( source[f>>16] * 256 ) + dest[u] ];
					}
				}
			}
			else
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					sv = (skip + v)*pic->height/h;
					source = pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u++, f += fstep)
					{
						dest[u] = vid.alphamap[ ( dest[u] * 256 ) + source[f>>16] ];
					}
				}
			}
		}
		else
		{
			for (v=0; v<height; v++, dest += vid.rowbytes)
			{
				sv = (skip + v)*pic->height/h;
				source = pic->pixels[0] + sv*pic->width;
				if (w == pic->width)
					memcpy ( dest, source, w );
				else
				{
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u+=4)
					{
						dest[u] = source[f>>16];
						f += fstep;
						dest[u+1] = source[f>>16];
						f += fstep;
						dest[u+2] = source[f>>16];
						f += fstep;
						dest[u+3] = source[f>>16];
						f += fstep;
					}
				}
			}
		}
	}
}

/*
=============
Draw_StretchPicImplementation16bit
=============
*/
static void Draw_StretchPicImplementation16bit (int x, int y, int w, int h, float r, float g, float b, float a, image_t *pic)
{
	unsigned short *dest, *source;
	int v, u, sv;
	int height;
	int f, fstep;
	int skip;

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

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

	dest = (unsigned short *)vid.buffer + y * vid.rowbytes + x;

	//
	// see if we are going to color the pic
	//
	if ( r >= 0 )
	{
		int color;

		color = COLOR16BIT( r*255,g*255,b*255 );
		if (pic->transparent)
		{
			//
			// see it it is translucent
			//
			if ( a < 1.0f )
			{
				int sr,sg,sb;
				int alpha;

				alpha = NUMALPHALEVELS*a;
				sr = UNPACKRED16BIT( color );
				sg = UNPACKGREEN16BIT( color );
				sb = UNPACKBLUE16BIT( color );
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					unsigned short dst;
					int dr,dg,db;
					sv = (skip + v)*pic->height/h;
					source = (unsigned short *)pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u++, f += fstep)
					{
						if ( source[ f>>16 ] != TRANSPARENT_COLOR16 )
						{
							dst = dest[ u ];
							dr = UNPACKRED16BIT( dst );
							dg = UNPACKGREEN16BIT( dst );
							db = UNPACKBLUE16BIT( dst );
							dr = INTREDALPHA16( vid.alphamap16, alpha, sr, dr );
							dg = INTGREENALPHA16( vid.alphamap16, alpha, sg, dg );
							db = INTBLUEALPHA16( vid.alphamap16, alpha, sb, db );

							dest[u] = PACKALPHA16BIT( dr,dg,db );
						}
					}
				}
			}
			else
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					unsigned short pix;
					f = 0;
					sv = (skip + v)*pic->height/h;
					source = (unsigned short *)pic->pixels[0] + sv*pic->width;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u+=4)
					{
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u] = color;
						f += fstep;
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u+1] = color;
						f += fstep;
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u+2] = color;
						f += fstep;
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u+3] = color;
						f += fstep;
					}
				}
			}
		}
		else
		{
			//
			// see it it is translucent
			//
			if ( a < 1.0f )
			{
				int sr,sg,sb;
				int alpha;

				alpha = a * NUMALPHALEVELS;
				sr = UNPACKRED16BIT( color );
				sg = UNPACKGREEN16BIT( color );
				sb = UNPACKBLUE16BIT( color );
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					unsigned short dst;
					int dr,dg,db;
					for (u=0; u<w; u++)
					{
						dst = dest[ u ];
						dr = UNPACKRED16BIT( dst );
						dg = UNPACKGREEN16BIT( dst );
						db = UNPACKBLUE16BIT( dst );
						dr = INTREDALPHA16( vid.alphamap16, alpha, sr, dr );
						dg = INTGREENALPHA16( vid.alphamap16, alpha, sg, dg );
						db = INTBLUEALPHA16( vid.alphamap16, alpha, sb, db );
						dest[u] = PACKALPHA16BIT( dr,dg,db );
					}
				}
			}
			else
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					for (u=0; u<w; u++)
						dest[ u ] = color;
				}
			}
		}
	}
	else
	{
		if (pic->transparent)
		{
			//
			// see it it is translucent
			//
			if ( a < 1.0f )
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					unsigned short src;
					unsigned short dst;
					int sr,sg,sb,dr,dg,db;
					int alpha;

					alpha = a * NUMALPHALEVELS;
					sv = (skip + v)*pic->height/h;
					source = (unsigned short *)pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u++, f += fstep)
					{
						dst = dest[ u ];
						src = source[ f>>16 ];
						if ( src != TRANSPARENT_COLOR16 )
						{
							sr = UNPACKRED16BIT( src );
							sg = UNPACKGREEN16BIT( src );
							sb = UNPACKBLUE16BIT( src );
							dr = UNPACKRED16BIT( dst );
							dg = UNPACKGREEN16BIT( dst );
							db = UNPACKBLUE16BIT( dst );
							dr = INTREDALPHA16( vid.alphamap16, alpha, sr, dr );
							dg = INTGREENALPHA16( vid.alphamap16, alpha, sg, dg );
							db = INTBLUEALPHA16( vid.alphamap16, alpha, sb, db );
							dest[u] = PACKALPHA16BIT( dr,dg,db );
						}
					}
				}
			}
			else
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					unsigned short pix;
					sv = (skip + v)*pic->height/h;
					source = (unsigned short *)pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u+=4)
					{
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u] = pix;
						f += fstep;
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u+1] = pix;
						f += fstep;
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u+2] = pix;
						f += fstep;
						pix = source[f>>16];
						if ( pix != TRANSPARENT_COLOR16 )
							dest[u+3] = pix;
						f += fstep;
					}
				}
			}
		}
		else
		{
			//
			// see it it is translucent
			//
			if ( a < 1.0f )
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					unsigned short src;
					unsigned short dst;
					int sr,sg,sb,dr,dg,db;
					int alpha;

					alpha = a * NUMALPHALEVELS;
					sv = (skip + v)*pic->height/h;
					source = (unsigned short *)pic->pixels[0] + sv*pic->width;
					f = 0;
					fstep = pic->width*0x10000/w;
					for (u=0; u<w; u++, f += fstep)
					{
						dst = dest[ u ];
						src = source[ f>>16 ];
						sr = UNPACKRED16BIT( src );
						sg = UNPACKGREEN16BIT( src );
						sb = UNPACKBLUE16BIT( src );
						dr = UNPACKRED16BIT( dst );
						dg = UNPACKGREEN16BIT( dst );
						db = UNPACKBLUE16BIT( dst );
						dr = INTREDALPHA16( vid.alphamap16, alpha, sr, dr );
						dg = INTGREENALPHA16( vid.alphamap16, alpha, sg, dg );
						db = INTBLUEALPHA16( vid.alphamap16, alpha, sb, db );
						dest[u] = PACKALPHA16BIT( dr,dg,db );
					}
				}
			}
			else
			{
				for (v=0; v<height; v++, dest += vid.rowbytes)
				{
					sv = (skip + v)*pic->height/h;
					source = (unsigned short *)pic->pixels[0] + sv*pic->width;
					if (w == pic->width)
					{
						memcpy( dest, source, w<<1 );
					}
					else
					{
						f = 0;
						fstep = pic->width*0x10000/w;
						for (u=0; u<w; u+=4)
						{
							dest[u] = source[f>>16];
							f += fstep;
							dest[u+1] = source[f>>16];
							f += fstep;
							dest[u+2] = source[f>>16];
							f += fstep;
							dest[u+3] = source[f>>16];
							f += fstep;
						}
					}
				}
			}
		}
	}
}

/*
=============
Draw_StretchPic
=============
*/
void Draw_StretchPic (int x, int y, int w, int h, const char *name)
{
	image_t *pic;

	pic = Draw_FindPic ( name );
	if (!pic)
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "Can't find pic: %s\n", name );
		return;
	}

	if ( vid.bitdepth > 8 )
		Draw_StretchPicImplementation16bit ( x, y, w, h, -1, -1, -1, 1, pic );
	else
		Draw_StretchPicImplementation ( x, y, w, h, -1, 1, pic );
}

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

	pic.pixels[0] = data;
	pic.width = cols;
	pic.height = rows;
	pic.transparent = false;

	if ( vid.bitdepth > 8 )
		Draw_StretchPicImplementation16bit ( x, y, w, h, -1, -1, -1, 1, &pic );
	else
		Draw_StretchPicImplementation ( x, y, w, h, -1, 1, &pic );
}

/*
=============
Draw_Pic
=============
*/
void Draw_Pic (int x, int y, const char *name)
{
	image_t         *pic;
	byte            *dest, *source;
	int v, u;
	int tbyte;
	int height;

	pic = Draw_FindPic ( name );
	if (!pic)
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "Can't find pic: %s\n", name );
		return;
	}

	if ((x < 0) ||
	    (x + pic->width > vid.width) ||
	    (y + pic->height > vid.height))
		return; //	ri.Sys_Error (ERR_FATAL,"Draw_Pic: bad coordinates");

	if ( vid.bitdepth > 8 )
	{
		Draw_StretchPicImplementation16bit ( x, y, pic->width, pic->height, -1, -1, -1, 1, pic );
		return;
	}

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

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

	if (!pic->transparent)
	{
		for (v=0; v<height; v++)
		{
			memcpy( dest, source, pic->width );
			dest += vid.rowbytes;
			source += pic->width;
		}
	}
	else
	{
		if (pic->width & 7)
		{   // general
			for (v=0; v<height; v++)
			{
				for (u=0; u<pic->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<pic->width; u+=8)
				{
					if ( (tbyte=source[u]) != TRANSPARENT_COLOR)
						dest[u] = tbyte;
					if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR)
						dest[u+1] = tbyte;
					if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR)
						dest[u+2] = tbyte;
					if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR)
						dest[u+3] = tbyte;
					if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR)
						dest[u+4] = tbyte;
					if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR)
						dest[u+5] = tbyte;
					if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR)
						dest[u+6] = tbyte;
					if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR)
						dest[u+7] = tbyte;
				}
				dest += vid.rowbytes;
				source += pic->width;
			}
		}
	}
}

/*
=============
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, const char *name)
{
	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;

	pic = Draw_FindPic ( name );
	if (!pic)
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "Can't find pic: %s\n", name );
		return;
	}
	x2 = x + w;

	if ( vid.bitdepth > 8 )
	{
		unsigned short * psrc16;
		unsigned short * pdest16;
		pdest16 = (unsigned short *)vid.buffer + y*vid.rowbytes;
		for (i=0; i<h; i++, pdest16 += vid.rowbytes)
		{
			psrc16 = (unsigned short *)pic->pixels[0] + pic->width * ((i+y)&63);
			for (j=x; j<x2; j++)
				pdest16[j] = psrc16[j&63];
		}
	}
	else
	{
		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, float r, float g, float b)
{
	byte            *dest;
	int u, v;
	int col, c;

	// col = ((int)(r*31) << 11) | ( (int)(g*63) << 5 ) | (int)(b*31);		//	CJ: Endian
	col = (int)(r*31) | ( (int)(g*63) << 5 ) | ( (int)(b*31) << 11 );
	// col = (int)(r*31) | ( (int)(g*31) << 5 ) | ( (int)(b*31) << 10 );
	c = d_16to8table[col];

	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;

	if ( vid.bitdepth > 8 )
	{
		unsigned short * pdest16;
		c = COLOR16BIT( r*255,g*255,b*255 );
		pdest16 = (unsigned short *)vid.buffer + y*vid.rowbytes + x;
		for (v=0; v<h; v++, pdest16 += vid.rowbytes)
			for (u=0; u<w; u++)
				pdest16[u] = c;
	}
	else
	{
		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;
	}
}

/*
=============
Draw_FillAlpha

Fills a box of pixels with a single color
=============
*/
static void Draw_FillAlpha (int x, int y, int w, int h, int c, float alpha)
{
	byte *dest;
	byte *alphamap;
	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;
	if (alpha > 0.66f)
	{
		for (v=0; v<h; v++, dest += vid.rowbytes)
			for (u=0; u<w; u++)
				dest[u] = c;
	}
	else if (alpha > 0.33f)
	{
		alphamap = vid.alphamap + (c<<8);
		for (v=0; v<h; v++, dest += vid.rowbytes)
			for (u=0; u<w; u++)
				dest[u] = alphamap[dest[u]];
	}
	else
	{
		for (v=0; v<h; v++, dest += vid.rowbytes)
			for (u=0; u<w; u++)
				dest[u] = vid.alphamap[c + (int)dest[u]*256];
	}
}

/*
=============
Draw_FillAlpha16bit

Fills a box of pixels with a single color
=============
*/
static void Draw_FillAlpha16bit (int x, int y, int w, int h, float r, float g, float b, float alpha)
{
	unsigned short *dest;
	int u, v;
	int color;

	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;

	color = COLOR16BIT( r*255,g*255,b*255 );
	dest = (unsigned short *)vid.buffer + y*vid.rowbytes + x;
	if ( alpha == 1 )
	{
		for (v=0; v<h; v++, dest += vid.rowbytes)
		{
			for (u=0; u<w; u++)
			{
				dest[u] = color;
			}
		}
	}
	else
	{
		int sr,sg,sb;
		int aint;

		aint = alpha * NUMALPHALEVELS;
		sr = UNPACKRED16BIT( color );
		sg = UNPACKGREEN16BIT( color );
		sb = UNPACKBLUE16BIT( color );
		for (v=0; v<h; v++, dest += vid.rowbytes)
		{
			unsigned short dst;
			int dr,dg,db;
			for (u=0; u<w; u++)
			{
				dst = dest[ u ];
				dr = UNPACKRED16BIT( dst );
				dg = UNPACKGREEN16BIT( dst );
				db = UNPACKBLUE16BIT( dst );
				dr = INTREDALPHA16( vid.alphamap16, aint, sr, dr );
				dg = INTGREENALPHA16( vid.alphamap16, aint, sg, dg );
				db = INTBLUEALPHA16( vid.alphamap16, aint, sb, db );
				dest[u] = PACKALPHA16BIT( dr,dg,db );
			}
		}
	}
}

/*
=============
Draw_StretchPicColor
=============
*/
static void Draw_StretchPicColor (int x, int y, int w, int h, float r, float g, float b, float a, const char *name)
{
	image_t *pic;
	int col, c;
	float temp;

	pic = Draw_FindPic ( name );
	if (!pic)
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "Can't find pic: %s\n", name );
		return;
	}
	temp = r+g+b;
	if ( temp && temp < 3)
	{
//      col = ((int)(r*31) << 11) | ( (int)(g*63) << 5 ) | (int)(b*31);		//	CJ: Endian
		col = (int)(r*31) | ( (int)(g*63) << 5 ) | ( (int)(b*31) << 11 );
//   col = (int)(r*31) | ( (int)(g*31) << 5 ) | ( (int)(b*31) << 10 );
		c = d_16to8table[col];
	}
	else
	{
		c = -1;
	}
	if ( vid.bitdepth > 8 )
	{
		if ( c == -1 )
			Draw_StretchPicImplementation16bit ( x, y, w, h, -1, -1, -1, a, pic );
		else
			Draw_StretchPicImplementation16bit ( x, y, w, h, r, g, b, a, pic );
	}
	else
		Draw_StretchPicImplementation ( x, y, w, h, c, a, pic );
}

/*
================
Draw_ColorChar16bit

Draws one 8*8 graphics character, 16 bit
It can be clipped to the top of the screen to allow the console to be
smoothly scrolled off.
================
*/
static void Draw_ColorChar16bit (int x, int y, int num, float r, float g, float b, float a)
{
	unsigned short *dest;
	unsigned short  *source;
	int drawline;
	int row, col;
	int color;

	num &= 255;

	if (num == 32 || num == 32+128)
		return;

	if (y <= -8)
		return;         // totally off screen

	if ( y >= vid.height )
		return;

	row = num>>4;
	col = num&15;
	source = (unsigned short *)draw_chars->pixels[0] + (row<<10) + (col<<3);

	if (y < 0)
	{   // clipped
		drawline = 8 + y;
		source -= 128*y;
		y = 0;
	}
	else if ( y > vid.height - 8 )
	{
		drawline = vid.height - y;
	}
	else
		drawline = 8;

	dest = (unsigned short * )vid.buffer + y*vid.rowbytes + x;

	if ( r >= 0 )
	{
		color = COLOR16BIT( r*255,g*255,b*255 );

		if ( a < 1 )
		{
			int i;
			int sr,sg,sb;
			int dr,dg,db;
			int dst;
			int alpha;

			alpha = a * NUMALPHALEVELS;
			sr = UNPACKRED16BIT( color );
			sg = UNPACKGREEN16BIT( color );
			sb = UNPACKBLUE16BIT( color );
			while (drawline--)
			{
				for ( i = 0; i < 8; i++ )
				{
					if (source[i] != TRANSPARENT_COLOR16)
					{
						dst = dest[ i ];
						dr = UNPACKRED16BIT( dst );
						dg = UNPACKGREEN16BIT( dst );
						db = UNPACKBLUE16BIT( dst );
						dr = INTREDALPHA16( vid.alphamap16, alpha, sr, dr );
						dg = INTGREENALPHA16( vid.alphamap16, alpha, sg, dg );
						db = INTBLUEALPHA16( vid.alphamap16, alpha, sb, db );
						dest[ i ] = PACKALPHA16BIT( dr,dg,db );
					}
				}
				source += 128;
				dest += vid.rowbytes;
			}
		}
		else
		{
			while (drawline--)
			{
				if (source[0] != TRANSPARENT_COLOR16)
					dest[0] = color;
				if (source[1] != TRANSPARENT_COLOR16)
					dest[1] = color;
				if (source[2] != TRANSPARENT_COLOR16)
					dest[2] = color;
				if (source[3] != TRANSPARENT_COLOR16)
					dest[3] = color;
				if (source[4] != TRANSPARENT_COLOR16)
					dest[4] = color;
				if (source[5] != TRANSPARENT_COLOR16)
					dest[5] = color;
				if (source[6] != TRANSPARENT_COLOR16)
					dest[6] = color;
				if (source[7] != TRANSPARENT_COLOR16)
					dest[7] = color;
				source += 128;
				dest += vid.rowbytes;
			}
		}
	}
	else
	{
		if ( a < 1 )
		{
			int i;
			int sr,sg,sb;
			int dr,dg,db;
			int dst;
			int alpha;

			alpha = a * NUMALPHALEVELS;
			while (drawline--)
			{
				for ( i = 0; i < 8; i++ )
				{
					if (source[i] != TRANSPARENT_COLOR16)
					{
						dst = dest[ i ];
						sr = UNPACKRED16BIT( source[i] );
						sg = UNPACKGREEN16BIT( source[i] );
						sb = UNPACKBLUE16BIT( source[i] );
						dr = UNPACKRED16BIT( dst );
						dg = UNPACKGREEN16BIT( dst );
						db = UNPACKBLUE16BIT( dst );
						dr = INTREDALPHA16( vid.alphamap16, alpha, sr, dr );
						dg = INTGREENALPHA16( vid.alphamap16, alpha, sg, dg );
						db = INTBLUEALPHA16( vid.alphamap16, alpha, sb, db );
						dest[ i ] = PACKALPHA16BIT( dr,dg,db );
					}
				}
				source += 128;
				dest += vid.rowbytes;
			}
		}
		else
		{
			while (drawline--)
			{
				if (source[0] != TRANSPARENT_COLOR16)
					dest[0] = source[0];
				if (source[1] != TRANSPARENT_COLOR16)
					dest[1] = source[1];
				if (source[2] != TRANSPARENT_COLOR16)
					dest[2] = source[2];
				if (source[3] != TRANSPARENT_COLOR16)
					dest[3] = source[3];
				if (source[4] != TRANSPARENT_COLOR16)
					dest[4] = source[4];
				if (source[5] != TRANSPARENT_COLOR16)
					dest[5] = source[5];
				if (source[6] != TRANSPARENT_COLOR16)
					dest[6] = source[6];
				if (source[7] != TRANSPARENT_COLOR16)
					dest[7] = source[7];
				source += 128;
				dest += vid.rowbytes;
			}
		}
	}
}

/*
================
Draw_ColorChar

Draws one 8*8 graphics character
It can be clipped to the top of the screen to allow the console to be
smoothly scrolled off.
================
*/
static void Draw_ColorChar (int x, int y, int num, int c, float a)
{
	byte *dest;
	byte *source;
	int drawline;
	int row, col;
	int i;

	num &= 255;

	if (num == 32 || num == 32+128)
		return;

	if (y <= -8)
		return;         // totally off screen

	if ( y >= vid.height )
		return;

#ifdef PARANOID
	if (y > vid.height - 8 || x < 0 || x > vid.width - 8)
		ri.Sys_Error ( ERR_FATAL,"Con_DrawCharacter: (%i, %i)", x, y );
	if (num < 0 || num > 255)
		ri.Sys_Error ( ERR_FATAL,"Con_DrawCharacter: char %i", num );
#endif

	row = num>>4;
	col = num&15;
	source = draw_chars->pixels[0] + (row<<10) + (col<<3);

	if (y < 0)
	{   // clipped
		drawline = 8 + y;
		source -= 128*y;
		y = 0;
	}
	else if ( y > vid.height - 8 )
	{
		drawline = vid.height - y;
	}
	else
		drawline = 8;

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

	if (c >= 0)
	{
		if (a < 0.67f)
		{
			if (a > 0.33f)
			{
				byte * alphamap;
				alphamap = vid.alphamap + (c<<8);
				while (drawline--)
				{
					for (i=0; i<8; i++)
					{
						if (source[i] != TRANSPARENT_COLOR)
							dest[i] = alphamap[ dest[i] ];
					}
					source += 128;
					dest += vid.rowbytes;
				}
			}
			else
			{
				while (drawline--)
				{
					for (i=0; i<8; i++)
					{
						if (source[i] != TRANSPARENT_COLOR)
							dest[i] = vid.alphamap[c + (int)dest[i]*256];
					}
					source += 128;
					dest += vid.rowbytes;
				}
			}
		}
		else
		{
			while (drawline--)
			{
				if (source[0] != TRANSPARENT_COLOR)
					dest[0] = c;
				if (source[1] != TRANSPARENT_COLOR)
					dest[1] = c;
				if (source[2] != TRANSPARENT_COLOR)
					dest[2] = c;
				if (source[3] != TRANSPARENT_COLOR)
					dest[3] = c;
				if (source[4] != TRANSPARENT_COLOR)
					dest[4] = c;
				if (source[5] != TRANSPARENT_COLOR)
					dest[5] = c;
				if (source[6] != TRANSPARENT_COLOR)
					dest[6] = c;
				if (source[7] != TRANSPARENT_COLOR)
					dest[7] = c;
				source += 128;
				dest += vid.rowbytes;
			}
		}
	}
	else
	{
		if (a < 0.67f)
		{
			if (a > 0.33f)
			{
				while (drawline--)
				{
					for (i=0; i<8; i++)
					{
						if (source[i] != TRANSPARENT_COLOR)
							dest[i] = vid.alphamap[dest[i] + (int)source[i]*256]; ;
					}
					source += 128;
					dest += vid.rowbytes;
				}
			}
			else
			{
				while (drawline--)
				{
					for (i=0; i<8; i++)
					{
						if (source[i] != TRANSPARENT_COLOR)
							dest[i] = vid.alphamap[source[i] + (int)dest[i]*256]; ;
					}
					source += 128;
					dest += vid.rowbytes;
				}
			}
		}
		else
		{
			while (drawline--)
			{
				if (source[0] != TRANSPARENT_COLOR)
					dest[0] = source[0];
				if (source[1] != TRANSPARENT_COLOR)
					dest[1] = source[1];
				if (source[2] != TRANSPARENT_COLOR)
					dest[2] = source[2];
				if (source[3] != TRANSPARENT_COLOR)
					dest[3] = source[3];
				if (source[4] != TRANSPARENT_COLOR)
					dest[4] = source[4];
				if (source[5] != TRANSPARENT_COLOR)
					dest[5] = source[5];
				if (source[6] != TRANSPARENT_COLOR)
					dest[6] = source[6];
				if (source[7] != TRANSPARENT_COLOR)
					dest[7] = source[7];
				source += 128;
				dest += vid.rowbytes;
			}
		}
	}
}

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

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

================
*/
void Draw_FadeScreen (float r, float g, float b, float a)
{
	byte palette[256][4], *in, *out;
	int i, j;
	float alpha, one_minus_alpha;
	vec3_t premult;
	int v;

//	return;

	alpha = a;

	if (alpha > 1)
		alpha = 1;

	premult[0] = r*alpha*255;
	premult[1] = g*alpha*255;
	premult[2] = b*alpha*255;

	one_minus_alpha = (1.0 - alpha);

	in = d_8to24table;
	out = palette[0];
	i = 256;
	do {
		j = 0;
		do {
			v = premult[j] + one_minus_alpha * in[j];
			if (v > 255) {
				v = 255;
			}
			out[j] = v;
		} while (++j<3);
		out[3] = 255;
		in+=4;
		out+=4;
	} while (--i);

	R_GammaCorrectAndSetPalette( ( const unsigned char * ) palette[0] );
}

/*
=============
Draw_Fill3D
Fills a box of pixels with a single color
=============
*/
static void DoDraw_Fill3D (vec3_t org, float width, float height, vec3_t u, vec3_t v, float r, float g, float b, float a)
{
	vec3_t origin,svup,svright,point[4];
	int i,j;

	origin[0] = org[0];
	origin[1] = org[1];
	origin[2] = org[2];

	VectorCopy( v,svup );
	VectorCopy( u,svright );

	// lower left
	VectorCopy( origin,point[0] );

	// upper left
	VectorMA ( origin, height, svup, point[1] );

	// upper right
	VectorMA ( origin, height, svup, point[2] );
	VectorMA ( point[2], width, svright, point[2] );

	// lower right
	VectorCopy( origin,point[3] );
	VectorMA ( point[3], width, svright, point[3] );

	for ( i = 0; i < 3; i++ )
		for ( j = 0; j < 3; j++ )
			point[i][j] += 0.5f;


	R_IMFlatShadedQuad( point[0], point[1], point[2], point[3], r, g, b, a );
}

void Draw_Fill3D(float x1, float y1, float width, float height, float r, float g, float b, float a)
{
	vec3_t origin;
	vec3_t org1;
	float mat[3][3];

	if ( consoleInfo.onscreen )
	{
		if ( vid.bitdepth > 8 )
		{
			Draw_FillAlpha16bit
			(
				x1*consoleInfo.surface_width + 0.5f,
				vid.height-((y1+height)*consoleInfo.surface_height) + 0.5f,
				width*consoleInfo.surface_width + 0.5f,
				height*consoleInfo.surface_height + 0.5f,
				r,
				g,
				b,
				a );
			return;
		}
		else
		{
			int col, c;

			// col = ((int)(r*31) << 11) | ( (int)(g*63) << 5 ) | (int)(b*31);		//	CJ: Endian
			col = (int)(r*31) | ( (int)(g*63) << 5 ) | ( (int)(b*31) << 11 );
			// col = (int)(r*31) | ( (int)(g*31) << 5 ) | ( (int)(b*31) << 10 );
			c = d_16to8table[col];
			Draw_FillAlpha
			(
				x1*consoleInfo.surface_width + 0.5f,
				vid.height-((y1+height)*consoleInfo.surface_height) + 0.5f,
				width*consoleInfo.surface_width + 0.5f,
				height*consoleInfo.surface_height + 0.5f,
				c,
				a );
			return;
		}
	}

	origin[0] = x1 * consoleInfo.surface_width;
	origin[1] = y1 * consoleInfo.surface_height;
	origin[2] = 0;

	mat[0][0] = consoleInfo.u[0]; mat[0][1] = consoleInfo.u[1]; mat[0][2] = consoleInfo.u[2];
	mat[1][0] = consoleInfo.v[0]; mat[1][1] = consoleInfo.v[1]; mat[1][2] = consoleInfo.v[2];
	mat[2][0] = consoleInfo.n[0]; mat[2][1] = consoleInfo.n[1]; mat[2][2] = consoleInfo.n[2];

	MatrixTransformVector( origin,mat,org1 );
	VectorAdd( org1, consoleInfo.origin, org1 );
	VectorAdd( org1, consoleInfo.modelorigin, org1 );

	DoDraw_Fill3D( org1,
	               width * consoleInfo.surface_width,
	               height * consoleInfo.surface_height,
	               consoleInfo.u,consoleInfo.v,
	               r,g,b,a );
}

static void DrawQuadPic(image_t * image, vec3_t coord, vec3_t u, vec3_t v, float width, float height, float s, float t, float texwidth, float texheight,float r, float g, float b, float a)
{
	int color;
	vec3_t svup, svright;
	vec3_t origin,point;
	triangle_t tri;


	origin[0] = coord[0];
	origin[1] = coord[1];
	origin[2] = coord[2];

	VectorAdd( origin, consoleInfo.origin, origin );
	VectorAdd( origin, consoleInfo.modelorigin, origin );

	// color = (int)(r*31) | ( (int)(g*63) << 5 ) | ( (int)(b*31) << 11 );		//	CJ: Endian
	color = (int)(r*31) | ( (int)(g*63) << 5 ) | ( (int)(b*31) << 11 );
	// color = (int)(r*31) | ( (int)(g*31) << 5 ) | ( (int)(b*31) << 10 );
	color = d_16to8table[color];

	VectorCopy( v,svup );
	VectorCopy( u,svright );

	// upper left
	VectorMA ( origin, height, svup, point );
	VectorCopy( point,tri.verts[0] );
	tri.verts[0][3] = s;
	tri.verts[0][4] = t;
	tri.verts[0][5] = color;
	tri.verts[0][6] = color;
	tri.verts[0][7] = color;
	tri.verts[0][5] = r;
	tri.verts[0][6] = g;
	tri.verts[0][7] = b;
	// lower left
	VectorCopy ( origin, point );
	VectorCopy ( point, tri.verts[1] );
	tri.verts[1][3] = s;
	tri.verts[1][4] = t + texheight;
	tri.verts[1][5] = color;
	tri.verts[1][6] = color;
	tri.verts[1][7] = color;
	tri.verts[1][5] = r;
	tri.verts[1][6] = g;
	tri.verts[1][7] = b;
	// upper right
	VectorMA ( origin, height, svup, point );
	VectorMA ( point, width, svright, &tri.verts[2][0] );
	tri.verts[2][3] = s + texwidth;
	tri.verts[2][4] = t;
	tri.verts[2][5] = color;
	tri.verts[2][6] = color;
	tri.verts[2][7] = color;
	tri.verts[2][5] = r;
	tri.verts[2][6] = g;
	tri.verts[2][7] = b;
	// upper right
	VectorMA ( origin, height, svup, point );
	VectorMA ( point, width, svright, &tri.verts[3][0] );
	tri.verts[3][3] = s + texwidth;
	tri.verts[3][4] = t;
	tri.verts[3][5] = color;
	tri.verts[3][6] = color;
	tri.verts[3][7] = color;
	tri.verts[3][5] = r;
	tri.verts[3][6] = g;
	tri.verts[3][7] = b;
	// lower left
	VectorCopy ( origin,tri.verts[4] );
	tri.verts[4][3] = s;
	tri.verts[4][4] = t + texheight;
	tri.verts[4][5] = color;
	tri.verts[4][6] = color;
	tri.verts[4][7] = color;
	tri.verts[4][5] = r;
	tri.verts[4][6] = g;
	tri.verts[4][7] = b;
	// lower right
	VectorCopy ( origin, point );
	VectorMA ( point, width, svright, &tri.verts[5][0] );
	tri.verts[5][3] = s + texwidth;
	tri.verts[5][4] = t + texheight;
	tri.verts[5][5] = color;
	tri.verts[5][6] = color;
	tri.verts[5][7] = color;
	tri.verts[5][5] = r;
	tri.verts[5][6] = g;
	tri.verts[5][7] = b;

	tri.num_tris = 2;
	tri.texture = image;

	tri.alpha = a;

	tri.flags |= TRI_TWOSIDED;
	tri.flags |= TRI_MASKED;

	VectorClear( tri.origin );
	VectorClear( tri.angles );
	DrawTriangle ( &tri );
}

void Flush_Char3D (float r, float g, float b, float a) {} /* FS: Dummy for GL vertex arrays */

static void DoDraw_Char3D(vec3_t coord,vec3_t u, vec3_t v,int num,float font_width, float font_height,float r, float g, float b, float a)
{
	float row, col;
	float frow, fcol, size;

	num &= 255;

	if ( (num&127) == 32 )
		return;     // space

	row = num>>4;
	col = num&15;

	frow = row*0.0625;
	fcol = col*0.0625;
	size = 0.0625;

	DrawQuadPic( draw_chars, coord, u, v, font_width, font_height, fcol, frow, size, size, r, g, b, a );
}

void Draw_Char3D(float x, float y, int c, int rows, int cols, float r, float g, float b, float a)
{
	if ( consoleInfo.onscreen )
	{
		if ( vid.bitdepth > 8 )
		{
			float temp;

			temp = r+g+b;
			if ( temp && temp < 3)
			{
				Draw_ColorChar16bit
				(
					x*consoleInfo.surface_width+0.5f,
					(vid.height-(y*consoleInfo.surface_height)-8)+0.5f,
					c,
					r,
					g,
					b,
					a
				);
			}
			else
			{
				Draw_ColorChar16bit
				(
					x*consoleInfo.surface_width+0.5f,
					(vid.height-(y*consoleInfo.surface_height)-8)+0.5f,
					c,
					-1,
					-1,
					-1,
					a
				);
			}
			return;
		}
		else
		{
			int col, color;
			float temp;

			temp = r+g+b;
			if ( temp && temp < 3)
			{
				// col = ((int)(r*31) << 11) | ( (int)(g*63) << 5 ) | (int)(b*31);		//	CJ: Endian
				col = (int)(r*31) | ( (int)(g*63) << 5 ) | ( (int)(b*31) << 11 );
				// col = (int)(r*31) | ( (int)(g*31) << 5 ) | ( (int)(b*31) << 10 );
				color = d_16to8table[col];
				Draw_ColorChar
				(
					x*consoleInfo.surface_width+0.5f,
					(vid.height-(y*consoleInfo.surface_height)-8)+0.5f,
					c,
					color,
					a
				);
			}
			else
			{
				color = -1;
				Draw_ColorChar
				(
					x*consoleInfo.surface_width+0.5f,
					(vid.height-(y*consoleInfo.surface_height)-8)+0.5f,
					c,
					color,
					a
				);
			}
			return;
		}
	}
	else
	{
		vec3_t origin,org1;
		float mat[3][3];
		float font_width  = consoleInfo.surface_width  / cols;
		float font_height = consoleInfo.surface_height / rows;

		origin[0] = x * consoleInfo.surface_width;
		origin[1] = y * consoleInfo.surface_height;
		origin[2] = 0;

		mat[0][0] = consoleInfo.u[0]; mat[0][1] = consoleInfo.u[1]; mat[0][2] = consoleInfo.u[2];
		mat[1][0] = consoleInfo.v[0]; mat[1][1] = consoleInfo.v[1]; mat[1][2] = consoleInfo.v[2];
		mat[2][0] = consoleInfo.n[0]; mat[2][1] = consoleInfo.n[1]; mat[2][2] = consoleInfo.n[2];

		MatrixTransformVector( origin,mat,org1 );
		DoDraw_Char3D ( org1,consoleInfo.u,consoleInfo.v,c,font_width,font_height,r,g,b,a );
	}
}

static void DoDraw_Pic3D (vec3_t coord, vec3_t u, vec3_t v, const char *pic, float scalex, float scaley, float r, float g, float b, float a)
{
	image_t *gl;

	gl = Draw_FindPic ( pic );
	if (!gl)
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "Can't find pic: %s\n", pic );
		return;
	}

	DrawQuadPic( gl, coord, u, v, gl->width*scalex, gl->height*scaley, 0, 0, gl->width, gl->height, r, g, b, a );
}

void Draw_Pic3D(float x, float y, const char *picn, float scalex, float scaley, float r, float g, float b, float a)
{
	if ( consoleInfo.onscreen )
	{
		int w, h;
		Draw_GetPicSize( &w, &h, picn );
		Draw_StretchPicColor
		(
			x*consoleInfo.surface_width + 0.5f,
			(vid.height-(y*consoleInfo.surface_height)-(scaley*h*consoleInfo.surface_height)) + 0.5f,
			w*scalex*consoleInfo.surface_width + 0.5f,
			h*scaley*consoleInfo.surface_height + 0.5f,
			r,
			g,
			b,
			a,
			picn
		);
		return;
	}
	else
	{
		vec3_t origin,org1;
		float mat[3][3];

		// put this in to fix software crash which disables pics on consoles in the world, but oh well.
		return;

		origin[0] = x * consoleInfo.surface_width;
		origin[1] = y * consoleInfo.surface_height;
		origin[2] = 0;

		mat[0][0] = consoleInfo.u[0]; mat[0][1] = consoleInfo.u[1]; mat[0][2] = consoleInfo.u[2];
		mat[1][0] = consoleInfo.v[0]; mat[1][1] = consoleInfo.v[1]; mat[1][2] = consoleInfo.v[2];
		mat[2][0] = consoleInfo.n[0]; mat[2][1] = consoleInfo.n[1]; mat[2][2] = consoleInfo.n[2];

		MatrixTransformVector( origin,mat,org1 );

		DoDraw_Pic3D( org1,
		              consoleInfo.u,
		              consoleInfo.v,
		              picn,
		              consoleInfo.surface_width*scalex,
		              consoleInfo.surface_height*scaley,
		              r,g,b,a );
	}
}

static void DoDraw_StretchPic3D (vec3_t coord, vec3_t u, vec3_t v, int width, int height, const char *pic, float r, float g, float b, float a)
{
	image_t *gl;

	gl = Draw_FindPic ( pic );
	if (!gl)
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "Can't find pic: %s\n", pic );
		return;
	}

	DrawQuadPic( gl, coord, u, v, width, height, 0, 0, gl->width, gl->height, r, g, b, a );
}

void Draw_StretchPic3D(float x, float y, float width, float height, const char *picn, float r, float g, float b, float a)
{
	if ( consoleInfo.onscreen )
	{
		Draw_StretchPicColor
		(
			x*consoleInfo.surface_width + 0.5f,
			vid.height-((y+height)*consoleInfo.surface_height) + 0.5f,
			width*consoleInfo.surface_width + 0.5f,
			height*consoleInfo.surface_height + 0.5f,
			r,
			g,
			b,
			a,
			picn
		);
		return;
	}
	else
	{
		vec3_t origin,org1;
		float mat[3][3];

		origin[0] = x * consoleInfo.surface_width;
		origin[1] = y * consoleInfo.surface_height;
		origin[2] = 0;

		mat[0][0] = consoleInfo.u[0]; mat[0][1] = consoleInfo.u[1]; mat[0][2] = consoleInfo.u[2];
		mat[1][0] = consoleInfo.v[0]; mat[1][1] = consoleInfo.v[1]; mat[1][2] = consoleInfo.v[2];
		mat[2][0] = consoleInfo.n[0]; mat[2][1] = consoleInfo.n[1]; mat[2][2] = consoleInfo.n[2];

		MatrixTransformVector( origin,mat,org1 );

		DoDraw_StretchPic3D( org1,
		                     consoleInfo.u,
		                     consoleInfo.v,
		                     width * consoleInfo.surface_width,
		                     height * consoleInfo.surface_height,
		                     picn,
		                     r,g,b,a*consoleInfo.alpha );
	}
}

static void DoDraw_TilePic3D(vec3_t coord, vec3_t u, vec3_t v, int width, int height, const char *pic, float r, float g, float b, float a)
{
	image_t *gl;

	gl = Draw_FindPic ( pic );
	if (!gl)
	{
		ri.Con_Printf ( PRINT_DEVELOPER, "Can't find pic: %s\n", pic );
		return;
	}

	DrawQuadPic( gl, coord, u, v, width, height, 0, 0, gl->width, gl->height, r, g, b, a );
}

void Draw_TilePic3D(float x, float y, float width, float height, const char *picn, float r, float g, float b, float a)
{
	// FIXME: This is temporary.
	if ( consoleInfo.onscreen )
	{
		Draw_StretchPicColor
		(
			x*consoleInfo.surface_width + 0.5f,
			vid.height-((y+height)*consoleInfo.surface_height) + 0.5f,
			width*consoleInfo.surface_width + 0.5f,
			height*consoleInfo.surface_height + 0.5f,
			r,
			g,
			b,
			a,
			picn
		);
		return;
	}
	else
	{
		vec3_t origin,org1;
		float mat[3][3];

		origin[0] = x * consoleInfo.surface_width;
		origin[1] = y * consoleInfo.surface_height;
		origin[2] = 0;

		mat[0][0] = consoleInfo.u[0]; mat[0][1] = consoleInfo.u[1]; mat[0][2] = consoleInfo.u[2];
		mat[1][0] = consoleInfo.v[0]; mat[1][1] = consoleInfo.v[1]; mat[1][2] = consoleInfo.v[2];
		mat[2][0] = consoleInfo.n[0]; mat[2][1] = consoleInfo.n[1]; mat[2][2] = consoleInfo.n[2];

		MatrixTransformVector( origin,mat,org1 );

		DoDraw_TilePic3D( org1,
		                  consoleInfo.u,
		                  consoleInfo.v,
		                  width * consoleInfo.surface_width,
		                  height * consoleInfo.surface_height,
		                  picn,
		                  r,g,b,a*consoleInfo.alpha );
	}
}

static void * save_blitmap = NULL;
void Draw_BeginCinematic(void)
{
	int y;

	save_blitmap = NULL;
	if ( vid.blitmap )
		save_blitmap = vid.blitmap;
	vid.blitmap = NULL;
	if ( vid.bitdepth > 8 )
	{
		for ( y = 0; y < vid.height; y++ )
		{
			unsigned short * dest;
			dest = (unsigned short *)vid.buffer + y*vid.rowbytes;
			memset( dest, 0, vid.width*2 );
		}
		SWimp_EndFrame();
		for ( y = 0; y < vid.height; y++ )
		{
			unsigned short * dest;
			dest = (unsigned short *)vid.buffer + y*vid.rowbytes;
			memset( dest, 0, vid.width*2 );
		}
		SWimp_EndFrame();
		for ( y = 0; y < vid.height; y++ )
		{
			unsigned short * dest;
			dest = (unsigned short *)vid.buffer + y*vid.rowbytes;
			memset( dest, 0, vid.width*2 );
		}
		SWimp_EndFrame();
	}
	else
	{
		for ( y = 0; y < vid.height; y++ )
		{
			byte * dest;
			dest = vid.buffer + y*vid.rowbytes;
			memset( dest, 0, vid.width );
		}
		SWimp_EndFrame();
		for ( y = 0; y < vid.height; y++ )
		{
			byte * dest;
			dest = vid.buffer + y*vid.rowbytes;
			memset( dest, 0, vid.width );
		}
		SWimp_EndFrame();
		for ( y = 0; y < vid.height; y++ )
		{
			byte * dest;
			dest = vid.buffer + y*vid.rowbytes;
			memset( dest, 0, vid.width );
		}
		SWimp_EndFrame();
	}
}

void Draw_EndCinematic(void)
{
	vid.blitmap = (unsigned short*)save_blitmap;
	save_blitmap = NULL;
}

void Draw_CinematicFrame(int x, int y, int w, int h, int srcwidth, int srcheight, int pitch, unsigned short * data)
{
	int v, u, sv;
	int height;
	int f, fstep;
	int skip;

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

	height = h;
	if (y < 0)
	{
		skip = -y;
		height += y;
		y = 0;
	}
	else
		skip = 0;
	if ( vid.bitdepth > 8 )
	{
		unsigned short *dest, *source;

		dest = (unsigned short *)vid.buffer + y * vid.rowbytes + x;
		fstep = srcwidth*0x10000/w;
		for (v=0; v<height; v++, dest += vid.rowbytes)
		{
			sv = (skip + v)*srcheight/h;
			source = (unsigned short *)data + ((sv*pitch)>>1);
			if (w == srcwidth)
			{
				memcpy( dest, source, w<<1 );
			}
			else
			{
				f = 0;
				for (u=0; u<w; u+=4)
				{
					dest[u] = source[f>>16];
					f += fstep;
					dest[u+1] = source[f>>16];
					f += fstep;
					dest[u+2] = source[f>>16];
					f += fstep;
					dest[u+3] = source[f>>16];
					f += fstep;
				}
			}
		}
	}
	else
	{
		int i;
		byte *dest;
		unsigned short *source;

		dest = (byte *)vid.buffer + y * vid.rowbytes + x;
		fstep = srcwidth*0x10000/w;
		for (v=0; v<height; v++, dest += vid.rowbytes)
		{
			sv = (skip + v)*srcheight/h;
			source = (unsigned short *)data + ((sv*pitch)>>1);
			if (w == srcwidth)
			{
				for ( i = 0; i < w; i++ )
				{
					dest[ i ] = vid.color_16to8table[ source[ i ] ];
				}
			}
			else
			{
				f = 0;
				for (u=0; u<w; u+=4)
				{
					dest[ u ] = vid.color_16to8table[ source[ f>>16 ] ];
					f += fstep;
					dest[ u+1 ] = vid.color_16to8table[ source[ f>>16 ] ];
					f += fstep;
					dest[ u+2 ] = vid.color_16to8table[ source[ f>>16 ] ];
					f += fstep;
					dest[ u+3 ] = vid.color_16to8table[ source[ f>>16 ] ];
					f += fstep;
				}
			}
		}
	}
	//SWimp_EndFrame();
}
