|
|
/**************************************************************************\
* * Copyright (c) 1999 Microsoft Corporation * * Module Name: * * MetaFile.cpp * * Abstract: * * Metafile object handling * * Created: * * 4/14/1999 DCurtis * \**************************************************************************/
#include "precomp.hpp"
#include "MetaWmf.hpp"
#define GDIP_TRANSPARENT_COLOR_KEY 0xAA0D0B0C
#define GDIP_WMF_PLACEABLEKEY 0x9AC6CDD7 // for Placeable WMFs
#define GDIP_DO_CALLBACK_MASK 0x00000003 // when to do callback
// Metafile constants not in Windows.h
#define METAVERSION300 0x0300
#define METAVERSION100 0x0100
#define MEMORYMETAFILE 1
#define DISKMETAFILE 2
typedef VOID (EmfPlusRecordPlay::*PLAYRECORDFUNC)(MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize) const; PLAYRECORDFUNC RecordPlayFuncs[];
/**************************************************************************\
* * Function Description: * * If the points were stored as 16-bit points, then convert them back to * REAL points. Otherwise, just return convert the point data pointer * to a REAL point pointer and return. * * Arguments: * * [IN] pointData - the point data that was recorded * [IN] count - the number of points * [IN] flags - says if the point data is 16-bit points or not * [IN] bufferSize - the size of the buffer * [IN/OUT] buffer - for converting back to REAL points * [IN/OUT] allocedBuffer - if buffer not big enough, alloc new one here * * Return Value: * * GpPointF * - the REAL points to play back * * Created: * * 6/15/1999 DCurtis * \**************************************************************************/ GpPointF * GetPointsForPlayback( const BYTE * pointData, UINT pointDataSize, INT count, INT flags, UINT bufferSize, BYTE * buffer, BYTE * & allocedBuffer ) { GpPointF * points = NULL;
if (count > 0) { if ((flags & GDIP_EPRFLAGS_COMPRESSED) != 0) { if (pointDataSize >= (sizeof(GpPoint16) * count)) { UINT sizePoints = count * sizeof(GpPointF);
if (sizePoints <= bufferSize) { points = reinterpret_cast<GpPointF *>(buffer); } else { if ((allocedBuffer = new BYTE[sizePoints]) == NULL) { return NULL; } points = reinterpret_cast<GpPointF *>(allocedBuffer); } const GpPoint16 * points16 = reinterpret_cast<const GpPoint16 *>(pointData); do {
count--; points[count].X = points16[count].X; points[count].Y = points16[count].Y; } while (count > 0); } else { WARNING(("pointDataSize is too small")); } } else if (pointDataSize >= (sizeof(GpPointF) * count)) { points = (GpPointF *)(pointData); } else { WARNING(("pointDataSize is too small")); } } return points; }
/**************************************************************************\
* * Function Description: * * If the rects were stored as 16-bit rects, then convert them back to * REAL rects. Otherwise, just return convert the rect data pointer * to a REAL rect pointer and return. * * Arguments: * * [IN] rectData - the rect data that was recorded * [IN] count - the number of rects * [IN] flags - says if the point data is 16-bit rects or not * [IN] bufferSize - the size of the buffer * [IN/OUT] buffer - for converting back to REAL rects * [IN/OUT] allocedBuffer - if buffer not big enough, alloc new one here * * Return Value: * * GpPointF * - the REAL points to play back * * Created: * * 6/15/1999 DCurtis * \**************************************************************************/ GpRectF * GetRectsForPlayback( const BYTE * rectData, UINT rectDataSize, INT count, INT flags, UINT bufferSize, BYTE * buffer, BYTE * & allocedBuffer ) { GpRectF * rects = NULL;
if (count > 0) { if ((flags & GDIP_EPRFLAGS_COMPRESSED) != 0) { if (rectDataSize >= (sizeof(GpRect16) * count)) { UINT sizeRects = count * sizeof(GpRectF);
if (sizeRects <= bufferSize) { rects = reinterpret_cast<GpRectF *>(buffer); } else { if ((allocedBuffer = new BYTE[sizeRects]) == NULL) { return NULL; } rects = reinterpret_cast<GpRectF *>(allocedBuffer); } const GpRect16 * rects16 = reinterpret_cast<const GpRect16 *>(rectData); do {
count--; rects[count].X = rects16[count].X; rects[count].Y = rects16[count].Y; rects[count].Width = rects16[count].Width; rects[count].Height = rects16[count].Height; } while (count > 0); } else { WARNING(("rectDataSize is too small")); } } else if (rectDataSize >= (sizeof(GpRectF) * count)) { rects = (GpRectF *)(rectData); } else { WARNING(("rectDataSize is too small")); } } return rects; }
inline INT16 GetWmfPlaceableCheckSum( const WmfPlaceableFileHeader * wmfPlaceableFileHeader ) { const INT16 * headerWords = (const INT16 *)wmfPlaceableFileHeader; INT16 checkSum = *headerWords++;
for (INT i = 9; i > 0; i--) { checkSum ^= *headerWords++; } return checkSum; }
inline BOOL WmfPlaceableHeaderIsValid( const WmfPlaceableFileHeader * wmfPlaceableFileHeader ) { ASSERT(wmfPlaceableFileHeader != NULL);
return ((wmfPlaceableFileHeader->Key == GDIP_WMF_PLACEABLEKEY) && (wmfPlaceableFileHeader->Checksum == GetWmfPlaceableCheckSum(wmfPlaceableFileHeader)) && (wmfPlaceableFileHeader->BoundingBox.Left != wmfPlaceableFileHeader->BoundingBox.Right) && (wmfPlaceableFileHeader->BoundingBox.Top != wmfPlaceableFileHeader->BoundingBox.Bottom)); }
inline BOOL WmfHeaderIsValid( const METAHEADER * wmfHeader ) { return (((wmfHeader->mtType == MEMORYMETAFILE) || (wmfHeader->mtType == DISKMETAFILE)) && (wmfHeader->mtHeaderSize == (sizeof(METAHEADER)/sizeof(WORD))) && ((wmfHeader->mtVersion == METAVERSION300) || (wmfHeader->mtVersion ==METAVERSION100))); }
VOID Init32BppDibToTransparent( UINT32 * bits, UINT numPixels );
GpStatus Draw32BppDib( GpGraphics * g, UINT32 * bits, INT width, INT height, const GpRectF & destRect, REAL dpi, BOOL compareAlpha );
extern "C" BOOL CALLBACK GdipPlayMetafileRecordCallback( EmfPlusRecordType recordType, UINT recordFlags, UINT recordDataSize, const BYTE * recordData, VOID * callbackData // player
);
// This method (defined below) enumerates/plays EMF+ comment records and also
// plays down-level GDI records, when appropriate.
extern "C" int CALLBACK EnumEmfWithDownLevel( HDC hdc, HANDLETABLE FAR * gdiHandleTable, CONST ENHMETARECORD * emfRecord, int numHandles, LPARAM play );
extern "C" int CALLBACK EnumEmfDownLevel( HDC hdc, HANDLETABLE FAR * gdiHandleTable, CONST ENHMETARECORD * emfRecord, int numHandles, LPARAM play );
extern "C" int CALLBACK EnumEmfToStream( HDC hdc, HANDLETABLE FAR * gdiHandleTable, CONST ENHMETARECORD * emfRecord, int numHandles, LPARAM stream );
// Separate this out so we can initialize it to 0 all at once
class MetafilePlayerBuffers { protected: BYTE RecordBuffer [GDIP_METAFILE_BUFFERSIZE]; BYTE PointsBuffer [GDIP_METAFILE_BUFFERSIZE]; GpObject * ObjectList [GDIP_MAX_OBJECTS]; INT MemberStack [GDIP_SAVE_STACK_SIZE]; GpObject * BackupObject [ObjectTypeMax - ObjectTypeMin + 1]; };
class MetafilePlayer : public MetafilePlayerBuffers { protected: BOOL Valid; UINT32 MaxStackSize; INT * Stack; IStream * Stream; BYTE * RecordAllocedBuffer; BYTE * PointsAllocedBuffer; GpSolidFill SolidBrush; BYTE * ConcatRecordBuffer; INT ConcatRecordBufferSize; BYTE * ConcatRecord; INT ConcatRecordTotalSize; INT ConcatRecordSize; UINT ConcatRecordFlags; InterpolationMode Interpolation;
public: GpGraphics * Graphics; // The graphics we're playing to
BOOL PlayEMFRecords; // TRUE when we see GetDC record
HDC Hdc; // For playing downlevel records
GpMatrix PreContainerMatrix; // Xform to use for down-level
UINT32 * BitmapBits; INT BitmapWidth; INT BitmapHeight; GpRectF BitmapDestRect; REAL BitmapDpi; GpRecolor * Recolor; MfEnumState * MfState; ColorAdjustType AdjustType; UINT MultiFormatSection; UINT CurFormatSection; BOOL PlayMultiFormatSection; EnumerateMetafileProc EnumerateCallback; // for metafile enumeration
VOID * CallbackData; // for metafile enumeration
BOOL EnumerateAborted; DrawImageAbort DrawImageCallback; VOID* DrawImageCallbackData; INT DrawImageCallbackCount; BOOL RopUsed;
public: // stream is NULL if using GDI to enumerate the hEmf.
MetafilePlayer( GpGraphics * g, UINT maxStackSize, GpRecolor * recolor, ColorAdjustType adjustType, EnumerateMetafileProc enumerateCallback, VOID * callbackData, DrawImageAbort drawImageCallback, VOID* drawImageCallbackData );
~MetafilePlayer();
BOOL IsValid() const { return Valid; }
VOID PrepareToPlay( GpGraphics * g, GpRecolor * recolor, ColorAdjustType adjustType, EnumerateMetafileProc enumerateCallback, VOID * callbackData, DrawImageAbort drawImageCallback, VOID* drawImageCallbackData );
VOID DonePlaying();
VOID InitForDownLevel() { if (Hdc == NULL) { Hdc = Graphics->GetHdc(); ASSERT(Hdc != NULL);
if (BitmapBits != NULL) { Init32BppDibToTransparent(BitmapBits, BitmapWidth*BitmapHeight); MfState->ResetRopUsed(); } } }
VOID DoneWithDownLevel() { PlayEMFRecords = FALSE; if (Hdc != NULL) { Graphics->ReleaseHdc(Hdc); Hdc = NULL;
if (BitmapBits != NULL) { // This is a hack to get around the problem that we are
// inside a container, but we don't want to be in the
// container for drawing the down-level records. We also
// don't want any transforms inside the EMF+ to affect the
// down-level records.
// We should probably do something about the clipping too,
// but for now, we won't worry about it.
GpMatrix saveWorldToDevice = Graphics->Context->WorldToDevice; Graphics->Context->WorldToDevice = PreContainerMatrix;
// Don't use NearestNeighbor to draw the rotated metafile --
// it looks bad, and doesn't really save any time.
InterpolationMode saveInterpolationMode = Graphics->Context->FilterType;
if (saveInterpolationMode == InterpolationModeNearestNeighbor) { Graphics->Context->FilterType = InterpolationModeBilinear; }
Graphics->Context->InverseOk = FALSE; Draw32BppDib(Graphics, BitmapBits, BitmapWidth, BitmapHeight, BitmapDestRect, BitmapDpi, !RopUsed);
// restore the interpolation mode (in case we changed it).
Graphics->Context->FilterType = saveInterpolationMode; Graphics->Context->WorldToDevice = saveWorldToDevice; Graphics->Context->InverseOk = FALSE; } } }
// returns 0 to abort playback, 1 to continue
INT ProcessDrawImageCallback( BOOL forceCallback ) { if (DrawImageCallback) { // A DrawImage record could have already been aborted, so
// we should immediately return.
if (EnumerateAborted) { return 0; // abort
} if (forceCallback) { DrawImageCallbackCount = 0; } if ((DrawImageCallbackCount++ & GDIP_DO_CALLBACK_MASK) == 0) { // The callback returns TRUE to abort, FALSE to continue.
return ((*DrawImageCallback)(DrawImageCallbackData)) ? 0 : 1; } } return 1; }
GpPointF * GetPoints( const BYTE * pointData, UINT pointDataSize, INT count, INT flags ) { return GetPointsForPlayback(pointData, pointDataSize, count, flags, GDIP_METAFILE_BUFFERSIZE, PointsBuffer, PointsAllocedBuffer); }
GpRectF * GetRects( const BYTE * rectData, UINT rectDataSize, INT count, INT flags ) { return GetRectsForPlayback(rectData, rectDataSize, count, flags, GDIP_METAFILE_BUFFERSIZE, PointsBuffer, PointsAllocedBuffer); }
GpObject * GetObject( UINT metaObjectId, ObjectType objectType );
GpBrush * GetBrush( UINT brushValue, INT flags );
GpString * GetString( const BYTE * stringData, INT len, INT flags ) { // !!! convert back from 8-bit to 16-bit chars if necessary
return new GpString((const WCHAR *)stringData, len); }
VOID AddObject( INT flags, const BYTE * data, UINT dataSize );
VOID NewSave( UINT stackIndex, INT saveID );
INT GetSaveID( UINT stackIndex );
VOID FreePointsBuffer() { if (PointsAllocedBuffer != NULL) { delete [] PointsAllocedBuffer; PointsAllocedBuffer = NULL; } }
GpStatus ConcatenateRecords( UINT recordFlags, INT recordDataSize, const BYTE * recordData );
GpStatus EnumerateEmfPlusRecords( UINT dataSize, // size of EMF+ record data
const BYTE * data // pointer to the EMF+ record data
);
GpStatus EnumerateEmfRecords( HDC hdc, HENHMETAFILE hEmf, const RECT * dest, const RECT * deviceRect, ENHMFENUMPROC enumProc );
GpStatus EnumerateWmfRecords( HDC hdc, HMETAFILE hWmf, const RECT * dstRect, const RECT * deviceRect ); };
VOID MetafilePlayer::PrepareToPlay( GpGraphics * g, GpRecolor * recolor, ColorAdjustType adjustType, EnumerateMetafileProc enumerateCallback, VOID * callbackData, DrawImageAbort drawImageCallback, VOID* drawImageCallbackData ) { ASSERT(g != NULL);
GpMemset(Stack, 0, MaxStackSize * sizeof (INT));
// Initialize all the buffers to 0
MetafilePlayerBuffers * buffers = this; GpMemset(buffers, 0, sizeof(MetafilePlayerBuffers));
PlayEMFRecords = FALSE; Hdc = NULL; Graphics = g; BitmapBits = NULL; BitmapWidth = 0; BitmapHeight = 0; Interpolation = g->GetInterpolationMode(); Recolor = recolor; AdjustType = adjustType; MultiFormatSection = 0; CurFormatSection = 0; PlayMultiFormatSection = TRUE; EnumerateAborted = FALSE; RopUsed = FALSE; if (enumerateCallback == NULL) { EnumerateCallback = GdipPlayMetafileRecordCallback; CallbackData = this; } else { EnumerateCallback = enumerateCallback; CallbackData = callbackData; } DrawImageCallback = drawImageCallback; DrawImageCallbackData = drawImageCallbackData; DrawImageCallbackCount = 0; ConcatRecord = NULL; ConcatRecordTotalSize = 0; ConcatRecordSize = 0; ConcatRecordFlags = 0;
// We need this for rendering GDI records within a GDI+ file.
// We have to do it before starting the container.
g->GetWorldToDeviceTransform(&(this->PreContainerMatrix)); }
MetafilePlayer::MetafilePlayer( GpGraphics * g, UINT maxStackSize, GpRecolor * recolor, ColorAdjustType adjustType, EnumerateMetafileProc enumerateCallback, VOID * callbackData, DrawImageAbort drawImageCallback, VOID* drawImageCallbackData ) { Valid = FALSE; MaxStackSize = GDIP_SAVE_STACK_SIZE; Stack = MemberStack; if (maxStackSize > GDIP_SAVE_STACK_SIZE) { Stack = new INT[maxStackSize]; if (Stack == NULL) { return; // Valid is FALSE
} MaxStackSize = maxStackSize; }
RecordAllocedBuffer = NULL; PointsAllocedBuffer = NULL; Recolor = NULL; MfState = NULL; ConcatRecordBuffer = NULL; ConcatRecordBufferSize = 0; PrepareToPlay(g, recolor, adjustType, enumerateCallback, callbackData, drawImageCallback, drawImageCallbackData ); Valid = TRUE; }
MetafilePlayer::~MetafilePlayer() { if (Stack != MemberStack) { delete [] Stack; } if (ConcatRecordBuffer) { GpFree(ConcatRecordBuffer); } }
inline bool ObjectTypeIsText(ObjectType type) { return type == ObjectTypeFont || type == ObjectTypeStringFormat; }
VOID MetafilePlayer::DonePlaying() { INT i;
i = 0; do { GpObject* pObject = ObjectList[i]; if (pObject) { GlobalTextLockConditional(ObjectTypeIsText(pObject->GetObjectType())); delete pObject; } } while ((++i) < GDIP_MAX_OBJECTS); }
GpObject * MetafilePlayer::GetObject( UINT metaObjectId, ObjectType objectType ) { GpObject * object = NULL;
// If the object was an unused optional parameter of some kind
// it knows how to handle a NULL object, so we return that.
if(metaObjectId == GDIP_OBJECTID_NONE) { return NULL; }
ASSERT(metaObjectId < GDIP_MAX_OBJECTS);
if (metaObjectId < GDIP_MAX_OBJECTS) { object = ObjectList[metaObjectId]; ASSERT (object != NULL); if (object != NULL) { ASSERT(object->GetObjectType() == objectType); if (object->GetObjectType() == objectType) { return object; } } } if (ObjectTypeIsValid(objectType)) { return BackupObject[objectType - ObjectTypeMin]; } return NULL; }
GpBrush * MetafilePlayer::GetBrush( UINT brushValue, INT flags ) { GpBrush * brush;
if ((flags & GDIP_EPRFLAGS_SOLIDCOLOR) != 0) { brush = &SolidBrush; (reinterpret_cast<GpSolidFill *>(brush))->SetColor(GpColor(brushValue)); if (Recolor != NULL) { brush->ColorAdjust(Recolor, AdjustType); } } else { brush = (GpBrush *)this->GetObject(brushValue, ObjectTypeBrush); } return brush; }
VOID MetafilePlayer::AddObject( INT flags, const BYTE * data, UINT dataSize ) { ObjectType objectType = GetObjectType(flags); UINT objectId = GetMetaObjectId(flags); GpObject ** objectList = ObjectList;
ASSERT((objectId < GDIP_MAX_OBJECTS) || (objectId == GDIP_BACKUP_OBJECTID));
GlobalTextLockConditional(ObjectTypeIsText(objectType));
// First see if this is a backup object
if ((objectId == GDIP_BACKUP_OBJECTID) && ObjectTypeIsValid(objectType)) { objectList = BackupObject; objectId = objectType - ObjectTypeMin; } if (objectId < GDIP_MAX_OBJECTS) { GpObject * object = objectList[objectId];
if (object != NULL) { object->Dispose(); }
object = GpObject::Factory(objectType, (const ObjectData *)data, dataSize);
if (object) { if (object->SetData(data, dataSize) == Ok) { if (Recolor != NULL) { object->ColorAdjust(Recolor, AdjustType); } if (!object->IsValid()) { WARNING(("Object is not valid")); object->Dispose(); object = NULL; } } else { WARNING(("Object Set Data failed")); object->Dispose(); object = NULL; } } else { WARNING(("Object Factory failed to create object")); } objectList[objectId] = object; } }
VOID MetafilePlayer::NewSave( UINT stackIndex, INT saveID ) { if (stackIndex >= MaxStackSize) { UINT maxStackSize = MaxStackSize + GDIP_SAVE_STACK_SIZE;
if (stackIndex >= maxStackSize) { ASSERT (0); return; } INT * newStack = new INT[maxStackSize];
if (newStack == NULL) { return; }
GpMemcpy(newStack, Stack, MaxStackSize * sizeof(INT)); GpMemset(newStack + MaxStackSize, 0, GDIP_SAVE_STACK_SIZE * sizeof (INT)); MaxStackSize = maxStackSize; if (Stack != MemberStack) { delete [] Stack; } Stack = newStack; }
Stack[stackIndex] = saveID; }
INT MetafilePlayer::GetSaveID( UINT stackIndex ) { ASSERT(stackIndex < MaxStackSize);
INT saveID = 0;
if (stackIndex < MaxStackSize) { saveID = Stack[stackIndex]; Stack[stackIndex] = 0; } return saveID; }
GpStatus MetafilePlayer::ConcatenateRecords( UINT recordFlags, INT recordDataSize, const BYTE * recordData ) { ASSERT((recordData != NULL) && (recordDataSize > sizeof(INT32)));
GpStatus status = Ok;
if ((recordFlags & GDIP_EPRFLAGS_CONTINUEOBJECT) != 0) { INT dataSizeLeft = ((const INT32 *)recordData)[0]; recordData += sizeof(INT32); recordDataSize -= sizeof(INT32);
if (dataSizeLeft <= recordDataSize) { WARNING(("Total Data Size incorrect")); status = InvalidParameter; goto DoneWithRecord; }
recordFlags &= ~GDIP_EPRFLAGS_CONTINUEOBJECT;
if (ConcatRecord == NULL) { if ((ConcatRecordBuffer == NULL) || (ConcatRecordBufferSize < dataSizeLeft)) { GpFree(ConcatRecordBuffer); ConcatRecordBuffer = (BYTE *)GpMalloc(dataSizeLeft); if (ConcatRecordBuffer == NULL) { ConcatRecordBufferSize = 0; return OutOfMemory; } ConcatRecordBufferSize = dataSizeLeft; } ConcatRecord = ConcatRecordBuffer; ConcatRecordTotalSize = dataSizeLeft; ConcatRecordSize = 0; ConcatRecordFlags = recordFlags; goto SkipContinueChecks; } } if (recordFlags != ConcatRecordFlags) { WARNING(("Record headers do not match")); status = InvalidParameter; goto DoneWithRecord; }
SkipContinueChecks: if (recordDataSize + ConcatRecordSize > ConcatRecordTotalSize) { WARNING(("sizes do not match")); recordDataSize = ConcatRecordTotalSize - ConcatRecordSize; }
GpMemcpy(ConcatRecord + ConcatRecordSize, recordData, recordDataSize); ConcatRecordSize += recordDataSize;
// see if we're done concatenating this record
if (ConcatRecordSize >= ConcatRecordTotalSize) { if (EnumerateCallback(EmfPlusRecordTypeObject, recordFlags, ConcatRecordTotalSize, ConcatRecord, CallbackData) == 0) { status = Aborted; } DoneWithRecord: ConcatRecord = NULL; ConcatRecordTotalSize = 0; ConcatRecordSize = 0; ConcatRecordFlags = 0; } return status; }
// Enumerate a set of EMF+ record contained inside an EMF comment record
// which has been enumerated from an EMF file.
//
// NOTE that we can't change the metafile data. If we need to change it,
// we must change a copy of it.
GpStatus MetafilePlayer::EnumerateEmfPlusRecords( UINT dataSize, // size of EMF+ record data
const BYTE * data // pointer to the EMF+ record data
) { ASSERT((dataSize > 0) && (data != NULL));
UINT curSize = 0; UINT recordSize; EmfPlusRecordType recordType; UINT recordFlags; UINT recordDataSize; const BYTE * recordData;
// while there is at least one record header size left
while (curSize <= (dataSize - sizeof(EmfPlusRecord))) { recordSize = ((const EmfPlusRecord *)data)->Size; recordDataSize = recordSize - sizeof(EmfPlusRecord);
// Make sure we don't read past the end of the buffer
// and make sure the size field is valid.
if ((recordSize >= sizeof(EmfPlusRecord)) && ((curSize + recordSize) <= dataSize) && (recordDataSize == ((const EmfPlusRecord *)data)->DataSize)) { recordType = (EmfPlusRecordType)(((const EmfPlusRecord *)data)->Type);
// make sure the recordType is in some reasonable range
// before we enumerate this record
if ((recordType >= EmfPlusRecordTypeMin) && (recordType < (EmfPlusRecordTypeMax + 1000))) { recordFlags = ((const EmfPlusRecord *)data)->Flags;
if (recordDataSize == 0) { recordData = NULL; } else { recordData = data + sizeof(EmfPlusRecord);
// if this object record is spread over several GDI comment
// records, then we need to concatenate them together before
// giving it to the callback
// The GDIP_EPRFLAGS_CONTINUEOBJECT flag is only valid
// with object records (since that bit is reused for other
// flags with other record types).
if ((recordType == EmfPlusRecordTypeObject) && (((recordFlags & GDIP_EPRFLAGS_CONTINUEOBJECT) != 0) || (ConcatRecord != NULL))) { if (this->ConcatenateRecords(recordFlags, recordDataSize, recordData) == Aborted) { return Aborted; } goto Increment; } }
if (EnumerateCallback(recordType, recordFlags, recordDataSize, recordData, CallbackData) == 0) { return Aborted; } } else { WARNING1("Bad EMF+ record type"); }
Increment: data += recordSize; curSize += recordSize;
// We have to set this here, because if we are just enumerating
// for an application (not playing), then the GetDCEPR::Play
// method will never be hit, so it will never get set!
if (recordType == EmfPlusRecordTypeGetDC) { // Flag that the next down-level records should be played.
PlayEMFRecords = TRUE; } } else { WARNING1("Bad EMF+ record size"); return InvalidParameter; } } return Ok; }
// Callback for EnumerateMetafile methods. The parameters are:
// recordType (if >= EmfPlusRecordTypeMin, it's an EMF+ record)
// flags (always 0 for EMF records)
// dataSize size of the data, or 0 if no data
// data pointer to the data, or NULL if no data (UINT32 aligned)
// callbackData pointer to callbackData, if any
// This method can then call Metafile::PlayRecord to play the
// record that was just enumerated. If this method returns
// FALSE, the enumeration process is aborted. Otherwise, it continues.
extern "C" BOOL CALLBACK GdipPlayMetafileRecordCallback( EmfPlusRecordType recordType, UINT recordFlags, UINT recordDataSize, const BYTE * recordData, VOID * callbackData // player
) { MetafilePlayer * player = (MetafilePlayer *)callbackData;
// See if it is an EMF+ record
if ((recordType >= EmfPlusRecordTypeMin) && (recordType <= EmfPlusRecordTypeMax)) { if (player->PlayMultiFormatSection) { (((const EmfPlusRecordPlay *)recordData)->*RecordPlayFuncs[recordType-EmfPlusRecordTypeMin])(player, recordType, recordFlags, recordDataSize); return player->ProcessDrawImageCallback(FALSE); } return 1; }
// See if we should play the WMF or EMF record
// Always play the header and EOF EMF records
if (player->PlayEMFRecords || (recordType == EmfRecordTypeHeader) || (recordType == EmfRecordTypeEOF)) { ASSERT(player->MfState != NULL);
BOOL forceCallback = player->MfState->ProcessRecord( recordType, recordDataSize, recordData); return player->ProcessDrawImageCallback(forceCallback); }
ASSERT (0); // shouldn't get here unless caller is doing something strange
return 1; // Keep playing
}
GpStatus GpMetafile::PlayRecord( EmfPlusRecordType recordType, UINT recordFlags, UINT recordDataSize, // must be multiple of 4 for EMF
const BYTE * recordData ) const { if ((State != PlayingMetafileState) || (((recordDataSize & 0x03) != 0) && (!GDIP_IS_WMF_RECORDTYPE(recordType)))) { return InvalidParameter; }
ASSERT(Player != NULL);
GdipPlayMetafileRecordCallback( recordType, recordFlags, recordDataSize, recordData, Player );
return Ok; }
inline BOOL IsEmfPlusRecord( CONST ENHMETARECORD * emfRecord ) { // dParm[0] is the comment data size
return ((emfRecord->iType == EMR_GDICOMMENT) && (emfRecord->nSize >= (sizeof(EMR) + (2 * sizeof(DWORD)))) && (emfRecord->dParm[1] == EMFPLUS_SIGNATURE)); }
// This method enumerates/plays EMF+ comment records and also
// plays down-level GDI records, when appropriate.
extern "C" int CALLBACK EnumEmfWithDownLevel( HDC hdc, // should be non-NULL
HANDLETABLE FAR * gdiHandleTable, CONST ENHMETARECORD * emfRecord, int numHandles, LPARAM play ) { if ((emfRecord != NULL) && (emfRecord->nSize >= sizeof(EMR)) && (play != NULL)) { MetafilePlayer * player = (MetafilePlayer *)play;
if (IsEmfPlusRecord(emfRecord)) { // We're done displaying GDI down-level records
player->DoneWithDownLevel();
// NOTE: cbData is the size of the comment data, not including
// the record header and not including itself.
//
// Must subtract out the Signature
INT dataSize = ((CONST EMRGDICOMMENT *)emfRecord)->cbData;
// subtract out signature
dataSize -= sizeof(INT32);
if (dataSize > 0) { if (player->EnumerateEmfPlusRecords( dataSize, ((CONST EMRGDICOMMENT *)emfRecord)->Data + sizeof(INT32)) == Aborted) { player->EnumerateAborted = TRUE; return 0; } } } else { EmfPlusRecordType recordType = (EmfPlusRecordType)(emfRecord->iType);
if (player->PlayEMFRecords || (recordType == EmfRecordTypeHeader) || (recordType == EmfRecordTypeEOF)) { if ((recordType != EmfRecordTypeHeader) && (recordType != EmfRecordTypeEOF)) { player->InitForDownLevel(); }
INT recordDataSize = emfRecord->nSize - sizeof(EMR); const BYTE * recordData = (const BYTE *)emfRecord->dParm;
if (recordDataSize <= 0) { recordDataSize = 0; recordData = NULL; }
player->MfState->StartRecord(hdc, gdiHandleTable, numHandles, emfRecord, recordType, recordDataSize, recordData);
if (player->EnumerateCallback(recordType, 0, recordDataSize, recordData, player->CallbackData) == 0) { player->EnumerateAborted = TRUE; return 0; } } } } else { WARNING(("Bad Enumeration Parameter")); } return 1; }
#define GDIP_MAX_DIBSECTION_SIZE 1024
#define GDIP_MINSCALED_DIBSECTION_SIZE (GDIP_MAX_DIBSECTION_SIZE / 2)
inline VOID AdjustForMaximumSize( LONG & bigSide, LONG & smallSide ) { // Try to keep the aspect ratio the same,
// but don't let the smaller side get too small.
REAL scaleFactor = GDIP_MAX_DIBSECTION_SIZE / (REAL)bigSide;
bigSide = GDIP_MAX_DIBSECTION_SIZE; if (smallSide > GDIP_MINSCALED_DIBSECTION_SIZE) { smallSide = GpRound(scaleFactor * smallSide);
if (smallSide < GDIP_MINSCALED_DIBSECTION_SIZE) { smallSide = GDIP_MINSCALED_DIBSECTION_SIZE; } } }
// !!! If the hdc is a EMF, we really should take the rasterization limit
// into account when deciding the size of the dest bitmap.
static HBITMAP CreateDibSection32Bpp( HDC hdc, const GpRectF & destRect, RECT & dest, // actual dest
UINT32 ** bits, REAL * dpi, // must init dpi before calling this method
GpMatrix * matrix ) { GpPointF destPoints[3]; REAL width; REAL height;
// When we rasterize a WMF or EMF into a Dib Section, we limit the size
// so that we don't use huge amounts of memory when printing or when
// drawing the rotated metafile into another metafile.
*bits = NULL;
// the capped dpi keeps the image from getting too large
destPoints[0].X = destRect.X; destPoints[0].Y = destRect.Y; destPoints[1].X = destRect.GetRight(); destPoints[1].Y = destRect.Y; destPoints[2].X = destRect.X; destPoints[2].Y = destRect.GetBottom();
matrix->Transform(destPoints, 3);
// determine the size of the image by getting the distance
// between the transformed device points
width = ::GetDistance(destPoints[0], destPoints[1]); height = ::GetDistance(destPoints[0], destPoints[2]);
dest.left = 0; dest.top = 0; dest.right = GpRound(width); dest.bottom = GpRound(height);
// make sure we don't transform down to 0 size
if ((dest.right == 0) || (dest.bottom == 0)) { return NULL; }
if ((dest.right > GDIP_MAX_DIBSECTION_SIZE) || (dest.bottom > GDIP_MAX_DIBSECTION_SIZE)) { REAL area = (REAL) dest.right * dest.bottom;
if (dest.right >= dest.bottom) { AdjustForMaximumSize(dest.right, dest.bottom); } else { AdjustForMaximumSize(dest.bottom, dest.right); }
REAL newArea = (REAL) dest.right * dest.bottom;
ASSERT(newArea > 0.0f && newArea <= area);
// Adjust the effective DPI of the bitmap based on how much smaller it is.
*dpi = (*dpi)*newArea/area; }
BITMAPINFO bmi;
// Create a 32-bpp dib section so we can add alpha to it
GpMemset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = dest.right; bmi.bmiHeader.biHeight = dest.bottom; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = dest.right * dest.bottom * 4;
return CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (VOID**)(bits), NULL, 0); }
VOID Init32BppDibToTransparent( UINT32 * bits, UINT numPixels ) { ASSERT((bits != NULL) && (numPixels > 0));
// initialize the image to a "transparent" color
while (numPixels--) { *bits++ = GDIP_TRANSPARENT_COLOR_KEY; } }
GpStatus Draw32BppDib( GpGraphics * g, UINT32 * bits, INT width, INT height, const GpRectF & destRect, REAL dpi, BOOL compareAlpha ) { // Make sure Gdi is done drawing to the dib section
::GdiFlush();
// Set the alpha value to 0 whereever the transparent
// color is still in the image and to FF everywhere else
UINT32 * bitmapBits = bits; UINT numPixels = width * height;
if (compareAlpha) { while (numPixels--) { if (*bitmapBits != GDIP_TRANSPARENT_COLOR_KEY) { *bitmapBits |= 0xFF000000; } else { *bitmapBits = 0; } bitmapBits++; } } else { while (numPixels--) { if ((*bitmapBits & 0x00FFFFFF) != (GDIP_TRANSPARENT_COLOR_KEY & 0x00FFFFFF)) { *bitmapBits |= 0xFF000000; } else { *bitmapBits = 0; } bitmapBits++; } }
// Create a bitamp from the dib section memory (which
// we've added alpha to). This constructor uses the
// memory we give it without doing a copy.
GpStatus status = GenericError;
GpBitmap * bitmap = new GpBitmap(width, height, -(width * 4), PIXFMT_32BPP_PARGB, (BYTE *)(bits + (width * (height - 1))));
if (bitmap != NULL) { if (bitmap->IsValid()) { bitmap->SetResolution(dpi, dpi);
// If we want the outside edges to look smooth, then we have
// to outcrop both the src and dest rects (by at least a pixel).
GpRectF srcRect(-1.0f, -1.0f, width + 2.0f, height + 2.0f); GpRectF outCroppedDestRect; REAL xSize; REAL ySize;
g->GetWorldPixelSize(xSize, ySize);
if (destRect.Width < 0.0f) { xSize = -xSize; } if (destRect.Height < 0.0f) { ySize = -ySize; }
outCroppedDestRect.X = destRect.X - xSize; outCroppedDestRect.Width = destRect.Width + (xSize * 2.0f); outCroppedDestRect.Y = destRect.Y - ySize; outCroppedDestRect.Height = destRect.Height + (ySize * 2.0f);
if (g->IsPrinter()) { // If the resulting transform (and source rect/dest rect) is
// a rotation by 90, 180, or 270 degrees. Then flip the bitmap
// appropriately. Fix up source rect, dest rect, and world to
// device appropriately. Restore W2D afterwards.
GpMatrix worldToDevice; g->GetWorldToDeviceTransform(&worldToDevice);
// Create the entire image source to device mapping to determine
// the entire rotation.
GpMatrix transform; transform.InferAffineMatrix(destRect, srcRect); GpMatrix::MultiplyMatrix(transform, transform, worldToDevice);
MatrixRotate rotation = transform.GetRotation();
if (rotation == MatrixRotateBy90 || rotation == MatrixRotateBy180 || rotation == MatrixRotateBy270) { // Normalize the destination rectangle
TransformBounds(NULL, outCroppedDestRect.GetLeft(), outCroppedDestRect.GetTop(), outCroppedDestRect.GetRight(), outCroppedDestRect.GetBottom(), &outCroppedDestRect); // Compute the destination rectangle in device space. Transform
// to device space and normalize.
// We know the world transform can have a 90 degree rotation
// so we need to do a point transform. We can do a 2 point
// transform and get the min and the max to make the bounding
// box
GpRectF deviceDestRect; TransformBounds(&worldToDevice, outCroppedDestRect.GetLeft(), outCroppedDestRect.GetTop(), outCroppedDestRect.GetRight(), outCroppedDestRect.GetBottom(), &deviceDestRect);
// Construct new world to page transform. Infers from the
// normalized outCroppedDestRect to normalized deviceDestRect.
//
// The World To Device is ordinarily computed as:
//
// World-To-Page * Scale(PageMultipliers) *
// Translate-By-Pixel-Offset * ContainerTransform
//
// The SetWorldTransform API only sets the World-To-Page.
//
// So we set the new World Transform as:
//
// World-To-Page * Inverse(World-To-Device)*
// Transform-CroppedDestRect-To-DeviceDestRect
//
// The result, as you can see from substitution is just
// Transform-CroppedDestRect-To-DeviceDestRect
GpMatrix newTransform; newTransform.InferAffineMatrix(deviceDestRect, outCroppedDestRect); g->GetDeviceToWorldTransform(&transform); GpMatrix::MultiplyMatrix(newTransform, newTransform, transform); g->GetWorldTransform(transform); // really World To Page XForm
GpMatrix::MultiplyMatrix(newTransform, newTransform, transform);
ASSERT(newTransform.IsTranslateScale());
// We are free to rotate in place because we know this is a
// throw away bitmap.
switch (rotation) { case MatrixRotateBy90: status = bitmap->RotateFlip(Rotate90FlipNone); break;
case MatrixRotateBy180: status = bitmap->RotateFlip(Rotate180FlipNone); break;
case MatrixRotateBy270: status = bitmap->RotateFlip(Rotate270FlipNone); break;
default: status = GenericError; ASSERT(FALSE); break; }
if (status == Ok) { g->SetWorldTransform(newTransform);
// Get new size (in case Height & Width were flipped.
Size bitmapSize; bitmap->GetSize(&bitmapSize);
srcRect.Width = bitmapSize.Width + 2.0f; srcRect.Height = bitmapSize.Height + 2.0f;
// Because the bitmap is already at device resolution
// (in most cases), nearest neighbor best preserves
// the image when printing.
InterpolationMode interpolationMode= g->GetInterpolationMode(); if (interpolationMode != InterpolationModeNearestNeighbor) { g->SetInterpolationMode(InterpolationModeNearestNeighbor); }
// Draw the new image with the rotation/shear
status = g->DrawImage(bitmap, outCroppedDestRect, srcRect, UnitPixel);
if (interpolationMode != InterpolationModeNearestNeighbor) { g->SetInterpolationMode(interpolationMode); }
g->SetWorldTransform(worldToDevice); }
goto cleanupBitmap; } }
// Draw the new image with the rotation/shear
status = g->DrawImage(bitmap, outCroppedDestRect, srcRect, UnitPixel); }
cleanupBitmap: // Now clean up everything
bitmap->Dispose(); } return status; }
// Get multipliers to convert to pixel units
VOID GetPixelMultipliers( GpPageUnit srcUnit, REAL srcDpiX, REAL srcDpiY, REAL * pixelMultiplierX, REAL * pixelMultiplierY ) { REAL multiplierX; REAL multiplierY;
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
switch (srcUnit) { default: ASSERT(0); // FALLTHRU
case UnitPixel: // Each unit represents one device pixel.
multiplierX = 1.0f; multiplierY = 1.0f; break;
case UnitPoint: // Each unit represents a 1/72 inch.
multiplierX = srcDpiX / 72.0f; multiplierY = srcDpiY / 72.0f; break;
case UnitInch: // Each unit represents 1 inch.
multiplierX = srcDpiX; multiplierY = srcDpiY; break;
case UnitDocument: // Each unit represents 1/300 inch.
multiplierX = srcDpiX / 300.0f; multiplierY = srcDpiY / 300.0f; break;
case UnitMillimeter: // Each unit represents 1 millimeter.
// One Millimeter is 0.03937 inches
// One Inch is 25.4 millimeters
multiplierX = srcDpiX / 25.4f; multiplierY = srcDpiY / 25.4f; break; } *pixelMultiplierX = multiplierX; *pixelMultiplierY = multiplierY; }
extern "C" int CALLBACK EnumEmfDownLevel( HDC hdc, // handle to device context
HANDLETABLE FAR * gdiHandleTable, // pointer to metafile handle table
CONST ENHMETARECORD * emfRecord, // pointer to metafile record
int numHandles, // count of objects
LPARAM play // pointer to optional data
) { if ((emfRecord != NULL) && (emfRecord->nSize >= sizeof(EMR)) && (play != NULL)) { // If we're in this method, we don't want to play any EMF+ records,
// so skip them, so we don't record them into another metafile.
if (!IsEmfPlusRecord(emfRecord)) { EmfPlusRecordType recordType = (EmfPlusRecordType)(emfRecord->iType); const BYTE * recordData = (const BYTE *)emfRecord->dParm; INT recordDataSize = emfRecord->nSize - sizeof(EMR);
if (recordDataSize <= 0) { recordDataSize = 0; recordData = NULL; }
MetafilePlayer * player = (MetafilePlayer *)play;
player->MfState->StartRecord(hdc, gdiHandleTable, numHandles, emfRecord, recordType, recordDataSize, recordData);
if (player->EnumerateCallback(recordType, 0, recordDataSize, recordData, player->CallbackData) == 0) { player->EnumerateAborted = TRUE; return 0; } } } else { WARNING(("Bad Enumeration Parameter")); } return 1; }
// Assumes the hdc has already been set up with the correct transform and
// clipping for displaying the metafile.
GpStatus MetafilePlayer::EnumerateEmfRecords( HDC hdc, HENHMETAFILE hEmf, const RECT * dest, const RECT * deviceRect, ENHMFENUMPROC enumProc ) { ASSERT(hdc != NULL); ASSERT(hEmf != NULL); ASSERT(dest->bottom > dest->top && dest->right > dest->left);
// GDI uses an Inclusive-Inclusive bound for Metafile Playback
RECT destRect = *dest; destRect.bottom--; destRect.right--;
GpStatus status = GenericError; BOOL externalEnumeration = (EnumerateCallback != GdipPlayMetafileRecordCallback);
EmfEnumState emfState(hdc, hEmf, &destRect, deviceRect, externalEnumeration, Interpolation, Graphics->Context, Recolor, AdjustType);
if (emfState.IsValid()) { MfState = &emfState;
// If the metafile is empty the following fails.
status = ::EnumEnhMetaFile(hdc, hEmf, enumProc, this, &destRect) ? Ok : GenericError; RopUsed = MfState->GetRopUsed(); MfState = NULL; if (EnumerateAborted) { status = Aborted; } } return status; }
extern "C" int CALLBACK EnumWmfDownLevel( HDC hdc, HANDLETABLE FAR * gdiHandleTable, METARECORD FAR * wmfRecord, int numHandles, LPARAM play ) { if ((wmfRecord != NULL) && (((UNALIGNED METARECORD *)wmfRecord)->rdSize >= 3) && (play != NULL)) { EmfPlusRecordType recordType = (EmfPlusRecordType)(GDIP_WMF_RECORD_TO_EMFPLUS(wmfRecord->rdFunction)); const BYTE * recordData = (const BYTE *)((UNALIGNED METARECORD *)wmfRecord)->rdParm; INT recordDataSize = (((UNALIGNED METARECORD *)wmfRecord)->rdSize * 2) - SIZEOF_METARECORDHEADER;
if (recordDataSize <= 0) { recordDataSize = 0; recordData = NULL; }
MetafilePlayer * player = (MetafilePlayer *)play;
player->MfState->StartRecord(hdc, gdiHandleTable, numHandles, wmfRecord, recordType, recordDataSize, recordData);
if (player->EnumerateCallback(recordType, 0, recordDataSize, recordData, player->CallbackData) == 0) { player->EnumerateAborted = TRUE; return 0; } } else { WARNING(("Bad Enumeration Parameter")); } return 1; }
// Assumes the hdc has already been set up with the correct transform and
// clipping for displaying the metafile.
GpStatus MetafilePlayer::EnumerateWmfRecords( HDC hdc, HMETAFILE hWmf, const RECT * dstRect, const RECT * deviceRect ) { ASSERT(hdc != NULL); ASSERT(hWmf != NULL);
GpStatus status = GenericError; BOOL externalEnumeration = (EnumerateCallback != GdipPlayMetafileRecordCallback);
WmfEnumState wmfState(hdc, hWmf, externalEnumeration, Interpolation, dstRect, deviceRect, Graphics->Context, Recolor, AdjustType);
if (wmfState.IsValid()) { MfState = &wmfState;
// If the metafile is empty the following fails.
status = ::EnumMetaFile(hdc, hWmf, EnumWmfDownLevel, (LPARAM)this) ? Ok : GenericError; RopUsed = MfState->GetRopUsed(); MfState = NULL; if (EnumerateAborted) { status = Aborted; } } return status; }
inline BOOL IsMetafileHdc( HDC hdc ) { DWORD hdcType = GetDCType(hdc); return ((hdcType == OBJ_ENHMETADC) || (hdcType == OBJ_METADC)); }
class SetupClippingForMetafilePlayback { public:
SetupClippingForMetafilePlayback( HDC hdc, DpDriver * driver, DpContext * context, BOOL forEMFPlus = FALSE ) { Hdc = hdc; Driver = driver; IsClip = FALSE; ClippedOut = FALSE; ReenableClipEscapes = FALSE;
if (!context->VisibleClip.IsInfinite()) { // Use GDI path clipping for playback to metafile only
UsePathClipping = IsMetafileHdc(hdc) && !context->IsPrinter;
// NT4 has a postscript driver bug where embedded EPS corrupt the
// current postscript clipping stack. To get around this, we resort to
// using GDI to clip for us.
// The problem is not limited to NT4 drivers alooe. There seems to
// be a family of injected EPS which doesn't interop with embedded
// postscript clipping escapes. The reason may have to do with the
// fact that many implementations don't reset the current path after
// sending the escape. See Office bugs 284388, 316074
if (context->IsPrinter) { if ((!forEMFPlus && !Globals::IsNt) || (Globals::IsNt && Globals::VersionInfoInitialized && ((Globals::OsVer.dwMajorVersion <= 4) || ((Globals::OsVer.dwMajorVersion >= 5) && (context->VisibleClip.IsSimple())) ))) { DriverPrint *pdriver = (DriverPrint*) Driver;
pdriver->DisableClipEscapes(); ReenableClipEscapes = TRUE; } }
// The trick here is we want to force the driver to clip, even if
// totally visible because cropping requires this. We pass in the flag
// to force clipping
GpRect drawBounds; context->VisibleClip.GetBounds(&drawBounds); if (drawBounds.IsEmpty()) { ClippedOut = TRUE; return; }
// Use appropriate driver clipping on playback
Driver->SetupClipping(Hdc, context, &drawBounds, IsClip, UsePathClipping, TRUE);
// Prevent metafile from drawing outside of the DestRect
// Can only do it for NT because Win9x doesn't restore the
// MetaRgn properly
// We handle this in the Metafile Player for Win9x
if (Globals::IsNt) { ::SetMetaRgn(hdc); } } }
~SetupClippingForMetafilePlayback() { if (IsClip) { Driver->RestoreClipping(Hdc, IsClip, UsePathClipping);
if (ReenableClipEscapes) { DriverPrint *pdriver = (DriverPrint*) Driver; pdriver->EnableClipEscapes(); } } }
BOOL IsClippedOut() { return ClippedOut; }
private: DpDriver * Driver; HDC Hdc; BOOL IsClip; BOOL UsePathClipping; BOOL ClippedOut; BOOL ReenableClipEscapes; };
// We already set up the transform to handle the srcRect and also to
// handle any flipping in the srcRect and destRect, so the 2 rects
// should have positive widths and heights at this point.
GpStatus GpGraphics::EnumEmf( MetafilePlayer * player, HENHMETAFILE hEmf, const GpRectF & destRect, const GpRectF & srcRect, // in pixel units
const GpRectF & deviceDestRect, // The destRect in Device Units
MetafileType type, BOOL isTranslateScale, BOOL renderToBitmap, const GpMatrix & flipAndCropTransform ) { ASSERT(hEmf != NULL);
HDC hdc = Context->GetHdc(Surface);
if (hdc == NULL) { return GenericError; }
INT saveDC; if ((saveDC = ::SaveDC(hdc)) == 0) { Context->ReleaseHdc(hdc, Surface); return GenericError; }
// Since we might have an HDC from a GpBitmap that's not clean, clean the
// HDC for now....
Context->CleanTheHdc(hdc);
player->PlayEMFRecords = TRUE; // play all EMF records
GpStatus status = Ok;
// the srcRect is already in pixel units
GpRect deviceSrcRect; deviceSrcRect.X = GpRound(srcRect.X); deviceSrcRect.Y = GpRound(srcRect.Y); deviceSrcRect.Width = GpRound(srcRect.Width); deviceSrcRect.Height = GpRound(srcRect.Height);
RECT deviceClipRect; deviceClipRect.left = RasterizerCeiling(deviceDestRect.X); deviceClipRect.top = RasterizerCeiling(deviceDestRect.Y); deviceClipRect.right = RasterizerCeiling(deviceDestRect.GetRight()); deviceClipRect.bottom = RasterizerCeiling(deviceDestRect.GetBottom());
// If it's a translate/scale matrix, do the transform ourselves,
// even on NT, so that we can control how the rounding is done
// to avoid cases where we round the metafile dest differently
// than the clipping rect, resulting in clipped out edges.
if (isTranslateScale) { SetupClippingForMetafilePlayback clipPlayback(hdc, Driver, Context); if (!clipPlayback.IsClippedOut()) { RECT deviceRect; GpPointF points[2];
points[0] = GpPointF(destRect.X, destRect.Y); points[1] = GpPointF(destRect.GetRight(), destRect.GetBottom()); player->PreContainerMatrix.Transform(points, 2); // We have to use the same method to convert REAL -> INT
// that we do when we set up the clipping. Otherwise, some
// of the points get rounded differently, causing a
// portion of the metafile to get clipped out.
deviceRect.left = RasterizerCeiling(points[0].X); deviceRect.top = RasterizerCeiling(points[0].Y); deviceRect.right = RasterizerCeiling(points[1].X); deviceRect.bottom = RasterizerCeiling(points[1].Y);
if (deviceRect.left < deviceRect.right && deviceRect.top < deviceRect.bottom) { if ((type == MetafileTypeWmf) || (type == MetafileTypeWmfPlaceable)) { // map the source rect to the dest rect to play the metafile
::SetMapMode(hdc, MM_ANISOTROPIC); ::SetWindowOrgEx(hdc, deviceSrcRect.X, deviceSrcRect.Y, NULL); ::SetWindowExtEx(hdc, deviceSrcRect.Width, deviceSrcRect.Height, NULL); ::SetViewportOrgEx(hdc, deviceRect.left, deviceRect.top, NULL); ::SetViewportExtEx(hdc, deviceRect.right - deviceRect.left, deviceRect.bottom - deviceRect.top, NULL);
status = player->EnumerateWmfRecords(hdc, (HMETAFILE)hEmf, &deviceRect, &deviceClipRect); } else // play as down-level EMF
{ ASSERT((type == MetafileTypeEmf) || (type == MetafileTypeEmfPlusDual)); status = player->EnumerateEmfRecords(hdc, hEmf, &deviceRect, &deviceClipRect, EnumEmfDownLevel); }
} // else empty rect, nothing to draw
} // else it's all clipped out
} else // flip and/or rotate and/or shear
{ RECT dest;
// Can't play a WMF with any rotate or skew transformation.
// If we're on NT but we're drawing to a metafile hdc, then we
// can't rely on the transforms working for that case.
if (!renderToBitmap) { dest.left = GpRound(destRect.X); dest.top = GpRound(destRect.Y); dest.right = GpRound(destRect.GetRight()); dest.bottom = GpRound(destRect.GetBottom());
if ((dest.bottom > dest.top) && (dest.right > dest.left)) { // If NT, then set the transform in GDI, and play the metafile
SetupClippingForMetafilePlayback clipPlayback(hdc, Driver, Context); if (!clipPlayback.IsClippedOut()) { ASSERT(Globals::IsNt);
SetGraphicsMode(hdc, GM_ADVANCED);
ASSERT(sizeof(XFORM) == sizeof(REAL)*6);
XFORM xform; player->PreContainerMatrix.GetMatrix((REAL*) &xform); ::SetWorldTransform(hdc, &xform);
RECT dummyRect = {0,0,0,0}; status = player->EnumerateEmfRecords(hdc, hEmf, &dest, &dummyRect, EnumEmfDownLevel); } } } else // Win9x with rotation or shear
// WinNT WMF with Rotate or shear
{ // 1 - Draw into a 32-bit DIB Section
// 2 - Create an image from the DIB Section
// 3 - Call g->DrawImage
status = GenericError;
UINT32 * bits; HBITMAP hBitmap;
player->BitmapDpi = Context->ContainerDpiX; hBitmap = CreateDibSection32Bpp(hdc, destRect, dest, &bits, &player->BitmapDpi, &player->PreContainerMatrix); if (hBitmap != NULL) { Init32BppDibToTransparent(bits, dest.right * dest.bottom);
HDC hdcDib = CreateCompatibleDC(NULL);
if (hdcDib != NULL) { ::SelectObject(hdcDib, hBitmap);
if ((type == MetafileTypeWmf) || (type == MetafileTypeWmfPlaceable)) { // map the source rect to the dest rect to play the metafile
::SetMapMode(hdcDib, MM_ANISOTROPIC); ::SetWindowOrgEx(hdcDib, deviceSrcRect.X, deviceSrcRect.Y, NULL); ::SetWindowExtEx(hdcDib, deviceSrcRect.Width, deviceSrcRect.Height, NULL); ::SetViewportOrgEx(hdcDib, 0, 0, NULL); ::SetViewportExtEx(hdcDib, dest.right, dest.bottom, NULL);
status = player->EnumerateWmfRecords(hdcDib, (HMETAFILE)hEmf, &dest, &dest); } else // play as down-level EMF
{ ASSERT((type == MetafileTypeEmf) || (type == MetafileTypeEmfPlusDual));
status = player->EnumerateEmfRecords(hdcDib, hEmf, &dest, &dest, EnumEmfDownLevel); } ::DeleteDC(hdcDib);
if (status != Aborted) { // Don't use NearestNeighbor to draw the rotated metafile --
// it looks bad, and doesn't really save any time.
InterpolationMode saveInterpolationMode = Context->FilterType;
if (saveInterpolationMode == InterpolationModeNearestNeighbor) { Context->FilterType = InterpolationModeBilinear; }
// Apply the flip/crop transform. Now the worldToDevice transform
// should be equivalent to the PreContainerMatrix.
this->SetWorldTransform(flipAndCropTransform);
status = Draw32BppDib(this, bits, dest.right, dest.bottom, destRect, player->BitmapDpi, !player->RopUsed);
// restore the interpolation mode (in case we changed it).
Context->FilterType = saveInterpolationMode; } } DeleteObject(hBitmap); } else if ((dest.right == 0) || (dest.bottom == 0)) { status = Ok; } } }
::RestoreDC(hdc, saveDC); Context->ReleaseHdc(hdc, Surface); return status; }
// We already set up the transform to handle the srcRect and also to
// handle any flipping in the srcRect and destRect, so the 2 rects
// should have positive widths and heights at this point.
GpStatus GpGraphics::EnumEmfPlusDual( MetafilePlayer * player, HENHMETAFILE hEmf, const GpRectF& destRect, // inclusive, exclusive
const GpRectF& deviceDestRect, // inclusive, exclusive
BOOL isTranslateScale, BOOL renderToBitmap ) { GpStatus status = Ok; HDC hdc; HWND hwnd = Context->Hwnd; INT saveDC = -1; BOOL needToReleaseHdc = FALSE;
// We are going to take the role of the application and set up the HDC
// like we want it and then let GDI+ change it from there. This is so
// that when we play back the GDI records, the HDC will already be set
// up correctly so those records get played back in the right place.
// In other words, I'm doing my own version of Context->GetHdc().
Surface->Flush(FlushIntentionFlush);
if (hwnd != NULL) { // We have to guarantee that we use the same HDC throughout the
// enumeration/playing of the metafile -- so change how the HDC is
// set up in the graphics context (if we need to).
ASSERT(Context->Hdc == NULL); ASSERT(Context->SaveDc == 0);
hdc = ::GetCleanHdc(hwnd); if (hdc == NULL) { WARNING(("GetCleanHdc failed")); return Win32Error; }
Context->Hwnd = NULL; Context->Hdc = hdc; } else { if ((hdc = Context->Hdc) != NULL) { // Restore the HDC back to the state the application had it in.
Context->ResetHdc(); } else // might be a bitmap surface
{ hdc = Context->GetHdc(Surface);
// Still have to call CleanTheHdc to fix bug #121666.
// It seems like the hdc should have come back clean
// from the context.
if (hdc == NULL) { WARNING(("Could not get an hdc")); return InvalidParameter; } needToReleaseHdc = TRUE; } // Now save the state of the HDC so we can get back to it later.
saveDC = SaveDC(hdc);
// Get the hdc into a clean state before we start.
Context->CleanTheHdc(hdc); }
// This block needs to be within braces so that SetupClippingForMetafile
// will have it's destructor called before the cleanup code.
{ // set the clipping for the down-level records
SetupClippingForMetafilePlayback clipPlayback(hdc, Driver, Context, TRUE); if (!clipPlayback.IsClippedOut()) { RECT deviceClipRect; deviceClipRect.left = RasterizerCeiling(deviceDestRect.X); deviceClipRect.top = RasterizerCeiling(deviceDestRect.Y); deviceClipRect.right = RasterizerCeiling(deviceDestRect.GetRight()); deviceClipRect.bottom = RasterizerCeiling(deviceDestRect.GetBottom());
// If it's a translate/scale matrix, do the transform ourselves,
// even on NT, so that we can control how the rounding is done
// to avoid cases where we round the metafile dest differently
// than the clipping rect, resulting in clipped out edges.
if (isTranslateScale) { RECT deviceRect; GpPointF points[2];
points[0] = GpPointF(destRect.X, destRect.Y); points[1] = GpPointF(destRect.GetRight(), destRect.GetBottom()); player->PreContainerMatrix.Transform(points, 2);
// We have to use the same method to convert REAL -> INT
// that we do when we set up the clipping. Otherwise, some
// of the points get rounded differently, causing a
// portion of the metafile to get clipped out.
deviceRect.left = RasterizerCeiling(points[0].X); deviceRect.top = RasterizerCeiling(points[0].Y); deviceRect.right = RasterizerCeiling(points[1].X); deviceRect.bottom = RasterizerCeiling(points[1].Y);
// If we don't have a destrect then we are done
if (deviceRect.left < deviceRect.right && deviceRect.top < deviceRect.bottom) { status = player->EnumerateEmfRecords(hdc, hEmf, &deviceRect, &deviceClipRect, EnumEmfWithDownLevel); } } else // flip and/or rotate and/or shear
{ RECT dest;
dest.left = GpRound(destRect.X); dest.top = GpRound(destRect.Y); dest.right = GpRound(destRect.GetRight()); dest.bottom = GpRound(destRect.GetBottom());
if ((dest.bottom > dest.top) && (dest.right > dest.left)) { // If we're on NT but we're drawing to a metafile hdc, then we
// can't rely on the transforms working for that case.
if (!renderToBitmap) { ASSERT(Globals::IsNt);
// set the transform for the down-level records
SetGraphicsMode(hdc, GM_ADVANCED);
ASSERT(sizeof(XFORM) == sizeof(REAL)*6);
// We want to set the transform in the HDC to the Pre-container matrix,
// so that it will be used to render the down-level records.
XFORM xform; player->PreContainerMatrix.GetMatrix((REAL*)(&xform)); ::SetWorldTransform(hdc, &xform);
RECT dummyRect = {0,0,0,0};
status = player->EnumerateEmfRecords(hdc, hEmf, &dest, &dummyRect, EnumEmfWithDownLevel); } else { UINT32 * bits; HBITMAP hBitmap;
// The down-level records will get drawn into a dib section HDC
// which will then be drawn to the real hdc by g->DrawImage.
// !!! I should probably save the visible clip region at this
// point so that clipping in the EMF+ doesn't affect the down-level
// records.
// Set the World Tranform to be the PreContainer Transform
// And restore it after we're transformed the dest
player->BitmapDpi = Context->ContainerDpiX; hBitmap = CreateDibSection32Bpp(hdc, destRect, dest, &bits, &player->BitmapDpi, &player->PreContainerMatrix);
status = GenericError;
if (hBitmap != NULL) { HDC hdcDib = CreateCompatibleDC(NULL);
if (hdcDib != NULL) { // set up the player data
player->BitmapBits = bits; player->BitmapWidth = dest.right; player->BitmapHeight = dest.bottom; player->BitmapDestRect = destRect;
::SelectObject(hdcDib, hBitmap);
status = player->EnumerateEmfRecords(hdcDib, hEmf, &dest, &dest, EnumEmfWithDownLevel);
::DeleteDC(hdcDib);
// so DoneWithDownLevel call below works right
player->BitmapBits = NULL; } DeleteObject(hBitmap); } else if ((dest.right == 0) || (dest.bottom == 0)) { status = Ok; } } } } } // else Nothing to play Everything is clipped out
}
// The Hdc should get set back to null when we reach the EMF+ EOF record
// But clean up anyway, just in case something went wrong.
player->DoneWithDownLevel();
// Restore the HDC back to the state we initially set up.
Context->ResetHdc();
if (hwnd != NULL) { ReleaseDC(hwnd, hdc);
// Now, restore the hwnd in the graphics context.
Context->Hwnd = hwnd; Context->Hdc = NULL; } else { // Now restore the HDC back to the real application state.
RestoreDC(hdc, saveDC);
if (needToReleaseHdc) { Context->ReleaseHdc(hdc); } }
return status; }
/**************************************************************************\
* * Function Description: * * GpMetafile destructor * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 6/15/1999 DCurtis * \**************************************************************************/ GpMetafile::~GpMetafile() { CleanUp(); }
VOID GpMetafile::CleanUp() { if ((MetaGraphics != NULL) && (!RequestedMetaGraphics)) { // If for some reason the app never requsted the MetaGraphics,
// then we'd better delete it.
delete MetaGraphics; }
if (State == RecordingMetafileState) { // EndRecording was never called, which means that the MetaGraphics
// was never deleted. So clean things up and invalidate the
// MetaGraphics.
ASSERT(MetaGraphics->Metafile != NULL); MetaGraphics->Metafile->EndRecording(); // deletes the recorder
// Endrecording sets the MetaGraphics to NULL so don't touch it anymore
WARNING(("Deleted Metafile before deleting MetaGraphics")); }
if ((Hemf != NULL) && DeleteHemf) { if (Header.IsEmfOrEmfPlus()) { DeleteEnhMetaFile(Hemf); } else { DeleteMetaFile((HMETAFILE)Hemf); } } if (Filename != NULL) { GpFree(Filename); } else if (Stream != NULL) // only for recording
{ // the stream position should already be at the end
// of the metafile.
Stream->Release(); }
delete Player; }
extern "C" int CALLBACK EnumGetEmfPlusHeader( HDC hdc, // should be NULL
HANDLETABLE FAR * gdiHandleTable, CONST ENHMETARECORD * emfRecord, int numHandles, LPARAM emfPlusHeader ) { if ((emfRecord != NULL) && (emfRecord->nSize >= sizeof(EMR)) && (emfPlusHeader != NULL)) { if (emfRecord->iType == EMR_HEADER) { return 1; // skip the header and keep enumerating
} if (IsEmfPlusRecord(emfRecord) && (emfRecord->nSize >= (sizeof(EMR) + sizeof(DWORD) + // comment data size
sizeof(INT32) + // signature
sizeof(EmfPlusRecord) + sizeof(EmfPlusHeaderRecord)))) { GpMemcpy((VOID*)emfPlusHeader, ((CONST EMRGDICOMMENT *)emfRecord)->Data + sizeof(INT32), sizeof(EmfPlusRecord) + sizeof(EmfPlusHeaderRecord)); } } else { WARNING(("Bad Enumeration Parameter")); } return 0; // don't enumerate any more records
}
HENHMETAFILE GetEmfFromWmfData( HMETAFILE hWmf, BYTE * wmfData, UINT size ) { if (wmfData == NULL || hWmf == NULL || size < (sizeof(METAHEADER)+sizeof(META_ESCAPE_ENHANCED_METAFILE))) { ASSERTMSG(FALSE, ("GetEmfFromWmfData: Someone passed an invalid argument")); return NULL; }
HENHMETAFILE hemf32 = NULL; HDC hMFDC = NULL; PMETA_ESCAPE_ENHANCED_METAFILE pmfeEnhMF; PBYTE pMetaData32 = (PBYTE) NULL;
pmfeEnhMF = (PMETA_ESCAPE_ENHANCED_METAFILE) &wmfData[sizeof(METAHEADER)]; if (IsMetaEscapeEnhancedMetafile(pmfeEnhMF)) { UINT i; UINT cbMetaData32;
if (pmfeEnhMF->fFlags != 0) { ASSERTMSG(FALSE, ("GetEmfFromWmfData: Unrecognized Windows metafile\n")); goto SWMFB_UseConverter; }
// Validate checksum
if (GetWordCheckSum(size, (PWORD) wmfData)) { ASSERTMSG(FALSE, ("GetEmfFromWmfData: Metafile has been modified\n")); goto SWMFB_UseConverter; }
// Unpack the data from the small chunks of metafile comment records
// Windows 3.0 chokes on Comment Record > 8K?
// We probably could probably just error out if out of memory but
// lets try to convert just because the embedded comment might be bad.
TERSE(("GetEmfFromWmfData: Using embedded enhanced metafile\n"));
cbMetaData32 = (UINT) pmfeEnhMF->cbEnhMetaFile; if (!(pMetaData32 = (PBYTE) GpMalloc(cbMetaData32))) { ASSERTMSG(FALSE, ("GetEmfFromWmfData: LocalAlloc Failed")); goto SWMFB_UseConverter; }
i = 0; do { if (i + pmfeEnhMF->cbCurrent > cbMetaData32) { ASSERTMSG(FALSE, ("GetEmfFromWmfData: Bad metafile comment")); goto SWMFB_UseConverter; }
GpMemcpy(&pMetaData32[i], (PBYTE) &pmfeEnhMF[1], pmfeEnhMF->cbCurrent); i += (UINT) pmfeEnhMF->cbCurrent; pmfeEnhMF = (PMETA_ESCAPE_ENHANCED_METAFILE) ((PWORD) pmfeEnhMF + pmfeEnhMF->rdSize); } while (IsMetaEscapeEnhancedMetafile(pmfeEnhMF));
if (i != cbMetaData32) { ASSERTMSG(FALSE, ("GetEmfFromWmfData: Insufficient metafile data")); goto SWMFB_UseConverter; }
// Set the memory directly into the enhanced metafile and return the
// metafile.
hemf32 = SetEnhMetaFileBits(cbMetaData32, pMetaData32); } SWMFB_UseConverter: if( hemf32 == NULL) { hMFDC = CreateEnhMetaFileA(NULL, NULL, NULL, NULL); if (hMFDC != NULL) { // Set the MapMode and Extent to
INT iMapMode = MM_ANISOTROPIC;
HDC hdcRef = ::GetDC(NULL);
INT xExtPels = ::GetDeviceCaps(hdcRef, HORZRES); INT yExtPels = ::GetDeviceCaps(hdcRef, VERTRES);
::ReleaseDC(NULL, hdcRef);
BOOL success = (::SetMapMode(hMFDC, iMapMode) && ::SetViewportExtEx(hMFDC, xExtPels, yExtPels, NULL) && ::SetWindowExtEx(hMFDC, xExtPels, yExtPels, NULL) && ::PlayMetaFile(hMFDC, hWmf)); hemf32 = CloseEnhMetaFile(hMFDC); if ((!success) && (hemf32 != NULL)) { DeleteEnhMetaFile(hemf32); hemf32 = NULL; } } } if (pMetaData32 != NULL) { GpFree(pMetaData32); }
return hemf32 ; }
GpStatus GetEmfHeader( MetafileHeader & header, ENHMETAHEADER3 & emfHeader, EmfPlusRecord * record, INT signature ) { GpStatus status = Ok;
// !!! how to handle versioning for shipping?
// !!! allow different minor versions, but not major versions?
EmfPlusHeaderRecord * emfPlusHeader = (EmfPlusHeaderRecord *)(record + 1);
// See if this is an EMF+ file
if ((signature == EMFPLUS_SIGNATURE) && (record->Size >= (sizeof(EmfPlusRecord) + sizeof(EmfPlusHeaderRecord))) && (record->Type == EmfPlusRecordTypeHeader) && (record->DataSize == (record->Size - sizeof(EmfPlusRecord))) && (ObjectData::MajorVersionMatches(emfPlusHeader->Version)) && (emfPlusHeader->LogicalDpiX > 0) && (emfPlusHeader->LogicalDpiY > 0)) { if (GetIsEmfPlusDual(record->Flags)) { header.Type = MetafileTypeEmfPlusDual; } else { header.Type = MetafileTypeEmfPlusOnly; } header.EmfPlusHeaderSize = record->Size; header.Version = emfPlusHeader->Version; header.EmfPlusFlags = emfPlusHeader->EmfPlusFlags; header.LogicalDpiX = emfPlusHeader->LogicalDpiX; header.LogicalDpiY = emfPlusHeader->LogicalDpiY; } else { header.Type = MetafileTypeEmf; header.Version = emfHeader.nVersion; }
header.Size = emfHeader.nBytes;
// EmfHeaderIsValid() verifies that these are all > 0
REAL dpmmX = ((REAL)(emfHeader.szlDevice.cx) / (REAL)(emfHeader.szlMillimeters.cx)); REAL dpmmY = ((REAL)(emfHeader.szlDevice.cy) / (REAL)(emfHeader.szlMillimeters.cy));
header.DpiX = dpmmX * 25.4f; header.DpiY = dpmmY * 25.4f;
INT top; INT left; INT right; INT bottom;
// Make sure we have a normalized frameRect
if (emfHeader.rclFrame.left <= emfHeader.rclFrame.right) { left = emfHeader.rclFrame.left; right = emfHeader.rclFrame.right; } else { left = emfHeader.rclFrame.right; right = emfHeader.rclFrame.left; }
if (emfHeader.rclFrame.top <= emfHeader.rclFrame.bottom) { top = emfHeader.rclFrame.top; bottom = emfHeader.rclFrame.bottom; } else { top = emfHeader.rclFrame.bottom; bottom = emfHeader.rclFrame.top; }
// Make the device bounds reflect the frameRect,
// not the actual size of the drawing.
dpmmX *= 0.01f; dpmmY *= 0.01f;
// The frameRect is inclusive-inclusive, but the bounds in
// the header is inclusive-exclusive.
REAL x = (REAL)(left) * dpmmX; REAL y = (REAL)(top) * dpmmY; REAL w = ((REAL)(right - left) * dpmmX) + 1.0f; REAL h = ((REAL)(bottom - top) * dpmmY) + 1.0f;
header.X = GpRound(x); header.Y = GpRound(y); header.Width = GpRound(w); header.Height = GpRound(h); header.EmfHeader = emfHeader;
if ((header.Width == 0) || (header.Height == 0)) { status = InvalidParameter; } return status; }
HENHMETAFILE GetEmf( IStream * stream, BOOL isWmf, UINT size ) { HENHMETAFILE hEmf = NULL; #if PROFILE_MEMORY_USAGE
MC_LogAllocation(size); #endif
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, size);
if (hGlobal != NULL) { HRESULT hResult; IStream * memoryStream = NULL;
hResult = CreateStreamOnHGlobal(hGlobal, TRUE, &memoryStream); if (HResultSuccess(hResult) && (memoryStream != NULL)) { if (CopyStream(stream, memoryStream, size)) { BYTE * metaData = (BYTE *)GlobalLock(hGlobal);
if (metaData != NULL) { if (isWmf) { hEmf = (HENHMETAFILE)SetMetaFileBitsEx(size, metaData); } else { hEmf = SetEnhMetaFileBits(size, metaData); } } GlobalUnlock(hGlobal); } memoryStream->Release(); // frees the memory
} else { GlobalFree(hGlobal); } } return hEmf; }
static VOID GetWmfHeader( MetafileHeader & header, METAHEADER & wmfHeader, const WmfPlaceableFileHeader * wmfPlaceableFileHeader ) { ASSERT(WmfPlaceableHeaderIsValid(wmfPlaceableFileHeader)); ASSERT(WmfHeaderIsValid(&wmfHeader));
header.Type = MetafileTypeWmfPlaceable; header.Size = wmfHeader.mtSize * 2L; header.Version = wmfHeader.mtVersion; header.WmfHeader = wmfHeader;
if (wmfPlaceableFileHeader->Inch > 0) { header.DpiX = wmfPlaceableFileHeader->Inch; header.DpiY = wmfPlaceableFileHeader->Inch; } else // guess at the Dpi
{ header.DpiX = 1440.0f; header.DpiY = 1440.0f; // Something wrong but continue
}
// already verified the checksum
// Unlike the EMF header the Placeable header is Inclusive-Exclusive
// So don't add 1 device unit
if (wmfPlaceableFileHeader->BoundingBox.Left < wmfPlaceableFileHeader->BoundingBox.Right) { header.X = wmfPlaceableFileHeader->BoundingBox.Left; header.Width = wmfPlaceableFileHeader->BoundingBox.Right - wmfPlaceableFileHeader->BoundingBox.Left; } else { header.X = wmfPlaceableFileHeader->BoundingBox.Right; header.Width = wmfPlaceableFileHeader->BoundingBox.Left - wmfPlaceableFileHeader->BoundingBox.Right; } if (wmfPlaceableFileHeader->BoundingBox.Top < wmfPlaceableFileHeader->BoundingBox.Bottom) { header.Y = wmfPlaceableFileHeader->BoundingBox.Top; header.Height = wmfPlaceableFileHeader->BoundingBox.Bottom - wmfPlaceableFileHeader->BoundingBox.Top; } else { header.Y = wmfPlaceableFileHeader->BoundingBox.Bottom; header.Height = wmfPlaceableFileHeader->BoundingBox.Top - wmfPlaceableFileHeader->BoundingBox.Bottom; } }
extern "C" int CALLBACK EnumWmfToGetHeader( HDC hdc, // should be NULL
HANDLETABLE FAR * gdiHandleTable, METARECORD FAR * wmfRecord, int numHandles, LPARAM wmfHeader ) { ASSERT(wmfHeader != NULL);
if ((wmfRecord != NULL) && (((UNALIGNED METARECORD *)wmfRecord)->rdSize >= 3)) { // The first record that it gives us is the first one past the header,
// not the header itself, so we have to back up on the pointer.
GpMemcpy((VOID *)wmfHeader, ((BYTE *)wmfRecord) - sizeof(METAHEADER), sizeof(METAHEADER)); } else { WARNING(("Bad Enumeration Parameter")); } return 0; // Don't enumerate any more records
}
GpStatus GetMetafileHeader( HMETAFILE hWmf, const WmfPlaceableFileHeader * wmfPlaceableFileHeader, MetafileHeader & header ) { ASSERT((hWmf != NULL) && (wmfPlaceableFileHeader != NULL));
GpMemset(&header, 0, sizeof(header));
if (WmfPlaceableHeaderIsValid(wmfPlaceableFileHeader)) { METAHEADER wmfHeader;
GpMemset(&wmfHeader, 0, sizeof(wmfHeader)); ::EnumMetaFile(NULL, hWmf, EnumWmfToGetHeader, (LPARAM)&wmfHeader);
if (!WmfHeaderIsValid(&wmfHeader)) { //ASSERT(WmfHeaderIsValid(&wmfHeader));
WARNING(("GetMetafileHeader: WmfHeaderIsValid FAILED!")); wmfHeader.mtType = MEMORYMETAFILE; wmfHeader.mtHeaderSize = sizeof(METAHEADER) / sizeof(WORD); wmfHeader.mtVersion = METAVERSION300; wmfHeader.mtSize = GetMetaFileBitsEx(hWmf, 0, NULL) / 2; wmfHeader.mtNoObjects = 0; wmfHeader.mtMaxRecord = 0; wmfHeader.mtNoParameters = 0; }
GetWmfHeader(header, wmfHeader, wmfPlaceableFileHeader); return Ok; } return InvalidParameter; }
GpStatus GetMetafileHeader( HENHMETAFILE hEmf, MetafileHeader & header, BOOL * isCorrupted ) { ASSERT(hEmf != NULL);
GpMemset(&header, 0, sizeof(header));
ENHMETAHEADER3 emfHeader;
if ((GetEnhMetaFileHeader(hEmf, sizeof(emfHeader), (ENHMETAHEADER*)(&emfHeader)) <= 0) || !EmfHeaderIsValid(emfHeader)) { if (isCorrupted != NULL) { *isCorrupted = FALSE; } return InvalidParameter; }
// Now we know it is an EMF
BYTE buffer[sizeof(EmfPlusRecord) + sizeof(EmfPlusHeaderRecord)];
GpMemset(buffer, 0, sizeof(EmfPlusRecord) + sizeof(EmfPlusHeaderRecord));
// No reason to enumerate the metafile if there are only
// header and EOF records.
if (emfHeader.nRecords > 2) { ::EnumEnhMetaFile(NULL, hEmf, EnumGetEmfPlusHeader, buffer, NULL); }
GpStatus status; status = GetEmfHeader(header, emfHeader, (EmfPlusRecord *)buffer, (((EmfPlusRecord *)buffer)->Size != 0) ? EMFPLUS_SIGNATURE : 0);
if (isCorrupted != NULL) { *isCorrupted = (status != Ok); } return status; }
GpStatus GetEmfFromWmf( IStream * stream, UINT streamSize, MetafileHeader & header, HENHMETAFILE * hEMF ) { if (stream == NULL || hEMF == NULL) { ASSERT(FALSE); return InvalidParameter; }
GpStatus status = Win32Error; IStream * memStream;
ASSERT(hEMF != NULL); *hEMF = NULL ;
HMETAFILE hWMF = (HMETAFILE) GetEmf(stream, TRUE, streamSize); if (hWMF != NULL) { BYTE * wmfData = (BYTE*)GpMalloc(streamSize); if (wmfData != NULL) { GetMetaFileBitsEx(hWMF, streamSize, wmfData); *hEMF = GetEmfFromWmfData(hWMF, wmfData, streamSize); if (*hEMF != NULL) { status = GetMetafileHeader(*hEMF, header); } GpFree(wmfData); } } if (hWMF != NULL) { DeleteMetaFile(hWMF); } return status; }
// If we fail, the stream position will be right where it started.
// If we succeed, the stream position will be at the end of the WMF/EMF
static GpStatus GetHeaderAndMetafile( IStream * stream, MetafileHeader & header, HENHMETAFILE * hEMF, // We can have a NULL hEMF, then we just want the header.
BOOL * isCorrupted, BOOL tryWmfOnly = FALSE ) { GpMemset(&header, 0, sizeof(header)); if (stream == NULL || isCorrupted == NULL) { WARNING(("IN Parameter Stream or Corruption flag is NULL")); return InvalidParameter; }
GpStatus status = InvalidParameter; LONGLONG startPosition; LONGLONG streamSize; STATSTG statstg; BOOL corrupted = FALSE;
// Save the start position of the metafile in case we have to try
// more than once.
if (!GetStreamPosition(stream, startPosition)) { return Win32Error; }
// We don't want to read past the end of the steam so make sure
// that we don't exceed it. If we succeed the set the streamSize
if(SUCCEEDED(stream->Stat(&statstg, STATFLAG_NONAME))) { streamSize = statstg.cbSize.QuadPart; } else { WARNING1("Couldn't get size of Stream"); streamSize = INT_MAX; }
if (!tryWmfOnly) { ENHMETAHEADER3 emfHeader; BOOL isEmf;
// Read the EMF header and make sure it's valid
isEmf = (ReadBytes(stream, &emfHeader, sizeof(emfHeader)) && EmfHeaderIsValid(emfHeader));
if (isEmf) { struct EmfPlusSecondMetafileRecord { EMR emr; DWORD commentDataSize; INT32 signature; EmfPlusRecord record; EmfPlusHeaderRecord emfPlusHeader; } secondRecord;
GpMemset(&secondRecord, 0, sizeof(secondRecord));
// No reason to read the metafile if there are only
// header and EOF records.
if ((emfHeader.nRecords > 2) && (emfHeader.nBytes >= (emfHeader.nSize + sizeof(secondRecord)))) { if (SeekFromStart(stream, startPosition + emfHeader.nSize)) { ReadBytes(stream, &secondRecord, sizeof(secondRecord)); if (!IsEmfPlusRecord((ENHMETARECORD *)&secondRecord)) { // make sure that whatever data was there isn't
// interpreted as a EMF+ header
secondRecord.signature = 0; } } }
status = GetEmfHeader(header, emfHeader, &secondRecord.record, secondRecord.signature);
// Seek back to the start of the metafile.
if ((hEMF != NULL) && (status == Ok)) { if (!SeekFromStart(stream, startPosition)) { *isCorrupted = TRUE; return Win32Error; }
*hEMF = GetEmf(stream, FALSE /*isWMF*/, (UINT)min(header.GetMetafileSize(), streamSize - startPosition)); if (*hEMF == NULL) { status = GenericError; } }
corrupted = (status != Ok); goto Exit; }
// Seek back to the start of the metafile so we can try WMF
if (!SeekFromStart(stream, startPosition)) { *isCorrupted = FALSE; return Win32Error; } }
// It's not an EMF, try a WMF
{ WmfPlaceableFileHeader wmfPlaceableFileHeader; METAHEADER wmfHeader; BOOL isPlaceable; BOOL isWMF;
isPlaceable = (ReadBytes(stream, &wmfPlaceableFileHeader, sizeof(wmfPlaceableFileHeader)) && WmfPlaceableHeaderIsValid(&wmfPlaceableFileHeader) && ReadBytes(stream, &wmfHeader, sizeof(wmfHeader)) && WmfHeaderIsValid(&wmfHeader));
if (isPlaceable) { GetWmfHeader(header, wmfHeader, &wmfPlaceableFileHeader);
status = Ok; corrupted = FALSE;
if (hEMF != NULL) { if (!SeekFromStart(stream, startPosition + sizeof(wmfPlaceableFileHeader))) { *isCorrupted = TRUE; return Win32Error; }
*hEMF = GetEmf(stream, TRUE /* isWMF */, (UINT)min(header.GetMetafileSize(), streamSize - (startPosition + sizeof(wmfPlaceableFileHeader)))); if (*hEMF == NULL) { status = GenericError; corrupted = TRUE; } } goto Exit; }
// We could have an placeableWmf header with bad data in it, so skip
// the placeable header for subsequent access to the WMF.
INT wmfOffset = (wmfPlaceableFileHeader.Key == GDIP_WMF_PLACEABLEKEY) ? sizeof(WmfPlaceableFileHeader) : 0;
if (!SeekFromStart(stream, startPosition + wmfOffset)) { *isCorrupted = FALSE; return Win32Error; }
isWMF = (ReadBytes(stream, &wmfHeader, sizeof(wmfHeader)) && WmfHeaderIsValid(&wmfHeader));
if (isWMF) { // Seek to the start of the WMF metafile.
if (!SeekFromStart(stream, startPosition + wmfOffset)) { *isCorrupted = TRUE; return Win32Error; }
UINT wmfSize = min((wmfHeader.mtSize * 2L), (UINT)(streamSize - (startPosition + wmfOffset)));
if (hEMF != NULL) { status = GetEmfFromWmf(stream, wmfSize, header, hEMF); } else { HENHMETAFILE tmpEMF = NULL;
status = GetEmfFromWmf(stream, wmfSize, header, &tmpEMF); if (tmpEMF != NULL) { DeleteEnhMetaFile(tmpEMF); } } corrupted = (status != Ok); } }
Exit: *isCorrupted = corrupted; if (status == Ok) { // set the stream position to the end of the metafile
SeekFromStart(stream, startPosition + header.GetMetafileSize()); return Ok; }
// set the stream position to the start of the metafile
SeekFromStart(stream, startPosition); return status; }
VOID GpMetafile::InitStream( IStream* stream, BOOL tryWmfOnly ) { BOOL isCorrupted = FALSE;
// We just use the stream long enough to create an hEMF
stream->AddRef(); if ((GetHeaderAndMetafile(stream, Header, &Hemf, &isCorrupted, tryWmfOnly) == Ok) && (Hemf != NULL)) { State = DoneRecordingMetafileState; } else if (isCorrupted) { State = CorruptedMetafileState; } stream->Release(); }
GpStatus GetMetafileHeader( IStream * stream, MetafileHeader & header, BOOL tryWmfOnly ) { BOOL isCorrupted = FALSE; return GetHeaderAndMetafile(stream, header, NULL, &isCorrupted, tryWmfOnly); }
GpStatus GetMetafileHeader( const WCHAR * filename, MetafileHeader & header ) { GpStatus status = InvalidParameter;
ASSERT(filename != NULL);
if (filename != NULL) { const WCHAR* ext = UnicodeStringReverseSearch(filename, L'.');
// Get a stream only long enough to validate the metafile
IStream * metaStream = CreateStreamOnFile(filename, GENERIC_READ); if (metaStream != NULL) { // apm is for a Placeable Metafile
BOOL tryWmf = (ext && (UnicodeStringCompareCI(ext, L".WMF") || UnicodeStringCompareCI(ext, L".APM"))); BOOL isCorrupted = FALSE;
status = GetHeaderAndMetafile(metaStream, header, NULL, &isCorrupted, tryWmf);
// if we tried a WMF, but it's not a WMF, then try an EMF
if ((status != Ok) && tryWmf && !isCorrupted) { status = GetHeaderAndMetafile(metaStream, header, NULL, &isCorrupted, FALSE); } metaStream->Release(); } } return status; }
VOID GpMetafile::InitWmf( HMETAFILE hWmf, const WmfPlaceableFileHeader * wmfPlaceableFileHeader, BOOL deleteWmf ) { // See if there is an wmfPlaceableFileHeader we can use
if ((wmfPlaceableFileHeader != NULL) && (WmfPlaceableHeaderIsValid(wmfPlaceableFileHeader))) { if (GetMetafileHeader(hWmf, wmfPlaceableFileHeader, Header) == Ok) { DeleteHemf = (deleteWmf != 0); Hemf = (HENHMETAFILE)hWmf; State = DoneRecordingMetafileState; return; } else { // we know it's a WMF, but we couldn't get the header from it
State = CorruptedMetafileState; } } else // no valid wmfPlaceableFileHeader
{ // We can have a null or invalid header since we accept WMF files
// (by turning them into EMFs).
UINT size = GetMetaFileBitsEx(hWmf, 0, NULL); if (size > 0) { BYTE * wmfData = (BYTE*) GpMalloc(size); if (wmfData != NULL) { if (GetMetaFileBitsEx(hWmf, size, wmfData) > 0) { HENHMETAFILE hEmf = GetEmfFromWmfData(hWmf, wmfData, size); if (hEmf != NULL) { BOOL isCorrupted;
if (GetMetafileHeader(hEmf, Header, &isCorrupted) == Ok) { // Since we created this EMF we need to delete it afterwards
DeleteHemf = TRUE; Hemf = hEmf; State = DoneRecordingMetafileState; } else { if (isCorrupted) { // we know it's a metafile, but we couldn't get the header
State = CorruptedMetafileState; } DeleteEnhMetaFile(hEmf); } } } GpFree(wmfData); } } } if (deleteWmf) { DeleteMetaFile(hWmf); } }
VOID GpMetafile::InitEmf( HENHMETAFILE hEmf, BOOL deleteEmf ) { BOOL isCorrupted;
if (GetMetafileHeader(hEmf, Header, &isCorrupted) == Ok) { DeleteHemf = (deleteEmf != 0); Hemf = hEmf; State = DoneRecordingMetafileState; return; } if (deleteEmf) { DeleteEnhMetaFile(hEmf); } if (isCorrupted) { State = CorruptedMetafileState; } }
/**************************************************************************\
* * Function Description: * * GpMetafile constructor for read-only access to a metafile. * * Arguments: * * [IN] hWmf - the handle to the metafile to open for playback * [IN] wmfPlaceableFileHeader - the Placeable header to give size info about the WMF * * Return Value: * * NONE * * Created: * * 10/06/1999 DCurtis * \**************************************************************************/ GpMetafile::GpMetafile( HMETAFILE hWmf, const WmfPlaceableFileHeader * wmfPlaceableFileHeader, BOOL deleteWmf ) : GpImage(ImageTypeMetafile) { ASSERT(hWmf != NULL);
InitDefaults(); if (IsValidMetaFile(hWmf)) { InitWmf(hWmf, wmfPlaceableFileHeader, deleteWmf); } }
/**************************************************************************\
* * Function Description: * * GpMetafile constructor for read-only access to a metafile. * * Arguments: * * [IN] hEmf - the handle to the metafile to open for playback * * Return Value: * * NONE * * Created: * * 10/06/1999 DCurtis * \**************************************************************************/ GpMetafile::GpMetafile( HENHMETAFILE hEmf, BOOL deleteEmf ) : GpImage(ImageTypeMetafile) { ASSERT(hEmf != NULL);
InitDefaults(); if (GetObjectTypeInternal(hEmf) == OBJ_ENHMETAFILE) { InitEmf(hEmf, deleteEmf); } }
/**************************************************************************\
* * Function Description: * * GpMetafile constructor for read-only access to a metafile. * * Arguments: * * [IN] filename - the metafile to open for playback * * Return Value: * * NONE * * Created: * * 6/15/1999 DCurtis * \**************************************************************************/ GpMetafile::GpMetafile( const WCHAR* filename, const WmfPlaceableFileHeader * wmfPlaceableFileHeader ) : GpImage(ImageTypeMetafile) { ASSERT(filename != NULL);
InitDefaults();
if ((Filename = UnicodeStringDuplicate(filename)) != NULL) { const WCHAR* ext = UnicodeStringReverseSearch(filename, L'.');
// apm is for a Placeable Metafile
BOOL tryWmf = ((wmfPlaceableFileHeader != NULL) || (ext && (!UnicodeStringCompareCI(ext, L".WMF") || !UnicodeStringCompareCI(ext, L".APM"))));
BOOL triedEmf = FALSE;
AnsiStrFromUnicode nameStr(filename);
// If possible, use the filename to create the metafile handle
// so that we don't have to load the metafile into memory
// (GDI uses memory mapped files to access the metafile data).
if (Globals::IsNt || nameStr.IsValid()) { TryWmf: if (tryWmf) { HMETAFILE hWmf;
if (Globals::IsNt) { hWmf = ::GetMetaFileW(filename); } else { hWmf = ::GetMetaFileA(nameStr); }
if (hWmf != NULL) { InitWmf(hWmf, wmfPlaceableFileHeader, TRUE); if (IsValid() || IsCorrupted()) { return; } } else // might be a Placeable WMF file
{ IStream * metaStream = CreateStreamOnFile(filename, GENERIC_READ); if (metaStream != NULL) { InitStream(metaStream, TRUE /* tryWmfOnly */); metaStream->Release(); if (IsValid() || IsCorrupted()) { return; } } } } if (!triedEmf) { triedEmf = TRUE;
HENHMETAFILE hEmf;
if (Globals::IsNt) { hEmf = ::GetEnhMetaFileW(filename); } else { hEmf = ::GetEnhMetaFileA(nameStr); }
if (hEmf != NULL) { InitEmf(hEmf, TRUE); if (IsValid() || IsCorrupted()) { return; } } if (!tryWmf) { tryWmf = TRUE; goto TryWmf; } } } } }
/**************************************************************************\
* * Function Description: * * GpMetafile constructor for read-only access to a metafile. * * Arguments: * * [IN] stream - the metafile to read for playback * * Return Value: * * NONE * * Created: * * 6/15/1999 DCurtis * \**************************************************************************/ GpMetafile::GpMetafile( IStream* stream ) : GpImage(ImageTypeMetafile) { ASSERT(stream != NULL);
InitDefaults(); InitStream(stream); }
GpStatus GpMetafile::GetHemf( HENHMETAFILE * hEmf ) const { if ((State == DoneRecordingMetafileState) || (State == ReadyToPlayMetafileState)) { ASSERT(Hemf != NULL); *hEmf = Hemf; Hemf = NULL; State = InvalidMetafileState; return Ok; } *hEmf = NULL; return InvalidParameter; }
GpStatus GpMetafile::PrepareToPlay( GpGraphics * g, GpRecolor * recolor, ColorAdjustType adjustType, EnumerateMetafileProc enumerateCallback, VOID * callbackData, DrawImageAbort drawImageCallback, VOID* drawImageCallbackData ) const { if (State == DoneRecordingMetafileState) { ASSERT(Hemf != NULL); if (Player == NULL) { // Create a Player object
Player = new MetafilePlayer(g, MaxStackSize, recolor, adjustType, enumerateCallback, callbackData, drawImageCallback, drawImageCallbackData ); if (!CheckValid(Player)) { return GenericError; } } State = ReadyToPlayMetafileState; return Ok; } if (State == ReadyToPlayMetafileState) { ASSERT(Hemf != NULL); ASSERT(Player != NULL); Player->PrepareToPlay(g, recolor, adjustType, enumerateCallback, callbackData, drawImageCallback, drawImageCallbackData ); return Ok; } return InvalidParameter; }
GpStatus GpMetafile::EnumerateForPlayback( const RectF & destRect, const RectF & srcRect, Unit srcUnit, GpGraphics * g, EnumerateMetafileProc callback, // if null, just play the metafile
VOID * callbackData, GpRecolor * recolor, ColorAdjustType adjustType, DrawImageAbort drawImageCallback, VOID* drawImageCallbackData ) const { ASSERT (IsValid());
if ((destRect.Width == 0) || (destRect.Height == 0) || (srcRect.Width == 0) || (srcRect.Height == 0) || (Header.IsEmf() && (Header.EmfHeader.nRecords <= 2))) { return Ok; // nothing to play
}
GpRectF metaSrcRect = srcRect; GpRectF metaDestRect = destRect;
// The metafile player does not handle negative width/height
// in srcRect and destRect, so handle any negative values
// by setting up a flipping transform.
GpMatrix flipMatrix; // starts as identity
BOOL posWidths; BOOL posHeights;
posWidths = ((metaSrcRect.Width >= 0) && (metaDestRect.Width >= 0)); posHeights = ((metaSrcRect.Height >= 0) && (metaDestRect.Height >= 0));
if (!posWidths || !posHeights) { if (!posWidths) { if (metaSrcRect.Width < 0) { if (metaDestRect.Width < 0) { posWidths = TRUE; metaSrcRect.X = metaSrcRect.GetRight(); metaSrcRect.Width = -(metaSrcRect.Width); metaDestRect.X = metaDestRect.GetRight(); metaDestRect.Width = -(metaDestRect.Width); } else { metaSrcRect.X = metaSrcRect.GetRight(); metaSrcRect.Width = -(metaSrcRect.Width); } } else // metaDestRect.Width < 0
{ metaDestRect.X = metaDestRect.GetRight(); metaDestRect.Width = -(metaDestRect.Width); } } if (!posHeights) { if (metaSrcRect.Height < 0) { if (metaDestRect.Height < 0) { posHeights = TRUE; metaSrcRect.Y = metaSrcRect.GetBottom(); metaSrcRect.Height = -(metaSrcRect.Height); metaDestRect.Y = metaDestRect.GetBottom(); metaDestRect.Height = -(metaDestRect.Height); } else { metaSrcRect.Y = metaSrcRect.GetBottom(); metaSrcRect.Height = -(metaSrcRect.Height); } } else // metaDestRect.Height < 0
{ metaDestRect.Y = metaDestRect.GetBottom(); metaDestRect.Height = -(metaDestRect.Height); } } REAL scaleX = 1.0f; REAL scaleY = 1.0f; REAL dX = 0.0f; REAL dY = 0.0f;
// Create a matrix that is the equivalent of:
// 1) translate to the origin
// 2) do the flip
// 3) translate back
if (!posWidths) { scaleX = -1.0f; dX = metaDestRect.X + metaDestRect.GetRight(); } if (!posHeights) { scaleY = -1.0f; dY = metaDestRect.Y + metaDestRect.GetBottom(); }
flipMatrix.Translate(dX, dY, MatrixOrderPrepend); flipMatrix.Scale(scaleX, scaleY, MatrixOrderPrepend); }
// Note that even though the visibility of the destRect might be
// fully visible, we should still setup the clipping because:
// (1) we might do cropping based on the srcRect
// (2) the frameRect of the metafile might not include all
// the actual drawing within the metafile.
GpStatus status = GenericError;
// Must convert the source rect into UnitPixels (if not already
// in pixel units), to account for the dpi of the source metafile.
REAL multiplierX; REAL multiplierY;
GetPixelMultipliers(srcUnit, Header.GetDpiX(), Header.GetDpiY(), &multiplierX, &multiplierY);
GpRectF pixelsSrcRect;
pixelsSrcRect.X = metaSrcRect.X * multiplierX; pixelsSrcRect.Y = metaSrcRect.Y * multiplierY; pixelsSrcRect.Width = metaSrcRect.Width * multiplierX; pixelsSrcRect.Height = metaSrcRect.Height * multiplierY;
INT saveId = g->Save();
if (saveId != 0) { // We need to take into account the region from the source that we
// are drawing in order to do that we need to re-translate and
// rescale and the transform. The clipping will take care of only
// drawing the region that we are interested in.
// In order to acheive this we need to translate the dest rect back
// to the origin. Scale it by the same factor as the scale of the
// src rect and then translate it back to when it should be which
// is the scaled version of the left cropping of the src image.
GpMatrix preFlipPreCropTransform; g->GetWorldTransform(preFlipPreCropTransform);
// apply the flipping transform
g->MultiplyWorldTransform(flipMatrix, MatrixOrderPrepend);
BOOL widthsDifferent = (Header.Width != pixelsSrcRect.Width); BOOL heightsDifferent = (Header.Height != pixelsSrcRect.Height); BOOL cropOrOffset = ((Header.X != pixelsSrcRect.X) || (Header.Y != pixelsSrcRect.Y) || widthsDifferent || heightsDifferent);
if (cropOrOffset) { g->TranslateWorldTransform(((((REAL)(Header.X - pixelsSrcRect.X)) *metaDestRect.Width) /pixelsSrcRect.Width) + metaDestRect.X, ((((REAL)(Header.Y - pixelsSrcRect.Y)) *metaDestRect.Height)/pixelsSrcRect.Height) + metaDestRect.Y);
REAL xScale = 1.0f; REAL yScale = 1.0f;
if (widthsDifferent) { xScale = (REAL) Header.Width / pixelsSrcRect.Width; } if (heightsDifferent) { yScale = (REAL) Header.Height / pixelsSrcRect.Height; } g->ScaleWorldTransform(xScale, yScale);
g->TranslateWorldTransform(-metaDestRect.X, -metaDestRect.Y); }
// We don't use the deviceRect if we're rendering to a bitmap.
GpMatrix flipAndCropTransform; GpRectF deviceRect = metaDestRect;
// sets the PreContainerMatrix to the WorldToDevice Transform, which
// includes the flipping and cropping transforms.
if ((status = this->PrepareToPlay(g, recolor, adjustType, callback, callbackData, drawImageCallback, drawImageCallbackData)) != Ok) { goto CleanUp; }
ASSERT(Player != NULL);
State = PlayingMetafileState;
BOOL renderToBitmap = FALSE; GpMatrix * playMatrix = &(Player->PreContainerMatrix); BOOL isTranslateScale = playMatrix->IsTranslateScale();
// On Win9x and WinNT (except Whistler and beyond), stretchblt calls
// don't work if there is any flipping.
// On Win9x text does not work if there is any flipping.
// On WinNT, bitmap fonts don't work for 90,180,270 degree rotation
// (but we map all bitmap fonts to true-type fonts anyway).
if (isTranslateScale) { // if there is any flipping, render to a bitmap
if ((playMatrix->GetM11() < 0.0f) || (playMatrix->GetM22() < 0.0f)) { isTranslateScale = FALSE; renderToBitmap = TRUE; } } else { // It's okay to render rotated directly to the HDC on NT,
// unless the dest is a metafile or the src is a WMF.
renderToBitmap = (!Globals::IsNt || (g->Type == GpGraphics::GraphicsMetafile) || Header.IsWmf()); }
// Save what we have done into flipAndCropTransform. We will prepare the
// container with this world transform since the precontainerMatrix
// is only for the Downlevel and it needs that modified transform
g->GetWorldTransform(flipAndCropTransform);
// Restore the world transform to it's original self
// (w/o flipping and cropping transform applied).
g->SetWorldTransform(preFlipPreCropTransform);
// When we render to a bitmap, we render the entire metafile to
// the entire bitmap and then we clip out the cropped part of the
// metafile from the bitmap. So we have to set the clipping
// when we render to a bitmap if there is any cropping.
// It would be nice as an enhancement to just draw to a pre-cropped
// bitmap instead of clipping out part of the bitmap, but the math
// for that is tricky.
if ((!renderToBitmap) || cropOrOffset) { GpMatrix worldToDeviceTransform; g->GetWorldToDeviceTransform(&worldToDeviceTransform); if (isTranslateScale) { worldToDeviceTransform.TransformRect(deviceRect); }
// Don't set the clipping if we're rendering to a bitmap,
// because the rendering into the bitmap will do the clipping
// automatically, and if we also clip against the graphics, we
// sometimes clip too much, which can cause jagged edges on
// rotated metafiles.
// Clipping into a metafile causes problems. For example, if
// we're drawing outside the bounds of the referenece HDC, it
// works fine, but then when we add clipping into the HDC, it doesn't
// work anymore -- nothing gets drawn into the metafile, even though
// everything is within the clipping rect (but the clipping rect is
// outside the bounds of the reference HDC).
if (g->Type != GpGraphics::GraphicsMetafile) { if ((!(renderToBitmap && cropOrOffset)) && isTranslateScale) { g->SetClip(metaDestRect, CombineModeIntersect); } else // rendering to a bitmap with cropping or
// rotating to the screen
{ // Since we want the filtered (smooth) edges on the
// bitmap, we have to add in a little extra room on
// the edges of our clip rect.
// On rotations we need to inflate by one pixel also
// because it seems that GDI doesn't rasterize clipregions
// the same we that it rasterized rects. Do rects on the
// edges can have pixels missing. We might be introducing
// more pixels that should have been clipped out but we
// can live with that for now.
GpRectF tmpClipRect = metaDestRect; REAL xSize; REAL ySize;
g->GetWorldPixelSize(xSize, ySize);
// add 1 pixel all the way around
tmpClipRect.Inflate(xSize, ySize);
g->SetClip(tmpClipRect, CombineModeIntersect); }
if (isTranslateScale) { // We need to intersect the destRect with the Visible Clip
// in order to make sure that we don't draw outside the bounds
// in Win9x since we can't use a MetaRgn
GpRectF clipBounds; g->GetVisibleClipBounds(clipBounds); worldToDeviceTransform.TransformRect(clipBounds); GpRectF::Intersect(deviceRect, deviceRect, clipBounds); } } }
// If we're playing an EMF+ into another metafile, we have to be
// careful not to double-transform points. The HDC will have
// the srcRect to destRect transform in it, and the graphics might
// have a transform too, so we can end up double-transforming the
// points of any GDI+ records that are in an EMF+ file.
// One easy way to get around that is that if we are playing an
// EMF+ dual, we could just play the down-level records (i.e. play it
// as an EMF, not an EMF+), so that all the records get transformed
// the same way. But of course, that doesn't work if it's an
// EMF+ only file. A solution that works for both EMF+ dual and
// EMF+ only is to force the GDI+ transform to be the identity so that
// the down-level records that are generated by DriverMeta are in
// the original coordinate system of the metafile, not in the
// destination coordinate system (which then get transformed again
// erroneously).
if (Header.IsWmf() || Header.IsEmf()) { status = g->EnumEmf(Player, Hemf, metaDestRect, pixelsSrcRect, deviceRect, Header.GetType(), isTranslateScale, renderToBitmap, flipAndCropTransform); } else { ASSERT(Header.IsEmfPlus());
// When playing from a metafile into a metafile, Win9x does NOT
// allow you to override (reset) the srcRect->destRect metafile
// transform. So to keep from double transforming the records,
// we have to set the GDI+ transform to identity, instead of
// setting the HDC transform to identity as we would typically do.
// When rendering to a bitmap, we don't have to worry about
// double-transforming, because we play the metafile to the
// bitmap HDC, not to the dest metafile hdc, so there won't
// be a transform on the metafile hdc to mess us up.
INT containerId;
if ((g->Type != GpGraphics::GraphicsMetafile) || renderToBitmap) { // Now apply the flipping matrix.
// The g->Restore call below will reset the transform.
g->MultiplyWorldTransform(flipMatrix, MatrixOrderPrepend);
GpRectF gdiDestRect = metaDestRect;
// We need to calculate our transform so that the last point in the
// src maps to the last point in the destination. This is how GDI does
// it and we also need to do it so that we can play metafile properly
if (pixelsSrcRect.Width >= 2.0f) { pixelsSrcRect.Width -= 1.0f; } if (pixelsSrcRect.Height >= 2.0f) { pixelsSrcRect.Height -= 1.0f; }
if (gdiDestRect.Width >= 2.0f) { gdiDestRect.Width -= 1.0f; } if (gdiDestRect.Height >= 2.0f) { gdiDestRect.Height -= 1.0f; }
containerId = g->BeginContainer( gdiDestRect, pixelsSrcRect, UnitPixel, (REAL)Header.LogicalDpiX, (REAL)Header.LogicalDpiY, Header.IsDisplay()); } else // we're drawing into a metafile
{ containerId = g->BeginContainer( TRUE, // force xform to identity
(REAL)Header.LogicalDpiX, (REAL)Header.LogicalDpiY, Header.IsDisplay()); }
if (containerId != 0) { // There may be GDI records that we need to play!
status = g->EnumEmfPlusDual(Player, Hemf, metaDestRect, deviceRect, isTranslateScale, renderToBitmap); g->EndContainer(containerId); Player->DonePlaying(); // free up objects created by Player
} // make sure the status reflect the abort state of the player
ASSERT(!Player->EnumerateAborted || (status == Aborted)); } CleanUp: g->Restore(saveId); }
// Don't change the state unless we were playing the metafile
if (State == PlayingMetafileState) { State = ReadyToPlayMetafileState; }
return status; }
/**************************************************************************\
* * Function Description: * * Initialize the metafile object members to their default values. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 6/15/1999 DCurtis * \**************************************************************************/ VOID GpMetafile::InitDefaults() { ThreadId = 0; State = InvalidMetafileState; Filename = NULL; Stream = NULL; Hemf = NULL; MetaGraphics = NULL; Player = NULL; MaxStackSize = GDIP_SAVE_STACK_SIZE; DeleteHemf = TRUE; RequestedMetaGraphics = FALSE;
GpMemset(&Header, 0, sizeof(Header));
// Set the version for recording. If we're plyaing back,
// this will get overwritten later.
Header.Version = EMFPLUS_VERSION; }
GpStatus GpMetafile::GetImageInfo( ImageInfo * imageInfo ) const { ASSERT(imageInfo != NULL); ASSERT(IsValid());
if ((State == DoneRecordingMetafileState) || (State == ReadyToPlayMetafileState)) { if (Header.IsEmfOrEmfPlus()) { imageInfo->RawDataFormat = IMGFMT_EMF; } else // Wmf
{ imageInfo->RawDataFormat = IMGFMT_WMF; }
imageInfo->PixelFormat = PIXFMT_32BPP_RGB; imageInfo->Width = Header.Width; imageInfo->Height = Header.Height; imageInfo->TileWidth = Header.Width; imageInfo->TileHeight = 1; imageInfo->Xdpi = Header.DpiX; imageInfo->Ydpi = Header.DpiY; imageInfo->Flags = SinkFlagsTopDown | SinkFlagsFullWidth | SinkFlagsScalable | SinkFlagsHasAlpha;
return Ok; } return InvalidParameter; }
GpImage * GpMetafile::Clone() const { GpMetafile * clonedMetafile = NULL;
if ((State == DoneRecordingMetafileState) || (State == ReadyToPlayMetafileState)) { if (Header.IsEmfOrEmfPlus()) { HENHMETAFILE hEmf = CopyEnhMetaFileA(Hemf, NULL);
if (hEmf != NULL) { clonedMetafile = new GpMetafile(hEmf, TRUE); if (clonedMetafile != NULL) { if (!clonedMetafile->IsValid()) { DeleteEnhMetaFile(hEmf); clonedMetafile->Hemf = NULL; clonedMetafile->Dispose(); clonedMetafile = NULL; } } } } else // Wmf
{ HMETAFILE hWmf = CopyMetaFileA((HMETAFILE)Hemf, NULL);
if (hWmf != NULL) { WmfPlaceableFileHeader wmfPlaceableFileHeader;
wmfPlaceableFileHeader.Key = GDIP_WMF_PLACEABLEKEY; wmfPlaceableFileHeader.Hmf = 0; wmfPlaceableFileHeader.BoundingBox.Left = static_cast<INT16>(Header.X); wmfPlaceableFileHeader.BoundingBox.Right = static_cast<INT16>(Header.X + Header.Width); wmfPlaceableFileHeader.BoundingBox.Top = static_cast<INT16>(Header.Y); wmfPlaceableFileHeader.BoundingBox.Bottom = static_cast<INT16>(Header.Y + Header.Height); wmfPlaceableFileHeader.Inch = static_cast<INT16>(GpRound(Header.DpiX)); wmfPlaceableFileHeader.Reserved = 0; wmfPlaceableFileHeader.Checksum = GetWmfPlaceableCheckSum(&wmfPlaceableFileHeader);
clonedMetafile = new GpMetafile(hWmf, &wmfPlaceableFileHeader, TRUE); if (clonedMetafile != NULL) { if (!clonedMetafile->IsValid()) { DeleteMetaFile(hWmf); clonedMetafile->Hemf = NULL; clonedMetafile->Dispose(); clonedMetafile = NULL; } } } } } return clonedMetafile; }
GpImage* GpMetafile::CloneColorAdjusted( GpRecolor * recolor, ColorAdjustType adjustType ) const { ASSERT(recolor != NULL);
if ((State == DoneRecordingMetafileState) || (State == ReadyToPlayMetafileState)) { GpMetafile* clonedMetafile;
// FrameRect is Inclusive-Inclusive so subtrace 1 device unit
GpRectF frameRect((REAL)Header.X, (REAL)Header.Y, (REAL)(Header.Width - 1), (REAL)(Header.Height - 1)); EmfType type;
if (Header.Type <= MetafileTypeEmf) { type = EmfTypeEmfOnly; } else { // we don't need the down-level dual sections for embedded files
type = EmfTypeEmfPlusOnly; }
// It doesn't matter if we lose the description string, since this
// metafile is just being embedded inside another one anyway.
clonedMetafile = new GpMetafile(Globals::DesktopIc, type, &frameRect,MetafileFrameUnitPixel,NULL);
if ((clonedMetafile != NULL) && (clonedMetafile->IsValid())) { GpStatus status; GpPageUnit srcUnit; GpRectF srcRect; GpGraphics * g = clonedMetafile->GetGraphicsContext(); ASSERT (g != NULL);
this->GetBounds(&srcRect, &srcUnit);
// We pass Inclusive-Exclusive bounds to play so add 1 device
// unit to the framerect
frameRect.Width++; frameRect.Height++; status = this->Play(frameRect, srcRect, srcUnit, g, recolor, adjustType);
delete g;
if ((status == Ok) && (clonedMetafile->State == DoneRecordingMetafileState)) { return clonedMetafile; } } delete clonedMetafile; } return NULL; }
GpStatus GpMetafile::ColorAdjust( GpRecolor * recolor, ColorAdjustType adjustType ) { ASSERT(recolor != NULL);
GpMetafile * clone;
if (DeleteHemf && ((clone = (GpMetafile *)CloneColorAdjusted(recolor, adjustType)) != NULL)) { CleanUp(); InitDefaults();
if (GetMetafileHeader(clone->Hemf, Header) == Ok) { Hemf = clone->Hemf; DeleteHemf = TRUE; State = DoneRecordingMetafileState; clone->DeleteHemf = FALSE; delete clone; return Ok; } } return GenericError; }
VOID GpMetafile::Dispose() { delete this; }
class RemoveDualRecords { public: BYTE * MetaData; INT Size; INT NumRecords; BOOL GetGdiRecords;
RemoveDualRecords() { Init(); }
VOID Init() { MetaData = NULL; Size = 0; NumRecords = 0; GetGdiRecords = TRUE; // so we write the EMR_HEADER record
}
VOID GetRecord(CONST ENHMETARECORD * emfRecord) { UINT recordSize = emfRecord->nSize;
if (MetaData != NULL) { GpMemcpy(MetaData, emfRecord, recordSize); MetaData += recordSize; } Size += recordSize; NumRecords++; } };
extern "C" int CALLBACK EnumEmfRemoveDualRecords( HDC hdc, // should be NULL
HANDLETABLE FAR * gdiHandleTable, CONST ENHMETARECORD * emfRecord, int numHandles, LPARAM removeDualRecords ) { if ((emfRecord != NULL) && (emfRecord->nSize >= sizeof(EMR)) && (removeDualRecords != NULL)) { if (IsEmfPlusRecord(emfRecord)) { // See if the last record of this set of EMF+ records is a GetDC
// record. If it is, then we know to play the next set of
// GDI records that we encounter.
// I prefer not to have to parse through all these records,
// but there is always the slight possibility that this will
// result in a false positive. But the worst thing that can
// happen is that we write a little too much data to the stream.
EmfPlusRecord * lastRecord;
lastRecord = (EmfPlusRecord *)(((BYTE *)emfRecord) + emfRecord->nSize - sizeof(EmfPlusRecord));
((RemoveDualRecords *)removeDualRecords)->GetGdiRecords = ((lastRecord->Type == EmfPlusRecordTypeGetDC) && (lastRecord->Size == sizeof(EmfPlusRecord)) && (lastRecord->DataSize == 0)); } else if ((emfRecord->iType != EMR_EOF) && // Write EOF record
(!(((RemoveDualRecords *)removeDualRecords)->GetGdiRecords))) { return 1; // skip this GDI record
} ((RemoveDualRecords *)removeDualRecords)->GetRecord(emfRecord); } else { WARNING(("Bad Enumeration Parameter")); } return 1; }
extern "C" int CALLBACK EnumEmfToStream( HDC hdc, // handle to device context
HANDLETABLE FAR * gdiHandleTable, // pointer to metafile handle table
CONST ENHMETARECORD * emfRecord, // pointer to metafile record
int numHandles, // count of objects
LPARAM stream // pointer to optional data
) { if ((emfRecord != NULL) && (emfRecord->nSize >= sizeof(EMR)) && (stream != NULL)) { ((IStream *)stream)->Write(emfRecord, emfRecord->nSize, NULL); } else { WARNING(("Bad Enumeration Parameter")); } return 1; }
class MetafileData : public ObjectTypeData { public: INT32 MetaType; INT32 MetaDataSize; };
/**************************************************************************\
* * Function Description: * * Get the metafile data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpMetafile::GetData( IStream * stream ) const { ASSERT (stream != NULL);
if ((State != DoneRecordingMetafileState) && (State != ReadyToPlayMetafileState)) { WARNING(("Wrong State To GetData")); return WrongState; }
ASSERT(Hemf != NULL);
MetafileData metafileData; metafileData.Type = ImageTypeMetafile;
if (Header.IsWmf()) { INT wmfDataSize = GetMetaFileBitsEx((HMETAFILE)Hemf, 0, NULL);
if (wmfDataSize <= 0) { WARNING(("Empty WMF")); return Win32Error; }
BYTE * wmfData = (BYTE *)GpMalloc(wmfDataSize); if (wmfData == NULL) { return OutOfMemory; }
if (GetMetaFileBitsEx((HMETAFILE)Hemf, wmfDataSize, wmfData) == 0) { WARNING(("Problem retrieving WMF Data")); GpFree(wmfData); return Win32Error; }
// We don't save MetafileTypeWmf -- we convert it to the Placeable type
metafileData.MetaType = MetafileTypeWmfPlaceable; metafileData.MetaDataSize = wmfDataSize; stream->Write(&metafileData, sizeof(metafileData), NULL);
ASSERT(sizeof(WmfPlaceableFileHeader) == 22); #define PLACEABLE_BUFFER_SIZE (sizeof(WmfPlaceableFileHeader) + 2)
BYTE placeableBuffer[PLACEABLE_BUFFER_SIZE]; WmfPlaceableFileHeader * wmfPlaceableFileHeader = (WmfPlaceableFileHeader *)placeableBuffer; REAL aveDpi;
// set pad word to 0
*((INT16 *)(placeableBuffer + sizeof(WmfPlaceableFileHeader))) = 0;
aveDpi = (Header.GetDpiX() + Header.GetDpiY()) / 2.0f;
wmfPlaceableFileHeader->Key = GDIP_WMF_PLACEABLEKEY; wmfPlaceableFileHeader->Hmf = 0; wmfPlaceableFileHeader->BoundingBox.Left = static_cast<INT16>(Header.X); wmfPlaceableFileHeader->BoundingBox.Top = static_cast<INT16>(Header.Y); wmfPlaceableFileHeader->BoundingBox.Right = static_cast<INT16>(Header.X + Header.Width); wmfPlaceableFileHeader->BoundingBox.Bottom = static_cast<INT16>(Header.Y + Header.Height); wmfPlaceableFileHeader->Inch = static_cast<INT16>(GpRound(aveDpi)); wmfPlaceableFileHeader->Reserved = 0; wmfPlaceableFileHeader->Checksum = GetWmfPlaceableCheckSum(wmfPlaceableFileHeader); stream->Write(placeableBuffer, PLACEABLE_BUFFER_SIZE, NULL); stream->Write(wmfData, wmfDataSize, NULL); GpFree(wmfData);
// align
if ((wmfDataSize & 0x03) != 0) { INT pad = 0; stream->Write(&pad, 4 - (wmfDataSize & 0x03), NULL); } } else if (!Header.IsEmfPlusDual()) { INT emfDataSize = GetEnhMetaFileBits(Hemf, 0, NULL);
if (emfDataSize <= 0) { WARNING(("Empty EMF")); return Win32Error; }
metafileData.MetaType = Header.GetType(); metafileData.MetaDataSize = emfDataSize; stream->Write(&metafileData, sizeof(metafileData), NULL);
if (!::EnumEnhMetaFile(NULL, Hemf, EnumEmfToStream, stream, NULL)) { WARNING(("Problem retrieving EMF Data")); return Win32Error; } } else // it is EMF+ Dual. Remove the dual records for embedding.
{ RemoveDualRecords removeDualRecords;
// First, figure out how big a buffer we need to allocate
if (!::EnumEnhMetaFile(NULL, Hemf, EnumEmfRemoveDualRecords, &removeDualRecords, NULL)) { WARNING(("Problem retrieving EMF Data")); return Win32Error; }
INT emfDataSize = removeDualRecords.Size;
BYTE * emfData = (BYTE *)GpMalloc(emfDataSize); if (emfData == NULL) { return OutOfMemory; }
removeDualRecords.Init(); removeDualRecords.MetaData = emfData;
if (!::EnumEnhMetaFile(NULL, Hemf, EnumEmfRemoveDualRecords, &removeDualRecords, NULL)) { WARNING(("Problem retrieving EMF Data")); GpFree(emfData); return Win32Error; }
// make sure we get the same value back the 2nd time
ASSERT(emfDataSize == removeDualRecords.Size);
// We convert MetafileTypeEmfPlusDual into MetafileTypeEmfPlusOnly
metafileData.MetaType = MetafileTypeEmfPlusOnly; metafileData.MetaDataSize = removeDualRecords.Size; stream->Write(&metafileData, sizeof(metafileData), NULL);
((ENHMETAHEADER3 *)emfData)->nBytes = removeDualRecords.Size; ((ENHMETAHEADER3 *)emfData)->nRecords = removeDualRecords.NumRecords; stream->Write(emfData, removeDualRecords.Size, NULL); GpFree(emfData); }
return Ok; }
UINT GpMetafile::GetDataSize() const { if ((State != DoneRecordingMetafileState) && (State != ReadyToPlayMetafileState)) { WARNING(("Wrong State To GetDataSize")); return 0; }
ASSERT(Hemf != NULL);
UINT dataSize = sizeof(MetafileData);
if (Header.IsWmf()) { INT wmfDataSize = GetMetaFileBitsEx((HMETAFILE)Hemf, 0, NULL);
if (wmfDataSize <= 0) { WARNING(("Empty WMF")); return 0; } // add aligned size of the placeable header and aligned wmf size
dataSize += 24 + ((wmfDataSize + 3) & ~3); } else if (!Header.IsEmfPlusDual()) { INT emfDataSize = GetEnhMetaFileBits(Hemf, 0, NULL);
if (emfDataSize <= 0) { WARNING(("Empty EMF")); return 0; } dataSize += emfDataSize; } else // it is EMF+ Dual. Remove the dual records for embedding.
{ RemoveDualRecords removeDualRecords;
if (!::EnumEnhMetaFile(NULL, Hemf, EnumEmfRemoveDualRecords, &removeDualRecords, NULL)) { WARNING(("Problem retrieving EMF Data")); return 0; }
dataSize += removeDualRecords.Size; }
return dataSize; }
/**************************************************************************\
* * Function Description: * * Read the metafile object from memory. * * Arguments: * * [IN] data - the data to set the metafile with * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpMetafile::SetData( const BYTE * dataBuffer, UINT size ) { ASSERT ((GpImageType)(((MetafileData *)dataBuffer)->Type) == ImageTypeMetafile);
InitDefaults();
if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; }
if (size < sizeof(MetafileData)) { WARNING(("size too small")); return InvalidParameter; }
const MetafileData * metaData;
metaData = reinterpret_cast<const MetafileData *>(dataBuffer);
if (!metaData->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; }
dataBuffer += sizeof(MetafileData); size -= sizeof(MetafileData);
MetafileType type = (MetafileType)metaData->MetaType; UINT metaDataSize = metaData->MetaDataSize;
if (type == MetafileTypeWmfPlaceable) { HMETAFILE hWmf;
if (size < (metaDataSize + 24)) { WARNING(("size too small")); return InvalidParameter; }
hWmf = SetMetaFileBitsEx(metaDataSize, dataBuffer + 24); if (hWmf != NULL) { if (GetMetafileHeader(hWmf, (WmfPlaceableFileHeader*)dataBuffer, Header) == Ok) { Hemf = (HENHMETAFILE)hWmf; State = DoneRecordingMetafileState; return Ok; } DeleteMetaFile(hWmf); } } else { // We'll let the object think it's dual, even if we've removed
// all the dual records. It shouldn't hurt anything.
HENHMETAFILE hEmf;
if (size < metaDataSize) { WARNING(("size too small")); return InvalidParameter; }
hEmf = SetEnhMetaFileBits(metaDataSize, dataBuffer);
if (hEmf != NULL) { BOOL isCorrupted;
if (GetMetafileHeader(hEmf, Header, &isCorrupted) == Ok) { Hemf = hEmf; State = DoneRecordingMetafileState; return Ok; } if (isCorrupted) { State = CorruptedMetafileState; } DeleteEnhMetaFile(hEmf); } } return GenericError; }
class CommentEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeComment);
return; } };
class GetDCEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeGetDC);
// Flag that the next down-level records should be played.
#if 0
// This is now done in the enumerator, so that it will happen
// for enumeration as well as playback.
player->PlayEMFRecords = TRUE; #endif
} };
#define EMFPLUS_MAJORVERSION(v) ((v) & 0xFFFF0000)
#define EMFPLUS_MINORVERSION(v) ((v) & 0x0000FFFF)
#define EMF_SKIP_ALL_MULTIFORMAT_SECTIONS 0x7FFFFFFF
#define MULTIFORMATSTARTEPR_MINSIZE (sizeof(UINT32) + sizeof(UINT32))
// Note: nesting multiformat records does NOT work.
class MultiFormatStartEPR : public EmfPlusRecordPlay { protected: UINT32 NumSections; UINT32 Version[1];
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeMultiFormatStart);
if (dataSize < MULTIFORMATSTARTEPR_MINSIZE) { WARNING(("MultiFormatStartEPR::Play dataSize is too small")); return; }
UINT sectionToPlay = EMF_SKIP_ALL_MULTIFORMAT_SECTIONS;
if (NumSections > 0) { if (dataSize < MULTIFORMATSTARTEPR_MINSIZE + ((NumSections - 1) * sizeof(UINT32))) { WARNING(("MultiFormatStartEPR::Play dataSize is too small")); return; }
if ((Version[0] == EMFPLUS_VERSION) || (NumSections == 1)) { sectionToPlay = 1; // start counting from 1, not 0
} else { UINT playVersion = 0; UINT curVersion;
// The multiformat section must match the major version.
// The first format whose minor version <= the current
// minor version is the one we play. If we don't find
// one of those, then we play the one whose minor version
// is closest to the current minor version.
for (UINT i = 0; i < NumSections; i++) { curVersion = Version[i]; if (EMFPLUS_MAJORVERSION(curVersion) == EMFPLUS_MAJORVERSION(EMFPLUS_VERSION)) { if (EMFPLUS_MINORVERSION(curVersion) <= EMFPLUS_MINORVERSION(EMFPLUS_VERSION)) { sectionToPlay = i + 1; break; } else if ((playVersion == 0) || (EMFPLUS_MINORVERSION(curVersion) < EMFPLUS_MINORVERSION(playVersion))) { playVersion = curVersion; sectionToPlay = i + 1; } } } } } player->MultiFormatSection = sectionToPlay; player->CurFormatSection = 0; player->PlayMultiFormatSection = FALSE; } };
class MultiFormatSectionEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeMultiFormatSection);
if (player->MultiFormatSection != 0) { player->PlayMultiFormatSection = (++(player->CurFormatSection) == player->MultiFormatSection); } } };
class MultiFormatEndEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeMultiFormatEnd);
player->MultiFormatSection = 0; player->CurFormatSection = 0; player->PlayMultiFormatSection = TRUE; } };
class SetAntiAliasModeEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetAntiAliasMode);
player->Graphics->SetAntiAliasMode(GetAntiAliasMode(flags)); } };
class SetTextRenderingHintEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetTextRenderingHint);
player->Graphics->SetTextRenderingHint(GetTextRenderingHint(flags)); } };
class SetTextContrastEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetTextContrast);
player->Graphics->SetTextContrast(GetTextContrast(flags)); } };
class SetInterpolationModeEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetInterpolationMode);
player->Graphics->SetInterpolationMode(GetInterpolationMode(flags)); } };
class SetPixelOffsetModeEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetPixelOffsetMode);
player->Graphics->SetPixelOffsetMode(GetPixelOffsetMode(flags)); } };
class SetCompositingModeEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetCompositingMode);
player->Graphics->SetCompositingMode(GetCompositingMode(flags)); } };
class SetCompositingQualityEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetCompositingQuality);
player->Graphics->SetCompositingQuality(GetCompositingQuality(flags)); } };
class SetRenderingOriginEPR : public EmfPlusRecordPlay { INT x; INT y; public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetRenderingOrigin);
player->Graphics->SetRenderingOrigin(x, y); } };
#define SAVEEPR_MINSIZE (sizeof(UINT32))
class SaveEPR : public EmfPlusRecordPlay { protected: UINT32 StackIndex;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSave);
if (dataSize < SAVEEPR_MINSIZE) { WARNING(("SaveEPR::Play dataSize is too small")); return; }
player->NewSave(StackIndex, player->Graphics->Save()); } };
#define RESTOREEPR_MINSIZE (sizeof(UINT32))
class RestoreEPR : public EmfPlusRecordPlay { protected: UINT32 StackIndex;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeRestore);
if (dataSize < RESTOREEPR_MINSIZE) { WARNING(("RestoreEPR::Play dataSize is too small")); return; }
player->Graphics->Restore(player->GetSaveID(StackIndex)); } };
#define BEGINCONTAINEREPR_MINSIZE (sizeof(GpRectF) + sizeof(GpRectF) + sizeof(UINT32))
class BeginContainerEPR : public EmfPlusRecordPlay { protected: GpRectF DestRect; GpRectF SrcRect; UINT32 StackIndex;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeBeginContainer);
if (dataSize < BEGINCONTAINEREPR_MINSIZE) { WARNING(("BeginContainerEPR::Play dataSize is too small")); return; }
player->NewSave(StackIndex, player->Graphics->BeginContainer(DestRect, SrcRect, GetPageUnit(flags))); } };
#define BEGINCONTAINERNOPARAMSEPR_MINSIZE (sizeof(UINT32))
class BeginContainerNoParamsEPR : public EmfPlusRecordPlay { protected: UINT32 StackIndex;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeBeginContainerNoParams);
if (dataSize < BEGINCONTAINERNOPARAMSEPR_MINSIZE) { WARNING(("BeginContainerNoParamsEPR::Play dataSize is too small")); return; }
player->NewSave(StackIndex, player->Graphics->BeginContainer()); } };
#define ENDCONTAINEREPR_MINSIZE (sizeof(UINT32))
class EndContainerEPR : public EmfPlusRecordPlay { protected: UINT32 StackIndex;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeEndContainer);
if (dataSize < ENDCONTAINEREPR_MINSIZE) { WARNING(("EndContainerEPR::Play dataSize is too small")); return; }
player->Graphics->EndContainer(player->GetSaveID(StackIndex)); } };
#define SETWORLDTRANSFORMEPR_MINSIZE GDIP_MATRIX_SIZE
class SetWorldTransformEPR : public EmfPlusRecordPlay { protected: REAL MatrixData[6];
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetWorldTransform);
if (dataSize < SETWORLDTRANSFORMEPR_MINSIZE) { WARNING(("SetWorldTransformEPR::Play dataSize is too small")); return; }
GpMatrix matrix(MatrixData[0], MatrixData[1], MatrixData[2], MatrixData[3], MatrixData[4], MatrixData[5]); player->Graphics->SetWorldTransform(matrix); } };
class ResetWorldTransformEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeResetWorldTransform);
player->Graphics->ResetWorldTransform(); } };
#define MULTIPLYWORLDTRANSFORMEPR_MINSIZE GDIP_MATRIX_SIZE
class MultiplyWorldTransformEPR : public EmfPlusRecordPlay { protected: REAL MatrixData[6];
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeMultiplyWorldTransform);
if (dataSize < MULTIPLYWORLDTRANSFORMEPR_MINSIZE) { WARNING(("MultiplyWorldTransformEPR::Play dataSize is too small")); return; }
GpMatrix matrix(MatrixData[0], MatrixData[1], MatrixData[2], MatrixData[3], MatrixData[4], MatrixData[5]); player->Graphics->MultiplyWorldTransform(matrix, GetMatrixOrder(flags)); } };
#define TRANSLATEWORLDTRANSFORMEPR_MINSIZE (sizeof(REAL) + sizeof(REAL))
class TranslateWorldTransformEPR : public EmfPlusRecordPlay { protected: REAL Dx; REAL Dy;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeTranslateWorldTransform);
if (dataSize < TRANSLATEWORLDTRANSFORMEPR_MINSIZE) { WARNING(("TranslateWorldTransformEPR::Play dataSize is too small")); return; }
player->Graphics->TranslateWorldTransform(Dx, Dy, GetMatrixOrder(flags)); } };
#define SCALEWORLDTRANSFORMEPR_MINSIZE (sizeof(REAL) + sizeof(REAL))
class ScaleWorldTransformEPR : public EmfPlusRecordPlay { protected: REAL Sx; REAL Sy;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeScaleWorldTransform);
if (dataSize < SCALEWORLDTRANSFORMEPR_MINSIZE) { WARNING(("ScaleWorldTransformEPR::Play dataSize is too small")); return; }
player->Graphics->ScaleWorldTransform(Sx, Sy, GetMatrixOrder(flags)); } };
#define ROTATEWORLDTRANSFORMEPR_MINSIZE (sizeof(REAL))
class RotateWorldTransformEPR : public EmfPlusRecordPlay { protected: REAL Angle;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeRotateWorldTransform);
if (dataSize < ROTATEWORLDTRANSFORMEPR_MINSIZE) { WARNING(("RotateWorldTransformEPR::Play dataSize is too small")); return; }
player->Graphics->RotateWorldTransform(Angle, GetMatrixOrder(flags)); } };
#define SETPAGETRANSFORMEPR_MINSIZE (sizeof(REAL))
class SetPageTransformEPR : public EmfPlusRecordPlay { protected: REAL Scale;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetPageTransform);
if (dataSize < SETPAGETRANSFORMEPR_MINSIZE) { WARNING(("SetPageTransformEPR::Play dataSize is too small")); return; }
player->Graphics->SetPageTransform(GetPageUnit(flags), Scale); } };
class ResetClipEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeResetClip);
player->Graphics->ResetClip(); } };
#define SETCLIPRECTEPR_MINSIZE (sizeof(GpRectF))
class SetClipRectEPR : public EmfPlusRecordPlay { protected: GpRectF ClipRect; // !!! Handle 16-bit rect
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetClipRect);
if (dataSize < SETCLIPRECTEPR_MINSIZE) { WARNING(("SetClipRectEPR::Play dataSize is too small")); return; }
player->Graphics->SetClip(ClipRect, GetCombineMode(flags)); } };
class SetClipPathEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetClipPath);
GpPath *path = (GpPath *)player->GetObject(GetMetaObjectId(flags), ObjectTypePath); if (path != NULL) { player->Graphics->SetClip(path, GetCombineMode(flags), GetIsDevicePath(flags)); }
} };
class SetClipRegionEPR : public EmfPlusRecordPlay { public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeSetClipRegion);
GpRegion *region = (GpRegion *)player->GetObject(GetMetaObjectId(flags), ObjectTypeRegion); if (region != NULL) { player->Graphics->SetClip(region, GetCombineMode(flags)); }
} };
#define OFFSETCLIPEPR_MINSIZE (sizeof(REAL) + sizeof(REAL))
class OffsetClipEPR : public EmfPlusRecordPlay { protected: REAL Dx; REAL Dy;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeOffsetClip);
if (dataSize < OFFSETCLIPEPR_MINSIZE) { WARNING(("OffsetClipEPR::Play dataSize is too small")); return; }
player->Graphics->OffsetClip(Dx, Dy); } };
#define OBJECTEPR_MINSIZE (sizeof(UINT32))
class ObjectEPR : public EmfPlusRecordPlay { protected: BYTE ObjectData[1];
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { if (dataSize < OBJECTEPR_MINSIZE) { WARNING(("ObjectEPR::Play dataSize is too small")); return; }
player->AddObject(flags, ObjectData, dataSize); } };
#define CLEAREPR_MINSIZE (sizeof(UINT32))
class ClearEPR : public EmfPlusBoundsRecord { protected: ARGB Color;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeClear);
if (dataSize < CLEAREPR_MINSIZE) { WARNING(("ClearEPR::Play dataSize is too small")); return; }
GpColor color;
color.SetColor(Color);
player->Graphics->Clear(color); } };
#define FILLRECTSEPR_MINSIZE (sizeof(UINT32) + sizeof(UINT32))
class FillRectsEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue; UINT32 Count; BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeFillRects);
if (dataSize < FILLRECTSEPR_MINSIZE) { WARNING(("FillRectsEPR::Play dataSize is too small")); return; }
GpBrush * brush = player->GetBrush(BrushValue, flags); GpRectF * rects = player->GetRects(RectData, dataSize - FILLRECTSEPR_MINSIZE, Count, flags);
if (rects != NULL) { if (brush != NULL) { player->Graphics->FillRects(brush, rects, Count); } player->FreePointsBuffer(); } } };
#define DRAWRECTSEPR_MINSIZE (sizeof(UINT32))
class DrawRectsEPR : public EmfPlusBoundsRecord { protected: UINT32 Count; BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawRects);
if (dataSize < DRAWRECTSEPR_MINSIZE) { WARNING(("DrawRectsEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpRectF * rects = player->GetRects(RectData, dataSize - DRAWRECTSEPR_MINSIZE, Count, flags);
if (rects != NULL) { if (pen != NULL) { player->Graphics->DrawRects(pen, rects, Count); } player->FreePointsBuffer(); } } };
#define FILLPOLYGONEPR_MINSIZE (sizeof(UINT32) + sizeof(UINT32))
class FillPolygonEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue; UINT32 Count; BYTE PointData[1]; // GpPoint16 or GpPointF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeFillPolygon);
if (dataSize < FILLPOLYGONEPR_MINSIZE) { WARNING(("FillPolygonEPR::Play dataSize is too small")); return; }
GpBrush * brush = player->GetBrush(BrushValue, flags); GpPointF * points = player->GetPoints(PointData, dataSize - FILLPOLYGONEPR_MINSIZE, Count, flags);
if (points != NULL) { if (brush != NULL) { player->Graphics->FillPolygon(brush, points, Count, GetFillMode(flags)); } player->FreePointsBuffer(); } } };
#define DRAWLINESEPR_MINSIZE (sizeof(UINT32))
class DrawLinesEPR : public EmfPlusBoundsRecord { protected: UINT32 Count; BYTE PointData[1]; // GpPoint16 or GpPointF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawLines);
if (dataSize < DRAWLINESEPR_MINSIZE) { WARNING(("DrawLinesEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpPointF * points = player->GetPoints(PointData, dataSize - DRAWLINESEPR_MINSIZE, Count, flags);
if (points != NULL) { if (pen != NULL) { player->Graphics->DrawLines(pen, points, Count, IsClosed(flags)); } player->FreePointsBuffer(); } } };
#define FILLELLIPSEEPR_MINSIZE (sizeof(UINT32))
class FillEllipseEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue; BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeFillEllipse);
if (dataSize < FILLELLIPSEEPR_MINSIZE) { WARNING(("FillEllipseEPR::Play dataSize is too small")); return; }
GpBrush * brush = player->GetBrush(BrushValue, flags); GpRectF * rect = player->GetRects(RectData, dataSize - FILLELLIPSEEPR_MINSIZE, 1, flags);
if (rect != NULL) { if (brush != NULL) { player->Graphics->FillEllipse(brush, *rect); } player->FreePointsBuffer(); } } };
class DrawEllipseEPR : public EmfPlusBoundsRecord { protected: BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawEllipse);
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpRectF * rect = player->GetRects(RectData, dataSize, 1, flags);
if (rect != NULL) { if (pen != NULL) { player->Graphics->DrawEllipse(pen, *rect); } player->FreePointsBuffer(); } } };
#define FILLPIEEPR_MINSIZE (sizeof(UINT32) + sizeof(REAL) + sizeof(REAL))
class FillPieEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue; REAL StartAngle; REAL SweepAngle; BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeFillPie);
if (dataSize < FILLPIEEPR_MINSIZE) { WARNING(("FillPieEPR::Play dataSize is too small")); return; }
GpBrush * brush = player->GetBrush(BrushValue, flags); GpRectF * rect = player->GetRects(RectData, dataSize - FILLPIEEPR_MINSIZE, 1, flags);
if (rect != NULL) { if (brush != NULL) { player->Graphics->FillPie(brush, *rect, StartAngle, SweepAngle); } player->FreePointsBuffer(); } } };
#define DRAWPIEEPR_MINSIZE (sizeof(REAL) + sizeof(REAL))
class DrawPieEPR : public EmfPlusBoundsRecord { protected: REAL StartAngle; REAL SweepAngle; BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawPie);
if (dataSize < DRAWPIEEPR_MINSIZE) { WARNING(("DrawPieEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpRectF * rect = player->GetRects(RectData, dataSize - DRAWPIEEPR_MINSIZE, 1, flags);
if (rect != NULL) { if (pen != NULL) { player->Graphics->DrawPie(pen, *rect, StartAngle, SweepAngle); } player->FreePointsBuffer(); } } };
#define DRAWARCEPR_MINSIZE (sizeof(REAL) + sizeof(REAL))
class DrawArcEPR : public EmfPlusBoundsRecord { protected: REAL StartAngle; REAL SweepAngle; BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawArc);
if (dataSize < DRAWARCEPR_MINSIZE) { WARNING(("DrawArcEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpRectF * rect = player->GetRects(RectData, dataSize - DRAWARCEPR_MINSIZE, 1, flags);
if (rect != NULL) { if (pen != NULL) { player->Graphics->DrawArc(pen, *rect, StartAngle, SweepAngle); } player->FreePointsBuffer(); } } };
#define FILLREGIONEPR_MINSIZE (sizeof(UINT32))
class FillRegionEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeFillRegion);
if (dataSize < FILLREGIONEPR_MINSIZE) { WARNING(("FillRegionEPR::Play dataSize is too small")); return; }
GpBrush * brush = player->GetBrush(BrushValue, flags); GpRegion * region = (GpRegion *)player->GetObject(GetMetaObjectId(flags), ObjectTypeRegion);
if ((brush != NULL) && (region != NULL)) { player->Graphics->FillRegion(brush, region); } } };
#define FILLPATHEPR_MINSIZE (sizeof(UINT32))
class FillPathEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeFillPath);
if (dataSize < FILLPATHEPR_MINSIZE) { WARNING(("FillPathEPR::Play dataSize is too small")); return; }
GpBrush * brush = player->GetBrush(BrushValue, flags); GpPath * path = (GpPath *)player->GetObject(GetMetaObjectId(flags), ObjectTypePath);
if ((brush != NULL) && (path != NULL)) { player->Graphics->FillPath(brush, path); } } };
#define DRAWPATHEPR_MINSIZE (sizeof(UINT32))
class DrawPathEPR : public EmfPlusBoundsRecord { protected: UINT32 PenId;
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawPath);
if (dataSize < DRAWPATHEPR_MINSIZE) { WARNING(("DrawPathEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(PenId, ObjectTypePen); GpPath * path = (GpPath *)player->GetObject(GetMetaObjectId(flags), ObjectTypePath);
if ((pen != NULL) && (path != NULL)) { player->Graphics->DrawPath(pen, path); } } };
#define FILLCLOSEDCURVEEPR_MINSIZE (sizeof(UINT32) + sizeof(REAL) + sizeof(UINT32))
class FillClosedCurveEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue; REAL Tension; UINT32 Count; BYTE PointData[1]; // GpPoint16 or GpPointF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeFillClosedCurve);
if (dataSize < FILLCLOSEDCURVEEPR_MINSIZE) { WARNING(("FillClosedCurveEPR::Play dataSize is too small")); return; }
GpBrush * brush = player->GetBrush(BrushValue, flags); GpPointF * points = player->GetPoints(PointData, dataSize - FILLCLOSEDCURVEEPR_MINSIZE, Count, flags);
if (points != NULL) { if (brush != NULL) { player->Graphics->FillClosedCurve(brush, points, Count,Tension,GetFillMode(flags)); } player->FreePointsBuffer(); } } };
#define DRAWCLOSEDCURVEEPR_MINSIZE (sizeof(REAL) + sizeof(UINT32))
class DrawClosedCurveEPR : public EmfPlusBoundsRecord { protected: REAL Tension; UINT32 Count; BYTE PointData[1]; // GpPoint16 or GpPointF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawClosedCurve);
if (dataSize < DRAWCLOSEDCURVEEPR_MINSIZE) { WARNING(("DrawClosedCurveEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpPointF * points = player->GetPoints(PointData, dataSize - DRAWCLOSEDCURVEEPR_MINSIZE, Count, flags);
if (points != NULL) { if (pen != NULL) { player->Graphics->DrawClosedCurve(pen, points, Count, Tension); } player->FreePointsBuffer(); } } };
#define DRAWCURVEEPR_MINSIZE (sizeof(REAL) + sizeof(INT32) + sizeof(UINT32) + sizeof(UINT32))
class DrawCurveEPR : public EmfPlusBoundsRecord { protected: REAL Tension; INT32 Offset; UINT32 NumSegments; UINT32 Count; BYTE PointData[1]; // GpPoint16 or GpPointF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawCurve);
if (dataSize < DRAWCURVEEPR_MINSIZE) { WARNING(("DrawCurveEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpPointF * points = player->GetPoints(PointData, dataSize - DRAWCURVEEPR_MINSIZE, Count, flags);
if (points != NULL) { if (pen != NULL) { player->Graphics->DrawCurve(pen, points, Count, Tension, Offset, NumSegments); } player->FreePointsBuffer(); } } };
#define DRAWBEZIERSEPR_MINSIZE (sizeof(UINT32))
class DrawBeziersEPR : public EmfPlusBoundsRecord { protected: UINT32 Count; BYTE PointData[1]; // GpPoint16 or GpPointF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawBeziers);
if (dataSize < DRAWBEZIERSEPR_MINSIZE) { WARNING(("DrawBeziersEPR::Play dataSize is too small")); return; }
GpPen * pen = (GpPen *)player->GetObject(GetMetaObjectId(flags), ObjectTypePen); GpPointF * points = player->GetPoints(PointData, dataSize - DRAWBEZIERSEPR_MINSIZE, Count, flags);
if (points != NULL) { if (pen != NULL) { player->Graphics->DrawBeziers(pen, points, Count); } player->FreePointsBuffer(); } } };
#define DRAWIMAGEEPR_MINSIZE (sizeof(INT32) + sizeof(GpRectF))
class DrawImageEPR : public EmfPlusBoundsRecord { protected: UINT32 ImageAttributesId; INT32 SrcUnit; GpRectF SrcRect; BYTE RectData[1]; // GpRect16 or GpRectF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawImage);
if (dataSize < DRAWIMAGEEPR_MINSIZE) { WARNING(("DrawImageEPR::Play dataSize is too small")); return; }
GpImage *image = (GpImage *)player->GetObject(GetMetaObjectId(flags), ObjectTypeImage); GpRectF *destRect = player->GetRects(RectData, dataSize - DRAWIMAGEEPR_MINSIZE, 1, flags); GpImageAttributes *imageAttributes = (GpImageAttributes *)player->GetObject( ImageAttributesId, ObjectTypeImageAttributes );
if ( (image != NULL) && (NULL != destRect) ) { GpStatus status = player->Graphics->DrawImage( image, *destRect, SrcRect, static_cast<GpPageUnit>(SrcUnit), imageAttributes, player->DrawImageCallback, player->DrawImageCallbackData ); if (status == Aborted) { // stop enumerating records
player->EnumerateAborted = TRUE; } } } };
#define DRAWIMAGEPOINTSEPR_MINSIZE (sizeof(INT32) + sizeof(GpRectF) + sizeof(UINT32))
class DrawImagePointsEPR : public EmfPlusBoundsRecord { protected: UINT32 ImageAttributesId; INT32 SrcUnit; GpRectF SrcRect; UINT32 Count; BYTE PointData[1]; // GpPoint16 or GpPointF
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawImagePoints);
if (dataSize < DRAWIMAGEPOINTSEPR_MINSIZE) { WARNING(("DrawImagePointsEPR::Play dataSize is too small")); return; }
GpImage *image = (GpImage *)player->GetObject(GetMetaObjectId(flags), ObjectTypeImage); GpPointF *destPoints = player->GetPoints(PointData, dataSize - DRAWIMAGEPOINTSEPR_MINSIZE, Count, flags); GpImageAttributes *imageAttributes = (GpImageAttributes *)player->GetObject( ImageAttributesId, ObjectTypeImageAttributes );
if (destPoints != NULL) { if (image != NULL) { GpStatus status = player->Graphics->DrawImage( image, destPoints, Count, SrcRect, static_cast<GpPageUnit>(SrcUnit), imageAttributes, player->DrawImageCallback, player->DrawImageCallbackData ); if (status == Aborted) { // stop enumerating records
player->EnumerateAborted = TRUE; } } player->FreePointsBuffer(); } } };
#define DRAWSTRINGEPR_MINSIZE (sizeof(UINT32) + sizeof(UINT32) + sizeof(UINT32) + sizeof(GpRectF))
class DrawStringEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue; UINT32 FormatId; UINT32 Length; GpRectF LayoutRect; BYTE StringData[1];
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawString);
if (dataSize < DRAWSTRINGEPR_MINSIZE) { WARNING(("DrawStringEPR::Play dataSize is too small")); return; }
GlobalTextLock lock;
GpBrush * brush = player->GetBrush(BrushValue, flags); GpFont * font = (GpFont *)player->GetObject(GetMetaObjectId(flags), ObjectTypeFont);
// Optional parameter - can return NULL.
GpStringFormat *format = (GpStringFormat *)player->GetObject( FormatId, ObjectTypeStringFormat );
if (Length > 0) { if (dataSize >= (DRAWSTRINGEPR_MINSIZE + (Length * sizeof(WCHAR)))) { if ((brush != NULL) && (font != NULL)) { // !!! TODO:
// Determine whether the string is compressed or not.
// If so, decompress it.
player->Graphics->DrawString( (WCHAR *)StringData, Length, font, &LayoutRect, format, brush ); } } else { WARNING(("DrawStringEPR::Play dataSize is too small")); return; }
player->FreePointsBuffer(); } } };
#define DRAWDRIVERSTRINGEPR_MINSIZE (sizeof(UINT32) + sizeof(INT) + sizeof(UINT32) + sizeof(UINT32))
class DrawDriverStringEPR : public EmfPlusBoundsRecord { protected: UINT32 BrushValue; INT ApiFlags; UINT32 MatrixPresent; UINT32 GlyphCount; BYTE Data[1];
public: VOID Play( MetafilePlayer * player, EmfPlusRecordType recordType, UINT flags, UINT dataSize ) const { ASSERT(recordType == EmfPlusRecordTypeDrawDriverString);
if (dataSize < DRAWDRIVERSTRINGEPR_MINSIZE) { WARNING(("DrawDriverStringEPR::Play dataSize is too small")); return; }
GlobalTextLock lock; GpBrush * brush = player->GetBrush(BrushValue, flags); GpFont * font = (GpFont *)player->GetObject(GetMetaObjectId(flags), ObjectTypeFont);
if (GlyphCount > 0) { UINT requiredSize = DRAWDRIVERSTRINGEPR_MINSIZE + (GlyphCount * sizeof(WCHAR)) + (GlyphCount * sizeof(PointF)); if (dataSize >= requiredSize) { if ((brush != NULL) && (font != NULL)) { WCHAR *text = (WCHAR *) Data; PointF *positions = (PointF *) (Data + (GlyphCount * sizeof(WCHAR)));
if (MatrixPresent > 0) { if (dataSize < requiredSize + GDIP_MATRIX_SIZE) { WARNING(("DrawDriverStringEPR::Play dataSize is too small")); return; }
REAL *matrixData = (REAL *)((BYTE *) ((BYTE *)positions) + (GlyphCount * sizeof(PointF)));
GpMatrix matrix(matrixData);
player->Graphics->DrawDriverString( (unsigned short *)text, GlyphCount, font, brush, positions, ApiFlags | DriverStringOptionsMetaPlay, &matrix); } else { player->Graphics->DrawDriverString( (unsigned short *)text, GlyphCount, font, brush, positions, ApiFlags, NULL); } } } else { WARNING(("DrawDriverStringEPR::Play dataSize is too small")); return; }
player->FreePointsBuffer(); } } };
// The order of these methods must exactly match
// the order of the enums of the record numbers.
PLAYRECORDFUNC RecordPlayFuncs[EmfPlusRecordTypeMax - EmfPlusRecordTypeMin + 1] = { (PLAYRECORDFUNC)&EmfPlusHeaderRecord::Play, // Header
(PLAYRECORDFUNC)&EmfPlusRecordPlay::Play, // EndOfFile
(PLAYRECORDFUNC)&CommentEPR::Play,
(PLAYRECORDFUNC)&GetDCEPR::Play,
(PLAYRECORDFUNC)&MultiFormatStartEPR::Play, (PLAYRECORDFUNC)&MultiFormatSectionEPR::Play, (PLAYRECORDFUNC)&MultiFormatEndEPR::Play,
// For all persistent objects
(PLAYRECORDFUNC)&ObjectEPR::Play,
// Drawing Records
(PLAYRECORDFUNC)&ClearEPR::Play, (PLAYRECORDFUNC)&FillRectsEPR::Play, (PLAYRECORDFUNC)&DrawRectsEPR::Play, (PLAYRECORDFUNC)&FillPolygonEPR::Play, (PLAYRECORDFUNC)&DrawLinesEPR::Play, (PLAYRECORDFUNC)&FillEllipseEPR::Play, (PLAYRECORDFUNC)&DrawEllipseEPR::Play, (PLAYRECORDFUNC)&FillPieEPR::Play, (PLAYRECORDFUNC)&DrawPieEPR::Play, (PLAYRECORDFUNC)&DrawArcEPR::Play, (PLAYRECORDFUNC)&FillRegionEPR::Play, (PLAYRECORDFUNC)&FillPathEPR::Play, (PLAYRECORDFUNC)&DrawPathEPR::Play, (PLAYRECORDFUNC)&FillClosedCurveEPR::Play, (PLAYRECORDFUNC)&DrawClosedCurveEPR::Play, (PLAYRECORDFUNC)&DrawCurveEPR::Play, (PLAYRECORDFUNC)&DrawBeziersEPR::Play, (PLAYRECORDFUNC)&DrawImageEPR::Play, (PLAYRECORDFUNC)&DrawImagePointsEPR::Play, (PLAYRECORDFUNC)&DrawStringEPR::Play,
// Graphics State Records
(PLAYRECORDFUNC)&SetRenderingOriginEPR::Play, (PLAYRECORDFUNC)&SetAntiAliasModeEPR::Play, (PLAYRECORDFUNC)&SetTextRenderingHintEPR::Play, (PLAYRECORDFUNC)&SetTextContrastEPR::Play, (PLAYRECORDFUNC)&SetInterpolationModeEPR::Play, (PLAYRECORDFUNC)&SetPixelOffsetModeEPR::Play, (PLAYRECORDFUNC)&SetCompositingModeEPR::Play, (PLAYRECORDFUNC)&SetCompositingQualityEPR::Play, (PLAYRECORDFUNC)&SaveEPR::Play, (PLAYRECORDFUNC)&RestoreEPR::Play, (PLAYRECORDFUNC)&BeginContainerEPR::Play, (PLAYRECORDFUNC)&BeginContainerNoParamsEPR::Play, (PLAYRECORDFUNC)&EndContainerEPR::Play, (PLAYRECORDFUNC)&SetWorldTransformEPR::Play, (PLAYRECORDFUNC)&ResetWorldTransformEPR::Play, (PLAYRECORDFUNC)&MultiplyWorldTransformEPR::Play, (PLAYRECORDFUNC)&TranslateWorldTransformEPR::Play, (PLAYRECORDFUNC)&ScaleWorldTransformEPR::Play, (PLAYRECORDFUNC)&RotateWorldTransformEPR::Play, (PLAYRECORDFUNC)&SetPageTransformEPR::Play, (PLAYRECORDFUNC)&ResetClipEPR::Play, (PLAYRECORDFUNC)&SetClipRectEPR::Play, (PLAYRECORDFUNC)&SetClipPathEPR::Play, (PLAYRECORDFUNC)&SetClipRegionEPR::Play, (PLAYRECORDFUNC)&OffsetClipEPR::Play, (PLAYRECORDFUNC)&DrawDriverStringEPR::Play,
// New record types must be added here (at the end) -- do not add above,
// since that will invalidate previous metafiles!
};
HENHMETAFILE GetEmf( const WCHAR * fileName, MetafileType type ) { HENHMETAFILE hEmf = NULL;
if (type == MetafileTypeWmfPlaceable) { IStream * wmfStream; IStream * memStream;
wmfStream = CreateStreamOnFile(fileName, GENERIC_READ); if (wmfStream != NULL) { STATSTG statstg; HRESULT hResult;
hResult = wmfStream->Stat(&statstg, STATFLAG_NONAME); if (!HResultSuccess(hResult)) { wmfStream->Release(); return hEmf; } INT size = (INT)(statstg.cbSize.QuadPart - sizeof(WmfPlaceableFileHeader));
if (SeekFromStart(wmfStream, sizeof(WmfPlaceableFileHeader))) { HGLOBAL hGlobal;
hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, size); if (hGlobal != NULL) { hResult = CreateStreamOnHGlobal(hGlobal, TRUE, &memStream); if (HResultSuccess(hResult) && (memStream != NULL)) { if (CopyStream(wmfStream, memStream, size)) { BYTE * wmfData = (BYTE *)GlobalLock(hGlobal);
if (wmfData != NULL) { hEmf = (HENHMETAFILE) SetMetaFileBitsEx(size, wmfData); GlobalUnlock(hGlobal); } } memStream->Release(); } else { GlobalFree(hGlobal); } } } wmfStream->Release(); } } else { if (Globals::IsNt) { hEmf = GetEnhMetaFileW(fileName); } else // Windows 9x - non-Unicode
{ AnsiStrFromUnicode nameStr(fileName);
if (nameStr.IsValid()) { hEmf = GetEnhMetaFileA(nameStr); } } }
return hEmf; }
#if 0 // don't need this right now
WCHAR * GetTemporaryFilename() { if (Globals::IsNt) { WCHAR pathBuffer[MAX_PATH + 1]; WCHAR fileBuffer[MAX_PATH + 12 + 1]; // 12 for filename itself
UINT len = GetTempPathW(MAX_PATH, pathBuffer);
if ((len == 0) || (len > MAX_PATH)) { pathBuffer[0] = L'.'; pathBuffer[1] = L'\0'; } if (GetTempFileNameW(pathBuffer, L"Emp", 0, fileBuffer) == 0) { return NULL; } return UnicodeStringDuplicate(fileBuffer); } else // Windows 9x - non-Unicode
{ CHAR pathBuffer[MAX_PATH + 1]; CHAR fileBuffer[MAX_PATH + 12 + 1]; // 12 for filename itself
UINT len = GetTempPathA(MAX_PATH, pathBuffer);
if ((len == 0) || (len > MAX_PATH)) { pathBuffer[0] = '.'; pathBuffer[1] = '\0'; } if (GetTempFileNameA(pathBuffer, "Emp", 0, fileBuffer) == 0) { return NULL; } len = (strlen(fileBuffer) + 1) * sizeof(WCHAR); WCHAR * filename = (WCHAR *)GpMalloc(len); if (filename != NULL) { if (AnsiToUnicodeStr(fileBuffer, filename, len)) { return filename; } GpFree(filename); } return NULL; } } #endif
// For now, don't handle a source rect
GpBitmap * GpMetafile::GetBitmap( INT width, INT height, const GpImageAttributes * imageAttributes ) { GpRectF srcRect; GpPageUnit srcUnit;
this->GetBounds(&srcRect, &srcUnit);
ASSERT(srcUnit == UnitPixel);
// Determine what size to make the bitmap.
if ((width <= 0) || (height <= 0)) { if (this->IsEmfOrEmfPlus()) { width = GpRound(srcRect.Width); height = GpRound(srcRect.Height); } else // must be a WMF
{ // Convert size to use the dpi of this display.
// This is somewhat of a hack, but what else could I do,
// since I don't know where this brush will be used?
REAL srcDpiX; REAL srcDpiY; REAL destDpiX = Globals::DesktopDpiX; // guess
REAL destDpiY = Globals::DesktopDpiY;
this->GetResolution(&srcDpiX, &srcDpiY);
if ((srcDpiX <= 0) || (srcDpiY <= 0)) { WARNING(("bad dpi for WMF")); return NULL; }
width = GpRound((srcRect.Width / srcDpiX) * destDpiX); height = GpRound((srcRect.Height / srcDpiY) * destDpiY); } if ((width <= 0) || (height <= 0)) { WARNING(("bad size for metafile")); return NULL; } }
GpBitmap * bitmapImage = new GpBitmap(width, height, PIXFMT_32BPP_ARGB);
if (bitmapImage != NULL) { if (bitmapImage->IsValid()) { GpGraphics * graphics = bitmapImage->GetGraphicsContext();
if (graphics != NULL) { if (graphics->IsValid()) { // we have to lock the graphics so the driver doesn't assert
GpLock * lockGraphics = new GpLock(graphics->GetObjectLock());
if (lockGraphics != NULL) { ASSERT(lockGraphics->IsValid());
// now draw the metafile into the bitmap image
GpRectF destRect(0.0f, 0.0f, (REAL)width, (REAL)height);
// We don't want to interpolate the bitmaps in WMFs
// and EMFs when converting them to a texture.
graphics->SetInterpolationMode(InterpolationModeNearestNeighbor);
GpStatus status;
status = graphics->DrawImage( this, destRect, srcRect, srcUnit, imageAttributes);
// have to delete the lock before deleting the graphics
delete lockGraphics;
if (status == Ok) { delete graphics; return bitmapImage; } WARNING(("DrawImage failed")); } else { WARNING(("Could not create graphics lock")); } } else { WARNING(("graphics from bitmap image not valid")); } delete graphics; } else { WARNING(("could not create graphics from bitmap image")); } } else { WARNING(("bitmap image is not valid")); } bitmapImage->Dispose(); } else { WARNING(("could not create bitmap image")); } return NULL; }
|