Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

#include "Graphics.h"
#include "Image.h"
#include "Font.h"
#include "DDImage.h"
#include "MemoryImage.h"
#include "Rect.h"
#include "Debug.h"
#include "SexyMatrix.h"
#include <math.h>

using namespace Sexy;

Image GraphicsState::mStaticImage;
const Point* Graphics::mPFPoints;

//////////////////////////////////////////////////////////////////////////

void GraphicsState::CopyStateFrom(const GraphicsState* theState)
{
        mDestImage = theState->mDestImage;
        mTransX = theState->mTransX;
        mTransY = theState->mTransY;
        mClipRect = theState->mClipRect;
        mFont = theState->mFont;
        mColor = theState->mColor;
        mDrawMode = theState->mDrawMode;
        mColorizeImages = theState->mColorizeImages;
        mFastStretch = theState->mFastStretch;
        mWriteColoredString = theState->mWriteColoredString;
        mLinearBlend = theState->mLinearBlend;
        mScaleX = theState->mScaleX;
        mScaleY = theState->mScaleY;
        mScaleOrigX = theState->mScaleOrigX;
        mScaleOrigY = theState->mScaleOrigY;
        mIs3D = theState->mIs3D;
}

//////////////////////////////////////////////////////////////////////////

Graphics::Graphics(const Graphics& theGraphics)
{
        CopyStateFrom(&theGraphics);
}

Graphics::Graphics(Image* theDestImage)
{
        mTransX = 0;
        mTransY = 0;
        mScaleX = 1;
        mScaleY = 1;
        mScaleOrigX = 0;
        mScaleOrigY = 0;
        mFont = NULL;
        mDestImage = theDestImage;
        mDrawMode = DRAWMODE_NORMAL;
        mColorizeImages = false;
        mFastStretch = false;
        mWriteColoredString = true;
        mLinearBlend = false;

        if (mDestImage == NULL)
        {
                mDestImage = &mStaticImage;
                mIs3D = false;
        }
        else
        {
                mIs3D = DDImage::Check3D(theDestImage);
        }

        mClipRect = Rect(0, 0, mDestImage->GetWidth(), mDestImage->GetHeight());
}

Graphics::~Graphics()
{
}

void Graphics::PushState()
{
        mStateStack.push_back(GraphicsState());
        mStateStack.back().CopyStateFrom(this);
}

void Graphics::PopState()
{
        DBG_ASSERTE(mStateStack.size() > 0);
        if (mStateStack.size() > 0)
        {
                CopyStateFrom(&mStateStack.back());
                mStateStack.pop_back();
        }
}

Graphics* Graphics::Create()
{      
        return new Graphics(*this);
}

Sexy::Font* Graphics::GetFont()
{
        return mFont;
}

void Graphics::SetFont(Sexy::Font* theFont)
{
        mFont = theFont;
}

void Graphics::SetColor(const Color& theColor)
{
        mColor = theColor;
}

const Color& Graphics::GetColor()
{
        return mColor;
}

void Graphics::SetDrawMode(int theDrawMode)
{
        mDrawMode = theDrawMode;
}

int Graphics::GetDrawMode()
{
        return mDrawMode;
}

void Graphics::SetColorizeImages(bool colorizeImages)
{
        mColorizeImages = colorizeImages;
}

bool Graphics::GetColorizeImages()
{
        return mColorizeImages;
}

void Graphics::SetFastStretch(bool fastStretch)
{
        mFastStretch = fastStretch;
}

bool Graphics::GetFastStretch()
{
        return mFastStretch;
}

void Graphics::SetLinearBlend(bool linear)
{
        mLinearBlend = linear;
}

bool Graphics::GetLinearBlend()
{
        return mLinearBlend;
}

void Graphics::ClearRect(int theX, int theY, int theWidth, int theHeight)
{
        Rect aDestRect = Rect(theX + mTransX, theY + mTransY, theWidth, theHeight).Intersection(mClipRect);
        mDestImage->ClearRect(aDestRect);
}

void Graphics::ClearRect(const Rect& theRect)
{
        ClearRect(theRect.mX, theRect.mY, theRect.mWidth, theRect.mHeight);
}

void Graphics::FillRect(int theX, int theY, int theWidth, int theHeight)
{
        if (mColor.mAlpha == 0)
                return;

        Rect aDestRect = Rect(theX + mTransX, theY + mTransY, theWidth, theHeight).Intersection(mClipRect);
        mDestImage->FillRect(aDestRect, mColor, mDrawMode);
}

void Graphics::FillRect(const Rect& theRect)
{
        FillRect(theRect.mX, theRect.mY, theRect.mWidth, theRect.mHeight);
}

void Graphics::DrawRect(int theX, int theY, int theWidth, int theHeight)
{
        if (mColor.mAlpha == 0)
                return;

        Rect aDestRect = Rect(theX + mTransX, theY + mTransY, theWidth, theHeight);    
        Rect aFullDestRect = Rect(theX + mTransX, theY + mTransY, theWidth + 1, theHeight + 1);
        Rect aFullClippedRect = aFullDestRect.Intersection(mClipRect);

        if (aFullDestRect == aFullClippedRect)
        {              
                mDestImage->DrawRect(aDestRect, mColor, mDrawMode);
        }
        else
        {
                FillRect(theX, theY, theWidth + 1, 1);
                FillRect(theX, theY + theHeight, theWidth + 1, 1);
                FillRect(theX, theY + 1, 1, theHeight - 1);
                FillRect(theX + theWidth, theY + 1, 1, theHeight - 1);

                /*if (aClippedRect.mX == aDestRect.mX)
                        mDestImage->FillRect(Rect(aClippedRect.mX, aClippedRect.mY, 1, aClippedRect.mHeight), mColor, mDrawMode);
                if (aClippedRect.mY == aDestRect.mY)
                        mDestImage->FillRect(Rect(aClippedRect.mX, aClippedRect.mY, aClippedRect.mWidth, 1), mColor, mDrawMode);
                if (aClippedRect.mX + aClippedRect.mWidth == aDestRect.mX + aDestRect.mWidth)
                        mDestImage->FillRect(Rect(aClippedRect.mX + aClippedRect.mWidth, aClippedRect.mY, 1, aClippedRect.mHeight), mColor, mDrawMode);
                if (aClippedRect.mY + aClippedRect.mHeight == aDestRect.mY + aDestRect.mHeight)
                        mDestImage->FillRect(Rect(aClippedRect.mX, aClippedRect.mY + aClippedRect.mHeight, aClippedRect.mWidth, 1), mColor, mDrawMode);*/

        }
}

void Graphics::DrawRect(const Rect& theRect)
{
        DrawRect(theRect.mX, theRect.mY, theRect.mWidth, theRect.mHeight);
}

int Graphics::PFCompareInd(const void* u, const void* v)
{
        return mPFPoints[*((int*) u)].mY <= mPFPoints[*((int*) v)].mY ? -1 : 1;
}

int Graphics::PFCompareActive(const void* u, const void* v)
{
        return ((Edge*) u)->mX <= ((Edge*) v)->mX ? -1 : 1;
}

void Graphics::PFDelete(int i) // remove edge i from active list
{
    int j;

    for (j=0; j<mPFNumActiveEdges && mPFActiveEdgeList[j].i!=i; j++);    
        if (j>=mPFNumActiveEdges) return;       /* edge not in active list; happens at aMinY*/
   
        mPFNumActiveEdges--;
    memcpy(&mPFActiveEdgeList[j], &mPFActiveEdgeList[j+1], (mPFNumActiveEdges-j)*sizeof mPFActiveEdgeList[0]);
}

void Graphics::PFInsert(int i, int y) // append edge i to end of active list
{
    int j;
    double dx;
    const Point *p, *q;

    j = i<mPFNumVertices-1 ? i+1 : 0;
    if (mPFPoints[i].mY < mPFPoints[j].mY)
        {
                p = &mPFPoints[i];
                q = &mPFPoints[j];
        }
    else                   
        {
                p = &mPFPoints[j];
                q = &mPFPoints[i];
        }
    /* initialize x position at intersection of edge with scanline y */
    mPFActiveEdgeList[mPFNumActiveEdges].mDX = dx = (q->mX - p->mX)/(double) (q->mY - p->mY);
    mPFActiveEdgeList[mPFNumActiveEdges].mX = dx*(y+0.5 - p->mY - mTransY) + p->mX + mTransX;
    mPFActiveEdgeList[mPFNumActiveEdges].i = i;
        mPFActiveEdgeList[mPFNumActiveEdges].b = p->mY - 1.0/dx * p->mX;
    mPFNumActiveEdges++;
}

void Graphics::PolyFill(const Point *theVertexList, int theNumVertices, bool convex)
{
        if (convex && mDestImage->PolyFill3D(theVertexList,theNumVertices,&mClipRect,mColor,mDrawMode,mTransX,mTransY,convex))
                return;

        Span aSpans[MAX_TEMP_SPANS];
        int aSpanPos = 0;

    int k, y0, y1, y, i, j, xl, xr;
    int *ind;           /* list of vertex indices, sorted by mPFPoints[ind[j]].y */            

        int aMinX = mClipRect.mX;
        int aMaxX = mClipRect.mX + mClipRect.mWidth - 1;
        int aMinY = mClipRect.mY;
        int aMaxY = mClipRect.mY + mClipRect.mHeight - 1;

    mPFNumVertices = theNumVertices;
    mPFPoints = theVertexList;
   
        if (mPFNumVertices<=0) return;

    ind = new int[mPFNumVertices];
    mPFActiveEdgeList = new Edge[mPFNumVertices];

    /* create y-sorted array of indices ind[k] into vertex list */
    for (k=0; k<mPFNumVertices; k++)
                ind[k] = k;
    qsort(ind, mPFNumVertices, sizeof ind[0], PFCompareInd);    /* sort ind by mPFPoints[ind[k]].y */

    mPFNumActiveEdges = 0;                              /* start with empty active list */
    k = 0;                              /* ind[k] is next vertex to process */
    y0 = (int) max(aMinY, ceil(mPFPoints[ind[0]].mY-0.5 + mTransY));            /* ymin of polygon */
    y1 = (int) min(aMaxY, floor(mPFPoints[ind[mPFNumVertices-1]].mY-0.5 + mTransY));    /* ymax of polygon */

    for (y=y0; y<=y1; y++)
        {
                // step through scanlines
                // scanline y is at y+.5 in continuous coordinates

                // check vertices between previous scanline and current one, if any
                for (; (k < mPFNumVertices) && (mPFPoints[ind[k]].mY + mTransY <= y + 0.5); k++)
                {
                        // to simplify, if mPFPoints.mY=y+.5, pretend it's above
                        // invariant: y-.5 < mPFPoints[i].mY <= y+.5
                        i = ind[k];                            
                        // insert or delete edges before and after vertex i (i-1 to i,
                        // and i to i+1) from active list if they cross scanline y                       

                        j = i>0 ? i-1 : mPFNumVertices-1;       // vertex previous to i
                        if (mPFPoints[j].mY + mTransY <= y-0.5) // old edge, remove from active list
                                PFDelete(j);
                        else if (mPFPoints[j].mY + mTransY > y+0.5)     // new edge, add to active list
                                PFInsert(j, y);

                        j = i<mPFNumVertices-1 ? i+1 : 0;       // vertex next after i
                        if (mPFPoints[j].mY + mTransY <= y-0.5) // old edge, remove from active list
                                PFDelete(i);
                        else if (mPFPoints[j].mY + mTransY > y+0.5)     // new edge, add to active list
                                PFInsert(i, y);
                }

                // sort active edge list by active[j].mX
                qsort(mPFActiveEdgeList, mPFNumActiveEdges, sizeof mPFActiveEdgeList[0], PFCompareActive);

                // draw horizontal segments for scanline y
                for (j = 0; j < mPFNumActiveEdges; j += 2)
                {       // draw horizontal segments
                        // span 'tween j & j+1 is inside, span tween j+1 & j+2 is outside
                        xl = (int) ceil(mPFActiveEdgeList[j].mX-0.5);           // left end of span
                        if (xl<aMinX)
                                xl = aMinX;
                        xr = (int) floor(mPFActiveEdgeList[j+1].mX-0.5);        // right end of span
                        if (xr>aMaxX)
                                xr = aMaxX;
                       
                        if ((xl <= xr) && (aSpanPos < MAX_TEMP_SPANS))
                        {
                                Span* aSpan = &aSpans[aSpanPos++];
                                aSpan->mY = y;
                                aSpan->mX = xl;
                                aSpan->mWidth = xr - xl + 1;
                        }                      
                       
                        mPFActiveEdgeList[j].mX += mPFActiveEdgeList[j].mDX;    // increment edge coords
                        mPFActiveEdgeList[j+1].mX += mPFActiveEdgeList[j+1].mDX;
                }
        }

        mDestImage->FillScanLines(aSpans, aSpanPos, mColor, mDrawMode);

        delete ind;
        delete mPFActiveEdgeList;
}

void Graphics::PolyFillAA(const Point *theVertexList, int theNumVertices, bool convex)
{
        if (convex && mDestImage->PolyFill3D(theVertexList,theNumVertices,&mClipRect,mColor,mDrawMode,mTransX,mTransY,convex))
                return;

        int i;

        Span aSpans[MAX_TEMP_SPANS];
        int aSpanPos = 0;

        static BYTE aCoverageBuffer[256*256];
        int aCoverWidth = 256, aCoverHeight = 256;
        int aCoverLeft, aCoverRight, aCoverTop, aCoverBottom;

        for (i = 0; i < theNumVertices; ++i)
        {
                const Point* aPt = &theVertexList[i];
                if (i == 0)
                {
                        aCoverLeft = aCoverRight = aPt->mX;
                        aCoverTop = aCoverBottom = aPt->mY;
                }
                else
                {
                        aCoverLeft = min(aCoverLeft, aPt->mX);
                        aCoverRight = max(aCoverRight, aPt->mX);
                        aCoverTop = min(aCoverTop, aPt->mY);
                        aCoverBottom = max(aCoverBottom, aPt->mY);
                }
        }
        BYTE* coverPtr = aCoverageBuffer;
        if ((aCoverRight-aCoverLeft+1) > aCoverWidth || (aCoverBottom-aCoverTop+1) > aCoverHeight)
        {
                aCoverWidth = aCoverRight-aCoverLeft+1;
                aCoverHeight = aCoverBottom-aCoverTop+1;
                coverPtr = new BYTE[aCoverWidth*aCoverHeight];
        }
        memset(coverPtr, 0, aCoverWidth*aCoverHeight);

    int k, y0, y1, y, j, xl, xr;
    int *ind;           /* list of vertex indices, sorted by mPFPoints[ind[j]].y */            

        int aMinX = mClipRect.mX;
        int aMaxX = mClipRect.mX + mClipRect.mWidth - 1;
        int aMinY = mClipRect.mY;
        int aMaxY = mClipRect.mY + mClipRect.mHeight - 1;

    mPFNumVertices = theNumVertices;
    mPFPoints = theVertexList;
   
        if (mPFNumVertices<=0) return;

    ind = new int[mPFNumVertices];
    mPFActiveEdgeList = new Edge[mPFNumVertices];

    /* create y-sorted array of indices ind[k] into vertex list */
    for (k=0; k<mPFNumVertices; k++)
                ind[k] = k;
    qsort(ind, mPFNumVertices, sizeof ind[0], PFCompareInd);    /* sort ind by mPFPoints[ind[k]].y */

    mPFNumActiveEdges = 0;                              /* start with empty active list */
    k = 0;                              /* ind[k] is next vertex to process */
    y0 = (int) max(aMinY, ceil(mPFPoints[ind[0]].mY-0.5 + mTransY));            /* ymin of polygon */
    y1 = (int) min(aMaxY, floor(mPFPoints[ind[mPFNumVertices-1]].mY-0.5 + mTransY));    /* ymax of polygon */

    for (y=y0; y<=y1; y++)
        {
                // step through scanlines
                // scanline y is at y+.5 in continuous coordinates

                // check vertices between previous scanline and current one, if any
                for (; (k < mPFNumVertices) && (mPFPoints[ind[k]].mY + mTransY <= y + 0.5); k++)
                {
                        // to simplify, if mPFPoints.mY=y+.5, pretend it's above
                        // invariant: y-.5 < mPFPoints[i].mY <= y+.5
                        i = ind[k];                            
                        // insert or delete edges before and after vertex i (i-1 to i,
                        // and i to i+1) from active list if they cross scanline y                       

                        j = i>0 ? i-1 : mPFNumVertices-1;       // vertex previous to i
                        if (mPFPoints[j].mY + mTransY <= y-0.5) // old edge, remove from active list
                                PFDelete(j);
                        else if (mPFPoints[j].mY + mTransY > y+0.5)     // new edge, add to active list
                                PFInsert(j, y);

                        j = i<mPFNumVertices-1 ? i+1 : 0;       // vertex next after i
                        if (mPFPoints[j].mY + mTransY <= y-0.5) // old edge, remove from active list
                                PFDelete(i);
                        else if (mPFPoints[j].mY + mTransY > y+0.5)     // new edge, add to active list
                                PFInsert(i, y);
                }

                // sort active edge list by active[j].mX
                qsort(mPFActiveEdgeList, mPFNumActiveEdges, sizeof mPFActiveEdgeList[0], PFCompareActive);

                // draw horizontal segments for scanline y
                for (j = 0; j < mPFNumActiveEdges; j += 2)
                {       // draw horizontal segments
                        // span 'tween j & j+1 is inside, span tween j+1 & j+2 is outside
                        xl = (int) ceil(mPFActiveEdgeList[j].mX-0.5);           // left end of span
                        int lErr = int((fabs((mPFActiveEdgeList[j].mX-0.5) - xl)) * 255);
                        if (xl<aMinX)
                        {
                                xl = aMinX;
                                lErr = 255;
                        }
                        xr = (int) floor(mPFActiveEdgeList[j+1].mX-0.5);        // right end of span
                        int rErr = int((fabs((mPFActiveEdgeList[j+1].mX-0.5) - xr)) * 255);
                        if (xr>aMaxX)
                        {
                                xr = aMaxX;
                                rErr = 255;
                        }
                       
                        if ((xl <= xr) && (aSpanPos < MAX_TEMP_SPANS))
                        {
                                Span* aSpan = &aSpans[aSpanPos++];
                                aSpan->mY = y;
                                aSpan->mX = xl;
                                aSpan->mWidth = xr - xl + 1;

                                BYTE* coverRow = coverPtr + (y - aCoverTop) * aCoverWidth;
                                if (xr == xl)
                                {
                                        coverRow[xl-aCoverLeft] = min(255, coverRow[xl-aCoverLeft] + ((lErr*rErr)>>8));
                                }
                                else
                                {
                                        if (fabs(mPFActiveEdgeList[j].mDX) > 1.0f) // mostly horizontal on the left edge
                                        {
                                                double m = 1.0 / mPFActiveEdgeList[j].mDX,
                                                                b = mPFActiveEdgeList[j].b,
                                                                c = fabs(mPFActiveEdgeList[j].mDX);
                                                do
                                                {
                                                        double _y =     m * xl + b;
                                                        lErr = min(255, int(fabs((_y) - y - .5) * 255));
                                                        coverRow[xl-aCoverLeft] = min(255, coverRow[xl-aCoverLeft] + lErr);
                                                        xl++;
                                                        c -= 1.0;
                                                } while (xl <= xr && c > 0);
                                        }
                                        else
                                        {
                                                coverRow[xl-aCoverLeft] = min(255, coverRow[xl-aCoverLeft] + lErr);
                                                xl++;
                                        }

                                        if (fabs(mPFActiveEdgeList[j+1].mDX) > 1.0f) // mostly horizontal on the right edge
                                        {
                                                double m = 1.0 / mPFActiveEdgeList[j+1].mDX,
                                                                b = mPFActiveEdgeList[j+1].b,
                                                                c = fabs(mPFActiveEdgeList[j+1].mDX);
                                                do
                                                {
                                                        double _y =     m * xr + b;
                                                        rErr = min(255, int(fabs((_y) - y - .5) * 255));
                                                        coverRow[xr-aCoverLeft] = min(255, coverRow[xr-aCoverLeft] + rErr);
                                                        xr--;
                                                        c -= 1.0;
                                                } while (xr >= xl && c > 0);
                                        }
                                        else
                                        {
                                                coverRow[xr-aCoverLeft] = min(255, coverRow[xr-aCoverLeft] + rErr);
                                                xr--;
                                        }

                                        if (xl <= xr)
                                                memset(&coverRow[xl-aCoverLeft], 255, xr-xl+1);
                                }
                        }                      
                       
                        mPFActiveEdgeList[j].mX += mPFActiveEdgeList[j].mDX;    // increment edge coords
                        mPFActiveEdgeList[j+1].mX += mPFActiveEdgeList[j+1].mDX;
                }
        }

        mDestImage->FillScanLinesWithCoverage(aSpans, aSpanPos, mColor, mDrawMode, coverPtr, aCoverLeft, aCoverTop, aCoverWidth, aCoverHeight);
       
        if (coverPtr != aCoverageBuffer) delete[] coverPtr;
        delete[] ind;
        delete[] mPFActiveEdgeList;
}


bool Graphics::DrawLineClipHelper(double* theStartX, double* theStartY, double* theEndX, double* theEndY)
{
        double aStartX = *theStartX;
        double aStartY = *theStartY;
        double aEndX = *theEndX;
        double aEndY = *theEndY;

        // Clip X
        if (aStartX > aEndX)
        {
                std::swap(aStartX,aEndX);
                std::swap(aStartY,aEndY);
        }

        if (aStartX < mClipRect.mX)
        {
                if (aEndX < mClipRect.mX)
                        return false;
                                       
                double aSlope = (aEndY - aStartY) / (aEndX - aStartX);
                aStartY += (mClipRect.mX - aStartX ) * aSlope;
                aStartX = mClipRect.mX;
        }

        if (aEndX >= mClipRect.mX + mClipRect.mWidth)
        {
                if (aStartX >= mClipRect.mX + mClipRect.mWidth)
                        return false;

                double aSlope = (aEndY - aStartY) / (aEndX - aStartX);
                aEndY += (mClipRect.mX + mClipRect.mWidth - 1 - aEndX) * aSlope;
                aEndX = mClipRect.mX + mClipRect.mWidth - 1;
        }

        // Clip Y
        if (aStartY > aEndY)
        {
                std::swap(aStartX,aEndX);
                std::swap(aStartY,aEndY);
        }


        if (aStartY < mClipRect.mY)
        {
                if (aEndY < mClipRect.mY)
                        return false;
                                       
                double aSlope = (aEndX - aStartX) / (aEndY - aStartY);
                aStartX += (mClipRect.mY - aStartY ) * aSlope;                 

                aStartY = mClipRect.mY;
        }

        if (aEndY >= mClipRect.mY + mClipRect.mHeight)
        {
                if (aStartY >= mClipRect.mY + mClipRect.mHeight)
                        return false;

                double aSlope = (aEndX - aStartX) / (aEndY - aStartY);
                aEndX += (mClipRect.mY + mClipRect.mHeight - 1 - aEndY) * aSlope;                      
                aEndY = mClipRect.mY + mClipRect.mHeight - 1;
        }

        *theStartX = aStartX;
        *theStartY = aStartY;
        *theEndX = aEndX;
        *theEndY = aEndY;

        return true;
}

void Graphics::DrawLine(int theStartX, int theStartY, int theEndX, int theEndY)
{
        double aStartX = theStartX + mTransX;
        double aStartY = theStartY + mTransY;
        double aEndX = theEndX + mTransX;
        double aEndY = theEndY + mTransY;

        if (!DrawLineClipHelper(&aStartX, &aStartY, &aEndX, &aEndY))
                return;

        mDestImage->DrawLine(aStartX, aStartY, aEndX, aEndY, mColor, mDrawMode);
}

void Graphics::DrawLineAA(int theStartX, int theStartY, int theEndX, int theEndY)
{
        double aStartX = theStartX + mTransX;
        double aStartY = theStartY + mTransY;
        double aEndX = theEndX + mTransX;
        double aEndY = theEndY + mTransY;

        if (!DrawLineClipHelper(&aStartX, &aStartY, &aEndX, &aEndY))
                return;

        mDestImage->DrawLineAA(aStartX, aStartY, aEndX, aEndY, mColor, mDrawMode);
}

void Graphics::DrawString(const SexyString& theString, int theX, int theY)
{
        if (mFont != NULL)
                mFont->DrawString(this, theX, theY, theString, mColor, mClipRect);
}

void Graphics::DrawImage(Sexy::Image* theImage, int theX, int theY)
{
        if (mScaleX!=1 || mScaleY!=1)
        {
                DrawImage(theImage,theX,theY,Rect(0,0,theImage->mWidth,theImage->mHeight));
                return;
        }

        theX += mTransX;
        theY += mTransY;       

        Rect aDestRect = Rect(theX, theY, theImage->GetWidth(), theImage->GetHeight()).Intersection(mClipRect);
        Rect aSrcRect(aDestRect.mX - theX, aDestRect.mY - theY, aDestRect.mWidth, aDestRect.mHeight);

        if ((aSrcRect.mWidth > 0) && (aSrcRect.mHeight > 0))
                mDestImage->Blt(theImage, aDestRect.mX, aDestRect.mY, aSrcRect, mColorizeImages ? mColor : Color::White, mDrawMode);
}

void Graphics::DrawImage(Image* theImage, int theX, int theY, const Rect& theSrcRect)
{
        DBG_ASSERTE(theSrcRect.mX + theSrcRect.mWidth <= theImage->GetWidth());
        DBG_ASSERTE(theSrcRect.mY + theSrcRect.mHeight <= theImage->GetHeight());      

        if ((theSrcRect.mX + theSrcRect.mWidth > theImage->GetWidth()) ||
                (theSrcRect.mY + theSrcRect.mHeight > theImage->GetHeight()))
                return;

        theX += mTransX;
        theY += mTransY;

        if (mScaleX!=1 || mScaleY!=1)
        {
                Rect aDestRect(mScaleOrigX+floor((theX-mScaleOrigX)*mScaleX),mScaleOrigY+floor((theY-mScaleOrigY)*mScaleY),ceil(theSrcRect.mWidth*mScaleX),ceil(theSrcRect.mHeight*mScaleY));
                mDestImage->StretchBlt(theImage, aDestRect, theSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode, mFastStretch);
                return;
        }

        Rect aDestRect = Rect(theX, theY, theSrcRect.mWidth, theSrcRect.mHeight).Intersection(mClipRect);
        Rect aSrcRect(theSrcRect.mX + aDestRect.mX - theX, theSrcRect.mY + aDestRect.mY - theY, aDestRect.mWidth, aDestRect.mHeight);

        if ((aSrcRect.mWidth > 0) && (aSrcRect.mHeight > 0))
                mDestImage->Blt(theImage, aDestRect.mX, aDestRect.mY, aSrcRect, mColorizeImages ? mColor : Color::White, mDrawMode);
}

void Graphics::DrawImageMirror(Image* theImage, int theX, int theY, bool mirror)
{
        DrawImageMirror(theImage,theX,theY,Rect(0,0,theImage->mWidth,theImage->mHeight),mirror);
}

void Graphics::DrawImageMirror(Image* theImage, int theX, int theY, const Rect& theSrcRect, bool mirror)
{
        if (!mirror)
        {
                DrawImage(theImage, theX, theY, theSrcRect);
                return;
        }

        theX += mTransX;
        theY += mTransY;

        DBG_ASSERTE(theSrcRect.mX + theSrcRect.mWidth <= theImage->GetWidth());
        DBG_ASSERTE(theSrcRect.mY + theSrcRect.mHeight <= theImage->GetHeight());      

        if ((theSrcRect.mX + theSrcRect.mWidth > theImage->GetWidth()) ||
                (theSrcRect.mY + theSrcRect.mHeight > theImage->GetHeight()))
                return;

        Rect aDestRect = Rect(theX, theY, theSrcRect.mWidth, theSrcRect.mHeight).Intersection(mClipRect);

        int aTotalClip = theSrcRect.mWidth - aDestRect.mWidth;
        int aLeftClip = aDestRect.mX - theX;
        int aRightClip = aTotalClip-aLeftClip;

        Rect aSrcRect(theSrcRect.mX + aRightClip, theSrcRect.mY + aDestRect.mY - theY, aDestRect.mWidth, aDestRect.mHeight);

        if ((aSrcRect.mWidth > 0) && (aSrcRect.mHeight > 0))
                mDestImage->BltMirror(theImage, aDestRect.mX, aDestRect.mY, aSrcRect, mColorizeImages ? mColor : Color::White, mDrawMode);
}

void Graphics::DrawImageMirror(Image* theImage, const Rect& theDestRect, const Rect& theSrcRect, bool mirror)
{
        if (!mirror)
        {
                DrawImage(theImage,theDestRect,theSrcRect);
                return;
        }

        Rect aDestRect = Rect(theDestRect.mX + mTransX, theDestRect.mY + mTransY, theDestRect.mWidth, theDestRect.mHeight);

        mDestImage->StretchBltMirror(theImage, aDestRect, theSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode, mFastStretch);
}


void Graphics::DrawImage(Image* theImage, int theX, int theY, int theStretchedWidth, int theStretchedHeight)
{
        Rect aDestRect = Rect(theX + mTransX, theY + mTransY, theStretchedWidth, theStretchedHeight);
        Rect aSrcRect = Rect(0, 0, theImage->mWidth, theImage->mHeight);

        mDestImage->StretchBlt(theImage, aDestRect, aSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode, mFastStretch);
}

void Graphics::DrawImage(Image* theImage, const Rect& theDestRect, const Rect& theSrcRect)
{      
        Rect aDestRect = Rect(theDestRect.mX + mTransX, theDestRect.mY + mTransY, theDestRect.mWidth, theDestRect.mHeight);

        mDestImage->StretchBlt(theImage, aDestRect, theSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode, mFastStretch);
}

void Graphics::DrawImageF(Image* theImage, float theX, float theY)
{
        theX += mTransX;
        theY += mTransY;       

        Rect aSrcRect(0, 0, theImage->mWidth, theImage->mHeight);
        mDestImage->BltF(theImage, theX, theY, aSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode);
}

void Graphics::DrawImageF(Image* theImage, float theX, float theY, const Rect& theSrcRect)
{
        DBG_ASSERTE(theSrcRect.mX + theSrcRect.mWidth <= theImage->GetWidth());
        DBG_ASSERTE(theSrcRect.mY + theSrcRect.mHeight <= theImage->GetHeight());

        theX += mTransX;
        theY += mTransY;
       
        mDestImage->BltF(theImage, theX, theY, theSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode);
}

void Graphics::DrawImageRotated(Image* theImage, int theX, int theY, double theRot, const Rect *theSrcRect)
{      
        if (theSrcRect == NULL)
        {
                int aRotCenterX = theImage->GetWidth() / 2;
                int aRotCenterY = theImage->GetHeight() / 2;

                DrawImageRotatedF(theImage, theX, theY, theRot, aRotCenterX, aRotCenterY, theSrcRect);
        }
        else
        {
                int aRotCenterX = theSrcRect->mWidth / 2;
                int aRotCenterY = theSrcRect->mHeight / 2;

                DrawImageRotatedF(theImage, theX, theY, theRot, aRotCenterX, aRotCenterY, theSrcRect);
        }
}

void Graphics::DrawImageRotatedF(Image* theImage, float theX, float theY, double theRot, const Rect *theSrcRect)
{      
        if (theSrcRect == NULL)
        {
                float aRotCenterX = theImage->GetWidth() / 2.0f;
                float aRotCenterY = theImage->GetHeight() / 2.0f;

                DrawImageRotatedF(theImage, theX, theY, theRot, aRotCenterX, aRotCenterY, theSrcRect);
        }
        else
        {
                float aRotCenterX = theSrcRect->mWidth / 2.0f;
                float aRotCenterY = theSrcRect->mHeight / 2.0f;

                DrawImageRotatedF(theImage, theX, theY, theRot, aRotCenterX, aRotCenterY, theSrcRect);
        }
}

void Graphics::DrawImageRotated(Image* theImage, int theX, int theY, double theRot, int theRotCenterX, int theRotCenterY, const Rect *theSrcRect)
{
        DrawImageRotatedF(theImage,theX,theY,theRot,theRotCenterX,theRotCenterY,theSrcRect);
}

void Graphics::DrawImageRotatedF(Image* theImage, float theX, float theY, double theRot, float theRotCenterX, float theRotCenterY, const Rect *theSrcRect)
{
        theX += mTransX;
        theY += mTransY;       

        if (theSrcRect==NULL)
        {
                Rect aSrcRect(0,0,theImage->mWidth,theImage->mHeight);
                mDestImage->BltRotated(theImage, theX, theY, aSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode, theRot, theRotCenterX, theRotCenterY);
        }
        else
                mDestImage->BltRotated(theImage, theX, theY, *theSrcRect, mClipRect, mColorizeImages ? mColor : Color::White, mDrawMode, theRot, theRotCenterX, theRotCenterY);
}

void Graphics::DrawImageMatrix(Image* theImage, const SexyMatrix3 &theMatrix, float x, float y)
{      
        Rect aSrcRect(0,0,theImage->mWidth,theImage->mHeight);
        mDestImage->BltMatrix(theImage,x+mTransX,y+mTransY,theMatrix,mClipRect,mColorizeImages?mColor:Color::White,mDrawMode,aSrcRect,mLinearBlend);
}

void Graphics::DrawImageMatrix(Image* theImage, const SexyMatrix3 &theMatrix, const Rect &theSrcRect, float x, float y)
{
        mDestImage->BltMatrix(theImage,x+mTransX,y+mTransY,theMatrix,mClipRect,mColorizeImages?mColor:Color::White,mDrawMode,theSrcRect,mLinearBlend);
}

void Graphics::DrawImageTransformHelper(Image* theImage, const Transform &theTransform, const Rect &theSrcRect, float x, float y, bool useFloat)
{
        if (theTransform.mComplex || (DDImage::Check3D(mDestImage) && useFloat))
        {
                DrawImageMatrix(theImage,theTransform.GetMatrix(),theSrcRect,x,y);
                return;
        }

        // Translate into appropriate graphics call
        float w2 = theSrcRect.mWidth/2.0f;
        float h2 = theSrcRect.mHeight/2.0f;

        if (theTransform.mHaveRot)
        {
                float rx = w2-theTransform.mTransX1;
                float ry = h2-theTransform.mTransY1;
               
                x = x + theTransform.mTransX2 - rx + 0.5f;
                y = y + theTransform.mTransY2 - ry + 0.5f;

                if (useFloat)
                        DrawImageRotatedF(theImage,x,y,theTransform.mRot,rx,ry,&theSrcRect);
                else
                        DrawImageRotated(theImage,x,y,theTransform.mRot,rx,ry,&theSrcRect);
        }
        else if (theTransform.mHaveScale)
        {
                bool mirror = false;
                if (theTransform.mScaleX==-1)
                {
                        if (theTransform.mScaleY==1)
                        {
                                x = x + theTransform.mTransX1 + theTransform.mTransX2 - w2 + 0.5f;
                                y = y + theTransform.mTransY1 + theTransform.mTransY2 - h2 + 0.5f;
                                DrawImageMirror(theImage,x,y,theSrcRect);
                                return;
                        }
                        mirror = true;
                }

                float sw = w2*theTransform.mScaleX;
                float sh = h2*theTransform.mScaleY;

                x = x + theTransform.mTransX2 - sw;
                y = y + theTransform.mTransY2 - sh;
       
                Rect aDestRect(x,y,sw*2,sh*2);
                DrawImageMirror(theImage,aDestRect,theSrcRect,mirror);
        }
        else
        {
                x = x + theTransform.mTransX1 + theTransform.mTransX2 - w2 + 0.5f;
                y = y + theTransform.mTransY1 + theTransform.mTransY2 - h2 + 0.5f;

                if (useFloat)
                        DrawImageF(theImage,x,y,theSrcRect);
                else
                        DrawImage(theImage,x,y,theSrcRect);
        }
}

void Graphics::DrawImageTransform(Image* theImage, const Transform &theTransform, float x, float y)
{
        DrawImageTransformHelper(theImage,theTransform,Rect(0,0,theImage->mWidth,theImage->mHeight),x,y,false);
}

void Graphics::DrawImageTransformF(Image* theImage, const Transform &theTransform, float x, float y)
{
        DrawImageTransformHelper(theImage,theTransform,Rect(0,0,theImage->mWidth,theImage->mHeight),x,y,true);
}

void Graphics::DrawImageTransform(Image* theImage, const Transform &theTransform, const Rect &theSrcRect, float x, float y)
{
        DrawImageTransformHelper(theImage,theTransform,theSrcRect,x,y,false);
}

void Graphics::DrawImageTransformF(Image* theImage, const Transform &theTransform, const Rect &theSrcRect, float x, float y)
{
        DrawImageTransformHelper(theImage,theTransform,theSrcRect,x,y,true);
}

void Graphics::DrawTriangleTex(Image *theTexture, const TriVertex &v1, const TriVertex &v2, const TriVertex &v3)
{
        TriVertex v[1][3] = {{v1,v2,v3}};
        mDestImage->BltTrianglesTex(theTexture,v,1,mClipRect,mColorizeImages?mColor:Color::White,mDrawMode,mTransX,mTransY,mLinearBlend);
}

void Graphics::DrawTrianglesTex(Image *theTexture, const TriVertex theVertices[][3], int theNumTriangles)
{
        mDestImage->BltTrianglesTex(theTexture,theVertices,theNumTriangles,mClipRect,mColorizeImages?mColor:Color::White,mDrawMode,mTransX,mTransY,mLinearBlend);
}

void Graphics::ClearClipRect()
{
        mClipRect = Rect(0, 0, mDestImage->GetWidth(), mDestImage->GetHeight());
}

void Graphics::SetClipRect(int theX, int theY, int theWidth, int theHeight)
{
        mClipRect =
                Rect(0, 0, mDestImage->GetWidth(), mDestImage->GetHeight()).Intersection(
                        Rect(theX + mTransX, theY + mTransY, theWidth, theHeight));
}

void Graphics::SetClipRect(const Rect& theRect)
{
        SetClipRect(theRect.mX, theRect.mY, theRect.mWidth, theRect.mHeight);
}

void Graphics::ClipRect(int theX, int theY, int theWidth, int theHeight)
{
        mClipRect = mClipRect.Intersection(Rect(theX + mTransX, theY + mTransY, theWidth, theHeight));
}

void Graphics::ClipRect(const Rect& theRect)
{
        ClipRect(theRect.mX, theRect.mY, theRect.mWidth, theRect.mHeight);
}

void Graphics::Translate(int theTransX, int theTransY)
{
        mTransX += theTransX;
        mTransY += theTransY;
}

void Graphics::TranslateF(float theTransX, float theTransY)
{
        mTransX += theTransX;
        mTransY += theTransY;
}

void Graphics::SetScale(float theScaleX, float theScaleY, float theOrigX, float theOrigY)
{
        mScaleX = theScaleX;
        mScaleY = theScaleY;
        mScaleOrigX = theOrigX + mTransX;
        mScaleOrigY = theOrigY + mTransY;
}

int Graphics::StringWidth(const SexyString& theString)
{
        return mFont->StringWidth(theString);
}

void Graphics::DrawImageBox(const Rect& theDest, Image* theComponentImage)
{
        DrawImageBox(Rect(0,0,theComponentImage->mWidth,theComponentImage->mHeight),theDest,theComponentImage);
}

void Graphics::DrawImageBox(const Rect& theSrc, const Rect &theDest, Image* theComponentImage)
{      
        if (theSrc.mWidth<=0 || theSrc.mHeight<=0)
                return;

        int cw = theSrc.mWidth/3;
        int ch = theSrc.mHeight/3;
        int cx = theSrc.mX;
        int cy = theSrc.mY;
        int cmw = theSrc.mWidth - cw*2;
        int cmh = theSrc.mHeight - ch*2;
               
        // Draw 4 corners
        DrawImage(theComponentImage, theDest.mX, theDest.mY, Rect(cx,cy, cw, ch));
        DrawImage(theComponentImage, theDest.mX + theDest.mWidth-cw, theDest.mY, Rect(cx + cw + cmw, cy, cw, ch));
        DrawImage(theComponentImage, theDest.mX, theDest.mY + theDest.mHeight-ch, Rect(cx, cy + ch + cmh, cw, ch));
        DrawImage(theComponentImage, theDest.mX + theDest.mWidth-cw, theDest.mY + theDest.mHeight-ch, Rect(cx + cw + cmw, cy + ch + cmh, cw, ch));

        // Draw top and bottom
        Graphics aVertClip(*this);
        aVertClip.ClipRect(theDest.mX + cw, theDest.mY, theDest.mWidth-cw*2, theDest.mHeight);
        int aCol, aRow;
        for (aCol = 0; aCol < (theDest.mWidth-cw*2+cmw-1)/cmw; aCol++)
        {
                aVertClip.DrawImage(theComponentImage, theDest.mX + cw + aCol*cmw, theDest.mY, Rect(cx + cw, cy, cmw, ch));
                aVertClip.DrawImage(theComponentImage, theDest.mX + cw + aCol*cmw, theDest.mY + theDest.mHeight-ch, Rect(cx + cw, cy + ch + cmh, cmw, ch));
        }

        // Draw sides
        Graphics aHorzClip(*this);
        aHorzClip.ClipRect(theDest.mX, theDest.mY + ch, theDest.mWidth, theDest.mHeight-ch*2);
        for (aRow = 0; aRow < (theDest.mHeight-ch*2+cmh-1)/cmh; aRow++)
        {
                aHorzClip.DrawImage(theComponentImage, theDest.mX, theDest.mY + ch + aRow*cmh, Rect(cx, cy + ch, cw, cmh));
                aHorzClip.DrawImage(theComponentImage, theDest.mX + theDest.mWidth-cw, theDest.mY + ch + aRow*cmh, Rect(cx + cw + cmw, cy + ch, cw, cmh));
        }

        // Draw middle
        Graphics aMidClip(*this);
        aMidClip.ClipRect(theDest.mX + cw, theDest.mY + ch, theDest.mWidth-cw*2, theDest.mHeight-ch*2);
        for (aCol = 0; aCol < (theDest.mWidth-cw*2+cmw-1)/cmw; aCol++)
                for (aRow = 0; aRow < (theDest.mHeight-ch*2+cmh-1)/cmh; aRow++)
                        aMidClip.DrawImage(theComponentImage, theDest.mX + cw + aCol*cmw, theDest.mY + ch + aRow*cmh, Rect(cx + cw, cy + ch, cmw, cmh));
}

void Graphics::DrawImageCel(Image* theImageStrip, int theX, int theY, int theCel)
{
        DrawImageCel(theImageStrip, theX, theY, theCel % theImageStrip->mNumCols, theCel / theImageStrip->mNumCols);
}

void Graphics::DrawImageCel(Image* theImageStrip, const Rect& theDestRect, int theCel)
{
        DrawImageCel(theImageStrip, theDestRect, theCel % theImageStrip->mNumCols, theCel / theImageStrip->mNumCols);
}

void Graphics::DrawImageCel(Image* theImageStrip, int theX, int theY, int theCelCol, int theCelRow)
{
        if (theCelRow<0 || theCelCol<0 || theCelRow >= theImageStrip->mNumRows || theCelCol >= theImageStrip->mNumCols)
                return;

        int aCelWidth = theImageStrip->mWidth / theImageStrip->mNumCols;
        int aCelHeight = theImageStrip->mHeight / theImageStrip->mNumRows;
        Rect aSrcRect(aCelWidth*theCelCol, aCelHeight*theCelRow, aCelWidth, aCelHeight);

        DrawImage(theImageStrip,theX,theY,aSrcRect);
}

void Graphics::DrawImageAnim(Image* theImageAnim, int theX, int theY, int theTime)
{
        DrawImageCel(theImageAnim, theX, theY, theImageAnim->GetAnimCel(theTime));
}

void Graphics::DrawImageCel(Image* theImageStrip, const Rect& theDestRect, int theCelCol, int theCelRow)
{
        if (theCelRow<0 || theCelCol<0 || theCelRow >= theImageStrip->mNumRows || theCelCol >= theImageStrip->mNumCols)
                return;

        int aCelWidth = theImageStrip->mWidth / theImageStrip->mNumCols;
        int aCelHeight = theImageStrip->mHeight / theImageStrip->mNumRows;
        Rect aSrcRect(aCelWidth*theCelCol, aCelHeight*theCelRow, aCelWidth, aCelHeight);

        DrawImage(theImageStrip,theDestRect,aSrcRect);
}

int Graphics::WriteString(const SexyString& theString, int theX, int theY, int theWidth, int theJustification, bool drawString, int theOffset, int theLength, int theOldColor)
{
        Font* aFont = GetFont();
        if (theOldColor==-1)
                theOldColor = mColor.ToInt();
       
        if (drawString)
        {
                switch (theJustification)
                {      
                case 0:
                        theX += (theWidth - WriteString(theString, theX, theY, theWidth, -1, false, theOffset, theLength, theOldColor))/2;
                        break;
                case 1:
                        theX += theWidth - WriteString(theString, theX, theY, theWidth, -1, false, theOffset, theLength, theOldColor);
                        break;
                }
        }

        if(theLength<0 || theOffset+theLength>(int)theString.length())
                theLength = (int)theString.length();
        else
                theLength = theOffset + theLength;


        SexyString aString;
        int aXOffset = 0;

        for (int i = theOffset; i < theLength; i++)
        {
                if ((theString[i] == '^') && mWriteColoredString)
                {
                        if (i+1<theLength && theString[i+1] == '^') // literal '^'
                        {
                                aString += _S('^');
                                i++;
                        }
                        else if (i>theLength-8) // badly formatted color specification
                                break;
                        else // change color instruction
                        {
                                DWORD aColor = 0;
                                if (theString[i+1]==_S('o'))
                                {
                                        if (sexystrncmp(theString.c_str()+i+1, _S("oldclr"), 6) == 0)
                                                aColor = theOldColor;
                                }
                                else
                                {
                                        for (int aDigitNum = 0; aDigitNum < 6; aDigitNum++)
                                        {
                                                SexyChar aChar = theString[i+aDigitNum+1];
                                                int aVal = 0;

                                                if ((aChar >= _S('0')) && (aChar <= _S('9')))
                                                        aVal = aChar - _S('0');
                                                else if ((aChar >= _S('A')) && (aChar <= _S('F')))
                                                        aVal = (aChar - _S('A')) + 10;
                                                else if ((aChar >= _S('a')) && (aChar <= _S('f')))
                                                        aVal = (aChar - _S('a')) + 10;

                                                aColor += (aVal << ((5 - aDigitNum) * 4));
                                        }                              
                                }

                                if (drawString)
                                {
                                        DrawString(aString, theX + aXOffset, theY);
                                        SetColor(Color((aColor >> 16) & 0xFF, (aColor >> 8) & 0xFF, (aColor) & 0xFF, GetColor().mAlpha));                                      
                                }

                                i += 7;

                                aXOffset += GetFont()->StringWidth(aString);

                                aString = _S("");
                        }
                }
                else
                        aString += theString[i];
        }

        if (drawString)
        {
                DrawString(aString, theX + aXOffset, theY);
        }

        aXOffset += GetFont()->StringWidth(aString);

        return aXOffset;
}

static int WriteWordWrappedHelper(Graphics *g, const SexyString& theString, int theX, int theY, int theWidth, int theJustification, bool drawString, int theOffset, int theLength, int theOldColor, int theMaxChars)
{
        if (theOffset+theLength>theMaxChars)
        {
                theLength = theMaxChars-theOffset;
                if (theLength<=0)
                        return -1;
        }

        return g->WriteString(theString,theX,theY,theWidth,theJustification,drawString,theOffset,theLength,theOldColor);
}

int     Graphics::WriteWordWrapped(const Rect& theRect, const SexyString& theLine, int theLineSpacing, int theJustification, int *theMaxWidth, int theMaxChars, int *theLastWidth)
{
        Color anOrigColor = GetColor();
        int anOrigColorInt = anOrigColor.ToInt();
        if ((anOrigColorInt&0xFF000000)==0xFF000000)
                anOrigColorInt &= ~0xFF000000;

        if (theMaxChars<0)
                theMaxChars = (int)theLine.length();

        Font* aFont = GetFont();                                               

        int aYOffset = aFont->GetAscent() - aFont->GetAscentPadding();

        if (theLineSpacing == -1)
                theLineSpacing = aFont->GetLineSpacing();

        SexyString aCurString;
        ulong aCurPos = 0;
        int aLineStartPos = 0;
        int aCurWidth = 0;
        SexyChar aCurChar = 0;
        SexyChar aPrevChar = 0;
        int aSpacePos = -1;
        int aMaxWidth = 0;
        int anIndentX = 0;

        if (theLastWidth != NULL)
        {
                anIndentX = *theLastWidth;
                aCurWidth = anIndentX;
        }

        while (aCurPos < theLine.length())
        {      
                aCurChar = theLine[aCurPos];
                if(aCurChar==_S('^') && mWriteColoredString) // Handle special color modifier
                {
                        if(aCurPos+1<theLine.length())
                        {
                                if(theLine[aCurPos+1]==_S('^'))
                                        aCurPos++; // literal '^' -> just skip the extra '^'
                                else
                                {
                                        aCurPos+=8;
                                        continue; // skip the color specifier when calculating the width
                                }
                        }
                }
                else if(aCurChar==_S(' '))
                        aSpacePos = aCurPos;
                else if(aCurChar==_S('\n'))
                {
                        aCurWidth = theRect.mWidth+1; // force word wrap
                        aSpacePos = aCurPos;
                        aCurPos++; // skip enter on next go round
                }

                aCurWidth += aFont->CharWidthKern(aCurChar, aPrevChar);
                aPrevChar = aCurChar;

                if(aCurWidth > theRect.mWidth) // need to wrap
                {
                        int aWrittenWidth;
                        if(aSpacePos!=-1)
                        {
                                //aWrittenWidth = WriteWordWrappedHelper(this, theLine, theRect.mX, theRect.mY + aYOffset, theRect.mWidth,
                                //      theJustification, true, aLineStartPos, aSpacePos-aLineStartPos, anOrigColorInt, theMaxChars);

                                int aPhysPos = theRect.mY + aYOffset + mTransY;
                                if ((aPhysPos >= mClipRect.mY) && (aPhysPos < mClipRect.mY + mClipRect.mHeight + theLineSpacing))
                                {
                                        WriteWordWrappedHelper(this, theLine, theRect.mX + anIndentX, theRect.mY + aYOffset, theRect.mWidth,
                                                theJustification, true, aLineStartPos, aSpacePos-aLineStartPos, anOrigColorInt, theMaxChars);

                                        /*WriteString(theLine, theRect.mX + anIndentX, theRect.mY + aYOffset, theRect.mWidth,
                                        theJustification, true, aLineStartPos, aSpacePos-aLineStartPos);*/

                                }

                                aWrittenWidth = aCurWidth + anIndentX;

                                if (aWrittenWidth<0)
                                        break;

                                aCurPos = aSpacePos+1;
                                if (aCurChar != _S('\n'))
                                {
                                        while (aCurPos<theLine.length() && theLine[aCurPos]==_S(' '))
                                                aCurPos++;
                                }
                                aLineStartPos = aCurPos;
                        }
                        else
                        {
                                if((int)aCurPos<aLineStartPos+1)
                                        aCurPos++; // ensure at least one character gets written

                                aWrittenWidth = WriteWordWrappedHelper(this, theLine, theRect.mX + anIndentX, theRect.mY + aYOffset, theRect.mWidth,
                                        theJustification, true, aLineStartPos, aCurPos-aLineStartPos, anOrigColorInt, theMaxChars);

                                if (aWrittenWidth<0)
                                        break;

                                if (theMaxWidth!=NULL && aWrittenWidth>*theMaxWidth)
                                        *theMaxWidth = aWrittenWidth;
                                if (theLastWidth!=NULL)
                                        *theLastWidth = aWrittenWidth;
                        }

                        if (aWrittenWidth > aMaxWidth)
                                aMaxWidth = aWrittenWidth;

                        aLineStartPos = aCurPos;
                        aSpacePos = -1;
                        aCurWidth = 0;
                        aPrevChar = 0;
                        anIndentX = 0;
                        aYOffset += theLineSpacing;
                }
                else
                        aCurPos++;
        }

        if(aLineStartPos<(int)theLine.length()) // write the last piece
        {
                int aWrittenWidth = WriteWordWrappedHelper(this, theLine, theRect.mX + anIndentX, theRect.mY + aYOffset, theRect.mWidth,
                        theJustification, true, aLineStartPos, theLine.length()-aLineStartPos, anOrigColorInt, theMaxChars);

                if (aWrittenWidth>=0)
                {
                        if (aWrittenWidth > aMaxWidth)
                                aMaxWidth = aWrittenWidth;

                        if (theMaxWidth!=NULL && aWrittenWidth>*theMaxWidth)
                                *theMaxWidth = aWrittenWidth;
                        if (theLastWidth!=NULL)
                                *theLastWidth = aWrittenWidth;

                        aYOffset += theLineSpacing;
                }
        }
        else if (aCurChar == '\n')
        {
                aYOffset += theLineSpacing;
                if (theLastWidth != NULL)
                        *theLastWidth = 0;
        }

        SetColor(anOrigColor);

        if (theMaxWidth!=NULL)
                *theMaxWidth = aMaxWidth;

        return aYOffset + aFont->GetDescent() - theLineSpacing;
}

int     Graphics::DrawStringColor(const SexyString& theLine, int theX, int theY, int theOldColor)
{
        return WriteString(theLine, theX, theY, -1, -1,true,0,-1,theOldColor);
}

int     Graphics::DrawStringWordWrapped(const SexyString& theLine, int theX, int theY, int theWrapWidth, int theLineSpacing, int theJustification, int *theMaxWidth)
{
        int aYOffset = mFont->GetAscent() - mFont->GetAscentPadding();

        Rect aRect(theX,theY-aYOffset,theWrapWidth,0);
        return WriteWordWrapped(aRect, theLine, theLineSpacing, theJustification, theMaxWidth);
}

int     Graphics::GetWordWrappedHeight(int theWidth, const SexyString& theLine, int theLineSpacing, int *theMaxWidth)
{
        Graphics aTestG;
        aTestG.SetFont(mFont);
        int aHeight = aTestG.WriteWordWrapped(Rect(0, 0, theWidth, 0), theLine, theLineSpacing, -1, theMaxWidth);      

        return aHeight;
}