/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: pximage.cpp,v 1.1.26.1 2004/07/09 01:54:51 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxresult.h"
#include "hxcom.h"
#include "ihxpckts.h"
#include "hxvsurf.h"

// hxcont
#include "hxbuffer.h"

// hxmisc
#include "unkimp.h"
#include "baseobj.h"

// pxcomlib
#include "pxcolor.h"
#include "pxrect.h"
#include "pxeffect.h"
#include "pximage.h"

// hxdebug
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif

PXImage::PXImage()
{
    ResetMembers();
    m_lRefCount   = 0;
    m_pImageStore = NULL;
}

PXImage::~PXImage()
{
    Destroy();
}

HX_RESULT PXImage::CreateObject(PXImage** ppImg)
{
    HX_RESULT retVal = HXR_FAIL;

    if (ppImg)
    {
        PXImage* pImg = new PXImage();
        if (pImg)
        {
            *ppImg = pImg;
            retVal = HXR_OK;
        }
    }

    return retVal;
}

HX_RESULT PXImage::CreateInstance(PXImage** ppImg)
{
    HX_RESULT retVal = HXR_FAIL;

    if (ppImg)
    {
        PXImage* pImg = new PXImage();
        if (pImg)
        {
            // AddRef the object
            pImg->AddRef();
            // Assign the out parameter
            *ppImg = pImg;
            retVal = HXR_OK;
        }
    }

    return retVal;
}

STDMETHODIMP PXImage::QueryInterface(REFIID riid, void** ppvObj)
{
    HX_RESULT retVal = HXR_OK;

    if (IsEqualIID(riid, IID_IUnknown))
    {
        AddRef();
        *ppvObj = (IUnknown *) this;
    }
    else
    {
        *ppvObj = NULL;
        retVal  = HXR_NOINTERFACE;
    }

    return retVal;
}

STDMETHODIMP_(UINT32) PXImage::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32) PXImage::Release()
{
    
    if (InterlockedDecrement(&m_lRefCount) > 0)
        return m_lRefCount;

    delete this;

    return 0;
}

HX_RESULT PXImage::Create(INT32 lW, INT32 lH, UINT32 ulBpp, UINT32 ulFormat,
                          BOOL bRowsInverted, BOOL bAlloc)
{
    // Check for invalid input size
    if (lW <= 0          ||
        lW >  kMaxWidth  ||
        lH <= 0          ||
        lH >  kMaxHeight)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    // Check to see if we're asked to NOT alloc, but
    // don't have any image store
    if (!bAlloc && !m_pImageStore)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    // Filter out unsupported formats
    switch (ulBpp)
    {
        case 32:
            if (ulFormat != HX_RGB)
            {
                HX_ASSERT(FALSE);
                return HXR_FAIL;
            }
            break;
        default:
            HX_ASSERT(FALSE);
            return HXR_FAIL;
    }

    // Reset members (except image store)
    ResetMembers();

    // Assign member variables
    m_cBitmapInfo.biWidth       = lW;
    m_cBitmapInfo.biHeight      = lH;
    m_cBitmapInfo.biBitCount    = (UINT16) ulBpp;
    m_cBitmapInfo.biCompression = ulFormat;
    m_cSubImageRect.left        = 0;
    m_cSubImageRect.top         = 0;
    m_cSubImageRect.right       = lW;
    m_cSubImageRect.bottom      = lH;
    m_lSubImageWidth            = lW;
    m_lSubImageHeight           = lH;
    m_bRowsInverted             = bRowsInverted;
    m_ulBytesPerPixel           = (ulBpp + 7) >> 3;
    m_lRowBytes                 = lW * m_ulBytesPerPixel;
    m_lRowStride                = (m_lRowBytes + 3) & ~3;

    // Determine the number of bytes we need
    UINT32 ulNumBytes = m_lRowStride * m_cBitmapInfo.biHeight;

    // Check to see if we need to alloc memory. We will allocate
    // if the bAlloc parameter tells us to OR if bAlloc is FALSE
    // AND we have don't already have enough to satisfy the request.
    if (bAlloc || (!bAlloc && m_pImageStore->GetSize() < ulNumBytes))
    {
        // Now create the storage object
        HX_RELEASE(m_pImageStore);
        m_pImageStore = (IHXBuffer *) new CHXBuffer();
        if (!m_pImageStore)
        {
            ResetMembers();
            return HXR_OUTOFMEMORY;
        }
        m_pImageStore->AddRef();

        // Set its size
        HX_RESULT retVal = m_pImageStore->SetSize(ulNumBytes);
        if (retVal != HXR_OK)
        {
            HX_RELEASE(m_pImageStore);
            ResetMembers();
            return retVal;
        }
    }

    // Set the image buffer to beginning of storage
    if (m_bRowsInverted)
    {
        m_pImageBuffer = m_pImageStore->GetBuffer() + (m_cBitmapInfo.biHeight - 1) * m_lRowStride;
        m_lRowJump     = - m_lRowStride;
    }
    else
    {
        m_pImageBuffer = m_pImageStore->GetBuffer();
        m_lRowJump     = m_lRowStride;
    }

    // Set the initialization flag
    m_bInitialized = TRUE;

    return HXR_OK;
}

HX_RESULT PXImage::CreateFromBuffer(INT32 lW, INT32 lH, UINT32 ulBpp, UINT32 ulFormat,
                                    BOOL bRowsInverted, IHXBuffer* pBuffer)
{
    HX_RESULT retVal = HXR_OK;

    if (lW > 0 && lW <= kMaxWidth && lH > 0 && lH <= kMaxHeight &&
        ulBpp == 32 && ulFormat == HX_RGB && pBuffer)
    {
        // Reset members (except image store)
        ResetMembers();

        // Assign member variables
        m_cBitmapInfo.biWidth       = lW;
        m_cBitmapInfo.biHeight      = lH;
        m_cBitmapInfo.biBitCount    = (UINT16) ulBpp;
        m_cBitmapInfo.biCompression = ulFormat;
        m_cSubImageRect.left        = 0;
        m_cSubImageRect.top         = 0;
        m_cSubImageRect.right       = lW;
        m_cSubImageRect.bottom      = lH;
        m_lSubImageWidth            = lW;
        m_lSubImageHeight           = lH;
        m_bRowsInverted             = bRowsInverted;
        m_ulBytesPerPixel           = (ulBpp + 7) >> 3;
        m_lRowBytes                 = lW * m_ulBytesPerPixel;
        m_lRowStride                = (m_lRowBytes + 3) & ~3;

        // Determine the number of bytes we need
        UINT32 ulNumBytes = m_lRowStride * m_cBitmapInfo.biHeight;

        //  Make sure the provided buffer is big enough
        if (pBuffer->GetSize() >= ulNumBytes)
        {
            // Save the passed in buffer
            HX_RELEASE(m_pImageStore);
            m_pImageStore = pBuffer;
            m_pImageStore->AddRef();

            // Set the image buffer to beginning of storage
            if (m_bRowsInverted)
            {
                m_pImageBuffer = m_pImageStore->GetBuffer() + (m_cBitmapInfo.biHeight - 1) * m_lRowStride;
                m_lRowJump     = - m_lRowStride;
            }
            else
            {
                m_pImageBuffer = m_pImageStore->GetBuffer();
                m_lRowJump     = m_lRowStride;
            }

            // Set the initialization flag
            m_bInitialized = TRUE;
        }
        else
        {
            retVal = HXR_INVALID_PARAMETER;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::CreateSubImage(PXImage* pImg, INT32 lX, INT32 lY, INT32 lW, INT32 lH,
                                  BOOL bCopy, BOOL bAlloc)
{
    // Check for input error
    if (!pImg || lX < 0 || lY < 0 || lW < 0 || lH < 0)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    // Adjust for default input
    if (lW == 0)
    {
        lW = pImg->GetWidth();
    }
    if (lH == 0)
    {
        lH = pImg->GetHeight();
    }

    // Check for input error
    if (lX + lW >  pImg->GetWidth())
    {
        lW = pImg->GetWidth() - lX;
    }
    if (lY + lH >  pImg->GetHeight())
    {
        lH = pImg->GetHeight() - lY;
    }

    // Check to see if we're asked NOT to copy but to alloc
    if (!bCopy && bAlloc)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    // Reset everything (except image store)
    ResetMembers();

    // Are we copying or not?
    if (bCopy)
    {
        // Set member variables
        m_cBitmapInfo.biWidth       = lW;
        m_cBitmapInfo.biHeight      = lH;
        m_cBitmapInfo.biBitCount    = pImg->m_cBitmapInfo.biBitCount;
        m_cBitmapInfo.biCompression = pImg->m_cBitmapInfo.biCompression;
        m_cSubImageRect.left        = 0;
        m_cSubImageRect.top         = 0;
        m_cSubImageRect.right       = lW;
        m_cSubImageRect.bottom      = lH;
        m_lSubImageWidth            = lW;
        m_lSubImageHeight           = lH;
        m_bRowsInverted             = pImg->m_bRowsInverted;
        m_ulBytesPerPixel           = pImg->m_ulBytesPerPixel;
        m_lRowBytes                 = lW * m_ulBytesPerPixel;
        m_lRowStride                = (m_lRowBytes + 3) & ~3;
        m_bHasAlpha                 = pImg->m_bHasAlpha;

        // Determine the number of bytes we need
        UINT32 ulNumBytes = m_lRowStride * m_cBitmapInfo.biHeight;

        // Check to see if we need to alloc memory. We will allocate
        // if the bAlloc parameter tells us to OR if bAlloc is FALSE
        // AND we have don't already have enough to satisfy the request.
        if (bAlloc || (!bAlloc && m_pImageStore->GetSize() < ulNumBytes))
        {
            // Create a new image store
            HX_RELEASE(m_pImageStore);
            m_pImageStore = (IHXBuffer *) new CHXBuffer();
            if (!m_pImageStore)
            {
                ResetMembers();
                return HXR_OUTOFMEMORY;
            }
            m_pImageStore->AddRef();

            // Set its size
            HX_RESULT retVal     = m_pImageStore->SetSize(ulNumBytes);
            if (retVal != HXR_OK)
            {
                Destroy();
                return retVal;
            }
        }

        // Set the image buffer
        if (m_bRowsInverted)
        {
            m_pImageBuffer = m_pImageStore->GetBuffer() + (m_cBitmapInfo.biHeight - 1) * m_lRowStride;
            m_lRowJump     = - m_lRowStride;
        }
        else
        {
            m_pImageBuffer = m_pImageStore->GetBuffer();
            m_lRowJump     = m_lRowStride;
        }

        // Copy the data
        BYTE* pSrcRow = pImg->m_pImageBuffer + lY * pImg->m_lRowJump + lX * pImg->m_ulBytesPerPixel;
        BYTE* pDstRow = m_pImageBuffer;
        for (INT32 i = m_cBitmapInfo.biHeight; i; i--)
        {
            memcpy(pDstRow, pSrcRow, m_lRowBytes); /* Flawfinder: ignore */
            pSrcRow += pImg->m_lRowJump;
            pDstRow += m_lRowJump;
        }
    }
    else
    {
        // Set member variables
        m_cBitmapInfo          = pImg->m_cBitmapInfo;
        m_cSubImageRect.left   = lX;
        m_cSubImageRect.top    = lY;
        m_cSubImageRect.right  = lX + lW;
        m_cSubImageRect.bottom = lY + lH;
        m_lSubImageWidth       = lW;
        m_lSubImageHeight      = lH;
        m_ulBytesPerPixel      = pImg->m_ulBytesPerPixel;
        m_bRowsInverted        = pImg->m_bRowsInverted;
        m_lRowBytes            = lW * m_ulBytesPerPixel;
        m_bHasAlpha            = pImg->m_bHasAlpha;

        // We ARE NOT copying, so our row stride and jump are the same
        // as the source image
        m_lRowStride = pImg->m_lRowStride;
        m_lRowJump   = pImg->m_lRowJump;

        // Ref the other image store
        HX_RELEASE(m_pImageStore);
        m_pImageStore = pImg->m_pImageStore;
        m_pImageStore->AddRef();

        // Set the image buffer properly (note that we must offset
        // from the other object's m_pImageBuffer member and NOT the
        // image store member - because we may be a subimage of a subimage)
        m_pImageBuffer = pImg->m_pImageBuffer + (lY * m_lRowJump) + (lX * m_ulBytesPerPixel);
    }

    // Set the initialization flag
    m_bInitialized = TRUE;

    return HXR_OK;
}

HX_RESULT PXImage::CreateSubImage(PXImage* pImg, const PXRect& rSubRect,
                                  BOOL bCopy, BOOL bAlloc)
{
    return CreateSubImage(pImg, (INT32) rSubRect.GetX(), (INT32) rSubRect.GetY(),
                          (INT32) rSubRect.GetWidth(), (INT32) rSubRect.GetHeight(),
                          bCopy, bAlloc);
}

HX_RESULT PXImage::CreateSubImage(PXImage* pImg, const HXxRect& rSubRect,
                                  BOOL bCopy, BOOL bAlloc)
{
    return CreateSubImage(pImg, rSubRect.left, rSubRect.top, HXxRECT_WIDTH(rSubRect),
                          HXxRECT_HEIGHT(rSubRect), bCopy, bAlloc);
}
HX_RESULT PXImage::CreateSubImageFromBuffer(PXImage* pImg, INT32 lX, INT32 lY,
                                            INT32 lW, INT32 lH, IHXBuffer* pBuffer)
{
    HX_RESULT retVal = HXR_OK;

    // Check for input error
    if (pImg && lX >= 0 && lY >= 0 && lW >= 0 && lH >= 0)
    {
        // Adjust for default input
        if (lW == 0)
        {
            lW = pImg->GetWidth();
        }
        if (lH == 0)
        {
            lH = pImg->GetHeight();
        }

        // Check for input error
        if (lX + lW >  pImg->GetWidth())
        {
            lW = pImg->GetWidth() - lX;
        }
        if (lY + lH >  pImg->GetHeight())
        {
            lH = pImg->GetHeight() - lY;
        }

        // Reset everything (except image store)
        ResetMembers();

        // Set member variables
        m_cBitmapInfo.biWidth       = lW;
        m_cBitmapInfo.biHeight      = lH;
        m_cBitmapInfo.biBitCount    = pImg->m_cBitmapInfo.biBitCount;
        m_cBitmapInfo.biCompression = pImg->m_cBitmapInfo.biCompression;
        m_cSubImageRect.left        = 0;
        m_cSubImageRect.top         = 0;
        m_cSubImageRect.right       = lW;
        m_cSubImageRect.bottom      = lH;
        m_lSubImageWidth            = lW;
        m_lSubImageHeight           = lH;
        m_bRowsInverted             = pImg->m_bRowsInverted;
        m_ulBytesPerPixel           = pImg->m_ulBytesPerPixel;
        m_lRowBytes                 = lW * m_ulBytesPerPixel;
        m_lRowStride                = (m_lRowBytes + 3) & ~3;
        m_bHasAlpha                 = pImg->m_bHasAlpha;

        // Determine the number of bytes we need
        UINT32 ulNumBytes = m_lRowStride * m_cBitmapInfo.biHeight;

        // Make sure that the buffer we were provided is big enough
        if (pBuffer->GetSize() >= ulNumBytes)
        {
            // Save the buffer
            HX_RELEASE(m_pImageStore);
            m_pImageStore = pBuffer;
            m_pImageStore->AddRef();

            // Set the image buffer
            if (m_bRowsInverted)
            {
                m_pImageBuffer = m_pImageStore->GetBuffer() + (m_cBitmapInfo.biHeight - 1) * m_lRowStride;
                m_lRowJump     = - m_lRowStride;
            }
            else
            {
                m_pImageBuffer = m_pImageStore->GetBuffer();
                m_lRowJump     = m_lRowStride;
            }

            // Copy the data
            BYTE* pSrcRow = pImg->m_pImageBuffer + lY * pImg->m_lRowJump + lX * pImg->m_ulBytesPerPixel;
            BYTE* pDstRow = m_pImageBuffer;
            for (INT32 i = m_cBitmapInfo.biHeight; i; i--)
            {
                memcpy(pDstRow, pSrcRow, m_lRowBytes); /* Flawfinder: ignore */
                pSrcRow += pImg->m_lRowJump;
                pDstRow += m_lRowJump;
            }
        }
        else
        {
            retVal = HXR_INVALID_PARAMETER;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    if (SUCCEEDED(retVal))
    {
        // Set the initialization flag
        m_bInitialized = TRUE;
    }

    return retVal;
}

HX_RESULT PXImage::CreateSubImageFromBuffer(PXImage* pImg, const PXRect& rSubRect, IHXBuffer* pBuffer)
{
    return CreateSubImageFromBuffer(pImg, (INT32) rSubRect.GetX(), (INT32) rSubRect.GetY(),
                                    (INT32) rSubRect.GetWidth(), (INT32) rSubRect.GetHeight(), pBuffer);
}

void PXImage::Destroy()
{
    HX_RELEASE(m_pImageStore);
    ResetMembers();
}

HX_RESULT PXImage::Fill(const PXColor& rColor)
{
    UINT32 ulColor = MAKE_RGBA32(rColor.GetRed(),
                                 rColor.GetGreen(),
                                 rColor.GetBlue(),
                                 rColor.GetAlpha());
    return Fill32(ulColor);
}

HX_RESULT PXImage::Fill32(UINT32 ulColor)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (m_cBitmapInfo.biBitCount == 32 &&
            CompressionSupported())
        {
            UINT32* pRow    = (UINT32*) m_pImageBuffer;
            INT32   lJump   = m_lRowJump >> 2;
            for (UINT32 ulY = (UINT32) m_lSubImageHeight; ulY; ulY--)
            {
                UINT32* pPixel = pRow;
                for (UINT32 ulX = (UINT32) m_lSubImageWidth; ulX; ulX--)
                {
                    *pPixel++ = ulColor;
                }
                pRow += lJump;
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

    return retVal;
}

HX_RESULT PXImage::Blend(PXImage* pImg1, PXImage* pImg2, BYTE* pLUT1, BYTE* pLUT2)
{
    if (!pImg1 || !pImg2 || !pLUT1 || !pLUT2 || !m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    if (!Compatible(pImg1) || !Compatible(pImg2) || !SameSize(pImg1) || !SameSize(pImg2))
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    UINT32* pSrcRow1  = (UINT32*) pImg1->m_pImageBuffer;
    UINT32* pSrcRow2  = (UINT32*) pImg2->m_pImageBuffer;
    UINT32* pDstRow   = (UINT32*) m_pImageBuffer;
    INT32   lSrc1Jump = pImg1->m_lRowJump >> 2;
    INT32   lSrc2Jump = pImg2->m_lRowJump >> 2;
    INT32   lDstJump  = m_lRowJump       >> 2;
    for (INT32 lY = m_lSubImageHeight; lY; lY--)
    {
        UINT32* pSrc1 = pSrcRow1;
        UINT32* pSrc2 = pSrcRow2;
        UINT32* pDst  = pDstRow;
        if (pImg2->m_bHasAlpha)
        {
            for (INT32 lX = m_lSubImageWidth; lX; lX--)
            {
                UINT32 ulR = pLUT1[GETRED32(*pSrc1)]   + pLUT2[GETRED32(*pSrc2)];
                UINT32 ulG = pLUT1[GETGREEN32(*pSrc1)] + pLUT2[GETGREEN32(*pSrc2)];
                UINT32 ulB = pLUT1[GETBLUE32(*pSrc1)]  + pLUT2[GETBLUE32(*pSrc2)];
                UINT32 ulA = GETALPHA32(*pSrc2);
                ulA        = (ulA < 128 ? ulA : ulA + 1); // scale to 256
                *pDst      = MAKE_RGB32(ALPHABLEND(GETRED32(*pDst),   ulR, ulA),
                                        ALPHABLEND(GETGREEN32(*pDst), ulG, ulA),
                                        ALPHABLEND(GETBLUE32(*pDst),  ulB, ulA));
                pSrc1++;
                pSrc2++;
                pDst++;
            }
        }
        else
        {
            for (INT32 lX = m_lSubImageWidth; lX; lX--)
            {
                *pDst = MAKE_RGB32(pLUT1[GETRED32(*pSrc1)]   + pLUT2[GETRED32(*pSrc2)],
                                   pLUT1[GETGREEN32(*pSrc1)] + pLUT2[GETGREEN32(*pSrc2)],
                                   pLUT1[GETBLUE32(*pSrc1)]  + pLUT2[GETBLUE32(*pSrc2)]);
                pSrc1++;
                pSrc2++;
                pDst++;
            }
        }
        pSrcRow1 += lSrc1Jump;
        pSrcRow2 += lSrc2Jump;
        pDstRow  += lDstJump;
    }

    return HXR_OK;
}

HX_RESULT PXImage::RecursiveBlend(PXImage* pImg1, BYTE* pLUT1, BYTE* pSelfLUT)
{
    if (!pImg1 || !pLUT1 || !pSelfLUT || !m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    if (!Compatible(pImg1) || !SameSize(pImg1))
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    UINT32* pSrcRow1 = (UINT32*) pImg1->m_pImageBuffer;
    UINT32* pDstRow  = (UINT32*) m_pImageBuffer;
    INT32   lSrc1Jump = pImg1->m_lRowJump >> 2;
    INT32   lDstJump  = m_lRowJump       >> 2;
    for (INT32 lY = m_lSubImageHeight; lY; lY--)
    {
        UINT32* pSrc1 = pSrcRow1;
        UINT32* pDst  = pDstRow;
        for (INT32 lX = m_lSubImageWidth; lX; lX--)
        {
            *pDst = MAKE_RGB32(pLUT1[GETRED32(*pSrc1)]   + pSelfLUT[GETRED32(*pDst)],
                               pLUT1[GETGREEN32(*pSrc1)] + pSelfLUT[GETGREEN32(*pDst)],
                               pLUT1[GETBLUE32(*pSrc1)]  + pSelfLUT[GETBLUE32(*pDst)]);
            pSrc1++;
            pDst++;
        }
        pSrcRow1 += lSrc1Jump;
        pDstRow  += lDstJump;
    }

    return HXR_OK;
}

HX_RESULT PXImage::BlendToColor(PXImage* pImg1, const PXColor& rColor, BYTE* pLUT1, BYTE* pLUT2)
{
    if (!pImg1 || !pLUT1 || !pLUT2 || !m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    if (!Compatible(pImg1) || !SameSize(pImg1))
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    BYTE ucFadedR = pLUT2[rColor.GetRed()];
    BYTE ucFadedG = pLUT2[rColor.GetGreen()];
    BYTE ucFadedB = pLUT2[rColor.GetBlue()];

    UINT32* pSrcRow1 = (UINT32*) pImg1->m_pImageBuffer;
    UINT32* pDstRow  = (UINT32*) m_pImageBuffer;
    INT32   lSrc1Jump = pImg1->m_lRowJump >> 2;
    INT32   lDstJump  = m_lRowJump       >> 2;
    for (INT32 lY = m_lSubImageHeight; lY; lY--)
    {
        UINT32* pSrc1 = pSrcRow1;
        UINT32* pDst  = pDstRow;
        for (INT32 lX = m_lSubImageWidth; lX; lX--)
        {
            *pDst = MAKE_RGB32(pLUT1[GETRED32(*pSrc1)]   + ucFadedR,
                               pLUT1[GETGREEN32(*pSrc1)] + ucFadedG,
                               pLUT1[GETBLUE32(*pSrc1)]  + ucFadedB);
            pSrc1++;
            pDst++;
        }
        pSrcRow1 += lSrc1Jump;
        pDstRow  += lDstJump;
    }

    return HXR_OK;
}

HX_RESULT PXImage::RecursiveBlendToColor(const PXColor& rColor, BYTE* pLUT1, BYTE* pLUT2)
{
    if (!pLUT1 || !pLUT2 || !m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    BYTE ucFadedR = pLUT2[rColor.GetRed()];
    BYTE ucFadedG = pLUT2[rColor.GetGreen()];
    BYTE ucFadedB = pLUT2[rColor.GetBlue()];

    UINT32* pDstRow  = (UINT32*) m_pImageBuffer;
    INT32   lDstJump = m_lRowJump       >> 2;
    for (INT32 lY = m_lSubImageHeight; lY; lY--)
    {
        UINT32* pDst  = pDstRow;
        for (INT32 lX = m_lSubImageWidth; lX; lX--)
        {
            *pDst = MAKE_RGB32(pLUT1[GETRED32(*pDst)]   + ucFadedR,
                               pLUT1[GETGREEN32(*pDst)] + ucFadedG,
                               pLUT1[GETBLUE32(*pDst)]  + ucFadedB);
            pDst++;
        }
        pDstRow  += lDstJump;
    }

    return HXR_OK;
}

HX_RESULT PXImage::ChangeSizeFromNN(PXImage* pImg)
{
    if (!pImg || !m_bInitialized || !pImg->m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    if (!Compatible(pImg))
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    return ChangeSize32NN((UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                          pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2,
                          (UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                          m_lSubImageHeight,              m_lRowJump >> 2);
}

HX_RESULT PXImage::ChangeSizeFromNNTransparent32(PXImage* pImg)
{
    HX_RESULT retVal = HXR_OK;

    if (pImg)
    {
        if (m_bInitialized && pImg->m_bInitialized)
        {
            if (m_cBitmapInfo.biBitCount          == 32      &&
                pImg->m_cBitmapInfo.biBitCount    == 32      &&
                CompressionSupported()                       &&
                pImg->CompressionSupported())
            {
                if (Compatible(pImg))
                {
                    retVal = ChangeSize32NNTransparent((UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                                                       pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2,
                                                       (UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                                                       m_lSubImageHeight,              m_lRowJump >> 2);
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::ChangeSizeIntoNN(PXImage* pImg)
{
    if (!pImg || !m_bInitialized || !pImg->m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    if (!Compatible(pImg))
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    return ChangeSize32NN((UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                          m_lSubImageHeight,              m_lRowJump >> 2,
                          (UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                          pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2);
}

HX_RESULT PXImage::ChangeSizeIntoNNTransparent32(PXImage* pImg)
{
    HX_RESULT retVal = HXR_OK;

    if (pImg)
    {
        if (m_bInitialized && pImg->m_bInitialized)
        {
            if (m_cBitmapInfo.biBitCount          == 32      &&
                pImg->m_cBitmapInfo.biBitCount    == 32      &&
                CompressionSupported()                       &&
                pImg->CompressionSupported())
            {
                if (Compatible(pImg))
                {
                    retVal = ChangeSize32NNTransparent((UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                                                       m_lSubImageHeight,              m_lRowJump >> 2,
                                                       (UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                                                       pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2);
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::CopyFrom(PXImage* pImg)
{
    if (!pImg || !m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    if (!Compatible(pImg) || !SameSize(pImg))
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    Copy32((UINT32*) pImg->m_pImageBuffer, (UINT32*) m_pImageBuffer,
            pImg->m_lRowJump >> 2,         m_lRowJump >> 2,
            pImg->m_bHasAlpha);

    return HXR_OK;
}

HX_RESULT PXImage::CopyFromTransparent32(PXImage* pImg)
{
    HX_RESULT retVal = HXR_OK;

    if (pImg)
    {
        if (m_bInitialized && pImg->m_bInitialized)
        {
            if (m_cBitmapInfo.biBitCount          == 32      &&
                pImg->m_cBitmapInfo.biBitCount    == 32      &&
                CompressionSupported()                       &&
                pImg->CompressionSupported())
            {
                if (Compatible(pImg) && SameSize(pImg))
                {
                    CopyTransparent32((UINT32*) pImg->m_pImageBuffer,
                                      (UINT32*) m_pImageBuffer,
                                      pImg->m_lRowJump >> 2,
                                      m_lRowJump >> 2);
                }
                else
                {
                    retVal = HXR_INVALID_PARAMETER;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::CopyFromAlpha32(PXImage* pImg, BYTE* pLUT)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pImg)
    {
        if (m_bInitialized && pImg->m_bInitialized)
        {
            if (m_cBitmapInfo.biBitCount          == 32      &&
                pImg->m_cBitmapInfo.biBitCount    == 32      &&
                CompressionSupported()                       &&
                pImg->CompressionSupported())
            {
                if (Compatible(pImg) && SameSize(pImg))
                {
                    CopyAlpha32((UINT32*) pImg->m_pImageBuffer,
                                (UINT32*) m_pImageBuffer,
                                pImg->m_lRowJump >> 2,
                                m_lRowJump >> 2,
                                pLUT);
                    retVal = HXR_OK;
                }
            }
        }
    }

    return retVal;
}

HX_RESULT PXImage::CopyTo(PXImage* pImg)
{
    if (!pImg || !m_bInitialized)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    FILTER_OUT_UNSUPPORTED_FORMATS;

    if (!Compatible(pImg) || !SameSize(pImg))
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    Copy32((UINT32*) m_pImageBuffer, (UINT32*) pImg->m_pImageBuffer,
           m_lRowJump >> 2,          pImg->m_lRowJump >> 2,
           m_bHasAlpha);

    return HXR_OK;
}

HX_RESULT PXImage::CopyToTransparent32(PXImage* pImg)
{
    HX_RESULT retVal = HXR_OK;

    if (pImg)
    {
        if (m_bInitialized && pImg->m_bInitialized)
        {
            if (m_cBitmapInfo.biBitCount          == 32      &&
                pImg->m_cBitmapInfo.biBitCount    == 32      &&
                CompressionSupported()                       &&
                pImg->CompressionSupported())
            {
                if (Compatible(pImg) && SameSize(pImg))
                {
                    CopyTransparent32((UINT32*) m_pImageBuffer,
                                      (UINT32*) pImg->m_pImageBuffer,
                                      m_lRowJump >> 2,
                                      pImg->m_lRowJump >> 2);
                }
                else
                {
                    retVal = HXR_INVALID_PARAMETER;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::CopyToAlpha32(PXImage* pImg, BYTE* pLUT)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pImg)
    {
        if (m_bInitialized && pImg->m_bInitialized)
        {
            if (m_cBitmapInfo.biBitCount          == 32      &&
                pImg->m_cBitmapInfo.biBitCount    == 32      &&
                CompressionSupported()                       &&
                pImg->CompressionSupported())
            {
                if (Compatible(pImg) && SameSize(pImg))
                {
                    CopyAlpha32((UINT32*) m_pImageBuffer,
                                (UINT32*) pImg->m_pImageBuffer,
                                m_lRowJump >> 2,
                                pImg->m_lRowJump >> 2,
                                pLUT);
                    retVal = HXR_OK;
                }
            }
        }
    }

    return retVal;
}

HX_RESULT PXImage::FlipFrom(PXImage* pImg, BOOL bVerticalAxis)
{
    HX_RESULT retVal = HXR_OK;

    if (pImg)
    {
        if (m_bInitialized)
        {
            if (Compatible(pImg) && SameSize(pImg))
            {
                if (bVerticalAxis)
                {
                    VertAxisFlip32((UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                                   pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2,
                                   (UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                                   m_lSubImageHeight,              m_lRowJump >> 2);
                }
                else
                {
                    HorzAxisFlip32((UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                                   pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2,
                                   (UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                                   m_lSubImageHeight,              m_lRowJump >> 2);
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::FlipTo(PXImage* pImg, BOOL bVerticalAxis)
{
    HX_RESULT retVal = HXR_OK;

    if (pImg)
    {
        if (m_bInitialized)
        {
            if (Compatible(pImg) && SameSize(pImg))
            {
                if (bVerticalAxis)
                {
                    VertAxisFlip32((UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                                   m_lSubImageHeight,              m_lRowJump >> 2,
                                   (UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                                   pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2);
                }
                else
                {
                    HorzAxisFlip32((UINT32*) m_pImageBuffer,       m_lSubImageWidth,
                                   m_lSubImageHeight,              m_lRowJump >> 2,
                                   (UINT32*) pImg->m_pImageBuffer, pImg->m_lSubImageWidth,
                                   pImg->m_lSubImageHeight,        pImg->m_lRowJump >> 2);
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::Wipe(PXImage* pStartImg, PXImage* pEndImg, UINT32 ulType,
                        UINT32 ulDirection, UINT32 ulTime, UINT32 ulDuration)
{
    HX_RESULT retVal = HXR_OK;

    if (pStartImg                                       &&
        pEndImg                                         &&
        (ulType == PXEffect::kWipeTypeNormal         ||
         ulType == PXEffect::kWipeTypePush)             &&
        (ulDirection == PXEffect::kWipeDirectionUp   ||
         ulDirection == PXEffect::kWipeDirectionDown ||
         ulDirection == PXEffect::kWipeDirectionLeft ||
         ulDirection == PXEffect::kWipeDirectionRight)  &&
        ulTime     > 0                                  &&
        ulTime     < ulDuration                         &&
        ulDuration > 0)
    {
        if (m_bInitialized)
        {
            if (Compatible(pStartImg) && SameSize(pStartImg) &&
                Compatible(pEndImg)   && SameSize(pEndImg))
            {
                // Set up src and dst rectangles for both
                // the start and end images
                PXRect cStartSrc;
                PXRect cStartDst;
                PXRect cEndSrc;
                PXRect cEndDst;
                UINT32 ulW = pStartImg->GetWidth();
                UINT32 ulH = pStartImg->GetHeight();
                if (ulType == PXEffect::kWipeTypeNormal)
                {
                    if (ulDirection == PXEffect::kWipeDirectionUp)
                    {
                        UINT32 ulY = (ulDuration - ulTime) * ulH / ulDuration;
                        cStartSrc.Set(0, 0, ulW, ulY);
                        cEndSrc.Set(0, ulY, ulW, ulH - ulY);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionDown)
                    {
                        UINT32 ulY = ulTime * ulH / ulDuration;
                        cStartSrc.Set(0, ulY, ulW, ulH - ulY);
                        cEndSrc.Set(0, 0, ulW, ulY);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionLeft)
                    {
                        UINT32 ulX = (ulDuration - ulTime) * ulW / ulDuration;
                        cStartSrc.Set(0, 0, ulX, ulH);
                        cEndSrc.Set(ulX, 0, ulW - ulX, ulH);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionRight)
                    {
                        UINT32 ulX = ulTime * ulW / ulDuration;
                        cStartSrc.Set(ulX, 0, ulW - ulX, ulH);
                        cEndSrc.Set(0, 0, ulX, ulH);
                    }
                    cStartDst = cStartSrc;
                    cEndDst   = cEndSrc;
                }
                else
                {
                    if (ulDirection == PXEffect::kWipeDirectionUp)
                    {
                        UINT32 ulY = (ulDuration - ulTime) * ulH / ulDuration;
                        cStartSrc.Set(0, ulH - ulY, ulW, ulY);
                        cStartDst.Set(0, 0, ulW, ulY);
                        cEndSrc.Set(0, 0, ulW, ulH - ulY);
                        cEndDst.Set(0, ulY, ulW, ulH - ulY);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionDown)
                    {
                        UINT32 ulY = ulTime * ulH / ulDuration;
                        cStartSrc.Set(0, 0, ulW, ulH - ulY);
                        cStartDst.Set(0, ulY, ulW, ulH - ulY);
                        cEndSrc.Set(0, ulH - ulY, ulW, ulY);
                        cEndDst.Set(0, 0, ulW, ulY);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionLeft)
                    {
                        UINT32 ulX = (ulDuration - ulTime) * ulW / ulDuration;
                        cStartSrc.Set(ulW - ulX, 0, ulX, ulH);
                        cStartDst.Set(0, 0, ulX, ulH);
                        cEndSrc.Set(0, 0, ulW - ulX, ulH);
                        cEndDst.Set(ulX, 0, ulW - ulX, ulH);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionRight)
                    {
                        UINT32 ulX = ulTime * ulW / ulDuration;
                        cStartSrc.Set(0, 0, ulW - ulX, ulH);
                        cStartDst.Set(ulX, 0, ulW - ulX, ulH);
                        cEndSrc.Set(ulW - ulX, 0,ulX, ulH);
                        cEndDst.Set(0, 0, ulX, ulH);
                    }
                }

                // Copy from the start image
                if (cStartSrc.GetWidth() > 0 && cStartSrc.GetHeight() > 0)
                {
                    // Create the start src image
                    PXImage cStartSrcImg;
                    retVal = cStartSrcImg.CreateSubImage(pStartImg, cStartSrc);
                    if (SUCCEEDED(retVal))
                    {
                        // Create the start dst image
                        PXImage cStartDstImg;
                        retVal = cStartDstImg.CreateSubImage(this, cStartDst);
                        if (SUCCEEDED(retVal))
                        {
                            // Now copy the start image
                            retVal = cStartDstImg.CopyFrom(&cStartSrcImg);
                        }
                    }
                }

                if (SUCCEEDED(retVal))
                {
                    // Copy from the dst image
                    if (cEndSrc.GetWidth() > 0 && cEndSrc.GetHeight() > 0)
                    {
                        // Create the end src image
                        PXImage cEndSrcImg;
                        retVal = cEndSrcImg.CreateSubImage(pEndImg, cEndSrc);
                        if (SUCCEEDED(retVal))
                        {
                            // Create the end dst image
                            PXImage cEndDstImg;
                            retVal = cEndDstImg.CreateSubImage(this, cEndDst);
                            if (SUCCEEDED(retVal))
                            {
                                // Now copy the end image
                                retVal = cEndDstImg.CopyFrom(&cEndSrcImg);
                            }
                        }
                    }
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::RecursiveWipe(PXImage* pEndImage, UINT32 ulType, UINT32 ulDirection, UINT32 ulLastTime,
                                 UINT32 ulTime, UINT32 ulDuration, PXRect& rDamageRect)
{
    HX_RESULT retVal = HXR_OK;

    if (pEndImage                                       &&
        (ulType == PXEffect::kWipeTypeNormal         ||
         ulType == PXEffect::kWipeTypePush)             &&
        (ulDirection == PXEffect::kWipeDirectionUp   ||
         ulDirection == PXEffect::kWipeDirectionDown ||
         ulDirection == PXEffect::kWipeDirectionLeft ||
         ulDirection == PXEffect::kWipeDirectionRight)  &&
        ulTime     > 0                                  &&
        ulTime     < ulDuration                         &&
        ulTime     > ulLastTime                         &&
        ulDuration > 0)
    {
        if (m_bInitialized)
        {
            if (Compatible(pEndImage) && SameSize(pEndImage))
            {
                // Set up src and dst rectangles for both
                // the start and end images
                UINT32 ulW = pEndImage->GetWidth();
                UINT32 ulH = pEndImage->GetHeight();
                if (ulType == PXEffect::kWipeTypeNormal)
                {
                    PXRect cRect;
                    if (ulDirection == PXEffect::kWipeDirectionUp)
                    {
                        UINT32 ulY     = (ulDuration - ulTime) * ulH / ulDuration;
                        UINT32 ulLastY = (ulDuration - ulLastTime) * ulH / ulDuration;
                        cRect.Set(0, ulY, ulW, ulLastY - ulY);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionDown)
                    {
                        UINT32 ulY     = ulTime * ulH / ulDuration;
                        UINT32 ulLastY = ulLastTime * ulH / ulDuration;
                        cRect.Set(0, ulLastY, ulW, ulY - ulLastY);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionLeft)
                    {
                        UINT32 ulX     = (ulDuration - ulTime) * ulW / ulDuration;
                        UINT32 ulLastX = (ulDuration - ulLastTime) * ulW / ulDuration;
                        cRect.Set(ulX, 0, ulLastX - ulX, ulH);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionRight)
                    {
                        UINT32 ulX     = ulTime * ulW / ulDuration;
                        UINT32 ulLastX = ulLastTime * ulW / ulDuration;
                        cRect.Set(ulLastX, 0, ulX - ulLastX, ulH);
                    }

                    // Make sure that we have something to copy
                    if (cRect.GetWidth() > 0 && cRect.GetHeight() > 0)
                    {
                        // Set up the source image
                        PXImage cEndSrcImg;
                        retVal = cEndSrcImg.CreateSubImage(pEndImage, cRect);
                        if (SUCCEEDED(retVal))
                        {
                            // Set up the destination image
                            PXImage cEndDstImg;
                            retVal = cEndDstImg.CreateSubImage(this, cRect);
                            if (SUCCEEDED(retVal))
                            {
                                retVal = cEndDstImg.CopyFrom(&cEndSrcImg);
                                if (SUCCEEDED(retVal))
                                {
                                    rDamageRect = cRect;
                                }
                            }
                        }
                    }
                }
                else
                {
                    PXRect cIntraSrcRect;
                    PXRect cIntraDstRect;
                    PXRect cEndSrcRect;
                    PXRect cEndDstRect;
                    if (ulDirection == PXEffect::kWipeDirectionUp)
                    {
                        UINT32 ulY     = (ulDuration - ulTime) * ulH / ulDuration;
                        UINT32 ulLastY = (ulDuration - ulLastTime) * ulH / ulDuration;
                        UINT32 ulYDiff = ulLastY - ulY;
                        cIntraSrcRect.Set(0, ulYDiff, ulW, ulH - ulYDiff);
                        cIntraDstRect.Set(0, 0, ulW, ulH - ulYDiff);
                        cEndSrcRect.Set(0, ulH - ulLastY, ulW, ulYDiff);
                        cEndDstRect.Set(0, ulH - ulYDiff, ulW, ulYDiff);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionDown)
                    {
                        UINT32 ulY     = ulTime * ulH / ulDuration;
                        UINT32 ulLastY = ulLastTime * ulH / ulDuration;
                        UINT32 ulYDiff = ulY - ulLastY;
                        cIntraSrcRect.Set(0, 0, ulW, ulH - ulYDiff);
                        cIntraDstRect.Set(0, ulYDiff, ulW, ulH - ulYDiff);
                        cEndSrcRect.Set(0, ulH - ulY, ulW, ulYDiff);
                        cEndDstRect.Set(0, 0, ulW, ulYDiff);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionLeft)
                    {
                        UINT32 ulX     = (ulDuration - ulTime) * ulW / ulDuration;
                        UINT32 ulLastX = (ulDuration - ulLastTime) * ulW / ulDuration;
                        UINT32 ulXDiff = ulLastX - ulX;
                        cIntraSrcRect.Set(ulXDiff, 0, ulW - ulXDiff, ulH);
                        cIntraDstRect.Set(0, 0, ulW - ulXDiff, ulH);
                        cEndSrcRect.Set(ulW - ulLastX, 0, ulXDiff, ulH);
                        cEndDstRect.Set(ulW - ulXDiff, 0, ulXDiff, ulH);
                    }
                    else if (ulDirection == PXEffect::kWipeDirectionRight)
                    {
                        UINT32 ulX     = ulTime * ulW / ulDuration;
                        UINT32 ulLastX = ulLastTime * ulW / ulDuration;
                        UINT32 ulXDiff = ulX - ulLastX;
                        cIntraSrcRect.Set(0, 0, ulW - ulXDiff, ulH);
                        cIntraDstRect.Set(ulXDiff, 0, ulW - ulXDiff, ulH);
                        cEndSrcRect.Set(ulW - ulX, 0, ulXDiff, ulH);
                        cEndDstRect.Set(0, 0, ulXDiff, ulH);
                    }

                    // Make sure we have something to copy
                    if (cIntraSrcRect.GetWidth() == 0 || cIntraSrcRect.GetHeight() == 0)
                    {
                        // This is all end image, so just do a copy
                        retVal = CopyFrom(pEndImage);
                    }
                    else if (cEndSrcRect.GetWidth() > 0 && cEndSrcRect.GetHeight() > 0)
                    {
                        // First we do an intra copy to copy the part of 
                        // the image which is already there.
                        retVal = IntraCopy(cIntraSrcRect, cIntraDstRect);
                        if (SUCCEEDED(retVal))
                        {
                            PXImage cEndSrcImg;
                            retVal = cEndSrcImg.CreateSubImage(pEndImage, cEndSrcRect);
                            if (SUCCEEDED(retVal))
                            {
                                PXImage cEndDstImg;
                                retVal = cEndDstImg.CreateSubImage(this, cEndDstRect);
                                if (SUCCEEDED(retVal))
                                {
                                    retVal = cEndDstImg.CopyFrom(&cEndSrcImg);
                                    if (SUCCEEDED(retVal))
                                    {
                                        rDamageRect.Set(0, 0, ulW, ulH);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_NOT_INITIALIZED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::IntraCopy(const PXRect& rSrcRect, const PXRect& rDstRect)
{
    HX_RESULT retVal = HXR_OK;

    if (rSrcRect.GetWidth()  == rDstRect.GetWidth()        &&
        rSrcRect.GetHeight() == rDstRect.GetHeight()       &&
        rSrcRect.GetWidth()  <= (UINT32) m_lSubImageWidth  &&
        rSrcRect.GetHeight() <= (UINT32) m_lSubImageHeight &&
        rSrcRect.GetX() + rSrcRect.GetWidth()  <= (UINT32) m_lSubImageWidth  &&
        rSrcRect.GetY() + rSrcRect.GetHeight() <= (UINT32) m_lSubImageHeight &&
        rDstRect.GetX() + rDstRect.GetWidth()  <= (UINT32) m_lSubImageWidth  &&
        rDstRect.GetY() + rDstRect.GetHeight() <= (UINT32) m_lSubImageHeight)
    {
        // Make sure this is not a null copy
        if (rSrcRect.GetX() != rDstRect.GetX() ||
            rSrcRect.GetY() != rDstRect.GetY())
        {
            BOOL bUpCopy = (rDstRect.GetY() <= rSrcRect.GetY() ? TRUE : FALSE);
//            if (m_bRowsInverted)
//            {
//                // If we are row-inverted, then we reverse whatever, operation
//                // we were going to do.
//                bUpCopy = (bUpCopy ? FALSE : TRUE);
//            }
            if (rDstRect.GetX() <= rSrcRect.GetX())
            {
                // This is a left-ward copy
                if (bUpCopy)
                {
                    // This is an up-left copy, so we do L->R, T->B
                    UINT32* pSrcRow = (UINT32*) GetPixel(rSrcRect.GetX(), rSrcRect.GetY());
                    UINT32* pDstRow = (UINT32*) GetPixel(rDstRect.GetX(), rDstRect.GetY());
                    INT32   lJump   = m_lRowJump >> 2;
                    for (UINT32 ulY = rSrcRect.GetHeight(); ulY; ulY--)
                    {
                        UINT32* pSrcPixel = pSrcRow;
                        UINT32* pDstPixel = pDstRow;
                        for (UINT32 ulX = rSrcRect.GetWidth(); ulX; ulX--)
                        {
                            *pDstPixel++ = *pSrcPixel++;
                        }
                        pSrcRow += lJump;
                        pDstRow += lJump;
                    }
                }
                else
                {
                    // This is a down-left copy, so we do L->R, B->T
                    UINT32* pSrcRow = (UINT32*) GetPixel(rSrcRect.GetX(), rSrcRect.GetY() + rSrcRect.GetHeight() - 1);
                    UINT32* pDstRow = (UINT32*) GetPixel(rDstRect.GetX(), rDstRect.GetY() + rDstRect.GetHeight() - 1);
                    INT32   lJump   = m_lRowJump >> 2;
                    for (UINT32 ulY = rSrcRect.GetHeight(); ulY; ulY--)
                    {
                        UINT32* pSrcPixel = pSrcRow;
                        UINT32* pDstPixel = pDstRow;
                        for (UINT32 ulX = rSrcRect.GetWidth(); ulX; ulX--)
                        {
                            *pDstPixel++ = *pSrcPixel++;
                        }
                        pSrcRow -= lJump;
                        pDstRow -= lJump;
                    }
                }
            }
            else
            {
                // This is a right-ward copy
                if (bUpCopy)
                {
                    // This is an up-right copy, so we do R->L, T->B
                    UINT32* pSrcRow = (UINT32*) GetPixel(rSrcRect.GetX() + rSrcRect.GetWidth() - 1, rSrcRect.GetY());
                    UINT32* pDstRow = (UINT32*) GetPixel(rDstRect.GetX() + rDstRect.GetWidth() - 1, rDstRect.GetY());
                    INT32   lJump   = m_lRowJump >> 2;
                    for (UINT32 ulY = rSrcRect.GetHeight(); ulY; ulY--)
                    {
                        UINT32* pSrcPixel = pSrcRow;
                        UINT32* pDstPixel = pDstRow;
                        for (UINT32 ulX = rSrcRect.GetWidth(); ulX; ulX--)
                        {
                            *pDstPixel-- = *pSrcPixel--;
                        }
                        pSrcRow += lJump;
                        pDstRow += lJump;
                    }
                }
                else
                {
                    // This is a down-right copy, so we do R->L, B->T
                    UINT32* pSrcRow = (UINT32*) GetPixel(rSrcRect.GetX() + rSrcRect.GetWidth()  - 1,
                                                         rSrcRect.GetY() + rSrcRect.GetHeight() - 1);
                    UINT32* pDstRow = (UINT32*) GetPixel(rDstRect.GetX() + rDstRect.GetWidth()  - 1,
                                                         rDstRect.GetY() + rDstRect.GetHeight() - 1);
                    INT32   lJump   = m_lRowJump >> 2;
                    for (UINT32 ulY = rSrcRect.GetHeight(); ulY; ulY--)
                    {
                        UINT32* pSrcPixel = pSrcRow;
                        UINT32* pDstPixel = pDstRow;
                        for (UINT32 ulX = rSrcRect.GetWidth(); ulX; ulX--)
                        {
                            *pDstPixel-- = *pSrcPixel--;
                        }
                        pSrcRow -= lJump;
                        pDstRow -= lJump;
                    }
                }
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::DrawToHXSurface(IHXVideoSurface* pSurface, HXxRect& rDstRect)
{
    if (!m_bInitialized || !pSurface)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    // If the image has alpha, then we need to
    // blt a different colorspace
    UINT32 ulFormat = m_cBitmapInfo.biCompression;
    if (m_bHasAlpha)
    {
        m_cBitmapInfo.biCompression = HX_ARGB;
    }
    // Do the blt
    pSurface->AddRef();
    HX_RESULT retVal = pSurface->Blt(m_pImageStore->GetBuffer(), &m_cBitmapInfo, rDstRect, m_cSubImageRect);
    pSurface->Release();
    // Restore the old colorspace
    if (m_bHasAlpha)
    {
        m_cBitmapInfo.biCompression = ulFormat;
    }

    return retVal;
}

HX_RESULT PXImage::DrawToHXSurface(IHXVideoSurface* pSurface, HXxRect& rSrcRect, HXxRect& rDstRect)
{
    HX_RESULT retVal = HXR_OK;

    if (pSurface)
    {
        if (m_bInitialized)
        {
            // If the image has alpha, then we need to
            // blt a different colorspace
            UINT32 ulFormat = m_cBitmapInfo.biCompression;
            if (m_bHasAlpha)
            {
                m_cBitmapInfo.biCompression = HX_ARGB;
            }
            // Do the blt
            pSurface->AddRef();
            retVal = pSurface->Blt(m_pImageStore->GetBuffer(), &m_cBitmapInfo, rDstRect, rSrcRect);
            pSurface->Release();
            // Restore the old colorspace
            if (m_bHasAlpha)
            {
                m_cBitmapInfo.biCompression = ulFormat;
            }
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImage::GetPixel(INT32 lX, INT32 lY, BYTE** ppPixel)
{
    *ppPixel = GetPixel(lX, lY);
    return (*ppPixel ? HXR_OK : HXR_FAIL);
}

BYTE* PXImage::GetPixel(INT32 lX, INT32 lY)
{
    BYTE* pRet = NULL;

    if (m_bInitialized)
    {
        if (lX >= 0 && lX < m_lSubImageWidth &&
            lY >= 0 && lY < m_lSubImageHeight)
        {
            pRet = m_pImageBuffer + lY * m_lRowJump + lX * m_ulBytesPerPixel;
        }
    }

    return pRet;
}

HX_RESULT PXImage::GetImageStore(IHXBuffer** ppBuffer)
{
    HX_RESULT retVal = HXR_OK;
    if (ppBuffer && m_bInitialized)
    {
        m_pImageStore->AddRef();
        *ppBuffer = m_pImageStore;
    }
    else
    {
        retVal = HXR_FAIL;
    }
    return retVal;
}

void PXImage::ConvertToRGBOrder(INT32 lScanline, BYTE* pLine)
{
    if (lScanline >= 0 && lScanline < m_lSubImageHeight)
    {
        UINT32* pPixel = (UINT32*) GetPixel(0, lScanline);
        for (INT32 lX = m_lSubImageWidth; lX; lX--)
        {
            UINT32 ulPixel = *pPixel++;
            *pLine++ = (BYTE) ((ulPixel & 0x00FF0000) >> 16); // R
            *pLine++ = (BYTE) ((ulPixel & 0x0000FF00) >>  8); // G
            *pLine++ = (BYTE)  (ulPixel & 0x000000FF);        // B
        }
    }
}

void PXImage::ConvertFromRGBOrder(INT32 lScanline, BYTE* pLine)
{
    if (lScanline >= 0 && lScanline < m_lSubImageHeight)
    {
        UINT32* pPixel = (UINT32*) GetPixel(0, lScanline);
        for (INT32 lX = m_lSubImageWidth; lX; lX--)
        {
            UINT32 ulRed   = *pLine++;
            UINT32 ulGreen = *pLine++;
            UINT32 ulBlue  = *pLine++;
            *pPixel++      = (ulRed << 16) | (ulGreen << 8) | ulBlue;
        }
    }
}

void PXImage::GetSubRect(PXRect& rRect) const
{
    rRect.Set(m_cSubImageRect.left,
              m_cSubImageRect.top,
              HXxRECT_WIDTH(m_cSubImageRect),
              HXxRECT_HEIGHT(m_cSubImageRect));
}

BOOL PXImage::SameSize(PXImage* pImg) const
{
    if (pImg                                         &&
        m_lSubImageWidth  == pImg->m_lSubImageWidth  &&
        m_lSubImageHeight == pImg->m_lSubImageHeight)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

BOOL PXImage::Compatible(PXImage* pImg) const
{
    if (pImg                                                          &&
        m_cBitmapInfo.biBitCount    == pImg->m_cBitmapInfo.biBitCount &&
        m_cBitmapInfo.biCompression == pImg->m_cBitmapInfo.biCompression)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

void PXImage::Copy32(UINT32* pSrc, UINT32* pDst, INT32 lSrcJump, INT32 lDstJump, BOOL bUseAlpha)
{
    for (INT32 lY = m_lSubImageHeight; lY; lY--)
    {
        UINT32* pSrcPix = pSrc;
        UINT32* pDstPix = pDst;
        if (bUseAlpha)
        {
            for (INT32 lX = m_lSubImageWidth; lX; lX--)
            {
                UINT32 ulA = GETALPHA32(*pSrcPix);
                ulA        = (ulA < 128 ? ulA : ulA + 1);
                *pDstPix   = MAKE_RGB32(ALPHABLEND(GETRED32(*pDstPix),   GETRED32(*pSrcPix),   ulA),
                                        ALPHABLEND(GETGREEN32(*pDstPix), GETGREEN32(*pSrcPix), ulA),
                                        ALPHABLEND(GETBLUE32(*pDstPix),  GETBLUE32(*pSrcPix),  ulA));
                pSrcPix++;
                pDstPix++;
            }
        }
        else
        {
            for (INT32 lX = m_lSubImageWidth; lX; lX--)
            {
                *pDstPix++ = *pSrcPix++;
            }
        }
        pSrc += lSrcJump;
        pDst += lDstJump;
    }
}

void PXImage::CopyTransparent32(UINT32* pSrc, UINT32* pDst, INT32 lSrcJump, INT32 lDstJump)
{
    for (INT32 lY = m_lSubImageHeight; lY; lY--)
    {
        UINT32* pSrcPix = pSrc;
        UINT32* pDstPix = pDst;
        for (INT32 lX = m_lSubImageWidth; lX; lX--)
        {
            if (!(*pSrcPix & 0xFF000000))
            {
                *pDstPix = *pSrcPix;
            }
            pSrcPix++;
            pDstPix++;
        }
        pSrc += lSrcJump;
        pDst += lDstJump;
    }
}

void PXImage::CopyAlpha32(UINT32* pSrc, UINT32* pDst, INT32 lSrcJump, INT32 lDstJump, BYTE* pLUT)
{
    if (pLUT)
    {
        for (INT32 lY = m_lSubImageHeight; lY; lY--)
        {
            UINT32* pSrcPix = pSrc;
            UINT32* pDstPix = pDst;
            for (INT32 lX = m_lSubImageWidth; lX; lX--)
            {
                UINT32 ulAlpha = GETALPHA32(*pSrcPix);
                BYTE*  pAlp    = &pLUT[ulAlpha << 8];
                BYTE*  pInv    = &pLUT[(255 - ulAlpha) << 8];
                *pDstPix       = MAKE_RGB32(pAlp[GETRED32(*pDstPix)]   + pInv[GETRED32(*pSrcPix)],
                                            pAlp[GETGREEN32(*pDstPix)] + pInv[GETGREEN32(*pSrcPix)],
                                            pAlp[GETBLUE32(*pDstPix)]  + pInv[GETBLUE32(*pSrcPix)]);
                pSrcPix++;
                pDstPix++;
            }
            pSrc += lSrcJump;
            pDst += lDstJump;
        }
    }
    else
    {
        for (INT32 lY = m_lSubImageHeight; lY; lY--)
        {
            UINT32* pSrcPix = pSrc;
            UINT32* pDstPix = pDst;
            for (INT32 lX = m_lSubImageWidth; lX; lX--)
            {
                UINT32 ulAlpha = GETALPHA32(*pSrcPix);
                *pDstPix       = MAKE_RGB32(BLENDMULT(GETRED32(*pDstPix),   GETRED32(*pSrcPix),   ulAlpha),
                                            BLENDMULT(GETGREEN32(*pDstPix), GETGREEN32(*pSrcPix), ulAlpha),
                                            BLENDMULT(GETBLUE32(*pDstPix),  GETBLUE32(*pSrcPix),  ulAlpha));
                pSrcPix++;
                pDstPix++;
            }
            pSrc += lSrcJump;
            pDst += lDstJump;
        }
    }
}

HX_RESULT PXImage::ChangeSize32NN(UINT32* pSrc, INT32 lSrcW, INT32 lSrcH, INT32 lSrcJump,
                                  UINT32* pDst, INT32 lDstW, INT32 lDstH, INT32 lDstJump)
{
    // Allocate the divide LUT
    INT32* pSrcX = new INT32 [lDstW];
    if (!pSrcX)
    {
        return HXR_OUTOFMEMORY;
    }

    // Build the divide LUT
    INT32 lDstX;
    INT32 lDstWDiv2 = lDstW >> 1;
    for (lDstX = 0; lDstX < lDstW; lDstX++)
    {
        INT32 lSrcX = (lDstX * lSrcW + lDstWDiv2) / lDstW;
        if (lSrcX >= lSrcW)
        {
            lSrcX = lSrcW - 1;
        }
        pSrcX[lDstX] = lSrcX;
    }

    // Now run through the output image, doing nearest neighbor resampling
    INT32   lSrcY     = 0;
    INT32   lDstY     = 0;
    INT32   lLastSrcY = -1;
    UINT32* pDstRow   = pDst;
    INT32   lDstHDiv2 = lDstH >> 1;
    for (lDstY = 0; lDstY < lDstH; lDstY++)
    {
        lSrcY = (lDstY * lSrcH + lDstHDiv2) / lDstH;
        if (lSrcY >= lSrcH)
        {
            lSrcY = lSrcH - 1;
        }
        UINT32* pSrcRow = pSrc + lSrcY * lSrcJump;
        if (lSrcY == lLastSrcY)
        {
            // We're getting this resampled row from the same source row,
            // so just copy the last row.
            UINT32* pDstLastRow = pDstRow - lDstJump;
            for (lDstX = 0; lDstX < lDstW; lDstX++)
            {
                pDstRow[lDstX] = pDstLastRow[lDstX];
            }
        }
        else
        {
            // New row, we must do the work
            for (lDstX = 0; lDstX < lDstW; lDstX++)
            {
                pDstRow[lDstX] = pSrcRow[pSrcX[lDstX]];
            }
        }
        lLastSrcY = lSrcY;
        pDstRow  += lDstJump;
    }

    HX_VECTOR_DELETE(pSrcX);

    return HXR_OK;
}

HX_RESULT PXImage::ChangeSize32NNTransparent(UINT32* pSrc, INT32 lSrcW, INT32 lSrcH, INT32 lSrcJump,
                                             UINT32* pDst, INT32 lDstW, INT32 lDstH, INT32 lDstJump)
{
    // Allocate the divide LUT
    INT32* pSrcX = new INT32 [lDstW];
    if (!pSrcX)
    {
        return HXR_OUTOFMEMORY;
    }

    // Build the divide LUT
    INT32 lDstX;
    INT32 lDstWDiv2 = lDstW >> 1;
    for (lDstX = 0; lDstX < lDstW; lDstX++)
    {
        INT32 lSrcX = (lDstX * lSrcW + lDstWDiv2) / lDstW;
        if (lSrcX >= lSrcW)
        {
            lSrcX = lSrcW - 1;
        }
        pSrcX[lDstX] = lSrcX;
    }

    // Now run through the output image, doing nearest neighbor resampling
    INT32   lSrcY     = 0;
    INT32   lDstY     = 0;
    INT32   lLastSrcY = -1;
    UINT32* pDstRow   = pDst;
    INT32   lDstHDiv2 = lDstH >> 1;
    for (lDstY = 0; lDstY < lDstH; lDstY++)
    {
        lSrcY = (lDstY * lSrcH + lDstHDiv2) / lDstH;
        if (lSrcY >= lSrcH)
        {
            lSrcY = lSrcH - 1;
        }
        UINT32* pSrcRow = pSrc + lSrcY * lSrcJump;
        if (lSrcY == lLastSrcY)
        {
            // We're getting this resampled row from the same source row,
            // so just copy the last row.
            UINT32* pDstLastRow = pDstRow - lDstJump;
            for (lDstX = 0; lDstX < lDstW; lDstX++)
            {
                pDstRow[lDstX] = pDstLastRow[lDstX];
            }
        }
        else
        {
            // New row, we must do the work
            for (lDstX = 0; lDstX < lDstW; lDstX++)
            {
                UINT32 ulPixel = pSrcRow[pSrcX[lDstX]];
                if (!(ulPixel & 0xFF000000))
                {
                    pDstRow[lDstX] = ulPixel;
                }
            }
        }
        lLastSrcY = lSrcY;
        pDstRow  += lDstJump;
    }

    HX_VECTOR_DELETE(pSrcX);

    return HXR_OK;
}

void PXImage::HorzAxisFlip32(UINT32* pSrc, INT32 lSrcW, INT32 lSrcH, INT32 lSrcJump,
                             UINT32* pDst, INT32 lDstW, INT32 lDstH, INT32 lDstJump)
{
    UINT32* pSrcRow = pSrc + (lSrcH - 1) * lSrcJump;
    UINT32* pDstRow = pDst;
    for (INT32 lY = 0; lY < lSrcH; lY++)
    {
        UINT32* pSrcPixel = pSrcRow;
        UINT32* pDstPixel = pDstRow;
        for (INT32 lX = lSrcW; lX; lX--)
        {
            *pDstPixel++ = *pSrcPixel++;
        }
        pSrcRow -= lSrcJump;
        pDstRow += lDstJump;
    }
}

void PXImage::VertAxisFlip32(UINT32* pSrc, INT32 lSrcW, INT32 lSrcH, INT32 lSrcJump,
                             UINT32* pDst, INT32 lDstW, INT32 lDstH, INT32 lDstJump)
{
    UINT32* pSrcRow = pSrc + lSrcW - 1;
    UINT32* pDstRow = pDst;
    for (INT32 lY = 0; lY < lSrcH; lY++)
    {
        UINT32 *pSrcPixel = pSrcRow;
        UINT32 *pDstPixel = pDstRow;
        for (INT32 lX = lSrcW; lX; lX--)
        {
            *pDstPixel++ = *pSrcPixel--;
        }
        pSrcRow += lSrcJump;
        pDstRow += lDstJump;
    }
}

void PXImage::ResetMembers()
{
    m_cBitmapInfo.biSize          = 40;
    m_cBitmapInfo.biWidth         = 0;
    m_cBitmapInfo.biHeight        = 0;
    m_cBitmapInfo.biPlanes        = 1;
    m_cBitmapInfo.biBitCount      = 0;
    m_cBitmapInfo.biCompression   = HX_RGB;
    m_cBitmapInfo.biSizeImage     = 0;
    m_cBitmapInfo.biXPelsPerMeter = 0;
    m_cBitmapInfo.biYPelsPerMeter = 0;
    m_cBitmapInfo.biClrUsed       = 0;
    m_cBitmapInfo.biClrImportant  = 0;
    m_cBitmapInfo.rcolor          = 0;
    m_cBitmapInfo.gcolor          = 0;
    m_cBitmapInfo.bcolor          = 0;
    m_cSubImageRect.left          = 0;
    m_cSubImageRect.top           = 0;
    m_cSubImageRect.right         = 0;
    m_cSubImageRect.bottom        = 0;
    m_lSubImageWidth              = 0;
    m_lSubImageHeight             = 0;
    m_ulBytesPerPixel             = 0;
    m_lRowBytes                   = 0;
    m_lRowStride                  = 0;
    m_lRowJump                    = 0;
    m_bRowsInverted               = FALSE;
    m_pImageBuffer                = NULL;
    m_bInitialized                = FALSE;
    m_bHasAlpha                   = FALSE;
}

void PXImage::Write(const char* pszFileName, UINT32 ulWriteFormat)
{
    if (m_bInitialized && pszFileName)
    {
        switch (ulWriteFormat)
        {
            case kWriteFormatBinaryRaw:
                {
                    FILE* fp = fopen(pszFileName, "wb");
                    if (fp)
                    {
                        UINT32* pRow      = (UINT32*) m_pImageBuffer;
                        INT32   lJump     = m_lRowJump >> 2;
                        INT32   lNumBytes = m_lSubImageWidth << 2;
                        for (INT32 lY = m_lSubImageHeight; lY; lY--)
                        {
                            UINT32* pPixel = pRow;
                            for (INT32 lX = m_lSubImageWidth; lX; lX--)
                            {
                                BYTE ucTmp = 0;
                                ucTmp = (BYTE) GETRED32(*pPixel);
                                fwrite(&ucTmp, 1, 1, fp);
                                ucTmp = (BYTE) GETGREEN32(*pPixel);
                                fwrite(&ucTmp, 1, 1, fp);
                                ucTmp = (BYTE) GETBLUE32(*pPixel);
                                fwrite(&ucTmp, 1, 1, fp);
                                pPixel++;
                            }
                            pRow += lJump;
                        }
                    }
                    fclose(fp);
                }
                break;
            case kWriteFormatAscii:
                {
                    FILE* fp = fopen(pszFileName, "w");
                    if (fp)
                    {
                        fprintf(fp, "Width=%ld, Height=%ld\nFormat is RGBA RGBA ...\n",
                                m_lSubImageWidth, m_lSubImageHeight);
                        UINT32* pRow  = (UINT32*) m_pImageBuffer;
                        INT32   lJump = m_lRowJump >> 2;
                        for (INT32 lY = m_lSubImageHeight; lY; lY--)
                        {
                            UINT32* pPixel = pRow;
                            for (INT32 lX = m_lSubImageWidth; lX; lX--)
                            {
                                fprintf(fp, "%02X%02X%02X%02X ",
                                        GETRED32(*pPixel),
                                        GETGREEN32(*pPixel),
                                        GETBLUE32(*pPixel),
                                        GETALPHA32(*pPixel));
                                pPixel++;
                            }
                            fprintf(fp, "\n");
                            pRow += lJump;
                        }
                    }
                    fclose(fp);
                }
                break;
        }
    }
}

HX_RESULT PXImage::GetIterator(PXImageIterator** ppIterator, UINT32 ulType)
{
    HX_RESULT        retVal = HXR_OK;
    PXImageIterator* pIter  = NULL;

    switch (ulType)
    {
        case kIterLRTB:
            pIter = (PXImageIterator*) new PXImageIteratorLRTB(this);
            break;
        case kIterLRBT:
            pIter = (PXImageIterator*) new PXImageIteratorLRBT(this);
            break;
        case kIterRLTB:
            pIter = (PXImageIterator*) new PXImageIteratorRLTB(this);
            break;
        case kIterRLBT:
            pIter = (PXImageIterator*) new PXImageIteratorRLBT(this);
            break;
        case kIterTBLR:
            pIter = (PXImageIterator*) new PXImageIteratorTBLR(this);
            break;
        case kIterTBRL:
            pIter = (PXImageIterator*) new PXImageIteratorTBRL(this);
            break;
        case kIterBTLR:
            pIter = (PXImageIterator*) new PXImageIteratorBTLR(this);
            break;
        case kIterBTRL:
            pIter = (PXImageIterator*) new PXImageIteratorBTRL(this);
            break;
    }

    if (pIter)
    {
        *ppIterator = pIter;
    }
    else
    {
        *ppIterator = NULL;
        retVal      = HXR_OUTOFMEMORY;
    }

    return retVal;
}

void PXImage::SelfDetermineHasAlpha()
{
    if (m_bInitialized)
    {
        if (m_cBitmapInfo.biBitCount == 32 &&
            CompressionSupported())
        {
            // Pixels are stored as 0xAARRGGBB. If the image doesn't
            // use alpha, then all the AA bytes will be 0. If any
            // alpha bytes are non-zero, then the image uses alpha.
            BOOL    bHasAlpha = FALSE;
            UINT32* pRow      = (UINT32*) m_pImageBuffer;
            INT32   lJump     = m_lRowJump >> 2;
            for (UINT32 ulY = (UINT32) m_lSubImageHeight; ulY && !bHasAlpha; ulY--)
            {
                UINT32* pPixel = pRow;
                for (UINT32 ulX = (UINT32) m_lSubImageWidth; ulX && !bHasAlpha; ulX--)
                {
                    if (GETALPHA32(*pPixel++))
                    {
                        bHasAlpha = TRUE;
                    }
                }
                pRow += lJump;
            }
            // Set the member
            m_bHasAlpha = bHasAlpha;
        }
    }
}

void PXImage::PreMultiplyAlphaChannel(UINT32 ulColor)
{
    if (m_bInitialized && m_bHasAlpha)
    {
        UINT32  ulR   = GETRED32(ulColor);
        UINT32  ulG   = GETGREEN32(ulColor);
        UINT32  ulB   = GETBLUE32(ulColor);
        UINT32* pRow  = (UINT32*) m_pImageBuffer;
        INT32   lJump = m_lRowJump >> 2;
        for (INT32 lY = m_lSubImageHeight; lY; lY--)
        {
            UINT32* pPix = pRow;
            for (INT32 lX = m_lSubImageWidth; lX; lX--)
            {
                UINT32 ulA = GETALPHA32(*pPix);
                ulA        = (ulA < 128 ? ulA : ulA + 1);
                *pPix   = MAKE_RGB32(ALPHABLEND(ulR, GETRED32(*pPix),   ulA),
                                     ALPHABLEND(ulG, GETGREEN32(*pPix), ulA),
                                     ALPHABLEND(ulB, GETBLUE32(*pPix),  ulA));
                pPix++;
            }
            pRow += lJump;
        }
        // Clear the HasAlpha flag, since we just removed the alpha channel
        m_bHasAlpha = FALSE;
    }
}

BOOL PXImage::CompressionSupported()
{
    BOOL bRet = FALSE;

    if (m_cBitmapInfo.biCompression == HX_RGB)
    {
        bRet = TRUE;
    }

    return bRet;
}

