#include "Script.hpp"
//
//==========================================================================//
// File:	 Surface.cpp													//
// Project:  GameOS					                                        //
// Contents: DirectDraw Surface object										//
//--------------------------------------------------------------------------//
// Date      Who    Modification                                            //
// 11/15/97  JTR    Created													//
// 12/23/97  JTR    Documentation, Additions and Debugging 					//
// 01/05/98  JTR    Fixed blit clip bug					 					//
// 01/07/98  JTR    Added TGA loader, added setpixel	 					//
// 04/13/98  JTR	Added 8888RGB surface for 32bit desktops				//
// 04/23/98  JTR	Fixed bug in nativeformat for 24bit surfaces			//
//																			//
//--------------------------------------------------------------------------//
// Copyright (C) 2000, Microsoft, Inc.										//
// All Rights reserved worldwide                                            //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL              //
//==========================================================================//
//


extern void* DecodeJPG( const char* FileName, BYTE* Data, DWORD DataSize, DWORD* TextureWidth, DWORD* TextureHeight, bool TextureLoad, void *pDestSurf=NULL );
#ifdef USEPNG
extern void* DecodePNG( const char* FileName, BYTE* Data, DWORD DataSize, DWORD* TextureWidth, DWORD* TextureHeight, bool TextureLoad, void *pDestSurf=NULL );
#endif
extern void* DecodeTGA( const char* FileName, BYTE* Data, DWORD DataSize, DWORD* TextureWidth, DWORD* TextureHeight, bool TextureLoad, void *pDestSurf=NULL );
extern void* DecodeBMP( const char* FileName, BYTE* Data, DWORD DataSize, DWORD* TextureWidth, DWORD* TextureHeight, bool TextureLoad, void *pDestSurf=NULL );



extern HGOSHEAP gScriptHeap[];
// This list holds pointers to all the surfaces used in GameOS and the client app.
extern AllSurfacesList AllSurfaces;

// CLIENT VISISBLE
// Create a surface of a specified size and return its handle
void gos_CreateSurface(HGOSSURFACE* surf, DWORD width, DWORD height)
{
	gosASSERT(width > 0 && height > 0);
	SPEW(( GROUP_SURFACE, "Creating Surface (%d,%d) - 0x%x",width,height,(int)*surf ));
	*surf = new(gScriptHeap[2]) Surface(width, height);
}


// CLIENT VISISBLE
// Create a surface of a specified size and return its handle
void gos_CreateMemorySurface(HGOSSURFACE* surf, unsigned char * lpDyn, DWORD width, DWORD height, DWORD pitch)
{
	gosASSERT(width > 0 && height > 0);
	SPEW(( GROUP_SURFACE, "Creating Surface (%d,%d) - 0x%x",width,height,(int)*surf ));
	*surf = new(gScriptHeap[2]) Surface(lpDyn, width, height, pitch);
}

// CLIENT VISIBLE
// Create a surface of a specified format and load it with the image found in <path>. Any
// necessary format conversions will be taken care of by GameOS.
void gos_LoadSurface(const char* path, HGOSSURFACE* surf, bool is_volatile)
{
	gosASSERT(path != NULL);
	SPEW(( GROUP_SURFACE, "Loading Surface from '%s' - 0x%x",path,(int)*surf ));
	*surf = new(gScriptHeap[2]) Surface(path, is_volatile);
}


char * gos_GetSurfacePath(HGOSSURFACE surf)
{
	gosASSERT(surf != NULL);
	gosASSERT(IsValidSurface(surf));
	return surf->m_lpPath;
}
// CLIENT VISIBLE
// Deallocate a surface and remove it from the list of surfaces.
void gos_DestroySurface(HGOSSURFACE* surf)
{
	gosASSERT(IsValidSurface(*surf));
	delete *surf;
	*surf = 0;
}


// CLIENT VISIBLE
// Blit the <rct> portion of surface <src> to location <x>, <y> of surface <dst>.
// If <rct> is NULL, the function assume that the whole src surface is to be 
// blitted.	If the source and destination surfaces don't match, we have to lock
// them both, so we can read and write from/to them (we need to convert bitmaps
// on the fly).
void gos_BlitSurface(CUBE * rct, HGOSSURFACE src, int x, int y, HGOSSURFACE dst)
{
	gosASSERT(IsValidSurface(src));
	gosASSERT(IsValidSurface(dst));
	if (rct && (rct->p1.y == rct->p2.y || rct->p1.x == rct->p2.x)) 
		return;

	src->Blit((CUBE *)rct,  x,  y, dst);
}

void gos_StretchBlitSurface(CUBE * rct, HGOSSURFACE src, float sx, float sy, int x, int y, HGOSSURFACE dst)
{
	gosASSERT(IsValidSurface(src));
	gosASSERT(IsValidSurface(dst));
	src->StretchBlit((CUBE *)rct,  sx, sy, x, y, dst);
}



// CLIENT VISIBLE
// Set the entire surface color to the value specified in color.
// JTR - we should force the client app to specify a 888RGB color
// and do a best match with the color.
void gos_ClearSurface(HGOSSURFACE surf, DWORD color)
{
	gosASSERT(IsValidSurface(surf));
	surf->Clear(color);
}

void gos_RestoreSurface(HGOSSURFACE surf)
{
	gosASSERT(IsValidSurface(surf));
	surf->Restore();
}


// CLIENT VISIBLE
// Retrieve the width of the surface
DWORD gos_GetSurfaceWidth(HGOSSURFACE surf)
{
	gosASSERT(IsValidSurface(surf));
	return surf->m_nativeFormat.m_width;
}


// CLIENT VISIBLE
// Retrieve the height of the surface
DWORD gos_GetSurfaceHeight(HGOSSURFACE surf)
{
	gosASSERT(IsValidSurface(surf));
	return surf->m_nativeFormat.m_height;
}


// CLIENT VISIBLE
// Retrieve the pitch of the surface
DWORD gos_GetSurfacePitch(HGOSSURFACE surf)
{
	gosASSERT(IsValidSurface(surf));
	return surf->m_nativeFormat.m_pitch;
}


// CLIENT VISIBLE
// Set pixel at x,y of surface to color
void gos_SetPixel(HGOSSURFACE surf, DWORD x, DWORD y, DWORD color)
{
	gosASSERT(surf != NULL);
	surf->SetPixel(x,y,color);
}

// CLIENT VISIBLE
// Set pixel at x,y of surface to color
DWORD gos_GetPixel(HGOSSURFACE surf, DWORD x, DWORD y)
{
	gosASSERT(surf != NULL);
	return surf->GetPixel(x,y);
}




// CLIENT VISIBLE
// If set to true, color RGB 000 is not blitted with the surface
// data.
void gos_MakeChromaKeySurface(HGOSSURFACE surf, bool flag)
{
	gosASSERT(IsValidSurface(surf));
	surf->m_chromaKey = flag;
}


// CLIENT VISIBLE
// Draw a line
void gos_Line(HGOSSURFACE surf, DWORD x1, DWORD y1, DWORD x2, DWORD y2, DWORD c)
{
	gosASSERT(IsValidSurface(surf));
	if (y1 == y2)
	{
		if (x1 > x2)
		{
			for(DWORD i = x2; i <= x1; i++)
			{
				surf->SetPixelNoTranslate(i,y1,c);
			}
		}
		else
		{
			for(DWORD i = x1; i <= x2; i++)
			{
				surf->SetPixelNoTranslate(i,y1,c);
			}
		}
	}
	else
	if (x1 == x2)
	{
		if (y1 > y2)
		{
			for(DWORD i = y2; i <= y1; i++)
			{
				surf->SetPixelNoTranslate(x1,i,c);
			}
		}
		else
		{
			for(DWORD i = y1; i <= y2; i++)
			{
				surf->SetPixelNoTranslate(x1,i,c);
			}
		}

	}
	else
	{
		surf->BresenhamLine(x1,y1,x2,y2,c);
	}
}

// Constructor for a surface. A width, height, and format can be specified.
// As it is constructed, the surface's values are set to defaults.
Surface::Surface(DWORD width, DWORD height)
{
	AllSurfaces.Add(this);
	gosASSERT(width > 0 && height > 0);
	memset(&m_nativeFormat,0,sizeof(NATIVEFORMAT));
	m_nativeFormat.m_width = width;
	m_nativeFormat.m_height = height;
	m_nativeFormat.m_pitch = 4*width;
	m_nativeFormat.m_bitDepth = 32;
	m_lpPath = 0;
	m_chromaKey = FALSE;
	m_isGAF = false;
	m_isMemorySurface = false;
	isVolatile = true;

	m_nativeFormat.m_lpDynData = (unsigned char *) gos_Malloc(m_nativeFormat.m_pitch*m_nativeFormat.m_height,gScriptHeap[2]);
	memset(m_nativeFormat.m_lpDynData,0,m_nativeFormat.m_pitch*m_nativeFormat.m_height);
	m_nativeFormat.m_lpRawData = (unsigned char *) gos_Malloc(m_nativeFormat.m_pitch*m_nativeFormat.m_height,gScriptHeap[2]);
	memset(m_nativeFormat.m_lpRawData,0,m_nativeFormat.m_pitch*m_nativeFormat.m_height);
}	

// Constructor for a surface. A width, height, and format can be specified.
// As it is constructed, the surface's values are set to defaults.
Surface::Surface(unsigned char * lpDyn, DWORD width, DWORD height, DWORD pitch)
{
	AllSurfaces.Add(this);
	gosASSERT(width > 0 && height > 0);
	memset(&m_nativeFormat,0,sizeof(NATIVEFORMAT));
	m_nativeFormat.m_width = width;
	m_nativeFormat.m_height = height;
	m_nativeFormat.m_pitch = pitch;
	m_nativeFormat.m_bitDepth = 32;
	m_lpPath = 0;
	m_chromaKey = FALSE;
	m_isGAF = false;

	isVolatile = true;
	m_nativeFormat.m_lpDynData = lpDyn;
	m_isMemorySurface = true;
}	

// Constructor for a surface. Allows for the immediate load of a particular
// bitmap image in a variety of formats. Whatever the format, it internal
// data format is of the format specified in s_format.
Surface::Surface(const char * path, bool bIsVolatile = false)
{
	AllSurfaces.Add(this);
	gosASSERT(path != NULL);
	memset(&m_nativeFormat,0,sizeof(NATIVEFORMAT));
	m_chromaKey = FALSE;
	m_isGAF = false;
	m_isMemorySurface = false;
	m_lpPath = (char *) gos_Malloc(strlen(path)+1,gScriptHeap[2]);
	strcpy(m_lpPath,path);
	isVolatile = bIsVolatile;

	Load();
}

// Deconstructor. Removes the surface from the surface list and releases
// associated memory.
Surface::~Surface()
{
	AllSurfaces.Del(this);
	if (m_nativeFormat.m_lpDynData && !m_isMemorySurface)
		gos_Free(m_nativeFormat.m_lpDynData);
	if (m_lpPath) 
		gos_Free(m_lpPath);
	if (m_nativeFormat.m_lpRawData)
		gos_Free(m_nativeFormat.m_lpRawData);
}


// Code for loading a JPG
void Surface::LoadJPGFile(const char * FileName)
{
	gosASSERT( FileName != NULL );

	DWORD Size;
	BYTE * Bitmap;

	gos_PushCurrentHeap(gScriptHeap[2]);
	gos_GetFile( FileName, &Bitmap, &Size );
//
// Use GameOS to read textures
//
	m_nativeFormat.m_lpRawData = (BYTE*)DecodeJPG( FileName, Bitmap, Size, (DWORD*)&m_nativeFormat.m_width, (DWORD*)&m_nativeFormat.m_height, 0 );
//
// Update variables about bitmap
//
	m_nativeFormat.m_pitch = m_nativeFormat.m_width * 4;
	m_nativeFormat.m_bitDepth = 32;
    if (isVolatile)
	{
		//
		// Create another copy of the texture
		//
		m_nativeFormat.m_lpDynData = (unsigned char *) gos_Malloc(m_nativeFormat.m_pitch * m_nativeFormat.m_height);
		memcpy(m_nativeFormat.m_lpDynData, m_nativeFormat.m_lpRawData ,m_nativeFormat.m_pitch * m_nativeFormat.m_height);
	}
	else
	{
		m_nativeFormat.m_lpDynData = m_nativeFormat.m_lpRawData;
		m_nativeFormat.m_lpRawData = NULL;
	}
		
	gos_PopCurrentHeap();
//
// Remove the original file
//
	gos_Free(Bitmap);
}

#ifdef USEPNG
// Code for loading a PNG
void Surface::LoadPNGFile(const char * FileName)
{
	gosASSERT( FileName != NULL );

	DWORD Size;
	BYTE * Bitmap;

	gos_PushCurrentHeap(gScriptHeap[2]);
	gos_GetFile( FileName, &Bitmap, &Size );
//
// Use GameOS to read textures
//
	m_nativeFormat.m_lpRawData = (BYTE*)DecodePNG( FileName, Bitmap, Size, (DWORD*)&m_nativeFormat.m_width, (DWORD*)&m_nativeFormat.m_height, 0 );
//
// Update variables about bitmap
//
	m_nativeFormat.m_pitch = m_nativeFormat.m_width * 4;
	m_nativeFormat.m_bitDepth = 32;
    if (isVolatile)
	{
		//
		// Create another copy of the texture
		//
		m_nativeFormat.m_lpDynData = (unsigned char *) gos_Malloc(m_nativeFormat.m_pitch * m_nativeFormat.m_height);
		memcpy(m_nativeFormat.m_lpDynData, m_nativeFormat.m_lpRawData ,m_nativeFormat.m_pitch * m_nativeFormat.m_height);
	}
	else
	{
		m_nativeFormat.m_lpDynData = m_nativeFormat.m_lpRawData;
		m_nativeFormat.m_lpRawData = NULL;
	}
	gos_PopCurrentHeap();
//
// Remove the original file
//
	gos_Free(Bitmap);
}
#endif

// Code for loading a TGA
void Surface::LoadTGAFile(const char * FileName)
{
	gosASSERT( FileName != NULL );

	DWORD Size;
	BYTE * Bitmap;

	gos_PushCurrentHeap(gScriptHeap[2]);
	gos_GetFile( FileName, &Bitmap, &Size );
//
// Use GameOS to read textures
//
	m_nativeFormat.m_lpRawData = (BYTE*)DecodeTGA( FileName, Bitmap, Size, (DWORD*)&m_nativeFormat.m_width, (DWORD*)&m_nativeFormat.m_height, 0 );
//
// Update variables about bitmap
//
	m_nativeFormat.m_pitch = m_nativeFormat.m_width * 4;
	m_nativeFormat.m_bitDepth = 32;
    if (isVolatile)
	{
		//
		// Create another copy of the texture
		//
		m_nativeFormat.m_lpDynData = (unsigned char *) gos_Malloc(m_nativeFormat.m_pitch * m_nativeFormat.m_height);
		memcpy(m_nativeFormat.m_lpDynData, m_nativeFormat.m_lpRawData ,m_nativeFormat.m_pitch * m_nativeFormat.m_height);
	}
	else
	{
		m_nativeFormat.m_lpDynData = m_nativeFormat.m_lpRawData;
		m_nativeFormat.m_lpRawData = NULL;
	}
	gos_PopCurrentHeap();
//
// Remove the original file
//
	gos_Free(Bitmap);
}

// Code for loading a BMP
void Surface::LoadBMPFile(const char * FileName)
{
	gosASSERT( FileName != NULL );

	DWORD Size;
	BYTE * Bitmap;

	gos_PushCurrentHeap(gScriptHeap[2]);
	gos_GetFile( FileName, &Bitmap, &Size );
//
// Use GameOS to read textures
//
	m_nativeFormat.m_lpRawData = (BYTE*)DecodeBMP( FileName, Bitmap, Size, (DWORD*)&m_nativeFormat.m_width, (DWORD*)&m_nativeFormat.m_height, 0 );
//
// Update variables about bitmap
//
	m_nativeFormat.m_pitch = m_nativeFormat.m_width * 4;
	m_nativeFormat.m_bitDepth = 32;
    if (isVolatile)
	{
		//
		// Create another copy of the texture
		//
		m_nativeFormat.m_lpDynData = (unsigned char *) gos_Malloc(m_nativeFormat.m_pitch * m_nativeFormat.m_height);
		memcpy(m_nativeFormat.m_lpDynData, m_nativeFormat.m_lpRawData ,m_nativeFormat.m_pitch * m_nativeFormat.m_height);
	}
	else
	{
		m_nativeFormat.m_lpDynData = m_nativeFormat.m_lpRawData;
		m_nativeFormat.m_lpRawData = NULL;
	}
	gos_PopCurrentHeap();
//
// Remove the original file
//
	gos_Free(Bitmap);
}


/////////////////////////////////////////////////////////////
// Copy the original "on disk" picture into the dynamic area
// YES, IT'S GOOD TO HAVE THE ORIGINAL AROUND, ASK AN ARTIST
// 
void Surface::CopyBitmap()
{
    //
    //  select bitmap into a memoryDC so we can use it.
    //

	unsigned char * lpC;
	unsigned char * buffer = (unsigned char *)m_nativeFormat.m_lpRawData;
	DWORD countup = 0;
	
	switch (m_nativeFormat.m_bitDepth)
	{
		case 16:
		{
			for(int i=0; i < m_nativeFormat.m_height; i++)
			{
				for(int j=0; j < m_nativeFormat.m_width; j ++)
				{
					lpC = ((unsigned char*)m_nativeFormat.m_lpDynData) + (i*m_nativeFormat.m_pitch)
						+j*4;
					WORD color = ((WORD *) buffer)[countup*(m_nativeFormat.m_pitch)+j];
					DWORD red = (((color & 0x7c00) >> 10) << 3);
					DWORD grn = (((color & 0x03e0) >> 5) << 3);
					DWORD blu = (color & 0x001f) << 3;

					lpC[0] = (unsigned char) blu;
					lpC[1] = (unsigned char) grn;
					lpC[2] = (unsigned char) red;
					lpC[3] = 0xff;
				}
				countup++;
			}
			break;
		}
		case 24:
		{
			for(int i=0; i < m_nativeFormat.m_height; i++)
			{
				for(int j=0; j < m_nativeFormat.m_width; j++)
				{
					unsigned char * col = buffer+(countup*m_nativeFormat.m_pitch)+(j*3);
					lpC = ((unsigned char*)m_nativeFormat.m_lpDynData) + (i*m_nativeFormat.m_pitch)
						+(4*j);
					lpC[0] = col[0];
					lpC[1] = col[1];
					lpC[2] = col[2];
					lpC[3] = 0xff;
				}
				countup++;
			}
			break;
		}
		case 32:
		{
			for(int i=0; i < m_nativeFormat.m_height; i++)
			{
				lpC = ((unsigned char*)m_nativeFormat.m_lpDynData) + m_nativeFormat.m_pitch * i;
				memcpy(lpC,buffer+(countup*m_nativeFormat.m_pitch),m_nativeFormat.m_pitch);
				countup++;
			}
			break;
		}
		default:
		{
			ScriptError("Unsupported bit depth for file '%s'",m_lpPath);
			break;
		}
	}
}

// Get the surface data pointer with error checking (make sure it's locked).
void* Surface::GetDataPointer()
{
	return m_nativeFormat.m_lpDynData;
}


// Load the surface's image if it has one.
void Surface::Load()
{
	if (stricmp(".tga",m_lpPath + strlen(m_lpPath)-4) == 0)
	{
		LoadTGAFile(m_lpPath);
	}
	else
	if (stricmp(".bmp",m_lpPath + strlen(m_lpPath)-4) == 0)
	{
		LoadBMPFile(m_lpPath);
	}
	else
	if (stricmp(".jpg",m_lpPath + strlen(m_lpPath)-4) == 0)
	{
		LoadJPGFile(m_lpPath);
	}
	#ifdef USEPNG
	else
	if (stricmp(".png",m_lpPath + strlen(m_lpPath)-4) == 0)
	{
		LoadPNGFile(m_lpPath);
	}
	#endif
	else
	{
		ScriptError( "Unknown file format '%s'",m_lpPath );
	}
}

// This function is called by the surface manager (2DRasterizer.cpp)
// when it find a surface that has been lost. Surfaces are restored
// and loaded with the images they originally possessed.
void Surface::Restore()
{
	gosASSERT( isVolatile);
	if ( this->m_isGAF || m_lpPath != NULL )
		CopyBitmap();
}


// This function performs the work of the similar gos_ call.
void Surface::Clear(DWORD color)
{
	gosASSERT( isVolatile);
	DWORD * lp = (DWORD *) m_nativeFormat.m_lpDynData;
	DWORD SurfaceSize=m_nativeFormat.m_height * m_nativeFormat.m_width;
	_asm{
		push edi
		push ecx
		mov edi,lp
		mov ecx,SurfaceSize
		mov eax,color
		rep stosd
		pop ecx
		pop edi
	}
}


// The bulk of the matter. Blits any surface to any surface.
// Needs a lot of optimizing. See gos_BlitSurface above for
// more information.
void Surface::Blit(CUBE * rect_in, int x, int y, HGOSSURFACE dst)
{
	gosASSERT( dst->isVolatile);
	CUBE 
		rect;

	if (rect_in == NULL)
	{									   
		rect.p1.y = 0;
		rect.p1.x = 0;
		rect.p2.y = m_nativeFormat.m_height;
		rect.p2.x = m_nativeFormat.m_width;

	}
	else
	{
		memcpy(&rect,rect_in,sizeof(CUBE));
	}

	if (rect.p2.x + x < 0) return;
	
	if (x >= (signed) dst->m_nativeFormat.m_width-1 && m_nativeFormat.m_width-1 + x> dst->m_nativeFormat.m_width-1) return;
	
	if (rect.p2.y +y  < 0) return;

	if (y >= (signed) dst->m_nativeFormat.m_height-1 && m_nativeFormat.m_height-1 + y> dst->m_nativeFormat.m_height-1) return;
	
	if (x < 0 && x + (rect.p2.x-rect.p1.x) >= 0)
	{
		rect.p1.x -= x;
		x = 0;
	}

	if (x == 0 && y == 0)
	{
		if (rect.p1.x < 0)
		{
			x -= rect.p1.x;
			rect.p1.x = 0 ;
		}
		if (rect.p2.x > m_nativeFormat.m_width)//
		{
			rect.p2.x = m_nativeFormat.m_width;//
		}
		if (rect.p2.x-rect.p1.x > dst->m_nativeFormat.m_width) //
		{
			rect.p2.x = dst->m_nativeFormat.m_width;	 //
		}

		if (rect.p2.y-rect.p1.y > dst->m_nativeFormat.m_height)	 //
		{
			rect.p2.y = dst->m_nativeFormat.m_height;	//
		}

		if (rect.p1.y < 0)
		{
			y -= rect.p1.y;
			rect.p1.y = 0 ;
		}
		if (rect.p2.y > m_nativeFormat.m_height)//
		{
			rect.p2.y = m_nativeFormat.m_height;//
		}
		if (rect.p2.y <= rect.p1.y) return;
	}
	else
	{
		if (x + (rect.p2.x - rect.p1.x)-1 > dst->m_nativeFormat.m_width-1)
		{
			rect.p2.x -= (x + m_nativeFormat.m_width-1 -  (dst->m_nativeFormat.m_width-1));
			if (rect.p2.x <= rect.p1.x) return;
		}
		if (y < 0 && y + m_nativeFormat.m_height-1 >= 0)
		{
			rect.p1.y -= y;
			y = 0;
		}

		if (y < dst->m_nativeFormat.m_height-1 && (y + (rect.p2.y-rect.p1.y)) > dst->m_nativeFormat.m_height-1)
		{
			rect.p2.y -= (y + (rect.p2.y-rect.p1.y) -  (dst->m_nativeFormat.m_height));
			if (rect.p2.y <= rect.p1.y) return;
		}
	}

	unsigned char * lpSrc = m_nativeFormat.m_lpDynData;
	unsigned char * lpDst = dst->m_nativeFormat.m_lpDynData;

	if (m_chromaKey == TRUE)
	{
		for(int i=rect.p1.y; i < rect.p2.y; i++)
		{
			for(int j=rect.p1.x; j < rect.p2.x; j ++)
			{
				if (lpSrc[i*m_nativeFormat.m_pitch+j*4] != 0x00)
				{
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)];
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)+1] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)+1];
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)+2] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)+2];
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)+3] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)+3];
				}
			}
		}
	}
	else
	{
		unsigned char * endD = lpDst + (dst->m_nativeFormat.m_pitch * dst->m_nativeFormat.m_height) ;
		unsigned char * endS = lpSrc + (m_nativeFormat.m_pitch * m_nativeFormat.m_height) ;

		lpSrc += (m_nativeFormat.m_pitch*rect.p1.y) + (rect.p1.x*4);
		lpDst += (dst->m_nativeFormat.m_pitch*y) + (x*4);

		for(int i=rect.p1.y; i < rect.p2.y; i++)
		{
			if (endD > lpDst && endS > lpSrc )
				memcpy(lpDst, lpSrc,(rect.p2.x - rect.p1.x)*4);
			lpDst += dst->m_nativeFormat.m_pitch;
			lpSrc += m_nativeFormat.m_pitch;
		}
	}
}

// Return the pixel at x, y of surface in an 8888 RGBA format.
DWORD Surface::GetPixel(DWORD x, DWORD y)
{
	unsigned char * lp = (unsigned char *) GetDataPointer();
	DWORD 
		alp = lp[y*m_nativeFormat.m_pitch+x*4+3],
		red = lp[y*m_nativeFormat.m_pitch+x*4+2],
		grn = lp[y*m_nativeFormat.m_pitch+x*4+1],
		blu = lp[y*m_nativeFormat.m_pitch+x*4];
		return (alp << 24) + (red << 16) + (grn << 8) + blu;
}

// Set the pixel x, y of surface to 888RGB color or best match.
void Surface::SetPixel(DWORD x, DWORD y, DWORD color)
{
	gosASSERT( isVolatile && (int) x >=0 && (int) y >=0 && (int) x <m_nativeFormat.m_width && (int) y < m_nativeFormat.m_height);
	unsigned char * lp = (unsigned char *) m_nativeFormat.m_lpDynData;
	lp[y*m_nativeFormat.m_pitch+x*4+3] = (unsigned char) ((color & 0xff000000) >> 24),
	lp[y*m_nativeFormat.m_pitch+x*4+2] = (unsigned char) ((color & 0x00ff0000) >> 16),
	lp[y*m_nativeFormat.m_pitch+x*4+1] = (unsigned char) ((color & 0x0000ff00) >> 8),
	lp[y*m_nativeFormat.m_pitch+x*4] = (unsigned char) (color & 0x000000ff);
}

// Set the pixel x, y of surface to 888RGB color or best match.
void Surface::SetPixel(DWORD x, DWORD y, unsigned char alp, unsigned char red, unsigned char grn, unsigned char blu)
{
	gosASSERT( isVolatile && (int)x >=0 && (int)y >=0 && (int)x <m_nativeFormat.m_width && (int)y < m_nativeFormat.m_height);
	unsigned char * lp = (unsigned char *) m_nativeFormat.m_lpDynData;
	lp[y*m_nativeFormat.m_pitch+x*4+3] = alp;
	lp[y*m_nativeFormat.m_pitch+x*4+2] = red;
	lp[y*m_nativeFormat.m_pitch+x*4+1] = grn;
	lp[y*m_nativeFormat.m_pitch+x*4] = blu;
}

void Surface::BresenhamLine(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, DWORD color)
{
	gosASSERT(isVolatile && x1 >=0 && y1 >= 0 && x2 >= 0 && y2 >= 0);
	int dx = 1, dy;
	int step = 1;
	if (y1 > y2)
	{
		unsigned int temp = y1;
		y1 = y2;
		y2 = temp;
		temp = x1;
		x1 = x2;
		x2 = temp;
	}

	dx = x2 - x1;
	dy = y2 - y1;

	if (dx <= 0)
	{
		dx = -dx;
		step = -1;
	}
	
	if (dx > dy)
	{
		BHApproxNextY(x1,y1,dx,dy,step,color);
	}
	else
	{
		BHApproxNextX(x1,y1,dx,dy,step,color);
	}
}

void Surface::BHApproxNextY(unsigned int xp, unsigned int yp, unsigned int dx, unsigned int dy, int dirVector, DWORD color)
{
	int k1, k2, errorTerm;
	k1 = dy << 2;
	k2 = k1 - (int) (dx << 2);
	errorTerm = k1 - (int) dx;
	SetPixelNoTranslate(xp, yp, color);
	while(dx--)
	{
		if (errorTerm >= 0)
		{
			yp++;
			errorTerm += k2;
		}
		else
			errorTerm += k1;
		xp += dirVector;
		SetPixelNoTranslate(xp, yp, color);
	}
}

void Surface::BHApproxNextX(unsigned int xp, unsigned int yp, unsigned int dx, unsigned int dy, int dirVector, DWORD color)
{
	int k1, k2, errorTerm;
	k1 = dx << 2;
	k2 = k1 - (int) (dy << 2);
	errorTerm = k1 - (int) dy;
	SetPixelNoTranslate(xp, yp, color);
	while(dy--)
	{
		if (errorTerm >= 0)
		{
			xp += dirVector;
			errorTerm += k2;
		}
		else
			errorTerm += k1;
		yp++;
		SetPixelNoTranslate(xp, yp, color);
	}
}


void Surface::SetPixelNoTranslate(DWORD x, DWORD y, DWORD color)
{
	gosASSERT( isVolatile && (int)x >=0 && (int)y >=0 && (int)x <m_nativeFormat.m_width && (int)y < m_nativeFormat.m_height);

	unsigned char * lp = (unsigned char *) m_nativeFormat.m_lpDynData + y*m_nativeFormat.m_pitch;
	((DWORD *) lp)[x] = color;
}

void Surface::StretchBlit(CUBE * rect_in, float sx, float sy, int x, int y, Surface * dst)
{
	// CHANGE!
	sx = sx; sy = sy;
	gosASSERT( isVolatile );
	CUBE 
		rect;

	if (rect_in == NULL)
	{									   
		rect.p1.y = 0;
		rect.p1.x = 0;
		rect.p2.y = m_nativeFormat.m_height;
		rect.p2.x = m_nativeFormat.m_width;

	}
	else
	{
		memcpy(&rect,rect_in,sizeof(CUBE));
	}

	if (rect.p2.x + x < 0) return;
	
	if (x >= (signed) dst->m_nativeFormat.m_width-1 && m_nativeFormat.m_width-1 + x> dst->m_nativeFormat.m_width-1) return;
	
	if (rect.p2.y +y  < 0) return;

	if (y >= (signed) dst->m_nativeFormat.m_height-1 && m_nativeFormat.m_height-1 + y> dst->m_nativeFormat.m_height-1) return;
	
	if (x < 0 && x + (rect.p2.x-rect.p1.x) >= 0)
	{
		rect.p1.x -= x;
		x = 0;
	}

	if (x == 0 && y == 0)
	{
		if (rect.p1.x < 0)
		{
			x -= rect.p1.x;
			rect.p1.x = 0 ;
		}
		if (rect.p2.x > m_nativeFormat.m_width)//
		{
			rect.p2.x = m_nativeFormat.m_width;//
		}
		if (rect.p2.x > dst->m_nativeFormat.m_width) //
		{
			rect.p2.x = dst->m_nativeFormat.m_width;	 //
		}

		if (rect.p2.y > dst->m_nativeFormat.m_height)	 //
		{
			rect.p2.y = dst->m_nativeFormat.m_height;	//
		}

		if (rect.p1.y < 0)
		{
			y -= rect.p1.y;
			rect.p1.y = 0 ;
		}
		if (rect.p2.y > m_nativeFormat.m_height)//
		{
			rect.p2.y = m_nativeFormat.m_height;//
		}
		if (rect.p2.y <= rect.p1.y) return;
	}
	else
	{
		if (x + (rect.p2.x - rect.p1.x)-1 > dst->m_nativeFormat.m_width-1)
		{
			rect.p2.x -= (x + m_nativeFormat.m_width-1 -  (dst->m_nativeFormat.m_width-1));
			if (rect.p2.x <= rect.p1.x) return;
		}
		if (y < 0 && y + m_nativeFormat.m_height-1 >= 0)
		{
			rect.p1.y -= y;
			y = 0;
		}

		if (y < (signed) dst->m_nativeFormat.m_height-1 &&  (y + (rect.p2.y-rect.p1.y)) > dst->m_nativeFormat.m_height-1)
		{
			rect.p2.y -= (y + (rect.p2.y-rect.p1.y) -  (dst->m_nativeFormat.m_height));
			if (rect.p2.y <= rect.p1.y) return;
		}
	}

	unsigned char * lpSrc = m_nativeFormat.m_lpDynData;
	unsigned char * lpDst = dst->m_nativeFormat.m_lpDynData;

	if (m_chromaKey == TRUE)
	{
		for(int i=rect.p1.y; i < rect.p2.y; i++)
		{
			for(int j=rect.p1.x; j < rect.p2.x; j ++)
			{
				if (lpSrc[i*m_nativeFormat.m_pitch+j*4] != 0x00)
				{
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)];
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)+1] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)+1];
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)+2] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)+2];
					lpDst[i*dst->m_nativeFormat.m_pitch+(j*4)+3] = lpSrc[i*m_nativeFormat.m_pitch+(j*4)+3];
				}
			}
		}
	}
	else
	{
		lpSrc += (m_nativeFormat.m_pitch*rect.p1.y) + (rect.p1.x*4);
		lpDst += (dst->m_nativeFormat.m_pitch*y) + (x*4);

		for(int i=rect.p1.y; i < rect.p2.y; i++)
		{
			memcpy(lpDst, lpSrc,(rect.p2.x - rect.p1.x)*4);
			lpDst += dst->m_nativeFormat.m_pitch;
			lpSrc += m_nativeFormat.m_pitch;
		}
	}
}
