/*******************************************************************************
* DTBase.cpp *
*------------*
*   Description:
*    This module contains the CDXBaseNTo1 transform
*-------------------------------------------------------------------------------
*  Created By: Edward W. Connell                            Date: 07/28/97
*  Copyright (C) 1997 Microsoft Corporation
*  All Rights Reserved
*
*-------------------------------------------------------------------------------
*  Revisions:
*
*******************************************************************************/
//--- Additional includes
#include <DXTrans.h>
#include "DTBase.h"
#include "new.h"

//--- Initialize static member of debug scope class
#ifdef _DEBUG
CDXTDbgFlags CDXTDbgScope::m_DebugFlags;
#endif

//--- This should only be used locally in this file. We duplicated this GUID
//    value to avoid having to include DDraw.
static const IID IID_IDXDupDirectDraw =
    { 0x6C14DB80,0xA733,0x11CE, { 0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 } };

static const IID IID_IDXDupDDrawSurface =
    { 0x6C14DB81,0xA733,0x11CE, { 0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 } };

static const IID IID_IDXDupDirect3DRM =
    {0x2bc49361, 0x8327, 0x11cf, {0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1 } };

static const IID IID_IDXDupDirect3DRM3 =
    {0x4516ec83, 0x8f20, 0x11d0, {0x9b, 0x6d, 0x00, 0x00, 0xc0, 0x78, 0x1b, 0xc3 } };

static const IID IID_IDXDupDirect3DRMMeshBuilder3 =
    { 0x4516ec82, 0x8f20, 0x11d0, { 0x9b, 0x6d, 0x00, 0x00, 0xc0, 0x78, 0x1b, 0xc3} };

HRESULT CDXDataPtr::Assign(BOOL bMesh, IUnknown * pObject, IDXSurfaceFactory *pSurfaceFactory)
{
    HRESULT hr = S_OK;
    if (pObject)
    {
        IUnknown *pNative = NULL;
        if (!bMesh)
        {
            //--- Try to get a DX surface
            hr = pObject->QueryInterface( IID_IDXSurface, (void **)&pNative );
            if( FAILED( hr ) )
            {
                IDirectDrawSurface *pSurf;
                //--- Try to get a DDraw surface
                hr = pObject->QueryInterface( IID_IDXDupDDrawSurface, (void **)&pSurf );
                if( SUCCEEDED( hr ) )
                {
                    //--- Create a DXSurface from the DDraw surface
                    hr = pSurfaceFactory->CreateFromDDSurface(
                                pSurf, NULL, 0, NULL, IID_IDXSurface,
                                (void **)&pNative );
                    pSurf->Release();
                }
            }
        }
        else // Must be a mesh builder
        {
            hr = pObject->QueryInterface(IID_IDXDupDirect3DRMMeshBuilder3, (void **)&pNative);
        }
        if (SUCCEEDED(hr))
        {
            Release();
            m_pNativeInterface = pNative;
            pObject->AddRef();
            m_pUnkOriginalObject = pObject;
            if (SUCCEEDED(pNative->QueryInterface(IID_IDXBaseObject, (void **)&m_pBaseObj)))
            {
                m_pBaseObj->GetGenerationId(&m_dwLastDirtyGenId);
                m_dwLastDirtyGenId--;
            }
            if (!bMesh)
            {   
                ((IDXSurface *)pNative)->GetPixelFormat(NULL, &m_SampleFormat);
            }
        }
        else
        {
            if (hr == E_NOINTERFACE)
            {
                hr = E_INVALIDARG;
            }
        }
    }
    else 
    {
        Release();
    }
    return hr;
} /* CDXDataPtr::Assign */

bool CDXDataPtr::IsDirty(void)
{
    if (m_pBaseObj)
    {
        DWORD dwOldId = m_dwLastDirtyGenId;
        m_pBaseObj->GetGenerationId(&m_dwLastDirtyGenId);
        return dwOldId != m_dwLastDirtyGenId;
    }
    else
    {
        return false;
    }

}

DWORD CDXDataPtr::GenerationId(void)
{
    if (m_pBaseObj)
    {
        DWORD dwGenId;
        m_pBaseObj->GetGenerationId(&dwGenId);
        return dwGenId;
    }
    else
    {
        return 0;
    }
}


bool CDXDataPtr::UpdateGenerationId(void)
{
    if (m_pBaseObj)
    {
        DWORD dwOldId = m_dwLastUpdGenId;
        m_pBaseObj->GetGenerationId(&m_dwLastUpdGenId);
        return dwOldId != m_dwLastUpdGenId;
    }
    else
    {
        return false;
    }
} /* CDXDataPtr::UpdateGenerationId */

ULONG CDXDataPtr::ObjectSize(void)
{
    ULONG ulSize = 0;
    if (m_pBaseObj)
    {
        m_pBaseObj->GetObjectSize(&ulSize);
    }
    return ulSize;    
}

/*****************************************************************************
* CDXBaseNTo1::CDXBaseNTo1 *
*--------------------------*
*   Description:
*       Constructor
*-----------------------------------------------------------------------------
*   Created By: Ed Connell                            Date: 07/28/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
CDXBaseNTo1::CDXBaseNTo1() :
    m_aInputs(NULL),
    m_ulNumInputs(0),
    m_ulNumProcessors(1),   // Default to one until task manager is set
    m_dwGenerationId(1),
    m_dwCleanGenId(0),
    m_Duration(1.0f),
    m_StepResolution(0.0f),
    m_Progress(0.0f),
    m_dwBltFlags(0),
    m_bPickDoneByBase(false),
    m_bInMultiThreadWorkProc(FALSE),
    m_fQuality(0.5f),   // Default to normal quality.
    //  Wait forever before timing out on a lock by default
    m_ulLockTimeOut(INFINITE),
    //
    //  Override these flags if your object does not support one or more of these options.
    //  Typically, 3-D effects should set this member to 0.
    //
    m_dwMiscFlags(DXTMF_BLEND_WITH_OUTPUT | DXTMF_DITHER_OUTPUT |
                  DXTMF_BLEND_SUPPORTED | DXTMF_DITHER_SUPPORTED | DXTMF_BOUNDS_SUPPORTED | DXTMF_PLACEMENT_SUPPORTED),
    //
    //  If your object has a different number of objects or a different number of
    //  required objects than 1, simply set these members in the body of your
    //  constructor or in FinalConstruct().  For every input that is > the number
    //  required, that input will be reported as optional.
    //
    //  If your transform takes 2 required inputs, set both to 2.
    //  If your transform takes 2 optional inputs, set MaxInputs = 2, NumInRequired = 0
    //  If your transform takes 1 required and 2 optional inputs,
    //      set MaxInputs = 2, NumInRequired = 1
    //
    //  For more complex combinations of optinal/required, you will need to override
    //  the OnSetup method of this base class, and override the methods
    //      GetInOutInfo
    //
    m_ulMaxInputs(1),
    m_ulNumInRequired(1),
    //
    //  If the intputs or output types are not surfaces then set appropriate object type
    //
    m_dwOptionFlags(0),     // Inputs and output are surfaces, don't have to be the same size
    m_ulMaxImageBands(DXB_MAX_IMAGE_BANDS),
    m_fIsSetup(false)
{
    DXTDBG_FUNC( "CDXBaseNTo1::CDXBaseNTo1" );
    //
    //  Set event handles to NULL.
    //
    memset(m_aEvent, 0, sizeof(m_aEvent));
} /* CDXBaseNTo1::CDXBaseNTo1 */

/*****************************************************************************
* CDXBaseNTo1::~CDXBaseNTo1 *
*---------------------------*
*   Description:
*       Constructor
*-----------------------------------------------------------------------------
*   Created By: Ed Connell                            Date: 07/28/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
CDXBaseNTo1::~CDXBaseNTo1()
{
    DXTDBG_FUNC( "CDXBaseNTo1::~CDXBaseNTo1" );
    _ReleaseReferences();
    delete[] m_aInputs;

    //--- Release event objects
    for(ULONG i = 0; i < DXB_MAX_IMAGE_BANDS; ++i )
    {
        if( m_aEvent[i] ) ::CloseHandle( m_aEvent[i] );
    }
} /* CDXBaseNTo1::~CDXBaseNTo1 */


/*****************************************************************************
* CDXBaseNTo1::_ReleaseRefernces *
*--------------------------------*
*   Description:
*       Releases all references to input and output objects
*-----------------------------------------------------------------------------
*   Created By: RAL
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
void CDXBaseNTo1::_ReleaseReferences()
{
    //--- Release data objects
    if( m_aInputs )
    {
        for( ULONG i = 0; i < m_ulNumInputs; ++i )
        {
            m_aInputs[i].Release();
        }
    }

    m_Output.Release();

    m_fIsSetup = false;
} /* CDXBaseNTo1::_ReleaseRefernces */



STDMETHODIMP CDXBaseNTo1::GetGenerationId(ULONG *pGenerationId)
{
    DXTDBG_FUNC( "CDXBaseNTo1::GetGenerationId" );
    if (DXIsBadWritePtr(pGenerationId, sizeof(*pGenerationId)))
    {
        return E_POINTER;
    }
    Lock();
    OnUpdateGenerationId();
    *pGenerationId = m_dwGenerationId;
    Unlock();
    return S_OK;
}

STDMETHODIMP CDXBaseNTo1::IncrementGenerationId(BOOL bRefresh)
{
    DXTDBG_FUNC( "CDXBaseNTo1::IncrementGenerationId" );
    HRESULT hr = S_OK;
    Lock();
    m_dwGenerationId++;
    if (bRefresh)
    {
        //
        //  If we have any inputs or outputs, call Setup again to refresh all internal
        //  knowledge about the surfaces (formats, height or width could change, etc.)
        //
        //  Note that we need to AddRef the objects prior to calling Setup becuase the
        //  DXTransform may be the only object holding a referec
        //
        ULONG cInputs = m_ulNumInputs;
        ULONG cOutputs = 0;
        IUnknown *pOutput = m_Output.m_pUnkOriginalObject;
        if (pOutput)
        {
            cOutputs = 1;
            pOutput->AddRef();
        }
        IUnknown ** ppInputs = NULL;
        if (cInputs)
        {
            ppInputs = (IUnknown **)_alloca(m_ulNumInputs * sizeof(IUnknown *));
            for (ULONG i = 0; i < cInputs; i++)
            {
                ppInputs[i] = m_aInputs[i].m_pUnkOriginalObject;
                if (ppInputs[i]) ppInputs[i]->AddRef();
            }
        }
        if (cInputs || cOutputs)    // If we're not setup, skip this step.
        {
            hr = Setup(ppInputs, cInputs, &pOutput, cOutputs, 0);
            if (pOutput) pOutput->Release();
            for (ULONG i = 0; i < cInputs; i++)
            {
                if (ppInputs[i]) ppInputs[i]->Release();
            }
        }
    }
    Unlock();
    return hr;
}


STDMETHODIMP CDXBaseNTo1::GetObjectSize(ULONG *pcbSize)
{
    DXTDBG_FUNC( "CDXBaseNTo1::GetObjectSize" );
    HRESULT hr = S_OK;
    if (DXIsBadWritePtr(pcbSize, sizeof(*pcbSize)))
    {
        hr = E_POINTER;
    }
    else
    {
	Lock();
        *pcbSize = OnGetObjectSize();
        Unlock();
    }
    return hr;
}


void CDXBaseNTo1::_ReleaseServices(void)
{
    m_cpTransFact.Release();
    m_cpSurfFact.Release();
    m_cpTaskMgr.Release();
    m_cpDirectDraw.Release();
    m_cpDirect3DRM.Release();
}

//
//  The documentation for SetSite indicates that it is invaid to return
//  an error from this function, even if the site does not support the
//  functionality we want.  So, even if there is no service provider, or
//  the required services are not available, we will return S_OK.
//
STDMETHODIMP CDXBaseNTo1::SetSite(IUnknown * pUnkSite)
{
    DXTDBG_FUNC( "CDXBaseNTo1::SetSite" );
    HRESULT hr = S_OK;
    Lock();
    m_cpUnkSite = pUnkSite;
    _ReleaseServices();
    if (pUnkSite)
    {
        if (DXIsBadInterfacePtr(pUnkSite))
        {
            hr = E_INVALIDARG;
        }
        else
        {
            HRESULT hr2;
            hr2 = pUnkSite->QueryInterface(IID_IDXTransformFactory, (void **)&m_cpTransFact);
	    if (SUCCEEDED(hr2))
	    {
                //
                //  Allocate memory for inputs if necessary
                //
                if (m_aInputs == NULL && m_ulMaxInputs)
                {
                    m_aInputs  = new CDXDataPtr[m_ulMaxInputs];
                    if (!m_aInputs)
                    {
                        _ASSERT(TRUE);
                        hr2 = E_OUTOFMEMORY;
                    }
                }
                hr2 = m_cpTransFact->QueryService( SID_SDXSurfaceFactory, IID_IDXSurfaceFactory, (void **)&m_cpSurfFact);
                if (SUCCEEDED(hr2))
                {
                    hr2 = m_cpTransFact->QueryService( SID_SDXTaskManager, IID_IDXTaskManager, (void **)&m_cpTaskMgr);
                }
                if (SUCCEEDED(hr2))
                {
                    m_cpTaskMgr->QueryNumProcessors(&m_ulNumProcessors);
                    if (m_ulMaxImageBands && (m_dwOptionFlags & (DXBOF_INPUTS_MESHBUILDER | DXBOF_OUTPUT_MESHBUILDER)) == 0)
                    {
                        for (ULONG i = 0; SUCCEEDED(hr2) && i < m_ulMaxImageBands; i++)
                        {
                            //
                            // In theory we could get back here after failing to create an event, or
                            // by getting a new site, so make sure it's non-null before creating one.
                            //
                            if (m_aEvent[i] == NULL)
                            {
                                m_aEvent[i] = ::CreateEvent(NULL, true, false, NULL);
                                if (m_aEvent[i] == NULL)
                                {
                                    hr2 = E_OUTOFMEMORY;
                                }
                            }

                        }
                    }
                }
                if (SUCCEEDED(hr2))
                {
                    hr2 = m_cpTransFact->QueryService(SID_SDirectDraw, IID_IDXDupDirectDraw, (void**)&m_cpDirectDraw);
                }
                if (SUCCEEDED(hr2) && 
                    (m_dwOptionFlags & (DXBOF_INPUTS_MESHBUILDER | DXBOF_OUTPUT_MESHBUILDER)))
                {
                    hr2 = m_cpTransFact->QueryService(SID_SDirect3DRM, IID_IDXDupDirect3DRM3, (void **)&m_cpDirect3DRM);
                }
                if (FAILED(hr2))
                {
                    _ASSERT(TRUE);
                    _ReleaseServices();
                }
            }
        }
    }
    Unlock();
    return hr;
}


STDMETHODIMP CDXBaseNTo1::GetSite(REFIID riid, void **ppv)
{
    DXTDBG_FUNC( "CDXBaseNTo1::GetSite" );
    HRESULT hr = S_OK;
    if( DXIsBadWritePtr(ppv, sizeof(*ppv)) )
    {
        hr = E_POINTER;
    }
    else
    {
        Lock();
        if (m_cpUnkSite)
        {
            hr = m_cpUnkSite->QueryInterface(riid, ppv);
        }
        else
        {
            *ppv = NULL;
            hr = E_FAIL;    // This is the proper documented return code
                            // for this interface if no service provider.
        }
        Unlock();
    }
    return hr;
} 


void CDXBaseNTo1::_UpdateBltFlags(void)
{
    m_dwBltFlags = 0;
    if ((m_dwOptionFlags & DXBOF_OUTPUT_MESHBUILDER) == 0)
    {
        if (m_dwMiscFlags & DXTMF_BLEND_WITH_OUTPUT) 
        {
            if ((m_dwOptionFlags & DXBOF_INPUTS_MESHBUILDER) || m_ulNumInputs == 0)
            {
                m_dwBltFlags |= DXBOF_DO_OVER;
            }
            else
            {
                for(ULONG i = 0; i < m_ulNumInputs; ++i )
                {
                    if (InputSampleFormat(i) & DXPF_TRANSPARENCY)
                    {
                        m_dwBltFlags |= DXBOF_DO_OVER;
                        break;
                    }
                }
            }
        }
        //
        //  Set the dither flag to true only if output error is > at least one input
        //
        if (m_dwMiscFlags & DXTMF_DITHER_OUTPUT)
        {
            ULONG OutputErr = (OutputSampleFormat() & DXPF_ERRORMASK);
            if (OutputErr)
            {
                if (m_ulNumInputs)
                {
                    for(ULONG i = 0; i < m_ulNumInputs; ++i )
                    {
                        if (InputSurface(i) && (ULONG)(InputSampleFormat(i) & DXPF_ERRORMASK) < OutputErr)
                        {
                            m_dwBltFlags |= DXBOF_DITHER;
                            break;
                        }
                    }
                }
                else
                {
                    //
                    // If output has no error then don't set dither in blt flags
                    //  
                    if (OutputErr)
                    {
                        m_dwBltFlags |= DXBOF_DITHER; 
                    }
                }
            }
        }
    }
}



/*****************************************************************************
* CDXBaseNTo1::Setup *
*--------------------*
*   Description:
*       The Setup method is used to perform any required one-time setup
*   before the Execute method is called. Single surfaces or SurfaceSets may
*   be used as arguments in any combination. 
*   If punkOutputs is NULL, Execute will allocate an output result of the
*   appropriate size and return it.
*   if punkInputs and punkOutputs are NULL and it is a quick setup, the current
*   input and output objects are released.
*-----------------------------------------------------------------------------
*   Created By: Ed Connell                            Date: 07/28/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::Setup( IUnknown * const * punkInputs, ULONG ulNumInputs,
                                 IUnknown * const * punkOutputs, ULONG ulNumOutputs, DWORD dwFlags )
{
    DXTDBG_FUNC( "CDXBaseNTo1::Setup" );
    //--- Lock object so state cannot change during setup
    DXAUTO_OBJ_LOCK
    HRESULT hr = S_OK;
    ULONG i;

    //
    //  Early out for null setup.  Forget about all other param validation, just do it.
    //  
    if (ulNumInputs == 0 && ulNumOutputs == 0)
    {
        _ReleaseReferences();
        OnReleaseObjects();
        return hr;
    }

    //--- Validate Params
    //--- Make sure we have a reference to the transform factory
    if( !m_cpTransFact )
    {
        hr = DXTERR_UNINITIALIZED;
        DXTDBG_MSG0( _CRT_ERROR, "\nTransform has not been initialized" );
    }
    else
    {
        //
        //  We know that if we have a transform factory that we must also have
        //  allocated m_aInputs since this is done on SetSite to avoid work during
        //  each setup.
        //
        _ASSERT(m_aInputs || m_ulMaxInputs == 0);
        if( dwFlags ||              // No flags are valid
            ulNumOutputs != 1 ||
            ulNumInputs < m_ulNumInRequired ||
            ulNumInputs > m_ulMaxInputs ||
            (ulNumInputs && DXIsBadReadPtr( punkInputs , sizeof( *punkInputs ) * ulNumInputs )) ||
            DXIsBadReadPtr(punkOutputs, sizeof(*punkOutputs)) ||
            DXIsBadInterfacePtr(punkOutputs[0]))
        {
            hr = E_INVALIDARG;
            DXTDBG_MSG0( _CRT_ERROR, "\nTransform setup with invalid args" );
        }
        else
        {
            for( i = 0; i < ulNumInputs; ++i )
            {
                if((punkInputs[i] && DXIsBadInterfacePtr(punkInputs[i])) ||
                    (punkInputs[i] == NULL && i < m_ulNumInRequired))
                {
                    hr = E_INVALIDARG;
                    DXTDBG_MSG0( _CRT_ERROR, "\nTransform setup with invalid args" );
                    break;
                }
            }        
        }
    }

    //--- Allocate slots for input data object pointers
    if( SUCCEEDED( hr ) )
    {
        //--- Release data objects
        _ReleaseReferences();
        m_ulNumInputs = ulNumInputs;
    }

    //
    //  Assign 
    //
    for( i = 0; SUCCEEDED(hr) && i < m_ulNumInputs; ++i )
    {
        hr = m_aInputs[i].Assign((m_dwOptionFlags & DXBOF_INPUTS_MESHBUILDER), punkInputs[i], m_cpSurfFact);
    }

    if( SUCCEEDED(hr) )
    {
        hr = m_Output.Assign((m_dwOptionFlags & DXBOF_OUTPUT_MESHBUILDER), punkOutputs[0], m_cpSurfFact);
    }   

    if (SUCCEEDED(hr) && (m_dwOptionFlags & DXBOF_SAME_SIZE_INPUTS))
    {
        hr = _MakeInputsSameSize();
    }

    if (SUCCEEDED(hr))
    {
        _UpdateBltFlags();      // Do this before calling OnSetup...
        hr = OnSetup(dwFlags);
    }
    
    if (FAILED(hr))
    {
        _ReleaseReferences();
        OnReleaseObjects();
        DXTDBG_MSG0( _CRT_ERROR, "\nTransform setup failed" );
    }
    else
    {
        m_fIsSetup = true;
    }

    return hr;
} /* CDXBaseNTo1::Setup */


/*****************************************************************************
* CDXBaseNTo1::_MakeInputsSameSize *
*----------------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                            Date: 03/31/98
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/

HRESULT CDXBaseNTo1::_MakeInputsSameSize(void)
{
    _ASSERT((m_dwOptionFlags & DXBOF_INPUTS_MESHBUILDER) == 0);

    HRESULT hr = S_OK;
    if (m_ulNumInputs > 1)      // No need to do this for just one input!
    {
        CDXDBnds SurfBnds(false);
        CDXDBnds Union(true);
        ULONG i;
        for (i = 0; SUCCEEDED(hr) && i < m_ulNumInputs; i++)
        {
            if (InputSurface(i))
            {
                hr = SurfBnds.SetToSurfaceBounds(InputSurface(i));
                Union |= SurfBnds;
            }
        }
        for (i = 0; SUCCEEDED(hr) && i < m_ulNumInputs; i++)
        {
            if (InputSurface(i))
            {
                hr = SurfBnds.SetToSurfaceBounds(InputSurface(i));
                if (SUCCEEDED(hr) && SurfBnds != Union)
                {
                    IDXSurfaceModifier *pSurfMod;
                    hr = ::CoCreateInstance(CLSID_DXSurfaceModifier, NULL, CLSCTX_INPROC,
                                            IID_IDXSurfaceModifier, (void **)&pSurfMod);
                    if (SUCCEEDED(hr))
                    {
                        POINT p;
                        p.x = p.y = 0;
                        if (m_dwOptionFlags & DXBOF_CENTER_INPUTS)
                        {
                            p.x = (Union.Width() - SurfBnds.Width()) / 2;
                            p.y = (Union.Height() - SurfBnds.Height()) / 2;
                        }
                        pSurfMod->SetForeground(InputSurface(i), FALSE, &p);
                        pSurfMod->SetBounds(&Union);
                        InputSurface(i)->Release();
                        pSurfMod->QueryInterface(IID_IDXSurface, (void **)&(m_aInputs[i].m_pNativeInterface));
                        ((IDXSurface *)m_aInputs[i].m_pNativeInterface)->GetPixelFormat(NULL, &m_aInputs[i].m_SampleFormat);
                        pSurfMod->Release();
                    }
                }
            }
        }
    }
    return hr;
}


/*****************************************************************************
* CDXBaseNTo1::Execute *
*----------------------*
*   Description:
*       The Execute method is used to walk the inputs/outputs and break up the
*   work into suitably sized pieces to spread symetrically accross the available
*   processors in the system.
*-----------------------------------------------------------------------------
*   Created By: Ed Connell                            Date: 07/28/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::
    Execute( const GUID* pRequestID, const DXBNDS *pClipBnds, const DXVEC *pPlacement )
{
    DXTDBG_FUNC( "CDXBaseNTo1::Execute" );
    //--- Lock object so state cannot change during execution
    DXAUTO_OBJ_LOCK
    HRESULT hr = S_OK;

    //--- Check args
    if( !HaveOutput() )
    {
        DXTDBG_MSG0( _CRT_ERROR, "\nTransform has not been initialized" );
        return DXTERR_UNINITIALIZED;
    }

    if (m_ulMaxImageBands == 0 ||
        (m_dwOptionFlags & (DXBOF_INPUTS_MESHBUILDER | DXBOF_OUTPUT_MESHBUILDER)))
    {
        if ((pClipBnds && (m_dwMiscFlags & DXTMF_BOUNDS_SUPPORTED) == 0) ||
            (pPlacement && (m_dwMiscFlags & DXTMF_PLACEMENT_SUPPORTED) == 0) )
        {
            DXTDBG_MSG0( _CRT_ERROR, "\nTransform setup with invalid args" );
            return E_INVALIDARG;
        }
        return OnExecute( pRequestID, pClipBnds, pPlacement );
    }

    //--- Banded image working variables
    CDXTWorkInfoNTo1 WI;

    if ((pClipBnds && pClipBnds->eType != DXBT_DISCRETE) ||
        (pPlacement && pPlacement->eType != DXBT_DISCRETE))
    {
        hr = E_INVALIDARG;
        DXTDBG_MSG0( _CRT_ERROR, "\nTransform setup with invalid args" );
    }
    else
    {
        hr = MapBoundsIn2Out( NULL, 0, 0, &WI.DoBnds );
        if( hr == S_OK )
        {
            hr = WI.OutputBnds.SetToSurfaceBounds(OutputSurface());
            if (hr == S_OK)
            {
                hr = DXClipToOutputWithPlacement(WI.DoBnds, (CDXDBnds *)pClipBnds, WI.OutputBnds, (CDXDVec *)pPlacement);
            }
        }
    }

    //--- Check for clipping early exit
    if( hr != S_OK )
    {
        return hr;
    }

    //=== Process ====================================================
    _ASSERT(m_ulMaxImageBands <= DXB_MAX_IMAGE_BANDS);
    ULONG ulNumBandsToDo = m_ulNumProcessors;
    if( ulNumBandsToDo > 1 )
    {
        ulNumBandsToDo = 1 + ((WI.OutputBnds.Width() * WI.OutputBnds.Height()) / 0x1000);
        if (ulNumBandsToDo > m_ulMaxImageBands)
        {
            ulNumBandsToDo = m_ulMaxImageBands;
        }
        if (ulNumBandsToDo > m_ulNumProcessors)
        {
            ulNumBandsToDo = m_ulNumProcessors;
        }
    }
    hr = OnInitInstData(WI, ulNumBandsToDo);
    if( SUCCEEDED( hr ) )
    {
        if (ulNumBandsToDo == 1 && pRequestID == NULL)
        {
            static BOOL bContinue = TRUE;
            hr = WorkProc(WI, &bContinue);
        }
        else
        {
            _ASSERT( ulNumBandsToDo <= DXB_MAX_IMAGE_BANDS );
            _ASSERT( m_aEvent[ulNumBandsToDo-1] );

            long lStartAtRow = WI.DoBnds[DXB_Y].Min;
            ULONG ulRowCount = WI.DoBnds[DXB_Y].Max - lStartAtRow;
            _ASSERT( ( ulRowCount / ulNumBandsToDo ) != 0 );

            //--- Init the work info structures
            ULONG ulBand, RowsPerBand = ulRowCount / ulNumBandsToDo;
            CDXTWorkInfoNTo1 *WIArray = (CDXTWorkInfoNTo1*)alloca( sizeof(CDXTWorkInfoNTo1) *
                                                         ulNumBandsToDo );
            DWORD *TaskIDs = (DWORD*)alloca( sizeof(DWORD) * ulNumBandsToDo );
            DXTMTASKINFO* TaskInfo = (DXTMTASKINFO*)alloca( sizeof( DXTMTASKINFO ) *
                                                            ulNumBandsToDo );

            //--- Build task info list
            WI.hr       = S_OK;
            WI.pvThis   =  this;
            long Start  = lStartAtRow;
            ULONG Count = RowsPerBand;
            long OutputYDelta = WI.OutputBnds[DXB_Y].Min - WI.DoBnds[DXB_Y].Min;

            for (ulBand = 0; ulBand < ulNumBandsToDo; ++ulBand)
            {
                memcpy(&WIArray[ulBand], &WI, sizeof(WI));

                WIArray[ulBand].DoBnds[DXB_Y].Min       = Start;
                WIArray[ulBand].OutputBnds[DXB_Y].Min   = Start + OutputYDelta;

                // If this is the last band, make sure it includes the last row.

                if (ulBand == ulNumBandsToDo - 1)
                {
                    WIArray[ulBand].DoBnds[DXB_Y].Max       = WI.DoBnds[DXB_Y].Max;
                    WIArray[ulBand].OutputBnds[DXB_Y].Max   = WI.OutputBnds[DXB_Y].Max;
                }
                else // Not the last band.
                {
                    WIArray[ulBand].DoBnds[DXB_Y].Max       = Start + Count;
                    WIArray[ulBand].OutputBnds[DXB_Y].Max   = Start + Count 
                                                              + OutputYDelta;
                }

                TaskInfo[ulBand].pfnTaskProc      = _TaskProc;
                TaskInfo[ulBand].pTaskData        = &WIArray[ulBand];
                TaskInfo[ulBand].pfnCompletionAPC = NULL;
                TaskInfo[ulBand].dwCompletionData = 0;
                TaskInfo[ulBand].pRequestID       = pRequestID;

                // Advance.

                Start += Count;
            }

            //
            //  Procedural surfaces (and perhaps some transforms) need to "know" that
            //  they are in a multi-threaded work procedure to avoid deadlocks.  Procedural
            //  surfaces need to allow LockSurface to work WITHOUT taking the object
            //  critical section.  Other transforms may also want to know this information
            //  to avoid deadlocks.
            //
            m_bInMultiThreadWorkProc = TRUE;

            //--- Schedule the work and wait for it to complete
            hr = m_cpTaskMgr->ScheduleTasks( TaskInfo, m_aEvent,
                                             TaskIDs, ulNumBandsToDo, m_ulLockTimeOut );

            m_bInMultiThreadWorkProc = FALSE;

            //--- Check return codes from work info structures
            //    return the first bad hr if any
            for( ulBand = 0; SUCCEEDED( hr ) && ( ulBand < ulNumBandsToDo ); ++ulBand )
            {
                hr = WIArray[ulBand].hr;
                if( hr != S_OK ) break;
            }
        }
        OnFreeInstData( WI );
    }

#ifdef _DEBUG
    if( FAILED( hr ) ) DXTDBG_MSG1( _CRT_ERROR, "\nExecute failed. HR = %X", hr );
#endif

    return hr;
} /* CDXBaseNTo1::Execute */

/*****************************************************************************
* CDXBaseNTo1::_ImageMapIn2Out *
*------------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                            Date: 07/28/97
*****************************************************************************/
HRESULT CDXBaseNTo1::_ImageMapIn2Out( CDXDBnds & bnds, ULONG ulNumInBnds,
                                      const CDXDBnds * pInBounds )
{
    HRESULT hr = S_OK;
    if(ulNumInBnds)
    {
        for(ULONG i = 0; i < ulNumInBnds; ++i )
        {
            bnds |= pInBounds[i];
        }
    }
    else
    {
        for( ULONG i = 0; SUCCEEDED(hr) && i < m_ulNumInputs; ++i )
        {
            if (InputSurface(i))
            {
                CDXDBnds SurfBnds(InputSurface(i), hr);
                bnds |= SurfBnds;
            }
        }
    }
    if (SUCCEEDED(hr))
    {
        hr = DetermineBnds(bnds);
    }
    return hr;
} /* CDXBaseNTo1::_ImageMapIn2Out */

/*****************************************************************************
* CDXBaseNTo1::_MeshMapIn2Out *
*-----------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                            Date: 07/28/97
*****************************************************************************/
HRESULT CDXBaseNTo1::_MeshMapIn2Out(CDXCBnds & bnds, ULONG ulNumInBnds, CDXCBnds * pInBounds)
{
    HRESULT hr = S_OK;
    if (m_dwOptionFlags & DXBOF_INPUTS_MESHBUILDER)
    {
        if(ulNumInBnds)
        {
            for(ULONG i = 0; i < ulNumInBnds; ++i )
            {
                bnds |= pInBounds[i];
            }
        }
        else
        {
            for(ULONG i = 0; SUCCEEDED(hr) && i < m_ulNumInputs; ++i )
            {
                if (InputMeshBuilder(i))
                {
                    CDXCBnds MeshBnds(InputMeshBuilder(i), hr);
                    bnds |= MeshBnds;
                }
            }

        }
    }
    else
    {
        //  Already done -> bnds[DXB_T].Min = 0.0f;
        bnds[DXB_X].Min = bnds[DXB_Y].Min = bnds[DXB_Z].Min = -1.0f;
        bnds[DXB_X].Max = bnds[DXB_Y].Max = bnds[DXB_Z].Max = bnds[DXB_T].Max = 1.0f;
    }

    //
    //  Call the derived class to get the scale values.
    //
    if (SUCCEEDED(hr))
    {
	// Increase the size just a bit so we won't have rounding errors
	// result in bounds that don't actually contain the result.
	const float fBndsIncrease = 0.0001F;
	float fTemp = bnds.Width() * fBndsIncrease;

	bnds[DXB_X].Min -= fTemp;
	bnds[DXB_X].Max += fTemp;

	fTemp = fBndsIncrease * bnds.Height();
	bnds[DXB_Y].Min -= fTemp;
	bnds[DXB_Y].Max += fTemp;

	fTemp = fBndsIncrease * bnds.Depth();
	bnds[DXB_Z].Min -= fTemp;
	bnds[DXB_Z].Max += fTemp;

        hr = DetermineBnds(bnds);
    }
    return hr;
} /* CDXBaseNTo1::_MeshMapIn2Out */

/*****************************************************************************
* CDXBaseNTo1::MapBoundsIn2Out *
*------------------------------*
*   Description:
*       The MapBoundsIn2Out method is used to perform coordinate transformation
*   from the input to the output coordinate space.
*-----------------------------------------------------------------------------
*   Created By: Ed Connell                            Date: 07/28/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::MapBoundsIn2Out( const DXBNDS *pInBounds, ULONG ulNumInBnds,
                                           ULONG ulOutIndex, DXBNDS *pOutBounds )
{
    DXTDBG_FUNC( "CDXBaseNTo1::MapBoundsIn2Out" );
    if((ulNumInBnds && DXIsBadReadPtr( pInBounds, ulNumInBnds * sizeof( *pInBounds ) )) ||
        ulOutIndex)
    {
        return E_INVALIDARG;
    }

    if( DXIsBadWritePtr( pOutBounds, sizeof( *pOutBounds ) ) )
    {
        return E_POINTER;
    }
    //
    //  Set the bounds to empty and the appropriate type.
    //
    memset(pOutBounds, 0, sizeof(*pOutBounds));
    _ASSERT(DXBT_DISCRETE == 0);
    if (m_dwOptionFlags & DXBOF_OUTPUT_MESHBUILDER)
    {
        pOutBounds->eType = DXBT_CONTINUOUS;
    }

    //
    //  Make sure all input bounds are of the correct type.
    //
    if( ulNumInBnds )
    {
        DXBNDTYPE eType = (m_dwOptionFlags & DXBOF_INPUTS_MESHBUILDER) ? DXBT_CONTINUOUS : DXBT_DISCRETE;
        for (ULONG i = 0; i < ulNumInBnds; i++)
        {
            if (pInBounds[i].eType != eType)
            {
                return E_INVALIDARG;
            }
        }
    }

    //
    //  Now do the appropriate mapping
    //
    if (m_dwOptionFlags & DXBOF_OUTPUT_MESHBUILDER)
    {
        //
        //  NOTE:  In the case of non-mesh inputs, the inputs are discrete, but they will
        //         be completely ignored by the function so it's OK to cast them to CDXCBnds
        //
        return _MeshMapIn2Out(*((CDXCBnds *)pOutBounds), ulNumInBnds, (CDXCBnds *)pInBounds);
    }
    else 
    {
        return _ImageMapIn2Out(*(CDXDBnds *)pOutBounds, ulNumInBnds, (CDXDBnds *)pInBounds);
    }
} /* CDXBaseNTo1::MapBoundsIn2Out */

/*****************************************************************************
* CDXBaseNTo1::MapBoundsOut2In *
*------------------------------*
*   Description:
*       The MapBoundsOut2In method is used to perform coordinate transformation
*   from the input to the output coordinate space.
*-----------------------------------------------------------------------------
*   Created By: Ed Connell                            Date: 07/28/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::
    MapBoundsOut2In( ULONG ulOutIndex, const DXBNDS *pOutBounds, ULONG ulInIndex, DXBNDS *pInBounds )
{
    DXTDBG_FUNC( "CDXBaseNTo1::MapBoundsOut2In" );
    HRESULT hr = S_OK;
    
    if (m_dwOptionFlags & DXBOF_OUTPUT_MESHBUILDER)
    {
        hr = E_NOTIMPL;     // This is pointless for meshes.
    }
    else if(ulInIndex >= m_ulMaxInputs || ulOutIndex || DXIsBadReadPtr( pOutBounds, sizeof( *pOutBounds ) ) )
    {
        hr = E_INVALIDARG;
    }
    else if( DXIsBadWritePtr( pInBounds, sizeof( *pInBounds ) ) )
    {
        hr = E_POINTER;
    }
    else
    {
        *pInBounds = *pOutBounds;
    }
    return hr;
} /* CDXBaseNTo1::MapBoundsOut2In */

/*****************************************************************************
* CDXBaseNTo1::SetMiscFlags *
*---------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                            Date: 10/30.97
*-----------------------------------------------------------------------------
*   Parameters:
*       bMiscFlags - New value to set 
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::SetMiscFlags( DWORD dwMiscFlags )
{ 
    DXTDBG_FUNC( "CDXBaseNTo1::SetMiscFlags" );
    HRESULT hr = S_OK;
    Lock();
    WORD wOpts = (WORD)dwMiscFlags;     // Ignore high word.  Only set low word.
    if (((WORD)m_dwMiscFlags) != wOpts)
    {
        if ((wOpts & (~DXTMF_VALID_OPTIONS)) ||
            ((wOpts & DXTMF_BLEND_WITH_OUTPUT) && (m_dwMiscFlags & DXTMF_BLEND_SUPPORTED) == 0) ||
            ((wOpts & DXTMF_DITHER_OUTPUT) && (m_dwMiscFlags & DXTMF_DITHER_SUPPORTED) == 0))
        {
            hr = E_INVALIDARG;
        }
        else
        {
            m_dwMiscFlags &= 0xFFFF0000;
            m_dwMiscFlags |= wOpts;
            _UpdateBltFlags();
            m_dwGenerationId++;
        }
    }
    Unlock();  
    return hr;
} /* CDXBaseNTo1::SetMiscFlags */

/*****************************************************************************
* CDXBaseNTo1::GetMiscFlags *
*----------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                            Date: 10/30/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::GetMiscFlags( DWORD* pdwMiscFlags )
{
    if( DXIsBadWritePtr( pdwMiscFlags, sizeof( *pdwMiscFlags ) ) )
    {
        return E_POINTER;
    }
    *pdwMiscFlags = m_dwMiscFlags;
    return S_OK;
} /* CDXBaseNTo1::GetMiscFlags */


/*****************************************************************************
* CDXBaseNTo1::SetQuality *
*----------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                            Date: 10/30/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::SetQuality(float fQuality)
{
    if ((m_dwMiscFlags & DXTMF_QUALITY_SUPPORTED) == 0)
    {
        return E_NOTIMPL;
    }

    if (fQuality < 0.0f || fQuality > 1.0f)
    {
        return E_INVALIDARG;
    }

    Lock();
    if (m_fQuality != fQuality)
    {
        m_fQuality = fQuality;
        m_dwGenerationId++;
    }
    Unlock();

    return S_OK;
}

/*****************************************************************************
* CDXBaseNTo1::GetQuality *
*-------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                            Date: 10/30/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/

STDMETHODIMP CDXBaseNTo1::GetQuality(float *pfQuality)
{
    HRESULT hr = S_OK;

    if ((m_dwMiscFlags & DXTMF_QUALITY_SUPPORTED) == 0)
    {
        hr = E_NOTIMPL;
    }
    else 
    {
        if( DXIsBadWritePtr( pfQuality, sizeof( *pfQuality ) ) )
        {
            hr = E_POINTER;
        }
        else
        {
            *pfQuality = m_fQuality;
        }
    }
    return hr;
}



/*****************************************************************************
* GetInOutInfo
*-----------------------------------------------------------------------------
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::GetInOutInfo( BOOL bOutput, ULONG ulIndex, DWORD *pdwFlags,
                                        GUID * pIDs, ULONG *pcIDs, IUnknown **ppUnkCurObj )
{
    DXTDBG_FUNC( "CDXBaseNTo1::GetInOutInfo" );
    HRESULT hr = S_FALSE;
    DWORD dwFlags = 0;
    BOOL bImage;
    if( bOutput )
    {
        bImage = !(m_dwOptionFlags & DXBOF_OUTPUT_MESHBUILDER);
        if (ulIndex == 0)
        {
            hr = S_OK;
        }
    }
    else
    {
        bImage = !(m_dwOptionFlags & DXBOF_INPUTS_MESHBUILDER);
        if (ulIndex < m_ulMaxInputs)
        {
            hr = S_OK;
            if (ulIndex >= m_ulNumInRequired)
            {
                dwFlags = DXINOUTF_OPTIONAL;
            }
        }
    }
    if( hr == S_OK )
    {
        if( pdwFlags && !DXIsBadWritePtr( pdwFlags, sizeof( *pdwFlags ) ) )
        {
            *pdwFlags = dwFlags;
        }

        if( pIDs )
        {
            if( DXIsBadWritePtr( pcIDs, sizeof( *pcIDs ) ) ||
                DXIsBadWritePtr( pIDs, *pcIDs * sizeof( *pIDs ) ) )
            {
                hr = E_INVALIDARG;
            }
            else
            {
                if (bImage)
                {
                    if (*pcIDs > 0)
                    {
                        pIDs[0] = IID_IDXSurface;
                    }
                    if (*pcIDs > 1)
                    {
                        pIDs[1] = IID_IDXDupDDrawSurface;
                    }
                    else
                    {
                        hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
                    }
                    *pcIDs = 2;
                }
                else
                {
                    if (*pcIDs > 0)
                    {
                        pIDs[0] = IID_IDXDupDirect3DRMMeshBuilder3;
                    }
                    else
                    {
                        hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
                    }
                    *pcIDs = 1;
                }
            }
        }
        else if( pcIDs )
        {
            if( DXIsBadWritePtr( pcIDs, sizeof( *pcIDs ) ) )
            {
                hr = E_POINTER;
            }
            else
            {
                *pcIDs = bImage ? 2 : 1;
            }
        }
        if (hr == S_OK && ppUnkCurObj)
        {
            if (DXIsBadWritePtr(ppUnkCurObj, sizeof(*ppUnkCurObj)))
            {
                hr = E_POINTER;
            }
            else
            {
                if (bOutput)
                {
                    *ppUnkCurObj = m_Output.m_pNativeInterface;
                }
                else
                {
                    *ppUnkCurObj = NULL;
                    if (ulIndex < GetNumInputs())
                    {
                        *ppUnkCurObj = m_aInputs[ulIndex].m_pUnkOriginalObject;
                    }
                }
                if (*ppUnkCurObj)
                {
                    (*ppUnkCurObj)->AddRef();
                }
            }
        }
    }
    return hr;
} /* CDXBaseNTo1::GetInOutInfo */

/*****************************************************************************
* CDXBaseNTo1::OnUpdateGenerationId *
*-----------------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
void CDXBaseNTo1::OnUpdateGenerationId(void)
{
    DXTDBG_FUNC( "CDXBaseNTo1::OnUpdateGenerationId" );
    if( (m_dwMiscFlags & DXTMF_INPLACE_OPERATION) &&
        m_Output.UpdateGenerationId())
    {
        m_dwGenerationId++;
    }
    for (ULONG i = 0; i < m_ulNumInputs; i++)
    {
        if (m_aInputs[i].UpdateGenerationId())
        {
            m_dwGenerationId++;
        }
    }
} /* CDXBaseNTo1::OnUpdateGenerationId */

/*****************************************************************************
* CDXBaseNTo1::OnGetObjectSize *
*------------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*****************************************************************************/
ULONG CDXBaseNTo1::OnGetObjectSize(void)
{
    return sizeof(*this);
}

//
//  Effect interface
//

/*****************************************************************************
* CDXBaseNTo1::get_Progress *
*---------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::get_Progress(float *pVal)
{
    DXTDBG_FUNC( "CDXBaseNTo1::get_Progress" );
    HRESULT hr = S_OK;
    if( DXIsBadWritePtr(pVal, sizeof(*pVal)) )
    {
        hr = E_POINTER;
    }
    else
    {
        *pVal = m_Progress;
    }
    return hr;
}

/*****************************************************************************
* CDXBaseNTo1::put_Progress *
*---------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::put_Progress(float newVal)
{
    DXTDBG_FUNC( "CDXBaseNTo1::put_Progress" );
    HRESULT hr = S_OK;
    if (newVal < 0.0 || newVal > 1.0f)
    {
        hr = E_INVALIDARG;
    }
    else
    {
        Lock();
        m_Progress = newVal;
        m_dwCleanGenId++;       // This should not make the transform "dirty" internally
        m_dwGenerationId++;     
        Unlock();
    }
    return hr;
}

/*****************************************************************************
* CDXBaseNTo1::get_StepResolution *
*---------------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::get_StepResolution(float *pVal)
{
    DXTDBG_FUNC( "CDXBaseNTo1::get_StepResolution" );
    HRESULT hr = S_OK;
    if( DXIsBadWritePtr(pVal, sizeof(*pVal)) )
    {
        hr = E_POINTER;
    }
    else
    {
        *pVal = m_StepResolution;
    }
    return hr;
}

/*****************************************************************************
* CDXBaseNTo1::get_Duration *
*---------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::get_Duration(float *pVal)
{
    DXTDBG_FUNC( "CDXBaseNTo1::get_Duration" );
    if( DXIsBadWritePtr(pVal, sizeof(*pVal)) )
    {
        return E_POINTER;
    }
    else
    {
        *pVal = m_Duration;
    }
    return S_OK;
}

/*****************************************************************************
* CDXBaseNTo1::put_Duration *
*---------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::put_Duration(float newVal)
{
    DXTDBG_FUNC( "CDXBaseNTo1::put_Duration" );
    if (newVal <= 0.)
    {
        return E_INVALIDARG;
    }
    if(newVal != m_Duration)
    {
	Lock();
	m_dwGenerationId++;
        m_dwCleanGenId++;       // This should not make the transform "dirty" internally
        m_Duration = newVal;
    	Unlock();
    }
    return S_OK;
}


/*****************************************************************************
* CDXBaseNTo1::PointPick *
*------------------------*
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 5/5/98
*****************************************************************************/
STDMETHODIMP CDXBaseNTo1::PointPick(const DXVEC *pPoint,
                                    ULONG * pulInputSurfaceIndex,
                                    DXVEC *pInputPoint)
{
    HRESULT hr          = S_OK;
    BOOL    bFoundIt    = FALSE;

    // If we haven't been set up yet, we will just act as if we're transparent.

    if (!m_fIsSetup)
    {
        hr = S_FALSE;

        goto done;
    }

    if (DXIsBadReadPtr(pPoint, sizeof(*pPoint)) || pPoint->eType != DXBT_DISCRETE)
    {
        hr = E_INVALIDARG;
    }
    else 
    {
        if (DXIsBadWritePtr(pulInputSurfaceIndex, sizeof(*pulInputSurfaceIndex)) ||
            DXIsBadWritePtr(pInputPoint, sizeof(*pInputPoint)))
        {
            hr = E_POINTER;
        }
        else 
        {
            HRESULT     hr2         = S_OK;
            CDXDBnds    bndsOutput;
            CDXDBnds    OutBndsPoint(*((CDXDVec *)pPoint));
            CDXDVec &   InVec       = *(new(pInputPoint) CDXDVec(*((CDXDVec *)pPoint)));

            // Get the output size of the DXTransform.  If this point is not on
            // the output at all, we can return S_FALSE right now.

            hr = MapBoundsIn2Out(NULL, 0, 0, &bndsOutput);

            if (FAILED(hr))
            {
                goto done;
            }

            if (!bndsOutput.TestIntersect(OutBndsPoint))
            {
                hr = S_FALSE;

                goto done;
            }

            hr2 = OnSurfacePick(OutBndsPoint, *pulInputSurfaceIndex, InVec);

            if (hr2 != E_NOTIMPL)
            {
                hr = hr2;
            }
            else
            {
                //--- The derived class does not implement so we will do
                //    the hit test against the input for them.
                ULONG * aulInIndex = (ULONG *)_alloca(sizeof(ULONG) * m_ulMaxInputs);
                BYTE * aWeights = (BYTE *)_alloca(sizeof(BYTE) * m_ulMaxInputs);
                ULONG ulNumToTest;
                OnGetSurfacePickOrder(OutBndsPoint, ulNumToTest, aulInIndex, aWeights);

                if( m_bPickDoneByBase && ( m_ulNumInputs > 1 ) )
                {
                    //--- We don't know how to do multi-input picking from the base.
                    hr = E_NOTIMPL;
                }

                for (ULONG i = 0; SUCCEEDED(hr) && i < ulNumToTest; i++)
                {
                    ULONG ulInput = aulInIndex[i];
                    if (HaveInput(ulInput) && aWeights[i])
                    {
                        CDXDBnds Out2InBnds(false);
                        hr = MapBoundsOut2In(0, &OutBndsPoint, ulInput, &Out2InBnds);
                        if (SUCCEEDED(hr))
                        {
                            CDXDBnds InSurfBnds(InputSurface(ulInput), hr);
                            if (SUCCEEDED(hr) && InSurfBnds.IntersectBounds(Out2InBnds))
                            {
                                IDXARGBReadPtr * pPtr;
                                hr = InputSurface(ulInput)->LockSurface(&InSurfBnds, m_ulLockTimeOut, DXLOCKF_READ, 
                                                                        IID_IDXARGBReadPtr, (void **)&pPtr, NULL);
                                if( SUCCEEDED(hr) )
                                {
                                    DXPMSAMPLE val;
                                    pPtr->UnpackPremult(&val, 1, FALSE);
                                    pPtr->Release();
                                    if (val.Alpha * aWeights[i] / 255)
                                    {
                                        InSurfBnds.GetMinVector(InVec);
                                        bFoundIt = TRUE;
                                        *pulInputSurfaceIndex = ulInput;
                                        break;
                                    }
                                }
                            }   
                        }
                    }
                }
                if (SUCCEEDED(hr) & (!bFoundIt))
                {
                    hr = S_FALSE;
                }
            }
        }
    }

done:

    return hr;
} /* CDXBaseNTo1::PointPick */

/*****************************************************************************
* RegisterTansform (STATIC member function)
*-----------------------------------------------------------------------------
*   Description:
*-----------------------------------------------------------------------------
*   Created By: RAL                                 Date: 12/10/97
*-----------------------------------------------------------------------------
*   Parameters:
*****************************************************************************/
HRESULT CDXBaseNTo1::
RegisterTransform(REFCLSID rcid, int ResourceId, ULONG cCatImpl, const CATID * pCatImpl,
                  ULONG cCatReq, const CATID * pCatReq, BOOL bRegister)
{
    DXTDBG_FUNC( "CDXBaseNTo1::RegisterTransform" );
    HRESULT hr = bRegister ? _Module.UpdateRegistryFromResource(ResourceId, bRegister) : S_OK;
    if (SUCCEEDED(hr))
    {
        CComPtr<ICatRegister> pCatRegister;
        HRESULT hr = ::CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC, IID_ICatRegister, (void **)&pCatRegister);
        if (SUCCEEDED(hr))
        {
            if (bRegister)
            {
                hr = pCatRegister->RegisterClassImplCategories(rcid, cCatImpl, (CATID *)pCatImpl);
                if (SUCCEEDED(hr) && cCatReq && pCatReq) {
                    hr = pCatRegister->RegisterClassReqCategories(rcid, cCatReq, (CATID *)pCatReq);
                }
            } 
            else
            {
                pCatRegister->UnRegisterClassImplCategories(rcid, cCatImpl, (CATID *)pCatImpl);
                if (cCatReq && pCatReq)
                {
                    pCatRegister->UnRegisterClassReqCategories(rcid, cCatReq, (CATID *)pCatReq);
                }
            }
        }
    }
    if ((!bRegister) && SUCCEEDED(hr)) 
    { 
        _Module.UpdateRegistryFromResource(ResourceId, bRegister);
    }
    return hr;
}


void CDXBaseNTo1::_TaskProc(void* pTaskInfo, BOOL* pbContinue )
{ 
    _ASSERT( pTaskInfo );
    CDXTWorkInfoNTo1& WI = *((CDXTWorkInfoNTo1 *)pTaskInfo);
    CDXBaseNTo1& This = *((CDXBaseNTo1 *)WI.pvThis);
    WI.hr = This.WorkProc(WI, pbContinue);
}