WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/metarecord.cpp
2020-09-30 16:53:49 +02:00

5257 lines
155 KiB
C++

/**************************************************************************\
*
* Copyright (c) 1999 Microsoft Corporation
*
* Module Name:
*
* MetaFile.cpp
*
* Abstract:
*
* Metafile object handling
*
* Created:
*
* 4/14/1999 DCurtis
*
\**************************************************************************/
#include "precomp.hpp"
#include "..\imaging\api\comutils.hpp"
#define META_FORMAT_ENHANCED 0x10000 // Windows NT format
VOID
FrameToMM100(
const GpRectF * frameRect,
GpPageUnit frameUnit,
GpRectF & frameRectMM100,
REAL dpiX, // only used for pixel case
REAL dpiY
);
/**************************************************************************\
*
* Function Description:
*
* Determine if the REAL points can be converted to GpPoint16 points without
* losing accuracy. If so, then do the conversion, and set the flags to say
* we're using 16-bit points.
*
* Arguments:
*
* [IN] points - the REAL points to try to convert
* [IN] count - the number of points
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
MetafilePointData::MetafilePointData(
const GpPointF * points,
INT count
)
{
ASSERT((count > 0) && (points != NULL));
// Assume that the conversion to GpPoint16 will fail
PointData = (BYTE *)points;
PointDataSize = count * sizeof(points[0]);
Flags = 0;
AllocedPoints = NULL;
GpPoint16 * points16 = PointBuffer;
if (count > GDIP_POINTDATA_BUFFERSIZE)
{
AllocedPoints = new GpPoint16[count];
if (AllocedPoints == NULL)
{
return; // live with REAL data
}
points16 = AllocedPoints;
}
GpPoint16 * curPoint16 = points16;
INT i = count;
do
{
curPoint16->X = (INT16)GpRound(points->X);
curPoint16->Y = (INT16)GpRound(points->Y);
if (!IsPoint16Equal(curPoint16, points))
{
return; // the point data doesn't fit in 16 bits per value
}
curPoint16++;
points++;
} while (--i > 0);
// We succeeded in converting the point data to 16 bits per value
PointData = (BYTE *)points16;
PointDataSize = count * sizeof(points16[0]);
Flags = GDIP_EPRFLAGS_COMPRESSED;
}
/**************************************************************************\
*
* Function Description:
*
* Determine if the REAL rects can be converted to GpRect16 points without
* losing accuracy. If so, then do the conversion, and set the flags to say
* we're using 16-bit rects.
*
* Arguments:
*
* [IN] rects - the REAL rects to try to convert
* [IN] count - the number of rects
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
MetafileRectData::MetafileRectData(
const GpRectF * rects,
INT count
)
{
ASSERT((count > 0) && (rects != NULL));
// Assume that the conversion to GpRect16 will fail
RectData = (BYTE *)rects;
RectDataSize = count * sizeof(rects[0]);
Flags = 0;
AllocedRects = NULL;
GpRect16 * rects16 = RectBuffer;
if (count > GDIP_RECTDATA_BUFFERSIZE)
{
AllocedRects = new GpRect16[count];
if (AllocedRects == NULL)
{
return; // live with REAL data
}
rects16 = AllocedRects;
}
GpRect16 * curRect16 = rects16;
INT i = count;
do
{
curRect16->X = (INT16)GpRound(rects->X);
curRect16->Y = (INT16)GpRound(rects->Y);
curRect16->Width = (INT16)GpRound(rects->Width);
curRect16->Height = (INT16)GpRound(rects->Height);
if (!IsRect16Equal(curRect16, rects))
{
return; // the rect data doesn't fit in 16 bits per value
}
curRect16++;
rects++;
} while (--i > 0);
// We succeeded in converting the rect data to 16 bits per value
RectData = (BYTE *)rects16;
RectDataSize = count * sizeof(rects16[0]);
Flags = GDIP_EPRFLAGS_COMPRESSED;
}
///////////////////////////////////////////////////////////////////////////
// classes for handling recording of objects within the metafile
class MetafileRecordObject
{
friend class MetafileRecordObjectList;
protected:
const GpObject * ObjectPointer;
UINT Uid;
ObjectType Type;
UINT Next;
UINT Prev;
public:
MetafileRecordObject()
{
ObjectPointer = NULL;
Uid = 0;
Type = ObjectTypeInvalid;
Next = GDIP_LIST_NIL;
Prev = GDIP_LIST_NIL;
}
const GpObject * GetObject() const
{
return ObjectPointer;
}
UINT GetUid() const
{
return Uid;
}
ObjectType GetType() const
{
return Type;
}
};
class MetafileRecordObjectList
{
protected:
INT Count;
UINT LRU;
UINT MRU;
MetafileRecordObject Objects[GDIP_MAX_OBJECTS];
public:
MetafileRecordObjectList()
{
Count = 0;
LRU = GDIP_LIST_NIL;
MRU = GDIP_LIST_NIL;
}
MetafileRecordObject * GetMetaObject(UINT metaObjectID)
{
ASSERT(metaObjectID < GDIP_MAX_OBJECTS);
return Objects + metaObjectID;
}
// Search through the list, starting at the MRU entry, to see if we
// can find the object. If we do find it, return the index to the
// object in metaObjectId (even if the Uid's don't match). Return
// TRUE only if we found the object and the Uid's match.
BOOL
IsInList(
const GpObject * object,
ObjectType objectType,
UINT32 * metaObjectId
);
#if 0 // not used
VOID
RemoveAt(
UINT32 metaObjectId
);
#endif
// if metaObjectId is GDIP_LIST_NIL, use the next available slot
VOID
InsertAt(
const GpObject * object,
UINT32 * metaObjectId
);
VOID
UpdateMRU(
UINT32 metaObjectId
);
};
// Search through the list, starting at the MRU entry, to see if we
// can find the object. If we do find it, return the index to the
// object in metaObjectId (even if the Uid's don't match). Return
// TRUE only if we found the object and the Uid's match.
BOOL
MetafileRecordObjectList::IsInList(
const GpObject * object,
ObjectType objectType,
UINT32 * metaObjectId
)
{
ASSERT(object != NULL);
ASSERT(metaObjectId != NULL);
BOOL isInList = FALSE;
isInList = FALSE; // indicate object not found
*metaObjectId = GDIP_LIST_NIL; // indicate object not found
if (Count != 0)
{
UINT curIndex;
UINT uid;
curIndex = MRU;
uid = object->GetUid();
do
{
if (Objects[curIndex].ObjectPointer == object)
{
*metaObjectId = curIndex;
isInList = ((Objects[curIndex].Uid == uid) &&
(Objects[curIndex].Type == objectType));
break;
}
curIndex = Objects[curIndex].Prev;
} while (curIndex != GDIP_LIST_NIL);
}
return isInList;
}
#if 0 // not used
// We don't actually remove it from the list, we just put it at the
// front of the LRU so its spot gets used next. So the count stays
// the same.
VOID
MetafileRecordObjectList::RemoveAt(
UINT32 metaObjectId
)
{
ASSERT(metaObjectId < GDIP_MAX_OBJECTS);
MetafileRecordObject * removeObject = Objects + metaObjectId;
ASSERT(Count > 0);
removeObject->ObjectPointer = NULL;
removeObject->Uid = 0;
removeObject->Type = EmfPlusRecordTypeInvalid;
INT removeNext = removeObject->Next;
INT removePrev = removeObject->Prev;
if (removeNext != GDIP_LIST_NIL)
{
Objects[removeNext].Prev = removePrev;
}
else
{
ASSERT(MRU == metaObjectId);
if (removePrev != GDIP_LIST_NIL)
{
MRU = removePrev;
}
}
if (removePrev != GDIP_LIST_NIL)
{
ASSERT(LRU != metaObjectId);
Objects[removePrev].Next = removeNext;
removeObject->Prev = GDIP_LIST_NIL;
removeObject->Next = LRU;
Objects[LRU].Prev = metaObjectId;
LRU = metaObjectId;
}
else
{
ASSERT(LRU == metaObjectId);
}
}
#endif
// Make the specified object the MRU object.
VOID
MetafileRecordObjectList::UpdateMRU(
UINT32 metaObjectId
)
{
if (MRU != metaObjectId)
{
// Now we know there are at least 2 objects
MetafileRecordObject * object = &Objects[metaObjectId];
if (LRU != metaObjectId)
{
Objects[object->Prev].Next = object->Next;
}
else
{
LRU = object->Next;
}
Objects[object->Next].Prev = object->Prev;
object->Prev = MRU;
object->Next = GDIP_LIST_NIL;
Objects[MRU].Next = metaObjectId;
MRU = metaObjectId;
}
}
// if metaObjectId is GDIP_LIST_NIL, use the next available slot
VOID
MetafileRecordObjectList::InsertAt(
const GpObject * object,
UINT32 * metaObjectId
)
{
MetafileRecordObject * newObject;
UINT newIndex = *metaObjectId;
if (newIndex == GDIP_LIST_NIL)
{
if (Count != 0)
{
// use freed object before adding new one
if ((Objects[LRU].ObjectPointer == NULL) ||
(Count == GDIP_MAX_OBJECTS))
{
newIndex = LRU;
UseLRU:
LRU = Objects[newIndex].Next;
Objects[LRU].Prev = GDIP_LIST_NIL;
}
else
{
newIndex = Count++;
}
InsertObject:
Objects[MRU].Next = newIndex;
SetupObject:
*metaObjectId = newIndex;
newObject = &Objects[newIndex];
newObject->Next = GDIP_LIST_NIL;
newObject->Prev = MRU;
MRU = newIndex;
UseMRU:
newObject->ObjectPointer = object;
newObject->Uid = object->GetUid();
newObject->Type = object->GetObjectType();
return;
}
// else first object
newIndex = 0;
LRU = 0;
Count = 1;
goto SetupObject;
}
else // we already know where to put the object
{
ASSERT(Count > 0);
ASSERT(newIndex < GDIP_MAX_OBJECTS);
if (newIndex == MRU)
{
// This covers the case where there is only 1 object
newObject = &Objects[newIndex];
goto UseMRU;
}
// else there must be at least 2 objects
ASSERT(Count > 1);
if (newIndex == LRU)
{
goto UseLRU;
}
// Move middle object to MRU
newObject = &Objects[newIndex];
Objects[newObject->Prev].Next = newObject->Next;
Objects[newObject->Next].Prev = newObject->Prev;
goto InsertObject;
}
}
#define GDIP_MAX_COMMENT_SIZE 65020 // must be <= 65520 for Win9x bug
class EmfPlusCommentStream : public IUnknownBase<IStream>
{
private:
ObjectTag Tag; // Keep this as the 1st value in the object!
protected:
VOID SetValid(BOOL valid)
{
Tag = valid ? ObjectTagEmfPlusCommentStream : ObjectTagInvalid;
}
public:
EmfPlusCommentStream(HDC hdc)
{
ASSERT(hdc != NULL);
MetafileHdc = hdc;
Position = 0; // starts after signature
((INT32 *)CommentBuffer)[0] = EMFPLUS_SIGNATURE;
RecordDataStart = CommentBuffer + sizeof(INT32);
ContinuingObjectRecord = FALSE;
SetValid(TRUE);
}
~EmfPlusCommentStream()
{
this->Flush();
}
BOOL IsValid() const
{
ASSERT((Tag == ObjectTagEmfPlusCommentStream) || (Tag == ObjectTagInvalid));
return (Tag == ObjectTagEmfPlusCommentStream);
}
ULONG SpaceLeft() const
{
return (GDIP_MAX_COMMENT_SIZE - Position);
}
VOID
EndObjectRecord()
{
ASSERT ((Position & 0x03) == 0); // records should be 4-byte aligned
if (ContinuingObjectRecord)
{
ContinuingObjectRecord = FALSE;
if (Position > sizeof(EmfPlusContinueObjectRecord))
{
// Fix up the size of the last chunck of this object record
EmfPlusContinueObjectRecord * recordData;
recordData = (EmfPlusContinueObjectRecord *)RecordDataStart;
recordData->Size = Position;
recordData->DataSize = Position - sizeof(EmfPlusRecord);
}
else
{
// The object record ended exacly at the end of the buffer
// and has already been flushed.
Position = 0;
}
}
}
VOID
WriteRecordHeader(
UINT32 dataSize, // size of data (w/o record header)
EmfPlusRecordType type,
INT flags // 16 bits of flags
);
VOID Flush();
HRESULT STDMETHODCALLTYPE Write(
VOID const HUGEP *pv,
ULONG cb,
ULONG *pcbWritten)
{
if (cb == 0)
{
if (pcbWritten != NULL)
{
*pcbWritten = cb;
}
return S_OK;
}
ASSERT (pv != NULL);
if (IsValid())
{
// We've already written the record header; now we're writing
// the record data.
ASSERT(Position >= sizeof(EmfPlusRecord));
ULONG spaceLeft = SpaceLeft();
BYTE * recordData = RecordDataStart + Position;
// We flush as soon as we reach the end. We don't wait for
// the next write call to flush.
ASSERT(spaceLeft > 0);
if (pcbWritten)
{
*pcbWritten = cb;
}
// see if there is enough room for the data
if (cb <= spaceLeft)
{
GpMemcpy(recordData, pv, cb);
Position += cb;
if (Position < GDIP_MAX_COMMENT_SIZE)
{
return S_OK;
}
this->Flush();
if (IsValid())
{
return S_OK;
}
if (pcbWritten)
{
*pcbWritten = 0;
}
return E_FAIL;
}
ASSERT(ContinuingObjectRecord);
LoopStart:
GpMemcpy(recordData, pv, spaceLeft);
Position += spaceLeft;
if (Position == GDIP_MAX_COMMENT_SIZE)
{
this->Flush();
if (!IsValid())
{
if (pcbWritten)
{
*pcbWritten = 0; // not accurate, but who cares!
}
return E_FAIL;
}
}
cb -= spaceLeft;
if (cb == 0)
{
return S_OK;
}
pv = ((BYTE *)pv) + spaceLeft;
recordData = RecordDataStart + sizeof(EmfPlusContinueObjectRecord);
spaceLeft = GDIP_MAX_COMMENT_SIZE-sizeof(EmfPlusContinueObjectRecord);
if (spaceLeft > cb)
{
spaceLeft = cb;
}
goto LoopStart;
}
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE Read(
VOID HUGEP *pv,
ULONG cb,
ULONG *pcbRead)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER *plibNewPosition)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE SetSize(
ULARGE_INTEGER libNewSize)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CopyTo(
IStream *pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER *pcbRead,
ULARGE_INTEGER *pcbWritten)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE Commit(
DWORD grfCommitFlags)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE Revert(VOID)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE LockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE UnlockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE Stat(
STATSTG *pstatstg,
DWORD grfStatFlag)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE Clone(
IStream **ppstm)
{
return E_NOTIMPL;
}
private:
BYTE CommentBuffer[GDIP_MAX_COMMENT_SIZE + sizeof(INT32)];
BYTE * RecordDataStart;
ULONG Position;
HDC MetafileHdc;
BOOL ContinuingObjectRecord;
};
VOID
EmfPlusCommentStream::Flush()
{
ASSERT ((Position & 0x03) == 0); // records should be 4-byte aligned
if (IsValid() && (Position >= sizeof(EmfPlusRecord)))
{
// write the signature as well as the records
SetValid(GdiComment(MetafileHdc, (INT)Position + sizeof(INT32),
CommentBuffer) != 0);
#if DBG
if (!IsValid())
{
WARNING(("Failed to write GdiComment"));
}
#endif
if (!ContinuingObjectRecord)
{
Position = 0;
}
else
{
ASSERT(Position == GDIP_MAX_COMMENT_SIZE);
// Leave the object record header intact for the rest of the
// object data.
Position = sizeof(EmfPlusContinueObjectRecord);
}
}
}
VOID
EmfPlusCommentStream::WriteRecordHeader(
UINT32 dataSize, // size of data (w/o record header)
EmfPlusRecordType type,
INT flags // 16 bits of flags
)
{
ASSERT ((flags & 0xFFFF0000) == 0);
ASSERT (ContinuingObjectRecord == FALSE);
ASSERT ((Position & 0x03) == 0); // records should be 4-byte aligned
ASSERT ((dataSize & 0x03) == 0); // records should be 4-byte aligned
if (IsValid())
{
ULONG spaceLeft = SpaceLeft();
ULONG recordSize = sizeof(EmfPlusRecord) + dataSize;
ASSERT(spaceLeft > 0);
// see if the record fits in the space left
if (recordSize <= spaceLeft)
{
RecordFits:
EmfPlusRecord * recordData;
recordData = (EmfPlusRecord *)(RecordDataStart + Position);
recordData->Type = type;
recordData->Flags = (INT16)flags;
recordData->Size = recordSize;
recordData->DataSize = dataSize;
Position += sizeof(EmfPlusRecord);
if (Position < GDIP_MAX_COMMENT_SIZE)
{
return;
}
ASSERT((recordSize == sizeof(EmfPlusRecord)) && (dataSize == 0));
this->Flush();
return;
}
else // it doesn't fit in the space left
{
// maybe it will fit after flushing the current record buffer
if (spaceLeft < GDIP_MAX_COMMENT_SIZE)
{
this->Flush();
if (!IsValid())
{
return;
}
if (recordSize <= GDIP_MAX_COMMENT_SIZE)
{
goto RecordFits;
}
}
// Now we know the record does not fit in a single comment.
// This better be an object record!
ASSERT(type == EmfPlusRecordTypeObject);
flags |= GDIP_EPRFLAGS_CONTINUEOBJECT;
ContinuingObjectRecord = TRUE;
// We know that Position is 0
EmfPlusContinueObjectRecord * recordData;
recordData = (EmfPlusContinueObjectRecord *)RecordDataStart;
recordData->Type = type;
recordData->Flags = (INT16)flags;
recordData->Size = GDIP_MAX_COMMENT_SIZE;
recordData->DataSize = GDIP_MAX_COMMENT_SIZE - sizeof(EmfPlusRecord);
recordData->TotalObjectSize = dataSize; // size of object data (w/o header size)
Position = sizeof(EmfPlusContinueObjectRecord);
}
}
}
class MetafileRecorder : public IMetafileRecord
{
friend class GpMetafile;
private:
ObjectTag Tag; // Keep this as the 1st value in the object!
protected:
VOID SetValid(BOOL valid)
{
Tag = valid ? ObjectTagMetafileRecorder : ObjectTagInvalid;
}
public:
BOOL WroteFrameRect;
SIZEL Millimeters;
protected:
EmfPlusCommentStream * EmfPlusStream; // memory buffer stream
GpMetafile * Metafile; // being recorded
EmfType Type;
REAL XMinDevice; // device bounds
REAL YMinDevice;
REAL XMaxDevice;
REAL YMaxDevice;
BOOL BoundsInit;
INT NumRecords; // for debugging only
INT MaxStackSize;
HDC MetafileHdc;
DynArrayIA<INT,GDIP_SAVE_STACK_SIZE> SaveRestoreStack;
MetafileRecordObjectList ObjectList;
GpRectF MetafileBounds;
public:
MetafileRecorder(
GpMetafile * metafile,
EmfType type,
HDC metafileHdc,
BOOL wroteFrameRect,
SIZEL & effectiveMillimeters,
GpRectF & metafileBounds
);
~MetafileRecorder() // called by EndRecording
{
// Release the memory stream for writing the GdiComments
if (EmfPlusStream != NULL)
{
EmfPlusStream->Release();
}
}
BOOL IsValid() const
{
ASSERT((Tag == ObjectTagMetafileRecorder) || (Tag == ObjectTagInvalid));
return (Tag == ObjectTagMetafileRecorder);
}
virtual VOID GetMetafileBounds(GpRect & metafileBounds) const
{
// Use Floor to make sure we don't miss any pixels
metafileBounds.X = GpFloor(MetafileBounds.X);
metafileBounds.Y = GpFloor(MetafileBounds.Y);
metafileBounds.Width = GpCeiling(MetafileBounds.GetRight()) - metafileBounds.X;
metafileBounds.Height = GpCeiling(MetafileBounds.GetBottom()) - metafileBounds.Y;
}
virtual GpStatus
RecordClear(
const GpRectF * deviceBounds,
GpColor color
);
virtual GpStatus
RecordFillRects(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpRectF * rects,
INT count
);
virtual GpStatus
RecordDrawRects(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF * rects,
INT count
);
virtual GpStatus
RecordFillPolygon(
const GpRectF * deviceBounds,
GpBrush* brush,
const GpPointF * points,
INT count,
GpFillMode fillMode
);
virtual GpStatus
RecordDrawLines(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count,
BOOL closed
);
virtual GpStatus
RecordFillEllipse(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpRectF & rect
);
virtual GpStatus
RecordDrawEllipse(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF & rect
);
virtual GpStatus
RecordFillPie(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpRectF & rect,
REAL startAngle,
REAL sweepAngle
);
virtual GpStatus
RecordDrawPie(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF & rect,
REAL startAngle,
REAL sweepAngle
);
virtual GpStatus
RecordDrawArc(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF & rect,
REAL startAngle,
REAL sweepAngle
);
virtual GpStatus
RecordFillRegion(
const GpRectF * deviceBounds,
GpBrush * brush,
GpRegion * region
);
virtual GpStatus
RecordFillPath(
const GpRectF * deviceBounds,
const GpBrush * brush,
GpPath * path
);
virtual GpStatus
RecordDrawPath(
const GpRectF * deviceBounds,
GpPen * pen,
GpPath * path
);
virtual GpStatus
RecordFillClosedCurve(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpPointF * points,
INT count,
REAL tension,
GpFillMode fillMode
);
virtual GpStatus
RecordDrawClosedCurve(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count,
REAL tension
);
virtual GpStatus
RecordDrawCurve(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count,
REAL tension,
INT offset,
INT numberOfSegments
);
virtual GpStatus
RecordDrawBeziers(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count
);
virtual GpStatus
RecordDrawImage(
const GpRectF * deviceBounds,
const GpImage * image,
const GpRectF & destRect,
const GpRectF & srcRect,
GpPageUnit srcUnit,
const GpImageAttributes * imageAttributes
);
virtual GpStatus
RecordDrawImage(
const GpRectF * deviceBounds,
const GpImage * image,
const GpPointF * destPoints,
INT count,
const GpRectF & srcRect,
GpPageUnit srcUnit,
const GpImageAttributes * imageAttributes
);
virtual GpStatus
RecordDrawString(
const GpRectF * deviceBounds,
const WCHAR *string,
INT length,
const GpFont *font,
const RectF *layoutRect,
const GpStringFormat *format,
const GpBrush *brush
);
virtual GpStatus
RecordDrawDriverString(
const GpRectF * deviceBounds,
const UINT16 *text,
INT glyphCount,
const GpFont *font,
const GpBrush *brush,
const PointF *positions,
INT flags,
const GpMatrix *matrix
);
virtual GpStatus
RecordSave(
INT gstate
);
virtual GpStatus
RecordRestore(
INT gstate
);
virtual GpStatus
RecordBeginContainer(
const GpRectF & destRect,
const GpRectF & srcRect,
GpPageUnit srcUnit,
INT containerState
);
virtual GpStatus
RecordBeginContainer(
INT containerState
);
virtual GpStatus
RecordEndContainer(
INT containerState
);
virtual GpStatus
RecordSetWorldTransform(
const GpMatrix & matrix
);
virtual GpStatus
RecordResetWorldTransform();
virtual GpStatus
RecordMultiplyWorldTransform(
const GpMatrix & matrix,
GpMatrixOrder order
);
virtual GpStatus
RecordTranslateWorldTransform(
REAL dx,
REAL dy,
GpMatrixOrder order
);
virtual GpStatus
RecordScaleWorldTransform(
REAL sx,
REAL sy,
GpMatrixOrder order
);
virtual GpStatus
RecordRotateWorldTransform(
REAL angle,
GpMatrixOrder order
);
virtual GpStatus
RecordSetPageTransform(
GpPageUnit unit,
REAL scale
);
virtual GpStatus
RecordResetClip();
virtual GpStatus
RecordSetClip(
const GpRectF & rect,
CombineMode combineMode
);
virtual GpStatus
RecordSetClip(
GpRegion * region,
CombineMode combineMode
);
virtual GpStatus
RecordSetClip(
GpPath * path,
CombineMode combineMode,
BOOL isDevicePath
);
virtual GpStatus
RecordOffsetClip(
REAL dx,
REAL dy
);
virtual GpStatus
RecordGetDC();
virtual GpStatus
RecordSetAntiAliasMode(
BOOL newMode
);
virtual GpStatus
RecordSetTextRenderingHint(
TextRenderingHint newMode
);
virtual GpStatus
RecordSetTextContrast(
UINT gammaValue
);
virtual GpStatus
RecordSetInterpolationMode(
InterpolationMode newMode
);
virtual GpStatus
RecordSetPixelOffsetMode(
PixelOffsetMode newMode
);
virtual GpStatus
RecordSetCompositingMode(
GpCompositingMode newMode
);
virtual GpStatus
RecordSetCompositingQuality(
GpCompositingQuality newQuality
);
virtual GpStatus
RecordSetRenderingOrigin(
INT x,
INT y
);
virtual GpStatus
RecordComment(
UINT sizeData,
const BYTE * data
);
virtual VOID
EndRecording();
virtual GpStatus
RecordBackupObject(
const GpObject * object
);
protected:
GpStatus
RecordHeader(
INT logicalDpiX,
INT logicalDpiY,
INT emfPlusFlags
);
VOID RecordEndOfFile();
VOID
WriteObject(
ObjectType type,
const GpObject * object,
UINT32 metaObjectId
);
VOID
RecordObject(
const GpObject * object,
UINT32* metaObjectId
);
GpStatus
RecordZeroDataRecord(
EmfPlusRecordType type,
INT flags
);
VOID
WriteRecordHeader(
UINT32 dataSize,
EmfPlusRecordType type,
INT flags = 0, // 16 bits of flags
const GpRectF * deviceBounds = NULL
);
// To keep the number of comments low, this only needs to be called
// when there is a down-level representation of the GDI+ record.
VOID
WriteGdiComment()
{
// If we're doing dual (which means we're about to write
// down-level records) then write out the current list
// of records in the EmfPlusStream buffer.
if (Type == EmfTypeEmfPlusDual)
{
EmfPlusStream->Flush();
}
}
VOID
GetBrushValueForRecording(
const GpBrush *brush,
UINT32 &brushValue,
INT &flags
);
};
/**************************************************************************\
*
* Function Description:
*
* Construct a MetafileRecorder object and initialize it.
*
* Arguments:
*
* [IN] metafile - pointer to the metafile object being recorded
* [IN] stream - the stream being recorded into (if any)
* [IN] metafileHdc - handle to metafile DC being recorded into (if any)
* [IN] dpiX - the horizontal DPI
* [IN] dpiY - the vertical DPI
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
MetafileRecorder::MetafileRecorder(
GpMetafile * metafile,
EmfType emfType,
HDC metafileHdc,
BOOL wroteFrameRect,
SIZEL & effectiveMillimeters,
GpRectF & metafileBounds
)
{
SetValid(FALSE);
Type = emfType;
Metafile = metafile;
WroteFrameRect = wroteFrameRect;
NumRecords = 0; // currently for debugging only
MaxStackSize = 0;
MetafileHdc = metafileHdc;
XMinDevice = FLT_MAX;
YMinDevice = FLT_MAX;
XMaxDevice = -FLT_MAX;
YMaxDevice = -FLT_MAX;
BoundsInit = FALSE;
EmfPlusStream = NULL;
Millimeters = effectiveMillimeters;
// The metafileBounds are used as the bounds for FillRegion
// calls when the region has infinite bounds, to keep from
// exploding the bounds of the metafile.
MetafileBounds = metafileBounds;
if (emfType == EmfTypeEmfOnly)
{
Metafile->Header.Type = MetafileTypeEmf;
SetValid(TRUE);
}
else
{
// gets freed in the destructor
EmfPlusStream = new EmfPlusCommentStream(metafileHdc);
if (EmfPlusStream == NULL)
{
return;
}
SetValid(TRUE);
INT logicalDpiX = GetDeviceCaps(metafileHdc, LOGPIXELSX);
INT logicalDpiY = GetDeviceCaps(metafileHdc, LOGPIXELSY);
INT emfPlusFlags = 0;
if (GetDeviceCaps(metafileHdc, TECHNOLOGY) == DT_RASDISPLAY)
{
emfPlusFlags |= GDIP_EMFPLUSFLAGS_DISPLAY;
}
MetafileHeader * header = &(metafile->Header);
header->EmfPlusHeaderSize = sizeof(EmfPlusRecord) + sizeof(EmfPlusHeaderRecord);
header->LogicalDpiX = logicalDpiX;
header->LogicalDpiY = logicalDpiY;
header->EmfPlusFlags = emfPlusFlags;
if (emfType == EmfTypeEmfPlusOnly)
{
header->Type = MetafileTypeEmfPlusOnly;
}
else
{
ASSERT(emfType == EmfTypeEmfPlusDual);
header->Type = MetafileTypeEmfPlusDual;
}
if (RecordHeader(logicalDpiX, logicalDpiY, emfPlusFlags) != Ok)
{
SetValid(FALSE);
EmfPlusStream->Release();
EmfPlusStream = NULL;
}
}
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordClear.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] color - the clear color
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 04/28/2000 AGodfrey
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordClear(
const GpRectF * deviceBounds,
GpColor color
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
ASSERT (deviceBounds != NULL);
ARGB argbColor = color.GetValue();
UINT32 dataSize = sizeof(argbColor);
EmfPlusRecordType type = EmfPlusRecordTypeClear;
INT flags = 0;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, argbColor);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write Clear record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordFillRects.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] brush - brush to draw with
* [IN] rects - rectangles to fill
* [IN] count - number of rects
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordFillRects(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpRectF * rects,
INT count
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (brush != NULL) &&
(rects != NULL) && (count > 0));
if (IsValid())
{
MetafileRectData rectData(rects, count);
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue) +
sizeof(UINT32/* count */) +
rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeFillRects;
INT flags = rectData.GetFlags();
GetBrushValueForRecording(brush, brushValue, flags);
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteInt32(EmfPlusStream, count);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write FillRects record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawRects.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] rects - rectangles to draw
* [IN] count - number of rects
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawRects(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF * rects,
INT count
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL) &&
(rects != NULL) && (count > 0));
if (IsValid())
{
MetafileRectData rectData(rects, count);
UINT32 metaPenId;
UINT32 dataSize = sizeof(UINT32/* count */) +
rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawRects;
INT flags = rectData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, count);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawRects record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordFillPolygon.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] brush - brush to draw with
* [IN] points - polygon points
* [IN] count - number of points
* [IN] fillMode - Alternate or Winding
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordFillPolygon(
const GpRectF * deviceBounds,
GpBrush* brush,
const GpPointF * points,
INT count,
GpFillMode fillMode
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (brush != NULL) &&
(points != NULL) && (count > 0));
if (IsValid())
{
MetafilePointData pointData(points, count);
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue) +
sizeof(UINT32/* count */) +
pointData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeFillPolygon;
INT flags = pointData.GetFlags();
GetBrushValueForRecording(brush, brushValue, flags);
if (fillMode == FillModeWinding)
{
flags |= GDIP_EPRFLAGS_WINDINGFILL;
}
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteInt32(EmfPlusStream, count);
pointData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write FillPolygon record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawLines.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] points - polyline points
* [IN] count - number of points
* [IN] closed - TRUE if closed
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawLines(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count,
BOOL closed
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL) &&
(points != NULL) && (count > 0));
if (IsValid())
{
MetafilePointData pointData(points, count);
UINT32 metaPenId;
UINT32 dataSize = sizeof(UINT32/* count */) +
pointData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawLines;
INT flags = pointData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
if (closed)
{
flags |= GDIP_EPRFLAGS_CLOSED;
}
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, count);
pointData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawLines record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordFillEllipse.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] brush - brush to draw with
* [IN] rect - bounding rect of ellipse
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordFillEllipse(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpRectF & rect
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (brush != NULL));
if (IsValid())
{
MetafileRectData rectData(&rect, 1);
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue) +
rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeFillEllipse;
INT flags = rectData.GetFlags();
GetBrushValueForRecording(brush, brushValue, flags);
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write FillEllipse record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawEllipse.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] rect - bounding rect of ellipse
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawEllipse(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF & rect
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL));
if (IsValid())
{
MetafileRectData rectData(&rect, 1);
UINT32 metaPenId;
UINT32 dataSize = rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawEllipse;
INT flags = rectData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawEllipse record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordFillPie.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] brush - brush to draw with
* [IN] rect - bounding rect of ellipse
* [IN] startAngle - starting angle of pie
* [IN] sweepAngle - sweep angle of pie
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordFillPie(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpRectF & rect,
REAL startAngle,
REAL sweepAngle
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (brush != NULL));
if (IsValid())
{
MetafileRectData rectData(&rect, 1);
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue) +
sizeof(startAngle) +
sizeof(sweepAngle) +
rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeFillPie;
INT flags = rectData.GetFlags();
GetBrushValueForRecording(brush, brushValue, flags);
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteReal (EmfPlusStream, startAngle);
WriteReal (EmfPlusStream, sweepAngle);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write FillPie record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawPie.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] rect - bounding rect of ellipse
* [IN] startAngle - starting angle of pie
* [IN] sweepAngle - sweep angle of pie
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawPie(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF & rect,
REAL startAngle,
REAL sweepAngle
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL));
if (IsValid())
{
MetafileRectData rectData(&rect, 1);
UINT32 metaPenId;
UINT32 dataSize = sizeof(startAngle) +
sizeof(sweepAngle) +
rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawPie;
INT flags = rectData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteReal (EmfPlusStream, startAngle);
WriteReal (EmfPlusStream, sweepAngle);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawPie record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawArc.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] rect - bounding rect of ellipse
* [IN] startAngle - starting angle of arc
* [IN] sweepAngle - sweep angle of arc
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawArc(
const GpRectF * deviceBounds,
GpPen * pen,
const GpRectF & rect,
REAL startAngle,
REAL sweepAngle
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL));
if (IsValid())
{
MetafileRectData rectData(&rect, 1);
UINT32 metaPenId;
UINT32 dataSize = sizeof(startAngle) +
sizeof(sweepAngle) +
rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawArc;
INT flags = rectData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteReal (EmfPlusStream, startAngle);
WriteReal (EmfPlusStream, sweepAngle);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawArc record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordFillRegion.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] brush - brush to draw with
* [IN] region - region to fill
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordFillRegion(
const GpRectF * deviceBounds,
GpBrush * brush,
GpRegion * region
)
{
// The deviceBounds should never be infinite, because they are
// intersected with the metafileBounds before being passed in.
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (brush != NULL) && (region != NULL));
if (IsValid())
{
UINT32 metaRegionId;
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue);
EmfPlusRecordType type = EmfPlusRecordTypeFillRegion;
INT flags = 0;
GetBrushValueForRecording(brush, brushValue, flags);
RecordObject(region, &metaRegionId);
ASSERT((metaRegionId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaRegionId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write FillRegion record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordFillPath.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] brush - brush to draw with
* [IN] path - path to fill
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordFillPath(
const GpRectF * deviceBounds,
const GpBrush * brush,
GpPath * path
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (brush != NULL) && (path != NULL));
if (IsValid())
{
UINT32 metaPathId;
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue);
EmfPlusRecordType type = EmfPlusRecordTypeFillPath;
INT flags = 0;
GetBrushValueForRecording(brush, brushValue, flags);
RecordObject(path, &metaPathId);
ASSERT((metaPathId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPathId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write FillPath record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawPath.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] path - path to draw
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawPath(
const GpRectF * deviceBounds,
GpPen * pen,
GpPath * path
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL) && (path != NULL));
if (IsValid())
{
UINT32 metaPathId;
UINT32 metaPenId;
UINT32 dataSize = sizeof(metaPenId);
EmfPlusRecordType type = EmfPlusRecordTypeDrawPath;
INT flags = 0;
RecordObject(pen, &metaPenId);
RecordObject(path, &metaPathId);
ASSERT((metaPathId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPathId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, metaPenId);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawPath record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordFillClosedCurve.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] brush - brush to draw with
* [IN] points - curve points
* [IN] count - number of points
* [IN] tension - how tight to make curve
* [IN] fillMode - Alternate or Winding
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordFillClosedCurve(
const GpRectF * deviceBounds,
GpBrush * brush,
const GpPointF * points,
INT count,
REAL tension,
GpFillMode fillMode
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (brush != NULL) &&
(points != NULL) && (count > 0));
if (IsValid())
{
MetafilePointData pointData(points, count);
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue) +
sizeof(tension) +
sizeof(UINT32 /* count */) +
pointData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeFillClosedCurve;
INT flags = pointData.GetFlags();
GetBrushValueForRecording(brush, brushValue, flags);
if (fillMode == FillModeWinding)
{
flags |= GDIP_EPRFLAGS_WINDINGFILL;
}
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteReal (EmfPlusStream, tension);
WriteInt32(EmfPlusStream, count);
pointData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write FillClosedCurve record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawClosedCurve.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] points - curve points
* [IN] count - number of points
* [IN] tension - how tight to make curve
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawClosedCurve(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count,
REAL tension
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL) &&
(points != NULL) && (count > 0));
if (IsValid())
{
MetafilePointData pointData(points, count);
UINT32 metaPenId;
UINT32 dataSize = sizeof(tension) +
sizeof(UINT32/* count */) +
pointData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawClosedCurve;
INT flags = pointData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteReal (EmfPlusStream, tension);
WriteInt32(EmfPlusStream, count);
pointData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawClosedCurve record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawCurve.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] points - curve points
* [IN] count - number of points
* [IN] tension - how tight to make curve
* [IN] offset - offset
* [IN] numberOfSegments - number of segments
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawCurve(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count,
REAL tension,
INT offset,
INT numberOfSegments
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL) &&
(points != NULL) && (count > 0));
if (IsValid())
{
MetafilePointData pointData(points, count);
UINT32 metaPenId;
UINT32 dataSize = sizeof(tension) +
sizeof(INT32 /* offset */) +
sizeof(UINT32/* numberOfSegments */) +
sizeof(UINT32/* count */) +
pointData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawCurve;
INT flags = pointData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteReal (EmfPlusStream, tension);
WriteInt32(EmfPlusStream, offset);
WriteInt32(EmfPlusStream, numberOfSegments);
WriteInt32(EmfPlusStream, count);
pointData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawCurve record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawBeziers.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] pen - pen to draw with
* [IN] points - curve points
* [IN] count - number of points
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawBeziers(
const GpRectF * deviceBounds,
GpPen * pen,
const GpPointF * points,
INT count
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (pen != NULL) &&
(points != NULL) && (count > 0));
if (IsValid())
{
MetafilePointData pointData(points, count);
UINT32 metaPenId;
UINT32 dataSize = sizeof(UINT32/* count */) +
pointData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawBeziers;
INT flags = pointData.GetFlags();
RecordObject(pen, &metaPenId);
ASSERT((metaPenId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPenId;
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, count);
pointData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawBeziers record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawImage.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] image - image to draw
* [IN] destRect - where to draw image
* [IN] srcRect - portion of image to draw
* [IN] srcUnit - units of srcRect
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawImage(
const GpRectF * deviceBounds,
const GpImage * image,
const GpRectF & destRect,
const GpRectF & srcRect,
GpPageUnit srcUnit,
const GpImageAttributes * imageAttributes
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (image != NULL));
if (IsValid())
{
MetafileRectData rectData(&destRect, 1);
UINT32 metaImageId;
UINT32 metaImageAttributesId;
UINT32 dataSize = sizeof(INT32) + /* metaImageAttributesId*/
sizeof(INT32) + /* srcUnit */
sizeof(srcRect) +
rectData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawImage;
INT flags = rectData.GetFlags();
RecordObject(image, &metaImageId);
ASSERT((metaImageId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaImageId;
// Record the imageAttributes;
// imageAttributes can be NULL
RecordObject(imageAttributes, &metaImageAttributesId);
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, metaImageAttributesId);
WriteInt32(EmfPlusStream, srcUnit);
WriteRect (EmfPlusStream, srcRect);
rectData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawImage record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawImage.
*
* Arguments:
*
* [IN] deviceBounds - the bounding rect, in device units
* [IN] image - image to draw
* [IN] destPoints - where to draw image
* [IN] count - number of destPoints
* [IN] srcRect - portion of image to draw
* [IN] srcUnit - units of srcRect
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawImage(
const GpRectF * deviceBounds,
const GpImage * image,
const GpPointF * destPoints,
INT count,
const GpRectF & srcRect,
GpPageUnit srcUnit,
const GpImageAttributes * imageAttributes
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT ((deviceBounds != NULL) && (image != NULL) &&
(destPoints != NULL) && (count > 0));
if (IsValid())
{
MetafilePointData pointData(destPoints, count);
UINT32 metaImageId;
UINT32 metaImageAttributesId;
UINT32 dataSize = sizeof(INT32) + /* metaImageAttributesId*/
sizeof(INT32) + /* srcUnit */
sizeof(srcRect) +
sizeof(UINT32) + /* count */
pointData.GetDataSize();
EmfPlusRecordType type = EmfPlusRecordTypeDrawImagePoints;
INT flags = pointData.GetFlags();
RecordObject(image, &metaImageId);
ASSERT((metaImageId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaImageId;
// Record the imageAttributes;
// imageAttributes can be NULL
RecordObject(imageAttributes, &metaImageAttributesId);
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, metaImageAttributesId);
WriteInt32(EmfPlusStream, srcUnit);
WriteRect (EmfPlusStream, srcRect);
WriteInt32(EmfPlusStream, count);
pointData.WriteData(EmfPlusStream);
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawImagePoints record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawString.
*
* Arguments:
*
* [IN] string - string to draw
* [IN] length - length of string
* [IN] font - font to use when drawing string
* [IN] layoutRect - where to draw the string
* [IN] format - format
* [IN] brush - brush to draw with
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawString(
const GpRectF * deviceBounds,
const WCHAR *string,
INT length,
const GpFont *font,
const RectF *layoutRect,
const GpStringFormat *format,
const GpBrush *brush
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT (string && font && brush && layoutRect);
if (length < 0)
{
if (length == -1)
{
length = 0;
while (string[length] && (length < INT_MAX))
{
length++;
}
}
else
{
return InvalidParameter;
}
}
ASSERT (length > 0);
if (IsValid())
{
const BYTE * strData = (BYTE *)string; // BYTE or WCHAR
INT sizeString = length * sizeof(WCHAR);
INT flags = 0;
// !!! TODO:
// Compress the Unicode string.
// Use the GDIP_EPRFLAGS_COMPRESSED to indicate that
// the string has been compressed to ANSI.
UINT32 metaFontId;
UINT32 metaFormatId;
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 dataSize = sizeof(brushValue) +
sizeof(metaFormatId) +
sizeof(INT32 /* len */) +
sizeof(*layoutRect) +
sizeString;
EmfPlusRecordType type = EmfPlusRecordTypeDrawString;
dataSize = (dataSize + 3) & (~3); // align
RecordObject(font, &metaFontId);
ASSERT((metaFontId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaFontId;
// the format can be NULL
RecordObject(format, &metaFormatId);
GetBrushValueForRecording(brush, brushValue, flags);
WriteRecordHeader(dataSize, type, flags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteInt32(EmfPlusStream, metaFormatId);
WriteInt32(EmfPlusStream, length);
WriteRect (EmfPlusStream, *layoutRect);
WriteBytes(EmfPlusStream, strData, sizeString);
// align
if ((length & 0x01) != 0)
{
length = 0;
EmfPlusStream->Write(&length, sizeof(WCHAR), NULL);
}
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawString record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordDrawdriverString.
*
* Arguments:
*
* [IN] text - string/glyphs
* [IN] glyphCount - string length
* [IN] font - font to use when drawing string
* [IN] brush - brush to draw with
* [IN] positions - character/glyphs origins
* [IN] flags - API flags
* [IN] matrix - transofrmation matrix
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 7/11/2000 Tarekms
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordDrawDriverString(
const GpRectF *deviceBounds,
const UINT16 *text,
INT glyphCount,
const GpFont *font,
const GpBrush *brush,
const PointF *positions,
INT flags,
const GpMatrix *matrix
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
ASSERT (text && font && brush && positions);
if (glyphCount <= 0)
{
return InvalidParameter;
}
if (IsValid())
{
const BYTE * textData = (BYTE *)text;
const BYTE * positionData = (BYTE *)positions;
INT sizeText = glyphCount * sizeof(UINT16);
INT sizePositions = glyphCount * sizeof(PointF);
INT metaFlags = 0;
UINT32 metaFontId;
UINT32 brushValue; // Metafile Brush Id or ARGB value
UINT32 matrixPresent;
UINT32 dataSize = sizeof(brushValue) + // brush value
sizeof(flags) + // API flags
sizeof(matrixPresent)+ // matix prensences
sizeof(UINT32) + // glyphCoumt
sizeText + // Text
sizePositions; // Positions
if (matrix == NULL)
{
matrixPresent = 0;
}
else
{
matrixPresent = 1;
dataSize += GDIP_MATRIX_SIZE;
}
EmfPlusRecordType type = EmfPlusRecordTypeDrawDriverString;
dataSize = (dataSize + 3) & (~3); // align
RecordObject(font, &metaFontId);
ASSERT((metaFontId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
metaFlags |= metaFontId;
GetBrushValueForRecording(brush, brushValue, metaFlags);
WriteRecordHeader(dataSize, type, metaFlags, deviceBounds);
WriteInt32(EmfPlusStream, brushValue);
WriteInt32(EmfPlusStream, flags);
WriteInt32(EmfPlusStream, matrixPresent);
WriteInt32(EmfPlusStream, glyphCount);
WriteBytes(EmfPlusStream, textData, sizeText);
WriteBytes(EmfPlusStream, positionData, sizePositions);
if (matrix != NULL)
{
WriteMatrix(EmfPlusStream, *matrix);
}
// align
if ((glyphCount & 0x01) != 0)
{
sizeText = 0;
EmfPlusStream->Write(&sizeText, sizeof(WCHAR), NULL);
}
WriteGdiComment(); // is down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write DrawDriverString record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSave.
*
* Arguments:
*
* [IN] gstate - the pushed state (restore to state before this)
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSave(
INT gstate
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(UINT32/* index */);
EmfPlusRecordType type = EmfPlusRecordTypeSave;
INT index = SaveRestoreStack.GetCount();
SaveRestoreStack.Add(gstate);
WriteRecordHeader(dataSize, type);
WriteInt32(EmfPlusStream, index);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write Save record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordRestore.
*
* Arguments:
*
* [IN] gstate - the pushed state (restore to state before this)
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordRestore(
INT gstate
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
INT count = SaveRestoreStack.GetCount();
INT * stack = SaveRestoreStack.GetDataBuffer();
if ((count > 0) && (stack != NULL))
{
UINT32 dataSize = sizeof(UINT32/* index */);
EmfPlusRecordType type = EmfPlusRecordTypeRestore;
do
{
if (stack[--count] == gstate)
{
SaveRestoreStack.SetCount(count);
WriteRecordHeader(dataSize, type);
WriteInt32(EmfPlusStream, count);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
break;
}
} while (count > 0);
}
}
WARNING(("Failed to write Restore record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordBeginContainer.
*
* Arguments:
*
* [IN] destRect - rect to draw container inside of
* [IN] srcRect - maps source size to destRect
* [IN] srcUnit - units of srcRect
* [IN] containerState - the pushed state (restore to state before this)
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordBeginContainer(
const GpRectF & destRect,
const GpRectF & srcRect,
GpPageUnit srcUnit,
INT containerState
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = GDIP_RECTF_SIZE /* destRect */ +
GDIP_RECTF_SIZE /* srcRect */ +
sizeof(UINT32/* index */);
EmfPlusRecordType type = EmfPlusRecordTypeBeginContainer;
INT index = SaveRestoreStack.GetCount();
INT flags = srcUnit;
ASSERT((flags & (~GDIP_EPRFLAGS_PAGEUNIT)) == 0);
if (index >= MaxStackSize)
{
MaxStackSize = index + 1;
}
SaveRestoreStack.Add(containerState);
WriteRecordHeader(dataSize, type, flags);
WriteRect(EmfPlusStream, destRect);
WriteRect(EmfPlusStream, srcRect);
WriteInt32(EmfPlusStream, index);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write BeginContainer record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordBeginContainer.
*
* Arguments:
*
* [IN] containerState - the pushed state (restore to state before this)
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordBeginContainer(
INT containerState
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(UINT32/* index */);
EmfPlusRecordType type = EmfPlusRecordTypeBeginContainerNoParams;
INT index = SaveRestoreStack.GetCount();
INT flags = 0;
if (index >= MaxStackSize)
{
MaxStackSize = index + 1;
}
SaveRestoreStack.Add(containerState);
WriteRecordHeader(dataSize, type, flags);
WriteInt32(EmfPlusStream, index);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write BeginContainer record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordEndContainer.
*
* Arguments:
*
* [IN] containerState - the pushed state (restore to state before this)
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordEndContainer(
INT containerState
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
INT count = SaveRestoreStack.GetCount();
INT * stack = SaveRestoreStack.GetDataBuffer();
if ((count > 0) && (stack != NULL))
{
UINT32 dataSize = sizeof(UINT32/* index */);
EmfPlusRecordType type = EmfPlusRecordTypeEndContainer;
do
{
if (stack[--count] == containerState)
{
SaveRestoreStack.SetCount(count);
WriteRecordHeader(dataSize, type);
WriteInt32(EmfPlusStream, count);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
break;
}
} while (count > 0);
}
}
WARNING(("Failed to write EndContainer record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetWorldTransform.
*
* Arguments:
*
* [IN] matrix - matrix to set in graphics
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetWorldTransform(
const GpMatrix & matrix
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = GDIP_MATRIX_SIZE;
EmfPlusRecordType type = EmfPlusRecordTypeSetWorldTransform;
WriteRecordHeader(dataSize, type);
WriteMatrix(EmfPlusStream, matrix);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write SetWorldTransform record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordResetWorldTransform.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordResetWorldTransform()
{
return RecordZeroDataRecord(EmfPlusRecordTypeResetWorldTransform, 0);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordMultiplyWorldTransform.
*
* Arguments:
*
* [IN] matrix - matrix to set in graphics
* [IN] order - Append or Prepend
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordMultiplyWorldTransform(
const GpMatrix & matrix,
GpMatrixOrder order
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = GDIP_MATRIX_SIZE;
EmfPlusRecordType type = EmfPlusRecordTypeMultiplyWorldTransform;
INT flags = 0;
if (order == MatrixOrderAppend)
{
flags |= GDIP_EPRFLAGS_APPEND;
}
WriteRecordHeader(dataSize, type, flags);
WriteMatrix(EmfPlusStream, matrix);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write MultiplyWorldTransform record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordTranslateWorldTransform.
*
* Arguments:
*
* [IN] dx - x translation
* [IN] dy - y translation
* [IN] order - Append or Prepend
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordTranslateWorldTransform(
REAL dx,
REAL dy,
GpMatrixOrder order
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(dx) + sizeof(dy);
EmfPlusRecordType type = EmfPlusRecordTypeTranslateWorldTransform;
INT flags = 0;
if (order == MatrixOrderAppend)
{
flags |= GDIP_EPRFLAGS_APPEND;
}
WriteRecordHeader(dataSize, type, flags);
WriteReal(EmfPlusStream, dx);
WriteReal(EmfPlusStream, dy);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write TranslateWorldTransform record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordScaleWorldTransform.
*
* Arguments:
*
* [IN] sx - x scale
* [IN] sy - y scale
* [IN] order - Append or Prepend
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordScaleWorldTransform(
REAL sx,
REAL sy,
GpMatrixOrder order
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(sx) + sizeof(sy);
EmfPlusRecordType type = EmfPlusRecordTypeScaleWorldTransform;
INT flags = 0;
if (order == MatrixOrderAppend)
{
flags |= GDIP_EPRFLAGS_APPEND;
}
WriteRecordHeader(dataSize, type, flags);
WriteReal(EmfPlusStream, sx);
WriteReal(EmfPlusStream, sy);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write ScaleWorldTransform record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordRotateWorldTransform.
*
* Arguments:
*
* [IN] angle - rotation angle
* [IN] order - Append or Prepend
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordRotateWorldTransform(
REAL angle,
GpMatrixOrder order
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(angle);
EmfPlusRecordType type = EmfPlusRecordTypeRotateWorldTransform;
INT flags = 0;
if (order == MatrixOrderAppend)
{
flags |= GDIP_EPRFLAGS_APPEND;
}
WriteRecordHeader(dataSize, type, flags);
WriteReal(EmfPlusStream, angle);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write RotateWorldTransform record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetPageTransform.
*
* Arguments:
*
* [IN] unit - units to use
* [IN] scale - scale factor to apply
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetPageTransform(
GpPageUnit unit,
REAL scale
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(scale);
EmfPlusRecordType type = EmfPlusRecordTypeSetPageTransform;
INT flags = unit;
ASSERT((flags & (~GDIP_EPRFLAGS_PAGEUNIT)) == 0);
WriteRecordHeader(dataSize, type, flags);
WriteReal(EmfPlusStream, scale);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write SetPageTransform record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordResetClip.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordResetClip()
{
return RecordZeroDataRecord(EmfPlusRecordTypeResetClip, 0);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetClip.
*
* Arguments:
*
* [IN] rect - set clipping to this rect
* [IN] combineMode - the combine operator (and, or, xor, exclude, complement)
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetClip(
const GpRectF & rect,
CombineMode combineMode
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = GDIP_RECTF_SIZE;
EmfPlusRecordType type = EmfPlusRecordTypeSetClipRect;
INT flags = (combineMode << 8);
ASSERT((flags & (~GDIP_EPRFLAGS_COMBINEMODE)) == 0);
WriteRecordHeader(dataSize, type, flags);
WriteRect(EmfPlusStream, rect);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write SetClipRect record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetClip.
*
* Arguments:
*
* [IN] region - set clipping to this region
* [IN] combineMode - the combine operator (and, or, xor, exclude, complement)
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetClip(
GpRegion * region,
CombineMode combineMode
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = 0;
EmfPlusRecordType type = EmfPlusRecordTypeSetClipRegion;
INT flags = (combineMode << 8);
UINT32 metaRegionId;
ASSERT((flags & (~GDIP_EPRFLAGS_COMBINEMODE)) == 0);
RecordObject(region, &metaRegionId);
ASSERT((metaRegionId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaRegionId;
WriteRecordHeader(dataSize, type, flags);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write SetClipRegion record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetClip.
*
* Arguments:
*
* [IN] path - set clipping to this path
* [IN] combineMode - the combine operator (and, or, xor, exclude, complement)
* [IN] isDevicePath- if path is already in device units
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetClip(
GpPath * path,
CombineMode combineMode,
BOOL isDevicePath
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = 0;
EmfPlusRecordType type = EmfPlusRecordTypeSetClipPath;
INT flags = (combineMode << 8);
UINT32 metaPathId;
ASSERT((flags & (~GDIP_EPRFLAGS_COMBINEMODE)) == 0);
RecordObject(path, &metaPathId);
ASSERT((metaPathId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
flags |= metaPathId;
if (isDevicePath)
{
flags |= GDIP_EPRFLAGS_ISDEVICEPATH;
}
WriteRecordHeader(dataSize, type, flags);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write SetClipPath record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordOffsetClip.
*
* Arguments:
*
* [IN] dx - x translation amount
* [IN] dy - y translation amount
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordOffsetClip(
REAL dx,
REAL dy
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(dx) + sizeof(dy);
EmfPlusRecordType type = EmfPlusRecordTypeOffsetClip;
WriteRecordHeader(dataSize, type);
WriteReal(EmfPlusStream, dx);
WriteReal(EmfPlusStream, dy);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write OffsetClip record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordGetDC.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordGetDC()
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
GpStatus status = RecordZeroDataRecord(EmfPlusRecordTypeGetDC, 0);
// WriteGdiComment(); // is down-level for this record
// WriteGdiComment will only flush if writing EMF+ dual,
// but for EMF+-only, we also have to flush GetDC records!
EmfPlusStream->Flush();
return status;
}
return Ok;
}
// Write a record with no data besides the EMF+ record header
GpStatus
MetafileRecorder::RecordZeroDataRecord(
EmfPlusRecordType type,
INT flags
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = 0;
WriteRecordHeader(dataSize, type, flags);
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetAntiAliasMode.
*
* Arguments:
*
* [IN] newMode - new anti aliasing mode
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetAntiAliasMode(
BOOL newMode
)
{
return RecordZeroDataRecord(EmfPlusRecordTypeSetAntiAliasMode,
newMode ? GDIP_EPRFLAGS_ANTIALIAS : 0);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetTextRenderingHint.
*
* Arguments:
*
* [IN] newMode - new rendering hint
*
* Return Value:
*
* NONE
*
* Created:
*
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetTextRenderingHint(
TextRenderingHint newMode
)
{
ASSERT ((newMode & (~GDIP_EPRFLAGS_TEXTRENDERINGHINT)) == 0);
return RecordZeroDataRecord(EmfPlusRecordTypeSetTextRenderingHint, newMode);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetTextContrast.
*
* Arguments:
*
* [IN] gammaValue - new contrast value
*
* Return Value:
*
* NONE
*
* Created:
*
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetTextContrast(
UINT contrast
)
{
ASSERT ((contrast & (~GDIP_EPRFLAGS_CONTRAST)) == 0);
return RecordZeroDataRecord(EmfPlusRecordTypeSetTextContrast, contrast);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetInterpolationMode.
*
* Arguments:
*
* [IN] newMode - new interpolation mode
*
* Return Value:
*
* NONE
*
* Created:
*
* 5/1/2000 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetInterpolationMode(
InterpolationMode newMode
)
{
ASSERT ((newMode & (~GDIP_EPRFLAGS_INTERPOLATIONMODE)) == 0);
return RecordZeroDataRecord(EmfPlusRecordTypeSetInterpolationMode, newMode);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetPixelOffsetMode.
*
* Arguments:
*
* [IN] newMode - new pixel offset mode
*
* Return Value:
*
* NONE
*
* Created:
*
* 5/1/2000 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetPixelOffsetMode(
PixelOffsetMode newMode
)
{
ASSERT ((newMode & (~GDIP_EPRFLAGS_PIXELOFFSETMODE)) == 0);
return RecordZeroDataRecord(EmfPlusRecordTypeSetPixelOffsetMode, newMode);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetRenderingOrigin.
*
* Arguments:
*
* [IN] x, y - new rendering origin
*
* Return Value:
*
* NONE
*
* Created:
*
* 5/4/2000 asecchia
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetRenderingOrigin(
INT x,
INT y
)
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
if (IsValid())
{
UINT32 dataSize = sizeof(x) + sizeof(y);
EmfPlusRecordType type = EmfPlusRecordTypeSetRenderingOrigin;
WriteRecordHeader(dataSize, type);
WriteInt32(EmfPlusStream, x);
WriteInt32(EmfPlusStream, y);
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
WARNING(("Failed to write SetRenderingOrigin record"));
return Win32Error;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetCompositingMode.
*
* Arguments:
*
* [IN] newMode - new compositing mode
*
* Return Value:
*
* NONE
*
* Created:
*
* 10/11/1999 AGodfrey
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetCompositingMode(
GpCompositingMode newMode
)
{
ASSERT ((newMode & (~GDIP_EPRFLAGS_COMPOSITINGMODE)) == 0);
return RecordZeroDataRecord(EmfPlusRecordTypeSetCompositingMode, newMode);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordSetCompositingQuality.
*
* Arguments:
*
* [IN] newQuality - new quality setting
*
* Return Value:
*
* NONE
*
* Created:
*
* 04/22/2000 AGodfrey
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordSetCompositingQuality(
GpCompositingQuality newQuality
)
{
ASSERT ((newQuality & (~GDIP_EPRFLAGS_COMPOSITINGQUALITY)) == 0);
return RecordZeroDataRecord(EmfPlusRecordTypeSetCompositingQuality, newQuality);
}
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - RecordComment.
*
* Arguments:
*
* [IN] sizeData - number of bytes of data
* [IN] data - pointer to the data
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 6/29/1999 DCurtis
*
\**************************************************************************/
GpStatus
MetafileRecorder::RecordComment(
UINT sizeData,
const BYTE * data
)
{
if (IsValid() && (sizeData > 0) && (data != NULL))
{
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
UINT32 dataSize = (sizeData + 3) & (~3);
EmfPlusRecordType type = EmfPlusRecordTypeComment;
INT pad = dataSize - sizeData;
INT flags = pad;
WriteRecordHeader(dataSize, type, flags);
WriteBytes(EmfPlusStream, data, sizeData);
while(pad--)
{
WriteByte(EmfPlusStream, 0);
}
// WriteGdiComment(); no down-level for this record
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
}
else if (Type == EmfTypeEmfOnly)
{
GdiComment(MetafileHdc, sizeData, data);
return Ok;
}
}
WARNING(("Failed to write Comment record"));
return GenericError;
}
GpStatus
MetafileRecorder::RecordHeader(
INT logicalDpiX,
INT logicalDpiY,
INT emfPlusFlags
)
{
// Don't need to check for EmfPlusStream or Valid
UINT32 dataSize = sizeof(EmfPlusHeaderRecord);
EmfPlusRecordType type = EmfPlusRecordTypeHeader;
INT flags = 0;
if (Type != EmfTypeEmfPlusOnly)
{
flags |= GDIP_EPRFLAGS_EMFPLUSDUAL;
}
EmfPlusHeaderRecord emfPlusHeader(emfPlusFlags, logicalDpiX, logicalDpiY);
WriteRecordHeader(dataSize, type, flags);
WriteBytes(EmfPlusStream, &emfPlusHeader, sizeof(emfPlusHeader));
// We have to flush the EMF+ header immediately to guarantee that it
// is the first record in the EMF after the EMF header. Otherwise,
// CloneColorAdjusted fails, because immediately after the metafile
// constructor, it calls Play into the new metafile which writes a
// SaveDC record into the metafile.
EmfPlusStream->Flush();
if (EmfPlusStream->IsValid())
{
return Ok;
}
SetValid(FALSE);
WARNING(("Failed to write Metafile Header record"));
return Win32Error;
}
VOID
MetafileRecorder::RecordEndOfFile()
{
RecordZeroDataRecord(EmfPlusRecordTypeEndOfFile, 0);
}
extern "C"
int CALLBACK
EnumEmfToStream(
HDC hdc,
HANDLETABLE FAR * gdiHandleTable,
CONST ENHMETARECORD * emfRecord,
int numHandles,
LPARAM stream
);
/**************************************************************************\
*
* Function Description:
*
* IMetafileRecord interface method - EndRecording.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
VOID
MetafileRecorder::EndRecording()
{
GpMetafile::MetafileState state = GpMetafile::InvalidMetafileState;
if (IsValid() && (Metafile->State == GpMetafile::RecordingMetafileState))
{
INT success = 1; // assume success
// If doing down-level only, then EmfPlusStream will be NULL
if (EmfPlusStream != NULL)
{
// Force a flush of the Stream buffer to the EMF+ file
EmfPlusStream->Flush();
// We put a no-op PatBlt into the metafile to guarantee that
// the header of the metafile has the same size bounds and
// frame rect that GDI+ has recorded so that the EMF will
// play back the same way, whether GDI plays it back or we do.
// Otherwise, this may not be the case. For example, on a
// bezier curve, the GDI+ bounds would include the control
// points, whereas the down-level representation may not.
// If we haven't written any records to the file, then XMinDevice
// and other bounds are still initialized at FLT_MAX and can cause
// an exception. We don't need the empty PatBlt record in that case
// because we haven't written anything.
if (BoundsInit != FALSE)
{
// Try to match the GDI+ rasterizer
INT left = RasterizerCeiling(XMinDevice);
INT top = RasterizerCeiling(YMinDevice);
INT right = RasterizerCeiling(XMaxDevice); // exclusive
INT bottom = RasterizerCeiling(YMaxDevice); // exclusive
// to get the inclusive right and bottom, we'd now
// have to subtract 1 from each of them
if ((right > left) && (bottom > top))
{
Metafile->MetaGraphics->NoOpPatBlt(left, top, right - left, bottom - top);
}
}
// must be the last record in the file, except the EMF EOF record
RecordEndOfFile();
EmfPlusStream->Flush();
}
HENHMETAFILE hEmf = CloseEnhMetaFile(MetafileHdc);
if (hEmf == NULL)
{
goto Done;
}
// Get the EMF header
ENHMETAHEADER3 emfHeader;
if ((GetEnhMetaFileHeader(hEmf, sizeof(emfHeader),
(ENHMETAHEADER*)(&emfHeader)) <= 0) ||
!EmfHeaderIsValid(emfHeader))
{
DeleteEnhMetaFile(hEmf);
goto Done;
}
#if DBG
if ((emfHeader.rclBounds.right == -1) &&
(emfHeader.rclBounds.bottom == -1) &&
(emfHeader.rclBounds.left == 0) &&
(emfHeader.rclBounds.top == 0))
{
WARNING1("Empty metafile -- no drawing records");
}
#endif
MetafileHeader * header = &Metafile->Header;
INT32 emfPlusFlags = header->EmfPlusFlags;
// Save the header and various other info in the Metafile
Metafile->Hemf = hEmf;
Metafile->MaxStackSize = MaxStackSize;
header->EmfPlusFlags = emfPlusFlags;
header->EmfHeader = emfHeader;
header->Size = emfHeader.nBytes;
// Set the bounds in the Metafile header
{
REAL multiplierX = header->DpiX / 2540.0f;
REAL multiplierY = header->DpiY / 2540.0f;
// The frameRect is inclusive-inclusive, but the bounds in
// the header is inclusive-exclusive.
REAL x = (multiplierX * (REAL)(emfHeader.rclFrame.left));
REAL y = (multiplierY * (REAL)(emfHeader.rclFrame.top));
REAL w = (multiplierX * (REAL)(emfHeader.rclFrame.right -
emfHeader.rclFrame.left)) + 1.0f;
REAL h = (multiplierY * (REAL)(emfHeader.rclFrame.bottom -
emfHeader.rclFrame.top)) + 1.0f;
header->X = GpRound(x);
header->Y = GpRound(y);
header->Width = GpRound(w);
header->Height = GpRound(h);
}
// The metafile is either supposed to be in memory, in a file,
// or in a stream.
// If it goes in a file, we're done unless we need to rewrite
// any of the header information.
if (Metafile->Filename != NULL)
{
state = GpMetafile::DoneRecordingMetafileState;
}
else
{
// If it goes in memory, we're done.
// If it goes in a stream, then we have to write
// the bits to the stream.
if (Metafile->Stream != NULL)
{
// Write the emf data buffer to the stream,
// and leave the stream position at the end of the metafile.
if (!::EnumEnhMetaFile(NULL, hEmf, EnumEmfToStream,
Metafile->Stream, NULL))
{
WARNING(("Problem retrieving EMF Data"));
DeleteEnhMetaFile(hEmf);
Metafile->Hemf = NULL;
goto Done;
}
// Don't need the Stream any longer
Metafile->Stream->Release();
Metafile->Stream = NULL;
}
state = GpMetafile::DoneRecordingMetafileState;
}
}
else
{
DeleteEnhMetaFile(CloseEnhMetaFile(MetafileHdc));
WARNING(("Metafile in wrong state in EndRecording"));
}
Done:
Metafile->MetaGraphics->Metafile = NULL; // Graphics can't point to us anymore
Metafile->MetaGraphics->SetValid(FALSE); // Don't allow anymore operations on
// the graphics
Metafile->MetaGraphics = NULL; // graphics is not valid any more
Metafile->State = state;
delete this;
}
#if 0
inline INT
WriteActualSize(
IStream * stream,
LONGLONG & startOfRecord,
ULONG actualSize
)
{
ASSERT (actualSize > 0);
// get to size field
INT success = SeekFromStart(stream, startOfRecord + sizeof(INT32));
if (success)
{
success &= WriteInt32(stream, actualSize);
}
// get back to end of record
success &= SeekFromStart(stream, startOfRecord + actualSize);
return success;
}
#endif
/**************************************************************************\
*
* Function Description:
*
* Write an object (pen, brush, image, region, path, font) to metafile by
* writing its header, calling its serialize method, and then re-writing
* the size.
*
* Arguments:
*
* [IN] type - the record type
* [IN] flags - any flags for the record header
* [IN] object - pointer to the object to be recorded
* [IN] metaObjectId - ID to store in file that identifies object
* [IN] extraData - any extra data to store with object
* [IN] extraDataSize - size in BYTES of extraData
*
* Return Value:
*
* INT - 1 if we succeeded, else 0 if we failed
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
VOID
MetafileRecorder::WriteObject(
ObjectType type,
const GpObject * object,
UINT32 metaObjectId
)
{
ULONG objectDataSize = object->GetDataSize();
INT flags = ((INT)type << 8);
ASSERT((objectDataSize & 0x03) == 0);
ASSERT((flags & (~GDIP_EPRFLAGS_OBJECTTYPE)) == 0);
ASSERT((metaObjectId & (~GDIP_EPRFLAGS_METAOBJECTID)) == 0);
ASSERT(objectDataSize != 0); // cannot have an empty object
flags |= metaObjectId;
WriteRecordHeader(objectDataSize, EmfPlusRecordTypeObject, flags, NULL);
if (object->GetData(EmfPlusStream) != Ok)
{
WARNING(("GetData failed"));
}
EmfPlusStream->EndObjectRecord();
}
VOID
MetafileRecorder::RecordObject(
const GpObject * object,
UINT32* metaObjectId
)
{
if (object)
{
ObjectType type = object->GetObjectType();
if (ObjectList.IsInList(object, type, metaObjectId))
{
ObjectList.UpdateMRU(*metaObjectId);
}
else
{
ObjectList.InsertAt(object, metaObjectId);
WriteObject(type, object, *metaObjectId);
}
}
else
{
*metaObjectId = GDIP_OBJECTID_NONE;
}
}
// This is for backward compatiblity. If we are using a new object
// (such as a new kind of brush), then we can record a backup object
// for down-level apps to use when they see a new object that they
// don't know how to deal with.
GpStatus
MetafileRecorder::RecordBackupObject(
const GpObject * object
)
{
WriteObject(object->GetObjectType(), object, GDIP_BACKUP_OBJECTID) ;
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Write the initial portion of an EMF+ record. Every EMF+ record contains
* a size, a type, and some flags. Many also contain a rect that specifies
* the bounds of a drawing operation in REAL device units.
*
* Arguments:
*
* [IN] size - the size of the record (excluding the header)
* [IN] type - the EMF+ record type
* [IN] flags - any flags that are defined for this record
* [IN] deviceBounds - bounds of drawing operation, or NULL
*
* Return Value:
*
* INT - 1 if we succeeded, else 0 if we failed
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
VOID
MetafileRecorder::WriteRecordHeader(
UINT32 dataSize,
EmfPlusRecordType type,
INT flags, // 16 bits of flags
const GpRectF * deviceBounds
)
{
ASSERT((dataSize & 0x03) == 0);
EmfPlusStream->WriteRecordHeader(dataSize, type, flags);
NumRecords++;
if (deviceBounds != NULL)
{
// If the bounds aren't initalized then make sure we have 4 valid
// coordinates
ASSERT(BoundsInit ||
((deviceBounds->X < XMinDevice) &&
(deviceBounds->GetRight() > XMaxDevice) &&
(deviceBounds->Y < YMinDevice) &&
(deviceBounds->GetBottom() > YMaxDevice)));
BoundsInit = TRUE;
// Update the device bounds
if (deviceBounds->X < XMinDevice)
{
XMinDevice = deviceBounds->X;
}
if (deviceBounds->GetRight() > XMaxDevice)
{
XMaxDevice = deviceBounds->GetRight(); // exclusive
}
if (deviceBounds->Y < YMinDevice)
{
YMinDevice = deviceBounds->Y;
}
if (deviceBounds->GetBottom() > YMaxDevice)
{
YMaxDevice = deviceBounds->GetBottom(); // exclusive
}
}
}
/**************************************************************************\
*
* Function Description:
*
* If the brush is a 32-bit solid color, then return the solid color as
* the brush value and set the flags to indicate it's a solid color.
* Otherwise, record the brush and return the metafile brush id as the
* brush value.
*
* Arguments:
*
* [IN] brush - the brush that needs to be recorded
* [OUT] brushValue - the 32-bit color or metafile brush ID
* [OUT] flags - set if we're using a solid color
*
* Return Value:
*
* INT - 1 if we succeeded, else 0 if we failed
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
VOID
MetafileRecorder::GetBrushValueForRecording(
const GpBrush *brush,
UINT32 &brushValue,
INT &flags
)
{
if (brush->GetBrushType() == BrushTypeSolidColor)
{
const GpSolidFill * solidBrush = static_cast<const GpSolidFill *>(brush);
brushValue = solidBrush->GetColor().GetValue();
flags |= GDIP_EPRFLAGS_SOLIDCOLOR;
}
else
{
RecordObject(brush, &brushValue);
}
}
/**************************************************************************\
*
* Function Description:
*
* GpMetafile constructor for write/read access to a metafile. (Write must
* precede the read.)
*
* This version records an EMF+ to memory. The type specifies whether
* to record dual GDI records or not.
*
* If the frameRect is NULL, it will be calculated by accumulating the
* device bounds of the metafile. Otherwise, the supplied frameRect and
* corresponding frameUnit will be used to record the frameRect in the
* metafile header. The frameRect is inclusive-inclusive, which means
* that the width value is actually 1 less than the actual width.
* For example, a width of 0 is accepted and really means a width of 1.
*
* If the optional description is supplied, it will become part of the
* EMF header.
*
* Arguments:
*
* [IN] fileName - where to write the metafile
* [IN] referenceHdc - an HDC to use as a reference for creating metafile
* [IN] type - whether to record EMF+-only or EMF+-dual
* [IN] frameRect - optional frame rect for recording in header
* [IN] frameUnit - the units of the frameRect
* [IN] description - optional metafile description
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpMetafile::GpMetafile(
HDC referenceHdc,
EmfType type,
const GpRectF * frameRect, // can be NULL
MetafileFrameUnit frameUnit, // if NULL frameRect, doesn't matter
const WCHAR * description // can be NULL
) : GpImage(ImageTypeMetafile)
{
ASSERT(referenceHdc != NULL);
InitDefaults();
if ((referenceHdc != NULL) &&
InitForRecording(
referenceHdc,
type,
frameRect, // can be NULL
frameUnit, // if NULL frameRect, doesn't matter
description // can be NULL
))
{
State = RecordingMetafileState;
}
}
/**************************************************************************\
*
* Function Description:
*
* GpMetafile constructor for write/read access to a metafile. (Write must
* precede the read.)
*
* This version records an EMF+ to a file. The type specifies whether
* to record dual GDI records or not.
*
* If the frameRect is NULL, it will be calculated by accumulating the
* device bounds of the metafile. Otherwise, the supplied frameRect and
* corresponding frameUnit will be used to record the frameRect in the
* metafile header. The frameRect is inclusive-inclusive, which means
* that the width value is actually 1 less than the actual width.
* For example, a width of 0 is accepted and really means a width of 1.
*
* If the optional description is supplied, it will become part of the
* EMF header.
*
* Arguments:
*
* [IN] fileName - where to write the metafile
* [IN] referenceHdc - an HDC to use as a reference for creating metafile
* [IN] type - whether to record EMF+-only or EMF+-dual
* [IN] frameRect - optional frame rect for recording in header
* [IN] frameUnit - the units of the frameRect
* [IN] description - optional metafile description
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpMetafile::GpMetafile(
const WCHAR* fileName,
HDC referenceHdc,
EmfType type,
const GpRectF * frameRect, // can be NULL
MetafileFrameUnit frameUnit, // if NULL frameRect, doesn't matter
const WCHAR * description // can be NULL
) : GpImage(ImageTypeMetafile)
{
ASSERT((fileName != NULL) && (referenceHdc != NULL));
InitDefaults();
if ((fileName != NULL) && (referenceHdc != NULL) &&
((Filename = UnicodeStringDuplicate(fileName)) != NULL) &&
InitForRecording(
referenceHdc,
type,
frameRect, // can be NULL
frameUnit, // if NULL frameRect, doesn't matter
description // can be NULL
))
{
State = RecordingMetafileState;
}
}
/**************************************************************************\
*
* Function Description:
*
* GpMetafile constructor for write/read access to a metafile. (Write must
* precede the read.)
*
* This version records an EMF+ to a file. The type specifies whether
* to record dual GDI records or not.
*
* The metafile is first recorded to a temporary file, then it is copied
* from the file into the stream.
*
* If the frameRect is NULL, it will be calculated by accumulating the
* device bounds of the metafile. Otherwise, the supplied frameRect and
* corresponding frameUnit will be used to record the frameRect in the
* metafile header. The frameRect is inclusive-inclusive, which means
* that the width value is actually 1 less than the actual width.
* For example, a width of 0 is accepted and really means a width of 1.
*
* If the optional description is supplied, it will become part of the
* EMF header.
*
* Arguments:
*
* [IN] stream - where to copy the metafile, after it's recorded
* [IN] referenceHdc - an HDC to use as a reference for creating metafile
* [IN] type - whether to record EMF+-only or EMF+-dual
* [IN] frameRect - optional frame rect for recording in header
* [IN] frameUnit - the units of the frameRect
* [IN] description - optional metafile description
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
GpMetafile::GpMetafile(
IStream * stream,
HDC referenceHdc,
EmfType type,
const GpRectF * frameRect, // can be NULL
MetafileFrameUnit frameUnit, // if NULL frameRect, doesn't matter
const WCHAR * description // can be NULL
) : GpImage(ImageTypeMetafile)
{
ASSERT((stream != NULL) && (referenceHdc != NULL));
InitDefaults();
if ((stream != NULL) && (referenceHdc != NULL))
{
if (InitForRecording(
referenceHdc,
type,
frameRect, // can be NULL
frameUnit, // if NULL frameRect, doesn't matter
description // can be NULL
))
{
stream->AddRef();
Stream = stream;
State = RecordingMetafileState;
}
}
}
inline HDC CreateEmf(
HDC referenceHdc,
const WCHAR * fileName,
RECT * frameRect
)
{
HDC metafileHdc = NULL;
if (Globals::IsNt)
{
metafileHdc = CreateEnhMetaFileW(referenceHdc, fileName, frameRect, NULL);
}
else
{
AnsiStrFromUnicode fileBuffer(fileName);
if (fileBuffer.IsValid())
{
metafileHdc = CreateEnhMetaFileA(referenceHdc, fileBuffer, frameRect, NULL);
}
}
return metafileHdc;
}
static BOOL
GetFrameRectInMM100Units(
HDC hdc,
const GpRectF * frameRect,
MetafileFrameUnit frameUnit,
RECT & rclFrame
)
{
SIZEL szlDevice; // Size of device in pels
SIZEL szlMillimeters; // Size of device in millimeters
REAL dpiX;
REAL dpiY;
// NOTE: We have to use the szlDevice and szlMillimeters to get
// the dpi (instead of getting it directly from LOGPIXELSX/Y)
// so that the frame rect that is calculated for the metafile by GDI
// matches the one that GDI+ would have calculated. Because it's
// these 2 metrics that the GDI metafile code uses to get the frame
// rect from the bounds, not the logical DPI.
szlDevice.cx = ::GetDeviceCaps(hdc, HORZRES);
szlDevice.cy = ::GetDeviceCaps(hdc, VERTRES);
szlMillimeters.cx = ::GetDeviceCaps(hdc, HORZSIZE);
szlMillimeters.cy = ::GetDeviceCaps(hdc, VERTSIZE);
if ((szlDevice.cx <= 0) || (szlDevice.cy <= 0) ||
(szlMillimeters.cx <= 0) || (szlMillimeters.cy <= 0))
{
WARNING(("GetDeviceCaps failed"));
return FALSE;
}
// Now get the real DPI, adjusted for the round-off error.
dpiX = ((REAL)(szlDevice.cx) / (REAL)(szlMillimeters.cx)) * 25.4f;
dpiY = ((REAL)(szlDevice.cy) / (REAL)(szlMillimeters.cy)) * 25.4f;
GpRectF frameRectMM100;
FrameToMM100(frameRect, (GpPageUnit)frameUnit, frameRectMM100,
dpiX, dpiY);
rclFrame.left = GpRound(frameRectMM100.X);
rclFrame.top = GpRound(frameRectMM100.Y);
rclFrame.right = GpRound(frameRectMM100.GetRight());
rclFrame.bottom = GpRound(frameRectMM100.GetBottom());
// Make sure the .01MM frameRect is valid
// It's okay for left == right, because the frameRect
// is inclusive-inclusive.
if ((rclFrame.left > rclFrame.right) ||
(rclFrame.top > rclFrame.bottom))
{
WARNING(("Invalid GDI frameRect"));
return FALSE;
}
return TRUE;
}
/**************************************************************************\
*
* Function Description:
*
* Convert a frameRect in any units, to a frame rect that is in .01 MM units.
*
* Arguments:
*
* [IN] frameRect - the source frameRect
* [IN] frameUnit - the units of the source frameRect
* [OUT] frameRectMM100 - the frameRect in inch units
* [IN] dpiX - the horizontal DPI
* [IN] dpiY - the vertical DPI
*
* Return Value:
*
* NONE
*
* Created:
*
* 6/15/1999 DCurtis
*
\**************************************************************************/
static VOID
FrameToMM100(
const GpRectF * frameRect,
GpPageUnit frameUnit,
GpRectF & frameRectMM100,
REAL dpiX, // only used for pixel case
REAL dpiY
)
{
REAL pixelsToMM100X = (2540.0f / dpiX);
REAL pixelsToMM100Y = (2540.0f / dpiY);
// The GDI frameRect has right and bottom values that are
// inclusive, whereas the GDI+ frameRect has GetRight() and
// GetBottom() values that are exclusive (because GDI+ rects
// are specified with width/height, not right/bottom. To convert
// from the GDI+ value to the GDI value, we have to subtract 1 pixel.
// This means that we first convert the units to pixel units, then
// subtract one, then convert to MM100 units.
switch (frameUnit)
{
default:
ASSERT(0);
// FALLTHRU
case UnitPixel: // Each unit represents one device pixel.
frameRectMM100.X = frameRect->X * pixelsToMM100X;
frameRectMM100.Y = frameRect->Y * pixelsToMM100Y;
frameRectMM100.Width = frameRect->Width;
frameRectMM100.Height = frameRect->Height;
break;
case UnitPoint: // Each unit represents 1/72 inch.
frameRectMM100.X = frameRect->X * (2540.0f / 72.0f);
frameRectMM100.Y = frameRect->Y * (2540.0f / 72.0f);
frameRectMM100.Width = frameRect->Width * (dpiX / 72.0f);
frameRectMM100.Height = frameRect->Height * (dpiY / 72.0f);
break;
case UnitInch: // Each unit represents 1 inch.
frameRectMM100.X = frameRect->X * 2540.0f;
frameRectMM100.Y = frameRect->Y * 2540.0f;
frameRectMM100.Width = frameRect->Width * dpiX;
frameRectMM100.Height = frameRect->Height * dpiY;
break;
case UnitDocument: // Each unit represents 1/300 inch.
frameRectMM100.X = frameRect->X * (2540.0f / 300.0f);
frameRectMM100.Y = frameRect->Y * (2540.0f / 300.0f);
frameRectMM100.Width = frameRect->Width * (dpiX / 300.0f);
frameRectMM100.Height = frameRect->Height * (dpiY / 300.0f);
break;
case UnitMillimeter: // Each unit represents 1 millimeter.
// One Millimeter is 0.03937 inches
// One Inch is 25.4 millimeters
frameRectMM100.X = frameRect->X * (100.0f);
frameRectMM100.Y = frameRect->Y * (100.0f);
frameRectMM100.Width = frameRect->Width * (dpiX / 25.4f);
frameRectMM100.Height = frameRect->Height * (dpiY / 25.4f);
break;
}
frameRectMM100.Width = (frameRectMM100.Width - 1.0f) * pixelsToMM100X;
frameRectMM100.Height = (frameRectMM100.Height - 1.0f) * pixelsToMM100Y;
}
BOOL
GpMetafile::InitForRecording(
HDC referenceHdc,
EmfType type,
const GpRectF * frameRect, // can be NULL
MetafileFrameUnit frameUnit, // if NULL frameRect, doesn't matter
const WCHAR * description // can be NULL
)
{
RECT * frameRectParam = NULL;
RECT rclFrame;
if (frameRect != NULL)
{
// Validate the frameRect
// 0 is allowed, since the frameRect is inclusive-inclusive, which
// means that a width of 0 is actually a width of 1
if ((frameRect->Width < 0.0f) || (frameRect->Height < 0.0f))
{
WARNING(("Invalid frameRect"));
return FALSE;
}
if (frameUnit == MetafileFrameUnitGdi)
{
// Typically, the GDI+ frameRect is inclusive/exclusive
// as far as the GetLeft()/GetRight() values go, but the
// MetafileFrameUnitGdi unit is a special type of unit
// that specifies compatibility with GDI which is
// inclusive/inclusive, so we don't do any adjustment
// on those values at all -- we just assume they are ready
// to pass directly to GDI.
rclFrame.left = GpRound(frameRect->X);
rclFrame.top = GpRound(frameRect->Y);
rclFrame.right = GpRound(frameRect->GetRight());
rclFrame.bottom = GpRound(frameRect->GetBottom());
// Make sure the .01MM frameRect is valid
// It's okay for left == right, because the GDI frameRect
// is inclusive-inclusive.
if ((rclFrame.left > rclFrame.right) ||
(rclFrame.top > rclFrame.bottom))
{
WARNING(("Invalid GDI frameRect"));
return FALSE;
}
}
else
{
if (!GetFrameRectInMM100Units(referenceHdc, frameRect, frameUnit, rclFrame))
{
return FALSE;
}
}
frameRectParam = &rclFrame;
}
HDC metafileHdc;
// Now create the metafile HDC
// Note that FileName might be NULL
metafileHdc = CreateEmf(referenceHdc, Filename, frameRectParam);
if (metafileHdc == NULL)
{
return FALSE; // failed
}
// Now get the dpi based on the metafileHdc (which could be different
// than the referenceHdc).
SIZEL szlDevice; // Size of metafile device in pels
SIZEL szlMillimeters; // Size of metafile device in millimeters
GpRectF metafileBounds;
// NOTE: We have to use the szlDevice and szlMillimeters to get
// the dpi (instead of getting it directly from LOGPIXELSX/Y)
// so that the frame rect that is calculated for the metafile by GDI
// matches the one that GDI+ would have calculated. Because it's
// these 2 metrics that the GDI metafile code uses to get the frame
// rect from the bounds, not the logical DPI.
szlDevice.cx = ::GetDeviceCaps(metafileHdc, HORZRES);
szlDevice.cy = ::GetDeviceCaps(metafileHdc, VERTRES);
szlMillimeters.cx = ::GetDeviceCaps(metafileHdc, HORZSIZE);
szlMillimeters.cy = ::GetDeviceCaps(metafileHdc, VERTSIZE);
if ((szlDevice.cx <= 0) || (szlDevice.cy <= 0) ||
(szlMillimeters.cx <= 0) || (szlMillimeters.cy <= 0))
{
WARNING(("GetDeviceCaps failed"));
goto ErrorExit;
}
REAL dpiX;
REAL dpiY;
REAL dpmmX = (REAL)(szlDevice.cx) / (REAL)(szlMillimeters.cx);
REAL dpmmY = (REAL)(szlDevice.cy) / (REAL)(szlMillimeters.cy);
// Now get the real DPI, adjusted for the round-off error.
dpiX = dpmmX * 25.4f;
dpiY = dpmmY * 25.4f;
// Set the DPI in the metafile
this->Header.DpiX = dpiX;
this->Header.DpiY = dpiY;
// NOTE: On Win9x there are some hi-res printer drivers that use a
// different resolution for the metafileHdc than they do for the
// referenceHdc (Probably to avoid overflow.) The problem with that
// is, that the differing resolutions make it impossible for us to
// know which frameRect to use, because we don't know for certain
// what DPI the application is going to assume to do its drawing --
// whether the metafile resolution or the printer resolution. In any
// case, it's a safe bet that the original frameRect is wrong.
if (!Globals::IsNt && (frameRectParam != NULL) &&
(::GetDeviceCaps(metafileHdc, LOGPIXELSX) != ::GetDeviceCaps(referenceHdc, LOGPIXELSX)))
{
frameRectParam = NULL; // give up on the frameRect
// Now recreate the metafile HDC
::DeleteEnhMetaFile(::CloseEnhMetaFile(metafileHdc));
metafileHdc = CreateEmf(referenceHdc, Filename, frameRectParam);
if (metafileHdc == NULL)
{
return FALSE; // failed
}
}
// The metafileBounds are used as the bounds for FillRegion
// calls when the region has infinite bounds, to keep from
// exploding the bounds of the metafile.
if (frameRectParam != NULL)
{
dpmmX *= 0.01f;
dpmmY *= 0.01f;
metafileBounds.X = rclFrame.left * dpmmX;
metafileBounds.Y = rclFrame.top * dpmmY;
metafileBounds.Width = (rclFrame.right - rclFrame.left) * dpmmX;
metafileBounds.Height = (rclFrame.bottom - rclFrame.top) * dpmmY;
}
else
{
metafileBounds.X = 0.0f;
metafileBounds.Y = 0.0f;
metafileBounds.Width = (REAL)szlDevice.cx - 1; // metafile bounds are inclusive
metafileBounds.Height = (REAL)szlDevice.cy - 1;
}
// Now create the recorder object
MetafileRecorder * recorder = new MetafileRecorder(
this,
type,
metafileHdc,
(frameRectParam != NULL),
szlMillimeters,
metafileBounds);
if (CheckValid(recorder))
{
MetaGraphics = GpGraphics::GetForMetafile(recorder, type, metafileHdc);
if (MetaGraphics != NULL)
{
if (MetaGraphics->IsValid())
{
return TRUE;
}
recorder->SetValid(FALSE);// so we don't record stuff in EndRecording
delete MetaGraphics; // calls EndRecording which deletes recorder
MetaGraphics = NULL;
}
else
{
delete recorder;
}
}
ErrorExit:
DeleteEnhMetaFile(CloseEnhMetaFile(metafileHdc));
return FALSE;
}
// Returns NULL if the metafile was opened for reading or if already got
// the context for writing.
GpGraphics *
GpMetafile::GetGraphicsContext()
{
if (!RequestedMetaGraphics)
{
RequestedMetaGraphics = TRUE;
return MetaGraphics;
}
WARNING(("Requesting MetaGraphics more than once"));
return NULL;
}
GpStatus
GpMetafile::SetDownLevelRasterizationLimit(
UINT metafileRasterizationLimitDpi
)
{
ASSERT(IsValid());
// 0 means restore it to the default value; otherwise, the minumum is 10 dpi
if ((metafileRasterizationLimitDpi == 0) || (metafileRasterizationLimitDpi >= 10))
{
if ((State == GpMetafile::RecordingMetafileState) &&
(MetaGraphics != NULL))
{
MetaGraphics->Context->SetMetafileDownLevelRasterizationLimit(metafileRasterizationLimitDpi);
return Ok;
}
WARNING1("Metafile in Wrong State for this operation");
return WrongState;
}
WARNING1("rasterizationDpiLimit is non-zero but too small");
return InvalidParameter;
}
GpStatus
GpMetafile::GetDownLevelRasterizationLimit(
UINT * metafileRasterizationLimitDpi
) const
{
ASSERT(metafileRasterizationLimitDpi != NULL);
ASSERT(IsValid());
if ((State == GpMetafile::RecordingMetafileState) &&
(MetaGraphics != NULL))
{
*metafileRasterizationLimitDpi = MetaGraphics->Context->GetMetafileDownLevelRasterizationLimit();
return Ok;
}
WARNING1("Metafile in Wrong State for this operation");
return WrongState;
}