/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: pxpngrnd.cpp,v 1.4.22.1 2004/07/09 01:52:28 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 "hxcom.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxcore.h"
#include "hxrendr.h"
#include "hxhyper.h"
#include "hxplugn.h"
#include "hxwin.h"
#include "hxasm.h"
#include "hxevent.h"
#include "hxvsurf.h"
#include "hxver.h"
#include "hxupgrd.h"
#include "hxengin.h"
#include "hxmon.h"
#include "hxprefs.h"
#include "hxerror.h"

// pncont
#include "hxbuffer.h"
#include "hxstring.h"

// pnmisc
#include "unkimp.h"
#include "baseobj.h"
#include "hxtick.h"

// baserend
#include "baserend.h"
#include "vbasernd.h"

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

// pxcomlib
#include "glist.h"
#include "gstring.h"
#include "pxutil.h"
#include "pxcolor.h"
#include "parseurl.h"
#include "pxrect.h"
#include "pximage.h"
#include "pxtransp.h"

// libpng
#include "png.h"

// pxpnglib
#include "pxpngdec.h"

// pxpngrnd
#include "pngrmlog.h"
#include "pxpngrnd.h"
#include "pngrdll.ver"

#ifdef _AIX
#include "dllpath.h"
ENABLE_MULTILOAD_DLLACCESS_PATHS(Pxpngrnd);
#endif

// Define whether we're row inverted or not
#if defined(_WINDOWS)
#define ROWS_INVERTED TRUE
#else
#define ROWS_INVERTED FALSE
#endif

const char* const PXPNGRenderer::m_pszName        = "PNG";
const char* const PXPNGRenderer::m_pszDescription = "Helix PNG Renderer Plugin";
const char* const PXPNGRenderer::m_ppszMimeType[] = {"application/vnd.rn-pngstream", NULL};

PXPNGRenderer::PXPNGRenderer() : CRNVisualBaseRenderer()
{
    MLOG_LEAK("CON PXPNGRenderer this=0x%08x\n", this);
    m_ulImageWidth              = 0;
    m_ulImageHeight             = 0;
    m_ulNumPackets              = 0;
    m_ulBackgroundColor         = 0x00000000;
    m_pURLStr                   = NULL;
    m_pImage                    = NULL;
    m_pDisplayImage             = NULL;
    m_pDecode                   = NULL;
    m_ulPacketIndex             = 0;
    m_bFirstDraw                = TRUE;
    m_bDecodeFinished           = FALSE;
    m_ulBackgroundOpacity       = 255;
    m_ulMediaOpacity            = 255;
    m_ulMediaChromaKey          = 0;
    m_bMediaChromaKeySpecified  = FALSE;
    m_ulMediaChromaKeyTolerance = 0;
    m_ulMediaChromaKeyOpacity   = 0;
    m_bImageUsesAlphaChannel    = FALSE;
    m_bDisplayUsesAlphaChannel  = FALSE;
};

PXPNGRenderer::~PXPNGRenderer()
{
    MLOG_LEAK("DES PXPNGRenderer this=0x%08x\n", this);
    HX_RELEASE(m_pURLStr);
    HX_RELEASE(m_pImage);
    HX_RELEASE(m_pDisplayImage);
    HX_RELEASE(m_pDecode);
};

STDMETHODIMP PXPNGRenderer::QueryInterface(REFIID riid, void** ppvObj)
{
    return CRNVisualBaseRenderer::QueryInterface(riid, ppvObj);
}

STDMETHODIMP_(UINT32) PXPNGRenderer::AddRef()
{
    return CRNVisualBaseRenderer::AddRef();
}


STDMETHODIMP_(UINT32) PXPNGRenderer::Release()
{
    return CRNVisualBaseRenderer::Release();
}

STDMETHODIMP PXPNGRenderer::OnHeader(IHXValues* pHeader)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\png.log", (s, "OnHeader(0x%08X)\n", this));
#endif
    HX_RESULT retVal = HXR_FAIL;

    if (pHeader)
    {
        // Check presentation stream and content versions
        retVal = CheckStreamVersions(pHeader);
        if (SUCCEEDED(retVal))
        {
            // Get the opaque data from the stream header
            IHXBuffer* pOpaque = NULL;
            retVal              = pHeader->GetPropertyBuffer("OpaqueData", pOpaque);
            if (SUCCEEDED(retVal))
            {
                // Unpack the stream header
                BYTE* pBuf = pOpaque->GetBuffer();
                UnPack32(pBuf, m_ulImageWidth);
                UnPack32(pBuf, m_ulImageHeight);
                UnPack32(pBuf, m_ulNumPackets);
                UnPack32(pBuf, m_ulBackgroundColor);
                UINT16 usURLSize = 0;
                UnPack16(pBuf, usURLSize);
                if (usURLSize)
                {
                    HX_RELEASE(m_pURLStr);
                    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                                   (void**) &m_pURLStr);
                    if (SUCCEEDED(retVal))
                    {
                        retVal = m_pURLStr->Set((const BYTE*) pBuf, (UINT32) usURLSize);
                    }
                }
                if (SUCCEEDED(retVal))
                {
                    // Create the raw decoded image
                    HX_RELEASE(m_pImage);
                    retVal = PXImage::CreateObject(&m_pImage);
                    if (SUCCEEDED(retVal))
                    {
                        // AddRef the object
                        m_pImage->AddRef();
                        // Init the image
                        retVal = m_pImage->Create((INT32) m_ulImageWidth,  // width
                                                  (INT32) m_ulImageHeight, // height
                                                  32,                      // bpp
                                                  HX_RGB,                 // color format
                                                  ROWS_INVERTED,           // row inversion
                                                  TRUE);                   // always allocate
                        if (SUCCEEDED(retVal))
                        {
                            // Fill it initially with the background color
                            m_pImage->Fill32(m_ulBackgroundColor);
                            // Create the display image
                            HX_RELEASE(m_pDisplayImage);
                            retVal = PXImage::CreateObject(&m_pDisplayImage);
                            if (SUCCEEDED(retVal))
                            {
                                // AddRef the object
                                m_pDisplayImage->AddRef();
                                // Init the display image
                                retVal = m_pDisplayImage->Create((INT32) m_ulImageWidth,  // width
                                                                 (INT32) m_ulImageHeight, // height
                                                                 32,                      // bpp
                                                                 HX_RGB,                 // color format
                                                                 ROWS_INVERTED,           // row inversion
                                                                 TRUE);                   // always allocate
                                if (SUCCEEDED(retVal))
                                {
                                    // Fill it initially with the background color
                                    m_pDisplayImage->Fill32(m_ulBackgroundColor);
                                    // Add the alpha channel to the background color
                                    UINT32 ulAlpha = 255 - m_ulBackgroundOpacity;
                                    m_ulBackgroundColor = (m_ulBackgroundColor & 0x00FFFFFF) |
                                                          (ulAlpha << 24);
                                    // Create the PNG decode object
                                    HX_RELEASE(m_pDecode);
                                    m_pDecode = new PXPNGDecode();
                                    if (m_pDecode)
                                    {
                                        // AddRef the object
                                        m_pDecode->AddRef();
                                        // Init the packet index
                                        m_ulPacketIndex = 0;
                                    }
                                    else
                                    {
                                        retVal = HXR_OUTOFMEMORY;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            HX_RELEASE(pOpaque);
        }
        else
        {
            AddMimeToUpgradeCollection(m_ppszMimeType[0]);
        }
    }

    return retVal;
}


STDMETHODIMP PXPNGRenderer::OnPacketNoOffset(IHXPacket* pPacket)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\png.log", (s, "OnPacketNoOffset(0x%08X)\n", this));
#endif
    HX_RESULT retVal = HXR_OK;

    if (pPacket)
    {
        if (m_pImage && m_pDecode)
        {
            if (!pPacket->IsLost())
            {
                // If the valid flag is TRUE, life is good. However, if valid is
                // FALSE, then we've lost a packet and we cannot recover, so
                // don't even try it.
                if (m_pDecode->GetValid())
                {
                    IHXBuffer* pBuffer = pPacket->GetBuffer();
                    if (pBuffer)
                    {
                        if (!m_ulPacketIndex)
                        {
                            // This is the first packet, so we need to first Init()
                            retVal = m_pDecode->Init(m_pContext,
                                                     pBuffer,
                                                     TRUE);
                            if (SUCCEEDED(retVal))
                            {
                                // Get the image store from the PXImage
                                IHXBuffer* pImageStore = NULL;
                                retVal = m_pImage->GetImageStore(&pImageStore);
                                if (SUCCEEDED(retVal))
                                {
                                    // Set the decompress params
                                    retVal = m_pDecode->SetDecompressParam(pImageStore,
                                                                           (UINT32) m_pImage->GetWidth(),
                                                                           (UINT32) m_pImage->GetHeight(),
                                                                           (UINT32) m_pImage->GetRowStride(),
                                                                           m_pImage->GetBitsPerPixel(),
                                                                           m_pImage->GetFormat(),
                                                                           m_pImage->GetRowsInverted());
                                }
                                HX_RELEASE(pImageStore);
                            }
                        }
                        if (SUCCEEDED(retVal))
                        {
                            // Process the packet
                            retVal = m_pDecode->Decompress(pBuffer);
                            if (SUCCEEDED(retVal))
                            {
                                // Increment the packet index
                                m_ulPacketIndex++;
                                // If we're done, then we can delete the decompress object
                                if (m_pDecode->IsFinished())
                                {
                                    // Set the flag that says whether or not
                                    // the raw image uses an alpha channel.
                                    // Depending upon the externally applied
                                    // transparency parameters, this could be different
                                    // from the flag that says whether or not the
                                    // display buffer uses the alpha channel.
                                    m_bImageUsesAlphaChannel = m_pDecode->UsesAlphaChannel();
                                    // Release the decompress object
                                    HX_RELEASE(m_pDecode);
                                    // Initially update the display buffer
                                    retVal = UpdateDisplayBuffer();
                                    if (SUCCEEDED(retVal))
                                    {
                                        // Set the flag
                                        m_bDecodeFinished = TRUE;
                                    }
                                }
                            }
                            else
                            {
                                // Clear the valid flag
                                m_pDecode->SetValid(FALSE);
                                // Reset the error return - we don't want GetPacket to report failure
                                retVal = HXR_OK;
                            }
                        }
                    }
                    else
                    {
                        retVal = HXR_FAIL;
                    }
                    HX_RELEASE(pBuffer);
                }
            }
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

STDMETHODIMP PXPNGRenderer::OnTimeSyncOffset(UINT32 ulTime)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\png.log",
               (s, "0x%08x::OnTimeSyncOffset(%lu)\n", this, ulTime));
#endif
    // We should force a redraw ONLY on the first time sync after the image
    // has finished decoding
    if (m_bDecodeFinished && m_bFirstDraw)
    {
        // Redraw our data by damaging the entire area of our data
        HXxSize size;
        m_pSite->GetSize(size);
        HXxRect damageRect = {0, 0, size.cx, size.cy};
        m_pSite->DamageRect(damageRect);
        m_pSite->ForceRedraw();
        // Clear the first draw flag
        m_bFirstDraw = FALSE;
    }

    return HXR_OK;
}

STDMETHODIMP PXPNGRenderer::GetWindowSize(REF(HXxSize) rSize)
{
    rSize.cx = m_ulImageWidth;
    rSize.cy = m_ulImageHeight;
    return HXR_OK;
}

STDMETHODIMP PXPNGRenderer::IsMouseOverActiveLink(INT16 x, INT16 y, REF(BOOL) rbActive, REF(IHXBuffer*) rpLink)
{
    HX_RESULT retVal = HXR_OK;

    // By default we are NOT over a link
    rbActive = FALSE;

    // Make sure mouse is in the window
    if (x >= 0 && x < (INT16) m_ulImageWidth &&
        y >= 0 && y < (INT16) m_ulImageHeight)
    {
        // Make sure a URL string exists
        if (m_pURLStr)
        {
            rbActive = TRUE;
            HX_RELEASE(rpLink);
            rpLink = m_pURLStr;
            rpLink->AddRef();
        }
    }

    return retVal;
}

STDMETHODIMP PXPNGRenderer::RMASurfaceUpdate(IHXVideoSurface* pSurface)
{
#ifdef XXXMEH_DEBUG_LOG
    DEBUG_OUTF("c:\\png.log", (s, "RMASurfaceUpdate(0x%08X,0x%08X)\n", this, pSurface));
#endif
    if (pSurface && m_pSite && m_pDisplayImage && m_bDecodeFinished)
    {
        // Get the size of the site
        HXxSize size;
        m_pSite->GetSize(size);
        // Set up the dst rect
        HXxRect rDestRect = { 0, 0, size.cx, size.cy};
        // Set up the src rect
        HXxRect rSrcRect  = { 0,
                              0,
                              m_pDisplayImage->GetWidth(),
                              m_pDisplayImage->GetHeight() };
        // Set the values in the bitmap info header
        HXBitmapInfoHeader cHeader;
        cHeader.biSize          = 40;
        cHeader.biWidth         = m_pDisplayImage->GetWidth();
        cHeader.biHeight        = m_pDisplayImage->GetHeight();
        cHeader.biPlanes        = 1;
        cHeader.biBitCount      = 32;
        cHeader.biCompression   = (m_bDisplayUsesAlphaChannel ? HX_ARGB : HX_RGB);
        cHeader.biSizeImage     = 0;
        cHeader.biXPelsPerMeter = 0;
        cHeader.biYPelsPerMeter = 0;
        cHeader.biClrUsed       = 0;
        cHeader.biClrImportant  = 0;
        cHeader.rcolor          = 0;
        cHeader.gcolor          = 0;
        cHeader.bcolor          = 0;
        // Get the image store
        IHXBuffer* pBuffer = NULL;
        m_pDisplayImage->GetImageStore(&pBuffer);
        if (pBuffer)
        {
//            char szDbgStr[128];
//            DEBUGPRINTF(szDbgStr, "0x%08x PXPNGRenderer Blts alpha=%lu tick=%lu\n",
//                        this, m_bDisplayUsesAlphaChannel, HX_GET_BETTERTICKCOUNT());
            // Blit to the video surface
            pSurface->Blt(pBuffer->GetBuffer(),
                          &cHeader,
                          rDestRect,
                          rSrcRect);
        }
        HX_RELEASE(pBuffer);
    }

    return HXR_OK;
}

HX_RESULT PXPNGRenderer::RMASurfaceUpdate2(IHXSubRectVideoSurface* pSurface,
                                           HXxRect*                 pExtents,
                                           HXxBoxRegion*              pDirtyRegion )
{
    if (pSurface && m_pSite && m_pDisplayImage && m_bDecodeFinished)
    {
        // Get the size of the site
        HXxSize size;
        m_pSite->GetSize(size);
        
        //Scale dirty rects.
        float fx = (float)m_pDisplayImage->GetWidth()/(float)size.cx;
        float fy = (float)m_pDisplayImage->GetHeight()/(float)size.cy;

        //Go through each rect in the dirty region and scale it to 
        //generate the src rects.
        HXBOX* pSrcRects = new HXBOX[pDirtyRegion->numRects];
        for( int i=0 ; i<pDirtyRegion->numRects; i++ )
        {
            pSrcRects[i].x1 = (float)pDirtyRegion->rects[i].x1*fx+.5;
            pSrcRects[i].x2 = (float)pDirtyRegion->rects[i].x2*fx+.5;
            pSrcRects[i].y1 = (float)pDirtyRegion->rects[i].y1*fy+.5;
            pSrcRects[i].y2 = (float)pDirtyRegion->rects[i].y2*fy+.5;
        }

        //Set up Src region.
        HXxBoxRegion srcRegion;
        srcRegion.numRects = pDirtyRegion->numRects;
        srcRegion.rects    = pSrcRects;
        
        // Set the values in the bitmap info header
        HXBitmapInfoHeader cHeader;
        cHeader.biSize          = 40;
        cHeader.biWidth         = m_pDisplayImage->GetWidth();
        cHeader.biHeight        = m_pDisplayImage->GetHeight();
        cHeader.biPlanes        = 1;
        cHeader.biBitCount      = 32;
        cHeader.biCompression   = (m_bDisplayUsesAlphaChannel ? HX_ARGB : HX_RGB);
        cHeader.biSizeImage     = 0;
        cHeader.biXPelsPerMeter = 0;
        cHeader.biYPelsPerMeter = 0;
        cHeader.biClrUsed       = 0;
        cHeader.biClrImportant  = 0;
        cHeader.rcolor          = 0;
        cHeader.gcolor          = 0;
        cHeader.bcolor          = 0;
        
        // Get the image store
        IHXBuffer* pBuffer = NULL;
        m_pDisplayImage->GetImageStore(&pBuffer);
        if (pBuffer)
        {
            // Blit to the video surface
            pSurface->BltSubRects(pBuffer->GetBuffer(),
                                  &cHeader,
                                  pDirtyRegion,
                                  &srcRegion, 1.0/fx, 1.0/fy);
        }
        HX_RELEASE(pBuffer);

        HX_VECTOR_DELETE( pSrcRects );
    }
    return HXR_OK;
}

STDMETHODIMP PXPNGRenderer::HandleClick(INT16 x, INT16 y)
{
    if (m_pHyperNavigate && m_pURLStr)
    {
        const char* pszURL    = (const char*) m_pURLStr->GetBuffer();
        if (strlen(pszURL) > 0)
        {
            const char* pszTarget = (strstr(pszURL, "command:") ? "_player" : NULL);
            m_pHyperNavigate->GoToURL(pszURL, pszTarget);
        }
    }

    return HXR_OK;
}

STDMETHODIMP PXPNGRenderer::SetPropertyULONG32(const char* pName, ULONG32 ulVal)
{
    HX_RESULT retVal = HXR_OK;

    if (pName)
    {
        // Check to see if this is a property we're interested
        // in monitoring
        BOOL bUpdateNeeded = FALSE;
        if (!strcmp(pName, "backgroundOpacity"))
        {
            if (ulVal > 255) ulVal = 255;
            if (ulVal != m_ulBackgroundOpacity)
            {
                bUpdateNeeded = TRUE;
                // Update the background color
                UINT32 ulAlpha = 255 - ulVal;
                m_ulBackgroundColor = (m_ulBackgroundColor & 0x00FFFFFF) |
                                      (ulAlpha << 24);
            }
            m_ulBackgroundOpacity = ulVal;
        }
        else if (!strcmp(pName, "mediaOpacity"))
        {
            if (ulVal > 255) ulVal = 255;
            if (ulVal != m_ulMediaOpacity) bUpdateNeeded = TRUE;
            m_ulMediaOpacity = ulVal;
        }
        else if (!strcmp(pName, "chromaKey"))
        {
            if (ulVal != m_ulMediaChromaKey) bUpdateNeeded = TRUE;
            m_ulMediaChromaKey         = ulVal;
            m_bMediaChromaKeySpecified = TRUE;
        }
        else if (!strcmp(pName, "chromaKeyTolerance"))
        {
            if (ulVal != m_ulMediaChromaKeyTolerance) bUpdateNeeded = TRUE;
            m_ulMediaChromaKeyTolerance = ulVal;
        }
        else if (!strcmp(pName, "chromaKeyOpacity"))
        {
            if (ulVal > 255) ulVal = 255;
            if (ulVal != m_ulMediaChromaKeyOpacity) bUpdateNeeded = TRUE;
            m_ulMediaChromaKeyOpacity = ulVal;
        }
        // Did we change any of the above parameters?
        if (bUpdateNeeded)
        {
            // Have we done the initial display buffer update?
            // If we haven't, then we don't need to do anything, since
            // the initial display buffer update will capture all
            // of these transparency-related changes.
            if (m_bDecodeFinished)
            {
                // We have already done the initial display buffer update,
                // so we will need to re-blt the source image buffer
                // onto the display buffer using the new transparency
                // parameters which were just updated. This is probably
                // the case when one of our transparency-related parameters
                // is getting animated in SMIL.
                UpdateDisplayBuffer();
            }
        }
        // Now pass it off to our grandparent class
        retVal = CRNBaseRenderer::SetPropertyULONG32(pName, ulVal);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP PXPNGRenderer::SetPropertyCString(const char* pName, IHXBuffer* pVal)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pValues)
    {
        // Check for the default SMIL namespace
        BOOL bUpdateNeeded = FALSE;
        if (!strcmp(pName, "SMILDefaultNamespace"))
        {
            if (m_ulBackgroundOpacity != 0)
            {
                bUpdateNeeded = TRUE;
                // Update the background color
                m_ulBackgroundColor = (m_ulBackgroundColor & 0x00FFFFFF) | 0xFF000000;
            }
            m_ulBackgroundOpacity = 0;
        }
        // Do we need to update the display buffer
        if (bUpdateNeeded)
        {
            // Have we done the initial display buffer update?
            // If we haven't, then we don't need to do anything, since
            // the initial display buffer update will capture all
            // of these transparency-related changes.
            if (m_bDecodeFinished)
            {
                // We have already done the initial display buffer update,
                // so we will need to re-blt the source image buffer
                // onto the display buffer using the new transparency
                // parameters which were just updated. This is probably
                // the case when one of our transparency-related parameters
                // is getting animated in SMIL.
                UpdateDisplayBuffer();
            }
        }
        // Now pass it off to our grandparent class
        retVal = CRNBaseRenderer::SetPropertyCString(pName, pVal);
    }

    return retVal;
}

HX_RESULT STDAPICALLTYPE PXPNGRenderer::HXCreateInstance(IUnknown** ppIUnknown)
{
    HX_RESULT retVal = HXR_OK;

    if (ppIUnknown)
    {
        // Set default
        *ppIUnknown = NULL;
        // Create the object
        PXPNGRenderer *pObj = new PXPNGRenderer();
        if (pObj)
        {
            // QI for IUnknown
            retVal = pObj->QueryInterface(IID_IUnknown, (void**) ppIUnknown);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        if (FAILED(retVal))
        {
            HX_DELETE(pObj);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return HXR_OK;
}

void PXPNGRenderer::DoTransparencyBlt(IHXBuffer* pSrc,
                                      IHXBuffer* pDst,
                                      BOOL        bImageUsesAlphaChannel,
                                      UINT32      ulBackgroundColor,
                                      UINT32      ulMediaOpacity,
                                      BOOL        bMediaChromaKeySpecified,
                                      UINT32      ulMediaChromaKey,
                                      UINT32      ulMediaChromaKeyTolerance,
                                      UINT32      ulMediaChromaKeyOpacity,
                                      REF(BOOL)   rbDisplayUsesAlphaChannel)
{
    if (pSrc && pDst)
    {
        // Get the buffer pointers and the number
        // of 32bpp pixels in each buffer
        UINT32* pSrcPix     = (UINT32*) pSrc->GetBuffer();
        UINT32* pDstPix     = (UINT32*) pDst->GetBuffer();
        UINT32  ulNumSrcPix = pSrc->GetSize() >> 2;
        UINT32  ulNumDstPix = pDst->GetSize() >> 2;
        if (pSrcPix && pDstPix &&
            ulNumSrcPix > 0 &&
            ulNumSrcPix == ulNumDstPix)
        {
            // Initially set the display alpha channel
            // flag to FALSE
            rbDisplayUsesAlphaChannel = FALSE;
            // Is the background transparent?
            BOOL bBgTransparent = (ARGB32_ALPHA(ulBackgroundColor) == 255 ? TRUE : FALSE);
            // Compute the chroma alpha with pre-shift
            UINT32 ulChromaAlpha = 0;
            if (bMediaChromaKeySpecified)
            {
                if (ulMediaChromaKeyOpacity > 255) ulMediaChromaKeyOpacity = 255;
                ulChromaAlpha = (255 - ulMediaChromaKeyOpacity) << 24;
            }
            // Run through the pixels
            // XXXMEH - optimization
            // We can and should break out a few of these cases into
            // separate loops which don't have any conditional checks
            // in them (for instance, the straight copy). The compiler
            // should be able to optimize these uncluttered loops much
            // better than the one-size-fits-all loop below.
            while (ulNumSrcPix--)
            {
                // Get the src pixel
                UINT32 ulSrcPix = *pSrcPix++;
                // First apply the chroma key
                if (bMediaChromaKeySpecified)
                {
                    if (DoesChromaKeyMatch(ulSrcPix, ulMediaChromaKey, ulMediaChromaKeyTolerance))
                    {
                        ulSrcPix = (ulSrcPix & 0x00FFFFFF) | ulChromaAlpha;
                    }
                }
                // Next apply the media opacity
                if (ulMediaOpacity < 255)
                {
                    UINT32 ulAlpha      = ARGB32_ALPHA(ulSrcPix);
                    UINT32 ulOpacity    = 255 - ulAlpha;
                    UINT32 ulNewOpacity = ulOpacity * ulMediaOpacity / 255;
                    UINT32 ulNewAlpha   = 255 - ulNewOpacity;
                    ulSrcPix            = (ulSrcPix & 0x00FFFFFF) | (ulNewAlpha << 24);
                }
                // Now alpha blend onto the background color
                // Note that we only need to do this if the background
                // is not fully transparent, because if the background
                // is fully transparent, is has no effect whatsoever.
                // If this pixel is opaque, then we don't need to
                // do anything here, since the opaque pixel will
                // completely occlude the background color
                if (!bBgTransparent && (ulSrcPix & 0xFF000000))
                {
                    UINT32 ulAM    = ARGB32_ALPHA(ulSrcPix);
                    UINT32 ulAB    = ARGB32_ALPHA(ulBackgroundColor);
                    UINT32 ulDen   = (65535 - ulAM * ulAB);
                    UINT32 ulNumL  = ulAM * (255 - ulAB);
                    UINT32 ulNumR  = 255 * (255 - ulAM);
                    UINT32 ulRed   = (ulNumL * ARGB32_RED(ulBackgroundColor) +
                                      ulNumR * ARGB32_RED(ulSrcPix)) / ulDen;
                    UINT32 ulGreen = (ulNumL * ARGB32_GREEN(ulBackgroundColor) +
                                      ulNumR * ARGB32_GREEN(ulSrcPix)) / ulDen;
                    UINT32 ulBlue  = (ulNumL * ARGB32_BLUE(ulBackgroundColor) +
                                      ulNumR * ARGB32_BLUE(ulSrcPix)) / ulDen;
                    UINT32 ulAlpha = ulAM * ulAB / 255;
                    ulSrcPix       = ((ulAlpha << 24) & 0xFF000000) |
                                     ((ulRed   << 16) & 0x00FF0000) |
                                     ((ulGreen <<  8) & 0x0000FF00) |
                                     ( ulBlue         & 0x000000FF);
                }
                // Check the final alpha value
                if (ulSrcPix & 0xFF000000)
                {
                    rbDisplayUsesAlphaChannel = TRUE;
                }
                // Assign the dst pixel
                *pDstPix++ = ulSrcPix;
            }
        }
    }
}

HX_RESULT PXPNGRenderer::UpdateDisplayBuffer()
{
    HX_RESULT retVal = HXR_OK;

    if (m_pImage && m_pDisplayImage)
    {
        // Get the image store of the decoded image
        IHXBuffer* pSrc = NULL;
        retVal = m_pImage->GetImageStore(&pSrc);
        if (SUCCEEDED(retVal))
        {
            // Get the image store of the display image
            IHXBuffer* pDst = NULL;
            retVal = m_pDisplayImage->GetImageStore(&pDst);
            if (SUCCEEDED(retVal))
            {
                // Process any transparency parameters
                DoTransparencyBlt(pSrc,
                                  pDst,
                                  m_bImageUsesAlphaChannel,
                                  m_ulBackgroundColor,
                                  m_ulMediaOpacity,
                                  m_bMediaChromaKeySpecified,
                                  m_ulMediaChromaKey,
                                  m_ulMediaChromaKeyTolerance,
                                  m_ulMediaChromaKeyOpacity,
                                  m_bDisplayUsesAlphaChannel);
            }
            HX_RELEASE(pDst);
        }
        HX_RELEASE(pSrc);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

void PXPNGRenderer::_AttachSite()
{
    //Lets subscribe to the sub rect messages, HX_SURFACE_UPDATE2.
    IHXSubRectSite* pSubRectSite = NULL;
    m_pSite->QueryInterface(IID_IHXSubRectSite, (void**)&pSubRectSite);
    if( pSubRectSite )
    {
        //If so, since IHXSubRectSite inheirits from IHXSite, lets
        //just swap the pointers and sign up for the service.
        HX_RELEASE( m_pSite );
        m_pSite = pSubRectSite;
        pSubRectSite->SendSubRectMessages(TRUE);
    }
}

STDMETHODIMP PXPNGRenderer::GetName(REF(const char*) rpszName)
{
    rpszName = (const char*) m_pszName;
    return HXR_OK;
}

STDMETHODIMP PXPNGRenderer::GetDescription(REF(const char*) rpszDescription)
{
    rpszDescription = (const char*) m_pszDescription;
    return HXR_OK;
}

STDMETHODIMP PXPNGRenderer::GetMimeTypes(REF(const char**) rppszMimeType)
{
    rppszMimeType = (const char**) m_ppszMimeType;
    return HXR_OK;
}

STDMETHODIMP_(UINT32) PXPNGRenderer::GetPluginVersion()
{
    return TARVER_ULONG32_VERSION;
}

STDMETHODIMP_(UINT32) PXPNGRenderer::GetInitialGranularity()
{
    return 200;
}

