/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: pxrend.cpp,v 1.4.6.1 2004/07/09 01:51:47 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 "ihxcookies.h"
#include "hxerror.h"

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

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

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

// pxcomlib
#include "glist.h"
#include "gstring.h"
#include "pxutil.h"
#include "pxcolor.h"
#include "parseurl.h"
#include "pxcmpmgr.h"
#include "pxrect.h"
#include "pximage.h"
#include "pxrndcod.h"
#include "pxeffect.h"
#include "rpeffect.h"
#include "wirefmgr.h"
#include "pxcookie.h"
#include "pxcallbk.h"

// pxrend
#include "hlinkmgr.h"
#include "imghelpr.h"
#include "rncodmgr.h"
#include "imagemgr.h"
#include "fxpkgmgr.h"
#include "fxmanagr.h"
#include "pxremlog.h"
#include "pxrend.h"
#include "rprenddll.ver"

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

const char* CRealPixRenderer::m_pszName        = "RealPix";
const char* CRealPixRenderer::m_pszDescription = "Helix RealPix Renderer Plugin";
const char* CRealPixRenderer::m_ppszMimeType[] = {"application/vnd.rn-realpixstream",
                                                  "application/vnd.rn-realpixstream2",
                                                  NULL};

CRealPixRenderer::CRealPixRenderer() : CRNVisualBaseRenderer()
{
    m_pCodecManager              = NULL;
    m_pImageManager              = NULL;
    m_pEffectsManager            = NULL;
    m_pWireFormatManager         = NULL;
    m_pHyperlinkManager          = NULL;
    m_pEffectsPackageManager     = NULL;
    m_pCallback                  = NULL;
    m_pDrawFocus                 = NULL;
    m_bPaused                    = FALSE;
    m_bSeeking                   = FALSE;
    m_ulPerformanceScale         = 2;
    m_ulPerformanceAdjustedDelay = 0;
    m_ulLastDrawTime             = HX_GET_TICKCOUNT();
    m_bIsLive                    = FALSE;
    m_bBltSubRects               = FALSE;
};

CRealPixRenderer::~CRealPixRenderer()
{
    Deallocate();
};

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

    if (ppvObj)
    {
        if (IsEqualIID(riid, IID_IHXFocusNavigation))
        {
            AddRef();
            *ppvObj = (IHXFocusNavigation*) this;
        }
        else
        {
            retVal = CRNVisualBaseRenderer::QueryInterface(riid, ppvObj);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

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


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

STDMETHODIMP CRealPixRenderer::OnHeader(IHXValues *pHeader)
{
    HX_RESULT retVal = HXR_OK;

    if (pHeader)
    {
        // Check presentation stream and content versions
        retVal = CheckStreamVersions(pHeader);
        if (SUCCEEDED(retVal))
        {
            // Deallocate and reset
            Deallocate();
            Reset();

            // Create all the member objects
            retVal = CreateObjects();
            if (SUCCEEDED(retVal))
            {
                retVal = SetupObjects(pHeader);
                if (SUCCEEDED(retVal))
                {
                    retVal = SetupCodecsAndFXPackages();
                    if (SUCCEEDED(retVal))
                    {
                        // Set up quality info for determining scaling factor of the
                        // auto-frame rate scaling code
                        UINT32      ulQualPref = 4; // highest quality, most CPU
                        BOOL        bPresent   = FALSE;
                        IHXBuffer* pBuffer    = NULL;
                        retVal                 = GetPreference("Quality", bPresent, pBuffer);
                        if (SUCCEEDED(retVal) && bPresent)
                        {
                            ulQualPref = (UINT32) atol((const char*) pBuffer->GetBuffer());
                        }
                        HX_RELEASE(pBuffer);

                        // Quality is on a scale of 4 to 0, where 4 means high quality and
                        // 0 means lowest CPU usage. We will map this on to the follofing...
                        //  For 4, we will force redraw to take 1/2 the time
                        //  For 3, we will force redraw to take 1/3 of the time
                        //  For 2, we will force redraw to take 1/4 of the time
                        //  For 1, we will force redraw to take 1/5 of the time
                        //  For 0, we will force redraw to take 1/6 of the time
                        // This obviously maps onto....
                        m_ulPerformanceScale = 6 - ulQualPref;
                    }
                }
            }
        }
        else
        {
            AddMimeToUpgradeCollection(m_ppszMimeType[1]);
            retVal = HXR_FAIL;
        }
#ifdef XXXMEH_DEBUG_OUT
        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                  (s, "Stream Version from Stream Header  = %lu.%lu", 
                  HX_GET_MAJOR_VERSION(m_ulStreamVersion), HX_GET_MINOR_VERSION(m_ulStreamVersion)));
        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                  (s, "Content Version from Stream Header = %lu.%lu", 
                  HX_GET_MAJOR_VERSION(m_ulContentVersion), HX_GET_MINOR_VERSION(m_ulContentVersion)));
        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                  (s, "Highest Supported Stream Version   = %lu.%lu", 
                  HX_GET_MAJOR_VERSION(GetHighestSupportedStreamVersion()),
                  HX_GET_MINOR_VERSION(GetHighestSupportedStreamVersion())));
        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                  (s, "Highest Supported Content Version  = %lu.%lu", 
                  HX_GET_MAJOR_VERSION(GetHighestSupportedContentVersion()),
                  HX_GET_MINOR_VERSION(GetHighestSupportedContentVersion())));
        if (m_pWireFormatManager)
        {
            DEBUG_OUT(m_pErrorMessages, DOL_REALPIX,
                      (s, "Display Width = %lu, Display Height = %lu",
                      m_pWireFormatManager->GetDisplayWidth(),
                      m_pWireFormatManager->GetDisplayHeight()));
            IHXBuffer* pDefaultURLStr = NULL;
            m_pWireFormatManager->GetDefaultURL(pDefaultURLStr);
            DEBUG_OUT(m_pErrorMessages, DOL_REALPIX,
                      (s, "Default Clickthru URL = %s",
                      (pDefaultURLStr ? (const char*) pDefaultURLStr->GetBuffer() : "None")));
            HX_RELEASE(pDefaultURLStr);
        }
#endif
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}


STDMETHODIMP CRealPixRenderer::OnPacketNoOffset(IHXPacket *pPacket)
{
    HX_RESULT retVal = HXR_OK;

    if (pPacket)
    {
#ifdef XXXMEH_DUMP_TIMING_INFO
        static UINT32 ulLastMinute = 0xFFFFFFFF;
        UINT32        ulPlayerTime = m_pPlayer->GetCurrentPlayTime();
        UINT32        ulCurMinute  = ulPlayerTime / 60000;
        if (ulLastMinute != ulCurMinute)
        {
            UINT32 ulPacketTime = pPacket->GetTime();
            INT32  lDiff        = (INT32) ulPacketTime - ulPlayerTime;
            FILE* fp = fopen("pxrend2.log", "a");
            if (fp)
            {
                fprintf(fp, "PlayerTime=%10lu PacketTime=%10lu Diff=%6ld NumImages=%lu\n",
                        ulPlayerTime, ulPacketTime, lDiff, m_pImageManager->GetNumImages());
                fclose(fp);
            }
        }
        ulLastMinute = ulCurMinute;
#endif
        if (!pPacket->IsLost())
        {
            // Get the packet type
            UINT32 ulPacketType = 0;
            retVal              = m_pWireFormatManager->GetPacketType(pPacket, ulPacketType);
            if (SUCCEEDED(retVal))
            {
                if (ulPacketType == PXWireFormatManager::kPacketTypeImageHeader)
                {
                    UINT32      ulHandle     = 0;
                    UINT32      ulLength     = 0;
                    UINT32      ulFlags      = 0;
                    IHXBuffer* pMimeStr     = NULL;
                    UINT32      ulOpaqueSize = 0;
                    retVal                 = m_pWireFormatManager->GetImageHeaderInfo(pPacket, ulHandle, ulLength,
                                                                                      ulFlags, &pMimeStr, ulOpaqueSize);
                    if (SUCCEEDED(retVal))
                    {
#ifdef XXXMEH_DEBUG_OUT
                        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                                  (s, "Image Header Packet (handle=%lu) ts=%lu", ulHandle,
                                   pPacket->GetTime()));
#endif
                        retVal = m_pImageManager->CreateImage(ulHandle,
                                                              ulLength,
                                                              (const char*) pMimeStr->GetBuffer(),
                                                              ulOpaqueSize);
                    }
                    HX_RELEASE(pMimeStr);
                }
                else if (ulPacketType == PXWireFormatManager::kPacketTypeImageData)
                {
                    UINT32 ulHandle = 0;
                    retVal          = m_pWireFormatManager->GetImageDataHandle(pPacket, ulHandle);
                    if (SUCCEEDED(retVal))
                    {
#ifdef XXXMEH_DEBUG_OUT
                        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                                  (s, "Image Data Packet (handle=%lu) ts=%lu", ulHandle,
                                   pPacket->GetTime()));
#endif
                        UINT32 ulOpaqueSize = 0;
                        HX_RESULT rv = m_pImageManager->GetOpaqueDataSize(ulHandle, ulOpaqueSize);
                        if (SUCCEEDED(rv))
                        {
                            IHXBuffer* pOpaque = NULL;
                            IHXBuffer* pData   = NULL;
                            retVal              = m_pWireFormatManager->GetImageDataInfo(pPacket,
                                                                                         ulOpaqueSize,
                                                                                         pOpaque,
                                                                                         pData);
                            if (SUCCEEDED(retVal))
                            {
                                retVal = m_pImageManager->OnImageDataPacket(ulHandle, pOpaque, pData);
                                if (SUCCEEDED(retVal))
                                {
                                    retVal = m_pEffectsManager->CheckPostDurationPacket(ulHandle);
                                }
                            }
                            HX_RELEASE(pOpaque);
                            HX_RELEASE(pData);
                        }
                    }
                }
                else if (ulPacketType == PXWireFormatManager::kPacketTypeEffect)
                {
                    PXEffect* pEffect = NULL;
                    retVal            = m_pWireFormatManager->GetEffectInfo(pPacket, &pEffect);
                    if (SUCCEEDED(retVal))
                    {
#ifdef XXXMEH_DEBUG_OUT
                        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                                  (s, "Effect Packet (start=%lu,type=%lu,handle=%lu,lastuse=%lu) ts=%lu",
                                   pEffect->GetStart(), (UINT32) pEffect->GetEffectType(),
                                   pEffect->GetTarget(), pEffect->GetLastUse(), pPacket->GetTime()));
#endif
                        retVal = m_pEffectsManager->AddEffect(pEffect);
                    }
                    HX_RELEASE(pEffect);
                }
                else if (ulPacketType == PXWireFormatManager::kPacketTypeCookie)
                {
                    UINT32    ulNumCookies = 0;
                    HX_RESULT rv           = m_pWireFormatManager->GetNumCookies(pPacket, ulNumCookies);
                    if (SUCCEEDED(rv))
                    {
#ifdef XXXMEH_DEBUG_OUT
                        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED,
                                  (s, "Cookie Packet (%lu cookies) ts=%lu",
                                   ulNumCookies, pPacket->GetTime()));
#endif
                        if (ulNumCookies > 0)
                        {
                            // Get the IHXCookies interface
                            IHXCookies* pCookies = NULL;
                            m_pContext->QueryInterface(IID_IHXCookies, (void**) &pCookies);
                            if (pCookies)
                            {
                                // Go through each of the cookies
                                for (UINT32 i = 0; i < ulNumCookies; i++)
                                {
                                    IHXBuffer* pURLStr       = NULL;
                                    IHXBuffer* pCookieBuffer = NULL;
                                    rv                        = m_pWireFormatManager->GetCookie(pPacket,
                                                                                                i,
                                                                                                pURLStr,
                                                                                                pCookieBuffer);
                                    if (SUCCEEDED(rv))
                                    {
                                        IHXBuffer* pHostStr = NULL;
                                        IHXBuffer* pPathStr = NULL;
                                        rv                   = GetHostAndPathFromURL((const char*) pURLStr->GetBuffer(),
                                                                 m_pCommonClassFactory,
                                                                                     pHostStr,
                                                                                     pPathStr);
                                        if (SUCCEEDED(rv))
                                        {
                                            rv = pCookies->SetCookies((const char*) pHostStr->GetBuffer(),
                                                                      (const char*) pPathStr->GetBuffer(),
                                                                      pCookieBuffer);
                                        }
                                        HX_RELEASE(pHostStr);
                                        HX_RELEASE(pPathStr);
                                    }
                                    HX_RELEASE(pURLStr);
                                    HX_RELEASE(pCookieBuffer);
                                }
                            }
                            HX_RELEASE(pCookies);
                        }
                    }
                }
#ifdef XXXMEH_DEBUG_OUT
                else
                {
                    DEBUG_OUT(m_pErrorMessages, DOL_REALPIX_EXTENDED, (s, "Unknown packet type"));
                }
#endif
            }
        }
#ifdef XXXMEH_DEBUG_OUT
        else
        {
            DEBUG_OUT(m_pErrorMessages, DOL_REALPIX, (s, "***Packet Lost***"));
        }
#endif
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

STDMETHODIMP CRealPixRenderer::OnTimeSyncOffset(UINT32 ulTime)
{
    HX_RESULT retVal = UpdateDisplay(ulTime);
    if (SUCCEEDED(retVal))
    {
        // Determine if we need to send a backchannel notify
        // This is used with a particular kind of adserver
        if (m_pWireFormatManager->GetRealPixAdsFlag())
        {
            if (m_pWireFormatManager->GetRPACachingAdFlag() &&
                m_pEffectsManager->GetBackChannelNotify())
            {
                // Get a backchannel notify packet
                IHXPacket* pPacket = NULL;
                retVal = m_pWireFormatManager->SetBackChannelInfo(m_pEffectsManager->GetBackChannelHandle(),
                                                                  ulTime,
                                                                  pPacket);
                if (SUCCEEDED(retVal))
                {
                    if (m_pBackChannel)
                    {
#ifdef XXXMEH_DEBUG_OUT
                        DEBUG_OUT(m_pErrorMessages, DOL_REALPIX, (s, "Sending BackChannel packet at %lu", ulTime));
#endif
                        // Send the packet
                        m_pBackChannel->PacketReady(pPacket);
                    }
                }
                HX_RELEASE(pPacket);
            }
        }
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

STDMETHODIMP CRealPixRenderer::OnPreSeek(ULONG32 ulOldTime, ULONG32 ulNewTime)
{
    HX_RESULT retVal = HXR_OK;

    // We need to clear out all effects and delete all
    // current images. If we have seeked past the duration
    // of the presentation, we don't do this, since we won't
    // be getting any more packets.
    if (ulNewTime < m_pWireFormatManager->GetDuration())
    {
        m_pEffectsManager->ClearEffects();
        m_pImageManager->ReleasePresentationImages();
    }

    // Clear the hyperlink manager
    retVal = m_pHyperlinkManager->Init(m_pContext,
                                       m_pWireFormatManager->GetDisplayWidth(),
                                       m_pWireFormatManager->GetDisplayHeight());
    if (SUCCEEDED(retVal))
    {
        // Set the seeking flag
        m_bSeeking = TRUE;
    }

    return retVal;
}

STDMETHODIMP CRealPixRenderer::OnPostSeek(ULONG32 ulOldTime, ULONG32 ulNewTime)
{
    HX_RESULT retVal = HXR_OK;

    // Clear the seeking flag
    m_bSeeking = FALSE;

    return retVal;
}

STDMETHODIMP CRealPixRenderer::EndStream()
{
    // Call our base class's EndStream
    HX_RESULT retVal = CRNBaseRenderer::EndStream();

    if (SUCCEEDED(retVal) && m_pCodecManager && m_pEffectsManager)
    {
        // Now we can release the codec manager
        HX_RELEASE(m_pCodecManager);
        // Release the effects package manager
        HX_RELEASE(m_pEffectsPackageManager);
        // Determine if we need to have post-duration callbacks or not
        if (m_pEffectsManager->GetCheckIndefiniteDuration())
        {
            // We DO currently have indefinite-duration effects
            // executing. Therefore, we need to start getting callbacks
            //
            // Create the callback object
            HX_RELEASE(m_pCallback);
            m_pCallback = new PXCallback();
            if (m_pCallback)
            {
                // AddRef the callback
                m_pCallback->AddRef();
                // Init the callback object
                retVal = m_pCallback->Init(m_pContext, this, FALSE, m_ulLastTimeSync);
                if (SUCCEEDED(retVal))
                {
                    // Schedule a callback kCallbackInterval ms from now
                    retVal = m_pCallback->ScheduleRelativeCallback(kCallbackInterval);
                }
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
        else
        {
            // There are NOT currently any indefinite duration effects
            // executing. Therefore, we can shutdown as normal.
            //
            // Clear out all effects from the effects manager
            m_pEffectsManager->ClearEffects();
        }
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

STDMETHODIMP CRealPixRenderer::GetWindowSize(REF(HXxSize) rSize)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pWireFormatManager)
    {
        rSize.cx = m_pWireFormatManager->GetDisplayWidth();
        rSize.cy = m_pWireFormatManager->GetDisplayHeight();
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

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

    if (m_pHyperlinkManager)
    {
        HXxSize size;
        if (m_pSite)
        {
            m_pSite->GetSize(size);
        }
        else
        {
            if (m_pWireFormatManager)
            {
                size.cx = m_pWireFormatManager->GetDisplayWidth();
                size.cy = m_pWireFormatManager->GetDisplayHeight();
            }
            else
            {
                retVal = HXR_UNEXPECTED;
            }
        }

        if (SUCCEEDED(retVal))
        {
            rbActive = m_pHyperlinkManager->GetActiveLink(x, y, size.cx,
                                                          size.cy, rpLink);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

STDMETHODIMP CRealPixRenderer::RMASurfaceUpdate(IHXVideoSurface* pSurface)
{
    HX_RESULT retVal = HXR_OK;

    if (pSurface)
    {
        if (m_pImageManager && m_pEffectsManager &&
            m_pSite         && m_pWireFormatManager)
        {
            // Was this redraw forced from the effects manager
            HXxRect cSrcRect;
            if (m_pEffectsManager->WasRedrawForced())
            {
                // Yes it was, so get the damage rect from the effects manager
                m_pEffectsManager->GetDamageRect(cSrcRect);
                // XXXMEH - hack for now - use the whole display window
                cSrcRect.left   = 0;
                cSrcRect.top    = 0;
                cSrcRect.right  = m_pWireFormatManager->GetDisplayWidth();
                cSrcRect.bottom = m_pWireFormatManager->GetDisplayHeight();
            }
            else
            {
                // No, it wasn't, so since we cannot get the actual
                // damage rect from the HXxEvent, then we must redraw the
                // whole src window.
                cSrcRect.left   = 0;
                cSrcRect.top    = 0;
                cSrcRect.right  = m_pWireFormatManager->GetDisplayWidth();
                cSrcRect.bottom = m_pWireFormatManager->GetDisplayHeight();
            }
            // Get the current size of the site
            HXxSize cSize;
            retVal = m_pSite->GetSize(cSize);
            if (SUCCEEDED(retVal))
            {
                // Scale the destination rect if necessary
                HXxRect cDstRect;
                if (cSize.cx == (INT32) m_pWireFormatManager->GetDisplayWidth() &&
                    cSize.cy == (INT32) m_pWireFormatManager->GetDisplayHeight())
                {
                    cDstRect = cSrcRect;
                    if (HXxRECT_WIDTH(cDstRect) == 0)
                    {
                        cDstRect.right = cDstRect.left + cSize.cx;
                    }
                    if (HXxRECT_HEIGHT(cDstRect) == 0)
                    {
                        cDstRect.bottom = cDstRect.top + cSize.cy;
                    }
                }
                else
                {
                    cDstRect.left   = cSrcRect.left   * cSize.cx / m_pWireFormatManager->GetDisplayWidth();
                    cDstRect.top    = cSrcRect.top    * cSize.cy / m_pWireFormatManager->GetDisplayHeight();
                    cDstRect.right  = cSrcRect.right  * cSize.cx / m_pWireFormatManager->GetDisplayWidth();
                    cDstRect.bottom = cSrcRect.bottom * cSize.cy / m_pWireFormatManager->GetDisplayHeight();
                }
                // Get the display image
                PXImage* pDisplayImage = NULL;
                retVal                 = m_pImageManager->GetDisplayImage(&pDisplayImage);
                if (SUCCEEDED(retVal))
                {
                    retVal = pDisplayImage->DrawToHXSurface(pSurface, cSrcRect, cDstRect);
                    if (SUCCEEDED(retVal))
                    {
                        // Now we can reset the effect manager's damage rect IF
                        // the redraw was forced from it
                        if (m_pEffectsManager->WasRedrawForced())
                        {
                            m_pEffectsManager->SetRedrawForced(FALSE);
                            m_pEffectsManager->ResetDamage();
                        }
                    }
                }
                HX_RELEASE(pDisplayImage);
            }
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

STDMETHODIMP CRealPixRenderer::HandleClick(INT16 x, INT16 y)
{
    BOOL        bActive     = FALSE;
    IHXBuffer* pLinkBuffer = NULL;
    HX_RESULT   retVal      = IsMouseOverActiveLink(x, y, bActive, pLinkBuffer);
    if (SUCCEEDED(retVal) && bActive)
    {
        const char* pszCommand = strstr((const char*) pLinkBuffer->GetBuffer(), "command:");
	m_pHyperNavigate->GoToURL((const char*) pLinkBuffer->GetBuffer(), (pszCommand ? "_player" : (const char*) NULL));
    }
    HX_RELEASE(pLinkBuffer);

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

STDMETHODIMP CRealPixRenderer::DetachSite()
{
    // Call the super-class's DetachSite()
    HX_RESULT retVal = CRNVisualBaseRenderer::DetachSite();
    if (SUCCEEDED(retVal))
    {
        // Once the site is detached, then we shouldn't attempt to
        // draw to the display any more. Therefore, we will remove
        // any pending callbacks (if there were any).
        if (m_pCallback)
        {
            if (m_pCallback->IsCallbackPending())
            {
                m_pCallback->RemovePendingCallback();
            }
        }
    }

    return retVal;
}

#if 0 // XXXMEH - for testing

STDMETHODIMP CRealPixRenderer::HandleEvent(HXxEvent* pEvent)
{
    HX_RESULT retVal = HXR_OK;

    if (pEvent)
    {
        // Set defaults
        pEvent->handled = FALSE;
        pEvent->result  = HXR_OK;
        // Switch on event type
        switch (pEvent->event)
        {
            case HX_CHAR:
                {
                    sprintf(szDbgStr, /* Flawfinder: ignore */
                            "HX_CHAR\n\tparam1=0x%08x\n\tparam2=0x%08x\n",
                            pEvent->param1, pEvent->param2);
                    OutputDebugString(szDbgStr);
                }
                break;
            case HX_KEY_DOWN:
                {
                    sprintf(szDbgStr, /* Flawfinder: ignore */
                            "HX_KEY_DOWN\n\tparam1=0x%08x\n\tparam2=0x%08x\n",
                            pEvent->param1, pEvent->param2);
                    OutputDebugString(szDbgStr);
                }
                break;
            case HX_KEY_UP:
                {
                    sprintf(szDbgStr, /* Flawfinder: ignore */
                            "HX_KEY_UP\n\tparam1=0x%08x\n\tparam2=0x%08x\n",
                            pEvent->param1, pEvent->param2);
                    OutputDebugString(szDbgStr);
                }
                break;
#if defined(_WINDOWS)
            case WM_CHAR:
                {
                    char szDbgStr[128]; /* Flawfinder: ignore */
                    sprintf(szDbgStr, /* Flawfinder: ignore */
                            "WM_CHAR\n\tparam1=0x%08x\n\tparam2=0x%08x\n",
                            pEvent->param1, pEvent->param2);
                    OutputDebugString(szDbgStr);
                    if ((UINT32) pEvent->param1 == 0x00000009)
                    {
                        SetFocus(HXNextFocus);
                    }
                }
                break;
#endif
            default:
                retVal = CRNVisualBaseRenderer::HandleEvent(pEvent);
        }
    }

    return retVal;
}

#endif // #if 0 // XXXMEH - for testing

STDMETHODIMP CRealPixRenderer::HandleCallback(UINT32 ulSchedulerTime, UINT32 ulInstance)
{
    HX_RESULT retVal = UpdateDisplay(ulSchedulerTime);
    if (SUCCEEDED(retVal))
    {
        // Schedule another callback
        retVal = m_pCallback->ScheduleRelativeCallback(kCallbackInterval);
    }

#ifdef XXXMEH_DEBUG_ASSERT
    // Debug-only assert
    HX_ASSERT(SUCCEEDED(retVal));
#endif

    return retVal;
}

STDMETHODIMP CRealPixRenderer::SetFocus(HXFocusContext eFocus)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pHyperlinkManager)
    {
        // Navigate the keyboard focus
        m_pHyperlinkManager->NavigateKeyboardFocus(eFocus);
        // If we have focus, then we need to draw
        // a rectangle around the link
        HXxRect     cLinkRect = {0, 0, 0, 0};
        IHXBuffer* pLinkStr  = NULL;
        if (m_pHyperlinkManager->GetLinkWithKeyboardFocus(cLinkRect, pLinkStr))
        {
            // Get the IHXDrawFocus interface if we 
            // don't already have it
            if (!m_pDrawFocus && m_pSite)
            {
                m_pSite->QueryInterface(IID_IHXDrawFocus,
                                        (void**) &m_pDrawFocus);
            }
            // Draw the focus rect
            if (m_pDrawFocus)
            {
                m_pDrawFocus->SetFocusRect(&cLinkRect);
            }
        }
        HX_RELEASE(pLinkStr);
    }

    return retVal;
}

STDMETHODIMP CRealPixRenderer::ClearFocus()
{
    HX_RESULT retVal = HXR_OK;

    if (m_pHyperlinkManager)
    {
        // If we have focus, then we need to clear
        // the rectangle around the link
        HXxRect     cLinkRect = {0, 0, 0, 0};
        IHXBuffer* pLinkStr  = NULL;
        if (m_pHyperlinkManager->GetLinkWithKeyboardFocus(cLinkRect, pLinkStr) &&
            m_pDrawFocus)
        {
            m_pDrawFocus->ClearFocus();
        }
        HX_RELEASE(pLinkStr);
        // Now clear the focus
        m_pHyperlinkManager->ClearKeyboardFocus();
    }

    return retVal;
}

STDMETHODIMP CRealPixRenderer::ActivateFocus()
{
    HX_RESULT retVal = HXR_OK;

    HXxRect     cLinkRect = {0, 0, 0, 0};
    IHXBuffer* pLinkStr  = NULL;
    if (m_pHyperlinkManager &&
        m_pHyperlinkManager->GetLinkWithKeyboardFocus(cLinkRect, pLinkStr))
    {
        // Does this have a "command:" prefix?
        const char* pszLink = (const char*) pLinkStr->GetBuffer();
        const char* pszCmd  = (const char*) strstr(pszLink,  "command:");
        const char* pszTarg = (pszCmd ? "_player" : (const char*) NULL);
        if (m_pHyperNavigate)
        {
            m_pHyperNavigate->GoToURL(pszLink, pszTarg);
        }
    }
    HX_RELEASE(pLinkStr);

    return retVal;
}

STDMETHODIMP_(HXFocusState) CRealPixRenderer::GetFocusState()
{
    HXFocusState eRet = HXNoFocus;

    if (m_pHyperlinkManager &&
        m_pHyperlinkManager->AnyLinkWithKeyboardFocus())
    {
        eRet = HXFocused;
    }

    return eRet;
}

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

    if (ppIUnknown)
    {
        // Set default
        *ppIUnknown = NULL;
        // Create the object
        CRealPixRenderer *pObj = new CRealPixRenderer();
        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 CRealPixRenderer::Reset()
{
    m_bPaused = FALSE;
}

void CRealPixRenderer::Deallocate()
{
    HX_RELEASE(m_pEffectsPackageManager);
    HX_RELEASE(m_pHyperlinkManager);
    HX_RELEASE(m_pWireFormatManager);
    HX_RELEASE(m_pEffectsManager);
    HX_RELEASE(m_pImageManager);
    HX_RELEASE(m_pCodecManager);
    HX_RELEASE(m_pCallback); // callback destruct handles any pending callback
    HX_RELEASE(m_pDrawFocus);
}

HX_RESULT CRealPixRenderer::CreateObjects()
{
    HX_RESULT retVal = HXR_OK;

    if (!m_pCodecManager   && !m_pImageManager      &&
        !m_pEffectsManager && !m_pWireFormatManager &&
        !m_pHyperlinkManager)
    {
        // Create the codec manager
        retVal = PXRenderCodecManager::CreateObject(&m_pCodecManager);
        if (SUCCEEDED(retVal))
        {
            // Addref the object
            m_pCodecManager->AddRef();
            // Create the image manager
            retVal = PXImageManager::CreateObject(&m_pImageManager);
            if (SUCCEEDED(retVal))
            {
                // Addref the object
                m_pImageManager->AddRef();
                // Create the effects manager
                retVal = PXEffectsManager::CreateObject(&m_pEffectsManager);
                if (SUCCEEDED(retVal))
                {
                    // Addref the object
                    m_pEffectsManager->AddRef();
                    // Create the wire format manager
                    retVal = PXWireFormatManager::CreateObject(&m_pWireFormatManager);
                    if (SUCCEEDED(retVal))
                    {
                        // Addref the object
                        m_pWireFormatManager->AddRef();
                        // Create the hyperlink manager
                        HX_DELETE(m_pHyperlinkManager);
                        m_pHyperlinkManager = new PXHyperlinkManager();
                        if (m_pHyperlinkManager)
                        {
                            // Addref the object
                            m_pHyperlinkManager->AddRef();
                            // Create the effects package manager
                            retVal = PXEffectsPackageManager::CreateObject(&m_pEffectsPackageManager);
                            if (SUCCEEDED(retVal))
                            {
                                // Addref the object
                                m_pEffectsPackageManager->AddRef();
                            }
                        }
                        else
                        {
                            retVal = HXR_OUTOFMEMORY;
                        }
                    }
                }
            }
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT CRealPixRenderer::SetupObjects(IHXValues* pHeader)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pCodecManager   && m_pImageManager      &&
        m_pEffectsManager && m_pWireFormatManager &&
        m_pHyperlinkManager)
    {
        // Init the codec manager
        retVal = m_pCodecManager->Init(m_pContext, IID_IHXRealPixRendererCodec);
        if (SUCCEEDED(retVal))
        {
            // Init the wire format manager
            retVal = m_pWireFormatManager->Init(m_pContext, m_ulStreamVersion);
            if (SUCCEEDED(retVal))
            {
                // Send the header to the wire format manager
                retVal = m_pWireFormatManager->OnHeader(pHeader);
                if (SUCCEEDED(retVal))
                {
                    // Init the image manager
                    retVal = m_pImageManager->Init(m_pCodecManager,
                                                   m_pErrorMessages,
                                                   m_pWireFormatManager->GetDisplayWidth(),
                                                   m_pWireFormatManager->GetDisplayHeight(),
                                                   32,
                                                   HX_RGB,
                                                   kDefaultRowsInverted,
                                                   m_pWireFormatManager->GetBackgroundColor(),
                                                   m_pWireFormatManager->GetBackgroundOpacity());
                    if (SUCCEEDED(retVal))
                    {
                        // Init the effects manager
                        retVal = m_pEffectsManager->Init(m_pErrorMessages,
                                                         m_pImageManager,
                                                         m_pEffectsPackageManager,
                                                         m_pHyperlinkManager);
                        if (SUCCEEDED(retVal))
                        {
                            // Check to see if we are live
                            m_bIsLive = IsStreamLive();
                            // Set the live flag in the effects manager
                            m_pEffectsManager->SetLive(m_bIsLive);
                            // Init the hyperlink manager
                            retVal = m_pHyperlinkManager->Init(m_pContext,
                                                               m_pWireFormatManager->GetDisplayWidth(),
                                                               m_pWireFormatManager->GetDisplayHeight());
                            if (SUCCEEDED(retVal))
                            {
                                // Init the effects package manager
                                retVal = m_pEffectsPackageManager->Init(m_pContext, IID_IIMFEffect);
                                if (SUCCEEDED(retVal))
                                {
                                    // Set the default URL, if there is one
                                    IHXBuffer* pDefaultURLStr = NULL;
                                    HX_RESULT rv               = m_pWireFormatManager->GetDefaultURL(pDefaultURLStr);
                                    if (SUCCEEDED(rv))
                                    {
                                        // Give it to the hyperlink manager
                                        retVal = m_pHyperlinkManager->SetDefaultLink((const char*) pDefaultURLStr->GetBuffer());
                                    }
                                    HX_RELEASE(pDefaultURLStr);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT CRealPixRenderer::SetupCodecsAndFXPackages()
{
    HX_RESULT   retVal   = HXR_OK;
    IHXBuffer* pStr     = NULL;
    BOOL        bUpgrade = FALSE;

    if (m_pWireFormatManager && m_pCodecManager)
    {
        // Check for any missing codecs
        HX_RESULT rv = m_pWireFormatManager->GetFirstCodecMime(&pStr);
        while (SUCCEEDED(rv))
        {
            BOOL bPresent = FALSE;
            retVal        = m_pCodecManager->IsComponentPresent((const char*) pStr->GetBuffer(), &bPresent);
            if (SUCCEEDED(retVal))
            {
                if (bPresent)
                {
                    // A codec is required which we DO have - so inform the
                    // codec manager that this codec is active
                    retVal = m_pCodecManager->SetActiveComponent((const char*) pStr->GetBuffer());
                }
                else
                {
                    // A codec is required which is not present, so add that
                    // mime to the upgrade list
                    retVal = AddMimeToUpgradeCollection((const char*) pStr->GetBuffer());
                    if (SUCCEEDED(retVal))
                    {
                        bUpgrade = TRUE;
                    }
                }
            }

            if (FAILED(retVal))
            {
                break;
            }

            HX_RELEASE(pStr);
            rv = m_pWireFormatManager->GetNextCodecMime(&pStr);
        }

        // Now check for any missing effects packages
        if (SUCCEEDED(retVal))
        {
            HX_RELEASE(pStr);
            rv = m_pWireFormatManager->GetFirstFXPackageMime(&pStr);
            while (SUCCEEDED(rv))
            {
                BOOL bPresent = FALSE;
                retVal        = m_pEffectsPackageManager->IsComponentPresent((const char*) pStr->GetBuffer(),
                                                                             &bPresent);
                if (SUCCEEDED(retVal))
                {
                    if (bPresent)
                    {
                        // A codec is required which we DO have - so inform the
                        // codec manager that this codec is active
                        retVal = m_pEffectsPackageManager->SetActiveComponent((const char*) pStr->GetBuffer());
                    }
                    else
                    {
                        // A codec is required which is not present, so add that
                        // mime to the upgrade list
                        retVal = AddMimeToUpgradeCollection((const char*) pStr->GetBuffer());
                        if (SUCCEEDED(retVal))
                        {
                            // Set flag that we need to upgrade
                            bUpgrade = TRUE;
                        }
                    }
                }

                if (FAILED(retVal))
                {
                    break;
                }

                HX_RELEASE(pStr);
                rv = m_pWireFormatManager->GetNextFXPackageMime(&pStr);
            }

            // Now we can tell the codec and package managers to release
            // any codecs and packages which are not going to be used
            // in this presentation.
            if (SUCCEEDED(retVal))
            {
                retVal = m_pCodecManager->ReleaseInactiveComponents();
                if (SUCCEEDED(retVal))
                {
                    retVal = m_pEffectsPackageManager->ReleaseInactiveComponents();
                }
            }
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    HX_RELEASE(pStr);

    // If we determined we needed to upgrade, then we should fail
    if (SUCCEEDED(retVal) && bUpgrade)
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CRealPixRenderer::UpdateDisplay(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    UINT32 ulTime1     = HX_GET_TICKCOUNT();
    UINT32 ulDeltaTime = ulTime1 - m_ulLastDrawTime;
    if (ulDeltaTime >= m_ulPerformanceAdjustedDelay)
    {
        // Save this time
        m_ulLastDrawTime = ulTime1;
        // Just pass the time sync off to the effects manager
        retVal = m_pEffectsManager->OnTimeSync(ulTime);
        if (SUCCEEDED(retVal))
        {
            // Now we need to see if we damaged the display,
            // and if so, call IHXSite::ForceRedraw()
            if (m_pEffectsManager->IsDisplayDamaged())
            {
                // Set the flag saying we forced this redraw
                m_pEffectsManager->SetRedrawForced(TRUE);
                // Get the size of the display
                HXxSize cSize = {0, 0};
                cSize.cx = (INT32) m_pWireFormatManager->GetDisplayWidth();
                cSize.cy = (INT32) m_pWireFormatManager->GetDisplayHeight();
                // Are we able to really blt subrects to the site?
                HXxRect cDamageRect = {0, 0, 0, 0};
                if (m_bBltSubRects)
                {
                    // Get the damage rect from the effects manager
                    m_pEffectsManager->GetDamageRect(cDamageRect);
                    // Get the size of the site
                    HXxSize cSiteSize = {0, 0};
                    m_pSite->GetSize(cSiteSize);
                    // Do we need to scale the damage rect?
                    if (cSize.cx > 0 && cSize.cy > 0 &&
                        (cSiteSize.cx != cSize.cx ||
                         cSiteSize.cy != cSize.cy))
                    {
                        cDamageRect.left   = cDamageRect.left   * cSiteSize.cx / cSize.cx;
                        cDamageRect.top    = cDamageRect.top    * cSiteSize.cy / cSize.cy;
                        cDamageRect.right  = cDamageRect.right  * cSiteSize.cx / cSize.cx;
                        cDamageRect.bottom = cDamageRect.bottom * cSiteSize.cy / cSize.cy;
                    }
                }
                else
                {
                    // No, the site doesn't support bltting subrects
                    // so just damage the whole display
                    cDamageRect.right  = cSize.cx;
                    cDamageRect.bottom = cSize.cy;
                }
                // Damage the site
                MLOG_BLT(m_pErrorMessages,
                         "%lu CRealPixRenderer::UpdateDisplay(%lu) "
                         "damaging (%ld,%ld,%ld,%ld) (%ld x %ld)\n",
                         HX_GET_BETTERTICKCOUNT(), ulTime,
                         cDamageRect.left,  cDamageRect.top,
                         cDamageRect.right, cDamageRect.bottom,
                         HXxRECT_WIDTH(cDamageRect),
                         HXxRECT_HEIGHT(cDamageRect));
                m_pSite->DamageRect(cDamageRect);
                // Force a redraw
                m_pSite->ForceRedraw();
            }
        }
        // Find out how long this draw took
        UINT32 ulTime2 = HX_GET_TICKCOUNT();
        ulDeltaTime    = ulTime2 - ulTime1;
        // Recalculate the delay, never letting it be more than 1000 ms
        m_ulPerformanceAdjustedDelay = ulDeltaTime * m_ulPerformanceScale;
        m_ulPerformanceAdjustedDelay = PXMIN(1000, m_ulPerformanceAdjustedDelay);
    }

    return retVal;
}

BOOL CRealPixRenderer::IsStreamLive()
{
    BOOL bRet = FALSE;

    if (m_pStream)
    {
        IHXStreamSource* pStreamSource = NULL;
        HX_RESULT retVal = m_pStream->GetSource(pStreamSource);
        if (SUCCEEDED(retVal))
        {
            bRet = pStreamSource->IsLive();
        }
        HX_RELEASE(pStreamSource);
    }

    return bRet;
}

HX_RESULT CRealPixRenderer::RMASurfaceUpdate2(IHXSubRectVideoSurface* pSurface,
                                              HXxRect*                 pExtents,
                                              HXxBoxRegion*              pDirtyRegion)
{
    MLOG_BLT(m_pErrorMessages,
             "%lu CRealPixRenderer::RMASurfaceUpdate2() this=0x%08x\n",
             HX_GET_BETTERTICKCOUNT(), this);
    HX_RESULT retVal = HXR_FAIL;

    if (pSurface && pDirtyRegion && m_pSite && m_pImageManager)
    {
        // Get the size of the site
        HXxSize cSiteSize = {0, 0};
        m_pSite->GetSize(cSiteSize);
        if (cSiteSize.cx != 0 && cSiteSize.cy != 0)
        {
            // Get the display image
            PXImage* pDisplay = NULL;
            retVal = m_pImageManager->GetDisplayImage(&pDisplay);
            if (SUCCEEDED(retVal))
            {
                // Get the size of the display image
                HXxSize cImgSize = {0, 0};
                cImgSize.cx = (INT32) pDisplay->GetWidth();
                cImgSize.cy = (INT32) pDisplay->GetHeight();
                // Scale dirty rects.
                float fx = (float) cImgSize.cx / (float) cSiteSize.cx;
                float fy = (float) cImgSize.cy / (float) cSiteSize.cy;
                // Go through each rect in the dirty region and scale it to 
                // generate the src rects.
                HXBOX* pSrcRects = new HXBOX[pDirtyRegion->numRects];
                if (pSrcRects)
                {
                    INT32 i = 0;
                    for(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         = cImgSize.cx;
                    cHeader.biHeight        = cImgSize.cy;
                    cHeader.biPlanes        = 1;
                    cHeader.biBitCount      = 32;
                    cHeader.biCompression   = 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;
                    retVal = pDisplay->GetImageStore(&pBuffer);
                    if (pBuffer)
                    {
                        MLOG_BLT(m_pErrorMessages,
                                 "    numRects=%ld\n",
                                 srcRegion.numRects);
                        for (i = 0; i < srcRegion.numRects; i++)
                        {
                            MLOG_BLT(m_pErrorMessages,
                                     "        rect[%ld] = (%d,%d,%d,%d)\n",
                                     i,
                                     srcRegion.rects[i].x1,
                                     srcRegion.rects[i].y1,
                                     srcRegion.rects[i].x2,
                                     srcRegion.rects[i].y2);
                        }
                        // 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);
            }
            HX_RELEASE(pDisplay);
        }
    }

    return retVal;
}

void CRealPixRenderer::_AttachSite()
{
    if (m_pSite)
    {
        // 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);
            // Set the flag
            m_bBltSubRects = TRUE;
        }
    }
}

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

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

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

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

STDMETHODIMP_(UINT32) CRealPixRenderer::GetHighestSupportedContentVersion()
{
    return HX_ENCODE_PROD_VERSION(1, 4, 0, 0);
}

STDMETHODIMP_(UINT32) CRealPixRenderer::GetHighestSupportedStreamVersion()
{
    return HX_ENCODE_PROD_VERSION(1, 4, 0, 0);
}

