/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: basesurf.cpp,v 1.11.4.3 2004/07/26 10:27:38 pankajgupta 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 "hxcom.h"
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxvsurf.h"
#include "hxslist.h"
#include "hxthread.h"
#include "hxver.h"

#if defined(_WINDOWS)
#include "winsurf.h"

#if defined (HELIX_FEATURE_VS2)
#include "winsurf2.h"
#endif

#include "winroot.h"    // must be included before colormap.h
#endif

#include "region.h"
#include "basesurf.h"
#include "basesite.h"
#include "baseroot.h"
#include "hxprefs.h"
#include "hxtick.h"
#include "hxheap.h"
#include "colormap.h"

#include "microsleep.h"
#include "hxevent.h"
#include "infmt.h"

//MMX alphablending only available on linux and windows right now.
//Should work just fine on any IA plaform though (intel solaris for example)
#ifdef _USE_MMX
#include "mmx_util.h" //for checkMmxAvailablity()
extern "C"
{
    void AlphaBlendMMX( UINT32*, UINT32*, INT32 );
}
#endif

#include <math.h>  // fabs()

#include "drawline.h"

#if defined(_UNIX) && !defined(_MAC_UNIX)
#  include "unixsurf.h"
#endif

#if defined(_MACINTOSH) || defined(_MAC_UNIX)
//#include "../dcondev/dcon.h"
#include "platform/mac/macsurf.h"
#endif

#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif

#ifdef _DEBUG
#include "region.h" //just for _DumpString
#endif

#include "sitefact.h"
#include "hwmemobj.h"

#define NOT_IMPL    0

/*
 *  Defines used for testing various parts of the code.
 */
//#define _CHECK_PERFORMANCE
//#define _CHECK_SCROLLBARS
//#define _CHECK_MODES


#define NOT_IMPL 0
#define OVERLAY_BYPASS_THRESHOLD 8
#define OVERLAY_FAILURE_THRESHOLD 3 // What the heck should this number be? Does it really matter?

CBaseSurface::CBaseSurface(IUnknown* pContext, CHXBaseSite* pSite)
    : m_lRefCount(0)
    , m_pContext(pContext)
    , m_Brightness(0)
    , m_Contrast(0)
    , m_Saturation(0)
    , m_Hue(0)
    , m_Sharpness(0)
    , m_ModeSharpness(0)
    , m_PrevBrightness(0)
    , m_PrevContrast(0)
    , m_PrevSaturation(0)
    , m_PrevHue(0)
    , m_PrevSharpness(0)
    , m_pSite(pSite)
    , m_pRootSurface(NULL)
    , m_pOptimizedFormat(NULL)
    , m_nSrcCID(0)
    , m_nBltMode(HX_BASIC_BLT)
    , m_nSurfaceCID(0)
    , m_bBackGroundDirty(TRUE)
    , m_fScrollBarZoom(1.0)
    , zm_pColorAcc(NULL)
    , m_pyuvInputMngr(NULL)
    , m_pHwMemObj(NULL)
    , m_pucLastImage(NULL)
    , m_ulLastBlendTime(0)
    , m_bSpamUpdateOverlay(TRUE)
    , m_bFlipOverlay(FALSE)
    , m_nBackBufferCount(0)
    , m_nMaxBackBuffers(3)
    , m_bDisableFillColorKey(TRUE)
    , m_bMultipleOverlay(FALSE)
    , m_nOldBltMode(0)
    , m_nOldBltMode2(0)
    , m_oldOverlayColorDepth(0)
    , m_convertedOverlayColor(0x00010203)
    , m_bUseOverlays(TRUE)
    , m_nOverlayFailureCount(0)
    , m_nDDSurfaceSize(0)
    , m_nUpdateOverlayByPassCount(0)
    , m_scaleFactorX(1.0)
    , m_scaleFactorY(1.0)
    , m_paSrcRects(NULL)
    , m_paDestRects(NULL)
    , m_pAdditionalColorKey(NULL)
    , m_bNeedColorKeyFilled(FALSE)
    , m_bYUVBlending(FALSE)
    , m_bVideoSurface2(FALSE)
    , m_bOptimalVideoScheduler(FALSE)
    , m_pLinkedOverlay(NULL)
    , m_bAllowOverlayLinking(TRUE)
    , m_bLostHWAcceleration(FALSE)
    , m_bOffBecauseofShinking(FALSE)
    , m_bImageBlocksGood(FALSE)
    , m_pOverlayManager(NULL)
{
    HX_ASSERT( m_pContext );
    m_pContext->AddRef();
    memset( &m_bmiLastImage, 0, sizeof( HXBitmapInfoHeader ) );
    memset( &m_bmiLastBlt, 0, sizeof( HXBitmapInfoHeader ) );
    memset( &m_surfaceSize, 0, sizeof(HXxSize) );
    memset( &m_lastUpdateDestRect, 0, sizeof(m_lastUpdateDestRect) );
    memset( &m_lastUpdateSrcRect, 0, sizeof(m_lastUpdateSrcRect) );
    memset( &m_lastSrcRect, 0, sizeof(m_lastSrcRect) );
    memset( &m_bmi, 0, sizeof(m_bmi) );

#if !defined(_GOLD) && 0
    m_TimeStamps.Init(m_pContext, this);
#endif

    IHXPreferences*    pPreferences    = NULL;
    IHXBuffer*         pBuffer         = NULL;

    if (HXR_OK == m_pContext->QueryInterface(IID_IHXPreferences,(void**)&pPreferences))
    {
        if (pPreferences->ReadPref("SiteZoomFactor", pBuffer) == HXR_OK)
        {
            m_fScrollBarZoom = !(::atoi((char*) pBuffer->GetBuffer()) == 1);
        }
        HX_RELEASE(pBuffer);
        if (pPreferences->ReadPref("UseOverlay", pBuffer) == HXR_OK)
        {
            m_bUseOverlays = ::atoi((char*) pBuffer->GetBuffer()) == 1;
        }
        HX_RELEASE(pBuffer);
        if (pPreferences->ReadPref("SpamUpdateOverlay", pBuffer) == HXR_OK)
        {
            m_bSpamUpdateOverlay = ::atoi((char*) pBuffer->GetBuffer()) == 1;
        }
        HX_RELEASE(pBuffer);
        if (pPreferences->ReadPref("FlipOverlay", pBuffer) == HXR_OK)
        {
            m_bFlipOverlay = ::atoi((char*) pBuffer->GetBuffer()) == 1;
        }
        HX_RELEASE(pBuffer);
        if (pPreferences->ReadPref("YUY2First", pBuffer) == HXR_OK)
        {
            if (::atoi((char*) pBuffer->GetBuffer()) == 1)
            {
                int list[] = {0,3,1,4};
                m_pyuvInputMngr->SetOutputPriority(CID_I420, list, 4);
            }
        }
        HX_RELEASE(pBuffer);
        if (pPreferences->ReadPref("LinkOverlays", pBuffer) == HXR_OK)
        {
            m_bAllowOverlayLinking = ::atoi((char*) pBuffer->GetBuffer()) == 1;
        }
        HX_RELEASE(pBuffer);

        m_pContext->QueryInterface(IID_IHXOverlayManager, (void**)&m_pOverlayManager);
    }
    HX_RELEASE(pPreferences);

#ifdef _DEBUG
    const char* szKeyName = "Software\\"HXVER_COMMUNITY"\\Debug\\SetHook";
    m_bAllocHook = HXDebugOptionEnabled(szKeyName);
#endif
}

CBaseSurface::~CBaseSurface()
{
    HX_DELETE(zm_pColorAcc);
    HX_DELETE(m_pyuvInputMngr);
    HX_DELETE(m_pHwMemObj);

    HX_RELEASE(m_pRootSurface);

    // CBaseSite::Destroy calls EndOptimizedBlt.  We can't call
    // EndOptimizedBlt from the destructor since it calls DestroySurfaces
    // which calls _ReleaseSurface which is a pure virtual...the derived
    // class was already destoryed.
    HX_ASSERT(!m_pOptimizedFormat);

    HX_RELEASE(m_pContext);

    HX_FREE(m_pucLastImage);

    memset( &m_bmiLastImage, 0, sizeof( m_bmiLastImage ) );
    memset( &m_bmiLastBlt, 0, sizeof( m_bmiLastBlt ) );

    HX_FREE( m_paSrcRects );
    HX_FREE( m_paDestRects );

    //Clean up alphablending YUVA image list
    CHXMapPtrToPtr::Iterator i = m_YUVAImageList.Begin();
    while(i != m_YUVAImageList.End())
    {
        Image* pImage = (Image*)*i;
        HX_VECTOR_DELETE(pImage->pucImage);
        HX_DELETE(pImage);
        ++i;
    }
    m_YUVAImageList.RemoveAll();
    CHXSimpleList::Iterator j = m_imageBlocks.Begin();
    m_bImageBlocksGood = FALSE;

    while(j != m_imageBlocks.End())
    {
        ImageBlock* pBlock = (ImageBlock*)*j;
        Image* pImage = pBlock->pImage;
        HX_FREE(pImage->pucImage);
        HX_DELETE(pImage);
        HX_DELETE(pBlock);
        ++j;
    }
    m_imageBlocks.RemoveAll();

    if (m_pOverlayManager)
    {
        m_pOverlayManager->RemoveOverlayRequest((IHXOverlayResponse*)this);
    }
    HX_RELEASE(m_pOverlayManager);

    HXDestroyRegion(m_pAdditionalColorKey);
    m_pAdditionalColorKey = NULL;
    m_LinkedSites.RemoveAll();
}
void CBaseSurface::SetRootSurface( CBaseRootSurface* pSurface)
{
    m_pRootSurface = pSurface;
    pSurface->AddRef();
}

void CBaseSurface::DestroySurfaces()
{
    if (m_surfaceSize.cx || m_surfaceSize.cy)
    {
        CBaseRootSurface* pSurface = m_pSite->GetRootSurface();

        // XXXAH huh?  Should this not be releasing it's own internal
        //             surface?  Again, minimal Changes.
        //XXXgfw not to mention that you can't call this from the dtor
        //of this class like is happening. Lets make sure that whatever
        //is needed happens when the root surface is deleted.
        if (pSurface)
        {
            _ReleaseSurface(pSurface);
        }
        ::memset(&m_surfaceSize, 0, sizeof(m_surfaceSize));
    }

    HX_DELETE(m_pHwMemObj);
}

void CBaseSurface::ReInitSurfaces()
{
    if (m_pOptimizedFormat)
    {
        HXBitmapInfoHeader optFormat;
        memcpy(&optFormat, m_pOptimizedFormat, sizeof(HXBitmapInfoHeader)); /* Flawfinder: ignore */
        memset(&m_surfaceSize, 0, sizeof(HXxSize));
        BeginOptimizedBlt(&optFormat);
    }
}

BOOL CBaseSurface::ForceGDIMode(BOOL bForceGDI)
{
    if(bForceGDI)
    {
        if (m_nBltMode == HX_OVERLAY_BLT)
        {
            m_nBltMode = HX_BASIC_BLT;
#if defined(_UNIX) && !defined(_MAC_UNIX)
            _ReleaseSurface();
#endif
            m_pSite->InternalForceRedraw();
            m_pSite->m_pTopLevelSite->ScheduleCallback(CLIP, 0 );
            m_nOldBltMode = HX_OVERLAY_BLT;
        }
    }
    else
    {
        if (m_nOldBltMode == HX_OVERLAY_BLT)
        {
            m_nBltMode = HX_OVERLAY_BLT;
#if defined(_UNIX) && !defined(_MAC_UNIX)
            TryCreateOverlay(TRUE);
#endif
            m_pSite->InternalForceRedraw();

            m_pSite->m_pTopLevelSite->ScheduleCallback(CLIP, 0 );
            FillColorKey();
            m_nOldBltMode = HX_BASIC_BLT;

            m_bOffBecauseofShinking = FALSE;
        }
    }

    return TRUE;
}

/************************************************************************
 *  Method:
 *    IUnknown::QueryInterface
 */
STDMETHODIMP CBaseSurface::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IHXVideoSurface))
    {
        AddRef();
        *ppvObj = (IUnknown*)(IHXVideoSurface*)this;
        return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IUnknown))
    {
        AddRef();
        *ppvObj = (IUnknown*)(IHXSite*)this;
        return HXR_OK;
    }

    else if (IsEqualIID(riid, IID_IHXVideoControl))
    {
        AddRef();
        *ppvObj = (IUnknown*)(IHXVideoControl*)this;
        return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXOverlayResponse))
    {
        AddRef();
        *ppvObj = (IUnknown*)(IHXOverlayResponse*)this;
        return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

/************************************************************************
 *  Method:
 *    IUnknown::AddRef
 */
STDMETHODIMP_(ULONG32) CBaseSurface::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/************************************************************************
 *  Method:
 *    IUnknown::Release
 */
STDMETHODIMP_(ULONG32) CBaseSurface::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

BOOL  CBaseSurface::IsPixelTransparent(HXxPoint& point, INT32 nAlphaLevel )
{
    BOOL      retVal = FALSE;
    INT32     nCID   = -1;
    INT32     nPitch = 0;
    HXxPoint  origin = *(m_pSite->GetOrigin());

    //It can only be transparent if we are blt'ing ARGB32.
    nCID   = GETBITMAPCOLOR(&m_bmiLastBlt);
    nPitch = GETBITMAPPITCH(&m_bmiLastImage);

    if( nCID == CID_ARGB32 )
    {
        if( m_pucLastImage )
        {
            UINT32* pPixel=NULL;

            if( nPitch<0 )
            {
                pPixel = (UINT32*)(m_pucLastImage+m_bmiLastImage.biSizeImage+nPitch);
                pPixel -= (point.y-origin.y)*m_bmiLastImage.biWidth - (point.x-origin.x);
            }
            else
            {
                pPixel = (UINT32*)m_pucLastImage;
                pPixel += (point.y-origin.y)*m_bmiLastImage.biWidth + (point.x-origin.x);
            }

            if( pPixel>=(UINT32*)m_pucLastImage &&
                pPixel<(UINT32*)(m_pucLastImage+m_bmiLastImage.biSizeImage))
            {
                int alpha = (*pPixel&0xff000000)>>24;
                if( alpha > nAlphaLevel )
                {
                    retVal = TRUE;
                }
            }
        }
    }
    return retVal;
}

void CBaseSurface::_AlphaBlend( HXREGION*              pRegionToBlend,
                                UCHAR*               pBottomImage,
                                HXBitmapInfoHeader* pbmiBottomImageInfo,
                                HXxPoint*            pBottomPosition,
                                UCHAR*               pTopImage,
                                HXBitmapInfoHeader* pbmiTopImageInfo,
                                HXxPoint*            pTopPosition
                                )
{
#ifdef _USE_MMX
    static const BOOL bMMXAvailable = (checkMmxAvailablity()&CPU_HAS_MMX)?1:0;
#else
    static const BOOL bMMXAvailable = FALSE;
#endif

#if defined(_DEBUG) && 0
    _DumpRegion(pRegionToBlend);
#endif
    if( pBottomImage == NULL || pTopImage == NULL )
    {
        return;
    }

    //Make sure we only try to alpha blend ARGB32 content.
    int nCIDBottom   = GETBITMAPCOLOR( pbmiBottomImageInfo );
    int nCIDTop      = GETBITMAPCOLOR( pbmiTopImageInfo );
    int nBottomPitch = GETBITMAPPITCH( pbmiBottomImageInfo );
    int nTopPitch    = GETBITMAPPITCH( pbmiTopImageInfo );

    if( nCIDTop != CID_ARGB32 || (nCIDBottom!=CID_ARGB32 && nCIDBottom!=CID_RGB32) )
    {
        HX_ASSERT( "Trying to alphablend unsupported type." );
        return;
    }

    BOOL  bFade=FALSE;
    int   completeness = 0;
    if( (m_pSite->m_fpTransitionEffect == Crossfade ||
         m_pSite->m_fpTransitionEffect == FadeToColor ||
         m_pSite->m_fpTransitionEffect == FadeFromColor )
        && m_pSite->m_nTransitionState!= 1000 )
    {
        completeness = m_pSite->m_nTransitionState;

        if((m_pSite->m_fpTransitionEffect != Crossfade &&
            m_pSite->m_bTransitionReversed) ||
           (m_pSite->m_fpTransitionEffect == Crossfade &&
            (!m_pSite->m_bTransitionTranIn ^
             m_pSite->m_bTransitionReversed)))
        {
            completeness = 1000 - completeness;
        }


        if( m_pSite->m_fpTransitionEffect== FadeFromColor)
        {
            completeness = 1000 - completeness;
        }


        //Map [0,1000] --> [0,1024] for fixed point fade math.
        completeness = (int)((float)completeness*1024.0/1000.0);
        bFade=TRUE;
    }


    if( !HXEmptyRegion(pRegionToBlend) )
    {
        int nBottomOffset = 0;
        int nTopOffset    = 0;
        int nBotPosX      = 0;
        int nBotPosY      = 0;
        int nTopPosX      = 0;
        int nTopPosY      = 0;


        for(int i =0; i< pRegionToBlend->numRects; i++)
        {
            //We need to alpha blend each rect in the region.
            int topX=0, topY = 0;
            int botX=0, botY = 0;
            topX = pRegionToBlend->rects[i].x1;
            topY = pRegionToBlend->rects[i].y1;
            botX = pRegionToBlend->rects[i].x2;
            botY = pRegionToBlend->rects[i].y2;

            if( nBottomPitch<0 )
                nBottomOffset = -(pbmiBottomImageInfo->biWidth+(botX-topX));
            else
                nBottomOffset = pbmiBottomImageInfo->biWidth-(botX-topX);

            if( nTopPitch<0 )
                nTopOffset = -(pbmiTopImageInfo->biWidth+(botX-topX));
            else
                nTopOffset = pbmiTopImageInfo->biWidth-(botX-topX);

            nTopPosX = pTopPosition->x;
            nTopPosY = pTopPosition->y;
            nBotPosX = pBottomPosition->x;
            nBotPosY = pBottomPosition->y;

            //blend this rect.
            UINT32* pulBotPixel=NULL;
            UINT32* pulTopPixel=NULL;

            if( nBottomPitch<0 )
            {
                pulBotPixel = (UINT32*)(pBottomImage+pbmiBottomImageInfo->biSizeImage+nBottomPitch);
                pulBotPixel -= ((topY-nBotPosY)*pbmiBottomImageInfo->biWidth - (topX-nBotPosX));
            }
            else
            {
                pulBotPixel = (UINT32*)pBottomImage;
                pulBotPixel += ((topY-nBotPosY)*pbmiBottomImageInfo->biWidth + (topX-nBotPosX));
            }


            if( nTopPitch<0 )
            {
                pulTopPixel = (UINT32*)(pTopImage+pbmiTopImageInfo->biSizeImage+nTopPitch);
                pulTopPixel -= ((topY-nTopPosY)*pbmiTopImageInfo->biWidth    - (topX-nTopPosX));
            }
            else
            {
                pulTopPixel = (UINT32*)pTopImage;
                pulTopPixel += ((topY-nTopPosY)*pbmiTopImageInfo->biWidth    + (topX-nTopPosX));

            }

            int alpha     = 0;
            int alphasave = 0;

            for( int y=topY ; y<=botY-1 ; y++ )
            {
                //XXXgfw this blows! We need a version of our MMX version
                //for doing fades as well.
                if( !bMMXAvailable || bFade )
                {
                    for( int x=topX ; x<=botX-1 ; x++ )
                    {
                        //Blend it.
                        //We *MUST* preserve the alpha channel in the top image.
                        //This is needed for hit testing on transparent pixels.
                        if( !bFade )
                        {
                            alpha = 255-((*pulTopPixel & 0xff000000)>>24);
                            alphasave = *pulTopPixel & 0xff000000;
                            alpha = alpha>128 ? alpha + 1 : alpha;
                        }
                        else
                        {
                            if( m_pSite->m_fpTransitionEffect == Crossfade )
                            {
                                //fixed point version of (comp/1000 * alpha )
                                alpha = (*pulTopPixel & 0xff000000)>>24;
                                if( GETBITMAPCOLOR(&m_bmiLastBlt) != CID_ARGB32 )
                                    alpha = 0x00;
                                alpha = ((completeness*(255-alpha)+512))>>10;
                                alphasave = (255-alpha)<<24;
                            }
                            else if( m_pSite->m_fpTransitionEffect == FadeToColor ||
                                     m_pSite->m_fpTransitionEffect == FadeFromColor)
                            {
                                //Not only affect alpha but also fading to color.
                                alpha = (completeness*255+512)>>10;
                                UINT32 fadeColor = (m_pSite->m_ulTransitionFadeColor&0x00ffffff)|(alpha<<24);
                                int a1 = 255-((*pulTopPixel&0xff000000)>>24);
                                *pulTopPixel =
                                    (((alpha*( (fadeColor&0x00ff0000)-(*pulTopPixel&0x00ff0000))+((*pulTopPixel&0x00ff0000)<<8))>>8)&0x00ff0000)|
                                    (((alpha*( (fadeColor&0x0000ff00)-(*pulTopPixel&0x0000ff00))+((*pulTopPixel&0x0000ff00)<<8))>>8)&0x0000ff00)|
                                    (((alpha*( (fadeColor&0x000000ff)-(*pulTopPixel&0x000000ff))+((*pulTopPixel&0x000000ff)<<8))>>8)&0x000000ff);


                                if( alpha<a1 )
                                    alpha = a1;
                                //Now make sure the rest of the blending uses the
                                //new alpha channel from the FadeToColor state.
                                alphasave = *pulTopPixel & ((255-alpha)<<24);

                                //OK, we never alpha blend if a renderer outputs in plain old
                                //RGB, only if they output in ARGB. Now, some renderer blt'ing in
                                //RGB32 output random numbers, all zeros or all 0xFF in the alpha
                                //channel. That can screw us up, so we will just set it here to not
                                //have an alpha channel.
                                if( GETBITMAPCOLOR(&m_bmiLastBlt) != CID_ARGB32 )
                                    alpha = 0xFF;
                            }
                        }
                        *pulTopPixel =
                            (((alpha*( (*pulTopPixel&0x00ff0000)-(*pulBotPixel&0x00ff0000))+((*pulBotPixel&0x00ff0000)<<8))>>8)&0x00ff0000)|
                            (((alpha*( (*pulTopPixel&0x0000ff00)-(*pulBotPixel&0x0000ff00))+((*pulBotPixel&0x0000ff00)<<8))>>8)&0x0000ff00)|
                            (((alpha*( (*pulTopPixel&0x000000ff)-(*pulBotPixel&0x000000ff))+((*pulBotPixel&0x000000ff)<<8))>>8)&0x000000ff);
                        *pulTopPixel = *pulTopPixel|alphasave;

                        //Next pixel
                        pulBotPixel++;
                        pulTopPixel++;
                    }

                    //Next row.
                    pulBotPixel += nBottomOffset;
                    pulTopPixel += nTopOffset;
                }
                else
                {
#ifdef _USE_MMX
                    //Use MMX version of blender
                    AlphaBlendMMX( pulTopPixel,
                                   pulBotPixel,
                                   (botX-topX)
                                   );
                    //Next row.
                    pulBotPixel += nBottomOffset+(botX-topX);
                    pulTopPixel += nTopOffset+(botX-topX);
#endif
                }
            }
#ifdef _USE_MMX
            //Do our emms instruction here so we don't have do it each
            //line. It is a expensive operation.
#ifdef _WIN32
            __asm emms
#elif defined(_LINUX)
                __asm__ __volatile__ ( "emms" );
#endif
#endif

        }
    }
}

#if 0
void CBaseSurface::DrawCheckerBoard(UCHAR *pY, UCHAR *pU, UCHAR *pV,
                                    INT32 nYPitch, INT32 nUPitch, INT32 nVPitch,
                                    INT32 nImageWidth, INT32 nImageHeight)
{
    const int COLS = 5;
    const int ROWS = 5;
    int Y[2] = {255, 0};
    int Cb = 128;
    int Cr = 128;

    int nColWidth = nImageWidth/COLS;
    int nRowWidth = nImageHeight/ROWS;

    IHXPreferences* pPreferences = NULL;
    IHXBuffer* pBuffer = NULL;

    if (HXR_OK == m_pContext->QueryInterface(IID_IHXPreferences,(void**)&pPreferences))
    {
        if (pPreferences->ReadPref("VideoBoost\\Y0", pBuffer) == HXR_OK)
        {
            Y[0] = ::atoi((char*) pBuffer->GetBuffer());
            HX_RELEASE(pBuffer);
        }

        if (pPreferences->ReadPref("VideoBoost\\Y1", pBuffer) == HXR_OK)
        {
            Y[1] = ::atoi((char*) pBuffer->GetBuffer());
            HX_RELEASE(pBuffer);
        }

        if (pPreferences->ReadPref("VideoBoost\\Cb", pBuffer) == HXR_OK)
        {
            Cb = ::atoi((char*) pBuffer->GetBuffer());
            HX_RELEASE(pBuffer);
        }

        if (pPreferences->ReadPref("VideoBoost\\Cr", pBuffer) == HXR_OK)
        {
            Cr = ::atoi((char*) pBuffer->GetBuffer());
            HX_RELEASE(pBuffer);
        }

        HX_RELEASE(pPreferences);
    }


    UCHAR* pTemp = pY;

    for (int i=0; i<nImageHeight; i++)
    {
        // Draw a line of y for each column
        for (int j=0; j<COLS; j++)
        {
            memset(pTemp, Y[j%2], nColWidth);
            pTemp += nColWidth;
        }

        // Move y pointer to next row
        pTemp = pY + nYPitch*(i+1);

        // Swap y colors at each new row
        if (i%nRowWidth == 0)
        {
            int nTemp = Y[0];
            Y[0] = Y[1];
            Y[1] = nTemp;
        }
    }

    // Draw the Cr and Cb planes
    memset(pU, Cb, nImageWidth*nImageHeight/4);
    memset(pV, Cr, nImageWidth*nImageHeight/4);
}
#endif //0

HXREGION* CBaseSurface::_DetermineBestRegion()
{
    HXRECTANGLE rect;
    ImageBlock* pBlock = NULL;

    //Create a region from out image block list.
    HXREGION* pRetReg = HXCreateRegion();
    CHXSimpleList::Iterator j = m_imageBlocks.Begin();
    while(j != m_imageBlocks.End())
    {
        pBlock = (ImageBlock*)*j;
        rect.x      = (short)pBlock->rect.left;
        rect.y      = (short)pBlock->rect.top;
        rect.width  = (short)(pBlock->rect.right-pBlock->rect.left);
        rect.height = (short)(pBlock->rect.bottom-pBlock->rect.top);
        HXUnionRectWithRegion( &rect, pRetReg, pRetReg );
        ++j;
    }

    return pRetReg;
}




void CBaseSurface::_RemoveYUVImageLists()
{
    m_pSite->_TLSLock();

    CHXSimpleList::Iterator jj = m_imageBlocks.Begin();
    while(jj != m_imageBlocks.End())
    {
        ImageBlock* pXBlock = (ImageBlock*)*jj;
        Image* pXTmp = pXBlock->pImage;
        HX_FREE(pXTmp->pucImage);
        HX_DELETE(pXTmp);
        HX_DELETE(pXBlock);
        ++jj;
    }
    m_imageBlocks.RemoveAll();
    m_bImageBlocksGood = FALSE;

    CHXMapPtrToPtr::Iterator i = m_YUVAImageList.Begin();
    while(i != m_YUVAImageList.End())
    {
        Image* pImage = (Image*)*i;
        HX_DELETE(pImage->pucImage);
        HX_DELETE(pImage);
        ++i;
    }
    m_YUVAImageList.RemoveAll();

    m_pSite->_TLSUnlock();
}


CHXBaseSite* CBaseSurface::_SearchForYUV(CHXBaseSite* pTopSite)
{
    HX_ASSERT( pTopSite );
    CHXBaseSite* pRetSite = NULL;
    CHXMapPtrToPtr::Iterator i;
    CBaseSurface* pSurf = pTopSite->m_pVideoSurface;

    if( pSurf )
    {
        int nCID2 = GETBITMAPCOLOR(&(pSurf->m_bmiLastBlt));
        if( IsYUV(nCID2) && pSurf->_IsDisplaySurfaceYuv())
        {
            pRetSite = pTopSite;
        }
    }

    if( !pRetSite )
    {
        //If the one passed in isn't any good, we need to go through its
        //lists of alphablend sites and so on.
        for( i=pTopSite->m_AlphaBlendSites.Begin() ;
             i!=pTopSite->m_AlphaBlendSites.End() && !pRetSite ;
             ++i)
        {
            CHXBaseSite* pSite = (CHXBaseSite*) i.get_key();
            pRetSite = _SearchForYUV( pSite );
        }
    }

    return pRetSite;
}


BOOL CBaseSurface::_BlendYUVRects( AlphaStruct* pList, int nCount, UINT32 ulDestFourCC)
{
    BOOL bBlend = TRUE;

    //These rects in pList *must* match the ones in m_imageBlocks.
    //If they don't, time for recompute clip again.
    CHXSimpleList::Iterator g = m_imageBlocks.Begin();
    int i=0;
    while(g != m_imageBlocks.End() )
    {
        ImageBlock*   pBlockOut = (ImageBlock*)*g;
        Image*        pImageOut = pBlockOut->pImage;
        CHXBaseSite*  pSite     = pBlockOut->pSrcSite;
        Image*        pImage    = NULL;

        //Find ourselves in that site's YUV list.
        CHXBaseSite* pTmp = NULL;
        CHXMapPtrToPtr::Iterator j = pSite->m_pVideoSurface->m_YUVAImageList.Begin();
        while( j!=pSite->m_pVideoSurface->m_YUVAImageList.End() )
        {
            pTmp = (CHXBaseSite*)j.get_key();
            if( pTmp == m_pSite )
            {
                pImage = (Image*)*j;
                break;
            }
            ++j;
        }
        //HX_ASSERT( pImage );
        if( pImage)
        {
            //Blend pList[i] under AYUV and put in pTmp
            int nSizeX = pBlockOut->rect.right-pBlockOut->rect.left;
            int nSizeY = pBlockOut->rect.bottom-pBlockOut->rect.top;

            //int nPImageDataPitch = GETBITMAPPITCH(pList[i].lPitch);
            int nPImageOutPitch  = GETBITMAPPITCH(&pImageOut->bmiImage);
            int nPImagePitch     = GETBITMAPPITCH(&pImage->bmiImage);

            // These rects should match...might crash if they don't
            if( nSizeX > pImage->bmiImage.biWidth ||
                nSizeX > pImageOut->bmiImage.biWidth ||
                nSizeX > HXxRECT_WIDTH(pList[i].rcImageRect) )
            {
                bBlend = FALSE;
            }
            if( nSizeY > pImage->bmiImage.biHeight ||
                nSizeY > pImageOut->bmiImage.biHeight ||
                nSizeY > HXxRECT_HEIGHT(pList[i].rcImageRect) )
            {
                bBlend = FALSE;
            }

            if( pBlockOut->startX<0 || pBlockOut->startY<0 )
            {
                bBlend = FALSE;
            }

            if (bBlend)
            {
                HX_RESULT hr = zm_pColorAcc->I420andYUVA(
                    pList[i].pBuffer,
                    pList[i].ulImageWidth, pList[i].ulImageHeight,
                    pList[i].lPitch,
                    pList[i].rcImageRect.left,
                    pList[i].rcImageRect.top,
                    //pBlockOut->rect.left, pBlockOut->rect.top,
                    pImage->pucImage,
                    pImage->bmiImage.biWidth, pImage->bmiImage.biHeight,
                    nPImagePitch,
                    pBlockOut->startX, pBlockOut->startY,
                    pImageOut->pucImage,
                    pImageOut->bmiImage.biWidth, pImageOut->bmiImage.biHeight,
                    nPImageOutPitch,
                    0, 0,
                    nSizeX, nSizeY,
                    MapFourCCtoCID(ulDestFourCC)
                    );
                bBlend = hr==HXR_OK;


                //Now keep drawing up the line through all
                //overlapping ARGB images.
                pSite->m_pVideoSurface->_RecursiveYUVBlend( pImageOut,
                                                            pBlockOut->rect,
                                                            this, 0, 0);
            }

            i++;
        }
        ++g;
    }
    return bBlend;
}

BOOL CBaseSurface::_DoYUVRectsIntersect()
{
    BOOL bBlend = FALSE;
    CHXMapPtrToPtr::Iterator i;

    while( i != m_pSite->m_AlphaBlendNotifiers.End() && !bBlend )
    {
        CHXBaseSite* pSite = (CHXBaseSite*)*i;
        if( pSite->m_AlphaBlendNotifiers.GetCount() )
        {
            bBlend = TRUE;
        }
    }
    return bBlend;
}

BOOL CBaseSurface::_RecursiveYUVBlend( Image* pImageOut,
                                       HXxRect boundingRect,
                                       CBaseSurface* pSurface,
                                       INT32 lXOffset,
                                       INT32 lYOffset)
{
    BOOL bRetVal = FALSE;
    CHXMapPtrToPtr::Iterator i;

    if( m_pSite->m_AlphaBlendNotifiers.GetCount()==0 )
    {
        return bRetVal;
    }

    for( i=m_pSite->m_AlphaBlendNotifiers.Begin() ;
         i!=m_pSite->m_AlphaBlendNotifiers.End()  ;
         ++i)
    {
        CHXBaseSite* pSite = (CHXBaseSite*)*i;
        HXREGION* pReg = NULL;

        //Find the region we need to blend with.
        CHXMapPtrToPtr::Iterator j = pSite->m_AlphaBlendSites.Begin();
        CHXBaseSite* pTmp = NULL;
        while( j != pSite->m_AlphaBlendSites.End() && pTmp != m_pSite )
        {
            pTmp = (CHXBaseSite*)j.get_key();
            if( pTmp == m_pSite )
            {
                pReg = (HXREGION*)*j;
                break;
            }
            ++j;
        }

        //Hmmmm, something is probably wrong here...
        if( HXEmptyRegion(pReg) )
        {
            HX_ASSERT("oops"==NULL);
            break;
        }

        //Find the YUVA buffer we need to blend against....
        Image* pImage = NULL;
        j = pSite->m_pVideoSurface->m_YUVAImageList.Begin();
        pTmp = NULL;
        while( j!=pSite->m_pVideoSurface->m_YUVAImageList.End() )
        {
            pTmp = (CHXBaseSite*)j.get_key();
            if( pTmp == m_pSite )
            {
                pImage = (Image*)*j;
                break;
            }
            ++j;
        }

        //Now blend with this image....
        if( pReg && pImage )
        {
            bRetVal = TRUE;

            //blend by extents, blt by rects, and save for Blt().
            for(int nIdx=0 ; nIdx<pReg->numRects ; nIdx++ )
            {
                HXxRect BlockOut;
                BlockOut.left   = pReg->rects[nIdx].x1;
                BlockOut.top    = pReg->rects[nIdx].y1;
                BlockOut.right  = pReg->rects[nIdx].x2;
                BlockOut.bottom = pReg->rects[nIdx].y2;


                //Downscale all rects....
                double scaleX = 1;
                double scaleY = 1;

                scaleX = pSurface->m_scaleFactorX/pSite->m_pVideoSurface->m_scaleFactorX;
                scaleY = pSurface->m_scaleFactorY/pSite->m_pVideoSurface->m_scaleFactorY;

                //We must restrict this region to just the part that
                //overlaps the actuall YUV surface...
                int tlx = (int)((double)(m_pSite->m_topleft.x)/scaleX+.5);
                int tly = (int)((double)(m_pSite->m_topleft.y)/scaleY+.5);
                int xx = tlx - boundingRect.left;
                int yy = tly - boundingRect.top;

                if( (int)(BlockOut.left/scaleX+.5) < boundingRect.left )
                    BlockOut.left = (INT32)(boundingRect.left*scaleX+.5);
                if( (int)(BlockOut.top/scaleY+.5) < boundingRect.top )
                    BlockOut.top = (INT32)(boundingRect.top*scaleY+.5);
                if( (int)(BlockOut.right/scaleX+.5) > boundingRect.right )
                    BlockOut.right = (INT32)(boundingRect.right*scaleX+.5);
                if( (int)(BlockOut.bottom/scaleY+.5) > boundingRect.bottom )
                    BlockOut.bottom = (INT32)(boundingRect.bottom*scaleY+.5);

                BlockOut.left   =
                    (int)((double)(BlockOut.left-m_pSite->m_topleft.x)/scaleX+.5);
                BlockOut.top    =
                    (int)((double)(BlockOut.top-m_pSite->m_topleft.y)/scaleY+.5);
                BlockOut.right  =
                    (int)((double)(BlockOut.right-m_pSite->m_topleft.x)/scaleX+.5);
                BlockOut.bottom =
                    (int)((double)(BlockOut.bottom-m_pSite->m_topleft.y)/scaleY+.5);


                // Ensure we start on even pixels (for yuv blending/conversion)
                BlockOut.left &= ~1;
                BlockOut.top &= ~1;

                if( (BlockOut.right-BlockOut.left) & 1 )
                {
                    BlockOut.right  &= ~1;
                }
                if( (BlockOut.bottom-BlockOut.top) & 1 )
                {
                    BlockOut.bottom &= ~1;
                }


                int nSizeX = BlockOut.right-BlockOut.left;
                int nSizeY = BlockOut.bottom-BlockOut.top;

                //Take care of odd number lines and pels that are
                //not a multimple of four.
                int nSizeOutX = (nSizeX+3)&~3;
                int nSizeOutY = (nSizeY+1)&~1;

                int nPSiteRectLeft =
                    (int)((double)(pReg->rects[nIdx].x1-pSite->m_topleft.x)/scaleX+.5);
                int nPSiteRectTop  =
                    (int)((double)(pReg->rects[nIdx].y1-pSite->m_topleft.y)/scaleY+.5);

                //Now blend into the new buffer.
                //Have to pass pointers to the actual pixel.
                int nPImagePitch     = GETBITMAPPITCH(&pImage->bmiImage);
                int nPImageOutPitch  = GETBITMAPPITCH(&pImageOut->bmiImage);
                int nPImageDataPitch = nPImageOutPitch;

                if( BlockOut.left+xx < 0)
                    xx = -BlockOut.left;
                if( BlockOut.top+yy < 0)
                    yy = -BlockOut.top;

                if( BlockOut.left+xx+nSizeX > pImageOut->bmiImage.biWidth )
                    xx -= 1;
                if( BlockOut.top+yy+nSizeY > pImageOut->bmiImage.biHeight )
                    yy -= 1;

                zm_pColorAcc->I420andYUVA(
                    //Botton YUV image
                    pImageOut->pucImage,
                    pImageOut->bmiImage.biWidth, pImageOut->bmiImage.biHeight,
                    nPImageDataPitch,
                    BlockOut.left+xx, BlockOut.top+yy,
                    //Top source YUVA Image
                    pImage->pucImage,
                    pImage->bmiImage.biWidth, pImage->bmiImage.biHeight,
                    nPImagePitch,
                    nPSiteRectLeft, nPSiteRectTop,
                    //Output Image
                    pImageOut->pucImage,
                    pImageOut->bmiImage.biWidth, pImageOut->bmiImage.biHeight,
                    nPImageOutPitch,
                    BlockOut.left+xx, BlockOut.top+yy,
                    nSizeX, nSizeY,
                    // Output color format.
                    CID_I420
                    );

                //Now keep drawing up the line through all
                //overlapping ARGB images.
                pSite->m_pVideoSurface->_RecursiveYUVBlend( pImageOut,
                                                            boundingRect,
                                                            pSurface,
                                                            lXOffset+xx,
                                                            lYOffset+yy
                                                            );
            }
        }
    }

    return bRetVal;
}


BOOL CBaseSurface::_SetUpBlendRects( HXBitmapInfoHeader* pBitmapInfo,
                                     UCHAR*               pImageData)
{
    BOOL bRetVal = FALSE;
    CHXMapPtrToPtr::Iterator i;

    //YUV optimized blending. We must blend 'up' the chain in this
    //case.  For each alpha notifier we have we must grab that sites
    //YUVA buffer and blend it against ourselves.
    CHXSimpleList::Iterator jj = m_imageBlocks.Begin();
    HXDestroyRegion(m_pAdditionalColorKey);
    m_pAdditionalColorKey = HXCreateRegion();

    if( m_pSite->m_AlphaBlendNotifiers.GetCount()==0 )
    {
        m_bYUVBlending = FALSE;
        return bRetVal;
    }

    for( i=m_pSite->m_AlphaBlendNotifiers.Begin() ;
         i!=m_pSite->m_AlphaBlendNotifiers.End()  ;
         ++i)
    {
        CHXBaseSite* pSite = (CHXBaseSite*)*i;
        HXREGION* pReg = NULL;

        //Find the region we need to blend with.
        CHXMapPtrToPtr::Iterator j = pSite->m_AlphaBlendSites.Begin();
        CHXBaseSite* pTmp = NULL;
        while( j != pSite->m_AlphaBlendSites.End() && pTmp != m_pSite )
        {
            pTmp = (CHXBaseSite*)j.get_key();
            if( pTmp == m_pSite )
            {
                pReg = (HXREGION*)*j;
                break;
            }
            ++j;
        }

        if( !pReg )
        {
            break;
        }

        //Find the YUVA buffer we need to blend against....
        Image* pImage = NULL;
        j = pSite->m_pVideoSurface->m_YUVAImageList.Begin();
        pTmp = NULL;
        while( j!=pSite->m_pVideoSurface->m_YUVAImageList.End() )
        {
            pTmp = (CHXBaseSite*)j.get_key();
            if( pTmp == m_pSite )
            {
                pImage = (Image*)*j;
                break;
            }
            ++j;
        }

        //Now blend with this image....
        if( pReg && pImage )
        {
            bRetVal = TRUE;
            //blend by extents, blt by rects, and save for Blt().
            for(int nIdx=0 ; nIdx<pReg->numRects ; nIdx++ )
            {
                //reuse the last imageblock....
                ImageBlock* pBlockOut = NULL;
                Image* pImageOut      = NULL;

                if( jj == m_imageBlocks.End() )
                {
                    //We ran out of image blocks.
                    pBlockOut = new ImageBlock;
                    pImageOut = new Image;
                    memset( pImageOut, 0, sizeof(Image) );
                    memset( pBlockOut, 0, sizeof(ImageBlock) );
                    pBlockOut->pImage = pImageOut;
                    m_imageBlocks.AddTail((void*)pBlockOut);
                    jj = m_imageBlocks.End();
                }
                else
                {
                    pBlockOut = (ImageBlock*)*jj;
                    ++jj;
                    pImageOut = pBlockOut->pImage;
                }

                pBlockOut->pSrcSite = pSite;

                //Downscale all rects....
                double scaleX = 1;
                double scaleY = 1;

                scaleX = m_scaleFactorX/pSite->m_pVideoSurface->m_scaleFactorX;
                scaleY = m_scaleFactorY/pSite->m_pVideoSurface->m_scaleFactorY;

                pBlockOut->rect.left   =
                    (int)((double)(pReg->rects[nIdx].x1-m_pSite->m_topleft.x)/scaleX+.5);
                pBlockOut->rect.top    =
                    (int)((double)(pReg->rects[nIdx].y1-m_pSite->m_topleft.y)/scaleY+.5);
                pBlockOut->rect.right  =
                    (int)((double)(pReg->rects[nIdx].x2-m_pSite->m_topleft.x)/scaleX+.5);
                pBlockOut->rect.bottom =
                    (int)((double)(pReg->rects[nIdx].y2-m_pSite->m_topleft.y)/scaleY+.5);

                // Ensure we start on even pixels (for yuv blending/conversion)
                pBlockOut->rect.left &= ~1;
                pBlockOut->rect.top &= ~1;

                if( (pBlockOut->rect.right-pBlockOut->rect.left) & 1 )
                {
                    pBlockOut->rect.right  &= ~1;
                }
                if( (pBlockOut->rect.bottom-pBlockOut->rect.top) & 1 )
                {
                    pBlockOut->rect.bottom &= ~1;
                }

                int nSizeX = pBlockOut->rect.right-pBlockOut->rect.left;
                int nSizeY = pBlockOut->rect.bottom-pBlockOut->rect.top;

                //Take care of odd number lines and pels that are
                //not a multimple of four.
                int nSizeOutX = (nSizeX+3)&~3;
                int nSizeOutY = (nSizeY+1)&~1;

                int nPSiteRectLeft =
                    (int)((double)(pReg->rects[nIdx].x1-pSite->m_topleft.x)/scaleX+.5);
                int nPSiteRectTop  =
                    (int)((double)(pReg->rects[nIdx].y1-pSite->m_topleft.y)/scaleY+.5);

                pBlockOut->startX = nPSiteRectLeft;
                pBlockOut->startY = nPSiteRectTop;

#ifdef _WIN32
                int nResult = MakeBitmap( (LPBITMAPINFO)&(pImageOut->bmiImage),
                                          sizeof(pImageOut->bmiImage),
                                          CID_I420,
                                          nSizeOutX,
                                          nSizeOutY,
                                          NULL,
                                          0);
#else
                int nResult = MakeBitmap( (HXBitmapInfo*)&(pImageOut->bmiImage),
                                          sizeof(pImageOut->bmiImage),
                                          CID_I420,
                                          nSizeOutX,
                                          nSizeOutY,
                                          NULL,
                                          0);
#endif

                //Alloc it.
                if( pImageOut->pucImage )
                {
                    pImageOut->pucImage = (UCHAR*)
                        realloc( pImageOut->pucImage,
                                 sizeof(UCHAR)*pImageOut->bmiImage.biSizeImage
                                 );
                }
                else
                {
                    pImageOut->pucImage = (UCHAR*)
                        malloc( sizeof(UCHAR)*pImageOut->bmiImage.biSizeImage);
                }


                if( pBitmapInfo && pImageData )
                {
                    //Now blend into the new buffer.
                    //Have to pass pointers to the actual pixel.
                    int nPImageDataPitch = GETBITMAPPITCH(pBitmapInfo);
                    int nPImagePitch     = GETBITMAPPITCH(&pImage->bmiImage);
                    int nPImageOutPitch  = GETBITMAPPITCH(&pImageOut->bmiImage);

                    //Sanity check for rapid SetSize calls.
                    BOOL bDoIt =
                        nSizeX                 <= pImage->bmiImage.biWidth  &&
                        nSizeY                 <= pImage->bmiImage.biHeight &&
                        nSizeX                 <= pBitmapInfo->biWidth  &&
                        nSizeY                 <= pBitmapInfo->biHeight &&
                        pBlockOut->rect.right  <= pBitmapInfo->biWidth  &&
                        pBlockOut->rect.left   <= pBitmapInfo->biWidth  &&
                        pBlockOut->rect.bottom <= pBitmapInfo->biHeight &&
                        pBlockOut->rect.top    <= pBitmapInfo->biHeight &&
                        pBlockOut->rect.right  >=0 &&
                        pBlockOut->rect.left   >=0 &&
                        pBlockOut->rect.bottom >=0 &&
                        pBlockOut->rect.top    >=0 &&
                        nPSiteRectLeft         >= 0 &&
                        nPSiteRectTop          >= 0;

                    if( bDoIt )
                    {
                        zm_pColorAcc->I420andYUVA(
                            //Botton YUV image
                            pImageData,
                            pBitmapInfo->biWidth, pBitmapInfo->biHeight,
                            nPImageDataPitch,
                            pBlockOut->rect.left, pBlockOut->rect.top,
                            //Top source YUVA Image
                            pImage->pucImage,
                            pImage->bmiImage.biWidth, pImage->bmiImage.biHeight,
                            nPImagePitch,
                            nPSiteRectLeft, nPSiteRectTop,
                            //Output Image
                            pImageOut->pucImage,
                            pImageOut->bmiImage.biWidth, pImageOut->bmiImage.biHeight,
                            nPImageOutPitch,
                            0, 0,
                            nSizeX, nSizeY,
                            // Output color format.
                            CID_I420
                            );

                        //Now keep drawing up the line through all
                        //overlapping ARGB images.
                        pSite->m_pVideoSurface->_RecursiveYUVBlend( pImageOut,
                                                                    pBlockOut->rect,
                                                                    this, 0, 0);
                    }
                }
            }

            if( m_imageBlocks.GetCount() )
            {
                //Save the last time we did a blend on this site. This is used to
                //help keep animated images blended over paused or stopped YUV
                //content because on YUV we blend from the bottom up.
                m_ulLastBlendTime = HX_GET_BETTERTICKCOUNT();
            }

            //Merge the region to m_Region for the YUV image.
            HXUnionRegion( m_pAdditionalColorKey, pReg, m_pAdditionalColorKey);

            //Remove the region from the overlapping ARGB surface
            //so it doesn't blt any more unless it changes.
            if( pSite && pSite->m_Region )
                HXSubtractRegion( pSite->m_Region, pReg, pSite->m_Region );

        }
    }

    return bRetVal;
}



BOOL CBaseSurface::_AlphaSetupAndBlending(HXBitmapInfoHeader* pBitmapInfo,
                                          UCHAR*               pImageData,
                                          HXxRect&             rSrcRect,
                                          HXxRect&             rDestRect
                                          )
{

    BOOL  bDoAlpha = FALSE;
    CHXMapPtrToPtr::Iterator i;

    if (!pImageData)
        return bDoAlpha;

    //Are borders being drawn?
    INT32 nCID = GETBITMAPCOLOR(pBitmapInfo);

    HX_ASSERT( m_pSite );
    if( ( m_pucLastImage==NULL ||
          m_bmiLastImage.biWidth!=m_pSite->m_size.cx ||
          m_bmiLastImage.biHeight!=m_pSite->m_size.cy
          ) &&
        (( m_pSite->_BordersActive() ||
           m_pSite->_FadeTransitionActive()  ||
           m_pSite->_TakesPartInAlphaChain()) ||
         (( m_pSite->m_AlphaBlendNotifiers.GetCount() != 0 ||
            m_pSite->m_AlphaBlendSites.GetCount() != 0)
          &&
          !(IsYUV(nCID) && _IsDisplaySurfaceYuv())
          )
         ))
    {
        memset( &m_bmiLastImage, 0, sizeof( m_bmiLastImage ) );

        //Alloc new frame data...
#ifdef _WIN32
        int nResult = MakeBitmap( (LPBITMAPINFO)&m_bmiLastImage,
                                  sizeof(m_bmiLastImage),
                                  CID_ARGB32,
                                  m_pSite->m_size.cx,
                                  m_pSite->m_size.cy,
                                  NULL,
                                  0);
#else
        int nResult = MakeBitmap( (HXBitmapInfo*)&m_bmiLastImage,
                                  sizeof(m_bmiLastImage),
                                  CID_ARGB32,
                                  m_pSite->m_size.cx,
                                  m_pSite->m_size.cy,
                                  NULL,
                                  0);
#endif
        int size = sizeof(UCHAR)*m_bmiLastImage.biSizeImage;
        if( !m_pucLastImage )
            m_pucLastImage  = (UCHAR*)malloc(size);
        else
            m_pucLastImage  = (UCHAR*)realloc((void*)m_pucLastImage, size);

        HX_ASSERT( m_pucLastImage);
    }

    if( ( m_pucLastImage &&
          !m_pSite->_BordersActive() &&
          !m_pSite->_FadeTransitionActive()  &&
          !m_pSite->_TakesPartInAlphaChain() &&
          (
              m_pSite->m_AlphaBlendNotifiers.GetCount() == 0 &&
              m_pSite->m_AlphaBlendSites.GetCount() == 0 ||
              (IsYUV(nCID) && _IsDisplaySurfaceYuv())
              )
          )
        )
    {
        //We don't need the allocated buffer. Delete it.
        HX_FREE(m_pucLastImage);
    }




    //Save the image data. This is where all the alpha blending
    //will take place.
    if( m_pucLastImage &&
        ( m_pSite->m_AlphaBlendNotifiers.GetCount()!=0 ||
          CID_ARGB32==nCID ||
          m_pSite->_BordersActive() || m_pSite->_FadeTransitionActive()
          )
        )
    {
        //Stretch or shrink, if nessecary, to make the the image
        //the same size as our site. If we dont' we will get
        //alpha blending artifacts...
#ifdef _WINDOWS
        zm_pColorAcc->SetPlayer(m_pSite->m_pRootSurface->GetPlayer());
#endif

        BOOL bConverter = zm_pColorAcc->CheckColorConverter(nCID, CID_ARGB32);
        m_pSite->ColorConverterRequest(nCID, CID_ARGB32, bConverter);

        if (bConverter )
        {
            int cx = m_bmiLastImage.biWidth;
            int cy = m_bmiLastImage.biHeight;

            int pitchIn  = GETBITMAPPITCH( pBitmapInfo );
            int pitchOut = GETBITMAPPITCH( &m_bmiLastImage );

            zm_pColorAcc->ColorConvert(CID_ARGB32,
                                       m_pucLastImage,
                                       cx, cy,
                                       pitchOut,
                                       rDestRect.left,  rDestRect.top,
                                       rDestRect.right-rDestRect.left,
                                       rDestRect.bottom-rDestRect.top,
                                       nCID,
                                       pImageData,
                                       pBitmapInfo->biWidth,
                                       pBitmapInfo->biHeight,
                                       pitchIn,
                                       rSrcRect.left,  rSrcRect.top,
                                       rSrcRect.right-rSrcRect.left,
                                       rSrcRect.bottom-rSrcRect.top
                                       );
        }
        else
        {
#ifdef _DEBUG
            fprintf( stderr, "Can't get color converter!\n" );
#endif
        }

        bDoAlpha=TRUE;

        //MOVE
        if( !IsYUV(nCID) )
            rSrcRect = rDestRect;
    }


    if(m_pSite->_BordersActive())
    {
        DrawTransitionBorders(&m_bmiLastImage,
                              m_pSite->m_nTransitionBorderWidth,
                              m_pSite->m_ulTransitionBorderColor,
                              m_pSite->m_bTransitionBlendBorder
                              );
        bDoAlpha = TRUE;
    }

    //Do we even care about alpha blening ourselves?
    if( ( nCID==CID_ARGB32 ||
          m_pSite->_BlendedBordersActive() ||
          m_pSite->_FadeTransitionActive() ) &&
        m_pSite->GetParentSite() )
    {
        //Alphablend against all of our regions.
        for( i=m_pSite->m_AlphaBlendSites.Begin() ;
             i!=m_pSite->m_AlphaBlendSites.End()  ;
             ++i)
        {
            CHXBaseSite* pSite = (CHXBaseSite*) i.get_key();
            HXREGION* pRegion = (HXREGION*)*i;

            //We need to intersect this region with the rect that we
            //are actually blt'ing. SubRect support.
            HXxRect TmpRect = rDestRect;
            TmpRect.left   += m_pSite->m_topleft.x;
            TmpRect.right  += m_pSite->m_topleft.x;
            TmpRect.top    += m_pSite->m_topleft.y;
            TmpRect.bottom += m_pSite->m_topleft.y;

            HXREGION* pInter  = HXCreateRectRegion( TmpRect.left,
                                                    TmpRect.top,
                                                    TmpRect.right-TmpRect.left,
                                                    TmpRect.bottom-TmpRect.top
                                                    );
            HXIntersectRegion( pInter, pRegion, pInter );

            CBaseSurface* pTmp = pSite->m_pVideoSurface;
            if( pTmp->m_pucLastImage &&
                !(IsYUV(GETBITMAPCOLOR(&pTmp->m_bmiLastImage)) && pTmp->_IsDisplaySurfaceYuv())
                )
            {
                _AlphaBlend( pInter,
                             pSite->m_pVideoSurface->m_pucLastImage,
                             &(pSite->m_pVideoSurface->m_bmiLastImage),
                             &(pSite->m_topleft),
                             m_pucLastImage,
                             &m_bmiLastImage,
                             &(m_pSite->m_topleft)
                             );
            }

            HXDestroyRegion( pInter );

        }
        bDoAlpha = TRUE;
    }


    //For optimized YUV alphablending.  Create a YUVA image here, if
    //we have a YUV site that we blend against, and then down-scale
    //and color convert to YUVA. The YUV render's call to Blt() will
    //grab this and alphablend against us.
    //Clean up the old ones..

    i = m_YUVAImageList.Begin();
    while(i != m_YUVAImageList.End())
    {
        Image* pImage = (Image*)*i;
        HX_DELETE(pImage->pucImage);
        HX_DELETE(pImage);
        ++i;
    }
    m_YUVAImageList.RemoveAll();

    BOOL bHaveYUVBlender = FALSE;
    for( i=m_pSite->m_AlphaBlendSites.Begin() ;
         i!=m_pSite->m_AlphaBlendSites.End() ;
         ++i)
    {
        //CHXBaseSite* pSite = (CHXBaseSite*) i.get_key();
        CHXBaseSite* pSite = NULL;
        //Do a recursive search for YUV surfaces below us.
        CHXBaseSite* pSiteOrig = (CHXBaseSite*)i.get_key();
        pSite = _SearchForYUV( pSiteOrig );
        if( pSite && pSite->m_pVideoSurface )
        {
            int nCID2 = GETBITMAPCOLOR(&(pSite->m_pVideoSurface->m_bmiLastBlt));
            if( IsYUV(nCID2) && pSite->m_pVideoSurface->_IsDisplaySurfaceYuv())
            {
                //Create a YUVA buffer to hold the last frame.
                Image* pImage = new Image;
                memset( pImage, 0, sizeof( Image) );

                //Color convert to scaled YUVA into image struct.
                double scaleX = pSite->m_pVideoSurface->m_scaleFactorX/m_scaleFactorX;
                double scaleY = pSite->m_pVideoSurface->m_scaleFactorY/m_scaleFactorY;

                int nSizeX = (int)(((double)(m_bmiLastImage.biWidth))/scaleX+.5);
                int nSizeY = (int)(((double)(m_bmiLastImage.biHeight))/scaleY+.5);

                //Fix non mod quad sizes
                nSizeX = (nSizeX+3)&~3;
                nSizeY = (nSizeY+1)&~1;


                UCHAR*              pucDownscaledRGB = NULL;
                HXBitmapInfoHeader bmiDownscaledRGB;
                memset( &bmiDownscaledRGB, 0, sizeof( bmiDownscaledRGB ) );

#ifdef _WIN32
                MakeBitmap( (LPBITMAPINFO)&(pImage->bmiImage),
                            sizeof(pImage->bmiImage),
                            CID_YUVA,
                            nSizeX,
                            nSizeY,
                            NULL,
                            0);
                MakeBitmap( (LPBITMAPINFO)&bmiDownscaledRGB,
                            sizeof(bmiDownscaledRGB),
                            CID_ARGB32,
                            nSizeX,
                            nSizeY,
                            NULL,
                            0);
#else
                MakeBitmap( (HXBitmapInfo*)&(pImage->bmiImage),
                            sizeof(pImage->bmiImage),
                            CID_YUVA,
                            nSizeX,
                            nSizeY,
                            NULL,
                            0);

                MakeBitmap( (HXBitmapInfo*)&bmiDownscaledRGB,
                            sizeof(bmiDownscaledRGB),
                            CID_ARGB32,
                            nSizeX,
                            nSizeY,
                            NULL,
                            0);
#endif
                //Alloc it.
                pImage->pucImage = new UCHAR[pImage->bmiImage.biSizeImage];
                pucDownscaledRGB = new UCHAR[bmiDownscaledRGB.biSizeImage];

                //downscale the ARGB first....
                int ik = 0;
                UCHAR*               pBits = pImageData;
                HXBitmapInfoHeader* pBIS  = pBitmapInfo;
                if( IsYUV(nCID) && bDoAlpha )
                {
                    pBits = m_pucLastImage;
                    pBIS  = &m_bmiLastImage;
                }


                BOOL bConverter = zm_pColorAcc->CheckColorConverter(CID_ARGB32, CID_ARGB32);
                pSite->ColorConverterRequest(CID_ARGB32, CID_ARGB32, bConverter);

                if (bConverter)
                {
                    ik = zm_pColorAcc->ColorConvert(
                        CID_ARGB32,
                        pucDownscaledRGB,
                        bmiDownscaledRGB.biWidth,
                        bmiDownscaledRGB.biHeight,
                        GETBITMAPPITCH(&bmiDownscaledRGB),
                        0,
                        0,
                        nSizeX,
                        nSizeY,
                        CID_ARGB32,
                        pBits,
                        pBIS->biWidth,
                        pBIS->biHeight,
                        GETBITMAPPITCH(pBIS),
                        0,
                        0,
                        pBitmapInfo->biWidth,
                        pBitmapInfo->biHeight
                        );
                }

                HX_ASSERT(ik==0);

                //We must apply any fade transitions here......
                BOOL  bFade=FALSE;
                int   completeness = 0;
                if( (m_pSite->m_fpTransitionEffect == Crossfade ||
                     m_pSite->m_fpTransitionEffect == FadeToColor ||
                     m_pSite->m_fpTransitionEffect == FadeFromColor )
                    && m_pSite->m_nTransitionState!=1000 )
                {
                    completeness = m_pSite->m_nTransitionState;

                    if(m_pSite->m_bTransitionReversed)
                    {
                        completeness = 1000 - completeness;
                    }

                    if( m_pSite->m_fpTransitionEffect== FadeFromColor)
                    {
                        completeness = 1000 - completeness;
                    }

                    //Map [0,1000] --> [0,1024] for fixed point fade math.
                    completeness = (int)((float)completeness*1024.0/1000.0);

                    bFade=TRUE;

                    if( bFade )
                    {
                        int alpha     = 0;
                        int alphasave = 0;
                        ULONG32* pTmp = (ULONG32*)pucDownscaledRGB;

                        for( UINT32 i=0 ; i< bmiDownscaledRGB.biSizeImage/4; i++, pTmp++ )
                        {
                            if( m_pSite->m_fpTransitionEffect == Crossfade )
                            {
                                //fixed point version of (comp/1000 * alpha )
                                alpha = (*pTmp & 0xff000000)>>24;
                                alpha = ((completeness*(255-alpha)+512))>>10;
                                alphasave = (255-alpha)<<24;
                                *pTmp = (*pTmp&0x00ffffff) | alphasave;
                            }
                            else if( m_pSite->m_fpTransitionEffect == FadeToColor ||
                                     m_pSite->m_fpTransitionEffect == FadeFromColor)
                            {
                                //Not only affect alpha but also fading to color.
                                alpha = (completeness*255+512)>>10;
                                UINT32 fadeColor = (m_pSite->m_ulTransitionFadeColor&0x00ffffff)|(alpha<<24);
                                int pTmpAlpha = (*pTmp&0xff000000)>>24;
                                int newalpha = (((0xff-alpha)*pTmpAlpha)>>8)<<24;
                                *pTmp = newalpha |
                                    (((alpha*( (fadeColor&0x00ff0000)-(*pTmp&0x00ff0000))+((*pTmp&0x00ff0000)<<8))>>8)&0x00ff0000)|
                                    (((alpha*( (fadeColor&0x0000ff00)-(*pTmp&0x0000ff00))+((*pTmp&0x0000ff00)<<8))>>8)&0x0000ff00)|
                                    (((alpha*( (fadeColor&0x000000ff)-(*pTmp&0x000000ff))+((*pTmp&0x000000ff)<<8))>>8)&0x000000ff);
                            }
                        }
                    }
                }

                //Color convert downscaled ARGB to YUVA
                bConverter = zm_pColorAcc->CheckColorConverter(CID_ARGB32, CID_YUVA);
                pSite->ColorConverterRequest(CID_ARGB32, CID_YUVA, bConverter);

                if( bConverter )
                    ik = zm_pColorAcc->ColorConvert(
                        CID_YUVA,
                        pImage->pucImage,
                        pImage->bmiImage.biWidth,
                        pImage->bmiImage.biHeight,
                        GETBITMAPPITCH(&pImage->bmiImage),
                        0,
                        0,
                        nSizeX,
                        nSizeY,
                        CID_ARGB32,
                        pucDownscaledRGB,
                        bmiDownscaledRGB.biWidth,
                        bmiDownscaledRGB.biHeight,
                        GETBITMAPPITCH(&bmiDownscaledRGB),
                        0,
                        0,
                        bmiDownscaledRGB.biWidth,
                        bmiDownscaledRGB.biHeight
                        );

                HX_ASSERT( ik==0 );

                HX_VECTOR_DELETE( pucDownscaledRGB );

                //Add into YUVA image map for this site.
                m_pSite->_TLSLock();
                m_YUVAImageList.SetAt( pSiteOrig, pImage );
                pSiteOrig->m_pVideoSurface->m_bYUVBlending = TRUE;
                m_pSite->_TLSUnlock();

                //Remove the region from the overlapping ARGB surface
                //so it doesn't blt any more unless it changes.
                HXREGION* pRegion = (HXREGION*)*i;

                //Wow, recursive YUV blending is not fun. If we are on
                //top of an ARGB image and it is on top of a YUV image
                //then we need subtract only that part of our region
                //that overlaps the ARGB image *AND* the YUV image
                //down below.
                HXREGION*   hTemp  = NULL;
                HXxSize   size;
                HXxPoint* pPosition = NULL;

                pSite->GetSize( size );
                pPosition = pSite->GetOrigin();
                hTemp = pSite->Transition(pPosition->x, pPosition->y,
                                          pPosition->x + size.cx,
                                          pPosition->y + size.cy);
                HXIntersectRegion( hTemp, pRegion, hTemp );
                HXSubtractRegion( m_pSite->m_Region, hTemp, m_pSite->m_Region );
                HXDestroyRegion( hTemp );
            }
        }
    }

    //-----------------------------------------
    // YUV Blending Part 2
    //-----------------------------------------
    if( IsYUV(nCID)&& _IsDisplaySurfaceYuv() &&
        m_pSite->m_AlphaBlendNotifiers.GetCount() )
    {
        _SetUpBlendRects(pBitmapInfo, pImageData);
    }


#if defined(_DEBUG) && 0
    if( IsYUV(nCID) && ((m_nBltMode==HX_OVERLAY_BLT&&!m_bOffBecauseofShinking)  || m_pLinkedOverlay) )
    {
        char szBuff[256]; /* Flawfinder: ignore */

        sprintf( szBuff, "Left with m_imageBlocks: %d\n", m_imageBlocks.GetCount() ); /* Flawfinder: ignore */
        _DumpString(szBuff);
        CHXSimpleList::Iterator jjj = m_imageBlocks.Begin();
        while(jjj != m_imageBlocks.End())
        {
            ImageBlock* pBlock = (ImageBlock*)*jjj;
            sprintf( szBuff, "   Block: (%d, %d) -- (%d, %d)\n", /* Flawfinder: ignore */
                     pBlock->rect.left,
                     pBlock->rect.top,
                     pBlock->rect.right,
                     pBlock->rect.bottom
                     );
            _DumpString(szBuff);
            ++jjj;
        }
    }

#endif
    return bDoAlpha;
}


void CBaseSurface::_ConstructRects(HXxRect&   rSrcRectCopy,
                                   HXxRect&   rDestRectCopy,
                                   BOOL       bDoAlpha,
                                   int&       nNumRects,
                                   HXxRect**  paSrcRects,
                                   HXxRect**  paDestRects,
                                   HXREGION* pAdditionalRegion
                                   )
{
    HXxRect  newDestRect;
    HXxRect  origDestRect = rDestRectCopy;
    HXxPoint originOffset;

    memcpy(&originOffset,m_pSite->GetOrigin(),sizeof(HXxPoint)); /* Flawfinder: ignore */

    rDestRectCopy.left      = (INT32) ((double) rDestRectCopy.left   *
                                       m_fScrollBarZoom + 0.5);
    rDestRectCopy.right     = (INT32) ((double) rDestRectCopy.right  *
                                       m_fScrollBarZoom + 0.5);
    rDestRectCopy.top       = (INT32) ((double) rDestRectCopy.top    *
                                       m_fScrollBarZoom + 0.5);
    rDestRectCopy.bottom    = (INT32) ((double) rDestRectCopy.bottom *
                                       m_fScrollBarZoom + 0.5);

    newDestRect.left    = rDestRectCopy.left   + originOffset.x;
    newDestRect.right   = rDestRectCopy.right  + originOffset.x;
    newDestRect.top     = rDestRectCopy.top    + originOffset.y;
    newDestRect.bottom  = rDestRectCopy.bottom + originOffset.y;

    //If we are in full screen we have to offset our origin
    HXxPoint screenOffset = m_pSite->GetScreenOffset();

    //compute our scaling factor if we don't have access to the real
    //site scaling info via BltSubRects or IHXOriginalSize.
    if( !m_pSite->SiteScaleingInfoAvailable() || m_pSite->_TakesPartInAlphaChain()  )
    {
        int nDestDX = rDestRectCopy.right  - rDestRectCopy.left;
        int nSrcDX  = rSrcRectCopy.right   - rSrcRectCopy.left;
        int nDestDY = rDestRectCopy.bottom - rDestRectCopy.top;
        int nSrcDY  = rSrcRectCopy.bottom  - rSrcRectCopy.top;

        m_scaleFactorX = (double)nDestDX/(double)nSrcDX;
        m_scaleFactorY = (double)nDestDY/(double)nSrcDY;
    }

    //Scrolling support. XXXAH phew. We might want to go over this!
    IHXValues* pValues = NULL;
    ULONG32     bScroll = FALSE;
    HXxRect     rect;

    int xSrcOffSet = 0;
    int ySrcOffSet = 0;

    if( m_pSite->QueryInterface( IID_IHXValues, (void**) &pValues )==HXR_OK )
    {
        pValues->GetPropertyULONG32("ScrollingSite", bScroll);
        if (bScroll)
        {
            m_pSite->GetWindowRect(&rect);
            HXxSize size;
            size.cx = rect.right - rect.left;
            size.cy = rect.bottom - rect.top;

            if (rDestRectCopy.right - rDestRectCopy.left > size.cx)
            {
                int xDelta = rDestRectCopy.right - rDestRectCopy.left - 1;
                m_pSite->SafeSetXSliderRange(xDelta);

                int xsliderPos      = m_pSite->GetXSliderPos();
                int currentXDelta   = m_pSite->GetXSliderRange();

                // find the thumb size
                HXxSize parentSize;
                m_pSite->m_pParentSite->GetSize(parentSize);

                xDelta = xDelta - parentSize.cx + m_pSite->GetSliderWidth();
                currentXDelta -=parentSize.cx;

                if(xsliderPos && currentXDelta)
                {
                    double sliderPercentage = (double)xsliderPos / currentXDelta;
                    if (sliderPercentage > 1.0) sliderPercentage = 1.0; // how does this happen?
                    xSrcOffSet = (int) ((sliderPercentage * xDelta ) / m_scaleFactorX + 0.5) ;
                }

                //Modify the dest rect. This is a bit crazy.
                newDestRect.left    = originOffset.x;
                newDestRect.right   = originOffset.x + size.cx - m_pSite->GetSliderWidth();
            }

            if (rDestRectCopy.bottom - rDestRectCopy.top > size.cy)
            {
                int yDelta = rDestRectCopy.bottom - rDestRectCopy.top - 1;
                m_pSite->SafeSetYSliderRange(yDelta);

                int ysliderPos      = m_pSite->GetYSliderPos();
                int currentYDelta   = m_pSite->GetYSliderRange();

                // find the thumb size
                HXxSize parentSize;
                m_pSite->m_pParentSite->GetSize(parentSize);

                yDelta = yDelta - parentSize.cy + m_pSite->GetSliderHeight();
                currentYDelta -=parentSize.cy;

                if(ysliderPos && currentYDelta)
                {
                    double sliderPercentage = (double)ysliderPos / currentYDelta;
                    if (sliderPercentage > 1.0) sliderPercentage = 1.0; // how does this happen?
                    ySrcOffSet = (int) ((sliderPercentage * yDelta ) / m_scaleFactorY + 0.5) ;
                }

                //Modify the dest rect. This is a bit crazy.
                newDestRect.top     = originOffset.y;
                newDestRect.bottom  = originOffset.y + size.cy - m_pSite->GetSliderHeight();
            }
        }
        HX_RELEASE(pValues);
    }
    rDestRectCopy = newDestRect;

    CBaseRootSurface* pRootSurface = m_pSite->GetRootSurface();
    HX_ASSERT( pRootSurface );

    if( !pRootSurface )
        return;

    nNumRects = 0;

    if( !m_pSite->m_Region )
        return;

    HXREGION* pWhole = HXCreateRegion();
    if( pAdditionalRegion)
    {
        HXUnionRegion( m_pSite->m_Region, pAdditionalRegion, pWhole );
    }
    else
    {
        HXUnionRegion( m_pSite->m_Region, pWhole, pWhole );
    }

    //Intersect incoming rectangle with our region.
    HXREGION* pDirty = HXCreateRectRegion( newDestRect.left,
                                           newDestRect.top,
                                           newDestRect.right-newDestRect.left,
                                           newDestRect.bottom-newDestRect.top
                                           );
    if( pDirty )
    {
        HXIntersectRegion( pDirty, pWhole, pWhole );
        HXDestroyRegion( pDirty );
    }

    HX_ASSERT( pWhole );

    //Set up the array of src and dest rect returns
    if ((_IsDisplaySurfaceYuv() && HX_OVERLAY_BLT == m_nBltMode) &&
        (m_bVideoSurface2 || !m_bYUVBlending) &&
        !m_pLinkedOverlay && !m_LinkedSites.GetCount()
        )
        nNumRects = 1;
    else
        nNumRects = pWhole->numRects;

    if( NULL == paSrcRects )
        *paSrcRects  = (HXxRect*)malloc(sizeof(HXxRect)*nNumRects);
    else
    {
#ifdef _DEBUG
        // We can not trace reallocs in pndebug...so use allocs when tracing
        if (m_bAllocHook)
        {
            free(*paSrcRects);
            *paSrcRects  = (HXxRect*)malloc(sizeof(HXxRect)*nNumRects);
        }
        else
            *paSrcRects  = (HXxRect*)realloc(*paSrcRects, sizeof(HXxRect)*nNumRects);
#else
        *paSrcRects  = (HXxRect*)realloc(*paSrcRects, sizeof(HXxRect)*nNumRects);
#endif
    }

    if( NULL == paDestRects )
        *paDestRects  = (HXxRect*)malloc(sizeof(HXxRect)*nNumRects);
    else
    {
#ifdef _DEBUG
        // We can not trace reallocs in pndebug...so use allocs when tracing
        if (m_bAllocHook)
        {
            free(*paDestRects);
            *paDestRects  = (HXxRect*)malloc(sizeof(HXxRect)*nNumRects);
        }
        else
            *paDestRects  = (HXxRect*)realloc(*paDestRects, sizeof(HXxRect)*nNumRects);
#else
        *paDestRects  = (HXxRect*)realloc(*paDestRects, sizeof(HXxRect)*nNumRects);
#endif
    }

    HX_ASSERT( paSrcRects );
    HX_ASSERT( paDestRects );

    for( int i=0 ; i<nNumRects ; i++)
    {
        HXxRect rgnRect;
        rgnRect.left     = pWhole->rects[i].x1;
        rgnRect.top      = pWhole->rects[i].y1;
        rgnRect.right    = pWhole->rects[i].x2;
        rgnRect.bottom   = pWhole->rects[i].y2;

        if( (_IsDisplaySurfaceYuv() && HX_OVERLAY_BLT == m_nBltMode) &&
            (m_bVideoSurface2 || !m_bYUVBlending ) &&
            !m_pLinkedOverlay && !m_LinkedSites.GetCount()
            )
        {
            //Merge our region with the list of regions in the image block
            //list....

            HXREGION* pRegTmp = HXCreateRegion();
            HXUnionRegion( pRegTmp, pWhole, pRegTmp );
            CHXMapPtrToPtr::Iterator i;
            for( i=m_pSite->m_AlphaBlendNotifiers.Begin() ;
                 i!=m_pSite->m_AlphaBlendNotifiers.End()  ;
                 ++i)
            {
                CHXBaseSite* pSite = (CHXBaseSite*)*i;
                HXREGION* pReg = NULL;
                //Find the region we need to blend with.
                CHXMapPtrToPtr::Iterator j = pSite->m_AlphaBlendSites.Begin();
                CHXBaseSite* pTmp = NULL;
                while( j != pSite->m_AlphaBlendSites.End() && pTmp != m_pSite )
                {
                    pTmp = (CHXBaseSite*)j.get_key();
                    if( pTmp == m_pSite )
                    {
                        pReg = (HXREGION*)*j;
                        HXUnionRegion( pRegTmp, pReg, pRegTmp );
                        break;
                    }
                    ++j;
                }
            }
            //now just take the extents.....
            rgnRect.left     = pRegTmp->extents.x1;
            rgnRect.top      = pRegTmp->extents.y1;
            rgnRect.right    = pRegTmp->extents.x2;
            rgnRect.bottom   = pRegTmp->extents.y2;
            HXDestroyRegion( pRegTmp );

        }

        UINT32 whichMonitor;

        for (whichMonitor = 0; whichMonitor < pRootSurface->_GetNumberOfMonitors(); whichMonitor++)
        {
            HXxRect tempRect;
            if (pRootSurface->_RectOnNthMonitor(rgnRect, whichMonitor, tempRect))
            {

                HXxRect finalRect = ComputeIntersection(&tempRect, &rDestRectCopy);

                if(finalRect.left  == 0 &&
                   finalRect.right == 0 &&
                   finalRect.top   == 0 &&
                   finalRect.bottom == 0)
                {
                    continue;
                }

                HXxRect finalSrcRect;
                finalSrcRect.left   = (int)(((double)(finalRect.left   - originOffset.x)/m_scaleFactorX) + 0.5) + xSrcOffSet;
                finalSrcRect.top    = (int)(((double)(finalRect.top    - originOffset.y)/m_scaleFactorY) + 0.5) + ySrcOffSet;
                finalSrcRect.right  = (int)(((double)(finalRect.right  - originOffset.x)/m_scaleFactorX) + 0.5) + xSrcOffSet;
                finalSrcRect.bottom = (int)(((double)(finalRect.bottom - originOffset.y)/m_scaleFactorY) + 0.5) + ySrcOffSet;

                //We need to compensate for renderers that pass a
                //pointer to a very large buffer that they are BLT'ing
                //just a small rect of. RealText for example uses a
                //very long image for scrolling text with the text
                //repeated in it and just adjust the source rect to
                //make it look like scrolling is happening. Up to this
                //point we have just assumed that the source rects are
                //relative to 0,0 in some bitmap.
                int dx = 0;
                int dy = 0;
                if( !bDoAlpha )
                {
                    //If we are doing alpha blending then this site has been
                    //color converted and scaled to 1:1 into the pucLastImage
                    //so the scale factor is always 1.0.
                    dx = (int)(rSrcRectCopy.left-((float)origDestRect.left/m_scaleFactorX));
                    dy = (int)(rSrcRectCopy.top-((float)origDestRect.top/m_scaleFactorY));
                }
                else
                {
                    dx = rSrcRectCopy.left-origDestRect.left;
                    dy = rSrcRectCopy.top-origDestRect.top;
                }


                finalSrcRect.left   += dx;
                finalSrcRect.right  += dx;
                finalSrcRect.top    += dy;
                finalSrcRect.bottom += dy;

                if (rSrcRectCopy.bottom != 0)
                {
                    finalSrcRect.bottom = finalSrcRect.bottom<1 ? 1 : finalSrcRect.bottom;
                }

                if (rSrcRectCopy.right != 0)
                {
                    finalSrcRect.right  = finalSrcRect.right<1 ? 1 : finalSrcRect.right;
                }

                if( finalSrcRect.right-finalSrcRect.left==0 ||
                    finalSrcRect.bottom-finalSrcRect.top==0 )
                {
                    // 1 pixel blts cause issues...so special case it.
                    if (rSrcRectCopy.left == 0 &&
                        rSrcRectCopy.top == 0  &&
                        rSrcRectCopy.right == 1 &&
                        rSrcRectCopy.bottom == 1)
                    {
                        finalSrcRect = rSrcRectCopy;
                    }
                    else
                    {
                        continue;
                    }
                }

                finalRect.left  += screenOffset.x;
                finalRect.top   += screenOffset.y;
                finalRect.bottom+= screenOffset.y;
                finalRect.right += screenOffset.x;


                if( m_nBltMode==HX_BASIC_BLT && bDoAlpha)
                {
                    //Scale the finalSrcRect. We color converted
                    //earlier to our sites size.
                    finalSrcRect.top    = (int)((float)finalSrcRect.top*m_scaleFactorY+.5);
                    finalSrcRect.left   = (int)((float)finalSrcRect.left*m_scaleFactorX+.5);
                    finalSrcRect.bottom = (int)((float)finalSrcRect.bottom*m_scaleFactorY+.5);
                    finalSrcRect.right  = (int)((float)finalSrcRect.right*m_scaleFactorX+.5);
                }
                //Combine finalSrcRect with the BitmapInfoRect (in case
                //something went wrong earlier....
                if( !bDoAlpha )
                {
                    if (finalSrcRect.right > m_bmiLastBlt.biWidth)
                        finalSrcRect.right = m_bmiLastBlt.biWidth;

                    if (finalSrcRect.bottom > m_bmiLastBlt.biHeight)
                        finalSrcRect.bottom = m_bmiLastBlt.biHeight;
                }
                else
                {
                    if (finalSrcRect.right > m_bmiLastImage.biWidth)
                        finalSrcRect.right = m_bmiLastImage.biWidth;

                    if (finalSrcRect.bottom > m_bmiLastImage.biHeight)
                        finalSrcRect.bottom = m_bmiLastImage.biHeight;
                }


                (*paSrcRects)[i]  = finalSrcRect;
                (*paDestRects)[i] = finalRect;
            }
        }
    }

    if( pWhole )
    {
        HXDestroyRegion( pWhole );
    }
}

//IHXSubRectVideoSurface method.
STDMETHODIMP CBaseSurface::BltSubRects( UCHAR*               pImageData,
                                        HXBitmapInfoHeader* pBitmapInfo,
                                        HXxBoxRegion*          pDestRects,
                                        HXxBoxRegion*          pSrcRects,
                                        float                fScaleFactorX,
                                        float                fScaleFactorY)
{
    HX_RESULT ret = HXR_OK;

    //quick exits...
    if( m_pSite->m_bSiteNeverBlts )
        return HXR_OK;

    if( NULL==pImageData || NULL==pBitmapInfo )
        return HXR_FAIL;

    HX_ASSERT( pDestRects->numRects == pSrcRects->numRects );
    if( pDestRects->numRects != pSrcRects->numRects )
        return HXR_FAIL;

    HX_ASSERT( pDestRects->rects && pSrcRects->rects );
    if( !pDestRects->rects || !pSrcRects->rects )
        return HXR_FAIL;

    //Wrap calls to all the individual Blts with a lock.
    m_pSite->_TLSLock();

    //Set the scale factors
    if( !m_pSite->_TakesPartInAlphaChain() )
    {
        m_scaleFactorX = fScaleFactorX;
        m_scaleFactorY = fScaleFactorY;
    }

    HXxRect destRect;
    HXxRect srcRect;

    for( int i=0 ; i<pDestRects->numRects && ret==HXR_OK ; i++ )
    {
        destRect.left   = pDestRects->rects[i].x1;
        destRect.top    = pDestRects->rects[i].y1;
        destRect.right  = pDestRects->rects[i].x2;
        destRect.bottom = pDestRects->rects[i].y2;
        srcRect.left    = pSrcRects->rects[i].x1;
        srcRect.top     = pSrcRects->rects[i].y1;
        srcRect.right   = pSrcRects->rects[i].x2;
        srcRect.bottom  = pSrcRects->rects[i].y2;

        ret = Blt( pImageData, pBitmapInfo, destRect, srcRect );
    }

    m_pSite->_TLSUnlock();

    return ret;
}



STDMETHODIMP CBaseSurface::Blt( UCHAR*               pImageData,
                                HXBitmapInfoHeader* pBitmapInfo,
                                REF(HXxRect)         rDestRect,
                                REF(HXxRect)         rSrcRect)
{
//     static ULONG32 ulThen = 0;
//     if( ulThen == 0 )
//     {
//         ulThen = HX_GET_TICKCOUNT();
//     }
//     ULONG32 ulNow = HX_GET_TICKCOUNT();

//     FILE* pFP=fopen("c:\\greg.txt", "a+");
//     if( pFP )
//     {
//         fprintf( pFP, "%lu\n", ulNow-ulThen);
//         fclose( pFP );
//     }
//     ulThen = ulNow;

    int j=0;
    HX_RESULT res = HXR_OK;
    CHXSimpleList::Iterator gg;
    HXxRect rSrcRectCopy  = rSrcRect;
    HXxRect rDestRectCopy = rDestRect;
    UCHAR*  pHWSurface = NULL;
    LONG32  nSurfPitch = 0;
    HXxSize srcSize;
    BOOL    bLockedSurf = FALSE;
    BOOL    bTransitionInProg = FALSE;

    //
    //quick exits...
    //
    if( m_pSite->m_bSiteNeverBlts )
        return HXR_OK;

    if( NULL==pImageData || NULL==pBitmapInfo )
        return HXR_FAIL;

#ifdef _DEBUG
    // are we testing windowing?
    if (m_pSite->m_bTestWindowing)
    {
        _ColorWindows();
        return HXR_OK;
    }
#endif

    m_pSite->_TLSLock();
    m_pSite->m_bBltHasBeenCalled = TRUE;

    INT32 nCID = GETBITMAPCOLOR(pBitmapInfo);
    INT32 nCIDOld = GETBITMAPCOLOR(&m_bmiLastBlt);

    if( m_pSite->IsCompositionLocked() && !IsYUV(nCID) )
    {
        m_pSite->_TLSUnlock();
        return HXR_OK;
    }

    //If we have changed color formats, save the info.
    BOOL bColorFormatChanged = FALSE;
    if( nCID != nCIDOld )
    {
        if (m_pSite && m_pSite->m_pTopLevelSite)
        {
            // Set the flag saying our color format changed
            bColorFormatChanged = TRUE;
            // Damage the entire rect
            HXxRect cTmp = { m_pSite->m_topleft.x,
                             m_pSite->m_topleft.y,
                             m_pSite->m_topleft.x + m_pSite->m_size.cx,
                             m_pSite->m_topleft.y + m_pSite->m_size.cy };
            m_pSite->m_pTopLevelSite->_RecursiveDamageRect(&cTmp, TRUE);
            // Schedule a RecomputeClip()
            m_pSite->m_pTopLevelSite->ScheduleCallback(CLIP, 0, TRUE);
        }

        //Save what we are actually blt'ing.
        memset( &m_bmiLastImage, 0, sizeof(m_bmiLastImage) );
        //Clean up old frame data.
        HX_FREE(m_pucLastImage);
    }
    memcpy( &m_bmiLastBlt, pBitmapInfo, sizeof(HXBitmapInfoHeader) ); /* Flawfinder: ignore */

    //Check the display Format. If it is less than 8 bits then we have
    //no color format that is going to work. So we'll just Blt Black.
    if (m_pSite)
    {
        UINT16 uBitsPerPixel;
        UINT16 uHorzRes;
        UINT16 uVertRes;

        m_pSite->_GetDeviceCaps(NULL, uBitsPerPixel, uHorzRes, uVertRes);

        if (uBitsPerPixel && uBitsPerPixel < 8)
        {
            HXxWindow* pWindow = m_pSite->GetWindow();
            if (pWindow && pWindow->window)
            {
                _DrawBlack(pWindow->window);
            }
            if (m_pSite->m_pTopLevelSite)
            {
                m_pSite->_TLSUnlock();
            }
            return HXR_OK;
        }
    }

    // Can we restore hw acceleratoin (don't do it during a video surface switch)
    if (m_bLostHWAcceleration)
    {
        CBaseRootSurface* pSurface = m_pSite->GetRootSurface();
        m_bLostHWAcceleration = !pSurface->_IsHardwareAccelerationAvail();

        if (!m_bLostHWAcceleration)
        {
            pSurface->_AcquireHardwareAcceleration();
            ReInitSurfaces();
        }
    }

    //
    //Sharpen Image
    //
    BOOL bDidSharpen = m_pSite->SharpenImage(pBitmapInfo, pImageData, rSrcRectCopy);

    //
    //Do all of the alpha blending....
    //
    BOOL bDoAlpha = _AlphaSetupAndBlending(pBitmapInfo,
                                           pImageData,
                                           rSrcRectCopy,
                                           rDestRectCopy);

    // We must invalidate the rects for any site above us that is
    // alphablending.
    HXxRect   Dirt      = rDestRectCopy;
    HXxPoint* pPosition = m_pSite->GetOrigin();

    Dirt.left   += pPosition->x;
    Dirt.right  += pPosition->x;
    Dirt.top    += pPosition->y;
    Dirt.bottom += pPosition->y;

    //If sites above us are blending against us then we need to dirty
    //their rects otherwise they will not redraw when forcre redraw is
    //called.
    CHXMapPtrToPtr::Iterator it;
    for( it=m_pSite->m_AlphaBlendNotifiers.Begin() ;
         it!=m_pSite->m_AlphaBlendNotifiers.End()  ;
         ++it)
    {
        CHXBaseSite* pSite = (CHXBaseSite*)*it;
        pSite->DamageRectWindowRel(Dirt);
    }

    //Now, if we are over YUV and we are in YUV optimized blending
    //mode then we need to dirty the rect of the people below us.  If
    //we don't, they won't redraw when force redraw is called on them.
    it = m_YUVAImageList.Begin();
    while(it != m_YUVAImageList.End())
    {
        CHXBaseSite* pTmp = (CHXBaseSite*)it.get_key();
        pTmp->DamageRectWindowRel(Dirt);
        ++it;
    }


    //If our video is under a fade transition we need to blt in ARGB
    //not YUV. Drop to it here.
    if( m_pSite->_FadeTransitionActive() || m_pSite->_BlendedBordersActive() )
    {
        if( m_pSite->m_nTransitionState != 1000 && HX_OVERLAY_BLT==m_nBltMode )
        {
            m_nOldBltMode2 = HX_OVERLAY_BLT;
            ForceGDIMode(TRUE);
        }

        if (m_pSite->m_nTransitionState < 1000)
            bTransitionInProg = TRUE;
    }

    if( m_pSite->m_nTransitionState == 1000 && HX_OVERLAY_BLT==m_nOldBltMode2 )
    {
        //We are done, turn overlays back on.
        m_nOldBltMode2 = HX_BASIC_BLT;
        ForceGDIMode(FALSE);

        bTransitionInProg = FALSE;
    }

    //If the blt mode is overlay check to see if we are shrinking.
    int nBltModeTemp = HX_BASIC_BLT;
    m_bDisableFillColorKey = FALSE;
    if (HX_OVERLAY_BLT == m_nBltMode)
    {
        if( (m_scaleFactorX<1.0)||(m_scaleFactorY<1.0) && !_AllowsOverlayShrinking() )
        {
            //XXXgfw we should think about putting some effort into
            //removing all this subrect business for unix. All the
            //clipping is handled by X and we can stay in OVERLAY mode
            //much longer...
            m_nBltMode   = HX_BASIC_BLT;
            nBltModeTemp = HX_OVERLAY_BLT;
            m_bDisableFillColorKey = TRUE;
            m_bOffBecauseofShinking = TRUE;
            m_bYUVBlending=FALSE;
        }
        else
        {
            m_bOffBecauseofShinking = FALSE;
        }
    }

    // Lose overlay if moving
    if (m_pSite->IsMoving() && m_nBltMode == HX_OVERLAY_BLT && !m_bYUVBlending)
    {
        ForceGDIMode(TRUE);
    }
    else if (!m_pSite->IsMoving() &&
             m_nBltMode != HX_OVERLAY_BLT &&
             !m_bOffBecauseofShinking &&
             !bTransitionInProg)
    {
        //restore overlay
        ForceGDIMode(FALSE);
    }


    //
    // Lock the surface....
    //
  startOver:

    CBaseRootSurface* pSurface = NULL;
    if (m_nBltMode == HX_BASIC_BLT)
    {
        pSurface = m_pSite->GetRootSurface();

        //This can only be hit if a parent is destroyed before the child
        //AND the child is still playing. ROB renderer allows this.
        if (!pSurface)
        {
            if (m_pSite && m_pSite->m_pTopLevelSite)
            {
                m_pSite->_TLSUnlock();
            }
            return HXR_OK;
        }
        //  Ok. This call can fail on some video cards. I know, I
        //  know, you are saying but that is INSANE! That is
        //  IMPOSSIBLE. Yup I agree. Looks like a threading issue. If
        //  you turn off IHXInterupt safe then it works
        //  fine. Probably has something to do with more than one
        //  thread talking to a direct draw object. Yeah. Whatever.
        //  Luckly not many DD Driver writers are this crappy.
        //  Anyway, the question is what to do? We could bail, and
        //  return HXR_FAIL. But then we lose frames. We could just
        //  spin, but that consumes CPU. Or we could sleep, but of
        //  course, sleeping 0 on crappy win9x does NOTHING, so we
        //  have to wait for some amount of time.  The problem is that
        //  this means that once in a while a BLT call can take a
        //  WHOLE BUNCH of time. Which is really bad. What to do,
        //  what to do...  Well I am going to call sleep. If I am
        //  overruled, so be it.
        int count = 0;

        while( HXR_OK != pSurface->Lock(0))
        {
#if defined(_MACINTOSH) || defined(_MAC_UNIX)
            if (m_pSite && m_pSite->m_pTopLevelSite)
            {
                m_pSite->_TLSUnlock();
            }
            return HXR_OK;
            // if it fails to lock it's prob'ly at interrupt time, and
            // we can wait until the cows come home but it ain't gonna
            // magically fix itself!  XXXbobclark
#endif
            // XXXbobclark. on the Mac, microsleep() uses the Delay OS
            // call. Hmmm, I wonder if that is entirely interrupt-safe
            // and this just happens not to crash 'cause Lock() always
            // happens to return HXR_OK. It will bear looking into.
            // (And I know that a preposition is a bad thing to end
            // a sentence with.)
            microsleep( 10 );
            count++;
            if (count>5)
            {
                if (m_pSite && m_pSite->m_pTopLevelSite && m_pSite->m_pTopLevelSite->m_pMutex)
                {
                    m_pSite->_TLSUnlock();
                }
                return HXR_OK;
            }
        }
    }


    //
    // Setup new src and dest rects....
    //
    int      nNumRects   = 0;
    _ConstructRects( rSrcRectCopy,
                     rDestRectCopy,
                     bDoAlpha,
                     nNumRects,
                     &m_paSrcRects,
                     &m_paDestRects
                     );


    //
    // Do the actual BLTing.
    //
    //Get the hardware/system memory pointer to the overlay surface....
    srcSize.cx = rSrcRectCopy.right-rSrcRectCopy.left;
    srcSize.cy = rSrcRectCopy.bottom-rSrcRectCopy.top;

    if (m_pLinkedOverlay)
    {
        m_nBltMode = HX_OVERLAY_BLT;
        // and this is where it gets a little .. uhm ... nuts.
        m_nSurfaceCID = m_pLinkedOverlay->m_nSurfaceCID;
        m_nDDSurfaceSize = m_pLinkedOverlay->m_nDDSurfaceSize;
        memcpy(&m_surfaceSize, &m_pLinkedOverlay->m_surfaceSize, sizeof(m_surfaceSize)); /* Flawfinder: ignore */
    }

    if( m_nBltMode == HX_OVERLAY_BLT || m_nBltMode==HX_BLT_YUV_STRETCH)
    {
        if (m_pLinkedOverlay)
        {
            res = m_pLinkedOverlay->_LockInternalSurface( &pHWSurface, &nSurfPitch, srcSize );
        }
        else
        {
            res = _LockInternalSurface( &pHWSurface, &nSurfPitch, srcSize );
        }
        if( HXR_OK != res )
        {
            // If we lost hw acceleration, switch to gdi
            pSurface = m_pSite->GetRootSurface();
            m_bLostHWAcceleration = !pSurface->_IsHardwareAccelerationAvail();

            if (m_bLostHWAcceleration)
            {
                DestroySurfaces();
                pSurface->_LoseHardwareAcceleration();
            }

            m_nBltMode = HX_BASIC_BLT;
            goto startOver;
        }
        bLockedSurf = TRUE;
    }

    if( NULL==m_paSrcRects || NULL==m_paDestRects || bColorFormatChanged)
    {
        goto cleanup;
    }


    for( j=0 ; j<nNumRects ; j++ )
    {
        switch (m_nBltMode)
        {
           case HX_BASIC_BLT:
           {
               if (!pSurface)
               {
                   pSurface = m_pSite->GetRootSurface();
                   HX_VERIFY(HXR_OK == pSurface->Lock(0));
               }
               pSurface->_PreFlightBlt(m_paDestRects[j]);
               if( !bDoAlpha )
               {
                   pSurface->Blt(pImageData,
                                 pBitmapInfo,
                                 m_paDestRects[j],
                                 m_paSrcRects[j],
                                 m_pSite);
               }
               else
               {
                   pSurface->Blt( m_pucLastImage,
                                  (HXBitmapInfoHeader*)&m_bmiLastImage,
                                  m_paDestRects[j],
                                  m_paSrcRects[j],
                                  m_pSite);
               }
           }
           break;
           case HX_OVERLAY_BLT:
           {
               HXxRect  src  = m_paSrcRects[j];
               HXxRect  dest = m_paSrcRects[j];
               res = ByPassCompositionSurface( pImageData,
                                               pBitmapInfo,
                                               dest,
                                               src,
                                               pHWSurface,
                                               nSurfPitch);

               if(HXR_OK!=res)
               {
                   ForceGDIMode(TRUE);
               }

               if (m_nBltMode != HX_OVERLAY_BLT)
               {
                   _UnlockInternalSurface(pHWSurface);
                   bLockedSurf = FALSE;
                   goto startOver;
               }
           }
           break;
           case HX_BLT_YUV_STRETCH:
           {
               //Check to see if we are in SMIL if we are then we MUST go
               //to Basic Mode Not a nice method to be sure.
               CHXBaseSite* pSite = m_pSite->GetParentSite();

               if (pSite && pSite->GetParentSite())
               {
                   m_nBltMode = HX_BASIC_BLT;
                   goto startOver;
               }

               HX_RESULT p1 = ByPassCompositionSurface( pImageData,
                                                        pBitmapInfo,
                                                        rSrcRectCopy,
                                                        rSrcRectCopy,
                                                        pHWSurface,
                                                        nSurfPitch);
               _UnlockInternalSurface(pHWSurface);
               HX_RESULT p2 = _BltToPrimary(m_paDestRects[j], m_paSrcRects[j]);
               res = _LockInternalSurface( &pHWSurface, &nSurfPitch, srcSize );
               if( HXR_OK != res || p1 != HXR_OK || p2 != HXR_OK )
               {
                   ForceGDIMode(TRUE);
               }

               if (m_nBltMode != HX_BLT_YUV_STRETCH)
               {
                   _UnlockInternalSurface(pHWSurface);
                   bLockedSurf = FALSE;
                   goto startOver;
               }
           }
        }
    }

  cleanup:

    //
    //Do we have any optimized alphablend regions?
    //
    if( m_nBltMode == HX_OVERLAY_BLT || m_pLinkedOverlay)
    {
        gg = m_imageBlocks.Begin();
        while(gg != m_imageBlocks.End())
        {
            ImageBlock* pBlock    = (ImageBlock*)*gg;
            Image*      pTmp      = pBlock->pImage;
            HXxRect     zeroRect;

            zeroRect.left   = 0;
            zeroRect.top    = 0;
            zeroRect.right  = pBlock->rect.right-pBlock->rect.left;
            zeroRect.bottom = pBlock->rect.bottom-pBlock->rect.top;

            //Draw this block....
            res = ByPassCompositionSurface( pTmp->pucImage,
                                            &pTmp->bmiImage,
                                            pBlock->rect,
                                            zeroRect,
                                            pHWSurface,
                                            nSurfPitch
                                            );
            if(HXR_OK!=res)
            {
                ForceGDIMode(TRUE);
            }

            if (m_nBltMode != HX_OVERLAY_BLT)
            {
                _UnlockInternalSurface(pHWSurface);
                bLockedSurf = FALSE;
                goto startOver;
            }

            ++gg;
        }
    }

    // hmmmm ... I wonder if I should do this...
    if (m_pLinkedOverlay)
    {
        m_nBltMode = HX_BASIC_BLT;
    }


    if( bLockedSurf )
    {
        if (m_pLinkedOverlay)
        {
            m_pLinkedOverlay->_UnlockInternalSurface(pHWSurface);
        }
        else
        {
            _UnlockInternalSurface(pHWSurface);
        }
        bLockedSurf = FALSE;

        // Map UpdateOverlay coords to that of UpdateDestRect
        HXxPoint* off = m_pSite->GetOrigin();
        if( off->x < 0 )
        {
            rSrcRectCopy.left   -= off->x;
            rSrcRectCopy.right  += off->x;
            rDestRectCopy.left  -= off->x;
            rDestRectCopy.right += off->x;
        }
        if( off->y < 0)
        {
            rSrcRectCopy.top    -= off->y;
            rSrcRectCopy.bottom += off->y;
            rDestRectCopy.top   -= off->y;
            rDestRectCopy.top   += off->y;
        }

        UpdateOverlay( rDestRectCopy, rSrcRectCopy );
    }
    if( m_bNeedColorKeyFilled && (m_nBltMode==HX_OVERLAY_BLT || m_pLinkedOverlay) )
    {
        _FillColorKey();
    }


    HXxWindow* pWindow = NULL;
    pWindow = m_pSite->GetWindow();

    if (pSurface)
    {
        pSurface->Unlock(0, pWindow, m_pSite);
    }

    //We are done blt'ing all our rects. Lets notify everyone that
    //might be interested in alpha blending that we changed.
    int  nTot = m_pSite->m_AlphaBlendNotifiers.GetCount();
    if( nTot>0 &&
        !m_pSite->IsCompositionMode() &&
        (!IsYUV(nCID)||m_bOffBecauseofShinking)
        )
    {
        //XXXgfw Once we have optimized YUV blenders, do even bother
        //calling this as YUV blends from the bottom up and all this
        //has already been done.
        if( m_pSite && m_pSite->m_pTopLevelSite )
            m_pSite->m_pTopLevelSite->ScheduleCallback(REDRAW_ALL, 0);
    }

    //If we are an animated GIF blending ontop of a video,then we
    //might still be animating while the video is paused. Since, in
    //the case of YUV optimized blending, it is the YUV's
    //responsibility to do the blending, and it isn't BLT'ing anymore,
    //we won't see anything. So, if the YUV hasn't done an alpha blend
    //for X number of miliseconds, and we overlap, force a redraw.
    UINT32 now = HX_GET_BETTERTICKCOUNT();
    CHXMapPtrToPtr::Iterator i = m_YUVAImageList.Begin();
    while(i != m_YUVAImageList.End())
    {
        CHXBaseSite* pTmp = (CHXBaseSite*)i.get_key();
        if( now-pTmp->m_pVideoSurface->m_ulLastBlendTime > 50 &&
            IsYUV(GETBITMAPCOLOR(&(pTmp->m_pVideoSurface->m_bmiLastBlt)))
            )
        {
            pTmp->ForceRedraw();
        }

        ++i;
    }



    if (nBltModeTemp == HX_OVERLAY_BLT)
    {
        m_nBltMode = HX_OVERLAY_BLT;
    }

    if (bDidSharpen)
    {
        //change one corner pixel to put a digital watermark
        INT32     ulYOffset     = (rSrcRectCopy.top * pBitmapInfo->biWidth)
            + rSrcRectCopy.left;
        *(pImageData+ulYOffset)=1;
    }

    if (m_pSite && m_pSite->m_pTopLevelSite)
    {
        m_pSite->_TLSUnlock();
    }

    m_pSite->m_UpdateBltStatsRect = rDestRect;

    return HXR_OK;
}


void CBaseSurface::DrawTransitionBorders( HXBitmapInfoHeader* pBitmapInfo,
                                          INT32                width,
                                          ULONG32              color,
                                          BOOL                 bBlendedBorders
                                          )
{
    if (width && m_pucLastImage)
    {
        width *= 2;

        int pitchIn = GETBITMAPPITCH(pBitmapInfo);

        LineSegment ls;
        double slope;
        HXxPoint* pOrigin = m_pSite->GetOrigin();

        for (int i = 0; i < m_pSite->m_TransitionBorderLines.m_nLines; ++i)
        {
            // We have to use a temp variable here because we never
            // know if we're gonna have to blt before we reset the
            // member variable.
            ls = m_pSite->m_TransitionBorderLines.m_pLines[i];

            ls.start.x  = ls.start.x-pOrigin->x;
            ls.start.y  = ls.start.y-pOrigin->y;
            ls.finish.x = ls.finish.x-pOrigin->x;
            ls.finish.y = ls.finish.y-pOrigin->y;


            if (pitchIn < 0)
            {
                ls.start.y = pBitmapInfo->biHeight - 1 - ls.start.y;
                ls.finish.y = pBitmapInfo->biHeight - 1 - ls.finish.y;
            }


            HXxPoint  start = ls.start;
            HXxPoint  finish = ls.finish;

            if (ls.finish.x - ls.start.x == 0) // dividing by 0 is bad
                slope = 2.0; // all we really care about is that this is > 1
            else
                slope = fabs(double(ls.finish.y - ls.start.y) / double(ls.finish.x - ls.start.x));

            for (int j = 0; j < width; ++j)
            {
                if( bBlendedBorders )
                {
                    //We need to march the alpha from 0-->100 over the width
                    //of the border.
                    int alpha = 255-(j*256/width);
                    color = alpha << 24;
                }

                if(j)
                {
                    ls = m_pSite->m_TransitionBorderLines.m_pLines[i];

                    ls.start.x = ls.start.x-pOrigin->x;
                    ls.start.y = ls.start.y-pOrigin->y;
                    ls.finish.x = ls.finish.x-pOrigin->x;
                    ls.finish.y = ls.finish.y-pOrigin->y;

                    if (pitchIn < 0)
                    {
                        ls.start.y = pBitmapInfo->biHeight - 1 - ls.start.y;
                        ls.finish.y = pBitmapInfo->biHeight - 1 - ls.finish.y;
                    }

                    start = ls.start;
                    finish = ls.finish;

                    int d = (j + 1) / 2;
                    if (slope > 1)
                    {
                        if (j & 1)
                        {
                            ls.start.x = start.x + d;
                            ls.finish.x = finish.x + d;
                        }
                        else
                        {
                            ls.start.x = start.x - d;
                            ls.finish.x = finish.x - d;
                        }
                    }
                    else if (slope < 1)
                    {
                        if (j & 1)
                        {
                            ls.start.y = start.y + d;
                            ls.finish.y = finish.y + d;
                        }
                        else
                        {
                            ls.start.y = start.y - d;
                            ls.finish.y = finish.y - d;
                        }
                    }
                    else    // slope == 1
                    {
                        int dy;
                        int dx = (j + 2) / 4;

                        if (ls.start.y > ls.finish.y)
                        {
                            if (j & 1)
                            {
                                ls.start.x -= dx;
                                ls.finish.x -= dx;
                                dy = (j + 3) / 4;
                                ls.start.y -= dy;
                                ls.finish.y -= dy;
                            }
                            else
                            {
                                ls.start.x += dx;
                                ls.finish.x += dx;
                                dy = j / 4;
                                ls.start.y += dy;
                                ls.finish.y += dy;
                            }
                        }
                        else
                        {
                            if (j & 1)
                            {
                                ls.start.x -= dx;
                                ls.finish.x -= dx;
                                dy = (j + 3) / 4;
                                ls.start.y += dy;
                                ls.finish.y += dy;
                            }
                            else
                            {
                                ls.start.x += dx;
                                ls.finish.x += dx;
                                dy = j / 4;
                                ls.start.y -= dy;
                                ls.finish.y -= dy;
                            }
                        }
                    }
                }
                if (!ls.Clip(0,0,pBitmapInfo->biWidth - 1,pBitmapInfo->biHeight - 1))
                    continue;

                DrawLine((int*)m_pucLastImage,pBitmapInfo->biWidth,
                         ls.start.x,
                         ls.start.y,
                         ls.finish.x,
                         ls.finish.y,
                         color);
            }
        }
    }
}

void CBaseSurface::SetColorspacePriorities(int* pList, int nEntries, int cid)
{
    m_pyuvInputMngr->SetOutputPriority(cid, pList, nEntries);
}
void CBaseSurface::RestoreColorspacePriorities(int cid)
{
    m_pyuvInputMngr->SetDefaultOutputPriority(cid);
}

void CBaseSurface::LinkOverlay(CHXBaseSite* pSite)
{
    if (m_bAllowOverlayLinking)
    {
        m_pLinkedOverlay = pSite->m_pVideoSurface;
    }
}

void CBaseSurface::AddLinkedSite(CHXBaseSite* pSite)
{
    if (m_bAllowOverlayLinking)
    {
        if (!m_LinkedSites.Find(pSite))
        {
            m_LinkedSites.AddTail(pSite);
        }
    }
}

HX_RESULT CBaseSurface::RelinquishOverlay()
{
    if (m_LinkedSites.GetCount() || m_pLinkedOverlay)
    {
        return HXR_FAIL;
    }

    if (UsingOverlay())
    {
        _ReleaseSurface();

        ForceGDIMode(TRUE);
        m_nOldBltMode = HX_BASIC_BLT;

        return HXR_OK;
    }
    return HXR_FAIL;
}

HX_RESULT CBaseSurface::AcquireOverlay()
{
    HX_RESULT res = HXR_FAIL;

    if (m_LinkedSites.GetCount() || m_pLinkedOverlay)
    {
        return HXR_FAIL;
    }

    HXBitmapInfoHeader m_OptimizedFormat;
    memcpy(&m_OptimizedFormat, m_pOptimizedFormat, sizeof(HXBitmapInfoHeader)); /* Flawfinder: ignore */

    DestroySurfaces();

    HX_DELETE(m_pOptimizedFormat);
    res = BeginOptimizedBlt(&m_OptimizedFormat);

    return res;
}

BOOL CBaseSurface::_IsDisplaySurfaceYuv()
{
    return (m_nBltMode==HX_OVERLAY_BLT&&!m_bOffBecauseofShinking) || m_pLinkedOverlay;
}

void CBaseSurface::_ColorWindows()
{
}

void CBaseSurface::TryCreateOffScreenBuffer()
{
    //Do we have an existing surface?
    if (m_surfaceSize.cx || m_surfaceSize.cy)
    {
        return;
    }

    //Get our windraw object from the root surface

    // XXXAH Weird. Looks like I create a normal RGB surface here. I
    // should be creating a YUV surface.
    //XXXgfw what does this do?????
#if !defined(_UNIX) || defined(_MAC_UNIX)
    _CreateBuffer();
#endif
}

void CBaseSurface::TryCreateOverlay(BOOL bOverlay)
{
    //Do we have an existing overlay?
    if (m_surfaceSize.cx || m_surfaceSize.cy)
    {
        return;
    }

    //Get our windraw object from the root surface
    UINT32 fSurfaceCaps = 0;
    if (HXR_OK != _GetCaps(&fSurfaceCaps))
    {
        return;
    }


    if(((fSurfaceCaps & HX_OVERLAY) && bOverlay) ||
       ((fSurfaceCaps & HX_BLTFOURCC) && !bOverlay))
    {
        //Now attempt to create YUV overlays in the most benificial
        //formats possible
        if (m_pyuvInputMngr->IsFormatSupported(m_nSrcCID))
        {
            for (int i=0; m_pyuvInputMngr->GetOutputFormat(m_nSrcCID, i) >= 0; i++)
            {
                HX_RESULT result = _CreateOverlay( bOverlay,
                                                   m_pyuvInputMngr->GetOutputFormat(m_nSrcCID, i),
                                                   m_pOptimizedFormat->biWidth,
                                                   m_pOptimizedFormat->biHeight);

                if (HXR_OK == result)
                {
                    if (m_pOverlayManager)
                    {
                        m_pOverlayManager->HasOverlay((IHXOverlayResponse*)this);
                    }
                    m_nOverlayFailureCount = 0;
                    return;
                }
            }
        }

    }
}

HX_RESULT CBaseSurface::ByPassCompositionSurface(UCHAR*               pImageData,
                                                 HXBitmapInfoHeader* pBitmapInfo,
                                                 REF(HXxRect)         rDestRect,
                                                 REF(HXxRect)         rSrcRect,
                                                 UCHAR*               pSurface,
                                                 INT32                nSurfPitch

                                                 )
{
    HX_RESULT retVal = HXR_FAIL;
    HXBitmapInfoHeader bmiFrame;
    UCHAR* pucBuff      = NULL;
    UINT32 nFrameWidth  = 0;
    UINT32 nFrameHeight = 0;
    UINT32 nFramePitch  = 0;
    ULONG32 nCIDIn      = m_nSrcCID;

    int pitchIn = GETBITMAPPITCH(pBitmapInfo);

    // What to do here?  If we fail, we drop to RGB which is bad...
    if (!pBitmapInfo->biWidth ||
        !pBitmapInfo->biHeight)
    {
        return HXR_OK;
    }

    m_pSite->CheckColorSettings();

#ifdef _WINDOWS
    zm_pColorAcc->SetPlayer(m_pSite->m_pRootSurface->GetPlayer());
#endif


    BOOL bConverter = zm_pColorAcc->CheckColorConverter(nCIDIn, m_nSurfaceCID);
    m_pSite->ColorConverterRequest(nCIDIn, m_nSurfaceCID, bConverter);

    //If we don't have a color converter then we need to do a 2 step process.
    //we will do cIdIn-->I420-->cIdOut.
    if( !bConverter )
    {
        // If we don't have a converter to match the source, do an AU
        // otherwise, continue with the 2 step.
        bConverter = zm_pColorAcc->CheckColorConverter(nCIDIn, nCIDIn);
        m_pSite->ColorConverterRequest(nCIDIn, m_nSurfaceCID, bConverter);

        if (!bConverter)
        {
            m_pSite->ColorConverterRequest(nCIDIn, m_nSurfaceCID, bConverter);
            return HXR_FAILED;
        }

        int nResult     = 0;
        int nFramePitch = 0;

#ifdef _WIN32
        nResult = MakeBitmap( (LPBITMAPINFO)&bmiFrame,
                              sizeof(bmiFrame),
                              CID_I420,
                              rDestRect.right - rDestRect.left,
                              rDestRect.bottom - rDestRect.top,
                              NULL,
                              0);
#else
        nResult = MakeBitmap( (HXBitmapInfo*)&bmiFrame,
                              sizeof(bmiFrame),
                              CID_I420,
                              rDestRect.right - rDestRect.left,
                              rDestRect.bottom - rDestRect.top,
                              NULL,
                              0);
#endif

        HX_ASSERT( nResult != 0 );

        pucBuff      = (UCHAR*)new char[bmiFrame.biSizeImage*sizeof(UCHAR)];
        nFramePitch  = GETBITMAPPITCH( &bmiFrame );
        nFrameWidth  = bmiFrame.biWidth;
        nFrameHeight = bmiFrame.biHeight;

        HXxSize size   = { nFrameWidth, nFrameHeight };
        HXxRect destRc = { rDestRect.left,
                           rDestRect.top,
                           rDestRect.right-rDestRect.left,
                           rDestRect.bottom-rDestRect.top };

        bConverter = zm_pColorAcc->CheckColorConverter(nCIDIn, CID_I420);
        m_pSite->ColorConverterRequest(nCIDIn, CID_I420, bConverter);

        if( !bConverter )
        {
            //Can't do 2 step either.
            HX_VECTOR_DELETE( pucBuff );
            return HXR_FAIL;
        }

        //Colorconvert into new bitmap.
        retVal = zm_pColorAcc->ColorConvert(
            CID_I420,
            pucBuff,
            nFrameWidth, nFrameHeight, nFramePitch,
            destRc.left, destRc.top,
            HXxRECT_WIDTH(destRc), HXxRECT_HEIGHT(destRc),
            nCIDIn,
            pImageData,
            pBitmapInfo->biWidth,
            pBitmapInfo->biHeight, pitchIn,
            rSrcRect.left, rSrcRect.top,
            HXxRECT_WIDTH(rSrcRect), HXxRECT_HEIGHT(rSrcRect)
            );

        if( retVal != HXR_OK)
        {
            HX_VECTOR_DELETE( pucBuff );
            return HXR_FAIL;
        }

        //change pointers for 2nd stage below.....
        bConverter = zm_pColorAcc->CheckColorConverter(CID_I420, m_nSurfaceCID);
        m_pSite->ColorConverterRequest(CID_I420, m_nSurfaceCID, bConverter);

        nCIDIn       = CID_I420;
        pImageData   = pucBuff;
        pitchIn      = nFramePitch;
        pBitmapInfo  = &bmiFrame;

    }

    if (bConverter)
    {

        //Well krap(tm). We just can't win with odd number of lines
        //and YUV formats. This sucks. I guess just fake it.
        AlignRect(&rSrcRect, pBitmapInfo->biWidth, pBitmapInfo->biHeight);
        AlignRect(&rDestRect, m_surfaceSize.cx, m_surfaceSize.cy);
//            {
//                char szBuff[256];
//                sprintf( szBuff,"src (%d, %d)-(%d, %d)  dest(%d, %d)-(%d, %d)\n",
//                         rSrcRect.left,
//                         rSrcRect.top,
//                         rSrcRect.right,
//                         rSrcRect.bottom,
//                         rDestRect.left,
//                         rDestRect.top,
//                         rDestRect.right,
//                         rDestRect.bottom
//                         );
//                _DumpString(szBuff);
//            }

        int dxDst = rDestRect.right  - rDestRect.left;
        int dyDst = rDestRect.bottom - rDestRect.top;
        int dxSrc = rSrcRect.right   - rSrcRect.left;
        int dySrc = rSrcRect.bottom  - rSrcRect.top;

        HX_RESULT res = zm_pColorAcc->ColorConvert(
            m_nSurfaceCID,
            pSurface,
            m_surfaceSize.cx, m_surfaceSize.cy, nSurfPitch,
            rDestRect.left, rDestRect.top, dxDst, dyDst,
            nCIDIn,
            pImageData,
            pBitmapInfo->biWidth,
            pBitmapInfo->biHeight, pitchIn,
            rSrcRect.left, rSrcRect.top, dxSrc, dySrc
            );

        retVal = HXR_OK;
    }

#if !defined(_GOLD) && 0
    m_TimeStamps.AddSample();
#endif

    //Clean up the 2-stage color bits.
    HX_VECTOR_DELETE(pucBuff);

    return retVal;
}

void CBaseSurface::AlignRect(HXxRect *pRect, int nMaxWidth, int nMaxHeight)
{
    if( pRect->top&1 )
    {
        pRect->top--;
    }
    if( (pRect->bottom-pRect->top)&1 )
    {
        pRect->bottom++;
    }

    if( pRect->left&1 )
    {
        pRect->left--;
    }

    if( (pRect->right-pRect->left)&1 )
    {
        pRect->right++;
    }

    if( pRect->right>nMaxWidth )
        pRect->right = nMaxWidth;

    if( pRect->bottom>nMaxHeight )
        pRect->bottom = nMaxHeight;
}

void CBaseSurface::FillColorKey(HXxDC hdc)
{
    if(m_bDisableFillColorKey || !m_pSite->m_Region )
    {
        return;
    }

    // XXXAH No HXxBrush or HXxPen... use void*
    void* hOldBrush = NULL;
    void* hOldPen   = NULL;
    _SetupDCObjects(hdc, &hOldBrush, &hOldPen);


    //Add all of our YUV optimized regions into this....
    HXREGION* pTmp = HXCreateRegion();

    HXUnionRegion( pTmp, m_pSite->m_Region, pTmp );
    CHXMapPtrToPtr::Iterator ii;
    for( ii=m_pSite->m_AlphaBlendNotifiers.Begin() ;
         ii!=m_pSite->m_AlphaBlendNotifiers.End()  ;
         ++ii)
    {
        CHXBaseSite* pSite = (CHXBaseSite*)*ii;
        HXREGION* pReg = NULL;
        //Find the region we need to blend with.
        CHXMapPtrToPtr::Iterator j = pSite->m_AlphaBlendSites.Begin();
        CHXBaseSite* pTmpSite = NULL;
        while( j != pSite->m_AlphaBlendSites.End() && pTmpSite != m_pSite )
        {
            pTmpSite = (CHXBaseSite*)j.get_key();
            if( pTmpSite == m_pSite )
            {
                pReg = (HXREGION*)*j;
                HXUnionRegion( pTmp, pReg, pTmp );
                break;
            }
            ++j;
        }
    }

    // Do we have linked overlays?
    if (m_LinkedSites.GetCount())
    {
        CHXSimpleList::Iterator i = m_LinkedSites.Begin();
        CHXBaseSite* pSite = NULL;
        for(; i!= m_LinkedSites.End(); ++i)
        {
            pSite = (CHXBaseSite*) *i;
            HXUnionRegion( pTmp , pSite->m_Region, pTmp );

            if (pSite->m_pVideoSurface->m_pAdditionalColorKey)
            {
                HXUnionRegion( pTmp , pSite->m_pVideoSurface->m_pAdditionalColorKey, pTmp );
            }
        }
    }

    HXxPoint offset;
    offset = m_pSite->GetScreenOffset();
    for( int i=0 ; i<pTmp->numRects ; i++)
    {
        HXxRect tempRect;
        tempRect.left     = pTmp->rects[i].x1;
        tempRect.top      = pTmp->rects[i].y1;
        tempRect.right    = pTmp->rects[i].x2;
        tempRect.bottom   = pTmp->rects[i].y2;
        _FillRectangle( hdc,
                        tempRect.left   + offset.x,
                        tempRect.top    + offset.y,
                        tempRect.right  + offset.x,
                        tempRect.bottom + offset.y);
    }

    HXDestroyRegion( pTmp );
    _RestoreDCObjects(hdc, hOldBrush, hOldPen);
}

void CBaseSurface::FillColorKey()
{
    m_bNeedColorKeyFilled = TRUE;
}

void CBaseSurface::_FillColorKey()
{
    if (m_pLinkedOverlay)
    {
        m_pLinkedOverlay->_FillColorKey();
    }

    if ((!m_surfaceSize.cy && !m_surfaceSize.cx) || m_nBltMode != HX_OVERLAY_BLT  )
    {
        return;
    }

    CHXBaseSite* pSite = m_pSite->GetTopLevelSite();
    if (pSite && pSite->m_bDisableForceRedraw && !pSite->m_bCalledComputeClipFromTransition)
    {
        return;
    }

    // If our window is completely obscurred, we can not paint the colorkey
    if (!m_pSite->_IsWindowVisible())
        return;

    m_bNeedColorKeyFilled = FALSE;
    //XXXgfw, this is not needed on unix because our overlay support
    //automatically fills the window with the color key....

    CBaseRootSurface* pSurface = m_pSite->GetRootSurface();

    pSurface->LockCompMutex();
    if (pSurface->IsCompositionEnabled())
    {
        //If composition is enabled we must also blt to that.
        HXxDC hdc;
        _GetCompositionSurfaceHXxDC(&hdc);
        FillColorKey(hdc);
        _ReleaseCompositionSurfaceHXxDC(hdc);
    }
    pSurface->UnlockCompMutex();

    //Use GDI to fill in this rect.
    HXxWindow* pWindow = m_pSite->GetWindow();
    if (pWindow && pWindow->window)
    {
        HXxDC hdc = _GetDC(pWindow);
        FillColorKey(hdc);
        _ReleaseDC(pWindow, hdc);
    }

}

INT32 CBaseSurface::GetOverlayColor()
{
    UINT16 uBitsPerPixel;
    UINT16 uHorzRes;
    UINT16 uVertRes;

    m_pSite->_GetDeviceCaps(NULL, uBitsPerPixel, uHorzRes, uVertRes);

    // XXXAH I should really changes these colors.
    switch (uBitsPerPixel)
    {
       case 8:
#ifdef _DEBUG
           return 0x00FFFF00;
#else
           return 0x00FFFF00;
#endif
       case 15:
       case 16:
#ifdef _DEBUG
           return 0x001010F0;
#else
           return 0x00001010;
#endif
       case 24:
#ifdef _DEBUG
           return 0x0010F010;
#else
           return 0x00100701;
#endif
       case 32:
#ifdef _DEBUG
           return 0x00F01010;
#else
           return 0x00100701;
#endif
       default:
           HX_ASSERT(0);
           return 0x00FEFEFE;
    }
}

void CBaseSurface::ResetUpdateOverlay()
{
    m_nUpdateOverlayByPassCount = OVERLAY_BYPASS_THRESHOLD+1;
}

HX_RESULT CBaseSurface::UpdateOverlay(  REF(HXxRect) /*IN*/  rDestRect,
                                        REF(HXxRect) /*IN*/  rSrcRect,
                                        int x, int y)
{
//      {
//          char szBuff[256];
//          sprintf( szBuff,"UpdateOverlay src (%d, %d)-(%d, %d)  dest(%d, %d)-(%d, %d)\n",
//                   rSrcRect.left,
//                   rSrcRect.top,
//                   rSrcRect.right,
//                   rSrcRect.bottom,
//                   rDestRect.left,
//                   rDestRect.top,
//                   rDestRect.right,
//                   rDestRect.bottom
//                   );
//          _DumpString(szBuff);
//      }

    HX_RESULT retVal = HXR_FAIL;

    if (m_pLinkedOverlay)
    {
        m_pLinkedOverlay->UpdateOverlay(rDestRect, rSrcRect, x, y);
    }

    if (m_nBltMode != HX_OVERLAY_BLT)
    {
        return retVal;
    }

// #ifdef _CHECK_PERFORMANCE
//    static LARGE_INTEGER PerfCounter = {0,0};
//    static LARGE_INTEGER PerfFreq = {0,0};

//    static int NumOfUpdateOverlays = 0;
//    QueryPerformanceFrequency(&PerfFreq);

//    double frequency = ((double)PerfFreq.LowPart +
//                        4294967296.0*PerfFreq.HighPart);

//    QueryPerformanceCounter(&PerfCounter);
//    double startTime = ((double)PerfCounter.LowPart +
//                        4294967296.0*PerfCounter.HighPart)/frequency;
// #endif

    HXxRect destRect;
    memset( &destRect, 0, sizeof(HXxRect) );

    if (x || y)
    {
        destRect.right  = x + rDestRect.right;
        destRect.bottom = y + rDestRect.bottom;
        destRect.left   = x + rDestRect.left;
        destRect.top    = y + rDestRect.top;
    }
    else
    {
        //Convert the dest rect to device coordinates...
        HXxRect xyRect;
        _GetWindowDeviceCords(&xyRect);
        destRect. right  = xyRect.left + rDestRect.right;
        destRect. left   = xyRect.left + rDestRect.left;
        destRect. top    = xyRect.top  + rDestRect.top;
        destRect. bottom = xyRect.top  + rDestRect.bottom;
    }

    HXxRect srcRect;
    if (rSrcRect.bottom || rSrcRect.right)
    {
        memcpy(&srcRect, &rSrcRect, sizeof(HXxRect)); /* Flawfinder: ignore */
    }
    else
    {
        srcRect.left    = 0;
        srcRect.top     = 0;
        srcRect.right   = m_surfaceSize.cx;
        srcRect.bottom  = m_surfaceSize.cy;
    }
    //Check to see if we are attemping to place the overlay off the end
    //of the screen. They do not like that.  if so we will have to clip
    //the size of the overlay, and remember this for the next time we
    //blt.
    UINT16 uBitsPerPixel;
    UINT16 uHorzRes;
    UINT16 uVertRes;

    m_pSite->_GetDeviceCaps(NULL, uBitsPerPixel, uHorzRes, uVertRes);

    if (uBitsPerPixel != m_oldOverlayColorDepth)
    {
        m_oldOverlayColorDepth = uBitsPerPixel;
        // XXXAH Probably do not need this, but keeping it to make sure.
        m_convertedOverlayColor = _InsureColorMatch(GetOverlayColor());

    }

    //Adjust for full screen playback.
    if( !m_bVideoSurface2 )
    {
        HXxPoint screenOffset = m_pSite->GetScreenOffset();
        destRect.left  += screenOffset.x;
        destRect.top   += screenOffset.y;
        destRect.right += screenOffset.x;
        destRect.bottom+= screenOffset.y;
    }

//XXXgfw We don't need this check on UNIX...get rid of it greg...
#if !defined(_UNIX) || defined(_MAC_UNIX)

    double ratio;
    if (destRect.left < 0)
    {
        ratio = ((double)abs(destRect.left)) / (double)(destRect.right - destRect.left);
        destRect.left = 0;
        srcRect.left = (int) (((double) m_surfaceSize.cx) * ratio);
    }

    if (destRect.right> uHorzRes)
    {
        ratio = ((double)(destRect.right - uHorzRes)) / (double)(destRect.right - destRect.left);
        destRect.right = uHorzRes;
        srcRect.right = (int) (((double) m_surfaceSize.cx) * (1.0 - ratio));
    }

    if (destRect.top < 0)
    {
        ratio = ((double)abs(destRect.top)) / (double)(destRect.bottom - destRect.top);
        destRect.top = 0;
        srcRect.top = (int) (((double) m_surfaceSize.cy) * ratio);
    }

    if (destRect.bottom > uVertRes)
    {
        ratio = ((double)(destRect.bottom - uVertRes)) / (double)(destRect.bottom - destRect.top);
        destRect.bottom = uVertRes;
        srcRect.bottom = (int) (((double) m_surfaceSize.cy) * (1.0 - ratio));
    }

#endif


    _SetColorKey(m_convertedOverlayColor, m_convertedOverlayColor);
    INT32 flags = HX_OVER_KEYDEST;

    if ( (destRect.right - destRect.left) < 0 || (destRect.bottom - destRect.top) < 0 )
    {
        flags |= HX_OVER_HIDE;

        //Set the src and the dest to dummy values.
        memset(&destRect, 0, sizeof(HXxRect));
        destRect.right  = m_surfaceSize.cx;
        destRect.bottom = m_surfaceSize.cy;
        memcpy(&srcRect, &destRect, sizeof(HXxRect)); /* Flawfinder: ignore */
        _UpdateOverlay(&destRect, &srcRect, flags);

#ifdef _CHECK_PERFORMANCE
        NumOfUpdateOverlays++;
#endif
        return HXR_OK;
    }
    else
    {
        flags |= HX_OVER_SHOW;
#ifdef _WINDOWS
        flags |= DDOVER_SHOW;
#endif
        if (m_lastUpdateDestRect.left   != destRect.left   ||
            m_lastUpdateDestRect.right  != destRect.right  ||
            m_lastUpdateDestRect.top    != destRect.top    ||
            m_lastUpdateDestRect.bottom != destRect.bottom ||
            m_lastUpdateSrcRect.left    != srcRect.left    ||
            m_lastUpdateSrcRect.right   != srcRect.right   ||
            m_lastUpdateSrcRect.top     != srcRect.top     ||
            m_lastUpdateSrcRect.bottom  != srcRect.bottom  ||
            m_nUpdateOverlayByPassCount > OVERLAY_BYPASS_THRESHOLD ||
            !_IsSurfaceVisible() ||
            m_bSpamUpdateOverlay)
        {
            m_nUpdateOverlayByPassCount = 0;
            memcpy(&m_lastUpdateDestRect, &destRect, sizeof(HXxRect)); /* Flawfinder: ignore */
            memcpy(&m_lastUpdateSrcRect,  &srcRect,  sizeof(HXxRect)); /* Flawfinder: ignore */
            _UpdateOverlay(&destRect, &srcRect, flags);
#ifdef _CHECK_PERFORMANCE
            NumOfUpdateOverlays++;
#endif
        }
        else
        {
            m_nUpdateOverlayByPassCount++;
        }
    }

    memcpy(&m_lastSrcRect, &srcRect, sizeof(HXxRect)); /* Flawfinder: ignore */

    //Check to see if the overlay is visible.  If not then something
    //has gone horribly wrong.  we should turn off overlays.
    if (!_IsSurfaceVisible())
    {
        m_nOverlayFailureCount++;
    }
    else
    {
        m_nOverlayFailureCount--;
    }

    if (m_nOverlayFailureCount > OVERLAY_FAILURE_THRESHOLD)
    {
        //A good idea this assert, but a little TOO noisy... which
        //should worry you...  HX_ASSERT(OVERLAY_NOT_VISIBLE);

        if (m_bVideoSurface2)
        {
            m_bMultipleOverlay = TRUE;
            ForceGDIMode(TRUE);
        }
        else
        {
            m_nBltMode = HX_BASIC_BLT;
            memset(&m_surfaceSize, 0, sizeof(HXxSize));
#ifdef _DEBUG
            fprintf( stderr, "Overlays have failed. Switching to HX_BASIC_BLT.\n" );
#endif
            _ReleaseSurface();
        }
    }

    retVal = HXR_OK;

// #ifdef _CHECK_PERFORMANCE
//    QueryPerformanceCounter(&PerfCounter);
//    double endTime = ((double)PerfCounter.LowPart +
//                      4294967296.0*PerfCounter.HighPart)/frequency;

//    static UINT32 z_nNumTimes = 0;
//    static double z_fTotalTime;
//    static double z_fAverageTime;

//    z_nNumTimes++;
//    z_fTotalTime += endTime - startTime;
//    z_fAverageTime = z_fTotalTime / (double) z_nNumTimes;

//    if (! (z_nNumTimes % 25))
//    {
//       FILE* f1 = ::fopen("c:\\performance.txt", "a+");
//       ::fprintf(f1, "WINDRAW2 - UPDATE OVERLAY %d blts. Total CPU time: %f, CPU/Blt: %f -- Blt/s Second Max: %f Num of Updates: %d\n", z_nNumTimes, z_fTotalTime, z_fAverageTime, 1.0/z_fAverageTime, NumOfUpdateOverlays);
//       fclose(f1);
//    }
// #endif

    return retVal;
}

void CBaseSurface::UpdateDestRect(int x, int y)
{
    if (m_nBltMode != HX_OVERLAY_BLT)
    {
        return;
    }

    CHXBaseSite* pSite = m_pSite->GetTopLevelSite();
    if (pSite && pSite->m_bDisableForceRedraw)
    {
        return;
    }

    if(m_lastSrcRect.left || m_lastSrcRect.right ||
       m_lastSrcRect.top || m_lastSrcRect.bottom)
    {
        HXxSize     size;
        HXxPoint    loc;
        HXxPoint*   off;

        HXxRect rect;

        if(m_bYUVBlending  || m_LinkedSites.GetCount() || m_pLinkedOverlay || !m_bVideoSurface2  )
        {
            size = m_pSite->m_size;
        }
        else
        {
            m_pSite->GetWindowRect(&rect);
            size.cx = rect.right - rect.left;
            size.cy = rect.bottom - rect.top;
        }

        m_pSite->GetPosition(loc);
        off = m_pSite->GetOrigin();
        HXxRect destRect;
        destRect.left   = off->x>0?off->x:0;
        destRect.top    = off->y>0?off->y:0;

        destRect.right  = destRect.left + size.cx;
        destRect.bottom = destRect.top + size.cy;

        if( m_bVideoSurface2 )
        {
            if( destRect.right>(m_pSite->m_size.cx+off->x) )
                destRect.right = m_pSite->m_size.cx+off->x;
            if( destRect.bottom>(m_pSite->m_size.cy+off->y) )
                destRect.bottom = m_pSite->m_size.cy+off->y;
        }

        UpdateOverlay(destRect, m_lastSrcRect, x, y);
    }
}

STDMETHODIMP CBaseSurface::BeginOptimizedBlt(HXBitmapInfoHeader* pBitmapInfo)
{
    if (!pBitmapInfo)
    {
        HX_ASSERT(FALSE);
        return HXR_FAIL;
    }

    // Are the new parameters the same as the old paramaters? If not
    // then delete the old surfaces.
    if (m_pOptimizedFormat && pBitmapInfo)
    {
        if (pBitmapInfo->biWidth       != m_pOptimizedFormat->biWidth       ||
            pBitmapInfo->biHeight      != m_pOptimizedFormat->biHeight      ||
            pBitmapInfo->biPlanes      != m_pOptimizedFormat->biPlanes      ||
            pBitmapInfo->biBitCount    != m_pOptimizedFormat->biBitCount    ||
            pBitmapInfo->biCompression != m_pOptimizedFormat->biCompression)
        {
            DestroySurfaces();
        }
    }

    m_pSite->_TLSLock();

    HX_DELETE(m_pOptimizedFormat);
    m_pOptimizedFormat = new HXBitmapInfoHeader;

    memcpy(m_pOptimizedFormat, pBitmapInfo, sizeof(HXBitmapInfoHeader)); /* Flawfinder: ignore */

    //Check to see if this is YUV, if so we should attempt to create an
    //overlay surface

    m_nSrcCID = GETBITMAPCOLOR( pBitmapInfo);

    if (m_nSrcCID == CID_XING)
    {
        m_bFlipOverlay = TRUE;
        m_nBackBufferCount = max(1, m_nBackBufferCount);
        m_bSpamUpdateOverlay = FALSE;
    }


    //Overlays were not working within the ROB renderer. Thus,
    //overlays are currently disabeled within the rob renderer. This
    //is done by checking to see if the containing CHXSiteWindowless
    //is equal to the top level site. The ROB renderer is the only
    //currently existing renderer which does not match this criteria.
    //More than likely that any other renderer which matched this
    //criteria would also fail. Some time in the future a better fix
    //should be put in to allow video to function efficiently within
    //the ROB renderer.  Maybe after the release XXXAH 02/21/00 (I
    //always like seeing how long comments like this can continue to
    //exist in the code base, my guess is at least 3 years).
    UINT16 uBitsPerPixel;
    UINT16 uHorzRes;
    UINT16 uVertRes;

    m_pSite->_GetDeviceCaps(NULL, uBitsPerPixel, uHorzRes, uVertRes);

    if( IsYUV(m_nSrcCID) &&
        m_pSite->GetContainingCHXBaseSite() == m_pSite->GetTopLevelSite() &&
        (!uBitsPerPixel || (uBitsPerPixel >= 8))
        )
    {
        // Check for manual yuv output
        IHXPreferences*    pPreferences = NULL;
        IHXBuffer*         pBuffer      = NULL;

        if (HXR_OK == m_pContext->QueryInterface(IID_IHXPreferences,(void**)&pPreferences))
        {
            if (pPreferences->ReadPref("SetYUVOutputFormat", pBuffer) == HXR_OK)
            {
                char* szFourCC = (char*)pBuffer->GetBuffer();

                // Ensure entry is the length of a four character code
                if (strlen(szFourCC) == 4)
                {
                    // Create a cid from the string fourCC entry
                    UINT32 ulFourCC = MAKEFOURCC(szFourCC[0], szFourCC[1],
                                                 szFourCC[2], szFourCC[3]);

                    int newCID = MapFourCCtoCID(ulFourCC);

                    // Get the current output prioirty list for the source
                    if (m_pyuvInputMngr->IsFormatSupported(m_nSrcCID) &&
                        m_pyuvInputMngr->GetOutputFormat(m_nSrcCID, 0) != newCID)
                    {
                        // Restore defaults before issuing the priority
                        m_pyuvInputMngr->SetDefaultOutputPriority(m_nSrcCID);

                        int newlist[MAX_OUTPUT_FORMATS];
                        int nListCount = 1;
                        memset(newlist, 0, sizeof(newlist));

                        newlist[0] = newCID;

                        for (int i=0;
                             m_pyuvInputMngr->GetOutputFormat(m_nSrcCID, i)>=0 && i<MAX_OUTPUT_FORMATS;
                             i++)
                        {
                            newlist[nListCount] = m_pyuvInputMngr->GetOutputFormat(m_nSrcCID, i);
                            ++nListCount;
                        }

                        // Create a new list add the new cid at the front
                        m_pyuvInputMngr->SetOutputPriority(m_nSrcCID, newlist, nListCount);
                    }
                }
            }

            HX_RELEASE(pBuffer);
            HX_RELEASE(pPreferences);
        }

        if (m_bUseOverlays)
        {
            TryCreateOverlay(TRUE);
        }

        TryCreateOverlay(FALSE);
        TryCreateOffScreenBuffer();
    }

    if (m_pSite && m_pSite->m_pTopLevelSite)
    {
        m_pSite->_TLSUnlock();
    }

    ResetUpdateOverlay();

    CreateHWMemObj(m_nSurfaceCID);

    // Just pretend the source format is I420 in this case
    if (IsStructured(m_nSurfaceCID) || IsStructured(m_nSrcCID))
    {
        m_nSrcCID = CID_I420;
    }

    // Set current color control values
    m_Brightness = m_pSite->GetBrightness();
    m_Contrast = m_pSite->GetContrast();
    m_Saturation = m_pSite->GetSaturation();
    m_Hue = m_pSite->GetHue();
    m_Sharpness = m_pSite->GetSharpness();

    return HXR_OK;
}

STDMETHODIMP CBaseSurface::OptimizedBlt( UCHAR* pImageBits,
                                         REF(HXxRect) rDestRect,
                                         REF(HXxRect) rSrcRect)
{
    if (m_pOptimizedFormat)
    {
        return Blt(pImageBits, m_pOptimizedFormat, rDestRect, rSrcRect);
    }
    else
    {
        return HXR_FAIL;
    }
}

STDMETHODIMP CBaseSurface::EndOptimizedBlt(void)
{
    _WaitForFlush();
    DestroySurfaces();

    // Release our gdi root surface
    if (m_bLostHWAcceleration)
    {
        CBaseRootSurface* pSurface = m_pSite->GetRootSurface();

        if (pSurface)
        {
            // Make sure the gdi surface was destroyed
            _ReleaseSurface(pSurface);
            pSurface->_AcquireHardwareAcceleration();
        }

        m_bLostHWAcceleration = FALSE;
    }

    HX_DELETE(m_pOptimizedFormat);
    return HXR_OK;
}

STDMETHODIMP CBaseSurface::GetOptimizedFormat(REF(HX_COMPRESSION_TYPE) ulType)
{
    if (!m_pOptimizedFormat)
    {
        return HXR_FAIL;
    }

    ulType = m_pOptimizedFormat->biCompression;

    return HXR_OK;
}

STDMETHODIMP CBaseSurface::GetPreferredFormat(REF(HX_COMPRESSION_TYPE) ulType)
{
    ulType = 0L; // m_PreferredFormat;
    return HXR_OK;
}


//
//   IHXVideoControls methods
//
STDMETHODIMP_(float) CBaseSurface::GetBrightness(void)
{
    HX_ASSERT(NOT_IMPL);
    return 0.0;
}

STDMETHODIMP CBaseSurface::SetBrightness(float Brightness)
{
    HX_ASSERT(NOT_IMPL);
    return HXR_NOTIMPL;
}

STDMETHODIMP_(float) CBaseSurface::GetContrast(void)
{
    HX_ASSERT(NOT_IMPL);
    return 0.0;
}

STDMETHODIMP CBaseSurface::SetContrast(float Contrast)
{
    HX_ASSERT(NOT_IMPL);
    return HXR_NOTIMPL;
}

STDMETHODIMP_(float) CBaseSurface::GetSaturation(void)
{
    HX_ASSERT(NOT_IMPL);
    return 0.0;
}

STDMETHODIMP CBaseSurface::SetSaturation(float Saturation)
{
    HX_ASSERT(NOT_IMPL);
    return HXR_NOTIMPL;
}

STDMETHODIMP_(float) CBaseSurface::GetHue(void)
{
    HX_ASSERT(NOT_IMPL);
    return 0.0;
}

STDMETHODIMP CBaseSurface::SetHue(float Hue)
{
    HX_ASSERT(NOT_IMPL);
    return HXR_NOTIMPL;
}

STDMETHODIMP_(float) CBaseSurface::GetSharpness(void)
{
    HX_ASSERT(NOT_IMPL);
    return 0.0;
}

STDMETHODIMP CBaseSurface::SetSharpness(float Sharpness)
{
    HX_ASSERT(NOT_IMPL);
    return HXR_NOTIMPL;
}

STDMETHODIMP CBaseSurface::SetModeSharpness(UINT16 dFlag)
{
    HX_ASSERT(NOT_IMPL);
    return HXR_NOTIMPL;
}

HXxRect CBaseSurface::ComputeIntersection(HXxRect* pRect1, HXxRect* pRect2)
{
    HXxRect returnRect;
    returnRect.left     = max(pRect1->left, pRect2->left);
    returnRect.right    = min(pRect1->right, pRect2->right);
    returnRect.top      = max(pRect1->top, pRect2->top);
    returnRect.bottom   = min(pRect1->bottom, pRect2->bottom);

    if (returnRect.left >= returnRect.right || returnRect.top >= returnRect.bottom)
    {
        memset(&returnRect, 0, sizeof(HXxRect));
    }

    return returnRect;
}

BOOL CBaseSurface::_AllowsOverlayShrinking()
{
    return FALSE;
}

void CBaseSurface::_WaitForFlush()
{
}


STDMETHODIMP CBaseSurface::OverlayGranted()
{
    AcquireOverlay();
    return HXR_OK;
}

STDMETHODIMP CBaseSurface::OverlayRevoked()
{
    RelinquishOverlay();
    return HXR_OK;
}

BOOL CBaseSurface::UsingOverlay()
{
    if (m_nBltMode == HX_OVERLAY_BLT)
    {
        return TRUE;
    }

    if (m_pLinkedOverlay)
    {
        return m_pLinkedOverlay->UsingOverlay();
    }

    return FALSE;
}

void CBaseSurface::UpdateBltStats(HXxRect* pRect)
{
    if (IsYUV(GETBITMAPCOLOR(&m_bmiLastBlt)) && m_pOverlayManager)
    {
        UINT32 nPixles = (pRect->right - pRect->left)  * ( pRect->bottom - pRect->top);
        m_pOverlayManager->AddStats((IHXOverlayResponse*)this, nPixles);
    }
}

void CBaseSurface::DisableColorControls(float &fBrightness,
                                        float &fContrast,
                                        float &fSaturation,
                                        float &fHue)
{
    m_pSite->DisableColorControls(fBrightness,
                                  fContrast,
                                  fSaturation,
                                  fHue);
}

void CBaseSurface::EnableColorControls(float fBrightness,
                                       float fContrast,
                                       float fSaturation,
                                       float fHue)
{
    m_pSite->EnableColorControls(fBrightness,
                                 fContrast,
                                 fSaturation,
                                 fHue);
}

HX_RESULT CBaseSurface::_ScheduleFrameForDisplay(void* pOsData,
                                                 tFrameElement* pItem,
                                                 UINT32 nResolution)
{
    return HXR_NOTIMPL;
}

void CBaseSurface::CreateYuvInputMngr()
{
    m_pyuvInputMngr = new CYUVInputFormatMngr;
}

void CBaseSurface::InitSurface(IUnknown* pUnk)
{
    // Get our color conversion functions.
    CreateColorAccess(pUnk);

    // Get our yuv input type manager
    CreateYuvInputMngr();
}
