/* -*- tab-width: 4 -*- */
/******************************************************************************      
 *
 *	Copyright 2004 The Orion Compiler Group. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE ORION COMPILER GROUP ``AS IS'' AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE ORION COMPILER GROUP OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are 
 * those of the authors and should not be interpreted as representing official 
 * policies, either expressed or implied, of the Orion Compiler Group.
 *
 ******************************************************************************/

/**************************************************************************
	Graphics support code
 **************************************************************************/

// For the curious, tabs are set to 1, 5, 9, etc... Do not modify and resubmit
// with spaces. It makes the code harder to merge in changes.

#include <math.h>
#include "types.h"
#include "memory.h"
#include "gfx.h"
#include "osdep.h"

#define DIRTY_TILE_SIZE	16			// 16x16 pixel dirty buffer
#define DIRTY_TILE_XSHIFT	4
#define DIRTY_TILE_YSHIFT	4

typedef struct
{
	UINT8 u8Red;
	UINT8 u8Green;
	UINT8 u8Blue;
} SPaletteEntry;

// Palette related entries
#if ((defined(USE_SDL_GRAPHICS)) || (defined(USE_SDL2_GRAPHICS)))
static SPaletteEntry *sg_psPalette;
static UINT8 *sg_pu8DirtyBufferArray;
static UINT32 sg_u32PaletteEntryCount = 0;
static UINT32 sg_u32BackBufferXSize;
static UINT32 sg_u32BackBufferYSize;
static UINT32 sg_u32XOverscan;
static UINT32 sg_u32YOverscan;
static UINT8 sg_u8BackBufferColorDepth;
static UINT8 *sg_pu8BackBufferStart;
static UINT8 sg_u8YShift;
static BOOL sg_bChanged = FALSE;

static void AllocImageSingleOrientation(struct SRasterDecode *psRD, 
									    struct SImage **psImage, 
									    UINT32 u32Images)
{
    UINT32 u32ImageSize;
	UINT8 u8Transparent = (psRD->u32Flags & DGF_TRANSPARENT) != 0 ? 1 : 0;
	UINT8 u8Under = (psRD->u32Flags & DGF_UNDER) != 0 ? 1 : 0;

	ASSERT(psRD->u8Mirror >= 1 && psRD->u8Mirror <= 4);

	u32Images *= psRD->u16Width * psRD->u16Height;

	u32ImageSize = sizeof(struct SImage) + u32Images * (1 + u8Under + u8Transparent);

	*psImage = (struct SImage *)MyMalloc(u32ImageSize);

	(*psImage)->u16Images = psRD->u16Image * psRD->u8Mirror;
	(*psImage)->u16Width = psRD->u16Width;
	(*psImage)->u16Height = psRD->u16Height;
	(*psImage)->u32Flags = psRD->u32Flags;
	(*psImage)->pu8Data = (UINT8 *)((*psImage) + 1);
	(*psImage)->pu8Mask = u8Transparent ? (*psImage)->pu8Data + u32Images : NULL;
	(*psImage)->pu8Under = u8Under ? (*psImage)->pu8Mask + u32Images : NULL;
	(*psImage)->u8BPP = psRD->u8BPP;
	(*psImage)->pu32TransFlags = MyMalloc((*psImage)->u16Images * sizeof(UINT32));
}

static UINT8 DecodePixel(struct SRasterDecode *psRD, 
						 UINT8 *pu8Source,
						 UINT16 u16Image, 
						 UINT16 i, 
						 UINT16 j, 
						 struct SImage *psImage)
{
	UINT8 u8Dir;
	UINT8 u8Plane;
	UINT16 u16ImageSize = psRD->u16Width * psRD->u16Height;
	UINT8 u8Transparent = (psRD->u32Flags & DGF_TRANSPARENT) != 0;
	UINT8 u8Under = (psRD->u32Flags & DGF_UNDER) != 0;
	UINT8 u8Byte = 0;
	UINT8 u8Trans = FALSE;
	UINT16	u16SX[4];
	UINT16	u16SY[4];

	// Decode all of the bitplanes for the current pixel.
	for(u8Plane = 0; u8Plane < psRD->u8Plane; u8Plane++)
	{
		UINT32 u32Offset = u16Image * psRD->u16ImagePitch + psRD->u32PlanePitch[u8Plane];
		UINT32 u32BitNum = u32Offset + psRD->u32YOffset[i] + psRD->u32XOffset[j];
		UINT8 u8Bits = pu8Source[u32BitNum >> 3] << (u32BitNum & 0x07);
		
		u8Byte = (u8Byte << 1);

		if(u8Bits & 0x80)
		{
			u8Byte |= 1;
		}
	}

	u16SX[0] = u16SX[1] = j;
	u16SY[0] = u16SY[2] = i;
	u16SY[1] = u16SY[3] = (psRD->u16Height - 1) - i;
	u16SX[2] = u16SX[3] = (psRD->u16Width - 1) - j;

	// Set up the mirroring if necessary.
	for(u8Dir = 0; u8Dir < psRD->u8Mirror; u8Dir++)
	{
		UINT32	u32Mask, u32Data, u32Under;

		u32Data = ((psRD->u8ImageInc * u16Image + psRD->u32Mirror[u8Dir]) * u16ImageSize) +
				psRD->u16Width * u16SY[u8Dir] + u16SX[u8Dir];
		u32Mask = u32Data;
		u32Under = u32Data;

		psImage->pu8Data[u32Data] = u8Byte << psRD->u8BitShift;

		// if we're doing tileprio (under)
		if(0 != u8Under)
		{
			// and if the tile-priority color array is defined..
			if (NULL != psRD->pu8Under) {
				// mark the pixel as tile-priority
				u8Trans = psRD->pu8Under[u8Byte];
				// if the color is a tile-priority color (nonzero),
				// set bits in mask so that tile will show through.
				psImage->pu8Under[u32Under] = u8Trans ? 0xFF : 0x00;
			}
		}

		if(u8Transparent)
		{
			if(psRD->pu8Transparent)
			{
				u8Trans = psRD->pu8Transparent[u8Byte];
			}

			if (u8Trans)
			{
				u8Trans = FALSE;
				psImage->pu8Mask[u32Mask] = 0x00;
			}
			else
			{
				u8Trans = TRUE;
				psImage->pu8Mask[u32Mask] = 0xFF;
			}
		}
		else
		{
			u8Trans = FALSE;
		}
	}

	return(u8Trans);
}
#endif

void GfxDecode(struct SRasterDecode *psRD, 
			   UINT8 *pu8Source,
			   struct SImage **psImage)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	UINT16 u16Image, u32Y, u32X;
	UINT32 u32Images = psRD->u16Image * psRD->u8Mirror;
	UINT32 u32TransparentCount = 0;

	ASSERT(NULL != pu8Source);
	ASSERT(psRD->u8ImageInc > 0);

	// go alloc a chunk of memory.
	AllocImageSingleOrientation(psRD, psImage, u32Images);

	for(u16Image = 0; u16Image < psRD->u16Image; u16Image++)
	{
		u32TransparentCount = 0;

		for (u32Y = 0; u32Y < psRD->u16Height; u32Y++)
		{
			for (u32X = 0; u32X < psRD->u16Width; u32X++)
			{
				if (DecodePixel(psRD, pu8Source, u16Image, u32Y, u32X, *psImage))
				{
					u32TransparentCount++;
				}
			}
		}

		if (0 == u32TransparentCount)
		{
			(*psImage)->pu32TransFlags[u16Image] |= RASTER_FULLY_SOLID;
		}

		if ((UINT32) (psRD->u16Height * psRD->u16Width) == u32TransparentCount)
		{
			(*psImage)->pu32TransFlags[u16Image] |= RASTER_FULLY_TRANSPARENT;
		}
	}
#endif
}

void GfxSetPaletteSize(UINT32 u32Entries)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	if (sg_psPalette)
	{
		MyFree((void **) &sg_psPalette);
	}

	sg_psPalette = MyMalloc(u32Entries * sizeof(*sg_psPalette));
	memset(sg_psPalette, 0, (u32Entries * sizeof(*sg_psPalette)));
	sg_u32PaletteEntryCount = u32Entries;
#endif
}

void GfxSetPaletteEntry(UINT32 u32EntryNumber,
						UINT8 u8Red,
						UINT8 u8Green,
						UINT8 u8Blue)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	BOOL bPaletteEntryChanged = FALSE;

	ASSERT(sg_psPalette);
	ASSERT(u32EntryNumber < sg_u32PaletteEntryCount);

	if (sg_psPalette[u32EntryNumber].u8Red != u8Red)
	{
		bPaletteEntryChanged = TRUE;
		sg_psPalette[u32EntryNumber].u8Red = u8Red;
	}

	if (sg_psPalette[u32EntryNumber].u8Green != u8Green)
	{
		bPaletteEntryChanged = TRUE;
		sg_psPalette[u32EntryNumber].u8Green = u8Green;
	}

	if (sg_psPalette[u32EntryNumber].u8Blue != u8Blue)
	{
		bPaletteEntryChanged = TRUE;
		sg_psPalette[u32EntryNumber].u8Blue = u8Blue;
	}

	if (bPaletteEntryChanged)
	{
		sg_bChanged = TRUE;
		HostSetPaletteEntry(u32EntryNumber,
							u8Red,
							u8Green,
							u8Blue);
	}
#endif
}

void GfxFreeImage(struct SImage **ppsImage)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	if (NULL != ppsImage && *ppsImage)
	{
		if ((*ppsImage)->pu32TransFlags)
		{
			MyFree((void **) &(*ppsImage)->pu32TransFlags);
		}

		MyFree((void **)ppsImage);
	}
#endif
}

#if ((defined(USE_SDL_GRAPHICS)) || (defined(USE_SDL2_GRAPHICS)))
static UINT32 sg_u32DirtyBufferSize;
#endif

void GfxSetBackBuffer(UINT32 u32XSize,
					  UINT32 u32YSize,
					  UINT32 u32XOverscan,
					  UINT32 u32YOverscan,
					  UINT8 u8ColorDepth)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	UINT32 u32Temp;

	sg_u32BackBufferXSize = u32XSize + (u32XOverscan << 1);
	sg_u32BackBufferYSize = u32YSize + (u32YOverscan << 1);
	sg_u32XOverscan = u32XOverscan;
	sg_u32YOverscan = u32YOverscan;

	ASSERT(8 == u8ColorDepth);	// Only 8bpp supported for now...
	sg_u8BackBufferColorDepth = u8ColorDepth;

	sg_pu8BackBufferStart = (UINT8 *) HostCreateBackBuffer(sg_u32BackBufferXSize,
														   sg_u32BackBufferYSize,
														   u8ColorDepth);
	ASSERT(sg_pu8BackBufferStart);

	if (8 == u8ColorDepth)
	{
		memset(sg_pu8BackBufferStart, 0, sg_u32BackBufferXSize * sg_u32BackBufferYSize);
	}

	if (sg_pu8DirtyBufferArray)
	{
		MyFree((void **) &sg_pu8DirtyBufferArray);
	}

	u32Temp = (sg_u32BackBufferXSize / DIRTY_TILE_SIZE);
	sg_u8YShift = 0;

	while (((UINT32) 1 << sg_u8YShift) < u32Temp)
	{
		sg_u8YShift++;
	}

	u32Temp = (sg_u32BackBufferYSize + (DIRTY_TILE_SIZE)) / DIRTY_TILE_SIZE;

	sg_u32DirtyBufferSize = u32Temp * (1 << sg_u8YShift);
	sg_pu8DirtyBufferArray = MyMalloc(sg_u32DirtyBufferSize);
	memset(sg_pu8DirtyBufferArray, 0, sg_u32DirtyBufferSize);
#endif
}

typedef struct Gfx_Rect { // Same as SDL_Rect
  int x, y;
  int w, h;
} Gfx_Rect;

void GfxBlit(void)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
    /*SDL_Rect*/Gfx_Rect sSource;
    /*SDL_Rect*/Gfx_Rect sDest;
	INT32 s32YRemaining = (INT32) sg_u32BackBufferYSize - (sg_u32YOverscan << 1);
	INT32 s32XRemaining;
	UINT32 u32DirtyStride;
	UINT32 u32Run = 0;
	UINT8 *pu8Dirty;

	// HACK HACK
//	memset(sg_pu8DirtyBufferArray, 0xff, sg_u32DirtyBufferSize);
	sg_bChanged = TRUE;

	if (FALSE == sg_bChanged)
	{
		HostProcessMessages();
		return;
	}


	pu8Dirty = sg_pu8DirtyBufferArray + (sg_u32XOverscan >> DIRTY_TILE_XSHIFT) +
			   ((sg_u32YOverscan >> DIRTY_TILE_YSHIFT) << sg_u8YShift);

	u32DirtyStride = ((1 << sg_u8YShift) - (sg_u32BackBufferXSize >> DIRTY_TILE_XSHIFT)) + ((sg_u32XOverscan << 1) >> DIRTY_TILE_XSHIFT);

	sSource.y = sg_u32YOverscan;
	sSource.h = DIRTY_TILE_SIZE;

	sDest.y = 0;
	sDest.h = DIRTY_TILE_SIZE;

	while (s32YRemaining)
	{
		s32XRemaining = (INT32) sg_u32BackBufferXSize - (sg_u32XOverscan << 1);
		sDest.x = 0;
		sSource.x = sg_u32XOverscan;
		
		while (s32XRemaining > 0)
		{
			if (*pu8Dirty)
			{
				sDest.w = 0;
				sSource.w = 0;
				u32Run = 0;

				while ((s32XRemaining > 0) && (*pu8Dirty))
				{
					*pu8Dirty = 0;
					sDest.w += (1 << DIRTY_TILE_XSHIFT);
					sSource.w += (1 << DIRTY_TILE_XSHIFT);
					s32XRemaining -= (1 << DIRTY_TILE_XSHIFT);
					++pu8Dirty;
					++u32Run;
				} 

				HostBlit(&sSource,
						 &sDest);

				sSource.x += (u32Run << DIRTY_TILE_XSHIFT);
				sDest.x += (u32Run << DIRTY_TILE_XSHIFT);
			}
			else
			{
				s32XRemaining -= (1 << DIRTY_TILE_XSHIFT);
				++pu8Dirty;
				sDest.x += (1 << DIRTY_TILE_XSHIFT);
				sSource.x += (1 << DIRTY_TILE_XSHIFT);
			}
		}

		s32YRemaining -= (1 << DIRTY_TILE_YSHIFT);
		sDest.y += (1 << DIRTY_TILE_YSHIFT);
		sSource.y += (1 << DIRTY_TILE_YSHIFT);
		pu8Dirty += u32DirtyStride;
	}

	sg_bChanged = FALSE;

	HostProcessFrame();
#endif
}

#if ((defined(USE_SDL_GRAPHICS)) || (defined(USE_SDL2_GRAPHICS)))
static UINT32 sg_u32ViewportXSize;
static UINT32 sg_u32ViewportYSize;
#endif

void GfxGetViewportSize(UINT32 *pu32XSize,
						UINT32 *pu32YSize)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	ASSERT(pu32XSize);
	ASSERT(pu32YSize);
	*pu32XSize = sg_u32ViewportXSize;
	*pu32YSize = sg_u32ViewportYSize;
#endif
}

void GfxSetDisplaySurface(UINT32 u32XSize,
						  UINT32 u32YSize,
						  UINT8 u8ColorDepth,
						  BOOL bWindowed)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	sg_u32ViewportXSize = u32XSize;
	sg_u32ViewportYSize = u32YSize;

	HostSetDisplaySurface(u32XSize,
						  u32YSize,
						  u8ColorDepth,
						  bWindowed);
#endif
}

void GfxDraw(struct SGfx *psGfx)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	UINT8 *pu8Dest;
	UINT8 *pu8Src;
	UINT8 *pu8Dirty;
	UINT8 *pu8Mask;
	UINT32 u32DirtyAdvance;
	UINT32 u32BackBufferPitch = HostGetBackBufferPitch();
	UINT32 u32BackBufferLineAdvance;
	UINT32 u32Color = 0;
	UINT32 u32ReferenceX;
	register UINT32 u32X;
	register UINT32 u32Y;

	// Figure out the place on the backbuffer to draw

	pu8Dest = sg_pu8BackBufferStart + psGfx->u32XPos +
			  (psGfx->u32YPos * u32BackBufferPitch);
	u32BackBufferLineAdvance = u32BackBufferPitch - psGfx->u32XSize;
	sg_bChanged = TRUE;

	// Figure out the source address from the graphics character table
	
	pu8Src = psGfx->psImage->pu8Data + (psGfx->u32Offset * psGfx->u32XSize * psGfx->u32YSize);

	// Now the color mask (if appropriate)

	u32Color = (psGfx->u32Color << psGfx->psImage->u8BPP);
	u32Color |= (u32Color << 8);
	u32Color |= (u32Color << 16);

	// Set up countdown sizes

	u32Y = psGfx->u32YSize;

	// Go draw the graphic

	if (psGfx->psImage->u32Flags & DGF_TRANSPARENT)
	{
		pu8Mask = psGfx->psImage->pu8Mask + (psGfx->u32Offset * psGfx->u32XSize * psGfx->u32YSize);

		// It's transparent and we have transparent pixels to draw

		switch(psGfx->u8Orientation)
		{
			case 0:			// No flipping
			{
				while (u32Y--)
				{
					u32X = psGfx->u32XSize;
					while (u32X--)
					{
						*pu8Dest = ((*pu8Src++ | (UINT8) u32Color) & (~*pu8Mask)) |
									 (*pu8Dest & *pu8Mask);
                        pu8Dest += 1;
						++pu8Mask;
					}

					pu8Dest += u32BackBufferLineAdvance;
				}
				break;
			}

			case TILE_HFLIP:
			{
				pu8Src += ((psGfx->u32XSize) - 1);
				pu8Mask += ((psGfx->u32XSize) - 1);

				while (u32Y--)
				{
					u32X = psGfx->u32XSize;
					while (u32X--)
					{
						*pu8Dest = ((*pu8Src-- | (UINT8) u32Color) & (~*pu8Mask)) |
									 (*pu8Dest & *pu8Mask);
                        pu8Dest += 1;
						pu8Mask--;
					}

					pu8Dest += u32BackBufferLineAdvance;
					pu8Src += (psGfx->u32XSize << 1);
					pu8Mask += (psGfx->u32XSize << 1);
				}

				break;
			}

			case TILE_VFLIP:
			{
				pu8Dest += (psGfx->u32XSize * psGfx->u32YSize) + psGfx->u32XSize;
				while (u32Y--)
				{
					pu8Dest -= (u32BackBufferLineAdvance + psGfx->u32XSize);
					u32X = psGfx->u32XSize;
					while (u32X--)
					{
						*pu8Dest = ((*pu8Src++ | (UINT8) u32Color) & (~*pu8Mask)) |
									 (*pu8Dest & *pu8Mask);
                        pu8Dest += 1;
						++pu8Mask;
					}
				}
				break;
			}

			case TILE_VFLIP | TILE_HFLIP:
			{
				pu8Dest += ((u32BackBufferLineAdvance * psGfx->u32YSize) + psGfx->u32XSize);
				u32BackBufferLineAdvance += (psGfx->u32XSize << 1);
				pu8Src += (psGfx->u32XSize - 1);
				pu8Mask += (psGfx->u32XSize - 1);

				while (u32Y--)
				{
					u32X = psGfx->u32XSize;
					pu8Dest -= u32BackBufferLineAdvance;

					while (u32X--)
					{
						*pu8Dest = ((*pu8Src-- | (UINT8) u32Color) & (~*pu8Mask)) |
									 (*pu8Dest & *pu8Mask);
                        pu8Dest += 1;
						pu8Mask--;
					}
					pu8Src += (psGfx->u32XSize << 1);
					pu8Mask += (psGfx->u32XSize << 1);
				} 

				break;
			}

			default:
			{
				ASSERT(0);		// Unknown orientation
			}
		}
	}
	else
	{
		// It's a solid (not transparent)
		switch(psGfx->u8Orientation)
		{
			case 0:			// No flipping
			{
				while (u32Y--)
				{
					u32X = psGfx->u32XSize;
					while (u32X--)
					{
						*pu8Dest++ = *pu8Src++ | (UINT8) u32Color;
					}

					pu8Dest += u32BackBufferLineAdvance;
				}
				break;
			}

			case TILE_HFLIP:
			{
				pu8Src += ((psGfx->u32XSize) - 1);

				while (u32Y--)
				{
					u32X = psGfx->u32XSize;
					while (u32X--)
					{
						*pu8Dest++ = *pu8Src-- | (UINT8) u32Color;
					}

					pu8Dest += u32BackBufferLineAdvance;
					pu8Src += (psGfx->u32XSize << 1);
				}

				break;
			}

			case TILE_VFLIP:
			{
/*				pu8Dest += (psGfx->u32XSize * psGfx->u32YSize) + psGfx->u32XSize;
				while (u32Y--)
				{
					pu8Dest -= (u32BackBufferLineAdvance << 1);
					u32X = psGfx->u32XSize;
					while (u32X--)
					{
						*pu8Dest++ = *pu8Src++ | (UINT8) u32Color;
					}
				} */
				break;
			}

			case TILE_VFLIP | TILE_HFLIP:
			{
				pu8Dest += ((u32BackBufferLineAdvance * psGfx->u32YSize) + psGfx->u32XSize);
				u32BackBufferLineAdvance += (psGfx->u32XSize << 1);
				pu8Src += (psGfx->u32XSize - 1);

				while (u32Y--)
				{
					u32X = psGfx->u32XSize;
					pu8Dest -= u32BackBufferLineAdvance;

					while (u32X--)
					{
						*pu8Dest++ = *pu8Src-- | (UINT8) u32Color;
					}
					pu8Src += (psGfx->u32XSize << 1);
				} 

				break;
			}

			default:
			{
				ASSERT(0);		// Unknown orientation
			}
		}
	}

	// Now dirty the buffer underneath

	pu8Dirty = sg_pu8DirtyBufferArray + 
				(psGfx->u32XPos >> DIRTY_TILE_XSHIFT) +
			   ((psGfx->u32YPos >> DIRTY_TILE_YSHIFT) << sg_u8YShift);

	u32ReferenceX = (psGfx->u32XSize + (DIRTY_TILE_SIZE - 1)) >> DIRTY_TILE_XSHIFT;
	u32Y = (psGfx->u32YSize + (DIRTY_TILE_SIZE - 1)) >> DIRTY_TILE_YSHIFT;

	if ((psGfx->u32XPos & ~(DIRTY_TILE_SIZE - 1)) != ((psGfx->u32XPos + psGfx->u32XSize - 1) & ~(DIRTY_TILE_SIZE - 1)))
	{
		u32ReferenceX++;
	}

	if ((psGfx->u32YPos & ~(DIRTY_TILE_SIZE - 1)) != ((psGfx->u32YPos + psGfx->u32YSize - 1) & ~(DIRTY_TILE_SIZE - 1)))
	{
		u32Y++;
	}

	ASSERT(u32ReferenceX);
	ASSERT(u32Y);

	u32DirtyAdvance = (1 << sg_u8YShift);

	while (u32Y--)
	{
		memset(pu8Dirty, 1, u32ReferenceX);
		pu8Dirty += u32DirtyAdvance;
	}
#endif
}

/************************************************************************
 *					
 * Name : ClipLine
 *					
 * Entry: fromx,y tox,y left/top/right/bottom
 *					
 * Exit : TRUE If line is properly clipped, FALSE if it is outside the area
 * 	    entirely.
 *					
 * Description:
 *					
 * This routine will clip a line to the dwLeft/dwTop,dwRight/dwBottom
 * coordinates. ALL COORDINATES ARE IN 16.16 FIXED POINT NOTATION!
 * 				
 ************************************************************************/

UINT8 ClipLine(INT32 *dwFromX, INT32 *dwFromY,
				   INT32 *dwToX, INT32 *dwToY,
					INT32 dwLeft, INT32 dwTop,
					INT32 dwRight, INT32 dwBottom)
{								  
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return 0;
#else
	INT32 dwXSlope = 0;
	INT32 dwYSlope = 0;
	INT32 dwXDistance = 0;
	INT32 dwYDistance = 0;

	// Quick reject - are we within our X/Y coordinates & range?
	if ( (*dwFromY >= dwTop) &&
		  (*dwFromY <= dwBottom) &&
	     (*dwToY   >= dwTop) &&
		  (*dwToY   <= dwBottom) &&
		  (*dwFromX >= dwLeft) &&
		  (*dwFromX <= dwRight) &&
	     (*dwToX   >= dwLeft) &&
		  (*dwToX   <= dwRight))
		return(TRUE);		// It's within the range and clipped OK!

	if (*dwFromX < dwLeft && *dwToX < dwLeft)
		return(FALSE);
	if (*dwFromX > dwRight && *dwToX > dwRight)
		return(FALSE);

	// Quick reject - are we outside either Y coordinates entirely?

	if (*dwFromY < dwTop && *dwToY < dwTop)
		return(FALSE);

	if (*dwFromY > dwBottom && *dwToY > dwBottom)
		return(FALSE);

	// Here we really need to clip it!

	dwXDistance = (*dwToX - *dwFromX);
	dwYDistance = (*dwToY - *dwFromY);

	if (dwYDistance >> 16)
		dwXSlope = (dwXDistance / (dwYDistance >> 16));
	if (dwXDistance >> 16)
		dwYSlope = (dwYDistance / (dwXDistance >> 16));

	// Left side

	if (*dwFromX < dwLeft)
	{
		*dwFromY += (dwYSlope * ((dwLeft - *dwFromX) >> 16));
		*dwFromX = dwLeft;
	}

	if (*dwToX < dwLeft)
	{
		*dwToY += (dwYSlope * ((dwLeft - *dwToX) >> 16));
		*dwToX = dwLeft;
	}

	// Top side

	if (*dwFromY < dwTop)
	{
		*dwFromX += (dwXSlope * ((dwTop - *dwFromY) >> 16));
		*dwFromY = dwTop;
	}

	if (*dwToY < dwTop)
	{
		*dwToX += (dwXSlope * ((dwTop - *dwToY) >> 16));
		*dwToY = dwTop;
	}

	// Right side

	if (*dwFromX > dwRight)
	{
		*dwFromY += (dwYSlope * ((dwRight - *dwFromX) >> 16));
		*dwFromX = dwRight;
	}

	if (*dwToX > dwRight)
	{
		*dwToY += (dwYSlope * ((dwRight - *dwToX) >> 16));
		*dwToX = dwRight;
	}

	// Bottom side

	if (*dwFromY > dwBottom)
	{
		*dwFromX += (dwXSlope * ((dwBottom - *dwFromY) >> 16));
		*dwFromY = dwBottom;
	}

	if (*dwToY > dwBottom)
	{
		*dwToX += (dwXSlope * ((dwBottom - *dwToY) >> 16));
		*dwToY = dwBottom;
	}

	if (*dwFromX < dwLeft && *dwToX < dwLeft)
		return(FALSE);
	if (*dwFromX > dwRight && *dwToX > dwRight)
		return(FALSE);

	// Quick reject - are we outside either Y coordinates entirely?

	if (*dwFromY < dwTop && *dwToY < dwTop)
		return(FALSE);

	if (*dwFromY > dwBottom && *dwToY > dwBottom)
		return(FALSE);

	return(TRUE);
#endif
}

void DrawRasterLine(struct sVector *psVect, UINT32 dwColor)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	UINT32 dwPitch;
	INT32 s32XCounter, s32YCounter;
	INT32 s32XPos, s32YPos;
	INT32 s32XSrc, s32YSrc;
	INT32 s32XDest, s32YDest;
	INT32 s32DistX, s32DistY;
	INT32 s32XStep, s32YStep;
	INT32 s32Temp;
	UINT8 *pbBuffer, *pbDirty;
	INT32 s32SwapVar;

	s32XSrc = psVect->s32FromX;
	s32YSrc = psVect->s32FromY;
	s32XDest = psVect->s32ToX;
	s32YDest = psVect->s32ToY;

	s32DistX = abs(s32XDest - s32XSrc);
	s32DistY = abs(s32YDest - s32YSrc);

	pbBuffer = sg_pu8BackBufferStart;
	dwPitch = sg_u32BackBufferXSize;

	// If s32DistY == 0, then we just need to draw a horizontal line


	if (0 == s32DistY)
	{
		if (s32XDest < s32XSrc)
		{
			s32XPos = s32XDest;
		}
		else
		{
			s32XPos = s32XSrc;
		}

		pbBuffer += ((s32YSrc * dwPitch) + s32XPos);

		// Draw the horizontal line

		s32DistX++;			// Add 1 since we're counting end and start points
		memset(pbBuffer, dwColor, s32DistX);

		// Now time to dirty the buffer!

		s32DistX += (s32XPos & 0x0f) + (1 << DIRTY_TILE_XSHIFT);
		s32DistX >>= DIRTY_TILE_XSHIFT;
		pbDirty = &sg_pu8DirtyBufferArray[((s32YSrc >> DIRTY_TILE_YSHIFT) << sg_u8YShift)] + (s32XPos >> DIRTY_TILE_XSHIFT);
		memset(pbDirty, 1, s32DistX);
		return;		
	}

	// If s32DistX == 0, then we just need to draw a vertical

	if (0 == s32DistX)
	{
		if (s32YDest < s32YSrc)
		{
			s32YPos = s32YDest;
		}
		else
		{
			s32YPos = s32YSrc;
		}

		s32YDest++;	// Since we're counting the start and end pixels
		pbBuffer += ((s32YPos * dwPitch) + s32XSrc);

		// Draw the vertical line

		pbDirty = &sg_pu8DirtyBufferArray[(s32XSrc >> DIRTY_TILE_XSHIFT)];
		memset(pbBuffer, dwColor, s32DistX);
		s32XSrc >>= DIRTY_TILE_XSHIFT;

		do
		{
			pbDirty = &sg_pu8DirtyBufferArray[(s32YPos >> DIRTY_TILE_YSHIFT) << sg_u8YShift] + s32XSrc;
			s32YPos++;
			*pbDirty = 1;
			*pbBuffer = (UINT8) dwColor;
			pbBuffer += dwPitch;
		}
		while (s32DistY--);

		return;		
	}

	// It's an arbitrarily angled line. Rats...

	if ((s32DistX >= s32DistY && s32XSrc > s32XDest) || 
		 (s32DistY > s32DistX && s32YSrc > s32YDest))
	{
		// Swap from/to so we're always drawing upper left to lower right

		s32SwapVar = s32XSrc; 
		s32XSrc = s32XDest; 
		s32XDest = s32SwapVar;

		s32SwapVar = s32YSrc; 
		s32YSrc = s32YDest; 
		s32YDest = s32SwapVar;
	}

	if (s32XSrc <= s32XDest)
	{
		s32XStep = 1;
	}
	else
	{
		s32XStep = -1;
	}

	if (s32YSrc <= s32YDest)
	{
		s32YStep = 1;
	}
	else
	{
		s32YStep = -1;
	}

	s32XCounter = s32DistX >> 1;
	s32YCounter = s32DistY >> 1;

	pbBuffer += (s32XSrc + (dwPitch * s32YSrc));

	pbDirty = sg_pu8DirtyBufferArray;

	if (s32DistX == s32DistY)
	{
		s32Temp = (s32YStep * dwPitch) + s32XStep;

		while (s32XSrc <= s32XDest)
		{
			pbDirty[((s32YSrc >> DIRTY_TILE_YSHIFT) << sg_u8YShift) + (s32XSrc >> DIRTY_TILE_XSHIFT)] = 1;
			*pbBuffer = (UINT8) dwColor;
			pbBuffer += s32Temp;
			s32XSrc += s32XStep;
			s32YSrc += s32YStep;
		}

		return;
	}

	s32Temp = s32YStep * dwPitch;

	if (s32DistX > s32DistY)
	{
		while (s32XSrc <= s32XDest)
		{
			pbDirty[((s32YSrc >> DIRTY_TILE_YSHIFT) << sg_u8YShift) + (s32XSrc >> DIRTY_TILE_XSHIFT)] = 1;
			*pbBuffer = (UINT8) dwColor;
			pbBuffer += s32XStep;

			s32XSrc += s32XStep;
			s32XCounter -= s32DistY;

			if (s32XCounter < 0)
			{
				s32YSrc += s32YStep;
				pbBuffer += s32Temp;
				s32XCounter += s32DistX;
			}
		}
	}
	else
	{
		while (s32YSrc <= s32YDest)
		{
			pbDirty[((s32YSrc >> DIRTY_TILE_YSHIFT) << sg_u8YShift) + (s32XSrc >> DIRTY_TILE_XSHIFT)] = 1;

			*pbBuffer = (UINT8) dwColor;
			pbBuffer += s32Temp;

			s32YSrc += s32YStep;
			s32YCounter -= s32DistX;

			if (s32YCounter < 0)
			{
				pbBuffer += s32XStep;
				s32XSrc += s32XStep;
				s32YCounter += s32DistY;
			}
		}
	}			
#endif
}

void RasterVectorDirtyBuffer(void)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	UINT32 dwXPos = 0;
	UINT32 dwYPos = 0;
	UINT32 dwXRunStart = 0;

	/*
//	if (bRepaintAll)
	{
		sDirty[dwDirtyCount].bSurface = SURFACE_GAME;
		sDirty[dwDirtyCount].dwXULSrcPos = 0;
		sDirty[dwDirtyCount].dwYULSrcPos = 0;
		sDirty[dwDirtyCount].dwXLRSrcPos = HostReturnXVideoSize();
		sDirty[dwDirtyCount].dwYLRSrcPos = HostReturnYVideoSize();
		sDirty[dwDirtyCount].dwXDestPos = 0;
		sDirty[dwDirtyCount].dwYDestPos = 0;
		++dwDirtyCount; 
		return;
	}

	for (dwYPos = 0; dwYPos < psActiveGame->psGrMode->dwYSize; dwYPos += 8)
	{
		pbDirty = &psDirtyBuf->pbBuffer[(dwYPos >> 3) << psDirtyBuf->dwYShift];
		dwXPos = 0;

		while (dwXPos < psActiveGame->psGrMode->dwXSize)
		{
			while (dwXPos < psActiveGame->psGrMode->dwXSize && 0 == *pbDirty)
			{
				dwXPos += 8;
				++pbDirty;
			}

			// If it's dirty, figure out how long to make the run

			if (*pbDirty && dwXPos < psActiveGame->psGrMode->dwXSize)
			{
				*pbDirty = 0;
				++pbDirty;
				dwXRunStart = dwXPos;
				dwXPos += 8;

				while (dwXPos < psActiveGame->psGrMode->dwXSize && *pbDirty)
				{
					*pbDirty = 0;
					dwXPos += 8;
					++pbDirty;
				}

				// Let's add this sucker to the list

				sDirty[dwDirtyCount].bSurface = SURFACE_GAME;
				sDirty[dwDirtyCount].dwXULSrcPos = dwXRunStart;
				sDirty[dwDirtyCount].dwYULSrcPos = dwYPos;
				sDirty[dwDirtyCount].dwXLRSrcPos = dwXPos;
				sDirty[dwDirtyCount].dwYLRSrcPos = dwYPos + 16;
				sDirty[dwDirtyCount].dwXDestPos = dwXRunStart;
				sDirty[dwDirtyCount].dwYDestPos = dwYPos;
				++dwDirtyCount;
			}
		}
	} */
#endif
}

/************************************************************************
 *					
 * Name : RasterVectorRender
 *					
 * Entry: List of current, prior,  and last vector draw lists
 *					
 * Exit : Nothing
 *					
 * Description:
 *					
 * This routine takes a prior line list, erases the old lines, draws the
 * new ones, and sets the dirty buffer bytes.
 * 				
 ************************************************************************/

void RasterVectorRender(struct sVector *psThisVB, UINT32 dwThisCount,
								 struct sVector *psPriorVB, UINT32 dwPriorCount,
								 struct sVector *psCurrentVB, UINT32 dwCurrentCount)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	UINT32 dwLoop;
	struct sVector *psTempVector = NULL;
	struct sVector *psPriorVector = NULL;
	INT32 s32XFrom, s32YFrom, s32XTo, s32YTo;

	// Point to our structures

	psTempVector = psCurrentVB;

	// Run through all new vector possibilities and clip them

	dwLoop = 0;

	while (dwLoop < dwCurrentCount)
	{
		s32XFrom = psTempVector->s32FromX << 16;
		s32YFrom = psTempVector->s32FromY << 16;
		s32XTo = psTempVector->s32ToX << 16;
		s32YTo = psTempVector->s32ToY << 16;

		if (ClipLine(&s32XFrom, &s32YFrom,
						 &s32XTo, &s32YTo,
						 0, 0, 
						 (sg_u32BackBufferXSize - 1) << 16,
						 (sg_u32BackBufferYSize - 1) << 16) == FALSE)
		{
			// If it's outside our range, kill it!

			psTempVector->s32FromX = 0;
			psTempVector->s32FromY = 0;
			psTempVector->s32ToX = 0;
			psTempVector->s32ToY = 0;
			psTempVector->bColor = 0;
		}
		else
		{
			psTempVector->s32FromX = s32XFrom >> 16;	
			psTempVector->s32FromY = s32YFrom >> 16;	
			psTempVector->s32ToX = s32XTo >> 16;	
			psTempVector->s32ToY = s32YTo >> 16;
		}

		++psTempVector;
		++dwLoop;
	}

	psPriorVector = psPriorVB;
	psTempVector = psCurrentVB;
	dwLoop = 0;

	// First erase the prior vectors. We don't need to clip them, as they
	// would've been clipped before.

	while (dwLoop < dwPriorCount)
	{
		if (dwLoop < dwCurrentCount)
		{
			if (memcmp(psTempVector, psPriorVector, sizeof(struct sVector)))
			{
				DrawRasterLine(psPriorVector, 0);
			}
		}
		else
		{
			DrawRasterLine(psPriorVector, 0);
		}

		++psTempVector;
		++psPriorVector;
		dwLoop++;
	}

	// Cool. We've erased things that didn't matter. Now let's redraw
	// everything!

	while (dwCurrentCount)
	{
		DrawRasterLine(psCurrentVB, psCurrentVB->bColor);
		++psCurrentVB;
		dwCurrentCount--;
	}

	// Now let's dirty run the vector stuff

	RasterVectorDirtyBuffer();
#endif
}

void GfxSetFrameRate(UINT32 u32FramesPerSecond)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	HostSetFrameRate(u32FramesPerSecond);
#endif
}

void GfxInit(void)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	sg_psPalette = NULL;
	sg_u32PaletteEntryCount = 0;
	sg_u32BackBufferXSize = 0;
	sg_u32BackBufferYSize = 0;
	sg_u32XOverscan = 0;
	sg_u32YOverscan = 0;
	sg_u8BackBufferColorDepth = 0;
	sg_pu8BackBufferStart = NULL;

	if (FALSE == HostGfxInit())
	{
		printf("Couldn't initialize graphics engine\n");
		exit(1);
	}
#endif
}

void GfxShutdown(void)
{
#if ((!defined(USE_SDL_GRAPHICS)) && (!defined(USE_SDL2_GRAPHICS)))
return;
#else
	HostGfxShutdown();
#endif
}



