Windows-Server-2003/inetcore/published/inc/dxhelper.h

1088 lines
35 KiB
C++

/*******************************************************************************
* DXHelper.h *
*------------*
* Description:
* This is the header file for core helper functions implementation.
*-------------------------------------------------------------------------------
* Created By: Edward W. Connell Date: 07/11/95
* Copyright (C) 1995 Microsoft Corporation
* All Rights Reserved
*
*-------------------------------------------------------------------------------
* Revisions:
*
*******************************************************************************/
#ifndef DXHelper_h
#define DXHelper_h
#include <DXTError.h>
#include <DXBounds.h>
#include <DXTrans.h>
#include <limits.h>
#include <crtdbg.h>
#include <malloc.h>
#include <math.h>
//=== Constants ==============================================================
#define DX_MMX_COUNT_CUTOFF 16
//=== Class, Enum, Struct and Union Declarations =============================
/*** DXLIMAPINFO
* This structure is used by the array linear interpolation and image
* filtering routines.
*/
typedef struct DXLIMAPINFO
{
float IndexFrac;
USHORT Index;
BYTE Weight;
} DXLIMAPINFO;
//
// Declare this class as a global to use for determining when to call MMX optimized
// code. You can use MinMMXOverCount to determine if MMX instructions are present.
// Typically, you would only want to use MMX instructions when you have a reasonably
// large number of pixels to work on. In this case your code can always be coded like
// this:
//
// if (CountOfPixelsToDo >= g_MMXInfo.MinMMXOverCount())
// {
// Do MMX Stuff
// } else {
// Do integer / float based stuff
// }
//
// If you code your MMX sequences like this, you will not have to use a special test
// for the presence of MMX since the MinMMXOverCount will be set to 0xFFFFFFFF if there
// is no MMX present on the processor.
//
// You do not need to use this unless your module needs to conditionally execute MMX vs
// non-MMX code. If you only call the helper functions provided by DXTrans.Dll, such as
// DXOverArrayMMX, you do NOT need this test. You can always call these functions and they
// will use the MMX code path only when MMX instructions are present.
//
class CDXMMXInfo
{
ULONG m_MinMMXOver;
public:
CDXMMXInfo()
{
#ifndef _X86_
m_MinMMXOver = 0xFFFFFFFF;
#else
m_MinMMXOver = DX_MMX_COUNT_CUTOFF;
__try
{
__asm
{
//--- Try the MMX exit multi-media state instruction
EMMS;
}
}
__except( GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION )
{
//--- MMX instructions not available
m_MinMMXOver = 0xFFFFFFFF;
}
#endif
}
inline ULONG MinMMXOverCount() { return m_MinMMXOver; }
};
//=== Function Prototypes ==========================================
_DXTRANS_IMPL_EXT void WINAPI
DXLinearInterpolateArray( const DXBASESAMPLE* pSamps, DXLIMAPINFO* pMapInfo,
DXBASESAMPLE* pResults, DWORD dwResultCount );
_DXTRANS_IMPL_EXT void WINAPI
DXLinearInterpolateArray( const DXBASESAMPLE* pSamps, PUSHORT pIndexes,
PBYTE pWeights, DXBASESAMPLE* pResults,
DWORD dwResultCount );
//
// DXOverArray
//
// Composits an array of source samples over the samples in the pDest buffer.
//
// pDest - Pointer to the samples that will be modified by compositing the pSrc
// samples over the pDest samples.
// pSrc - The samples to composit over the pDest samples
// nCount - The number of samples to process
//
_DXTRANS_IMPL_EXT void WINAPI
DXOverArray(DXPMSAMPLE* pDest, const DXPMSAMPLE* pSrc, ULONG nCount);
//
// DXOverArrayMMX
//
// Identical to DXOverArray except that the MMX instruction set will be used for
// large arrays of samples. If the CPU does not support MMX, you may still call
// this function, which will perform the same operation without the use of the MMX
// unit.
//
// Note that it is LESS EFFICIENT to use this function if the majority of the pixels
// in the pSrc buffer are either clear (alpha 0) or opaque (alpha 0xFF). This is
// because the MMX code must process every pixel and can not special case clear or
// opaque pixels. If there are a large number of translucent pixels then this function
// is much more efficent than DXOverArray.
//
// pDest - Pointer to the samples that will be modified by compositing the pSrc
// samples over the pDest samples.
// pSrc - The samples to composit over the pDest samples
// nCount - The number of samples to process
//
_DXTRANS_IMPL_EXT void WINAPI
DXOverArrayMMX(DXPMSAMPLE* pDest, const DXPMSAMPLE* pSrc, ULONG nCount);
//
// DXConstOverArray
//
// Composits a single color over an array of samples.
//
// pDest - Pointer to the samples that will be modified by compositing the color (val)
// over the pDest samples.
// val - The premultiplied color value to composit over the pDest array.
// nCount - The number of samples to process
//
_DXTRANS_IMPL_EXT void WINAPI
DXConstOverArray(DXPMSAMPLE* pDest, const DXPMSAMPLE & val, ULONG nCount);
//
// DXConstOverArray
//
// Composits a single color over an array of samples.
//
// pDest - Pointer to the samples that will be modified by compositing the samples
// in the buffer over the color (val).
// val - The premultiplied color value to composit under the pDest array.
// nCount - The number of samples to process
//
_DXTRANS_IMPL_EXT void WINAPI
DXConstUnderArray(DXPMSAMPLE* pDest, const DXPMSAMPLE & val, ULONG nCount);
//===================================================================================
//
// Dithering Helpers
//
// Image transforms are sometimes asked to dither their output. This helper function
// should be used by all image transforms to enusure a consistant dither pattern.
//
// DXDitherArray is used to dither pixels prior to writing them to a DXSurface.
// The caller must fill in the DXDITHERDESC structure, setting X and Y to the
// output surface X,Y coordinates that the pixels will be placed in. The samples
// will be modified in place.
//
// Once the samples have been dithered, they should be written to or composited with
// the destination surface.
//
#define DX_DITHER_HEIGHT 4 // The dither pattern is 4x4 pixels
#define DX_DITHER_WIDTH 4
typedef struct DXDITHERDESC
{
DXBASESAMPLE * pSamples; // Pointer to the 32-bit samples to dither
ULONG cSamples; // Count of number of samples in pSamples buffer
ULONG x; // X coordinate of the output surface
ULONG y; // Y coordinate of the output surface
DXSAMPLEFORMATENUM DestSurfaceFmt; // Pixel format of the output surface
} DXDITHERDESC;
_DXTRANS_IMPL_EXT void WINAPI
DXDitherArray(const DXDITHERDESC *pDitherDesc);
//=== Enumerated Set Definitions =============================================
//=== Function Type Definitions ==============================================
//=== Class, Struct and Union Definitions ====================================
//=== Inline Functions =======================================================
//===================================================================================
//
// Memory allocation helpers.
//
// These macros are used to allocate arrays of samples from the stack (using _alloca)
// and cast them to the appropriate type. The ulNumSamples parameter is the count
// of samples required.
//
#define DXBASESAMPLE_Alloca( ulNumSamples ) \
(DXBASESAMPLE *)_alloca( (ulNumSamples) * sizeof( DXBASESAMPLE ) )
#define DXSAMPLE_Alloca( ulNumSamples ) \
(DXSAMPLE *)_alloca( (ulNumSamples) * sizeof( DXSAMPLE ) )
#define DXPMSAMPLE_Alloca( ulNumSamples ) \
(DXPMSAMPLE *)_alloca( (ulNumSamples) * sizeof( DXPMSAMPLE ) )
//===================================================================================
//
// Critical section helpers.
//
// These C++ classes, CDXAutoObjectLock and CDXAutoCritSecLock are used within functions
// to automatically claim critical sections upon constuction, and the critical section
// will be released when the object is destroyed (goes out of scope).
//
// The macros DXAUTO_OBJ_LOCK and DX_AUTO_SEC_LOCK(s) are normally used at the beginning
// of a function that requires a critical section. Any exit from the scope in which the
// auto-lock was taken will automatically release the lock.
//
#ifdef __ATLCOM_H__ //--- Only enable these if ATL is being used
class CDXAutoObjectLock
{
protected:
CComObjectRootEx<CComMultiThreadModel>* m_pObject;
public:
CDXAutoObjectLock(CComObjectRootEx<CComMultiThreadModel> * const pobject)
{
m_pObject = pobject;
m_pObject->Lock();
};
~CDXAutoObjectLock() {
m_pObject->Unlock();
};
};
#define DXAUTO_OBJ_LOCK CDXAutoObjectLock lck(this);
#define DXAUTO_OBJ_LOCK_( t ) CDXAutoObjectLock lck(t);
class CDXAutoCritSecLock
{
protected:
CComAutoCriticalSection* m_pSec;
public:
CDXAutoCritSecLock(CComAutoCriticalSection* pSec)
{
m_pSec = pSec;
m_pSec->Lock();
};
~CDXAutoCritSecLock()
{
m_pSec->Unlock();
};
};
#define DXAUTO_SEC_LOCK( s ) CDXAutoCritSecLock lck(s);
#endif // __ATLCOM_H__
//--- This function is used to compute the coefficient for a gaussian filter coordinate
inline float DXGaussCoeff( double x, double y, double Sigma )
{
double TwoSigmaSq = 2 * ( Sigma * Sigma );
return (float)(exp( ( -(x*x + y*y) / TwoSigmaSq ) ) /
( 3.1415927 * TwoSigmaSq ));
}
//--- This function is used to initialize a gaussian convolution filter
inline void DXInitGaussianFilter( float* pFilter, ULONG Width, ULONG Height, double Sigma )
{
int i, NumCoeff = Width * Height;
float val, CoeffAdjust, FilterSum = 0.;
double x, y;
double LeftX = -(double)(Width / 2);
double RightX = Width - LeftX;
double TopY = -(double)(Height / 2);
double BottomY = Height - TopY;
for( y = -TopY; y <= BottomY; y += 1. )
{
for( x = -LeftX; x <= RightX; x += 1. )
{
val = DXGaussCoeff( x, y, Sigma );
pFilter[i++] = val;
}
}
//--- Normalize filter (make it sum to 1.0)
for( i = 0; i < NumCoeff; ++i ) FilterSum += pFilter[i];
if( FilterSum < 1. )
{
CoeffAdjust = 1.f / FilterSum;
for( i = 0; i < NumCoeff; ++i )
{
pFilter[i] *= CoeffAdjust;
}
}
} /* DXInitGaussianFilter*/
//
// DXConvertToGray
//
// Translates a color sample to a gray scale sample
//
// Sample - The sample to convert to gray scale.
// Return value is the gray scale sample.
//
inline DXBASESAMPLE DXConvertToGray( DXBASESAMPLE Sample )
{
DWORD v = Sample;
DWORD r = (BYTE)(v >> 16);
DWORD g = (BYTE)(v >> 8);
DWORD b = (BYTE)(v);
DWORD sat = (r*306 + g*601 + b*117) / 1024;
v &= 0xFF000000;
v |= (sat << 16) | (sat << 8) | sat;
return v;
} /* DXConvertToGray */
//--- This returns into the destination the value of the source
// sample scaled by its own alpha (producing a premultiplied alpha sample)
//
inline DXPMSAMPLE DXPreMultSample(const DXSAMPLE & Src)
{
if(Src.Alpha == 255 )
{
return (DWORD)Src;
}
else if(Src.Alpha == 0 )
{
return 0;
}
else
{
unsigned t1, t2;
t1 = (Src & 0x00ff00ff) * Src.Alpha + 0x00800080;
t1 = ((t1 + ((t1 >> 8) & 0x00ff00ff)) >> 8) & 0x00ff00ff;
t2 = (((Src >> 8) & 0x000000ff) | 0x01000000) * Src.Alpha + 0x00800080;
t2 = (t2 + ((t2 >> 8) & 0x00ff00ff)) & 0xff00ff00;
return (t1 | t2);
}
} /* DXPreMultSample */
inline DXPMSAMPLE * DXPreMultArray(DXSAMPLE *pBuffer, ULONG cSamples)
{
for (ULONG i = 0; i < cSamples; i++)
{
BYTE SrcAlpha = pBuffer[i].Alpha;
if (SrcAlpha != 0xFF)
{
if (SrcAlpha == 0)
{
pBuffer[i] = 0;
}
else
{
DWORD S = pBuffer[i];
DWORD t1 = (S & 0x00ff00ff) * SrcAlpha + 0x00800080;
t1 = ((t1 + ((t1 >> 8) & 0x00ff00ff)) >> 8) & 0x00ff00ff;
DWORD t2 = (((S >> 8) & 0x000000ff) | 0x01000000) * SrcAlpha + 0x00800080;
t2 = (t2 + ((t2 >> 8) & 0x00ff00ff)) & 0xff00ff00;
pBuffer[i] = (t1 | t2);
}
}
}
return (DXPMSAMPLE *)pBuffer;
}
inline DXSAMPLE DXUnPreMultSample(const DXPMSAMPLE & Src)
{
if(Src.Alpha == 255 || Src.Alpha == 0)
{
return (DWORD)Src;
}
else
{
DXSAMPLE Dst;
Dst.Blue = (BYTE)((Src.Blue * 255) / Src.Alpha);
Dst.Green = (BYTE)((Src.Green * 255) / Src.Alpha);
Dst.Red = (BYTE)((Src.Red * 255) / Src.Alpha);
Dst.Alpha = Src.Alpha;
return Dst;
}
} /* DXUnPreMultSample */
inline DXSAMPLE * DXUnPreMultArray(DXPMSAMPLE *pBuffer, ULONG cSamples)
{
for (ULONG i = 0; i < cSamples; i++)
{
BYTE SrcAlpha = pBuffer[i].Alpha;
if (SrcAlpha != 0xFF && SrcAlpha != 0)
{
pBuffer[i].Blue = (BYTE)((pBuffer[i].Blue * 255) / SrcAlpha);
pBuffer[i].Green = (BYTE)((pBuffer[i].Green * 255) / SrcAlpha);
pBuffer[i].Red = (BYTE)((pBuffer[i].Red * 255) / SrcAlpha);
}
}
return (DXSAMPLE *)pBuffer;
}
//
// This returns the result of 255-Alpha which is computed by doing a NOT
//
inline BYTE DXInvertAlpha( BYTE Alpha ) { return (BYTE)~Alpha; }
inline DWORD DXScaleSample( DWORD Src, ULONG beta )
{
ULONG t1, t2;
t1 = (Src & 0x00ff00ff) * beta + 0x00800080;
t1 = ((t1 + ((t1 >> 8) & 0x00ff00ff)) >> 8) & 0x00ff00ff;
t2 = ((Src >> 8) & 0x00ff00ff) * beta + 0x00800080;
t2 = (t2 + ((t2 >> 8) & 0x00ff00ff)) & 0xff00ff00;
return (DWORD)(t1 | t2);
}
inline DWORD DXScaleSamplePercent( DWORD Src, float Percent )
{
if (Percent > (254.0f / 255.0f)) {
return Src;
}
else
{
return DXScaleSample(Src, (BYTE)(Percent * 255));
}
}
inline void DXCompositeOver(DXPMSAMPLE & Dst, const DXPMSAMPLE & Src)
{
if (Src.Alpha)
{
ULONG Beta = DXInvertAlpha(Src.Alpha);
if (Beta)
{
Dst = Src + DXScaleSample(Dst, Beta);
}
else
{
Dst = Src;
}
}
}
inline DXPMSAMPLE DXCompositeUnder(DXPMSAMPLE Dst, DXPMSAMPLE Src )
{
return Dst + DXScaleSample(Src, DXInvertAlpha(Dst.Alpha));
}
inline DXBASESAMPLE DXApplyLookupTable(const DXBASESAMPLE Src, const BYTE * pTable)
{
DXBASESAMPLE Dest;
Dest.Blue = pTable[Src.Blue];
Dest.Green = pTable[Src.Green];
Dest.Red = pTable[Src.Red];
Dest.Alpha = pTable[Src.Alpha];
return Dest;
}
inline DXBASESAMPLE * DXApplyLookupTableArray(DXBASESAMPLE *pBuffer, ULONG cSamples, const BYTE * pTable)
{
for (ULONG i = 0; i < cSamples; i++)
{
DWORD v = pBuffer[i];
DWORD a = pTable[v >> 24];
DWORD r = pTable[(BYTE)(v >> 16)];
DWORD g = pTable[(BYTE)(v >> 8)];
DWORD b = pTable[(BYTE)v];
pBuffer[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
return pBuffer;
}
inline DXBASESAMPLE * DXApplyColorChannelLookupArray(DXBASESAMPLE *pBuffer,
ULONG cSamples,
const BYTE * pAlphaTable,
const BYTE * pRedTable,
const BYTE * pGreenTable,
const BYTE * pBlueTable)
{
for (ULONG i = 0; i < cSamples; i++)
{
pBuffer[i].Blue = pBlueTable[pBuffer[i].Blue];
pBuffer[i].Green = pGreenTable[pBuffer[i].Green];
pBuffer[i].Red = pRedTable[pBuffer[i].Red];
pBuffer[i].Alpha = pAlphaTable[pBuffer[i].Alpha];
}
return pBuffer;
}
//
// CDXScale helper class
//
// This class uses a pre-computed lookup table to scale samples. For scaling large
// arrays of samples to a constant scale, this is much faster than using even MMX
// instructions. This class is usually declared as a member of another class and
// is most often used to apply a global opacity to a set of samples.
//
// When using this class, you must always check for the two special cases of clear
// and opaque before calling any of the scaling member functions. Do this by using
// the ScaleType() inline function. Your code should look somthing like this:
//
// if (ScaleType() == DXRUNTYPE_CLEAR)
// Do whatever you do for a 0 alpha set of samples -- usually just ignore them
// else if (ScaleType() == DXRUNTYPE_OPAQUE)
// Do whatever you would do for a non-scaled set of samples
// else
// Scale the samples by using ScaleSample or one of the ScaleArray members
//
// If you call any of the scaling members when the ScaleType() is either clear or
// opaque, you will GP fault becuase the lookup table will not be allocated.
//
// The scale can be set using either a floating point number between 0 and 1 using:
// CDXScale::SetScale / CDXScale::GetScale
// or you can use a byte integer value by using:
// CDXScale::SetScaleAlphaValue / CDXScale::GetScaleAlphaValue
//
class CDXScale
{
private:
float m_Scale;
BYTE m_AlphaScale;
BYTE *m_pTable;
HRESULT InternalSetScale(BYTE Scale)
{
if (m_AlphaScale == Scale) return S_OK;
if (Scale == 0 || Scale == 255)
{
delete m_pTable;
m_pTable = NULL;
}
else
{
if(!m_pTable)
{
m_pTable = new BYTE[256];
if(!m_pTable )
{
return E_OUTOFMEMORY;
}
}
for (int i = 0; i < 256; ++i )
{
m_pTable[i] = (BYTE)((i * Scale) / 255);
}
}
m_AlphaScale = Scale;
return S_OK;
}
public:
CDXScale() :
m_Scale(1.0f),
m_AlphaScale(0xFF),
m_pTable(NULL)
{}
~CDXScale()
{
delete m_pTable;
}
DXRUNTYPE ScaleType()
{
if (m_AlphaScale == 0) return DXRUNTYPE_CLEAR;
if (m_AlphaScale == 0xFF) return DXRUNTYPE_OPAQUE;
return DXRUNTYPE_TRANS;
}
HRESULT SetScaleAlphaValue(BYTE Alpha)
{
HRESULT hr = InternalSetScale(Alpha);
if (SUCCEEDED(hr))
{
m_Scale = ((float)Alpha) / 255.0f;
}
return hr;
}
BYTE GetScaleAlphaValue(void)
{
return m_AlphaScale;
}
HRESULT SetScale(float Scale)
{
HRESULT hr = S_OK;
if(( Scale < 0.0f ) || ( Scale > 1.0f ) )
{
hr = E_INVALIDARG;
}
else
{
ULONG IntScale = (ULONG)(Scale * 256.0f); // Round up alpha (.9999 = 255 = Solid)
if (IntScale > 255)
{
IntScale = 255;
}
hr = SetScaleAlphaValue((BYTE)IntScale);
if (SUCCEEDED(hr))
{
m_Scale = Scale;
}
}
return hr;
}
float GetScale() const
{
return m_Scale;
}
DXRUNTYPE ScaleType() const
{
return (m_pTable ? DXRUNTYPE_TRANS : (m_AlphaScale ? DXRUNTYPE_OPAQUE : DXRUNTYPE_CLEAR));
}
DWORD ScaleSample(const DWORD s) const
{
return DXApplyLookupTable((DXBASESAMPLE)s, m_pTable);
}
DXBASESAMPLE * ScaleBaseArray(DXBASESAMPLE * pBuffer, ULONG cSamples) const
{
return DXApplyLookupTableArray(pBuffer, cSamples, m_pTable);
}
DXPMSAMPLE * ScalePremultArray(DXPMSAMPLE * pBuffer, ULONG cSamples) const
{
return (DXPMSAMPLE *)DXApplyLookupTableArray(pBuffer, cSamples, m_pTable);
}
DXSAMPLE * ScaleArray(DXSAMPLE * pBuffer, ULONG cSamples) const
{
return (DXSAMPLE *)DXApplyLookupTableArray(pBuffer, cSamples, m_pTable);
}
DXSAMPLE * ScaleArrayAlphaOnly(DXSAMPLE *pBuffer, ULONG cSamples) const
{
const BYTE *pTable = m_pTable;
for (ULONG i = 0; i < cSamples; i++)
{
pBuffer[i].Alpha = pTable[pBuffer[i].Alpha];
}
return pBuffer;
}
};
inline DWORD DXWeightedAverage( DXBASESAMPLE S1, DXBASESAMPLE S2, ULONG Wgt )
{
_ASSERT( Wgt < 256 );
ULONG t1, t2;
ULONG InvWgt = Wgt ^ 0xFF;
t1 = (((S1 & 0x00ff00ff) * Wgt) + ((S2 & 0x00ff00ff) * InvWgt )) + 0x00800080;
t1 = ((t1 + ((t1 >> 8) & 0x00ff00ff)) >> 8) & 0x00ff00ff;
t2 = ((((S1 >> 8) & 0x00ff00ff) * Wgt) + (((S2 >> 8) & 0x00ff00ff) * InvWgt )) + 0x00800080;
t2 = (t2 + ((t2 >> 8) & 0x00ff00ff)) & 0xff00ff00;
return (t1 | t2);
} /* DXWeightedAverage */
inline void DXWeightedAverageArray( DXBASESAMPLE* pS1, DXBASESAMPLE* pS2, ULONG Wgt,
DXBASESAMPLE* pResults, DWORD dwCount )
{
_ASSERT( pS1 && pS2 && pResults && dwCount );
for( DWORD i = 0; i < dwCount; ++i )
{
pResults[i] = DXWeightedAverage( pS1[i], pS2[i], Wgt );
}
} /* DXWeightedAverageArray */
inline void DXWeightedAverageArrayOver( DXPMSAMPLE* pS1, DXPMSAMPLE* pS2, ULONG Wgt,
DXPMSAMPLE* pResults, DWORD dwCount )
{
_ASSERT( pS1 && pS2 && pResults && dwCount );
DWORD i;
if( Wgt == 255 )
{
for( i = 0; i < dwCount; ++i )
{
DXCompositeOver( pResults[i], pS1[i] );
}
}
else
{
for( i = 0; i < dwCount; ++i )
{
DXPMSAMPLE Avg = DXWeightedAverage( (DXBASESAMPLE)pS1[i],
(DXBASESAMPLE)pS2[i], Wgt );
DXCompositeOver( pResults[i], Avg );
}
}
} /* DXWeightedAverageArrayOver */
inline void DXScalePremultArray(DXPMSAMPLE *pBuffer, ULONG cSamples, BYTE Weight)
{
for (DXPMSAMPLE *pBuffLimit = pBuffer + cSamples; pBuffer < pBuffLimit; pBuffer++)
{
*pBuffer = DXScaleSample(*pBuffer, Weight);
}
}
//
//
inline HRESULT DXClipToOutputWithPlacement(CDXDBnds & LogicalOutBnds, const CDXDBnds * pClipBnds, CDXDBnds & PhysicalOutBnds, const CDXDVec *pPlacement)
{
if(pClipBnds && (!LogicalOutBnds.IntersectBounds(*pClipBnds)))
{
return S_FALSE; // no intersect, we're done
}
else
{
CDXDVec vClipPos(false);
LogicalOutBnds.GetMinVector( vClipPos );
if (pPlacement)
{
vClipPos -= *pPlacement;
}
PhysicalOutBnds += vClipPos;
if (!LogicalOutBnds.IntersectBounds(PhysicalOutBnds))
{
return S_FALSE;
}
PhysicalOutBnds = LogicalOutBnds;
PhysicalOutBnds -= vClipPos;
}
return S_OK;
}
//
// Helper for converting a color ref to a DXSAMPLE
//
inline DWORD DXSampleFromColorRef(COLORREF cr)
{
DXSAMPLE Samp(0xFF, GetRValue(cr), GetGValue(cr), GetBValue(cr));
return Samp;
}
//
// Fill an entire surface with a color
//
inline HRESULT DXFillSurface( IDXSurface *pSurface, DXPMSAMPLE Color,
BOOL bDoOver = FALSE, ULONG ulTimeOut = 10000 )
{
IDXARGBReadWritePtr * pPtr;
HRESULT hr = pSurface->LockSurface( NULL, ulTimeOut, DXLOCKF_READWRITE,
IID_IDXARGBReadWritePtr, (void **)&pPtr, NULL);
if( SUCCEEDED(hr) )
{
pPtr->FillRect(NULL, Color, bDoOver);
pPtr->Release();
}
return hr;
} /* DXFillSurface */
//
// Fill a specified sub-rectangle of a surface with a color.
//
inline HRESULT DXFillSurfaceRect( IDXSurface *pSurface, RECT & rect, DXPMSAMPLE Color,
BOOL bDoOver = FALSE, ULONG ulTimeOut = 10000 )
{
CDXDBnds bnds(rect);
IDXARGBReadWritePtr * pPtr;
HRESULT hr = pSurface->LockSurface( &bnds, ulTimeOut, DXLOCKF_READWRITE,
IID_IDXARGBReadWritePtr, (void **)&pPtr, NULL);
if( SUCCEEDED(hr) )
{
pPtr->FillRect(NULL, Color, bDoOver);
pPtr->Release();
}
return hr;
} /* DXFillSurfaceRect */
//
// The DestBnds height and width must be greater than or equal to the source bounds.
//
// The dwFlags parameter uses the flags defined by IDXSurfaceFactory::BitBlt:
//
// DXBOF_DO_OVER
// DXBOF_DITHER
//
inline HRESULT DXBitBlt(IDXSurface * pDest, const CDXDBnds & DestBnds,
IDXSurface * pSrc, const CDXDBnds & SrcBnds,
DWORD dwFlags, ULONG ulTimeout)
{
IDXARGBReadPtr * pIn;
HRESULT hr;
hr = pSrc->LockSurface( &SrcBnds, INFINITE,
(dwFlags & DXBOF_DO_OVER) ? (DXLOCKF_READ | DXLOCKF_WANTRUNINFO) : DXLOCKF_READ,
IID_IDXARGBReadPtr, (void**)&pIn, NULL);
if(SUCCEEDED(hr))
{
IDXARGBReadWritePtr * pOut;
hr = pDest->LockSurface( &DestBnds, INFINITE, DXLOCKF_READWRITE,
IID_IDXARGBReadWritePtr, (void**)&pOut, NULL );
if (SUCCEEDED(hr))
{
DXSAMPLEFORMATENUM InNativeType = pIn->GetNativeType(NULL);
DXSAMPLEFORMATENUM OutNativeType = pOut->GetNativeType(NULL);
BOOL bSrcIsOpaque = !(InNativeType & (DXPF_TRANSLUCENCY | DXPF_TRANSPARENCY));
const ULONG Width = SrcBnds.Width();
DXPMSAMPLE *pSrcBuff = NULL;
if( InNativeType != DXPF_PMARGB32 )
{
pSrcBuff = DXPMSAMPLE_Alloca(Width);
}
//
// Don't dither unless the dest has a greater error term than the source.
//
if ((dwFlags & DXBOF_DITHER) &&
((OutNativeType & DXPF_ERRORMASK) <= (InNativeType & DXPF_ERRORMASK)))
{
dwFlags &= (~DXBOF_DITHER);
}
if ((dwFlags & DXBOF_DITHER) || ((dwFlags & DXBOF_DO_OVER) && bSrcIsOpaque== 0))
{
//--- Allocate a working output buffer if necessary
DXPMSAMPLE *pDestBuff = NULL;
if( OutNativeType != DXPF_PMARGB32 )
{
pDestBuff = DXPMSAMPLE_Alloca(Width);
}
//--- Process each output row
// Note: Output coordinates are relative to the lock region
const ULONG Height = SrcBnds.Height();
if (dwFlags & DXBOF_DITHER)
{
DXPMSAMPLE * pSrcDitherBuff = pSrcBuff;
if (pSrcDitherBuff == NULL)
{
pSrcDitherBuff = DXPMSAMPLE_Alloca(Width);
}
const BOOL bCopy = ((dwFlags & DXBOF_DO_OVER) == 0);
//
// Set up the dither descriptor (some things are constant)
//
DXDITHERDESC dd;
dd.pSamples = pSrcDitherBuff;
dd.DestSurfaceFmt = OutNativeType;
for(ULONG Y = 0; Y < Height; ++Y )
{
dd.x = DestBnds.Left();
dd.y = DestBnds.Top() + Y;
const DXRUNINFO *pRunInfo;
ULONG cRuns = pIn->MoveAndGetRunInfo(Y, &pRunInfo);
pOut->MoveToRow( Y );
do
{
ULONG ulRunLen = pRunInfo->Count;
if (pRunInfo->Type == DXRUNTYPE_CLEAR)
{
pIn->Move(ulRunLen);
if (bCopy)
{
//
// The only way to avoid calling a constructor function to create
// a pmsample from 0 is to declare a variable and then assign it!
//
DXPMSAMPLE NullColor;
NullColor = 0;
pOut->FillAndMove(pSrcDitherBuff, NullColor, ulRunLen, FALSE);
}
else
{
pOut->Move(ulRunLen);
}
dd.x += ulRunLen;
}
else
{
pIn->UnpackPremult(pSrcDitherBuff, ulRunLen, TRUE);
dd.cSamples = ulRunLen;
DXDitherArray(&dd);
dd.x += ulRunLen;
if (bCopy || pRunInfo->Type == DXRUNTYPE_OPAQUE)
{
pOut->PackPremultAndMove(pSrcDitherBuff, ulRunLen);
}
else
{
pOut->OverArrayAndMove(pDestBuff, pSrcDitherBuff, ulRunLen);
}
}
pRunInfo++;
cRuns--;
} while (cRuns);
}
}
else
{
for(ULONG Y = 0; Y < Height; ++Y )
{
const DXRUNINFO *pRunInfo;
ULONG cRuns = pIn->MoveAndGetRunInfo(Y, &pRunInfo);
pOut->MoveToRow( Y );
do
{
ULONG ulRunLen = pRunInfo->Count;
switch (pRunInfo->Type)
{
case DXRUNTYPE_CLEAR:
pIn->Move(ulRunLen);
pOut->Move(ulRunLen);
break;
case DXRUNTYPE_OPAQUE:
pOut->CopyAndMoveBoth(pDestBuff, pIn, ulRunLen, TRUE);
break;
case DXRUNTYPE_TRANS:
{
DXPMSAMPLE *pSrc = pIn->UnpackPremult(pSrcBuff, ulRunLen, TRUE);
DXPMSAMPLE *pDest = pOut->UnpackPremult(pDestBuff, ulRunLen, FALSE);
DXOverArrayMMX(pDest, pSrc, ulRunLen);
pOut->PackPremultAndMove(pDestBuff, ulRunLen);
break;
}
case DXRUNTYPE_UNKNOWN:
{
pOut->OverArrayAndMove(pDestBuff,
pIn->UnpackPremult(pSrcBuff, ulRunLen, TRUE),
ulRunLen);
break;
}
}
pRunInfo++;
cRuns--;
} while (cRuns);
}
}
}
else // if ((dwFlags & DXBOF_DITHER) || ((dwFlags & DXBOF_DO_OVER) && bSrcIsOpaque== 0))
{
// This code is run if:
//
// !(dwFlags & DXBOF_DITHER)
// && !((dwFlags & DXBOF_DO_OVER) && bSrcIsOpaque == 0)
//
// In English:
//
// This code is run if 1) dithering is not required
// and 2) blending with output is not required because it was
// not requested or because it's not needed because the source
// pixels are all opaque.
// hrDD is initialized to failure so that in the event that the
// pixel formats don't match or the pixel format supports
// transparency, the CopyRect will still run.
HRESULT hrDD = E_FAIL;
DXSAMPLEFORMATENUM formatIn = pIn->GetNativeType(NULL);
// If the pixel formats match and do not support transparency
// (because it's not supported by ddraw yet) try to use a
// ddraw blit instead of CopyRect.
if ((formatIn == pOut->GetNativeType(NULL))
&& !(formatIn & DXPF_TRANSPARENCY))
{
CComPtr<IDirectDrawSurface> cpDDSrc;
// Get source ddraw surface pointer.
hrDD = pSrc->QueryInterface(IID_IDirectDrawSurface,
(void **)&cpDDSrc);
if (SUCCEEDED(hrDD))
{
CComPtr<IDirectDrawSurface> cpDDDest;
// Get destination ddraw surface pointer.
hrDD = pDest->QueryInterface(IID_IDirectDrawSurface,
(void **)&cpDDDest);
if (SUCCEEDED(hrDD))
{
RECT rcSrc;
RECT rcDest;
SrcBnds.GetXYRect(rcSrc);
DestBnds.GetXYRect(rcDest);
// Attempt the ddraw blit.
hrDD = cpDDDest->Blt(&rcDest, cpDDSrc, &rcSrc,
0, NULL);
}
}
}
// If hrDD has failed at this point, it means a direct draw blit
// was not possible and a CopyRect is needed to perform the
// copy.
if (FAILED(hrDD))
{
pOut->CopyRect(pSrcBuff, NULL, pIn, NULL, bSrcIsOpaque);
}
}
pOut->Release();
}
pIn->Release();
}
return hr;
}
inline HRESULT DXSrcCopy(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
IDXSurface *pSrcSurface, int nXSrc, int nYSrc)
{
IDXDCLock *pDCLock;
HRESULT hr = pSrcSurface->LockSurfaceDC(NULL, INFINITE, DXLOCKF_READ, &pDCLock);
if (SUCCEEDED(hr))
{
::BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, pDCLock->GetDC(), nXSrc, nYSrc, SRCCOPY);
pDCLock->Release();
}
return hr;
}
//
//=== Pointer validation functions
//
inline BOOL DXIsBadReadPtr( const void* pMem, UINT Size )
{
#if !defined( _DEBUG ) && defined( DXTRANS_NOROBUST )
return false;
#else
return ::IsBadReadPtr( pMem, Size );
#endif
}
inline BOOL DXIsBadWritePtr( void* pMem, UINT Size )
{
#if !defined( _DEBUG ) && defined( DXTRANS_NOROBUST )
return false;
#else
return ::IsBadWritePtr( pMem, Size );
#endif
}
inline BOOL DXIsBadInterfacePtr( const IUnknown* pUnknown )
{
#if !defined( _DEBUG ) && defined( DXTRANS_NOROBUST )
return false;
#else
return ( ::IsBadReadPtr( pUnknown, sizeof( *pUnknown ) ) ||
::IsBadCodePtr( (FARPROC)((void **)pUnknown)[0] ))?
(true):(false);
#endif
}
#define DX_IS_BAD_OPTIONAL_WRITE_PTR(p) ((p) && DXIsBadWritePtr(p, sizeof(p)))
#define DX_IS_BAD_OPTIONAL_READ_PTR(p) ((p) && DXIsBadReadPtr(p, sizeof(p)))
#define DX_IS_BAD_OPTIONAL_INTERFACE_PTR(p) ((p) && DXIsBadInterfacePtr(p))
#endif /* This must be the last line in the file */