//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS2_2D_Graphics.cpp
//
// Copyright (c) 2002-2004 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Rendering/Asura_PS2_2D_Graphics.h"

// Engine independent.
#include "Camera/Asura_Camera.h"
#include "Core/Asura_TextureManagement.h"
#include "Debugging/Asura_Assert.h"

// Engine platform-specific.
#include "Core/Asura_PS2_DMA.h"
#include "Rendering/Asura_PlatformRendering.h"
#include "Rendering/Asura_PlatformRenderStates.h"
#include "Rendering/Asura_PlatformTextureManagement.h"
#include "Rendering/Asura_PS2_DeferredRendering.h"
#include "Rendering/Asura_PS2_GS.h"
#include "Rendering/Asura_PS2_VU1.h"
#include "Rendering/Asura_Viewport.h"

// T Y P E S /////////////////////////////////////////////////////////////////

enum Asura_PS2_2D_SpriteType 
{
    ASURA_PS2_SPRITE,
    ASURA_PS2_QUAD,
    ASURA_PS2_GOURAUD_QUAD,
    ASURA_PS2_Z_SPRITE,
    ASURA_PS2_Z_QUAD,
};

// C O N S T A N T S /////////////////////////////////////////////////////////

static const u_int MAX_QUAD_BATCHES = 16;

static const int NUM_POLY_REGS_SPRITE       = 6;
static const int NUM_POLY_REGS_ROTATED_QUAD = 10;
static const int NUM_POLY_REGS_GOURAUD_QUAD = 13;
static const int NUM_POLY_REGS_Z_SPRITE     = 6;
static const int NUM_POLY_REGS_Z_QUAD       = 10;

// G L O B A L S /////////////////////////////////////////////////////////////

// To allow display of a mipmap of a texture. See CheckForTexDisplayRequest()
// below. X and Y are specified in viewport coordinates; the default -1 values
// will centre the image.
DEBUGVAR int   dbg_iTexDisplay_ID            = ASURA_TEXTURE_NONE;
DEBUGVAR u_int dbg_uTexDisplay_Mip           = 0;
DEBUGVAR bool  dbg_bTexDisplay_Alpha         = false;
DEBUGVAR u_int dbg_uTexDisplay_Magnification = 0;

static Asura_PS2_2D_Graphics::QuadBatch* g_pxQuadBatches = NULL;
static u_int g_uNumQuadBatches = 0;

#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
static Asura_PS2_2D_Graphics::Line2D* g_pxLines = NULL;
static u_int g_uNumLines = 0;
#endif

// P R O T O T Y P E S   A N D   I N L I N E   F U N C T I O N S /////////////

static inline float CalculatePerspectiveScreenspaceZ( float fViewspaceZ )
{
    sceVu0FMATRIX& xV2S = Asura_PS2_VU1::GetViewToScreenMat();

    // Assume matrix is of the form
    //
    //     | ? ? ? ? |
    //     | ? ? ? ? |
    //     | 0 0 a b |
    //     | 0 0 1 0 |
    //
    // so that
    //
    //     z' = a*z + b*w = a + b
    //          ---------       -
    //              z           z

    return( xV2S[2][2] + xV2S[3][2] / fViewspaceZ );
}

DEBUGVAR bool dbg_bScaleXForWidescreen = true;
static inline float ScaleXForWidescreen(float fX)
{
    if (dbg_bScaleXForWidescreen && Asura_Render::ShouldRenderForWidescreen())
    {
		float fAdjustment = static_cast<float>(Asura_PS2_GS::GetScreenWidth() >> 1);
        fX -= fAdjustment; // Adjust coordinate so we centre at zero.
        fX *= 0.75f;    // up to edge of of 4:3 part of 16:9 widescreen
        fX += fAdjustment; // Adjust coordinate back again.
    }
    return fX;
}

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

void Asura_PS2_2D_Graphics::CheckForTexDisplayRequest( void )
{
    if( ASURA_TEXTURE_NONE == dbg_iTexDisplay_ID ) { return; }

    if( !Asura_Texture_Management::IsTextureIDValid(dbg_iTexDisplay_ID) ) { return; }
    
    u_int uNumMipmaps = Asura_PS2_TextureManagement::GetNumMipmapLevels( dbg_iTexDisplay_ID );
    if( dbg_uTexDisplay_Mip >= uNumMipmaps ) { return; }

    Asura_Render::AllowFullScreenRendering();

    // Renderstate.
    {
        Asura_PlatformRenderStates::CreateDefaultRenderStatePacket( Asura_PlatformRenderStates::CONTEXT_1 );

        Asura_PlatformRenderStates::SetPixelTest( false, false );
        Asura_PlatformRenderStates::SetTextureClamp( true );

        if( dbg_bTexDisplay_Alpha )
        {
            // See comment by TEX0 setup below.
            Asura_PlatformRenderStates::SetAlphaBlend( SCE_GS_ALPHA_CS, SCE_GS_ALPHA_ZERO, SCE_GS_ALPHA_AS, SCE_GS_ALPHA_ZERO, 0 );
        }
        else
        {
            Asura_PlatformRenderStates::SetAlphaBlend( SCE_GS_ALPHA_ZERO, SCE_GS_ALPHA_ZERO, SCE_GS_ALPHA_FIX, SCE_GS_ALPHA_CS, 0 );
        }

        Asura_PlatformRenderStates::ApplyRenderStates();
    }

    Asura_PS2_TextureManagement::SetBufferLayout( ASURA_PS2_TEXBFRLAYOUT_2_LARGE );
    bool bUploaded = Asura_PS2_TextureManagement::UploadTexture( dbg_iTexDisplay_ID );

    // Must setup TEX1 first.
    {
        Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer( 3 );
        Asura_PS2_QW* pxOut = pxBase;

        (reinterpret_cast<Asura_PS2_DmaTag*>(pxOut++))->Set( 2,
                                                             Asura_PS2_DmaTag::PCE_NOCHANGE,
                                                             Asura_PS2_DmaTag::ID_CNT,
                                                             false,
                                                             0,
                                                             false,
                                                             SCE_VIF1_SET_NOP(false),
                                                             SCE_VIF1_SET_DIRECT(2, false) );

        (reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( 1,                   // NLOOP
                                                             true,                // EOP
                                                             false,               // PRE
                                                             0,                   // PRIM
                                                             SCE_GIF_PACKED,      // FLG
                                                             1,                   // NREG
                                                             SCE_GIF_PACKED_AD ); // REG0

        Asura_PS2_TextureManagement::GetTEX1Setting( &pxOut->u64s[0], dbg_iTexDisplay_ID, SCE_GS_NEAREST, SCE_GS_NEAREST_MIPMAP_NEAREST );
    
        // Force the relevant mip level to be used.
        reinterpret_cast<sceGsTex1&>(pxOut->u64s[0]).LCM = 1;
        reinterpret_cast<sceGsTex1&>(pxOut->u64s[0]).K   = dbg_uTexDisplay_Mip << 4;

        pxOut->u64s[1] = SCE_GS_TEX1_1;
        ++pxOut;
    }

    Asura_PS2_TextureManagement::SetupTextureEnvironment( bUploaded,
                                                          ASURA_PS2_TEXREGFLAG_MIPTBP1 | ASURA_PS2_TEXREGFLAG_TEXFLUSH,
                                                          dbg_iTexDisplay_ID,
                                                          Asura_PS2_TextureManagement::BFR_AUTO );

    const u_int uNumRegs  = 6;
    const u_int uQWC      = 1 + uNumRegs;
    const u_int uTotalQWs = 1 + uQWC;

    Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer( uTotalQWs );
    Asura_PS2_QW* pxOut = pxBase;

    // DMA tag.
    (reinterpret_cast<Asura_PS2_DmaTag*>(pxOut++))->Set( uQWC,
                                                         Asura_PS2_DmaTag::PCE_NOCHANGE,
                                                         Asura_PS2_DmaTag::ID_CNT,
                                                         false,
                                                         0,
                                                         false,
                                                         SCE_VIF1_SET_NOP(false),
                                                         SCE_VIF1_SET_DIRECT(uQWC, false) );

    // GIF tag.
    {
        const u_int uPRIM = SCE_GS_SET_PRIM( SCE_GS_PRIM_SPRITE,    // PRIM
                                             0,                     // IIP
                                             true,                  // TME
                                             false,                 // FGE
                                             dbg_bTexDisplay_Alpha, // ABE
                                             false,                 // AA1
                                             1,                     // FST
                                             0,                     // CTXT
                                             0 );                   // FIX

        (reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( 1,                 // NLOOP
                                                             true,              // EOP
                                                             true,              // PRE
                                                             uPRIM,             // PRIM
                                                             SCE_GIF_PACKED,    // FLG
                                                             uNumRegs,          // NREG
                                                             SCE_GIF_PACKED_AD, // REG0
                                                             SCE_GIF_PACKED_AD, // REG1
                                                             SCE_GS_UV,         // REG2
                                                             SCE_GS_XYZ2,       // REG3
                                                             SCE_GS_UV,         // REG4
                                                             SCE_GS_XYZ2 );     // REG5
    }

    // TEX0.
    {
        u_long uTEX0;
        Asura_PS2_TextureManagement::GetTEX0Setting( &uTEX0, dbg_iTexDisplay_ID, Asura_PS2_TextureManagement::BFR_AUTO );

        if( dbg_bTexDisplay_Alpha )
        {
            // If we're displaying alpha channel, change TFX to HIGHLIGHT2.
            // That way, Rs=Rt*Rf+Af (similarly for G and B) and As=At. If we
            // then set Rf=Gf=Bf=0 and Af=128, we get Rs=Gs=Bs=128 and As=At,
            // meaning we get Rd=Gd=Bd=At when using the alpha blend setup
            // up above.
            (reinterpret_cast<sceGsTex0&>(uTEX0)).TFX = SCE_GS_HIGHLIGHT2;
        }
        else
        {
            (reinterpret_cast<sceGsTex0&>(uTEX0)).TFX = SCE_GS_DECAL;
        }

        pxOut->u64s[0] = uTEX0;
        pxOut->u64s[1] = SCE_GS_TEX0_1;
        ++pxOut;
    }

    // RGBA.
    {
        if( dbg_bTexDisplay_Alpha )
        {
            // See comment by TEX0 setup above.
            pxOut->u64s[0] = SCE_GS_SET_RGBAQ( 0, 0, 0, 128, 0x3F800000 );
        }
        else
        {
            pxOut->u64s[0] = SCE_GS_SET_RGBAQ( 128, 128, 128, 128, 0x3F800000 );
        }

        pxOut->u64s[1] = SCE_GS_RGBAQ;
        ++pxOut;
    }

    int iTexWidth  = Asura_PS2_TextureManagement::GetTextureWidth( dbg_iTexDisplay_ID );
    int iTexHeight = Asura_PS2_TextureManagement::GetTextureHeight( dbg_iTexDisplay_ID );

    int iRenderWidth  = iTexWidth;
    int iRenderHeight = iTexHeight;

	// KOR - this gives a warning in release because comparison with 0 is always false
	// Given that the result is nothing happening, simply removed the code entirely in release.
#ifdef _DEBUG
    for( u_int u = 0; u < dbg_uTexDisplay_Magnification; ++u )
    {
        iRenderWidth  <<= 1;
        iRenderHeight <<= 1;
    }

    for( u_int u = 0; u < dbg_uTexDisplay_Mip; ++u )
    {
        iRenderWidth  >>= 1;
        iRenderHeight >>= 1;
    }
#endif

    int iX = (Asura_Viewport::GetWidth()  >> 1) - (iRenderWidth  >> 1);
    int iY = (Asura_Viewport::GetHeight() >> 1) - (iRenderHeight >> 1);

    // Transform into primitive coordinate system.
    iX += ASURA_PS2_SCREEN_CENTRE_X - (Asura_PS2_GS::GetScreenWidth() >> 1);
    iY += ASURA_PS2_SCREEN_CENTRE_Y - (Asura_PS2_GS::GetScreenHeight()>> 1);

    // TL.
    pxOut->u32s[0] = 0 << 4;
    pxOut->u32s[1] = 0 << 4;
    ++pxOut;
    pxOut->u32s[0] = iX << 4;
    pxOut->u32s[1] = iY << 4;
    pxOut->u64s[1] = 0;
    ++pxOut;

    // BR.
    pxOut->u32s[0] = iTexWidth  << 4;
    pxOut->u32s[1] = iTexHeight << 4;
    ++pxOut;
    pxOut->u32s[0] = (iX + iRenderWidth ) << 4;
    pxOut->u32s[1] = (iY + iRenderHeight) << 4;
    pxOut->u64s[1] = 0;
    ++pxOut;

    Asura_Assert( static_cast<u_int>(pxOut - pxBase) == uTotalQWs, "" );
}

void Asura_PS2_2D_Graphics::Initialise()
{
    g_pxQuadBatches   = new QuadBatch[MAX_QUAD_BATCHES];
    g_uNumQuadBatches = 0;

#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
    g_pxLines   = new Line2D[ASURA_PS2_MAX_2D_LINES_PER_FRAME];
    g_uNumLines = 0;
#endif
}

void Asura_PS2_2D_Graphics::Shutdown()
{
#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
    delete[] g_pxLines;
    g_pxLines = NULL;
#endif

    delete[] g_pxQuadBatches;
    g_pxQuadBatches = NULL;
}

void Asura_PS2_2D_Graphics::QuadToSprite(const Asura_Overlay_Quad* pQuad, struct BatchSprite* pxSprite)
{
    // Convert an Asura overlay quad to a BatchSprite
    pxSprite->X[0] = pQuad->X[0];
    pxSprite->X[1] = pQuad->X[2];
    pxSprite->Y[0] = pQuad->Y[0];
    pxSprite->Y[1] = pQuad->Y[2];
    pxSprite->U[0] = pQuad->U[0];
    pxSprite->U[1] = pQuad->U[2];
    pxSprite->V[0] = pQuad->V[0];
    pxSprite->V[1] = pQuad->V[2];
    pxSprite->uARGB = pQuad->Colour;
    pxSprite->iTextureIndex = pQuad->TextureIndex;
    pxSprite->eTranslucencyMode = pQuad->TranslucencyMode;
}

void Asura_PS2_2D_Graphics::QuadToRotatedQuad(const Asura_Overlay_Quad* pQuad, struct RotatedBatchQuad* pxSprite)
{
    // Covert an Asura overlay quad to a rotatedbatchquad
    for (int i = 0; i < 2; i++)
    {
        pxSprite->X[i] = pQuad->X[i];
        pxSprite->Y[i] = pQuad->Y[i];
        pxSprite->U[i] = pQuad->U[i];
        pxSprite->V[i] = pQuad->V[i];
    }
    // swap last ones around for strip
    for (int i = 0; i < 2; i++)
    {
        pxSprite->X[2 + i] = pQuad->X[3 - i];
        pxSprite->Y[2 + i] = pQuad->Y[3 - i];
        pxSprite->U[2 + i] = pQuad->U[3 - i];
        pxSprite->V[2 + i] = pQuad->V[3 - i];
    }
    pxSprite->uARGB = pQuad->Colour;            
    pxSprite->iTextureIndex = pQuad->TextureIndex;
    pxSprite->eTranslucencyMode = pQuad->TranslucencyMode;
}

void Asura_PS2_2D_Graphics::QuadToGouraudQuad(const Asura_Overlay_Quad* pQuad, struct GouraudBatchQuad* pxSprite)
{
    // Covert an Asura overlay quad to a rotatedbatchquad
    for (int i = 0; i < 2; i++)
    {
        pxSprite->X[i] = pQuad->X[i];
        pxSprite->Y[i] = pQuad->Y[i];
        pxSprite->U[i] = pQuad->U[i];
        pxSprite->V[i] = pQuad->V[i];
        pxSprite->auARGBs[i] = pQuad->GouraudColour[i];
    }
    // swap last ones around for strip
    for (int i = 0; i < 2; i++)
    {
        pxSprite->X[2 + i] = pQuad->X[3 - i];
        pxSprite->Y[2 + i] = pQuad->Y[3 - i];
        pxSprite->U[2 + i] = pQuad->U[3 - i];
        pxSprite->V[2 + i] = pQuad->V[3 - i];
        pxSprite->auARGBs[2 + i] = pQuad->GouraudColour[3 - i];
    }
    
    pxSprite->iTextureIndex = pQuad->TextureIndex;
    pxSprite->eTranslucencyMode = pQuad->TranslucencyMode;
}

bool Asura_PS2_2D_Graphics::AddSpriteToThisBatch(QuadBatch* pxBatch, const BatchSprite* pxSpriteToAdd)
{
#if _DEBUG
    Asura_Assert( Asura_PlatformRenderStates::IsTranslucencyModeSupported( pxSpriteToAdd->eTranslucencyMode ), "Unsupported translucency mode" );
#endif

    if (pxBatch->iNumSpritesInBatch < ASURA_PS2_MAX_SPRITES_IN_BATCH)
    {
        BatchSprite* pxSprite = &pxBatch->a_xSprites[pxBatch->iNumSpritesInBatch++];
        memcpy(pxSprite, pxSpriteToAdd, sizeof(BatchSprite));
        return true;
    }
    return false;
}

bool Asura_PS2_2D_Graphics::AddRotatedQuadToThisBatch(QuadBatch* pxBatch, const RotatedBatchQuad* pxRotQuadToAdd)
{
#if _DEBUG
    Asura_Assert( Asura_PlatformRenderStates::IsTranslucencyModeSupported( pxRotQuadToAdd->eTranslucencyMode ), "Unsupported translucency mode" );
#endif

    if (pxBatch->iNumRotatedQuadsInBatch < ASURA_PS2_MAX_ROTATED_QUADS_IN_BATCH)
    {
        RotatedBatchQuad* pxRotQuad = &pxBatch->a_xRotatedQuads[pxBatch->iNumRotatedQuadsInBatch++];
        memcpy(pxRotQuad, pxRotQuadToAdd, sizeof(RotatedBatchQuad));
        return true;
    }
    return false;
}

bool Asura_PS2_2D_Graphics::AddGouraudQuadToThisBatch(QuadBatch* pxBatch, const GouraudBatchQuad* pxGouraudQuadToAdd)
{
#if _DEBUG
    Asura_Assert( Asura_PlatformRenderStates::IsTranslucencyModeSupported( pxGouraudQuadToAdd->eTranslucencyMode ), "Unsupported translucency mode" );
#endif

    if (pxBatch->iNumGouraudQuadsInBatch < ASURA_PS2_MAX_GOURAUD_QUADS_IN_BATCH)
    {
        GouraudBatchQuad* pxGouraudQuad = &pxBatch->a_xGouraudQuads[pxBatch->iNumGouraudQuadsInBatch++];
        memcpy(pxGouraudQuad, pxGouraudQuadToAdd, sizeof(GouraudBatchQuad));
        return true;
    }
    return false;
}

bool Asura_PS2_2D_Graphics::AddZSpriteToThisBatch(QuadBatch* pxBatch, const ZSortedBatchSprite * pxZSpriteToAdd)
{
#if _DEBUG
    Asura_Assert( Asura_PlatformRenderStates::IsTranslucencyModeSupported( pxZSpriteToAdd->eTranslucencyMode ), "Unsupported translucency mode" );
#endif

    if (pxBatch->iNumZSpritesInBatch < ASURA_PS2_MAX_ZSPRITES_IN_BATCH)
    {
        ZSortedBatchSprite* pxZSprite = &pxBatch->a_xZSprites[pxBatch->iNumZSpritesInBatch++];
        memcpy(pxZSprite, pxZSpriteToAdd, sizeof(ZSortedBatchSprite));
        return true;
    }
    return false;
}

bool Asura_PS2_2D_Graphics::AddZQuadToThisBatch(QuadBatch* pxBatch, const ZSortedBatchQuad *pxZQuadToAdd)
{
#if _DEBUG
    Asura_Assert( Asura_PlatformRenderStates::IsTranslucencyModeSupported( pxZQuadToAdd->eTranslucencyMode ), "Unsupported translucency mode" );
#endif

    if (pxBatch->iNumZQuadsInBatch < ASURA_PS2_MAX_ZQUADS_IN_BATCH)
    {
        ZSortedBatchQuad* pxZQuad = &pxBatch->a_xZQuads[pxBatch->iNumZQuadsInBatch++];
        memcpy(pxZQuad, pxZQuadToAdd, sizeof(ZSortedBatchQuad));
        return true;
    }
    return false;
}


void Asura_PS2_2D_Graphics::AddSpriteToNewBatch(const BatchSprite* pxSprite)
{
    QuadBatch* pBatch;
    if(CreateNewBatch(pBatch))
    {
        pBatch->iTexID = pxSprite->iTextureIndex;
        pBatch->eBlendMode = pxSprite->eTranslucencyMode;
        bool bSuccess = AddSpriteToThisBatch(pBatch, pxSprite);
        Asura_Assert(bSuccess, "Failed to add a sprite to a new batch");
    }
    else
    {
        // batches have been flushed by CreateNewBatch
        AddSpriteToNewBatch(pxSprite);
    }
}

void Asura_PS2_2D_Graphics::AddRotatedQuadToNewBatch(const RotatedBatchQuad* pxRotQuad)
{
    QuadBatch* pBatch;
    if(CreateNewBatch(pBatch))
    {
        pBatch->iTexID = pxRotQuad->iTextureIndex;
        pBatch->eBlendMode = pxRotQuad->eTranslucencyMode;
        bool bSuccess = AddRotatedQuadToThisBatch(pBatch, pxRotQuad);
        Asura_Assert(bSuccess, "Failed to add a rotated quad to a new batch");
    }
    else
    {
        AddRotatedQuadToNewBatch(pxRotQuad);
    }
}

void Asura_PS2_2D_Graphics::AddGouraudQuadToNewBatch(const GouraudBatchQuad* pxGouraudQuad)
{
    QuadBatch* pBatch;
    if(CreateNewBatch(pBatch))
    {
        pBatch->iTexID = pxGouraudQuad->iTextureIndex;
        pBatch->eBlendMode = pxGouraudQuad->eTranslucencyMode;
        bool bSuccess = AddGouraudQuadToThisBatch(pBatch, pxGouraudQuad);
        Asura_Assert(bSuccess, "Failed to add a Gouraud quad to a new batch");
    }
    else
    {
        AddGouraudQuadToNewBatch(pxGouraudQuad);
    }
}

void Asura_PS2_2D_Graphics::AddZSpriteToNewBatch(const ZSortedBatchSprite * pxZSprite)
{
    QuadBatch* pBatch;
    if(CreateNewBatch(pBatch))
    {
        pBatch->iTexID = pxZSprite->iTextureIndex;
        pBatch->eBlendMode = pxZSprite->eTranslucencyMode;
        bool bSuccess = AddZSpriteToThisBatch(pBatch, pxZSprite);
        Asura_Assert(bSuccess, "Failed to add a Z sorted sprite to a new batch");
    }
    else
    {
        AddZSpriteToNewBatch(pxZSprite);
    }
}

void Asura_PS2_2D_Graphics::AddZQuadToNewBatch(const ZSortedBatchQuad* pxZQuad)
{
    QuadBatch* pBatch;
    if(CreateNewBatch(pBatch))
    {
        pBatch->iTexID = pxZQuad->iTextureIndex;
        pBatch->eBlendMode = pxZQuad->eTranslucencyMode;
        bool bSuccess = AddZQuadToThisBatch(pBatch, pxZQuad);
        Asura_Assert(bSuccess, "Failed to add a Z sorted quad to a new batch");
    }
    else
    {
        AddZQuadToNewBatch(pxZQuad);
    }
    
}

bool Asura_PS2_2D_Graphics::CreateNewBatch(QuadBatch*& pxNewBatch)
{
    if( g_uNumQuadBatches < MAX_QUAD_BATCHES )
    {
        pxNewBatch = &g_pxQuadBatches[g_uNumQuadBatches++];
        pxNewBatch->iNumSpritesInBatch = 0;
        pxNewBatch->iNumRotatedQuadsInBatch = 0;
        pxNewBatch->iNumGouraudQuadsInBatch = 0;
        pxNewBatch->iNumZSpritesInBatch = 0;
        pxNewBatch->iNumZQuadsInBatch = 0;
        return true;
    }
    else
    {
        Asura_Assert( MAX_QUAD_BATCHES, "" );
        Asura_Render::FlushDisplay();
        Asura_Assert(!g_uNumQuadBatches, "Flush display did not empty the buffers");
//        Asura_Assert(0, "ran out of space for quad batches");
        // flush all batches?
    }
    return false;
}

int Asura_PS2_2D_Graphics::FindBatch(int iTexId, ASURA_TRANSLUCENCY_MODE eBlendMode, int iFreeType, QuadBatch*& pxBatch)
{
    // return pointer to batch if found
    for( u_int u = 0; u < g_uNumQuadBatches; ++u )
    {
        QuadBatch * pBatch = &g_pxQuadBatches[u];
        if ((pBatch->iTexID == iTexId) && (pBatch->eBlendMode == eBlendMode))
        {
            switch(iFreeType)
            {
                case ASURA_PS2_SPRITE:
                {
                    if(pBatch->iNumSpritesInBatch >= ASURA_PS2_MAX_SPRITES_IN_BATCH)
                    {
                        continue;           // batch is full, find another
                    }
                }
                break;
                case ASURA_PS2_QUAD:
                {
                    if(pBatch->iNumRotatedQuadsInBatch >= ASURA_PS2_MAX_ROTATED_QUADS_IN_BATCH)
                    {
                        continue;           // batch is full, find another
                    }
                }
                break;
                case ASURA_PS2_GOURAUD_QUAD:
                {
                    if(pBatch->iNumGouraudQuadsInBatch >= ASURA_PS2_MAX_GOURAUD_QUADS_IN_BATCH)
                    {
                        continue;           // batch is full, find another
                    }
                }
                break;
                case ASURA_PS2_Z_SPRITE:
                {
                    if(pBatch->iNumZSpritesInBatch >= ASURA_PS2_MAX_ZSPRITES_IN_BATCH)
                    {
                        continue;           // batch is full, find another
                    }
                }
                break;
                case ASURA_PS2_Z_QUAD:
                {
                    if(pBatch->iNumZQuadsInBatch >= ASURA_PS2_MAX_ZQUADS_IN_BATCH)
                    {
                        continue;           // batch is full, find another
                    }
                }
                break;
            }
            pxBatch = pBatch;
            return u;
        }
    }
    return -1;
}

void Asura_PS2_2D_Graphics::SwapBatches( u_int uStartIndex, QuadBatch* pxBatch )
{
    // We are creating a new batch, but it will have the same texture as
    // the current batch (s_xQuadBatch[i]), shuffle the batches around
    // to ensure that the 2 quad batches with the same texture are rendered together.aw.
    // Implicit Assumption 1 : Quad batches are rendered in index order.
    // Implicit Assumption 2 : New batch index is g_uNumQuadBatches - 1.
    for( u_int u = uStartIndex + 1; u < g_uNumQuadBatches - 1; ++u )
    {
        if( g_pxQuadBatches[u].iTexID != pxBatch->iTexID )
        {
            // NB - Not happy about this method of swapping.
            // Must be a more efficient way.aw.
            QuadBatch xSwap = g_pxQuadBatches[g_uNumQuadBatches - 1];
            g_pxQuadBatches[g_uNumQuadBatches - 1] = g_pxQuadBatches[u - 1];
            g_pxQuadBatches[u - 1] = xSwap;
            return;
        }
    }
}

void Asura_PS2_2D_Graphics::AddQuad( const Asura_Overlay_Quad* quad )
{
    if (quad->m_bGouraudShaded)
    {
        GouraudBatchQuad xGouraudQuad;
        QuadToGouraudQuad(quad, &xGouraudQuad);
        AddGouraudQuad(&xGouraudQuad);
    }
    else if ( quad->m_bNonRotated ) // (quad->X[0] == quad->X[3]) && (quad->Y[0] == quad->Y[1]) )
    {
        Asura_Assert((quad->X[0] == quad->X[3]) && (quad->Y[0] == quad->Y[1]), "quad is rotated but says it isn't");
        BatchSprite xSprite;
        QuadToSprite(quad, &xSprite);
        AddSprite(&xSprite);
    }
    else
    {
        RotatedBatchQuad xRotQuad;
        QuadToRotatedQuad(quad, &xRotQuad);
        AddRotatedQuad(&xRotQuad);
    }
}

void Asura_PS2_2D_Graphics::AddSprite(const Asura_PS2_2D_Graphics::BatchSprite *pxSprite)
{
    // see if batch exists with these settings
    QuadBatch * pBatch = &g_pxQuadBatches[0];
    int iIndexOfBatch = FindBatch(pxSprite->iTextureIndex, pxSprite->eTranslucencyMode, ASURA_PS2_SPRITE, pBatch);
    if(iIndexOfBatch >= 0)
    {
        if (!AddSpriteToThisBatch(pBatch, pxSprite))
        {
            AddSpriteToNewBatch(pxSprite);
            SwapBatches(iIndexOfBatch, pBatch);
        }
    }
    else
    {
        AddSpriteToNewBatch(pxSprite);
    }
}

void Asura_PS2_2D_Graphics::AddRotatedQuad(const RotatedBatchQuad* pxRotQuad)
{
    // see if batch exists with these settings
    QuadBatch * pBatch = &g_pxQuadBatches[0];
    int iIndexOfBatch = FindBatch(pxRotQuad->iTextureIndex, pxRotQuad->eTranslucencyMode, ASURA_PS2_QUAD, pBatch);
    if(iIndexOfBatch >= 0)
    {
        if (!AddRotatedQuadToThisBatch(pBatch, pxRotQuad))
        {
            AddRotatedQuadToNewBatch(pxRotQuad);
            SwapBatches(iIndexOfBatch, pBatch);
        }
    }
    else
    {
        AddRotatedQuadToNewBatch(pxRotQuad);
    }
}

void Asura_PS2_2D_Graphics::AddGouraudQuad(const GouraudBatchQuad* pxGouraudQuad)
{
    // see if batch exists with these settings
    QuadBatch * pBatch = &g_pxQuadBatches[0];
    int iIndexOfBatch = FindBatch(pxGouraudQuad->iTextureIndex, pxGouraudQuad->eTranslucencyMode, ASURA_PS2_QUAD, pBatch);
    if(iIndexOfBatch >= 0)
    {
        if (!AddGouraudQuadToThisBatch(pBatch, pxGouraudQuad))
        {
            AddGouraudQuadToNewBatch(pxGouraudQuad);
            SwapBatches( iIndexOfBatch, pBatch );
        }
    }
    else
    {
        AddGouraudQuadToNewBatch(pxGouraudQuad);
    }
}

void Asura_PS2_2D_Graphics::AddZSprite(const ZSortedBatchSprite* pxZSprite)
{
    // see if batch exists with these settings
    QuadBatch * pBatch = &g_pxQuadBatches[0];
    int iIndexOfBatch = FindBatch(pxZSprite->iTextureIndex, pxZSprite->eTranslucencyMode, ASURA_PS2_Z_SPRITE, pBatch);
    if(iIndexOfBatch >= 0)
    {
        if (!AddZSpriteToThisBatch(pBatch, pxZSprite))
        {
            AddZSpriteToNewBatch(pxZSprite);
            SwapBatches( iIndexOfBatch, pBatch );
        }
    }
    else
    {
        AddZSpriteToNewBatch(pxZSprite);
    }
}

void Asura_PS2_2D_Graphics::AddZQuad(const ZSortedBatchQuad* pxZQuad)
{
    // see if batch exists with these settings
    QuadBatch * pBatch = &g_pxQuadBatches[0];
    int iIndexOfBatch = FindBatch(pxZQuad->iTextureIndex, pxZQuad->eTranslucencyMode, ASURA_PS2_Z_QUAD, pBatch);
    if(iIndexOfBatch >= 0)
    {
        if (!AddZQuadToThisBatch(pBatch, pxZQuad))
        {
            AddZQuadToNewBatch(pxZQuad);
            SwapBatches( iIndexOfBatch, pBatch );
        }
    }
    else
    {
        AddZQuadToNewBatch(pxZQuad);
    }
}

void Asura_PS2_2D_Graphics::Flush()
{
    int iTransX, iTransY;

    {
        const Asura_PS2_GS::ViewportParams& xViewportParams = ( Asura_PS2_GS::IsFullScreenRendering()
                                                                    ? Asura_PS2_GS::GetViewportParamsForFullScreenRendering()
                                                                    : Asura_PS2_GS::GetViewportParams(Asura_Camera::GetCameraIndex()) );

        iTransX = xViewportParams.sPrimTransX;
        iTransY = xViewportParams.sPrimTransY;
    }
    
    // Set texture buffer layout.
    Asura_PS2_TextureManagement::SetBufferLayout( ASURA_PS2_TEXBFRLAYOUT_2_LARGE );     

    // Keep track of the current blend mode, so we only reset as needed
    ASURA_TRANSLUCENCY_MODE eCurrentBlendMode = ASURA_TRANSLUCENCY_NOT_SET;

    int iLastTextId = ASURA_TEXTURE_NONE;
    for( u_int u = 0; u < g_uNumQuadBatches; ++u )
    {
        QuadBatch * pBatch = &g_pxQuadBatches[u];
        
        bool bUploadedTexture = false;
        bool bNewTexture = false;

        // Does this batch have a new texture ?
        if (pBatch->iTexID != iLastTextId)
        {
            // Ok, it's a new texture.. should we upload it ?
            iLastTextId = pBatch->iTexID;
        
            if (pBatch->iTexID != ASURA_TEXTURE_NONE)
            {
                bUploadedTexture = Asura_PS2_TextureManagement::UploadTexture( pBatch->iTexID );
                // bUploadedTexture tells us if we did a texture upload, but the texture might be in texture cache.
                // In which case we'll still need to reset the texture environment
                bNewTexture = true;
            }
        }

        // now send the texture data and renderstates
        {
            // NRM 22-08-02 - added extra 4 Qwords for switching Z test on/off halfway through the packet
            int num_qwords = 4;

            if ( pBatch->eBlendMode != eCurrentBlendMode )
            {
                num_qwords += Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS;
            }
            if (pBatch->iNumSpritesInBatch)
            {
                num_qwords += 1 + (pBatch->iNumSpritesInBatch * NUM_POLY_REGS_SPRITE);
            }
            if (pBatch->iNumRotatedQuadsInBatch)
            {
                num_qwords += 1 + (pBatch->iNumRotatedQuadsInBatch * NUM_POLY_REGS_ROTATED_QUAD);
            }
            if (pBatch->iNumGouraudQuadsInBatch)
            {
                num_qwords += 1 + (pBatch->iNumGouraudQuadsInBatch * NUM_POLY_REGS_GOURAUD_QUAD);
            }
            if (pBatch->iNumZSpritesInBatch)
            {
                num_qwords += 1 + (pBatch->iNumZSpritesInBatch * NUM_POLY_REGS_Z_SPRITE);
            }
            if (pBatch->iNumZQuadsInBatch)
            {
                num_qwords += 1 + (pBatch->iNumZQuadsInBatch * NUM_POLY_REGS_Z_QUAD);
            }

            // Update render stats.
            {
                // Quads will be tessellated to 2 tris. Count sprites as 1
                // tri, since we don't tessellate them and they're roughly
                // equivalent to render.
                u_int uNumTris = ((pBatch->iNumRotatedQuadsInBatch + pBatch->iNumGouraudQuadsInBatch + pBatch->iNumZQuadsInBatch) << 1)
                               + pBatch->iNumSpritesInBatch + pBatch->iNumZSpritesInBatch;

                Asura_PS2_Render::UpdateTriStats( uNumTris, 1 );
            }

            // Are we using a different texture to last time ?
            if ( bNewTexture )
            {
                Asura_PS2_TextureManagement::SetupTextureEnvironment( bUploadedTexture,
                                                                      ASURA_PS2_TEXREGFLAG_ALL,
                                                                      pBatch->iTexID,
                                                                      Asura_PS2_TextureManagement::BFR_AUTO,
                                                                      SCE_GS_LINEAR,
                                                                      SCE_GS_LINEAR_MIPMAP_NEAREST,
                                                                      true );
            }

            // Add a dma tag for all this stuff
            Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer(num_qwords+1);
            Asura_PS2_QW* pxOut = pxBase;

            pxOut->u32s[0] = PY_DMA_CNT | num_qwords;
            pxOut->u32s[1] = 0;
            pxOut->u32s[2] = SCE_VIF1_SET_NOP(0);
            pxOut->u32s[3] = SCE_VIF1_SET_DIRECT( num_qwords, false );
            pxOut++;

            // A new blend mode is needed
            if ( pBatch->eBlendMode != eCurrentBlendMode )
            {
                CreateRenderStateForQuads(pBatch, pxOut);
                eCurrentBlendMode = pBatch->eBlendMode;
            }

            // Draw Z-sorted sprites
            AddZTestPacket(pxOut, true);
            DrawZSprites(pBatch, pxOut, iTransX, iTransY);
            DrawZQuads(pBatch, pxOut, iTransX, iTransY);

            // Draw non-Z-sorted sprites
            AddZTestPacket(pxOut, false);
            DrawSprites(pBatch, pxOut, iTransX, iTransY);
            DrawRotatedQuads(pBatch, pxOut, iTransX, iTransY);
            DrawGouraudQuads(pBatch, pxOut, iTransX, iTransY);

            Asura_Assert( pxOut - pxBase == num_qwords + 1, "" );
        }
    }

    g_uNumQuadBatches = 0;

#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
    // Draw the line batches (if any)
    if( g_uNumLines )
    {
        // How much data are we sending ?
        const u_int uLineQwords =  1 + (g_uNumLines * 3);
        const u_int uTotalQwords =  2 + uLineQwords + Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS;
        Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer(uTotalQwords);
        const u_int uLastDmaTag = PY_DMA_CNT;
        Asura_PS2_QW* pxOut = pxBase;

        // Add a dma tag for the render state
        pxOut->u32s[0] = PY_DMA_CNT | Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS;
        pxOut->u32s[1] = 0;
        pxOut->u32s[2] = SCE_VIF1_SET_NOP(false);
        pxOut->u32s[3] = SCE_VIF1_SET_DIRECT( Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS, false );
        pxOut++;

        // set the pixel tests to off
        Asura_PlatformRenderStates::CreateDefaultRenderStatePacket( Asura_PlatformRenderStates::CONTEXT_1, pxOut, false, true );
        Asura_PlatformRenderStates::SetPixelTest( false, false );
        Asura_PlatformRenderStates::ForgetMyBuffer(pxOut);
        pxOut += Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS;

        // Add a dma tag for the lines
        pxOut->u32s[0] = uLastDmaTag | uLineQwords;
        pxOut->u32s[1] = 0;
        pxOut->u32s[2] = SCE_VIF1_SET_NOP(false);
        pxOut->u32s[3] = SCE_VIF1_SET_DIRECT( uLineQwords, false );
        pxOut++;

        // Add a gif tag for the lines
        pxOut->u64s[0] = SCE_GIF_SET_TAG( g_uNumLines,          // NLOOP - number of lines
                                          1,                    // EOP (End Of Packet)
                                          1,                    // PRE (PRIM field Enable)
                                          SCE_GS_SET_PRIM( SCE_GS_PRIM_LINESTRIP,   // PRIM (Type of drawing primitive)
                                                           0,                       // IIP (Flat Shading/Gouraud Shading)
                                                           0,                       // TME (Texture Mapping Enabled)
                                                           0,                       // FGE (Fogging Enabled)
                                                           0,                       // ABE (Alpha Blending Enabled)
                                                           0,                       // AA1 (1 Pass Anti-Aliasing Enabled)
                                                           0,                       // FST (Method of specifying Texture Coords)
                                                           0,                       // CTXT (Context)
                                                           0 ),                     // FIX (Fragment Value Control)
                                         0,                     // FLG (Data format, 0 = PACKED, 1 = REGLIST, 2 = IMAGE)
                                         3 );                   // NREG (Number of registers)
        pxOut->u64s[1] = (SCE_GS_XYZ2<<8) | (SCE_GS_XYZ2<<4) | (SCE_GS_RGBAQ << 0);
        pxOut++;

        for ( u_int u = 0; u<g_uNumLines; u++ )
        {
            const Line2D& xLine = g_pxLines[u];

            WriteRGBAQs( &xLine.uARGB, 1, pxOut, 3, false );

            WriteXYZs( xLine.afX, xLine.afY, 0.0f, 2, iTransX, iTransY, pxOut + 1, 1, false );

            pxOut += 3;
        }

    }
    g_uNumLines = 0;
#endif //#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
}

void Asura_PS2_2D_Graphics::CreateRenderStateForQuads( const QuadBatch* pBatch, Asura_PS2_QW*& pxOut )
{
    // set the pixel tests to off and set transluscency mode
    Asura_PlatformRenderStates::CreateDefaultRenderStatePacket( Asura_PlatformRenderStates::CONTEXT_1, pxOut, false, true );

    if (pBatch->eBlendMode != ASURA_TRANSLUCENCY_NORMAL)
    {
        Asura_PlatformRenderStates::SetTranslucencyMode(pBatch->eBlendMode);
    }

    Asura_PlatformRenderStates::SetPixelTest( false, false );

    // Disable the general z write mask. We'll use the usual alpah test trick
    // to selectively disable z write for primitives in AddZTestPacket().
    Asura_PlatformRenderStates::SetZBufferWriteEnabled( true );

    Asura_PlatformRenderStates::ForgetMyBuffer(pxOut);

    pxOut += Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS;
}

void Asura_PS2_2D_Graphics::DrawSprites( const QuadBatch* pBatch, Asura_PS2_QW*& pxOut, int iTransX, int iTransY )
{
    if( !pBatch->iNumSpritesInBatch ) { return; }

    const bool bTextured = ( ASURA_TEXTURE_NONE != pBatch->iTexID );

    {
        const u_int uPRIM = SCE_GS_SET_PRIM( SCE_GS_PRIM_SPRITE, // PRIM
                                             0,                  // IIP
                                             bTextured,          // TME
                                             false,              // FGE
                                             true,               // ABE
                                             false,              // AA1
                                             1,                  // FST
                                             0,                  // CTXT
                                             0 );                // FIX

        (reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( pBatch->iNumSpritesInBatch, // NLOOP
                                                             true,                       // EOP
                                                             true,                       // PRE
                                                             uPRIM,                      // PRIM
                                                             SCE_GIF_PACKED,             // FLG
                                                             NUM_POLY_REGS_SPRITE,       // NREG
                                                             SCE_GIF_PACKED_AD,          // REG0
                                                             SCE_GS_RGBAQ,               // REG1
                                                             SCE_GS_UV,                  // REG2
                                                             SCE_GS_XYZ2,                // REG3
                                                             SCE_GS_UV,                  // REG4
                                                             SCE_GS_XYZ2 );              // REG5
    }

    const float fTexWidth  = ( bTextured ? Asura_PS2_TextureManagement::GetTextureWidth(pBatch->iTexID)  : 0.0f );
    const float fTexHeight = ( bTextured ? Asura_PS2_TextureManagement::GetTextureHeight(pBatch->iTexID) : 0.0f );

    for (int j = 0; j < pBatch->iNumSpritesInBatch; j++)
    {
        const BatchSprite* pSprite = &pBatch->a_xSprites[j];

        // Ugly fix for the widescreen adjustment affecting full screen
        // 'sprites'. Arguably they should just go down a different path.
        bool bScaleForWidescreen = ( 0.0f != pSprite->X[0] || 512.0f != pSprite->X[1] );

        SetupClampAndUVs( pSprite->U, pSprite->V, 2, fTexWidth, fTexHeight, (pxOut + 2)->u32s, 8, pxOut );

        WriteRGBAQs( &pSprite->uARGB, 1, pxOut + 1, 1, bTextured );

        WriteXYZs( pSprite->X, pSprite->Y, 0.0f, 2, iTransX, iTransY, pxOut + 3, 2, bScaleForWidescreen );

        pxOut += NUM_POLY_REGS_SPRITE;
    }
}

void Asura_PS2_2D_Graphics::DrawRotatedQuads( const QuadBatch* pBatch, Asura_PS2_QW*& pxOut, int iTransX, int iTransY )
{
    if( !pBatch->iNumRotatedQuadsInBatch ) { return; }

    const bool bTextured = ( ASURA_TEXTURE_NONE != pBatch->iTexID );

    {
        const u_int uPRIM = SCE_GS_SET_PRIM( SCE_GS_PRIM_TRISTRIP, // PRIM
                                             0,                    // IIP
                                             bTextured,            // TME
                                             false,                // FGE
                                             true,                 // ABE
                                             false,                // AA1
                                             1,                    // FST
                                             0,                    // CTXT
                                             0 );                  // FIX

        (reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( pBatch->iNumRotatedQuadsInBatch, // NLOOP
                                                             true,                            // EOP
                                                             true,                            // PRE
                                                             uPRIM,                           // PRIM
                                                             SCE_GIF_PACKED,                  // FLG
                                                             NUM_POLY_REGS_ROTATED_QUAD,      // NREG
                                                             SCE_GIF_PACKED_AD,               // REG0
                                                             SCE_GS_RGBAQ,                    // REG1
                                                             SCE_GS_UV,                       // REG2
                                                             SCE_GS_XYZ2,                     // REG3
                                                             SCE_GS_UV,                       // REG4
                                                             SCE_GS_XYZ2,                     // REG5
                                                             SCE_GS_UV,                       // REG6
                                                             SCE_GS_XYZ2,                     // REG7
                                                             SCE_GS_UV,                       // REG8
                                                             SCE_GS_XYZ2 );                   // REG9
    }

    const float fTexWidth  = ( bTextured ? Asura_PS2_TextureManagement::GetTextureWidth(pBatch->iTexID)  : 0.0f );
    const float fTexHeight = ( bTextured ? Asura_PS2_TextureManagement::GetTextureHeight(pBatch->iTexID) : 0.0f );

    for (int j = 0; j < pBatch->iNumRotatedQuadsInBatch; j++)
    {
        const RotatedBatchQuad* pSprite = &pBatch->a_xRotatedQuads[j];

        SetupClampAndUVs( pSprite->U, pSprite->V, 4, fTexWidth, fTexHeight, (pxOut + 2)->u32s, 8, pxOut );

        WriteRGBAQs( &pSprite->uARGB, 1, pxOut + 1, 1, bTextured );

        WriteXYZs( pSprite->X, pSprite->Y, 0.0f, 4, iTransX, iTransY, pxOut + 3, 2 );

        pxOut += NUM_POLY_REGS_ROTATED_QUAD;
    }
}

void Asura_PS2_2D_Graphics::DrawGouraudQuads( const QuadBatch* pBatch, Asura_PS2_QW*& pxOut, int iTransX, int iTransY )
{
    if( !pBatch->iNumGouraudQuadsInBatch ) { return; }

    const bool bTextured = ( ASURA_TEXTURE_NONE != pBatch->iTexID );

    {
        const u_int uPRIM = SCE_GS_SET_PRIM( SCE_GS_PRIM_TRISTRIP, // PRIM
                                             1,                    // IIP
                                             bTextured,            // TME
                                             false,                // FGE
                                             true,                 // ABE
                                             false,                // AA1
                                             1,                    // FST
                                             0,                    // CTXT
                                             0 );                  // FIX

        (reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( pBatch->iNumGouraudQuadsInBatch, // NLOOP
                                                             true,                            // EOP
                                                             true,                            // PRE
                                                             uPRIM,                           // PRIM
                                                             SCE_GIF_PACKED,                  // FLG
                                                             NUM_POLY_REGS_GOURAUD_QUAD,      // NREG
                                                             SCE_GIF_PACKED_AD,               // REG0
                                                             SCE_GS_RGBAQ,                    // REG1
                                                             SCE_GS_UV,                       // REG2
                                                             SCE_GS_XYZ2,                     // REG3
                                                             SCE_GS_RGBAQ,                    // REG4
                                                             SCE_GS_UV,                       // REG5
                                                             SCE_GS_XYZ2,                     // REG6
                                                             SCE_GS_RGBAQ,                    // REG7
                                                             SCE_GS_UV,                       // REG8
                                                             SCE_GS_XYZ2,                     // REG9
                                                             SCE_GS_RGBAQ,                    // REG10
                                                             SCE_GS_UV,                       // REG11
                                                             SCE_GS_XYZ2 );                   // REG12
    }

    const float fTexWidth  = ( bTextured ? Asura_PS2_TextureManagement::GetTextureWidth(pBatch->iTexID)  : 0.0f );
    const float fTexHeight = ( bTextured ? Asura_PS2_TextureManagement::GetTextureHeight(pBatch->iTexID) : 0.0f );

    for (int j = 0; j < pBatch->iNumGouraudQuadsInBatch; j++)
    {
        const GouraudBatchQuad* pSprite = &pBatch->a_xGouraudQuads[j];

        SetupClampAndUVs( pSprite->U, pSprite->V, 4, fTexWidth, fTexHeight, (pxOut + 2)->u32s, 12, pxOut );

        WriteRGBAQs( pSprite->auARGBs, 4, pxOut + 1, 3, bTextured );

        WriteXYZs( pSprite->X, pSprite->Y, 0.0f, 4, iTransX, iTransY, pxOut + 3, 3 );

        pxOut += NUM_POLY_REGS_GOURAUD_QUAD;
    }
}

void Asura_PS2_2D_Graphics::AddZTestPacket(Asura_PS2_QW*& pxOut, bool bZTest)
{
    pxOut->u64s[0] = SCE_GIF_SET_TAG( 1, true, false, 0, SCE_GIF_PACKED, 1 );
    pxOut->u64s[1] = SCE_GIF_PACKED_AD;
    pxOut++;

    if( bZTest )
    {
        // Write to the z buffer, but only for bits that are a more or less opaque.
        pxOut->u64s[0] = SCE_GS_SET_TEST_1( true,                // ATE
                                            SCE_GS_ALPHA_GEQUAL,  // ATST
                                            124,                  // AREF
                                            SCE_GS_AFAIL_FB_ONLY, // AFAIL
                                            0,                    // DATE
                                            0,                    // DATM
                                            true,                 // ZTE
                                            SCE_GS_ZGEQUAL );     // ZTST
    }
    else
    {
        // Use usual alpha test trick to prevent non-z-tested primitives from
        // writing to the Z buffer.
        pxOut->u64s[0] = SCE_GS_SET_TEST_1( true,                 // ATE
                                            SCE_GS_ALPHA_NEVER,   // ATST
                                            0,                    // AREF
                                            SCE_GS_AFAIL_FB_ONLY, // AFAIL
                                            0,                    // DATE
                                            0,                    // DATM
                                            true,                 // ZTE
                                            SCE_GS_ZALWAYS );     // ZTST
    }
    pxOut->u64s[1] = SCE_GS_TEST_1;
    pxOut++;
}

void Asura_PS2_2D_Graphics::DrawZSprites( const QuadBatch* pBatch, Asura_PS2_QW*& pxOut, int iTransX, int iTransY )
{
    if( !pBatch->iNumZSpritesInBatch ) { return; }

    const bool bTextured = ( ASURA_TEXTURE_NONE != pBatch->iTexID );

    {
        const u_int uPRIM = SCE_GS_SET_PRIM( SCE_GS_PRIM_SPRITE, // PRIM
                                             0,                  // IIP
                                             bTextured,          // TME
                                             false,              // FGE
                                             true,               // ABE
                                             false,              // AA1
                                             1,                  // FST
                                             0,                  // CTXT
                                             0 );                // FIX

        (reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( pBatch->iNumZSpritesInBatch, // NLOOP
                                                             true,                        // EOP
                                                             true,                        // PRE
                                                             uPRIM,                       // PRIM
                                                             SCE_GIF_PACKED,              // FLG
                                                             NUM_POLY_REGS_Z_SPRITE,      // NREG
                                                             SCE_GIF_PACKED_AD,           // REG0
                                                             SCE_GS_RGBAQ,                // REG1
                                                             SCE_GS_UV,                   // REG2
                                                             SCE_GS_XYZ2,                 // REG3
                                                             SCE_GS_UV,                   // REG4
                                                             SCE_GS_XYZ2 );               // REG5
    }

    const float fTexWidth  = ( bTextured ? Asura_PS2_TextureManagement::GetTextureWidth(pBatch->iTexID)  : 0.0f );
    const float fTexHeight = ( bTextured ? Asura_PS2_TextureManagement::GetTextureHeight(pBatch->iTexID) : 0.0f );

    for (int j = 0; j < pBatch->iNumZSpritesInBatch; j++)
    {
        const ZSortedBatchSprite* pSprite = &pBatch->a_xZSprites[j];

        SetupClampAndUVs( pSprite->U, pSprite->V, 2, fTexWidth, fTexHeight, (pxOut + 2)->u32s, 8, pxOut );

        WriteRGBAQs( &pSprite->uARGB, 1, pxOut + 1, 1, bTextured );

        WriteXYZs( pSprite->X, pSprite->Y, CalculatePerspectiveScreenspaceZ(pSprite->fViewspaceZ), 2, iTransX, iTransY, pxOut + 3, 2 );

        pxOut += NUM_POLY_REGS_Z_SPRITE;
    }
}

void Asura_PS2_2D_Graphics::DrawZQuads( const QuadBatch* pxBatch, Asura_PS2_QW*& pxOut, int iTransX, int iTransY )
{
    if( !pxBatch->iNumZQuadsInBatch ) { return; }

    const bool bTextured = ( ASURA_TEXTURE_NONE != pxBatch->iTexID );

    {
        const u_int uPRIM = SCE_GS_SET_PRIM( SCE_GS_PRIM_TRISTRIP, // PRIM
                                             0,                    // IIP
                                             bTextured,            // TME
                                             false,                // FGE
                                             true,                 // ABE
                                             false,                // AA1
                                             1,                    // FST
                                             0,                    // CTXT
                                             0 );                  // FIX

        (reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( pxBatch->iNumZQuadsInBatch, // NLOOP
                                                             true,                       // EOP
                                                             true,                       // PRE
                                                             uPRIM,                      // PRIM
                                                             SCE_GIF_PACKED,             // FLG
                                                             NUM_POLY_REGS_Z_QUAD,       // NREG
                                                             SCE_GIF_PACKED_AD,          // REG0
                                                             SCE_GS_RGBAQ,               // REG1
                                                             SCE_GS_UV,                  // REG2
                                                             SCE_GS_XYZ2,                // REG3
                                                             SCE_GS_UV,                  // REG4
                                                             SCE_GS_XYZ2,                // REG5
                                                             SCE_GS_UV,                  // REG6
                                                             SCE_GS_XYZ2,                // REG7
                                                             SCE_GS_UV,                  // REG8
                                                             SCE_GS_XYZ2 );              // REG9
    }

    const float fTexWidth  = ( bTextured ? Asura_PS2_TextureManagement::GetTextureWidth(pxBatch->iTexID)  : 0.0f );
    const float fTexHeight = ( bTextured ? Asura_PS2_TextureManagement::GetTextureHeight(pxBatch->iTexID) : 0.0f );

    for (int j = 0; j < pxBatch->iNumZQuadsInBatch; j++)
    {
        const ZSortedBatchQuad * pxSprite = &pxBatch->a_xZQuads[j];

        SetupClampAndUVs( pxSprite->U, pxSprite->V, 4, fTexWidth, fTexHeight, (pxOut + 2)->u32s, 8, pxOut );

        WriteRGBAQs( &pxSprite->uARGB, 1, pxOut + 1, 1, bTextured );

        WriteXYZs( pxSprite->X, pxSprite->Y, CalculatePerspectiveScreenspaceZ(pxSprite->fViewspaceZ), 4, iTransX, iTransY, pxOut + 3, 2 );

        pxOut += NUM_POLY_REGS_Z_QUAD;
    }
}

#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
Asura_PS2_2D_Graphics::Line2D* Asura_PS2_2D_Graphics::GetFreeLine( void )
{
    Asura_Assert( g_uNumLines <= ASURA_PS2_MAX_2D_LINES_PER_FRAME, "" );
    if( g_uNumLines < ASURA_PS2_MAX_2D_LINES_PER_FRAME )
    {
        return &g_pxLines[g_uNumLines++];
    }
    else
    {
        if( ASURA_PS2_MAX_2D_LINES_PER_FRAME )
        {
            Asura_Assert( false, "No more space for lines.. increase line batch size ??" );
        }
        else
        {
            Asura_Assert( false, "No line batch space allocated." );
        }
        return NULL;
    }
}
#endif

void Asura_PS2_2D_Graphics::WriteRGBAQs( const u_int* puARGBs, u_int uNumARGBs,
                                         Asura_PS2_QW* pxOut, u_int uStride,
                                         bool bTextured )
{
    const u_int uMask = 0xFFu;
    for( ; uNumARGBs; --uNumARGBs )
    {
        u_int uARGB = *puARGBs++;
        
        u_int uR, uG, uB, uA;

        // Unpack.
        uB = uARGB & uMask;
        uARGB >>= 8u;
        uG = uARGB & uMask;
        uARGB >>= 8u;
        uR = uARGB & uMask;
        uARGB >>= 8u;
        uA = uARGB & uMask;

        // [0,255] -> [0,128]
        // (MP - 10.08.2005) Don't halve colours if graphic is untextured.
        ++uA;
        if( bTextured )
        {
            ++uR;
            ++uG;
            ++uB;
            uR >>= 1;
            uG >>= 1;
            uB >>= 1;
        }
        uA >>= 1;
        
        pxOut->u32s[0] = uR;
        pxOut->u32s[1] = uG;
        pxOut->u32s[2] = uB;
        pxOut->u32s[3] = uA;

        pxOut += uStride;
    }
}

void Asura_PS2_2D_Graphics::WriteXYZs( const float* pfXs, const float* pfYs, float fZ, u_int uNumCoordinateSets,
                                       int iTransX, int iTransY,
                                       Asura_PS2_QW* pxOut, u_int uStride,
                                       bool bScaleForWidescreen )
{
    // Currently we expect there to be either two or four coordinate sets,
    // for a sprite or a quad respectively.
    Asura_Assert( 2 == uNumCoordinateSets || 4 == uNumCoordinateSets, "" );

    u_int uNumADCs = uNumCoordinateSets >> 1;

    // To match FTOI4 on VU...
    fZ *= 16.0f;

    while( uNumCoordinateSets-- )
    {
        float fX = *pfXs++;
        float fY = *pfYs++;

        if( bScaleForWidescreen ) { fX = ScaleXForWidescreen( fX ); }

		// (MH - 17.05.2005) do anti-aliasing?
		const Asura_Vector_2& xOffset = Asura_Render::GetSubPixelOffset();
		fX += xOffset.x;
		fY += xOffset.y;

        fX *= 16.0f;
        fY *= 16.0f;

        pxOut->u32s[0] = iTransX + Asura_Maths::FloatToInt_Near( fX );
        pxOut->u32s[1] = iTransY + Asura_Maths::FloatToInt_Near( fY );
        pxOut->u32s[2] = Asura_Maths::FloatToInt( fZ );

        if( uNumADCs )
        {
            pxOut->u32s[3] = 0x8000u;
            --uNumADCs;
        }
        else
        {
            pxOut->u32s[3] = 0u;
        }

        pxOut += uStride;
    }
}

void Asura_PS2_2D_Graphics::SetupClampAndUVs( const float* pfSs, const float* pfTs, u_int uNumCoordinateSets,
                                              float fTexWidth, float fTexHeight,
                                              u_int* puUVOut, u_int uUVStride,
                                              Asura_PS2_QW* pxCLAMPOut )
{
    float fMinU = 1024.0f * 16.0f, fMinV = 1024.0f * 16.0f,
          fMaxU =    0.0f * 16.0f, fMaxV =    0.0f * 16.0f;

    for( ; uNumCoordinateSets; --uNumCoordinateSets )
    {
        float fS = *pfSs++;
        float fT = *pfTs++;

        float fU = fS * fTexWidth;
        float fV = fT * fTexHeight;

        // Check coordinates are valid. We're directly translating them to
        // UVs here so they need to fit in that register's 0:10:4 format.
        Asura_Assert( 0.0f <= fU && fU < 1024.0f, "" );
        Asura_Assert( 0.0f <= fV && fV < 1024.0f, "" );

        fMinU = Asura_Maths::Min( fMinU, fU ); fMinV = Asura_Maths::Min( fMinV, fV );
        fMaxU = Asura_Maths::Max( fMaxU, fU ); fMaxV = Asura_Maths::Max( fMaxV, fV );

        *(puUVOut + 0) = Asura_Maths::FloatToInt_Near( fU * 16.0f );
        *(puUVOut + 1) = Asura_Maths::FloatToInt_Near( fV * 16.0f );

        puUVOut += uUVStride;
    }

    u_long uCLAMP = 0;

    const u_long uMinU = Asura_Maths::FloatToInt_Chop( fMinU );
    const u_long uMaxU = Asura_Maths::FloatToInt_Chop( fMaxU - 1.0f );
    if( fMaxU <= fTexWidth && uMinU < uMaxU )
    {
        uCLAMP |= (SCE_GS_REGION_CLAMP << GS_CLAMP_WMS_O) | (uMinU << GS_CLAMP_MINU_O) | (uMaxU << GS_CLAMP_MAXU_O);
    }
    else
    {
        uCLAMP |= SCE_GS_REPEAT << GS_CLAMP_WMS_O;
    }

    const u_long uMinV = Asura_Maths::FloatToInt_Chop( fMinV );
    const u_long uMaxV = Asura_Maths::FloatToInt_Chop( fMaxV - 1.0f );
    if( fMaxV <= fTexHeight && uMinV < uMaxV )
    {
        uCLAMP |= (SCE_GS_REGION_CLAMP << GS_CLAMP_WMT_O) | (uMinV << GS_CLAMP_MINV_O) | (uMaxV << GS_CLAMP_MAXV_O);
    }
    else
    {
        uCLAMP |= SCE_GS_REPEAT << GS_CLAMP_WMT_O;
    }

    pxCLAMPOut->u64s[0] = uCLAMP;
    pxCLAMPOut->u64s[1] = SCE_GS_CLAMP_1;
}

u_int Asura_PS2_2D_Graphics::GetNumQWsForSpriteStrips( u_int uWidth, bool bNeedUVs, u_int uStripWidth, u_int uLog2StripWidth, u_int uStride )
{
    Asura_Assert( uStripWidth == (1u << uLog2StripWidth), "" );
    u_int uNumStrips = (uWidth + (uStripWidth - 1)) >> uLog2StripWidth;
    
    if( uStride )
    {
        uNumStrips = (uNumStrips + uStride) / (1 + uStride);
    }
    
    return( bNeedUVs ? uNumStrips << 2 : uNumStrips << 1 );
}

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION     Asura_PS2_2D_Graphics::WriteSpriteStrips().
// PARAMETERS   pxOut           - Start address of output buffer.
//              uX              - TL x coordinate (in primitive system.)
//              uY              -  " y              "
//              uWidth          - Width in pixels.
//              uHeight         - Height    "
//              bNeedUVs        - Write UVs?
//              uU0             - TL u coordinate.
//              uV0             -  " v     "
//              uDU             - Width in texels.
//              uDV             - Height    "
//              bCentreUVs      - Offset UVs by 0.5?
//              uStripWidth     - Width of each strip.
//              uLog2StripWidth - log2(uStripWidth)
//              uStride         - Stride in units of strips. This many strips
//                                are then skipped after each rendered one.
//				uSpriteZ		- Z value of sprite, used for Z-buffered 2d sprites.
//				uUOffset		- value added to all U coordinates (in GS coords)
//				vUOffset		-   "     "   "   "  V      "
// RETURNS      Pointer to the QW following the last one output.
// DESCRIPTION  Draws a sprite over the specified region in strips. This is
//              useful in some circumstances since it allows the GS to
//              rasterise at peak speed.
// AUTHOR       MP - 01.04.2004.
//
//////////////////////////////////////////////////////////////////////////////
Asura_PS2_QW* Asura_PS2_2D_Graphics::WriteSpriteStrips( Asura_PS2_QW* pxOut,
                                                        u_int uX, u_int uY, u_int uWidth, u_int uHeight,
                                                        bool bNeedUVs, u_int uU0, u_int uV0, u_int uDU, u_int uDV, bool bCentreUVs,
                                                        u_int uStripWidth, u_int uLog2StripWidth, u_int uStride,
                                                        u_int uSpriteZ, u_int uUOffset, u_int uVOffset )
{
    Asura_Assert( uStripWidth == (1u << uLog2StripWidth), "" );
    u_int uNumStrips = (uWidth + (uStripWidth - 1)) >> uLog2StripWidth;

    uX      <<= 4;
    uY      <<= 4;
    uWidth  <<= 4;
    uHeight <<= 4;
    uU0     <<= 4;
    uV0     <<= 4;
    uDU     <<= 4;
    uDV     <<= 4;

    if( bCentreUVs )
    {
        uU0 += 8;
        uV0 += 8;
    }

	uU0 += uUOffset;
	uV0 += uVOffset;

    const u_int uBRX = uX  + uWidth;
    const u_int uBRY = uY  + uHeight;
    const u_int uBRU = uU0 + uDU;
    const u_int uBRV = uV0 + uDV;

    u_int uDX = 32 << 4;
    if( uNumStrips > 1 )
    {
        uDX = (uWidth + (uNumStrips - 1)) / uNumStrips;

        if( bNeedUVs )
        {
            uDU = (uDU + (uNumStrips - 1)) / uNumStrips;
        }
    }

    if( uStride )
    {
        uNumStrips = (uNumStrips + uStride) / (1 + uStride);
    }

    while( uNumStrips-- )
    {
        if( bNeedUVs )
        {
            pxOut->u64s[0] = SCE_GS_SET_UV( uU0, uV0 );
            pxOut->u64s[1] = SCE_GS_UV;
            ++pxOut;
        }
        
        pxOut->u64s[0] = SCE_GS_SET_XYZ2( uX, uY, uSpriteZ );
        pxOut->u64s[1] = SCE_GS_XYZ2;
        ++pxOut;

        if( bNeedUVs )
        {
            uU0 = ( uNumStrips ? uU0 + uDU : uBRU );
            pxOut->u64s[0] = SCE_GS_SET_UV( uU0, uBRV );
            pxOut->u64s[1] = SCE_GS_UV;
            ++pxOut;
        }

        uX = ( uNumStrips ? uX + uDX : uBRX );
        pxOut->u64s[0] = SCE_GS_SET_XYZ2( uX, uBRY, uSpriteZ );
        pxOut->u64s[1] = SCE_GS_XYZ2;
        ++pxOut;

        if( uStride )
        {
            uX  += uDX * uStride;
            uU0 += uDU * uStride;
        }
    }

    return pxOut;
}
// End function Asura_PS2_2D_Graphics::WriteSpriteStrips().



//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION     Asura_PS2_2D_Graphics::WriteSpriteStripsWithSTs().
// PARAMETERS   pxOut           - Start address of output buffer.
//              uX              - TL x coordinate (in primitive system.)
//              uY              -  " y              "
//              uWidth          - Width in pixels.
//              uHeight         - Height    "
//              fS              - TL S coordinate.
//              fT              -  " T     "
//              fTextureW       - Width (in ST coords)
//              fTextureH       - Height    "
//              bCentreUVs      - Offset UVs by 0.5?
//              uStripWidth     - Width of each strip.
//              uLog2StripWidth - log2(uStripWidth)
//              uStride         - Stride in units of strips. This many strips
//                                are then skipped after each rendered one.
//				uSpriteZ		- Z value of sprite, used for Z-buffered 2d sprites.
//				uUOffset		- value added to all U coordinates (in GS coords)
//				vUOffset		-   "     "   "   "  V      "
// RETURNS      Pointer to the QW following the last one output.
// DESCRIPTION  Draws a sprite over the specified region in strips. This is
//              useful in some circumstances since it allows the GS to
//              rasterise at peak speed. Using STs allows negative UV offsets to be used.
// AUTHOR       MH - 07.10.2004 - based on MP's WriteSpriteStrips()
//
//////////////////////////////////////////////////////////////////////////////
Asura_PS2_QW* Asura_PS2_2D_Graphics::WriteSpriteStripsWithSTs( Asura_PS2_QW* pxOut,
															   u_int uX, u_int uY, u_int uWidth, u_int uHeight,
															   float fS, float fT, float fTextureW, float fTextureH, 
															   u_int uTextureWidthTexels, u_int uTextureHeightTexels,
															   bool bCentreSTs /* = true */, 
															   u_int uStripWidth /* = 32 */, u_int uLog2StripWidth /* = 5 */, 
															   u_int uStride /* = 0 */, u_int uSpriteZ /* = 0 */ )
{
    Asura_Assert( uStripWidth == (1u << uLog2StripWidth), "" );
    u_int uNumStrips = (uWidth + (uStripWidth - 1)) >> uLog2StripWidth;
	const float fNumSourceStrips = static_cast<float>(static_cast<int>(uNumStrips));
	// note that all u_int->float casts are done using an intermediate cast to int as this 
	// results in fewer instructions with the current PS2 compiler.

    // convert XYs to Gs coords
	uX      <<= 4;
    uY      <<= 4;
    uWidth  <<= 4;
    uHeight <<= 4;

	// convert STs for non-power-of-2 textures
    const float fTW  = static_cast<float>(static_cast<int>(1 << Asura_Maths::GetCeilLog2( uTextureWidthTexels )));
    const float fTH  = static_cast<float>(static_cast<int>(1 << Asura_Maths::GetCeilLog2( uTextureHeightTexels )));
	const float fScaleS = static_cast<float>(static_cast<int>(uTextureWidthTexels)) / fTW;
	const float fScaleT = static_cast<float>(static_cast<int>(uTextureHeightTexels)) / fTH;
	fTextureW *= fScaleS;
	fTextureH *= fScaleT;

	if (bCentreSTs)
	{
		fS += 0.5f / fTW;
		fT += 0.5f / fTH;
	}
	
    const u_int uBRX = uX  + uWidth;
    const u_int uBRY = uY  + uHeight;
    const float fTLS = fS;
	const float fTLT = fT;
	const float fBRS = fS + fTextureW;
    const float fBRT = fT + fTextureH;

    u_int uDX = 32 << 4;
    if( uNumStrips > 1 )
    {
        uDX = (uWidth + (uNumStrips - 1)) / uNumStrips;
    }

    if( uStride )
    {
        uNumStrips = (uNumStrips + uStride) / (1 + uStride);
    }

	u_int uCurSourceStrip = 0;
	float fPrevSRight = fTLS;
    while( uNumStrips-- )
    {
        pxOut->u64s[0] = SCE_GS_SET_ST( *reinterpret_cast<const u_int*>(&fPrevSRight), *reinterpret_cast<const u_int*>(&fTLT) );
        pxOut->u64s[1] = SCE_GS_ST;
        ++pxOut;
        
        pxOut->u64s[0] = SCE_GS_SET_XYZ2( uX, uY, uSpriteZ );
        pxOut->u64s[1] = SCE_GS_XYZ2;
        ++pxOut;

        fPrevSRight = fTLS + fTextureW  * (static_cast<float>(static_cast<int>(++uCurSourceStrip)) / fNumSourceStrips);
        pxOut->u64s[0] = SCE_GS_SET_ST( *reinterpret_cast<const u_int*>(&fPrevSRight), *reinterpret_cast<const u_int*>(&fBRT) );
        pxOut->u64s[1] = SCE_GS_ST;
        ++pxOut;

        uX = ( uNumStrips ? uX + uDX : uBRX );
        pxOut->u64s[0] = SCE_GS_SET_XYZ2( uX, uBRY, uSpriteZ );
        pxOut->u64s[1] = SCE_GS_XYZ2;
        ++pxOut;

		if( uStride )
        {
            uX  += uDX * uStride;
			uCurSourceStrip += uStride;
            fPrevSRight = fTLS + fTextureW  * (static_cast<float>(static_cast<int>(uCurSourceStrip)) / fNumSourceStrips);
        }
    }

    return pxOut;
}
// End function Asura_PS2_2D_Graphics::WriteSpriteStripsWithSTs().

u_int Asura_PS2_2D_Graphics::GetNumQWsForSpriteStripsWithSTs( u_int uWidth, u_int uStripWidth, u_int uLog2StripWidth, u_int uStride )
{
	return GetNumQWsForSpriteStrips(uWidth, true, uStripWidth, uLog2StripWidth, uStride);
}

float Asura_PS2_2D_Graphics::CentreCoordinateOnTexel( float fCoord, u_int uDimensionSize )
{
	return fCoord + 0.5f / static_cast<float>(static_cast<int>(uDimensionSize));
}
