/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  

Doom 3 BFG Edition Source Code 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 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "Precompiled.h"
#include "globaldata.h"

#include "doomdef.h"

#include "i_system.h"
#include "z_zone.h"
#include "w_wad.h"

#include "r_local.h"

// Needs access to LFB (guess what).
#include "v_video.h"

// State.
#include "doomstat.h"


// ?

// status bar height at bottom of screen

//
// All drawing to the view buffer is accomplished in this file.
// The other refresh files only know about ccordinates,
//  not the architecture of the frame buffer.
// Conveniently, the frame buffer is a linear one,
//  and we need only the base address,
//  and the total size == width*height*depth/8.,
//



// Color tables for different ::g->players,
//  translate a limited part to another
//  (color ramps used for  suit colors).
//




//
// R_DrawColumn
// Source is the top of the column to scale.
//

// first pixel in a column (possibly virtual) 

// just for profiling 

//
// A column is a vertical slice/span from a wall texture that,
//  given the DOOM style restrictions on the view orientation,
//  will always have constant z depth.
// Thus a special case loop for very fast rendering can
//  be used. It has also been used with Wolfenstein 3D.
// 
void R_DrawColumn ( lighttable_t * dc_colormap,
					byte * dc_source ) 
{ 
	int			count; 
	byte*		dest; 
	fixed_t		frac;
	fixed_t		fracstep;
	int theght = 1; //GK:Get the height of EVERYTHING
	int mheight = 0;
	if (::g->usesprite) {
		theght = ::g->spriteheight[::g->texnum];
	}
	else if (::g->issky) {
		theght = 1 ;
	}
	else {
		theght = ::g->s_textureheight[::g->texnum]>>FRACBITS;
	}
	mheight = theght - 1;
	//GK:Sanity check
	if (::g->dc_yh >= SCREENHEIGHT) {
		::g->dc_yh = SCREENHEIGHT - 1;
	}
	
	if (::g->dc_yl < 0) {
		::g->dc_yl = 0;
	}else
	if (::g->dc_yl >= ::g->viewheight) {
		::g->dc_yl = ::g->viewheight - 1;
	}
	count = ::g->dc_yh - ::g->dc_yl ; 

	// Zero length, column does not exceed a pixel.
	if (count >= 0) {
		//return; 

	#ifdef RANGECHECK 
		if ((unsigned)::g->dc_x >= (unsigned)::g->SCREENWIDTH
			|| ::g->dc_yl < 0
			|| ::g->dc_yh >= SCREENHEIGHT) 
			I_Error ("R_DrawColumn: %i to %i at %i", ::g->dc_yl, ::g->dc_yh, ::g->dc_x); 
	#endif 

		// Framebuffer destination address.
		// Use ::g->ylookup LUT to avoid multiply with ScreenWidth.
		// Use ::g->columnofs LUT for subwindows? 
		dest = ::g->ylookup[::g->dc_yl] + ::g->columnofs[::g->dc_x];  

		// Determine scaling,
		//  which is the only mapping to be done.
		fracstep = ::g->dc_iscale; 
		frac = ::g->dc_texturemid + (::g->dc_yl-::g->centery)*fracstep; 
		if (theght & mheight) { // not a power of 2 -- killough
			mheight++;
			mheight <<= FRACBITS;

			if (frac < 0)
				while ((frac += mheight) < 0);
			else
				while (frac >= mheight)
					frac -= mheight;

			// Inner loop that does the actual texture mapping,
			//  e.g. a DDA-lile scaling.
			// This is as fast as it gets.
			do
			{
				// Re-map color indices from wall texture column
				//  using a lighting/special effects LUT.

				// heightmask is the Tutti-Frutti fix -- killough

				const int truncated1 = frac >> FRACBITS;
				//const int wrapped1 = truncated1 & mheight;

				*dest = dc_colormap[dc_source[truncated1]];
				dest += ::g->SCREENWIDTH;
				if ((frac += fracstep) >= mheight)
					frac -= mheight;
				
			} while (count--);
		}
		else
		{
			//GK: for texture height which is power of 2 do the vanilla procedure (eliminate weird red bottom lines and game breaking bugs)
			do//while ((count -= 2) >= 0)   // texture height is a power of 2 -- killough
			{
				const int truncated1 = frac >> FRACBITS;
				//GK:Now that it has the right height use the mheight and no more the 127
				const int wrapped1 = truncated1 & mheight;
				*dest = dc_colormap[dc_source[wrapped1]];
				dest += ::g->SCREENWIDTH;
				frac += fracstep;
				//*dest = dc_colormap[dc_source[(frac >> FRACBITS) & mheight]];
				//dest += SCREENWIDTH;
				//frac += fracstep;
			} while (count--);
			//if (count & 1)
			//	*dest = dc_colormap[dc_source[(frac >> FRACBITS) & mheight]];
		}
	}
} 



// UNUSED.
// Loop unrolled.
#if 0
void R_DrawColumn (void) 
{ 
	int			count; 
	byte*		source;
	byte*		dest;
	byte*		colormap;

	unsigned		frac;
	unsigned		fracstep;
	unsigned		fracstep2;
	unsigned		fracstep3;
	unsigned		fracstep4;	 

	count = ::g->dc_yh - ::g->dc_yl + 1; 

	source = ::g->dc_source;
	colormap = ::g->dc_colormap;		 
	dest = ::g->ylookup[::g->dc_yl] + ::g->columnofs[::g->dc_x];  

	fracstep = ::g->dc_iscale<<9; 
	frac = (::g->dc_texturemid + (::g->dc_yl-::g->centery)*::g->dc_iscale)<<9; 

	fracstep2 = fracstep+fracstep;
	fracstep3 = fracstep2+fracstep;
	fracstep4 = fracstep3+fracstep;

	while (count >= 8) 
	{ 
		dest[0] = colormap[source[frac>>25]]; 
		dest[SCREENWIDTH] = colormap[source[(frac+fracstep)>>25]]; 
		dest[SCREENWIDTH*2] = colormap[source[(frac+fracstep2)>>25]]; 
		dest[SCREENWIDTH*3] = colormap[source[(frac+fracstep3)>>25]];

		frac += fracstep4; 

		dest[SCREENWIDTH*4] = colormap[source[frac>>25]]; 
		dest[SCREENWIDTH*5] = colormap[source[(frac+fracstep)>>25]]; 
		dest[SCREENWIDTH*6] = colormap[source[(frac+fracstep2)>>25]]; 
		dest[SCREENWIDTH*7] = colormap[source[(frac+fracstep3)>>25]]; 

		frac += fracstep4; 
		dest += SCREENWIDTH*8; 
		count -= 8;
	} 

	while (count > 0)
	{ 
		*dest = colormap[source[frac>>25]]; 
		dest += SCREENWIDTH; 
		frac += fracstep; 
		count--;
	} 
}
#endif


void R_DrawColumnLow ( lighttable_t * dc_colormap,
					   byte * dc_source ) 
{ 
	int			count; 
	byte*		dest; 
	byte*		dest2;
	fixed_t		frac;
	fixed_t		fracstep;	 

	count = ::g->dc_yh - ::g->dc_yl; 

	// Zero length.
	if (count < 0) 
		return; 

#ifdef RANGECHECK 
	if ((unsigned)::g->dc_x >= (unsigned)::g->SCREENWIDTH
		|| ::g->dc_yl < 0
		|| ::g->dc_yh >= SCREENHEIGHT)
	{

		I_Error ("R_DrawColumn: %i to %i at %i", ::g->dc_yl, ::g->dc_yh, ::g->dc_x);
	}
	//	::g->dccount++; 
#endif 
	// Blocky mode, need to multiply by 2.
	::g->dc_x <<= 1;

	dest = ::g->ylookup[::g->dc_yl] + ::g->columnofs[::g->dc_x];
	dest2 = ::g->ylookup[::g->dc_yl] + ::g->columnofs[::g->dc_x+1];

	fracstep = ::g->dc_iscale; 
	frac = ::g->dc_texturemid + (::g->dc_yl-::g->centery)*fracstep;

	do 
	{
		// Hack. Does not work corretly.
		*dest2 = *dest = ::g->dc_colormap[::g->dc_source[(frac>>FRACBITS)&127]];
		dest += ::g->SCREENWIDTH;
		dest2 += ::g->SCREENWIDTH;
		frac += fracstep; 

	} while (count--);
}


//
// Spectre/Invisibility.
//





//
// Framebuffer postprocessing.
// Creates a fuzzy image by copying pixels
//  from adjacent ones to left and right.
// Used with an all black colormap, this
//  could create the SHADOW effect,
//  i.e. spectres and invisible ::g->players.
//
void R_DrawFuzzColumn ( lighttable_t * dc_colormap,
						  byte * dc_source ) 
{ 
	int			count; 
	byte*		dest; 
	fixed_t		frac;
	fixed_t		fracstep;	 

	// Adjust borders. Low... 
	if (!::g->dc_yl) 
		::g->dc_yl = 1;

	// .. and high.
	if (::g->dc_yh == ::g->viewheight-1) 
		::g->dc_yh = ::g->viewheight - 2; 

	count = ::g->dc_yh - ::g->dc_yl; 

	// Zero length.
	if (count < 0) 
		return; 


#ifdef RANGECHECK 
	if ((unsigned)::g->dc_x >= (unsigned)::g->SCREENWIDTH
		|| ::g->dc_yl < 0 || ::g->dc_yh >= SCREENHEIGHT)
	{
		I_Error ("R_DrawFuzzColumn: %i to %i at %i",
			::g->dc_yl, ::g->dc_yh, ::g->dc_x);
	}
#endif


	// Keep till ::g->detailshift bug in blocky mode fixed,
	//  or blocky mode removed.
	/* WATCOM code 
	if (::g->detailshift)
	{
	if (::g->dc_x & 1)
	{
	outpw (GC_INDEX,GC_READMAP+(2<<8) ); 
	outp (SC_INDEX+1,12); 
	}
	else
	{
	outpw (GC_INDEX,GC_READMAP); 
	outp (SC_INDEX+1,3); 
	}
	dest = destview + ::g->dc_yl*80 + (::g->dc_x>>1); 
	}
	else
	{
	outpw (GC_INDEX,GC_READMAP+((::g->dc_x&3)<<8) ); 
	outp (SC_INDEX+1,1<<(::g->dc_x&3)); 
	dest = destview + ::g->dc_yl*80 + (::g->dc_x>>2); 
	}*/


	// Does not work with blocky mode.
	dest = ::g->ylookup[::g->dc_yl] + ::g->columnofs[::g->dc_x];

	// Looks familiar.
	fracstep = ::g->dc_iscale; 
	frac = ::g->dc_texturemid + (::g->dc_yl-::g->centery)*fracstep; 

	// Looks like an attempt at dithering,
	//  using the colormap #6 (of 0-31, a bit
	//  brighter than average).
	do 
	{
		// Lookup framebuffer, and retrieve
		//  a pixel that is either one column
		//  left or right of the current one.
		// Add index from colormap to index.
		*dest = ::g->colormaps[6*256+dest[::g->fuzzoffset[::g->fuzzpos]]]; 

		// Clamp table lookup index.
		if (++::g->fuzzpos == FUZZTABLE) 
			::g->fuzzpos = 0;

		dest += ::g->SCREENWIDTH;

		frac += fracstep; 
	} while (count--); 
} 




//
// R_DrawTranslatedColumn
// Used to draw player ::g->sprites
//  with the green colorramp mapped to others.
// Could be used with different translation
//  tables, e.g. the lighter colored version
//  of the BaronOfHell, the HellKnight, uses
//  identical ::g->sprites, kinda brightened up.
//

void R_DrawTranslatedColumn ( lighttable_t * dc_colormap,
						  byte * dc_source ) 
{ 
	int			count; 
	byte*		dest; 
	fixed_t		frac;
	fixed_t		fracstep;	 
	
	count = ::g->dc_yh - ::g->dc_yl; 
	if (count < 0) 
		return; 

#ifdef RANGECHECK 
	if ((unsigned)::g->dc_x >= (unsigned)::g->SCREENWIDTH
		|| ::g->dc_yl < 0
		|| ::g->dc_yh >= SCREENHEIGHT)
	{
		I_Error ( "R_DrawColumn: %i to %i at %i",
			::g->dc_yl, ::g->dc_yh, ::g->dc_x);
	}

#endif 


	// WATCOM VGA specific.
	/* Keep for fixing.
	if (::g->detailshift)
	{
	if (::g->dc_x & 1)
	outp (SC_INDEX+1,12); 
	else
	outp (SC_INDEX+1,3);

	dest = destview + ::g->dc_yl*80 + (::g->dc_x>>1); 
	}
	else
	{
	outp (SC_INDEX+1,1<<(::g->dc_x&3)); 

	dest = destview + ::g->dc_yl*80 + (::g->dc_x>>2); 
	}*/


	// FIXME. As above.
	dest = ::g->ylookup[::g->dc_yl] + ::g->columnofs[::g->dc_x]; 

	// Looks familiar.
	fracstep = ::g->dc_iscale; 
	frac = ::g->dc_texturemid + (::g->dc_yl-::g->centery)*fracstep; 

	// Here we do an additional index re-mapping.
	do 
	{
		// Translation tables are used
		//  to map certain colorramps to other ones,
		//  used with PLAY ::g->sprites.
		// Thus the "green" ramp of the player 0 sprite
		//  is mapped to gray, red, black/indigo. 
		*dest = dc_colormap[::g->dc_translation[dc_source[frac>>FRACBITS]]];
		dest += ::g->SCREENWIDTH;

		frac += fracstep; 
	} while (count--); 
} 




//
// R_InitTranslationTables
// Creates the translation tables to map
//  the green color ramp to gray, brown, red.
// Assumes a given structure of the PLAYPAL.
// Could be read from a lump instead.
//
void R_InitTranslationTables (void)
{
	int		i;

	::g->translationtables = (byte*)DoomLib::Z_Malloc (256*3+255, PU_STATIC, 0);
	::g->translationtables = (byte *)(( (intptr_t)::g->translationtables + 255 )& ~255);

	// translate just the 16 green colors
	for (i=0 ; i<256 ; i++)
	{
		if (i >= 0x70 && i<= 0x7f)
		{
			// map green ramp to gray, brown, red
			::g->translationtables[i] = 0x60 + (i&0xf);
			::g->translationtables [i+256] = 0x40 + (i&0xf);
			::g->translationtables [i+512] = 0x20 + (i&0xf);
		}
		else
		{
			// Keep all other colors as is.
			::g->translationtables[i] = ::g->translationtables[i+256] 
			= ::g->translationtables[i+512] = i;
		}
	}
}




//
// R_DrawSpan 
// With DOOM style restrictions on view orientation,
//  the floors and ceilings consist of horizontal slices
//  or spans with constant z depth.
// However, rotation around the world z axis is possible,
//  thus this mapping, while simpler and faster than
//  perspective correct texture mapping, has to traverse
//  the texture at an angle in all but a few cases.
// In consequence, flats are not stored by column (like walls),
//  and the inner loop has to step in texture space u and v.
//



// start of a 64*64 tile image 

// just for profiling


//
// Draws the actual span.
void R_DrawSpan ( fixed_t xfrac,
				  fixed_t yfrac,
				  fixed_t ds_y,
				  int ds_x1,
				  int ds_x2,
				  fixed_t ds_xstep,
				  fixed_t ds_ystep,
				  lighttable_t * ds_colormap,
				  byte * ds_source ) 
{ 
	byte*		dest; 
	int			count;
	int			spot; 

#ifdef RANGECHECK
	if (::g->ds_x2 < ::g->ds_x1
		|| ::g->ds_x1<0
		|| ::g->ds_x2>= ::g->SCREENWIDTH
		|| (unsigned)::g->ds_y>SCREENHEIGHT)
	{
		I_Error( "R_DrawSpan: %i to %i at %i",
			::g->ds_x1,::g->ds_x2,::g->ds_y);
	}
	//	::g->dscount++; 
#endif 
	
	dest = ::g->ylookup[::g->ds_y] + ::g->columnofs[::g->ds_x1];

	// We do not check for zero spans here?
	count = ds_x2 - g->ds_x1; 

	if ( ds_x2 < ds_x1 ) {
		return;						// SMF - think this is the sky
	}

	do 
	{
		// Current texture index in u,v.
		spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);

		// Lookup pixel from flat texture tile,
		//  re-index using light/colormap.
		*dest++ = ds_colormap[ds_source[spot]];

		// Next step in u,v.
		xfrac += ds_xstep; 
		yfrac += ds_ystep;

	} while (count--); 
} 



// UNUSED.
// Loop unrolled by 4.
#if 0
void R_DrawSpan (void) 
{ 
	unsigned	position, step;

	byte*	source;
	byte*	colormap;
	byte*	dest;

	unsigned	count;
	usingned	spot; 
	unsigned	value;
	unsigned	temp;
	unsigned	xtemp;
	unsigned	ytemp;

	position = ((::g->ds_xfrac<<10)&0xffff0000) | ((::g->ds_yfrac>>6)&0xffff);
	step = ((::g->ds_xstep<<10)&0xffff0000) | ((::g->ds_ystep>>6)&0xffff);

	source = ::g->ds_source;
	colormap = ::g->ds_colormap;
	dest = ::g->ylookup[::g->ds_y] + ::g->columnofs[::g->ds_x1];	 
	count = ::g->ds_x2 - ::g->ds_x1 + 1; 

	while (count >= 4) 
	{ 
		ytemp = position>>4;
		ytemp = ytemp & 4032;
		xtemp = position>>26;
		spot = xtemp | ytemp;
		position += step;
		dest[0] = colormap[source[spot]]; 

		ytemp = position>>4;
		ytemp = ytemp & 4032;
		xtemp = position>>26;
		spot = xtemp | ytemp;
		position += step;
		dest[1] = colormap[source[spot]];

		ytemp = position>>4;
		ytemp = ytemp & 4032;
		xtemp = position>>26;
		spot = xtemp | ytemp;
		position += step;
		dest[2] = colormap[source[spot]];

		ytemp = position>>4;
		ytemp = ytemp & 4032;
		xtemp = position>>26;
		spot = xtemp | ytemp;
		position += step;
		dest[3] = colormap[source[spot]]; 

		count -= 4;
		dest += 4;
	} 
	while (count > 0) 
	{ 
		ytemp = position>>4;
		ytemp = ytemp & 4032;
		xtemp = position>>26;
		spot = xtemp | ytemp;
		position += step;
		*dest++ = colormap[source[spot]]; 
		count--;
	} 
} 
#endif


//
// Again..
//
void R_DrawSpanLow ( fixed_t xfrac,
				  fixed_t yfrac,
				  fixed_t ds_y,
				  int ds_x1,
				  int ds_x2,
				  fixed_t ds_xstep,
				  fixed_t ds_ystep,
				  lighttable_t * ds_colormap,
				  byte * ds_source ) 
{
	byte*		dest; 
	int			count;
	int			spot; 

#ifdef RANGECHECK 
	if (::g->ds_x2 < ::g->ds_x1
		|| ::g->ds_x1<0
		|| ::g->ds_x2>= ::g->SCREENWIDTH
		|| (unsigned)::g->ds_y>SCREENHEIGHT)
	{
		I_Error( "R_DrawSpan: %i to %i at %i",
			::g->ds_x1,::g->ds_x2,::g->ds_y);
	}
	//	::g->dscount++; 
#endif 

	// Blocky mode, need to multiply by 2.
	::g->ds_x1 <<= 1;
	::g->ds_x2 <<= 1;
	
	dest = ::g->ylookup[::g->ds_y] + ::g->columnofs[::g->ds_x1];


	count = ::g->ds_x2 - ::g->ds_x1; 
	do 
	{ 
		spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
		// Lowres/blocky mode does it twice,
		//  while scale is adjusted appropriately.
		*dest++ = ::g->ds_colormap[::g->ds_source[spot]]; 
		*dest++ = ::g->ds_colormap[::g->ds_source[spot]];

		xfrac += ::g->ds_xstep; 
		yfrac += ::g->ds_ystep; 

	} while (count--); 
}

//
// R_InitBuffer 
// Creats lookup tables that avoid
//  multiplies and other hazzles
//  for getting the framebuffer address
//  of a pixel to draw.
//
void
R_InitBuffer
( int		width,
 int		height ) 
{ 
	int		i; 

	// Handle resize,
	//  e.g. smaller view windows
	//  with border and/or status bar.
	::g->viewwindowx = (::g->SCREENWIDTH-width) >> 1;
	if (::g->viewwindowx < 0)
		::g->viewwindowx = 0;

	// Column offset. For windows.
	for (i=0 ; i<width ; i++) 
		::g->columnofs[i] = ::g->viewwindowx + i;

	// Samw with base row offset.
	if (width == ::g->SCREENWIDTH)
		::g->viewwindowy = 0; 
	else 
		::g->viewwindowy = (SCREENHEIGHT-SBARHEIGHT-height) >> 1; 

	// Preclaculate all row offsets.
	for (i=0 ; i<height ; i++) 
		::g->ylookup[i] = ::g->screens[0] + (i+::g->viewwindowy)*::g->SCREENWIDTH;
} 




//
// R_FillBackScreen
// Fills the back screen with a pattern
//  for variable screen sizes
// Also draws a beveled edge.
//
void R_FillBackScreen (void) 
{ 
	byte*		src;
	byte*		dest; 
	int			x;
	int			y; 
	int			width, height, windowx, windowy;
	patch_t*	patch;

	// DOOM border patch.
	char	name1[] = "FLOOR7_2";
	// DOOM II border patch.
	char	name2[] = "GRNROCK";	

	char*	name;

	if (::g->scaledviewwidth == ::g->SCREENWIDTH)
		return;

	if ( ::g->gamemode == commercial)
		name = name2;
	else
		name = name1;

	src = (byte*)W_CacheLumpName (name, PU_CACHE_SHARED); 
	dest = ::g->screens[1]; 

	for (y=0 ; y<SCREENHEIGHT-SBARHEIGHT ; y++) { 
		for (x=0 ; x< ::g->SCREENWIDTH/64 ; x++) 	{
			memcpy(dest, src+((y&63)<<6), 64); 
			dest += 64; 
		} 
		if (::g->SCREENWIDTH&63)
		{ 
			memcpy(dest, src+((y&63)<<6), ::g->SCREENWIDTH&63);
			dest += (::g->SCREENWIDTH&63);
		} 
	} 

	width = ::g->scaledviewwidth / GLOBAL_IMAGE_SCALER;
	height = ::g->viewheight / GLOBAL_IMAGE_SCALER;
	windowx = ::g->viewwindowx / GLOBAL_IMAGE_SCALER;
	windowy = ::g->viewwindowy / GLOBAL_IMAGE_SCALER;

	patch = /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_t",PU_CACHE_SHARED), W_GetNumForName("brdr_t"));
	for (x=0 ; x<width ; x+=8) {
		V_DrawPatch (windowx+x,windowy-8,1,patch, false);
	}

	patch = /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_b",PU_CACHE_SHARED), W_GetNumForName("brdr_b"));
	for (x=0 ; x<width ; x+=8) {
		V_DrawPatch (windowx+x,windowy+height,1,patch, false);
	}

	patch = /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_l",PU_CACHE_SHARED), W_GetNumForName("brdr_l"));
	for (y=0 ; y<height ; y+=8) {
		V_DrawPatch (windowx-8,windowy+y,1,patch, false);
	}

	patch = /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_r",PU_CACHE_SHARED), W_GetNumForName("brdr_r"));
	for (y=0 ; y<height ; y+=8) {
		V_DrawPatch (windowx+width,windowy+y,1,patch, false);
	}

	// Draw beveled edge. 
	V_DrawPatch(windowx-8, windowy-8, 1, /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_tl",PU_CACHE_SHARED), W_GetNumForName("brdr_tl")), false);
	V_DrawPatch(windowx+width, windowy-8, 1, /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_tr",PU_CACHE_SHARED), W_GetNumForName("brdr_tr")), false);
	V_DrawPatch(windowx-8, windowy+height, 1, /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_bl",PU_CACHE_SHARED), W_GetNumForName("brdr_bl")), false);
	V_DrawPatch (windowx+width, windowy+height, 1, /*(patch_t*)*/img2lmp(W_CacheLumpName ("brdr_br",PU_CACHE_SHARED), W_GetNumForName("brdr_br")), false);
} 


//
// Copy a screen buffer.
//
void
R_VideoErase
( unsigned	ofs,
 int		count ) 
{ 
	// LFB copy.
	// This might not be a good idea if memcpy
	//  is not optiomal, e.g. byte by byte on
	//  a 32bit CPU, as GNU GCC/Linux libc did
	//  at one point.
	memcpy(::g->screens[0]+ofs, ::g->screens[1]+ofs, count); 
} 


//
// R_DrawViewBorder
// Draws the border around the view
//  for different size windows?
//
void
V_MarkRect
( int		x,
 int		y,
 int		width,
 int		height ); 

void R_DrawViewBorder (void) 
{ 
	int		top;
	int		side;
	int		ofs;
	int		i; 

	if (::g->scaledviewwidth == ::g->SCREENWIDTH)
		return; 

	top = ((SCREENHEIGHT-SBARHEIGHT)-::g->viewheight)/2; 
	side = (::g->SCREENWIDTH-::g->scaledviewwidth)/2;

	// copy top and one line of left side 
	R_VideoErase (0, top*::g->SCREENWIDTH+side);

	// copy one line of right side and bottom 
	ofs = (::g->viewheight+top)*::g->SCREENWIDTH-side;
	R_VideoErase (ofs, top*::g->SCREENWIDTH+side);

	// copy ::g->sides using wraparound 
	ofs = top* ::g->SCREENWIDTH + ::g->SCREENWIDTH-side;
	side <<= 1;

	for (i=1 ; i < ::g->viewheight ; i++) 
	{ 
		R_VideoErase (ofs, side); 
		ofs += ::g->SCREENWIDTH;
	} 

	// ? 
	V_MarkRect (0,0, ::g->SCREENWIDTH, SCREENHEIGHT-SBARHEIGHT);
} 



