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

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

#ifdef _MACINTOSH
#include "hxwin.h"	/* for _HXFocusContext; needed for other platforms too? */
#endif

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

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

// pxcomlib
#include "pxrect.h"
#include "pxcolor.h"
#include "pximage.h"
#include "pxeffect.h"
#include "pxcmpmgr.h"
#include "pxrndcod.h"
#include "rpeffect.h"
#include "pxtime.h"

// pxrend
#include "hlinkmgr.h"
#include "rncodmgr.h"
#include "imghelpr.h"
#include "imagemgr.h"
#include "fxpkgmgr.h"
#include "fxmanagr.h"
#include "fxseshun.h"

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

BEGIN_INTERFACE_LIST_NOCREATE(PXEffectSession)
END_INTERFACE_LIST

PXEffectSession::PXEffectSession()
{
    m_pEffectsManager = NULL;
    m_pEffect         = NULL;
    m_pImageManager   = NULL;
    m_pErrorMessages  = NULL;
    m_pDstImage       = NULL;
    Reset();
};

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

HX_RESULT PXEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                PXEffect*          pEffect,
                                PXImageManager*    pImageManager,
                                IHXErrorMessages* pErrorMessages)
{
    HX_RESULT retVal = HXR_OK;

    if (pEffectsManager && pEffect && pImageManager)
    {
        // Clear out everything
        Reset();
        Deallocate();

        // Save copies of needed objects
        m_pEffectsManager = pEffectsManager;
        m_pEffectsManager->AddRef();
        m_pEffect = pEffect;
        m_pEffect->AddRef();
        m_pImageManager = pImageManager;
        m_pImageManager->AddRef();
        m_pErrorMessages = pErrorMessages;
        m_pErrorMessages->AddRef();


        // Init the last exe time and flag
        m_ulLastExeTime = m_pEffect->GetStart();
        m_bFirstExe     = TRUE;

        // Adjust the src rect if necessary
        if (m_pEffect->HasTarget())
        {
            PXImage* pTmpImg = NULL;
            retVal           = m_pImageManager->GetImage(m_pEffect->GetTarget(), &pTmpImg);
            if (SUCCEEDED(retVal))
            {
                PXRect cRect;
                cRect = m_pEffect->GetSrcRect();
                cRect.AdjustForZeroValues(pTmpImg->GetWidth(), pTmpImg->GetHeight());
                cRect.AdjustForOvershoot(pTmpImg->GetWidth(), pTmpImg->GetHeight());
                m_pEffect->SetSrcRect(cRect);
            }
            HX_RELEASE(pTmpImg);
        }

        if (SUCCEEDED(retVal))
        {
            // Fix any zero-values or overshoot in the dst rect
            PXRect cRect;
            cRect = m_pEffect->GetDstRect();
            cRect.AdjustForZeroValues(m_pImageManager->GetDisplayWidth(),
                                      m_pImageManager->GetDisplayHeight());
            cRect.AdjustForOvershoot(m_pImageManager->GetDisplayWidth(),
                                     m_pImageManager->GetDisplayHeight());
            m_pEffect->SetDstRect(cRect);
            // Get a subimage of the display image as our destination image
            retVal = m_pImageManager->GetDisplaySubImage(&m_pDstImage,
                                                         pEffect->GetDstRect(),
                                                         FALSE); // don't need a copy, just a ref
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

void PXEffectSession::ResetDamage()
{
    m_bDisplayDamaged    = FALSE;
    m_cDamageRect.left   = 0;
    m_cDamageRect.top    = 0;
    m_cDamageRect.right  = 0;
    m_cDamageRect.bottom = 0;
}

HX_RESULT PXEffectSession::GetEffect(PXEffect** ppEffect)
{
    HX_RESULT retVal = HXR_OK;

    if (ppEffect)
    {
        if (m_pEffect)
        {
            *ppEffect = m_pEffect;
            (*ppEffect)->AddRef();
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

BOOL PXEffectSession::NeedsPostDurationUpdate() const
{
    BOOL bRetVal = FALSE;

    if (m_pEffect && m_pImageManager)
    {
        if (m_pEffect->HasTarget())
        {
            BOOL      bComplete = FALSE;
            HX_RESULT retVal    = m_pImageManager->IsImageCompletelyDecoded(m_pEffect->GetTarget(), &bComplete);
            if (SUCCEEDED(retVal))
            {
                bRetVal = (bComplete ? FALSE : TRUE);
            }
        }
    }

    return bRetVal;
}

HX_RESULT PXEffectSession::ReleaseTargetImage()
{
    HX_RESULT retVal = HXR_OK;

    if (m_pEffect)
    {
        if (m_pEffect->HasTarget() && m_pEffect->GetLastUse())
        {
            retVal = m_pImageManager->DeleteImage(m_pEffect->GetTarget());
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

BOOL PXEffectSession::MaxFramesPerSecondCheck(UINT32 ulTime)
{
    BOOL bRet = FALSE;

    if (m_pEffect)
    {
        // If we haven't reached the start time, then of course
        // we won't do the effect
        if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime))
        {
            if (m_bFirstExe)
            {
                // If this is the first time we've executed the
                // effect, then we always do the effect. We also
                // initialize the last exe time to the current time.
                bRet            = TRUE;
                m_ulLastExeTime = ulTime;
                m_bFirstExe     = FALSE;
            }
            else
            {
                // This is not the first time we've done the effect, so
                // we will actually calculate if it's been long enough
                // in between execute's.
                //
                // Check the maxfps attribute
                if (m_pEffect->GetMaxFps())
                {
                    // maxfps WAS specified. Therefore, we must compute
                    // the minimum elapsed time in between draws.
                    UINT32 ulMinElapsedTime = 1000 / m_pEffect->GetMaxFps();
                    UINT32 ulTimeSinceLast  = ulTime - m_ulLastExeTime;
                    if (ulTimeSinceLast >= ulMinElapsedTime)
                    {
                        // We will update the last exe time at the end of execute
                        bRet = TRUE;
                    }
                }
                else
                {
                    // maxfps was not specified. Therefore, we do the
                    // effect every time we're called.
                    bRet = TRUE;
                }
            }
        }
    }

    return bRet;
}

void PXEffectSession::Reset()
{
    m_bInitialized                 = FALSE;
    m_bFinished                    = FALSE;
    m_ulPostDurationExpirationTime = 0;
    m_bFirstExe                    = TRUE;
    m_ulLastExeTime                = 0;
    m_bDelayedInit                 = FALSE;
    ResetDamage();
}

void PXEffectSession::Deallocate()
{
    HX_RELEASE(m_pEffectsManager);
    HX_RELEASE(m_pEffect);
    HX_RELEASE(m_pImageManager);
    HX_RELEASE(m_pErrorMessages);
    HX_RELEASE(m_pDstImage);
}

IMPLEMENT_COM_CREATE_FUNCS(PXFillEffectSession);

HX_RESULT PXFillEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                    PXEffect*          pEffect,
                                    PXImageManager*    pImageManager,
                                    IHXErrorMessages* pErrorMessages)
{
    // Call the super-class' Init()
    HX_RESULT retVal = PXEffectSession::Init(pEffectsManager,
                                             pEffect,
                                             pImageManager,
                                             pErrorMessages);
    if (SUCCEEDED(retVal))
    {
        // Make sure this is a fill effect
        if (pEffect->GetEffectType() == PXEffect::kEffectTypeFill)
        {
            // Set the initialized flag
            m_bInitialized = TRUE;
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    if (FAILED(retVal))
    {
        Reset();
        Deallocate();
    }

    return retVal;
}

HX_RESULT PXFillEffectSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime) && !m_bFinished)
        {
            // No need to do the maxfps check, since a fill is a one-shot effect.

            // Do the fill
            retVal      = m_pDstImage->Fill(m_pEffect->GetColor());
            // Set the finished flag
            m_bFinished = TRUE;
            // Set the damaged display flag
            m_bDisplayDamaged = TRUE;
            // Set the damage rect
            m_pDstImage->GetSubRect(m_cDamageRect);
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

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

    return retVal;
}

IMPLEMENT_COM_CREATE_FUNCS(PXFadeOutEffectSession);

HX_RESULT PXFadeOutEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                       PXEffect*          pEffect,
                                       PXImageManager*    pImageManager,
                                       IHXErrorMessages* pErrorMessages)
{
    // Call the super-class' Init()
    HX_RESULT retVal = PXEffectSession::Init(pEffectsManager,
                                             pEffect,
                                             pImageManager,
                                             pErrorMessages);
    if (SUCCEEDED(retVal))
    {
        // Make sure this is a fadeout effect
        if (pEffect->GetEffectType() == PXEffect::kEffectTypeFadeOut)
        {
            // Check to see if we can use recursive
            m_bRecursive = FALSE;
// XXXMEH - turn off recursive fadeouts for now 04/15/99
//            retVal       = m_pEffectsManager->CanUseRecursive(m_pEffect, m_bRecursive);
            if (SUCCEEDED(retVal))
            {
                // If we are NOT recursive, then we need to copy the dst rect
                if (!m_bRecursive)
                {
                    HX_RELEASE(m_pDstCopy);
                    retVal = m_pImageManager->GetDisplaySubImage(&m_pDstCopy,
                                                                 pEffect->GetDstRect(),
                                                                 TRUE); // need a copy
                }

                if (SUCCEEDED(retVal))
                {
                    m_bInitialized = TRUE;
                }
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    if (FAILED(retVal))
    {
        Deallocate();
        Reset();
        HX_RELEASE(m_pDstCopy);
        m_ulLastExeTime = 0;
        m_bRecursive    = FALSE;
    }

    return retVal;
}

HX_RESULT PXFadeOutEffectSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (!m_bFinished)
        {
            if (IsTimeEqualOrLater(m_pEffect->GetEnd(), ulTime))
            {
                // Just fill with the color
                retVal = m_pDstImage->Fill(m_pEffect->GetColor());
                // Set the finished flag
                m_bFinished = TRUE;
                // Set the damaged display flag
                m_bDisplayDamaged = TRUE;
                // Set the damage rect
                m_pDstImage->GetSubRect(m_cDamageRect);
            }
            else if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime) &&
                     IsTimeLater(ulTime, m_pEffect->GetEnd()))
            {
                // Check the maxfps
                if (MaxFramesPerSecondCheck(ulTime))
                {
                    // We WILL actually do the effect
                    BYTE* pLUT1 = NULL;
                    BYTE* pLUT2 = NULL;
                    if (m_bRecursive)
                    {
                        retVal = m_pEffectsManager->GetRecursiveFadeLUT(m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                                        ulTime, m_ulLastExeTime,
                                                                        &pLUT1, &pLUT2);
                        if (SUCCEEDED(retVal))
                        {
                            retVal = m_pDstImage->RecursiveBlendToColor(m_pEffect->GetColor(), pLUT1, pLUT2);
                        }
                    }
                    else
                    {
                        retVal = m_pEffectsManager->GetNormalFadeLUT(m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                                     ulTime, &pLUT1, &pLUT2);
                        if (SUCCEEDED(retVal))
                        {
                            retVal = m_pDstImage->BlendToColor(m_pDstCopy, m_pEffect->GetColor(), pLUT1, pLUT2);
                        }
                    }
                    // Set the damaged display flag
                    m_bDisplayDamaged = TRUE;
                    // Set the damage rect
                    m_pDstImage->GetSubRect(m_cDamageRect);
                    // Update the last exe time
                    m_ulLastExeTime = ulTime;
                }
            }
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

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

    return retVal;
}

IMPLEMENT_COM_CREATE_FUNCS(PXFadeInEffectSession);

HX_RESULT PXFadeInEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                      PXEffect*          pEffect,
                                      PXImageManager*    pImageManager,
                                      IHXErrorMessages* pErrorMessages)
{
    // Call the super-class' Init()
    HX_RESULT retVal = PXEffectSession::Init(pEffectsManager,
                                             pEffect,
                                             pImageManager,
                                             pErrorMessages);
    if (SUCCEEDED(retVal))
    {
        // Make sure this is a fadein or crossfade effect
        if (pEffect->GetEffectType() == PXEffect::kEffectTypeFadeIn ||
            pEffect->GetEffectType() == PXEffect::kEffectTypeCrossFade)
        {
            // Check to see if we can use recursive
            m_bRecursive = FALSE;
// XXXMEH - turn off recursive fadeins for now 04/15/99
//            retVal       = m_pEffectsManager->CanUseRecursive(m_pEffect, m_bRecursive);
            if (SUCCEEDED(retVal))
            {
                // If we are NOT recursive, then we need to copy the dst rect
                if (!m_bRecursive && pEffect->GetDuration() > 0)
                {
                    HX_RELEASE(m_pStartImage);
                    retVal = m_pImageManager->GetDisplaySubImage(&m_pStartImage,
                                                                 pEffect->GetDstRect(),
                                                                 TRUE); // need a copy
                }

                // Get our end image (what we're fading to)
                if (SUCCEEDED(retVal))
                {
                    HX_RELEASE(m_pEndImage);
                    retVal = m_pImageManager->GetPresentationSubImage(&m_pEndImage,
                                                                      pEffect->GetTarget(),
                                                                      pEffect->GetSrcRect(),
                                                                      pEffect->GetDstRect(),
                                                                      pEffect->GetAspectFlag());
                    if (SUCCEEDED(retVal))
                    {
                        m_bInitialized = TRUE;
                    }
                }
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    if (FAILED(retVal))
    {
        Reset();
        Deallocate();
        HX_RELEASE(m_pStartImage);
        HX_RELEASE(m_pEndImage);
        m_ulLastExeTime = 0;
        m_bRecursive    = FALSE;
    }

    return retVal;
}

HX_RESULT PXFadeInEffectSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (!m_bFinished)
        {
            if (IsTimeEqualOrLater(m_pEffect->GetEnd(), ulTime))
            {
                // Just copy the end image to the display image
                retVal = m_pDstImage->CopyFrom(m_pEndImage);
                // Set the finished flag
                m_bFinished = TRUE;
                // Set the damaged display flag
                m_bDisplayDamaged = TRUE;
                // Set the damage rect
                m_pDstImage->GetSubRect(m_cDamageRect);
            }
            else if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime) &&
                     IsTimeLater(ulTime, m_pEffect->GetEnd()))
            {
                // Check the maxfps
                if (MaxFramesPerSecondCheck(ulTime))
                {
                    // We passed the maxfps check, so we WILL do the effect
                    BYTE* pLUT1 = NULL;
                    BYTE* pLUT2 = NULL;
                    if (m_bRecursive)
                    {
                        retVal = m_pEffectsManager->GetRecursiveFadeLUT(m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                                        ulTime, m_ulLastExeTime,
                                                                        &pLUT1, &pLUT2);
                        if (SUCCEEDED(retVal))
                        {
                            retVal = m_pDstImage->RecursiveBlend(m_pEndImage, pLUT2, pLUT1);
                        }
                    }
                    else
                    {
                        retVal = m_pEffectsManager->GetNormalFadeLUT(m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                                     ulTime, &pLUT1, &pLUT2);
                        if (SUCCEEDED(retVal))
                        {
                            retVal = m_pDstImage->Blend(m_pStartImage, m_pEndImage, pLUT1, pLUT2);
                        }
                    }
                    // Set the damaged display flag
                    m_bDisplayDamaged = TRUE;
                    // Set the damage rect
                    m_pDstImage->GetSubRect(m_cDamageRect);
                    // Update the last exe time
                    m_ulLastExeTime = ulTime;
                }
            }
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

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

    return retVal;
}

IMPLEMENT_COM_CREATE_FUNCS(PXWipeEffectSession);

HX_RESULT PXWipeEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                    PXEffect*          pEffect,
                                    PXImageManager*    pImageManager,
                                    IHXErrorMessages* pErrorMessages)
{
    // Call the super-class' Init()
    HX_RESULT retVal = PXEffectSession::Init(pEffectsManager,
                                             pEffect,
                                             pImageManager,
                                             pErrorMessages);
    if (SUCCEEDED(retVal))
    {
        // Make sure this is a wipe effect
        if (pEffect->GetEffectType() == PXEffect::kEffectTypeWipe)
        {
            // Set the first time flag
            m_bFirstTime = TRUE;
            // Check to see if we can use recursive
            m_bRecursive = FALSE;
// XXXMEH - turn off recursive wipes for now 04/15/99
//            retVal       = m_pEffectsManager->CanUseRecursive(m_pEffect, m_bRecursive);
            if (SUCCEEDED(retVal))
            {
                // Get our end image (what we're fading to)
                if (SUCCEEDED(retVal))
                {
                    HX_RELEASE(m_pEndImage);
                    retVal = m_pImageManager->GetPresentationSubImage(&m_pEndImage,
                                                                      pEffect->GetTarget(),
                                                                      pEffect->GetSrcRect(),
                                                                      pEffect->GetDstRect(),
                                                                      pEffect->GetAspectFlag());
                    if (SUCCEEDED(retVal))
                    {
                        // If this is a push wipe AND m_pEndImage (the image we
                        // are wiping to) uses transparency, then we need to
                        // remove the transparency by blending it with the background
                        // color.
                        if (pEffect->GetWipeType() == PXEffect::kWipeTypePush &&
                            m_pEndImage->GetHasAlpha())
                        {
                            // We have to make a copy of the image
                            PXImage* pTmp = NULL;
                            retVal        = PXImage::CreateObject(&pTmp);
                            if (SUCCEEDED(retVal))
                            {
                                // AddRef the object
                                pTmp->AddRef();
                                // Copy the current end image
                                retVal = pTmp->CreateSubImage(m_pEndImage, 0, 0, m_pEndImage->GetWidth(),
                                                              m_pEndImage->GetHeight(), TRUE, TRUE);
                                if (SUCCEEDED(retVal))
                                {
                                    // This method removes the alpha channel by pre-multiplying
                                    // it against the specified color - in this case the
                                    // background color.
                                    pTmp->PreMultiplyAlphaChannel(m_pImageManager->GetBackgroundColor());
                                    // Now replace the end image
                                    HX_RELEASE(m_pEndImage);
                                    m_pEndImage = pTmp;
                                    m_pEndImage->AddRef();
                                }
                            }
                            HX_RELEASE(pTmp);
                        }
                        // Set the initialized flag
                        m_bInitialized = TRUE;
                    }
                }
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    if (FAILED(retVal))
    {
        Reset();
        Deallocate();
        HX_RELEASE(m_pStartImage);
        HX_RELEASE(m_pEndImage);
        m_ulLastExeTime = 0;
        m_bRecursive    = FALSE;
    }

    return retVal;
}

HX_RESULT PXWipeEffectSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (!m_bFinished)
        {
            if (IsTimeEqualOrLater(m_pEffect->GetEnd(), ulTime))
            {
                // Just copy the end image to the display image
                retVal = m_pDstImage->CopyFrom(m_pEndImage);
                // Set the finished flag
                m_bFinished = TRUE;
                // Set the damaged display flag
                m_bDisplayDamaged = TRUE;
                // Set the damage rect
                m_pDstImage->GetSubRect(m_cDamageRect);
            }
            else if (IsTimeLater(m_pEffect->GetStart(), ulTime) &&
                     IsTimeLater(ulTime, m_pEffect->GetEnd()))
            {
                // Check the maxfps
                if (MaxFramesPerSecondCheck(ulTime))
                {
                    // We passed the maxfps check, so we WILL do the effect
                    if (m_bRecursive)
                    {
                        PXRect cDamageRect;
                        retVal = m_pDstImage->RecursiveWipe(m_pEndImage,
                                                            m_pEffect->GetWipeType(),
                                                            m_pEffect->GetWipeDirection(),
                                                            m_ulLastExeTime - m_pEffect->GetStart(),
                                                            ulTime          - m_pEffect->GetStart(),
                                                            m_pEffect->GetDuration(),
                                                            cDamageRect);
                        if (SUCCEEDED(retVal))
                        {
                            m_bDisplayDamaged    = TRUE;
                            m_cDamageRect.left   = cDamageRect.GetX();
                            m_cDamageRect.top    = cDamageRect.GetY();
                            m_cDamageRect.right  = cDamageRect.GetX() + cDamageRect.GetWidth();
                            m_cDamageRect.bottom = cDamageRect.GetY() + cDamageRect.GetHeight();
                        }
                    }
                    else
                    {
                        if (m_bFirstTime)
                        {
                            HX_RELEASE(m_pStartImage);
                            retVal = m_pImageManager->GetDisplaySubImage(&m_pStartImage,
                                                                         m_pEffect->GetDstRect(),
                                                                         TRUE); // need a copy
                        }
                        if (SUCCEEDED(retVal))
                        {
                            retVal = m_pDstImage->Wipe(m_pStartImage,
                                                       m_pEndImage,
                                                       m_pEffect->GetWipeType(),
                                                       m_pEffect->GetWipeDirection(),
                                                       ulTime - m_pEffect->GetStart(),
                                                       m_pEffect->GetDuration());
                            if (SUCCEEDED(retVal))
                            {
                                // Set the damaged display flag
                                m_bDisplayDamaged = TRUE;
                                // Set the damage rect
                                m_pDstImage->GetSubRect(m_cDamageRect);
                            }
                        }
                    }
                    // Update the last exe time
                    m_ulLastExeTime = ulTime;
                    // Clear the first time flag if necessary
                    if (m_bFirstTime)
                    {
                        m_bFirstTime = FALSE;
                    }
                }
            }
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

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

    return retVal;
}

IMPLEMENT_COM_CREATE_FUNCS(PXViewchangeEffectSession);

HX_RESULT PXViewchangeEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                          PXEffect*          pEffect,
                                          PXImageManager*    pImageManager,
                                          IHXErrorMessages* pErrorMessages)
{
    HX_RESULT retVal = HXR_OK;

    if (pEffectsManager && pEffect && pImageManager)
    {
        // Clear out everything
        Reset();
        Deallocate();
        m_bSrcChanges = FALSE;
        m_bDstChanges = FALSE;
        m_bDstMoves   = FALSE;
        m_bRestoreDst = NULL;
        HX_RELEASE(m_pSrcImage);
        HX_RELEASE(m_pPrevDst);

        // Save copies of needed objects
        m_pEffectsManager = pEffectsManager;
        m_pEffectsManager->AddRef();
        m_pEffect = pEffect;
        m_pEffect->AddRef();
        m_pImageManager = pImageManager;
        m_pImageManager->AddRef();
        m_pErrorMessages = pErrorMessages;
        m_pErrorMessages->AddRef();

        // Initialize the last exe time and flag
        m_ulLastExeTime = m_pEffect->GetStart();
        m_bFirstExe     = TRUE;

        // Adjust the src rect if necessary
        PXImage* pTargetImage = NULL;
        retVal                = m_pImageManager->GetImage(m_pEffect->GetTarget(), &pTargetImage);
        if (SUCCEEDED(retVal))
        {
            // Set the proper src rectangles
            PXRect cRect;
            cRect = m_pEffect->GetSrcRect();
            cRect.AdjustForZeroValues(pTargetImage->GetWidth(), pTargetImage->GetHeight());
            cRect.AdjustForOvershoot(pTargetImage->GetWidth(), pTargetImage->GetHeight());
            m_pEffect->SetSrcRect(cRect);
            cRect = m_pEffect->GetStartSrcRect();
            cRect.AdjustForZeroValues(pTargetImage->GetWidth(), pTargetImage->GetHeight());
            cRect.AdjustForOvershoot(pTargetImage->GetWidth(), pTargetImage->GetHeight());
            m_pEffect->SetStartSrcRect(cRect);

            // Fix dst rectangles if necessary
            cRect = m_pEffect->GetDstRect();
            cRect.AdjustForZeroValues(m_pImageManager->GetDisplayWidth(),
                                      m_pImageManager->GetDisplayHeight());
            cRect.AdjustForOvershoot(m_pImageManager->GetDisplayWidth(),
                                     m_pImageManager->GetDisplayHeight());
            m_pEffect->SetDstRect(cRect);
            cRect = m_pEffect->GetStartDstRect();
            cRect.AdjustForZeroValues(m_pImageManager->GetDisplayWidth(),
                                      m_pImageManager->GetDisplayHeight());
            cRect.AdjustForOvershoot(m_pImageManager->GetDisplayWidth(),
                                     m_pImageManager->GetDisplayHeight());
            m_pEffect->SetStartDstRect(cRect);

            // Now set up the member booleans
            m_bSrcChanges = (m_pEffect->GetStartSrcRect() == m_pEffect->GetSrcRect() ? FALSE : TRUE);
            m_bDstChanges = (m_pEffect->GetStartDstRect() == m_pEffect->GetDstRect() ? FALSE : TRUE);

            // Determine if we need to restore the destination
            if (m_bDstChanges)
            {
                // Does the dst width and height stay constant?
                if (m_pEffect->GetStartDstRect().GetWidth()  == m_pEffect->GetDstRect().GetWidth() &&
                    m_pEffect->GetStartDstRect().GetHeight() == m_pEffect->GetDstRect().GetHeight())
                {
                    m_bDstMoves = TRUE;
                }
                else
                {
                    m_bDstMoves = FALSE;
                }

                // If the ending dst rect completely contains the starting dst
                // rect, then every draw of this viewchange will completely
                // obscure the last draw. However, the ending dst rect DOES NOT
                // completely contain the starting dst rect, then we need to
                // restore the background.
                if (m_pEffect->GetDstRect().Contains(m_pEffect->GetStartDstRect()))
                {
                    m_bRestoreDst = FALSE;
                }
                else
                {
                    m_bRestoreDst = TRUE;
                }
            }

            // Ok, now we can set up the images (maybe)
            if (!m_bSrcChanges)
            {
                // The src rect DOES NOT change throughout the entire effect.
                retVal = m_pImageManager->GetPresentationSubImage(&m_pSrcImage,
                                                                  m_pEffect->GetTarget(),
                                                                  m_pEffect->GetSrcRect());
            }

            if (SUCCEEDED(retVal))
            {
                if (m_bDstChanges)
                {
                    if (m_bRestoreDst)
                    {
                        // Compute the biggest size the restore image needs to be
                        INT32 lStartW = m_pEffect->GetStartDstRect().GetWidth();
                        INT32 lStartH = m_pEffect->GetStartDstRect().GetHeight();
                        INT32 lEndW   = m_pEffect->GetDstRect().GetWidth();
                        INT32 lEndH   = m_pEffect->GetDstRect().GetHeight();
                        INT32 lMaxW   = PXMAX(lStartW, lEndW);
                        INT32 lMaxH   = PXMAX(lStartH, lEndH);
                        // Get a scratch image which can handle this size
                        retVal = m_pImageManager->GetScratchImage(&m_pPrevDst, lMaxW, lMaxH);
                        if (SUCCEEDED(retVal))
                        {
                            // Set the initial restore rect
                            m_cRestoreRect.left   = m_pEffect->GetStartDstRect().GetX();
                            m_cRestoreRect.top    = m_pEffect->GetStartDstRect().GetY();
                            m_cRestoreRect.right  = m_pEffect->GetStartDstRect().GetX() +
                                                    m_pEffect->GetStartDstRect().GetWidth();
                            m_cRestoreRect.bottom = m_pEffect->GetStartDstRect().GetY() +
                                                    m_pEffect->GetStartDstRect().GetHeight();
                            // Now set up the image for the initial restore size
                            retVal = m_pPrevDst->Create(HXxRECT_WIDTH(m_cRestoreRect),
                                                        HXxRECT_HEIGHT(m_cRestoreRect),
                                                        m_pImageManager->GetDefaultBitsPerPixel(),
                                                        m_pImageManager->GetDefaultPixelFormat(),
                                                        m_pImageManager->GetDefaultRowsInverted(),
                                                        FALSE); // Use the same buffer!!
                            if (SUCCEEDED(retVal))
                            {
                                retVal = m_pPrevDst->Fill32(m_pImageManager->GetBackgroundColor());
                            }
                        }
                    }
                }
                else
                {
                    // The destination rect doesn't change, so we can set it up once.
                    retVal = m_pImageManager->GetDisplaySubImage(&m_pDstImage, m_pEffect->GetDstRect());
                }

                if (SUCCEEDED(retVal))
                {
                    m_bInitialized = TRUE;
                }
            }

        }
        HX_RELEASE(pTargetImage);
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    if (FAILED(retVal))
    {
        Reset();
        Deallocate();
        m_bSrcChanges = FALSE;
        m_bDstChanges = FALSE;
        m_bDstMoves   = FALSE;
        m_bRestoreDst = NULL;
        HX_RELEASE(m_pSrcImage);
        HX_RELEASE(m_pPrevDst);
        HX_ASSERT(FALSE);
    }

    return retVal;
}

HX_RESULT PXViewchangeEffectSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (!m_bFinished)
        {
            if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime))
            {
                // Do the maxfps check
                BOOL bDoEffect = MaxFramesPerSecondCheck(ulTime);

                // Check if this is the last time
                if (IsTimeEqualOrLater(m_pEffect->GetEnd(), ulTime))
                {
                    m_bFinished = TRUE;
                    bDoEffect   = TRUE;
                    ulTime      = m_pEffect->GetEnd();
                }

                if (bDoEffect)
                {
                    if (m_bSrcChanges)
                    {
                        PXRect cSrcRect;
                        cSrcRect.InterpolateRect(ulTime, m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                 m_pEffect->GetStartSrcRect(), m_pEffect->GetSrcRect());
                        HX_RELEASE(m_pSrcImage);
                        retVal = m_pImageManager->GetPresentationSubImage(&m_pSrcImage,
                                                                          m_pEffect->GetTarget(),
                                                                          cSrcRect);
                    }

                    if (SUCCEEDED(retVal))
                    {
                        PXRect cDstRect;
                        cDstRect = m_pEffect->GetDstRect();
                        if (m_bDstChanges)
                        {
                            cDstRect.InterpolateRect(ulTime, m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                     m_pEffect->GetStartDstRect(), m_pEffect->GetDstRect());
                            HX_RELEASE(m_pDstImage);
                            retVal = m_pImageManager->GetDisplaySubImage(&m_pDstImage, cDstRect);
                        }

                        if (SUCCEEDED(retVal))
                        {
                            HXxRect cOldRestoreRect;
                            if (m_bRestoreDst)
                            {
                                // Copy the old dest rect back - first we get a display
                                // subimage rect and then copy it.
                                PXImage* pTmp = NULL;
                                retVal        = m_pImageManager->GetDisplaySubImage(&pTmp, m_cRestoreRect);
                                if (SUCCEEDED(retVal))
                                {
                                    retVal = pTmp->CopyFrom(m_pPrevDst);
                                    if (SUCCEEDED(retVal))
                                    {
                                        // Now before we do the new draw we save what's under the
                                        // rect we're about to draw on
                                        //
                                        // First save the old restore rect, so we can resolve the
                                        // damage rect later and update the restore rect
                                        cOldRestoreRect       = m_cRestoreRect;
                                        m_cRestoreRect.left   = cDstRect.GetX();
                                        m_cRestoreRect.top    = cDstRect.GetY();
                                        m_cRestoreRect.right  = cDstRect.GetX() + cDstRect.GetWidth();
                                        m_cRestoreRect.bottom = cDstRect.GetY() + cDstRect.GetHeight();
                                        // Now setup our scratch image to handle the new size of the
                                        // the restore rect. It's very important that we tell the Create()
                                        // method FALSE for the last argument, for this forces the
                                        // image to reuse the same buffer (which we know is big enough).
                                        // This just makes for efficient use of memory.
                                        retVal = m_pPrevDst->Create(HXxRECT_WIDTH(m_cRestoreRect),
                                                                    HXxRECT_HEIGHT(m_cRestoreRect),
                                                                    m_pImageManager->GetDefaultBitsPerPixel(),
                                                                    m_pImageManager->GetDefaultPixelFormat(),
                                                                    m_pImageManager->GetDefaultRowsInverted(),
                                                                    FALSE); // Use the same buffer!!
                                        if (SUCCEEDED(retVal))
                                        {
                                            HX_RELEASE(pTmp);
                                            retVal = m_pImageManager->GetDisplaySubImage(&pTmp, m_cRestoreRect);
                                            if (SUCCEEDED(retVal))
                                            {
                                                retVal = m_pPrevDst->CopyFrom(pTmp);
                                            }
                                        }
                                    }
                                }
                                HX_RELEASE(pTmp);
                            }

                            if (SUCCEEDED(retVal))
                            {
                                retVal = SizeAspectCopy(m_pSrcImage,
                                                        m_pDstImage,
                                                        m_pEffect->GetAspectFlag(),
                                                        m_pImageManager->GetBackgroundColor());

                                if (SUCCEEDED(retVal))
                                {
                                    // Set the damaged display flag
                                    m_bDisplayDamaged = TRUE;
                                    // Set the damage rect
                                    m_pDstImage->GetSubRect(m_cDamageRect);

                                    if (m_bRestoreDst)
                                    {
                                        m_cDamageRect.left   = PXMIN(m_cDamageRect.left,   cOldRestoreRect.left);
                                        m_cDamageRect.top    = PXMIN(m_cDamageRect.top,    cOldRestoreRect.top);
                                        m_cDamageRect.right  = PXMAX(m_cDamageRect.right,  cOldRestoreRect.right);
                                        m_cDamageRect.bottom = PXMAX(m_cDamageRect.bottom, cOldRestoreRect.bottom);
                                    }
                                }
                            }
                        }
                    }

                    // Update the last exe time
                    m_ulLastExeTime = ulTime;
                }
            }
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

    if (FAILED(retVal))
    {
        HX_ASSERT(FALSE);
    }

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

    return retVal;
}

HX_RESULT PXViewchangeEffectSession::SizeAspectCopy(PXImage* pSrcImage, PXImage* pDstImage,
                                                    BOOL bAspectFlag, UINT32 ulBackgroundColor)
{
    HX_RESULT retVal = HXR_OK;

    if (pSrcImage && pDstImage)
    {
        if (bAspectFlag)
        {
            // We have to preserve aspect ratio, so we need to resolve the rectangles
            HXxRect cSrc;
            pSrcImage->GetSubRect(cSrc);
            HXxRect cDst;
            pDstImage->GetSubRect(cDst);

            HXxRect cNewDst;
            HXxRect cFill1;
            HXxRect cFill2;
            retVal = m_pImageManager->ResolveAspectRatio(cSrc, cDst, cNewDst, cFill1, cFill2, TRUE);
            if (SUCCEEDED(retVal))
            {
                if (HXxRECT_WIDTH(cFill1) > 0 && HXxRECT_HEIGHT(cFill1) > 0)
                {
                    PXImage cTmp;
                    retVal = cTmp.CreateSubImage(pDstImage, cFill1);
                    if (SUCCEEDED(retVal))
                    {
                        retVal = cTmp.Fill32(ulBackgroundColor);
                    }
                }

                if (SUCCEEDED(retVal))
                {
                    if (HXxRECT_WIDTH(cNewDst) > 0 && HXxRECT_HEIGHT(cNewDst) > 0)
                    {
                        PXImage cTmpDst;
                        retVal = cTmpDst.CreateSubImage(pDstImage, cNewDst);
                        if (SUCCEEDED(retVal))
                        {
                            if (cTmpDst.SameSize(pSrcImage))
                            {
                                retVal = cTmpDst.CopyFrom(pSrcImage);
                            }
                            else
                            {
                                retVal = cTmpDst.ChangeSizeFromNN(pSrcImage);
                            }
                        }
                    }

                    if (SUCCEEDED(retVal))
                    {
                        if (HXxRECT_WIDTH(cFill2) > 0 && HXxRECT_HEIGHT(cFill2) > 0)
                        {
                            PXImage cTmp;
                            retVal = cTmp.CreateSubImage(pDstImage, cFill2);
                            if (SUCCEEDED(retVal))
                            {
                                retVal = cTmp.Fill32(ulBackgroundColor);
                            }
                        }
                    }
                }
            }
        }
        else
        {
            // We don't have to preserve aspect ratio, so we simply
            // need to copy if they are the same size and ChangeSize
            // if they are not
            if (pDstImage->SameSize(pSrcImage))
            {
                retVal = pDstImage->CopyFrom(pSrcImage);
            }
            else
            {
                retVal = pDstImage->ChangeSizeFromNN(pSrcImage);
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

IMPLEMENT_COM_CREATE_FUNCS(PXSimpleViewchangeEffectSession);

HX_RESULT PXSimpleViewchangeEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                                PXEffect*          pEffect,
                                                PXImageManager*    pImageManager,
                                                IHXErrorMessages* pErrorMessages)
{
    // Call the super-class' Init()
    HX_RESULT retVal = PXEffectSession::Init(pEffectsManager,
                                             pEffect,
                                             pImageManager,
                                             pErrorMessages);
    if (SUCCEEDED(retVal))
    {
        // Make sure this is a viewchange effect
        if (pEffect->GetEffectType() == PXEffect::kEffectTypeViewChange)
        {
            PXImage* pTargetImage = NULL;
            retVal                = m_pImageManager->GetImage(m_pEffect->GetTarget(), &pTargetImage);
            if (SUCCEEDED(retVal))
            {
                // Adjust the starting src rect
                PXRect cRect;
                cRect = m_pEffect->GetStartSrcRect();
                cRect.AdjustForZeroValues(pTargetImage->GetWidth(), pTargetImage->GetHeight());
                cRect.AdjustForOvershoot(pTargetImage->GetWidth(), pTargetImage->GetHeight());
                m_pEffect->SetStartSrcRect(cRect);

                // Adjust the starting dst rect
                cRect = m_pEffect->GetStartDstRect();
                cRect.AdjustForZeroValues(m_pImageManager->GetDisplayWidth(),
                                          m_pImageManager->GetDisplayHeight());
                cRect.AdjustForOvershoot(m_pImageManager->GetDisplayWidth(),
                                         m_pImageManager->GetDisplayHeight());
                m_pEffect->SetStartDstRect(cRect);

                // Now we need to determine if we need to update the previous dst
                if (!(m_pEffect->GetStartDstRect() == m_pEffect->GetDstRect()) &&
                    !m_pEffect->GetDstRect().Contains(m_pEffect->GetStartDstRect()))
                {
                    // Yes, we will need to restore the dest rect, so 
                    // set the flag
                    m_bRestoreDst = TRUE;

                    // Compute the biggest size the restore image needs to be
                    INT32 lStartW = m_pEffect->GetStartDstRect().GetWidth();
                    INT32 lStartH = m_pEffect->GetStartDstRect().GetHeight();
                    INT32 lEndW   = m_pEffect->GetDstRect().GetWidth();
                    INT32 lEndH   = m_pEffect->GetDstRect().GetHeight();
                    INT32 lMaxW   = PXMAX(lStartW, lEndW);
                    INT32 lMaxH   = PXMAX(lStartH, lEndH);
                    // Get a scratch image which can handle this size
                    retVal = m_pImageManager->GetScratchImage(&m_pPrevDst, lMaxW, lMaxH);
                    if (SUCCEEDED(retVal))
                    {
                        // Set the initial restore rect
                        m_cRestoreRect.left   = m_pEffect->GetStartDstRect().GetX();
                        m_cRestoreRect.top    = m_pEffect->GetStartDstRect().GetY();
                        m_cRestoreRect.right  = m_pEffect->GetStartDstRect().GetX() +
                                                m_pEffect->GetStartDstRect().GetWidth();
                        m_cRestoreRect.bottom = m_pEffect->GetStartDstRect().GetY() +
                                                m_pEffect->GetStartDstRect().GetHeight();
                        // Now set up the image for the initial restore size
                        retVal = m_pPrevDst->Create(HXxRECT_WIDTH(m_cRestoreRect),
                                                    HXxRECT_HEIGHT(m_cRestoreRect),
                                                    m_pImageManager->GetDefaultBitsPerPixel(),
                                                    m_pImageManager->GetDefaultPixelFormat(),
                                                    m_pImageManager->GetDefaultRowsInverted(),
                                                    FALSE); // Use the same buffer!!
                        if (SUCCEEDED(retVal))
                        {
                            retVal = m_pPrevDst->Fill32(m_pImageManager->GetBackgroundColor());
                        }
                    }
                }

                if (SUCCEEDED(retVal))
                {
                    // Set the initialized flag
                    m_bInitialized = TRUE;
                }
            }
            HX_RELEASE(pTargetImage);
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    if (FAILED(retVal))
    {
        Reset();
        Deallocate();
    }

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

    return retVal;
}

HX_RESULT PXSimpleViewchangeEffectSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (!m_bFinished)
        {
            if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime))
            {
                // Do the maxfps check
                BOOL bDoEffect = MaxFramesPerSecondCheck(ulTime);

                // Check if this is the last time
                if (IsTimeEqualOrLater(m_pEffect->GetEnd(), ulTime))
                {
                    m_bFinished = TRUE;
                    bDoEffect   = TRUE;
                    ulTime      = m_pEffect->GetEnd();
                }

                if (bDoEffect)
                {
                    // Compute the current dst rect
                    PXRect cCurDstRect;
                    cCurDstRect.InterpolateRect(ulTime, m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                m_pEffect->GetStartDstRect(), m_pEffect->GetDstRect());

                    // If we need to restore the old dest, then do it now
                    HXxRect cOldRestoreRect;
                    if (m_bRestoreDst)
                    {
                        // Copy the old dest rect back - first we get a display
                        // subimage rect and then copy it.
                        PXImage* pTmp = NULL;
                        retVal        = m_pImageManager->GetDisplaySubImage(&pTmp, m_cRestoreRect);
                        if (SUCCEEDED(retVal))
                        {
                            retVal = pTmp->CopyFrom(m_pPrevDst);
                            if (SUCCEEDED(retVal))
                            {
                                // Now before we do the new draw we save what's under the
                                // rect we're about to draw on
                                //
                                // First save the old restore rect, so we can resolve the
                                // damage rect later and update the restore rect
                                cOldRestoreRect       = m_cRestoreRect;
                                m_cRestoreRect.left   = cCurDstRect.GetX();
                                m_cRestoreRect.top    = cCurDstRect.GetY();
                                m_cRestoreRect.right  = cCurDstRect.GetX() + cCurDstRect.GetWidth();
                                m_cRestoreRect.bottom = cCurDstRect.GetY() + cCurDstRect.GetHeight();
                                // Now setup our scratch image to handle the new size of the
                                // the restore rect. It's very important that we tell the Create()
                                // method FALSE for the last argument, for this forces the
                                // image to reuse the same buffer (which we know is big enough).
                                // This just makes for efficient use of memory.
                                retVal = m_pPrevDst->Create(HXxRECT_WIDTH(m_cRestoreRect),
                                                            HXxRECT_HEIGHT(m_cRestoreRect),
                                                            m_pImageManager->GetDefaultBitsPerPixel(),
                                                            m_pImageManager->GetDefaultPixelFormat(),
                                                            m_pImageManager->GetDefaultRowsInverted(),
                                                            FALSE); // Use the same buffer!!
                                if (SUCCEEDED(retVal))
                                {
                                    HX_RELEASE(pTmp);
                                    retVal = m_pImageManager->GetDisplaySubImage(&pTmp, m_cRestoreRect);
                                    if (SUCCEEDED(retVal))
                                    {
                                        retVal = m_pPrevDst->CopyFrom(pTmp);
                                    }
                                }
                            }
                        }
                        HX_RELEASE(pTmp);
                    }

                    if (SUCCEEDED(retVal))
                    {
                        // Compute the current src rect
                        PXRect cCurSrcRect;
                        cCurSrcRect.InterpolateRect(ulTime, m_pEffect->GetStart(), m_pEffect->GetEnd(),
                                                    m_pEffect->GetStartSrcRect(), m_pEffect->GetSrcRect());
                        // Get the current src image
                        PXImage* pCurSrcImg = NULL;
                        retVal = m_pImageManager->GetPresentationSubImage(&pCurSrcImg,
                                                                          m_pEffect->GetTarget(),
                                                                          cCurSrcRect);
                        if (SUCCEEDED(retVal))
                        {
                            // Now we adjust for aspect ratio if necessary
                            if (m_pEffect->GetAspectFlag())
                            {
                                // We have to adjust for preserving aspect ratio, so we
                                // will change the dst rect and leave the src rect the same
                                PXRect cNewDstRect;
                                PXRect cFill1;
                                PXRect cFill2;
                                retVal = PXImageManager::ResolveAspectRatio(cCurSrcRect, cCurDstRect,
                                                                            cNewDstRect, cFill1, cFill2, FALSE);
                                if (SUCCEEDED(retVal))
                                {
                                    // Do the first fill
                                    if (cFill1.GetWidth() > 0 && cFill1.GetHeight() > 0)
                                    {
                                        PXImage* pFill1Img = NULL;
                                        retVal = m_pImageManager->GetDisplaySubImage(&pFill1Img, cFill1);
                                        if (SUCCEEDED(retVal))
                                        {
                                            retVal = pFill1Img->Fill32(m_pImageManager->GetBackgroundColor());
                                        }
                                        HX_RELEASE(pFill1Img);
                                    }

                                    // Copy src to dst
                                    if (SUCCEEDED(retVal))
                                    {
                                        if (cNewDstRect.GetWidth() > 0 && cNewDstRect.GetHeight() > 0)
                                        {
                                            PXImage* pNewDstImg = NULL;
                                            retVal = m_pImageManager->GetDisplaySubImage(&pNewDstImg, cNewDstRect);
                                            if (SUCCEEDED(retVal))
                                            {
                                                if (pNewDstImg->SameSize(pCurSrcImg))
                                                {
                                                    retVal = pNewDstImg->CopyFrom(pCurSrcImg);
                                                }
                                                else
                                                {
                                                    retVal = pNewDstImg->ChangeSizeFromNN(pCurSrcImg);
                                                }
                                            }
                                            HX_RELEASE(pNewDstImg);
                                        }
                                    }

                                    // Do the second fill
                                    if (SUCCEEDED(retVal))
                                    {
                                        if (cFill2.GetWidth() > 0 && cFill2.GetHeight() > 0)
                                        {
                                            PXImage* pFill2Img = NULL;
                                            retVal = m_pImageManager->GetDisplaySubImage(&pFill2Img, cFill2);
                                            if (SUCCEEDED(retVal))
                                            {
                                                retVal = pFill2Img->Fill32(m_pImageManager->GetBackgroundColor());
                                            }
                                            HX_RELEASE(pFill2Img);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // We don't have to worry about aspect ratio
                                PXImage* pCurDstImg = NULL;
                                retVal = m_pImageManager->GetDisplaySubImage(&pCurDstImg, cCurDstRect);
                                if (SUCCEEDED(retVal))
                                {
                                    if (pCurDstImg->SameSize(pCurSrcImg))
                                    {
                                        retVal = pCurDstImg->CopyFrom(pCurSrcImg);
                                    }
                                    else
                                    {
                                        retVal = pCurDstImg->ChangeSizeFromNN(pCurSrcImg);
                                    }
                                }
                                HX_RELEASE(pCurDstImg);
                            }
                        }
                        HX_RELEASE(pCurSrcImg);

                        if (SUCCEEDED(retVal))
                        {
                            // Set the damaged display flag
                            m_bDisplayDamaged = TRUE;
                            // Set the damage rect
                            m_cDamageRect.left   = cCurDstRect.GetX();
                            m_cDamageRect.top    = cCurDstRect.GetY();
                            m_cDamageRect.right  = cCurDstRect.GetX() + cCurDstRect.GetWidth();
                            m_cDamageRect.bottom = cCurDstRect.GetY() + cCurDstRect.GetHeight();

                            if (m_bRestoreDst)
                            {
                                m_cDamageRect.left   = PXMIN(m_cDamageRect.left,   cOldRestoreRect.left);
                                m_cDamageRect.top    = PXMIN(m_cDamageRect.top,    cOldRestoreRect.top);
                                m_cDamageRect.right  = PXMAX(m_cDamageRect.right,  cOldRestoreRect.right);
                                m_cDamageRect.bottom = PXMAX(m_cDamageRect.bottom, cOldRestoreRect.bottom);
                            }
                        }
                    }

                    // Update the last exe time
                    m_ulLastExeTime = ulTime;
                }
            }
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

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

    return retVal;
}

IMPLEMENT_COM_CREATE_FUNCS(PXExternalEffectSession);

HX_RESULT PXExternalEffectSession::Init(PXEffectsManager*  pEffectsManager,
                                        PXEffect*          pEffect,
                                        PXImageManager*    pImageManager,
                                        IHXErrorMessages* pErrorMessages)
{
    // Call the super-class' Init()
    HX_RESULT retVal = PXEffectSession::Init(pEffectsManager,
                                             pEffect,
                                             pImageManager,
                                             pErrorMessages);
    if (SUCCEEDED(retVal))
    {
        // Make sure this is an external effect
        if (pEffect->GetEffectType() == PXEffect::kEffectTypeExternal)
        {
            HX_RELEASE(m_pStartImage);
            retVal = m_pImageManager->GetDisplaySubImage(&m_pStartImage,
                                                         pEffect->GetDstRect(),
                                                         TRUE); // need a copy
            // Get our end image (what we're fading to)
            if (SUCCEEDED(retVal))
            {
                HX_RELEASE(m_pEndImage);
                retVal = m_pImageManager->GetPresentationSubImage(&m_pEndImage,
                                                                  pEffect->GetTarget(),
                                                                  pEffect->GetSrcRect(),
                                                                  pEffect->GetDstRect(),
                                                                  pEffect->GetAspectFlag());
                if (SUCCEEDED(retVal))
                {
                    PXEffectsPackageManager* pMgr = NULL;
                    retVal                        = pEffectsManager->GetEffectsPackageManager(&pMgr);
                    if (SUCCEEDED(retVal))
                    {
                        BOOL bCmpPresent = FALSE;
                        retVal           = pMgr->IsComponentPresent((const char*) pEffect->GetExFxPackage(),
                                                                    &bCmpPresent);
                        if (SUCCEEDED(retVal))
                        {
                            if (bCmpPresent)
                            {
                                BOOL bFXPresent = FALSE;
                                retVal          = pMgr->IsEffectPresent((const char*) pEffect->GetExFxPackage(),
                                                                        (const char*) pEffect->GetExFxName(),
                                                                        &bFXPresent);
                                if (SUCCEEDED(retVal))
                                {
                                    if (bFXPresent)
                                    {
                                        IUnknown* pUnk        = NULL;
                                        BOOL      bPkgPresent = FALSE;
                                        retVal                = pMgr->GetComponent((const char*) pEffect->GetExFxPackage(),
                                                                                   &bPkgPresent,
                                                                                   &pUnk);
                                        if (SUCCEEDED(retVal))
                                        {
                                            HX_RELEASE(m_pEffectsPackage);
                                            retVal = pUnk->QueryInterface(IID_IIMFEffect, (void**) &m_pEffectsPackage);
                                            if (SUCCEEDED(retVal))
                                            {
                                                BYTE* pStart  = NULL;
                                                BYTE* pEnd    = NULL;
                                                BYTE* pDst    = NULL;
                                                INT32 lWidth  = m_pDstImage->GetWidth();
                                                INT32 lHeight = m_pDstImage->GetHeight();
                                                if (m_pDstImage->GetRowsInverted())
                                                {
                                                    pStart = m_pStartImage->GetPixel(0, lHeight - 1);
                                                    pEnd   = m_pEndImage->GetPixel(0, lHeight - 1);
                                                    pDst   = m_pDstImage->GetPixel(0, lHeight - 1);
                                                }
                                                else
                                                {
                                                    pStart = m_pStartImage->GetImageBuffer();
                                                    pEnd   = m_pEndImage->GetImageBuffer();
                                                    pDst   = m_pDstImage->GetImageBuffer();
                                                }
                                                // Now init the effect
                                                retVal = m_pEffectsPackage->InitEffect((const char*) pEffect->GetExFxName(),
                                                                                       pStart,
                                                                                       pEnd,
                                                                                       pDst,
                                                                                       lWidth,
                                                                                       lHeight,
                                                                                       m_pStartImage->GetRowStride(),
                                                                                       m_pEndImage->GetRowStride(),
                                                                                       m_pDstImage->GetRowStride(),
                                                                                       (const char*) pEffect->GetExFxData(),
                                                                                       NULL,
                                                                                       m_lHandle);
                                                if (SUCCEEDED(retVal))
                                                {
                                                    m_bInitialized = TRUE;
                                                }
                                            }
                                        }
                                        HX_RELEASE(pUnk);
                                    }
                                    else
                                    {
                                        HX_ASSERT(FALSE);
                                        retVal = HXR_FAIL;
                                    }
                                }
                            }
                            else
                            {
                                HX_ASSERT(FALSE);
                                retVal = HXR_FAIL;
                            }
                        }
                    }
                    HX_RELEASE(pMgr);
                }
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    if (FAILED(retVal))
    {
        Reset();
        Deallocate();
        HX_RELEASE(m_pEffectsPackage);
        HX_RELEASE(m_pStartImage);
        HX_RELEASE(m_pEndImage);
    }

    return retVal;
}

HX_RESULT PXExternalEffectSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (!m_bFinished)
        {
            if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime))
            {
                // Do the maxfps check
                BOOL bDoEffect = MaxFramesPerSecondCheck(ulTime);

                if (IsTimeEqualOrLater(m_pEffect->GetEnd(), ulTime))
                {
                    bDoEffect   = TRUE;
                    m_bFinished = TRUE;
                    ulTime      = m_pEffect->GetEnd();
                }

                if (bDoEffect)
                {
                    // Compute the pct done
                    UINT32 ulPctDone = 0;
                    if (m_pEffect->GetDuration())
                    {
                        ulPctDone = (ulTime - m_pEffect->GetStart()) * 100 / m_pEffect->GetDuration();
                    }
                    else
                    {
                        ulPctDone = 100;
                    }

                    // Do the external effect
                    retVal = m_pEffectsPackage->DoEffect(m_lHandle, ulPctDone);
                    if (SUCCEEDED(retVal))
                    {
                        // Since the effects interface currently doesn't have
                        // any way to pass back info about the damage rect, then
                        // we just assume it's the whole rect
                        m_bDisplayDamaged = TRUE;
                        m_pDstImage->GetSubRect(m_cDamageRect);
                    }

                    // Update the last exe time
                    m_ulLastExeTime = ulTime;
                }
            }
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

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

    return retVal;
}

IMPLEMENT_COM_CREATE_FUNCS(PXAnimationSession);

HX_RESULT PXAnimationSession::Init(PXEffectsManager*  pEffectsManager,
                                   PXEffect*          pEffect,
                                   PXImageManager*    pImageManager,
                                   IHXErrorMessages* pErrorMessages)
{
    // Call the super-class' Init()
    HX_RESULT retVal = PXEffectSession::Init(pEffectsManager,
                                             pEffect,
                                             pImageManager,
                                             pErrorMessages);
    if (SUCCEEDED(retVal))
    {
        PXImageHelper* pHelper = NULL;
        retVal                 = m_pImageManager->GetImageHelper(pEffect->GetTarget(), &pHelper);
        if (SUCCEEDED(retVal))
        {
            // Get the number of frames
            m_ulNumFrames = pHelper->GetNumFrames();
            // Allocate an array for the delay times
            HX_VECTOR_DELETE(m_pulNextFrameTime);
            m_pulNextFrameTime = new UINT32 [m_ulNumFrames];
            if (m_pulNextFrameTime)
            {
                for (UINT32 i = 0; i < m_ulNumFrames; i++)
                {
                    IHXValues* pValues = NULL;
                    retVal              = pHelper->GetFrameInfo(i, &pValues);
                    if (SUCCEEDED(retVal))
                    {
                        UINT32 ulTmp = 0;
                        retVal = pValues->GetPropertyULONG32("DelayTime", ulTmp);
                        if (SUCCEEDED(retVal))
                        {
                            ulTmp                *= 10; // convert from 1/100s to 1/1000s
                            m_pulNextFrameTime[i] = (i ? m_pulNextFrameTime[i-1] + ulTmp : ulTmp);
                        }
                    }
                    HX_RELEASE(pValues);

                    if (FAILED(retVal))
                    {
                        break;
                    }
                }
                // Set the cycle time
                m_ulCycleTime  = m_pulNextFrameTime[m_ulNumFrames - 1];
                // Check to see if our duration is indefinite. If it is, then
                // we need to set the flag in the effects manager.
                if (pEffect->GetDuration() == 0xFFFFFFFF)
                {
                    pEffectsManager->SetCheckIndefiniteDuration(TRUE);
                }
                // Set the initialized flag
                m_bInitialized = TRUE;
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
        HX_RELEASE(pHelper);
    }

    if (FAILED(retVal))
    {
        Reset();
        Deallocate();
        HX_VECTOR_DELETE(m_pulNextFrameTime);
    }

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

    return retVal;
}

HX_RESULT PXAnimationSession::Execute(UINT32 ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (m_bInitialized)
    {
        if (!m_bFinished)
        {
            if (IsTimeEqualOrLater(m_pEffect->GetStart(), ulTime))
            {
                // Compute which frame to display
                UINT32 ulFrameToDisplay = 0;
                retVal                  = ComputeFrameToDisplay(ulTime, m_pEffect->GetStart(), &ulFrameToDisplay);
                if (SUCCEEDED(retVal))
                {
                    if (ulFrameToDisplay != m_ulCurrentFrame)
                    {
                        // Get the image helper
                        PXImageHelper* pHelper = NULL;
                        retVal                 = m_pImageManager->GetImageHelper(m_pEffect->GetTarget(),
                                                                                 &pHelper);
                        if (SUCCEEDED(retVal))
                        {
                            // Here we force no skipping of frames
                            ulFrameToDisplay = m_ulCurrentFrame + 1;
                            if (ulFrameToDisplay >= pHelper->GetNumFrames())
                            {
                                ulFrameToDisplay = 0;
                            }

                            // Get the frame image
                            PXImage* pImage = NULL;
                            retVal          = pHelper->GetFrame(ulFrameToDisplay, &pImage);
                            if (SUCCEEDED(retVal))
                            {
                                // Get the frame rectangle
                                HXxRect cFrameRect;
                                retVal = pHelper->GetFrameDim(ulFrameToDisplay, cFrameRect);
                                if (SUCCEEDED(retVal))
                                {
                                    PXImage cSrcFrameImage;
                                    PXImage cDstFrameImage;
                                    BOOL    bDoCopy = FALSE;
                                    retVal = GetClippedImages(pImage, cFrameRect, m_pDstImage, m_pEffect,
                                                              &cSrcFrameImage, &cDstFrameImage, &bDoCopy);
                                    if (SUCCEEDED(retVal))
                                    {
                                        if (bDoCopy)
                                        {
                                            if (cDstFrameImage.SameSize(&cSrcFrameImage))
                                            {
                                                retVal = cDstFrameImage.CopyFromTransparent32(&cSrcFrameImage);
                                            }
                                            else
                                            {
                                                retVal = cDstFrameImage.ChangeSizeFromNNTransparent32(&cSrcFrameImage);
                                            }
                                            if (SUCCEEDED(retVal))
                                            {
                                                // Update the current frame
                                                m_ulCurrentFrame = ulFrameToDisplay;
                                                // Set the damaged display flag
                                                m_bDisplayDamaged = TRUE;
                                                // Set the damage rect
                                                cDstFrameImage.GetSubRect(m_cDamageRect);
                                                // Get the dest rect for the entire effect
                                                HXxRect cDstRect = m_pEffect->GetDstHXxRect();
                                                // Offset the damage rect by the dest rect
                                                // of the entire effect
                                                m_cDamageRect.left   += cDstRect.left;
                                                m_cDamageRect.top    += cDstRect.top;
                                                m_cDamageRect.right  += cDstRect.left;
                                                m_cDamageRect.bottom += cDstRect.top;
                                            }
                                        }
                                    }
                                }
                            }
                            HX_RELEASE(pImage);
                        }
                        HX_RELEASE(pHelper);
                    }
                }

                if (m_pEffect->GetDuration() != 0xFFFFFFFF &&
                    IsTimeEqualOrLater(m_pEffect->GetEnd(), ulTime))
                {
                    m_bFinished = TRUE;
                }
            }
        }
        else
        {
            ResetDamage();
        }
    }
    else
    {
        retVal = HXR_NOT_INITIALIZED;
    }

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

    return retVal;
}

HX_RESULT PXAnimationSession::ComputeFrameToDisplay(UINT32 ulTime, UINT32 ulStart, UINT32* pulFrameNum)
{
    HX_RESULT retVal = HXR_OK;

    if (pulFrameNum && IsTimeEqualOrLater(ulStart, ulTime))
    {
        // Set default
        *pulFrameNum = 0;

        // Compute frame number
        if (m_ulCycleTime)
        {
            UINT32 ulT    = ulTime - ulStart;
            // Modulo the cycle time
            UINT32 ulModT = ulT % m_ulCycleTime;
            // Search the next frame time list
            UINT32 ulLastTime = 0;
            for (UINT32 i = 0; i < m_ulNumFrames; i++)
            {
                if (ulModT >= ulLastTime && ulModT <= m_pulNextFrameTime[i])
                {
                    *pulFrameNum = i;
                    break;
                }
                ulLastTime = m_pulNextFrameTime[i];
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXAnimationSession::GetClippedImages(PXImage* pSrc, const HXxRect& rFrameRect,
                                               PXImage* pDst, PXEffect* pEffect,
                                               PXImage* pSrcFrame, PXImage* pDstFrame, BOOL* pbDoCopy)
{
    HX_RESULT retVal = HXR_OK;

    if (pSrc && pDst && pEffect && pSrcFrame && pDstFrame && pbDoCopy)
    {
        // Set defaults
        *pbDoCopy = FALSE;
        // Convert frame rect to PXRect
        PXRect cFrame;
        cFrame.Set(rFrameRect.left, rFrameRect.top,
                   HXxRECT_WIDTH(rFrameRect), HXxRECT_HEIGHT(rFrameRect));
        // Get the src rect of the effect
        PXRect cClipSrc;
        cClipSrc = pEffect->GetSrcRect();
        // Clip the src rect by the frame rect
        cClipSrc.Clip(cFrame);
        // If the clipped src rect has a null width or height, then
        // we don't need to do anything
        if (cClipSrc.GetWidth() > 0 && cClipSrc.GetHeight() > 0)
        {
            // Now we need to make the clipped src rect relative to
            // the frame rect, since it has its own memory.
            cClipSrc.ChangeOriginTo(cFrame);
            // Now we can get the src subimage
            retVal = pSrcFrame->CreateSubImage(pSrc, cClipSrc);
            if (SUCCEEDED(retVal))
            {
                // Now we need to scale this src rect into the dst rect,
                // so we first make the clipped src rect back relative to the
                // src rect.
                cClipSrc.ChangeOriginFrom(cFrame);
                cClipSrc.ChangeOriginTo(pEffect->GetSrcRect());
                // Now scale the clipped src rect into a clipped dst rect
                cClipSrc.Scale(pEffect->GetSrcRect(), pEffect->GetDstRect());
                // Now we can create the dst image
                retVal = pDstFrame->CreateSubImage(pDst, cClipSrc);
                if (SUCCEEDED(retVal))
                {
                    // Now we can set the flag that says to do the copy
                    *pbDoCopy = TRUE;
                }
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;

}

