Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3449 lines
108 KiB

/**************************************************************************\
*
* Copyright (c) 1999 Microsoft Corporation
*
* Module Name:
*
* MetaWmf.cpp
*
* Abstract:
*
* Methods for playing and recoloring a WMF.
*
* Created:
*
* 12/13/1999 DCurtis
*
\**************************************************************************/
#include "Precomp.hpp"
#include "MetaWmf.hpp"
#ifndef BI_CMYK // from wingdip.h
#define BI_CMYK 10L
#define BI_CMYKRLE8 11L
#define BI_CMYKRLE4 12L
#endif
inline static BOOL
IsDwordAligned(
VOID * pointer
)
{
return (((ULONG_PTR)pointer & (sizeof(DWORD) - 1)) == 0);
}
inline static BOOL
IsPostscriptPrinter(
HDC hdc
)
{
// It is a PostScript printer if POSTSCRIPT_PASSTHROUGH or
// POSTSCRIPT_IGNORE is available
int iWant1 = POSTSCRIPT_PASSTHROUGH;
int iWant2 = POSTSCRIPT_IGNORE;
return ((Escape(hdc, QUERYESCSUPPORT, sizeof(iWant1), (LPCSTR)&iWant1, NULL) != 0) ||
(Escape(hdc, QUERYESCSUPPORT, sizeof(iWant2), (LPCSTR)&iWant2, NULL) != 0));
}
// Some escapes apparently cause NT 3.51 to crash, so skip them
inline static BOOL
SkipEscape(
INT escapeCode
)
{
switch (escapeCode)
{
case GETPHYSPAGESIZE: // 12
case GETPRINTINGOFFSET: // 13
case GETSCALINGFACTOR: // 14
case BEGIN_PATH: // 4096
case CLIP_TO_PATH: // 4097
case END_PATH: // 4098
return TRUE;
default:
return FALSE;
}
}
inline static BOOL
IsOfficeArtData(
UINT recordSize,
const WORD * recordData
)
{
return (recordData[0] == MFCOMMENT) &&
(recordSize > 16) &&
((INT)recordSize >= (recordData[1] + 10)) &&
(GpMemcmp(recordData + 2, "TNPPOA", 6) == 0);
}
// The structure which defines the contents of a comment for a WMF or PICT,
// for an EMF use GdiComment() and the "approved" format (see the Win32
// documentation) - this basically is the same except that it has a 4 byte
// kind field. For a PICT this is the format of an ApplicationComment (kind
// 100).
#pragma pack(push, GDIP_pack, 2)
typedef struct
{
ULONG Signature; // Identifes the comment writer.
USHORT Kind; // Type of comment (writer specific)
// Comment data follows here.
} WmfComment;
typedef struct
{
WORD lbStyle;
COLORREF lbColor;
SHORT lbHatch;
} LOGBRUSH16;
typedef struct
{
WORD lopnStyle;
POINTS lopnWidth;
COLORREF lopnColor;
} LOGPEN16;
typedef struct
{
SHORT bmType;
SHORT bmWidth;
SHORT bmHeight;
SHORT bmWidthBytes;
BYTE bmPlanes;
BYTE bmBitsPixel;
LPBYTE bmBits;
} BITMAP16;
typedef struct tagLOGFONT16
{
SHORT lfHeight;
SHORT lfWidth;
SHORT lfEscapement;
SHORT lfOrientation;
SHORT lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
BYTE lfFaceName[LF_FACESIZE];
} LOGFONT16;
#pragma pack(pop, GDIP_pack)
inline static const WmfComment UNALIGNED *
GetWmfComment(
const WORD * recordData,
ULONG signature,
UINT kind
)
{
// Assumes you've already checked that
// (wmfCommentRecord->rdFunction == META_ESCAPE &&
// wmfCommentRecord->rdParm[0] == MFCOMMENT)
const WmfComment UNALIGNED * wmfComment = (const WmfComment UNALIGNED *)&(recordData[2]);
if ((wmfComment->Signature == signature) && (wmfComment->Kind == kind))
{
return wmfComment;
}
return NULL;
}
inline static INT
GetDibByteWidth(
INT biWidth,
INT biPlanes,
INT biBitCount
)
{
return (((biWidth * biPlanes * biBitCount) + 31) & ~31) / 8;
}
inline static RGBQUAD UNALIGNED *
GetDibColorTable(
BITMAPINFOHEADER UNALIGNED * dibInfo
)
{
return (RGBQUAD UNALIGNED *)(((BYTE *)dibInfo) + dibInfo->biSize);
}
static BYTE *
GetDibBits(
BITMAPINFOHEADER UNALIGNED * dibInfo,
UINT numPalEntries,
UINT usage
)
{
ASSERT(dibInfo->biSize >= sizeof(BITMAPINFOHEADER));
INT colorSize = 0;
if (numPalEntries > 0)
{
if ((usage == DIB_PAL_COLORS) &&
(dibInfo->biCompression != BI_BITFIELDS) &&
(dibInfo->biCompression != BI_CMYK))
{
// Make sure it is aligned
colorSize = ((numPalEntries * sizeof(INT16)) + 3) & ~3;
}
else
{
colorSize = numPalEntries * sizeof(RGBQUAD);
}
}
return ((BYTE *)GetDibColorTable(dibInfo)) + colorSize;
}
UINT
GetDibBitsSize(
BITMAPINFOHEADER UNALIGNED * dibInfo
)
{
// Check for PM-style DIB
if (dibInfo->biSize >= sizeof(BITMAPINFOHEADER))
{
// not a core header
if (dibInfo->biWidth > 0) // can't handle negative width
{
if ((dibInfo->biCompression == BI_RGB) ||
(dibInfo->biCompression == BI_BITFIELDS) ||
(dibInfo->biCompression == BI_CMYK))
{
INT posHeight = dibInfo->biHeight;
if (posHeight < 0)
{
posHeight = -posHeight;
}
return posHeight *
GetDibByteWidth(dibInfo->biWidth, dibInfo->biPlanes,
dibInfo->biBitCount);
}
return dibInfo->biSizeImage;
}
WARNING(("0 or negative DIB width"));
return 0;
}
else // it is a PM-style DIB
{
BITMAPCOREHEADER UNALIGNED * coreDibInfo = (BITMAPCOREHEADER UNALIGNED *)dibInfo;
// width and height must be > 0 for COREINFO dibs
if ((coreDibInfo->bcWidth > 0) &&
(coreDibInfo->bcHeight > 0))
{
return coreDibInfo->bcHeight *
GetDibByteWidth(coreDibInfo->bcWidth,coreDibInfo->bcPlanes,
coreDibInfo->bcBitCount);
}
WARNING(("0 or negative DIB width or height"));
return 0;
}
}
BOOL
GetDibNumPalEntries(
BOOL isWmfDib,
UINT biSize,
UINT biBitCount,
UINT biCompression,
UINT biClrUsed,
UINT * numPalEntries
)
{
UINT maxPalEntries = 0;
// Dibs in a WMF always have the bitfields for 16 and 32-bpp dibs.
if (((biBitCount == 16) || (biBitCount == 32)) && isWmfDib)
{
biCompression = BI_BITFIELDS;
}
switch (biCompression)
{
case BI_BITFIELDS:
//
// Handle 16 and 32 bit per pel bitmaps.
//
switch (biBitCount)
{
case 16:
case 32:
break;
default:
WARNING(("BI_BITFIELDS not Valid for this biBitCount"));
return FALSE;
}
if (biSize <= sizeof(BITMAPINFOHEADER))
{
biClrUsed = maxPalEntries = 3;
}
else
{
//
// masks are part of BITMAPV4 and greater
//
biClrUsed = maxPalEntries = 0;
}
break;
case BI_RGB:
switch (biBitCount)
{
case 1:
case 4:
case 8:
maxPalEntries = 1 << biBitCount;
break;
default:
maxPalEntries = 0;
switch (biBitCount)
{
case 16:
case 24:
case 32:
break;
default:
WARNING(("Invalid biBitCount in BI_RGB"));
return FALSE;
}
}
break;
case BI_CMYK:
switch (biBitCount)
{
case 1:
case 4:
case 8:
maxPalEntries = 1 << biBitCount;
break;
case 32:
maxPalEntries = 0;
break;
default:
WARNING(("Invalid biBitCount in BI_CMYK"));
return FALSE;
}
break;
case BI_RLE4:
case BI_CMYKRLE4:
if (biBitCount != 4)
{
WARNING(("Invalid biBitCount in BI_RLE4"));
return FALSE;
}
maxPalEntries = 16;
break;
case BI_RLE8:
case BI_CMYKRLE8:
if (biBitCount != 8)
{
WARNING(("Invalid biBitCount in BI_RLE8"));
return FALSE;
}
maxPalEntries = 256;
break;
case BI_JPEG:
case BI_PNG:
maxPalEntries = 0;
break;
default:
WARNING(("Invalid biCompression"));
return FALSE;
}
if (biClrUsed != 0)
{
if (biClrUsed <= maxPalEntries)
{
maxPalEntries = biClrUsed;
}
}
*numPalEntries = maxPalEntries;
return TRUE;
}
GdipHdcType
GetHdcType(
HDC hdc
)
{
GdipHdcType hdcType = UnknownHdc;
UINT dcType = GetDCType(hdc);
switch (dcType)
{
case OBJ_DC:
{
INT technology = GetDeviceCaps(hdc, TECHNOLOGY);
if (technology == DT_RASDISPLAY)
{
hdcType = ScreenHdc;
}
else if (technology == DT_RASPRINTER)
{
if (IsPostscriptPrinter(hdc))
{
hdcType = PostscriptPrinterHdc;
}
else
{
hdcType = PrinterHdc;
}
}
else
{
WARNING(("Unknown HDC technology!"));
}
}
break;
case OBJ_MEMDC:
hdcType = BitmapHdc;
break;
case OBJ_ENHMETADC:
// When metafile spooling, the printer DC will be of type
// OBJ_ENHMETADC on Win9x and NT4 (but not NT5 due to a fix
// to NT bug 98810). We need to do some more work to figure
// out whether it's really a printer DC or a true metafile
// DC:
if (Globals::GdiIsMetaPrintDCFunction(hdc))
{
if (IsPostscriptPrinter(hdc))
{
hdcType = PostscriptPrinterHdc;
}
else
{
hdcType = PrinterHdc;
}
}
else
{
hdcType = EmfHdc;
}
break;
case OBJ_METADC:
hdcType = WmfHdc;
break;
default:
WARNING(("Unknown HDC type!"));
break;
}
return hdcType;
}
DWORD
GetHdcBitmapBitsPixel(
HDC hdc
)
{
// This function returns the number of bits per pixel for a bitmap DC.
// On error, 0 is returned.
ASSERT(GetDCType(hdc) == OBJ_MEMDC);
HBITMAP hbm = (HBITMAP) GetCurrentObject(hdc, OBJ_BITMAP);
if (hbm)
{
BITMAP bm;
if (GetObjectA(hbm, sizeof(bm), &bm) >= sizeof(BITMAP))
{
return bm.bmBitsPixel;
}
}
return 0;
}
MfEnumState::MfEnumState(
HDC hdc,
BOOL externalEnumeration,
InterpolationMode interpolation,
GpRecolor * recolor,
ColorAdjustType adjustType,
const RECT * deviceRect,
DpContext * context
)
{
HdcType = GetHdcType(hdc);
Hdc = hdc;
HandleTable = NULL;
NumObjects = 0;
CurrentPalette = (HPALETTE)::GetStockObject(DEFAULT_PALETTE);
SizeAllocedRecord = 0;
ModifiedRecordSize = 0;
SaveDcCount = 0;
BytesEnumerated = 0;
ExternalEnumeration = externalEnumeration;
Recolor = recolor;
AdjustType = adjustType;
CurrentRecord = NULL;
ModifiedRecord = NULL;
AllocedRecord = NULL;
Interpolation = interpolation;
SaveDcVal = SaveDC(hdc);
FsmState = MfFsmStart;
GdiCentricMode = FALSE;
SoftekFilter = FALSE;
DefaultFont = NULL;
RopUsed = FALSE;
Context = context;
SrcCopyOnly = FALSE;
GpMemset(StockFonts, 0, sizeof(StockFonts[0]) * NUM_STOCK_FONTS);
GpMemset(RecoloredStockHandle, 0, sizeof(HGDIOBJ) * NUM_STOCK_RECOLOR_OBJS);
// See if we should halftone solid colors
if ((IsScreen() && (::GetDeviceCaps(hdc, BITSPIXEL) == 8)) ||
(IsBitmap() && (GetHdcBitmapBitsPixel(hdc) == 8)))
{
Is8Bpp = TRUE;
EpPaletteMap paletteMap(hdc);
IsHalftonePalette = (paletteMap.IsValid() && (!paletteMap.IsVGAOnly()));
}
else
{
Is8Bpp = FALSE;
IsHalftonePalette = FALSE;
}
// Since the transform can change as we are playing the metafile
// convert the destrect into DeviceUnits. We will make sure to have an
// identity matrix when we apply it.
DestRectDevice = *deviceRect;
}
MfEnumState::~MfEnumState()
{
// Delete all the true type fonts we created (first make sure they
// are not selected into the Hdc.
::SelectObject(Hdc, GetStockObject(SYSTEM_FONT));
if (DefaultFont)
{
DeleteObject(DefaultFont);
}
for (int i = 0; i < NUM_STOCK_FONTS; i++)
{
if (StockFonts[i] != NULL)
{
DeleteObject(StockFonts[i]);
}
}
// Not necessary to delete NULL_BRUSH, NULL_PEN into HDC. The subclasses
// restore DC state which should dissolve any selections of these pen/brushes.
for (int i = 0; i < NUM_STOCK_RECOLOR_OBJS; i++)
{
if (RecoloredStockHandle[i] != NULL)
{
DeleteObject(RecoloredStockHandle[i]);
}
}
}
WmfEnumState::WmfEnumState(
HDC hdc,
HMETAFILE hWmf,
BOOL externalEnumeration,
InterpolationMode interpolation,
const RECT * dstRect,
const RECT * deviceRect,
DpContext * context,
GpRecolor * recolor,
ColorAdjustType adjustType
)
: MfEnumState(hdc, externalEnumeration, interpolation,
recolor, adjustType, deviceRect, context)
{
if (IsValid())
{
IgnorePostscript = FALSE;
BytesEnumerated = sizeof(METAHEADER); // in WMF enumeration, we don't get header
MetafileSize = GetMetaFileBitsEx(hWmf, 0, NULL);
FirstViewportExt = TRUE;
FirstViewportOrg = TRUE;
IsFirstRecord = TRUE;
// The bad thing about this is that if the metafile is being recolored,
// the default pen, brush, text color, and background colors have NOT
// been recolored. So let's hope they're not really used.
HpenSave = (HPEN) ::SelectObject(hdc, GetStockObject(BLACK_PEN));
HbrushSave = (HBRUSH)::SelectObject(hdc, GetStockObject(WHITE_BRUSH));
if (!Globals::IsNt)
{
HpaletteSave = (HPALETTE)GetCurrentObject(hdc, OBJ_PAL);
HfontSave = (HFONT) GetCurrentObject(hdc, OBJ_FONT);
HbitmapSave = (HBITMAP) GetCurrentObject(hdc, OBJ_BITMAP);
HregionSave = (HRGN) GetCurrentObject(hdc, OBJ_REGION);
}
else
{
HpaletteSave = NULL;
HfontSave = NULL;
HbitmapSave = NULL;
HregionSave = NULL;
}
// Make sure a few default values are set in the hdc
::SetTextAlign(hdc, 0);
::SetTextJustification(hdc, 0, 0);
::SetTextColor(hdc, RGB(0,0,0));
TextColor = RGB(0,0,0);
::SetBkColor(hdc, RGB(255,255,255));
BkColor = RGB(255,255,255);
::SetROP2(hdc, R2_COPYPEN);
DstViewportOrg.x = dstRect->left;
DstViewportOrg.y = dstRect->top;
DstViewportExt.cx = dstRect->right - DstViewportOrg.x;
DstViewportExt.cy = dstRect->bottom - DstViewportOrg.y;
}
}
WmfEnumState::~WmfEnumState()
{
// Turn POSTSCRIPT_IGNORE back off if we need to.
if (IsPostscriptPrinter() && IgnorePostscript)
{
WORD wOn = FALSE;
::Escape(Hdc, POSTSCRIPT_IGNORE, sizeof(wOn), (LPCSTR)&wOn, NULL);
}
// According to Office GEL, SaveDC/RestoreDC doesn't always restore
// the brush and the pen correctly, so we have to do that ourselves.
::SelectObject(Hdc, HbrushSave);
::SelectObject(Hdc, HpenSave);
GpFree(AllocedRecord);
if (IsMetafile())
{
// Account for unbalanced SaveDC/RestoreDC pairs and
// restore to the saveDC state
::RestoreDC(Hdc, SaveDcCount - 1);
}
else
{
::RestoreDC(Hdc, SaveDcVal);
}
}
VOID
WmfEnumState::EndRecord(
)
{
// We rely on the number of bytes enumerated to determine if
// this is the last record of the WMF.
if ((MetafileSize - BytesEnumerated) < SIZEOF_METARECORDHEADER)
{
if (!Globals::IsNt)
{
// GDI won't delete objects that are still selected, so
// select out all WMF objects before proceeding.
::SelectObject(Hdc, HpenSave);
::SelectObject(Hdc, HbrushSave);
::SelectObject(Hdc, HpaletteSave);
::SelectObject(Hdc, HfontSave);
::SelectObject(Hdc, HbitmapSave);
::SelectObject(Hdc, HregionSave);
INT i;
HANDLE handle;
if (HandleTable != NULL)
{
for (i = 0; i < NumObjects; i++)
{
if ((handle = HandleTable->objectHandle[i]) != NULL)
{
::DeleteObject(handle);
HandleTable->objectHandle[i] = NULL;
}
}
}
}
}
}
BOOL
WmfEnumState::PlayRecord(
)
{
const METARECORD * recordToPlay = ModifiedWmfRecord;
// See if we've modified the record
if (recordToPlay == NULL)
{
// We haven't. See if we have a valid current record
if (CurrentWmfRecord != NULL)
{
recordToPlay = CurrentWmfRecord;
}
else
{
// we don't so we have to create one
if (!CreateCopyOfCurrentRecord())
{
return FALSE;
}
recordToPlay = ModifiedWmfRecord;
}
}
return PlayMetaFileRecord(Hdc, HandleTable, (METARECORD *)recordToPlay, NumObjects);
}
INT
MfEnumState::GetModifiedDibSize(
BITMAPINFOHEADER UNALIGNED * dibInfo,
UINT numPalEntries,
UINT dibBitsSize,
UINT & usage
)
{
ASSERT(dibInfo->biSize >= sizeof(BITMAPINFOHEADER));
INT byteWidth;
INT bitCount = dibInfo->biBitCount;
if ((usage == DIB_PAL_COLORS) &&
((bitCount > 8) || (dibInfo->biCompression == BI_BITFIELDS)))
{
usage = DIB_RGB_COLORS;
}
if ((Recolor != NULL) || (usage == DIB_PAL_COLORS))
{
INT biSize = dibInfo->biSize;
if (bitCount > 8)
{
if ((dibInfo->biCompression != BI_RGB) &&
(dibInfo->biCompression != BI_BITFIELDS))
{
return 0; // don't handle compressed images
}
ASSERT((bitCount == 16) || (bitCount == 24) || (bitCount == 32));
INT posHeight = dibInfo->biHeight;
if (posHeight < 0)
{
posHeight = -posHeight;
}
// We have to recolor the object, so we will convert it to a
// 24 bit image and send it down.
// Even if we have less then 256 pixels, it's not worth palettizing
// anymore because the palette will be the size of the image anyway
// make sure that the bitmap is width is aligned
// PERF: We could create a GpBitmap from the bitmap and recolor the
// GpBitmap
dibBitsSize = posHeight * (((dibInfo->biWidth * 3) + 3) & ~3);
numPalEntries = 0;
biSize = sizeof(BITMAPINFOHEADER);
}
else if ((numPalEntries == 0) ||
(dibInfo->biCompression == BI_CMYK) ||
(dibInfo->biCompression == BI_CMYKRLE4) ||
(dibInfo->biCompression == BI_CMYKRLE8))
{
return 0; // don't handle CMYK images
}
usage = DIB_RGB_COLORS;
return biSize + (numPalEntries * sizeof(RGBQUAD)) + dibBitsSize;
}
return 0; // no modifications needed
}
inline static INT
GetMaskShift(
INT maskValue
)
{
ASSERT (maskValue != 0);
INT shift = 0;
while (((maskValue & 1) == 0) && (shift < 24))
{
shift++;
maskValue >>= 1;
}
return shift;
}
inline static INT
GetNumMaskBits(
INT maskValue
)
{
ASSERT ((maskValue & 1) != 0);
INT numBits = 0;
while ((maskValue & 1) != 0)
{
numBits++;
maskValue >>= 1;
}
return numBits;
}
VOID
MfEnumState::Modify16BppDib(
INT width,
INT posHeight,
BYTE * srcPixels,
DWORD UNALIGNED * bitFields,
BYTE * dstPixels,
ColorAdjustType adjustType
)
{
INT rMask = 0x00007C00; // same as GDI default
INT gMask = 0x000003E0;
INT bMask = 0x0000001F;
INT rMaskShift = 10;
INT gMaskShift = 5;
INT bMaskShift = 0;
INT rNumBits = 5;
INT gNumBits = 5;
INT bNumBits = 5;
INT rRightShift = 2;
INT gRightShift = 2;
INT bRightShift = 2;
if (bitFields != NULL)
{
rMask = (INT)((WORD)(*bitFields++));
gMask = (INT)((WORD)(*bitFields++));
bMask = (INT)((WORD)(*bitFields));
rMaskShift = GetMaskShift(rMask);
gMaskShift = GetMaskShift(gMask);
bMaskShift = GetMaskShift(bMask);
rNumBits = GetNumMaskBits(rMask >> rMaskShift);
gNumBits = GetNumMaskBits(gMask >> gMaskShift);
bNumBits = GetNumMaskBits(bMask >> bMaskShift);
rRightShift = (rNumBits << 1) - 8;
gRightShift = (gNumBits << 1) - 8;
bRightShift = (bNumBits << 1) - 8;
}
INT palIndex = 0;
INT pixel;
INT r, g, b;
COLORREF color;
INT w, h;
INT srcByteWidth = ((width * 2) + 3) & (~3);
INT dstByteWidth = ((width * 3) + 3) & (~3);
for (h = 0; h < posHeight; h++)
{
for (w = 0; w < width; w++)
{
pixel = (INT)(((INT16 *)srcPixels)[w]);
r = (pixel & rMask) >> rMaskShift;
r = (r | (r << rNumBits)) >> rRightShift;
g = (pixel & gMask) >> gMaskShift;
g = (g | (g << gNumBits)) >> gRightShift;
b = (pixel & bMask) >> bMaskShift;
b = (b | (b << bNumBits)) >> bRightShift;
color = ModifyColor(RGB(r, g, b), adjustType);
dstPixels[3*w + 2] = GetRValue(color);
dstPixels[3*w + 1] = GetGValue(color);
dstPixels[3*w] = GetBValue(color);
}
srcPixels += srcByteWidth;
dstPixels += dstByteWidth;
}
}
inline static INT
Get24BppColorIndex(
INT maskValue
)
{
switch(GetMaskShift(maskValue))
{
default:
WARNING(("Invalid BitFields Mask"));
// FALLTHRU
case 0:
return 0;
case 8:
return 1;
case 16:
return 2;
}
}
VOID
MfEnumState::Modify24BppDib(
INT width,
INT posHeight,
BYTE * srcPixels,
DWORD UNALIGNED * bitFields,
BYTE * dstPixels,
ColorAdjustType adjustType
)
{
INT rIndex = 2;
INT gIndex = 1;
INT bIndex = 0;
if (bitFields != NULL)
{
INT rMask = (INT)((*bitFields++));
INT gMask = (INT)((*bitFields++));
INT bMask = (INT)((*bitFields));
rIndex = Get24BppColorIndex(rMask);
gIndex = Get24BppColorIndex(gMask);
bIndex = Get24BppColorIndex(bMask);
}
INT palIndex = 0;
INT r, g, b;
COLORREF color;
INT w, h;
INT srcByteWidth = ((width * 3) + 3) & (~3);
INT dstByteWidth = ((width * 3) + 3) & (~3);
BYTE * srcRaster = srcPixels;
for (h = 0; h < posHeight; h++)
{
srcPixels = srcRaster;
for (w = 0; w < width; w++)
{
r = srcPixels[rIndex];
g = srcPixels[gIndex];
b = srcPixels[bIndex];
srcPixels += 3;
color = ModifyColor(RGB(r, g, b), adjustType);
dstPixels[3*w + 2] = GetRValue(color);
dstPixels[3*w + 1] = GetGValue(color);
dstPixels[3*w] = GetBValue(color);
}
srcRaster += srcByteWidth;
dstPixels += dstByteWidth;
}
}
inline static INT
Get32BppColorIndex(
INT maskValue
)
{
switch(GetMaskShift(maskValue))
{
default:
WARNING(("Invalid BitFields Mask"));
// FALLTHRU
case 0:
return 0;
case 8:
return 1;
case 16:
return 2;
case 24:
return 3;
}
}
VOID
MfEnumState::Modify32BppDib(
INT width,
INT posHeight,
BYTE * srcPixels,
DWORD UNALIGNED * bitFields,
BYTE * dstPixels,
ColorAdjustType adjustType
)
{
INT rIndex = 2;
INT gIndex = 1;
INT bIndex = 0;
if (bitFields != NULL)
{
INT rMask = (INT)((*bitFields++));
INT gMask = (INT)((*bitFields++));
INT bMask = (INT)((*bitFields));
rIndex = Get32BppColorIndex(rMask);
gIndex = Get32BppColorIndex(gMask);
bIndex = Get32BppColorIndex(bMask);
}
INT palIndex = 0;
INT r, g, b;
COLORREF color;
INT w, h;
INT dstByteWidth = ((width * 3) + 3) & (~3);
for (h = 0; h < posHeight; h++)
{
for (w = 0; w < width; w++)
{
r = srcPixels[rIndex];
g = srcPixels[gIndex];
b = srcPixels[bIndex];
srcPixels += 4;
color = ModifyColor(RGB(r, g, b), adjustType);
dstPixels[3*w + 2] = GetRValue(color);
dstPixels[3*w + 1] = GetGValue(color);
dstPixels[3*w] = GetBValue(color);
}
dstPixels += dstByteWidth;
}
}
VOID
MfEnumState::ModifyDib(
UINT usage,
BITMAPINFOHEADER UNALIGNED * srcDibInfo,
BYTE * srcBits, // if NULL, it's a packed DIB
BITMAPINFOHEADER UNALIGNED * dstDibInfo,
UINT numPalEntries,
UINT srcDibBitsSize,
ColorAdjustType adjustType
)
{
INT srcBitCount = srcDibInfo->biBitCount;
BYTE * srcPixels = srcBits;
COLORREF color;
if (srcBitCount <= 8)
{
GpMemcpy(dstDibInfo, srcDibInfo, srcDibInfo->biSize);
RGBQUAD UNALIGNED * srcRgb = GetDibColorTable(srcDibInfo);
RGBQUAD UNALIGNED * dstRgb = GetDibColorTable(dstDibInfo);
dstDibInfo->biClrUsed = numPalEntries;
if ((usage == DIB_PAL_COLORS) &&
(dstDibInfo->biCompression != BI_BITFIELDS))
{
WORD * srcPal = (WORD *)srcRgb;
if (srcPixels == NULL)
{
srcPixels = (BYTE *)(srcPal + ((numPalEntries + 1) & ~1)); // align
}
// Copy the Dib pixel data
GpMemcpy(dstRgb + numPalEntries, srcPixels, srcDibBitsSize);
// Modify the palette colors
while (numPalEntries--)
{
color = ModifyColor(*srcPal++ | 0x01000000, adjustType);
dstRgb->rgbRed = GetRValue(color);
dstRgb->rgbGreen = GetGValue(color);
dstRgb->rgbBlue = GetBValue(color);
dstRgb->rgbReserved = 0;
dstRgb++;
}
}
else
{
if (srcPixels == NULL)
{
srcPixels = (BYTE *)(srcRgb + numPalEntries);
}
// Copy the Dib pixel data
GpMemcpy(dstRgb + numPalEntries, srcPixels, srcDibBitsSize);
// Modify the palette colors
while (numPalEntries--)
{
color = ModifyColor(RGB(srcRgb->rgbRed, srcRgb->rgbGreen, srcRgb->rgbBlue), adjustType);
dstRgb->rgbRed = GetRValue(color);
dstRgb->rgbGreen = GetGValue(color);
dstRgb->rgbBlue = GetBValue(color);
dstRgb->rgbReserved = 0;
dstRgb++;
srcRgb++;
}
}
}
else // Recolor the bitmap. There is no need to palettize the image since
// the palette will be as big as the image
{
INT posHeight = srcDibInfo->biHeight;
if (posHeight < 0)
{
posHeight = -posHeight;
}
ASSERT((srcDibInfo->biCompression == BI_RGB) ||
(srcDibInfo->biCompression == BI_BITFIELDS));
GpMemset(dstDibInfo, 0, sizeof(BITMAPINFOHEADER));
dstDibInfo->biSize = sizeof(BITMAPINFOHEADER);
dstDibInfo->biWidth = srcDibInfo->biWidth;
dstDibInfo->biHeight = srcDibInfo->biHeight;
dstDibInfo->biPlanes = 1;
dstDibInfo->biBitCount = 24;
BYTE * dstPixels = GetDibBits(dstDibInfo,0,0);
DWORD UNALIGNED * bitFields = NULL;
if (srcPixels == NULL)
{
srcPixels = (BYTE *)GetDibBits(srcDibInfo, numPalEntries, usage);
}
dstDibInfo->biClrUsed = 0;
dstDibInfo->biClrImportant = 0;
if (numPalEntries == 3)
{
ASSERT((srcBitCount == 16) || (srcBitCount == 32));
bitFields = (DWORD*) GetDibColorTable(srcDibInfo);
if ((bitFields[0] == 0) ||
(bitFields[1] == 0) ||
(bitFields[2] == 0))
{
bitFields = NULL;
}
}
else if (srcDibInfo->biSize >= sizeof(BITMAPV4HEADER))
{
BITMAPV4HEADER * srcHeaderV4 = (BITMAPV4HEADER *)srcDibInfo;
if ((srcHeaderV4->bV4RedMask != 0) &&
(srcHeaderV4->bV4GreenMask != 0) &&
(srcHeaderV4->bV4BlueMask != 0))
{
bitFields = &(srcHeaderV4->bV4RedMask);
}
}
switch (srcBitCount)
{
case 16:
Modify16BppDib(srcDibInfo->biWidth, posHeight, srcPixels,
bitFields, dstPixels, adjustType);
break;
case 24:
Modify24BppDib(srcDibInfo->biWidth, posHeight, srcPixels,
bitFields, dstPixels, adjustType);
break;
case 32:
Modify32BppDib(srcDibInfo->biWidth, posHeight, srcPixels,
bitFields, dstPixels, adjustType);
break;
}
}
}
VOID
WmfEnumState::DibCreatePatternBrush(
)
{
INT style = (INT)((INT16)(((WORD *)RecordData)[0]));
UINT usage = (UINT)((UINT16)(((WORD *)RecordData)[1]));
BITMAPINFOHEADER UNALIGNED * srcDibInfo = (BITMAPINFOHEADER UNALIGNED *)(&(((WORD *)RecordData)[2]));
UINT numPalEntries;
// Pattern brush should mean that it is a monochrome DIB
if (style == BS_PATTERN)
{
if (Recolor != NULL)
{
DWORD UNALIGNED * rgb = (DWORD UNALIGNED *)GetDibColorTable(srcDibInfo);
// See if it is a monochrome pattern brush. If it is, then
// the text color will be used for 0 bits and the background
// color will be used for 1 bits. These colors are already
// modified by their respective records, so there is no need
// to do anything here.
// If it is not a monochrome pattern brush, just create a
// solid black brush.
if ((usage != DIB_RGB_COLORS) ||
(srcDibInfo->biSize < sizeof(BITMAPINFOHEADER)) ||
!GetDibNumPalEntries(TRUE,
srcDibInfo->biSize,
srcDibInfo->biBitCount,
srcDibInfo->biCompression,
srcDibInfo->biClrUsed,
&numPalEntries) ||
(numPalEntries != 2) ||
(srcDibInfo->biBitCount != 1) || (srcDibInfo->biPlanes != 1) ||
(rgb[0] != 0x00000000) || (rgb[1] != 0x00FFFFFF))
{
// This shouldn't happen, at least not if recorded on NT
WARNING(("Non-monochrome pattern brush"));
MakeSolidBlackBrush();
}
}
}
else
{
UINT dibBitsSize;
if ((srcDibInfo->biSize >= sizeof(BITMAPINFOHEADER)) &&
GetDibNumPalEntries(TRUE,
srcDibInfo->biSize,
srcDibInfo->biBitCount,
srcDibInfo->biCompression,
srcDibInfo->biClrUsed,
&numPalEntries) &&
((dibBitsSize = GetDibBitsSize(srcDibInfo)) > 0))
{
UINT oldUsage = usage;
INT dstDibSize = GetModifiedDibSize(srcDibInfo, numPalEntries, dibBitsSize, usage);
if (dstDibSize > 0)
{
INT size = SIZEOF_METARECORDHEADER + (2 * sizeof(WORD)) + dstDibSize;
CreateRecordToModify(size);
ModifiedWmfRecord->rdSize = size / 2;
ModifiedWmfRecord->rdFunction = META_DIBCREATEPATTERNBRUSH;
ModifiedWmfRecord->rdParm[0] = BS_DIBPATTERN;
ModifiedWmfRecord->rdParm[1] = DIB_RGB_COLORS;
ModifyDib(oldUsage, srcDibInfo, NULL,
(BITMAPINFOHEADER UNALIGNED *)(&(ModifiedWmfRecord->rdParm[2])),
numPalEntries, dibBitsSize, ColorAdjustTypeBrush);
}
}
}
this->PlayRecord();
}
// This record is obsolete, because it uses a compatible bitmap
// instead of a DIB. It has a BITMAP16 structure that is
// used to call CreateBitmapIndirect. That HBITMAP is, in turn,
// used to call CreatePatternBrush. If this record is present,
// it is likely that the bitmap is monochrome, in which case
// the TextColor and the BkColor will be used, and these colors
// already get modified by their respective records.
VOID
WmfEnumState::CreatePatternBrush(
)
{
WARNING(("Obsolete META_CREATEPATTERNBRUSH record"));
BITMAP16 UNALIGNED * bitmap = (BITMAP16 UNALIGNED *)RecordData;
if (bitmap->bmBitsPixel != 1)
{
WARNING(("Non-monochrome pattern brush"));
MakeSolidBlackBrush();
}
this->PlayRecord();
}
VOID
WmfEnumState::CreatePenIndirect(
)
{
LOGPEN16 UNALIGNED * logPen = (LOGPEN16 UNALIGNED *)RecordData;
switch (logPen->lopnStyle)
{
default:
WARNING(("Unrecognized Pen Style"));
case PS_NULL:
break; // leave the pen alone
case PS_SOLID:
case PS_INSIDEFRAME:
case PS_DASH:
case PS_DOT:
case PS_DASHDOT:
case PS_DASHDOTDOT:
ModifyRecordColor(3, ColorAdjustTypePen);
break;
}
this->PlayRecord();
}
VOID
WmfEnumState::CreateBrushIndirect(
)
{
LOGBRUSH16 UNALIGNED * logBrush = (LOGBRUSH16 UNALIGNED *)RecordData;
switch (logBrush->lbStyle)
{
case BS_SOLID:
case BS_HATCHED:
{
ModifyRecordColor(1, ColorAdjustTypeBrush);
if (ModifiedWmfRecord != NULL)
{
logBrush = (LOGBRUSH16 UNALIGNED *)(ModifiedWmfRecord->rdParm);
}
// See if we need to halftone the color. We do if it is a solid
// color, and we have a halftone palette, and the color is not
// an exact match in the palette.
COLORREF color;
if (IsHalftonePalette && (logBrush->lbStyle == BS_SOLID) &&
(((color = logBrush->lbColor) & 0x02000000) == 0))
{
// create a halftone brush, instead of a solid brush
INT size = SIZEOF_METARECORDHEADER + (2 * sizeof(WORD)) +
sizeof(BITMAPINFOHEADER) + // DIB 8 bpp header
(8 * sizeof(RGBQUAD)) + // DIB 8 colors
(8 * 8); // DIB 8x8 pixels
ModifiedRecordSize = 0; // in case we already modified the record
CreateRecordToModify(size);
ModifiedWmfRecord->rdSize = size / 2;
ModifiedWmfRecord->rdFunction = META_DIBCREATEPATTERNBRUSH;
ModifiedWmfRecord->rdParm[0] = BS_DIBPATTERN;
ModifiedWmfRecord->rdParm[1] = DIB_RGB_COLORS;
HalftoneColorRef_216(color, &(ModifiedWmfRecord->rdParm[2]));
}
}
break;
case BS_HOLLOW:
break; // leave the record alone
default:
// Looking at the NT source code, there shouldn't be any
// other brush styles for an indirect brush.
WARNING(("Brush Style Not Valid"));
MakeSolidBlackBrush();
break;
}
this->PlayRecord();
}
// Also handles StretchBlt.
// These records are obsolete (when there is a source bitmap) because they
// have a compatible bitmap instead of a DIB. For that reason, we don't
// recolor them.
VOID
WmfEnumState::BitBlt(
)
{
DWORD rop = *((UNALIGNED DWORD *)RecordData);
// If No-Op ROP, do nothing; just return
if ((rop & 0xFFFF0000) == (GDIP_NOOP_ROP3 & 0xFFFF0000))
{
return;
}
if (!IsMetafile())
{
if (IsSourceInRop3(rop))
{
WARNING(("Obsolete META_BITBLT/META_STRETCHBLT record"));
if ((rop != SRCCOPY) && SrcCopyOnly &&
CreateCopyOfCurrentRecord())
{
*((DWORD UNALIGNED *)ModifiedWmfRecord->rdParm) = SRCCOPY;
}
}
else
{
if ((rop != PATCOPY) && SrcCopyOnly &&
CreateCopyOfCurrentRecord())
{
*((DWORD UNALIGNED *)ModifiedWmfRecord->rdParm) = PATCOPY;
}
}
}
this->PlayRecord();
}
VOID
WmfEnumState::Escape(
)
{
INT escapeCode = (INT)((INT16)(((WORD *)RecordData)[0]));
if (!IsPostscript())
{
if (SkipEscape(escapeCode))
{
return;
}
// Skip Office Art data when playing into another metafile
if (IsMetafile() &&
IsOfficeArtData(GetCurrentRecordSize(), (WORD *)RecordData))
{
return;
}
}
else // it is postscript
{
if (escapeCode == MFCOMMENT)
{
if (GetWmfComment((WORD *)RecordData, msosignature, msocommentBeginSrcCopy))
{
SrcCopyOnly = TRUE;
return;
}
if (GetWmfComment((WORD *)RecordData, msosignature, msocommentEndSrcCopy))
{
SrcCopyOnly = FALSE;
return;
}
}
if (escapeCode == POSTSCRIPT_DATA)
{
// Bug #98743 (Windows Bugs) Gdiplus must overcome GDI limitation
// with POSTSCRIPT_INJECTION. Comments from Rammanohar Arumugam:
//
// Being in xx-centric mode means POSTSCRIPT_DATA won't work. I
// take that to mean that PlayMetaFileRecord only works in
// compatibility mode.
//
// GdiPlus will check for the printer mode. In GDI-centric and
// Postscript-centric mode, it will not do PlayMetaFileRecord for
// any record that has POSTSCRIPT_DATA. Instead, it will output
// the postscript data through a PASSTHRU (for GDI-centric mode)
// or a POSTSCRIPT_PASSTHRU (for Postscript-Centric mode).
//
// You can find out the mode by querying the escape support.
// 1. Query for POSTSCRIPT_INJECTION support. If not supported,
// it's compat mode. If supported, find out the mode by doing step 2/3
// 2. Query for PASSTHROUGH support. If supported, it's GDI-centric.
// 3. Query for POSTSCRIPT_PASSTHROUGH support. If supported, it's
// PS-centric.
if (Globals::IsNt)
{
if (!SoftekFilter)
{
// Determine presence of Softek Filter EPS, if so, then
// we apply workaround patches.
WORD size = *((WORD*)RecordData);
LPSTR escape = (LPSTR)(&RecordData[6]);
const LPSTR softekString = "%MSEPS Preamble [Softek";
INT softekLen = strlen(softekString);
if (size >= softekLen)
{
SoftekFilter = !GpMemcmp(softekString, escape, softekLen);
}
}
DWORD EscapeValue = POSTSCRIPT_IDENTIFY;
if (::ExtEscape(Hdc,
QUERYESCSUPPORT,
sizeof(DWORD),
(LPSTR)&EscapeValue,
0,
NULL) <= 0)
{
// POSTSCRIPT_IDENTITY is not supported if the mode has
// been set because it can only be set once.
EscapeValue = POSTSCRIPT_PASSTHROUGH;
if (::ExtEscape(Hdc,
QUERYESCSUPPORT,
sizeof(DWORD),
(LPSTR)&EscapeValue,
0,
NULL) <= 0)
{
// GDI-centric mode
if (CreateCopyOfCurrentRecord())
{
*((WORD *)ModifiedWmfRecord->rdParm) = PASSTHROUGH;
}
GdiCentricMode = TRUE;
}
else
{
// PS-centric mode
if (CreateCopyOfCurrentRecord())
{
*((WORD *)ModifiedWmfRecord->rdParm) = POSTSCRIPT_PASSTHROUGH;
}
}
this->PlayRecord();
return;
}
else
{
// compatibility mode, uses POSTSCRIPT_DATA
}
}
else
{
// Win98 doesn't distinguish between GDI & compatibility mode
if (CreateCopyOfCurrentRecord())
{
*((WORD *)ModifiedWmfRecord->rdParm) = PASSTHROUGH;
}
}
}
}
// Keep track of the POSTSCRIPT_IGNORE state. If it is still on at
// the of the metafile, then turn it OFF
if (escapeCode == POSTSCRIPT_IGNORE && IsPostscript())
{
IgnorePostscript = ((WORD *)RecordData)[2] ? TRUE : FALSE;
}
this->PlayRecord();
}
VOID
WmfEnumState::Rectangle(
)
{
if (FsmState == MfFsmSetROP)
{
// There is a bug using PlayMetaFileRecord on Win2K for this
// type of escape, we must explicitly call ExtEscape. See bug
// #98743.
WORD* rdParm = (WORD*)&(RecordData[0]);
CHAR postscriptEscape[512];
RECT rect;
rect.left = (SHORT)rdParm[3];
rect.top = (SHORT)rdParm[2];
rect.right = (SHORT)rdParm[1];
rect.bottom = (SHORT)rdParm[0];
if (LPtoDP(Hdc, (POINT*)&rect, 2))
{
// Some injected postscript, strangely enough contains the equivalent
// of a stroke which is erroroneously executed on the current path. In
// one case, bug #281856 it results in a border about the object. To
// get around this, we output a 'N' which is newpath operator if it's
// defined (which should be always.) This, incidently, is done when
// calling GDI Rectangle() succeeds, it outputs "N x y w h B", so we
// are doing the equivalent here.
GpRect clipRect;
Context->VisibleClip.GetBounds(&clipRect);
wsprintfA(&postscriptEscape[2],
"\r\n%d %d %d %d CB\r\n"
"%s"
"%d %d %d %d B\r\n",
clipRect.Width,
clipRect.Height,
clipRect.X,
clipRect.Y,
Globals::IsNt ? "newpath\r\n" : "",
rect.right - rect.left,
rect.bottom - rect.top,
rect.left,
rect.top);
ASSERT(strlen(&postscriptEscape[2]) < 512);
*(WORD*)(&postscriptEscape[0]) = (WORD)(strlen(&postscriptEscape[2]));
::ExtEscape(Hdc,
PASSTHROUGH,
*(WORD*)(&postscriptEscape[0]) + sizeof(WORD) + 1,
(CHAR*)&postscriptEscape[0],
0,
NULL);
return;
}
}
this->PlayRecord();
}
VOID
WmfEnumState::RestoreHdc(
)
{
INT relativeCount = (INT)((INT16)(((WORD *)RecordData)[0]));
if (SaveDcCount < 0)
{
if (relativeCount >= SaveDcCount)
{
if (relativeCount >= 0)
{
// Modify the record
CreateCopyOfCurrentRecord(); // guaranteed to succeed
relativeCount = -1;
ModifiedWmfRecord->rdParm[0] = (INT16)(-1);
}
}
else
{
// Modify the record
CreateCopyOfCurrentRecord(); // guaranteed to succeed
relativeCount = SaveDcCount;
ModifiedWmfRecord->rdParm[0] = (INT16)(relativeCount);
}
SaveDcCount -= relativeCount;
this->PlayRecord();
}
else
{
WARNING(("RestoreDC not matched to a SaveDC"));
}
}
// The rop for this command is always SRCCOPY
VOID
WmfEnumState::SetDIBitsToDevice(
)
{
// !!!
// Office doesn't do anything with this record. For now, I don't think
// I will either. It's a tough one to deal with for a couple reasons:
// 1st - The xDest and yDest values are in world units, but the
// width and height values are in device units
// (unlike StretchDIBits).
// 2nd - The amount of bits data present may be different than
// what is in the DIB header (based on the cScanLines param).
// This makes it harder to deal with as a packed DIB.
this->PlayRecord();
}
VOID
MfEnumState::SelectPalette(
INT objectIndex
)
{
// For EMF the check really should be > 0
if ((objectIndex >= 0) && (objectIndex < NumObjects) && (HandleTable != NULL))
{
HGDIOBJ hPal = HandleTable->objectHandle[objectIndex];
if ((hPal != NULL) && (GetObjectTypeInternal(hPal) == OBJ_PAL))
{
CurrentPalette = (HPALETTE)hPal;
return;
}
}
WARNING(("SelectPalette Failure"));
}
inline static VOID
Point32FromPoint16(
POINTL * dstPoints,
POINTS UNALIGNED * srcPoints,
UINT numPoints
)
{
for (UINT i = 0; i < numPoints; i++, dstPoints++, srcPoints++)
{
dstPoints->x = (INT)((INT16)(srcPoints->x));
dstPoints->y = (INT)((INT16)(srcPoints->y));
}
}
// Apparently there is a bug on Win9x with PolyPolygons, so we
// parse the record ourselves.
VOID
WmfEnumState::PolyPolygon(
)
{
UINT numPolygons = ((WORD *)RecordData)[0];
UINT numPoints = 0;
UINT i;
for (i = 0; i < numPolygons; i++)
{
numPoints += ((LPWORD)&((WORD *)RecordData)[1])[i];
}
INT * polyCounts;
POINTL * points;
INT size = (numPolygons * sizeof(INT)) +
(numPoints * sizeof(POINTL));
if (CreateRecordToModify(size))
{
polyCounts = (INT *)ModifiedRecord;
points = (POINTL *)(polyCounts + numPolygons);
for (i = 0; i < numPolygons; i++)
{
polyCounts[i] = (INT)(UINT)((LPWORD)&((WORD *)RecordData)[1])[i];
}
Point32FromPoint16(points,
(POINTS UNALIGNED *)(((WORD *)RecordData) + numPolygons + 1),
numPoints);
::PolyPolygon(Hdc, (POINT *)points, polyCounts, numPolygons);
return;
}
this->PlayRecord();
}
#ifdef NEED_TO_KNOW_IF_BITMAP
static INT
GetBppFromMemDC(
HDC hMemDC
)
{
HBITMAP hBitmap = (HBITMAP)::GetCurrentObject(hMemDC, OBJ_BITMAP);
BITMAP bitmap;
if ((hBitmap == NULL) ||
(::GetObjectA(hBitmap, sizeof(bitmap), &bitmap) == 0))
{
WARNING(("Couldn't get Bitmap object"));
return 0; // error
}
if (bitmap.bmPlanes <= 0)
{
WARNING(("Bitmap with no planes"));
bitmap.bmPlanes = 1;
}
INT bpp = bitmap.bmPlanes * bitmap.bmBitsPixel;
if (bpp > 32)
{
WARNING(("Bitmap with too many bits"));
bpp = 32;
}
return bpp;
}
#endif
#define GDI_INTERPOLATION_MAX (1 << 23)
VOID
MfEnumState::OutputDIB(
HDC hdc,
const RECTL * bounds,
INT dstX,
INT dstY,
INT dstWidth,
INT dstHeight,
INT srcX,
INT srcY,
INT srcWidth,
INT srcHeight,
BITMAPINFOHEADER UNALIGNED * dibInfo,
BYTE * bits, // if NULL, this is a packed DIB
UINT usage,
DWORD rop,
BOOL isWmfDib
)
{
BITMAPINFO dibHeaderBuffer[1]; // To be sure it's aligned for 64Bits
BOOL restoreColors = FALSE;
COLORREF oldBkColor;
COLORREF oldTextColor;
ASSERT(dibInfo->biSize >= sizeof(BITMAPINFOHEADER));
if (bits == NULL)
{
UINT numPalEntries;
if (GetDibNumPalEntries(isWmfDib,
dibInfo->biSize,
dibInfo->biBitCount,
dibInfo->biCompression,
dibInfo->biClrUsed,
&numPalEntries))
{
bits = GetDibBits(dibInfo, numPalEntries, usage);
}
else
{
WARNING(("GetDibNumPalEntries failure"));
return;
}
}
INT posDstWidth = dstWidth;
if (posDstWidth < 0)
{
posDstWidth = -posDstWidth;
}
INT posDstHeight = dstHeight;
if (posDstHeight < 0)
{
posDstHeight = -posDstHeight;
}
INT stretchBltMode = HALFTONE;
GpBitmap * destBitmap = NULL;
POINT destPoints[3];
BitmapData bmpData;
BitmapData * bmpDataPtr = NULL;
HBITMAP hBitmap = NULL;
BYTE * bmpBits = NULL;
BITMAPINFO * dibBmpInfo = NULL;
BOOL deleteDIBSection = FALSE;
// Don't use GDI+ stretching for a mask
// Make this the first thing so that we are sure that they are set
if ((dibInfo->biBitCount == 1) && (rop != SRCCOPY))
{
oldBkColor = ::SetBkColor(hdc, BkColor);
oldTextColor = ::SetTextColor(hdc, TextColor);
restoreColors = TRUE;
goto DoGdiStretch;
}
// On Win9x we need to create an play a comment record so that the transform
// gets invalidated and recalculated
if (!Globals::IsNt && !IsMetafile())
{
CreateAndPlayCommentRecord();
}
destPoints[0].x = dstX;
destPoints[0].y = dstY;
destPoints[1].x = dstX + posDstWidth;
destPoints[1].y = dstY;
destPoints[2].x = dstX;
destPoints[2].y = dstY + posDstHeight;
if (!::LPtoDP(hdc, destPoints, 3))
{
goto DoGdiStretch;
}
posDstWidth = ::GetIntDistance(destPoints[0], destPoints[1]);
posDstHeight = ::GetIntDistance(destPoints[0], destPoints[2]);
if ((posDstWidth == 0) || (posDstHeight == 0))
{
return;
}
INT posSrcWidth;
INT srcWidthSign;
posSrcWidth = srcWidth;
srcWidthSign = 1;
if (posSrcWidth < 0)
{
posSrcWidth = -posSrcWidth;
srcWidthSign = -1;
}
INT posSrcHeight;
INT srcHeightSign;
posSrcHeight = srcHeight;
srcHeightSign = 1;
if (posSrcHeight < 0)
{
posSrcHeight = -posSrcHeight;
srcHeightSign = -1;
}
INT posSrcDibWidth;
posSrcDibWidth = dibInfo->biWidth;
if (posSrcDibWidth <= 0)
{
WARNING(("Bad biWidth value"));
return; // negative source dib width not allowed
}
INT posSrcDibHeight;
posSrcDibHeight = dibInfo->biHeight;
if (posSrcDibHeight < 0)
{
posSrcDibHeight = -posSrcDibHeight;
}
// We can have a negative source Width or height
// we need to verify that the two corners of the srcRect lie in the
// bitmap bounds
if (srcX < 0)
{
srcX = 0;
WARNING(("srcX < 0"));
}
if (srcX > posSrcDibWidth)
{
WARNING(("Bad srcWidth or srcX value"));
srcX = posSrcDibWidth;
}
if (srcY < 0)
{
srcY = 0;
WARNING(("srcY < 0"));
}
if (srcY > posSrcDibHeight)
{
WARNING(("Bad srcWidth or srcX value"));
srcY = posSrcDibHeight;
}
INT srcRight;
srcRight = srcX + srcWidth;
if (srcRight < 0)
{
WARNING(("Bad srcWidth or srcX value"));
srcWidth = -srcX;
}
if(srcRight > posSrcDibWidth)
{
WARNING(("Bad srcWidth or srcX value"));
srcWidth = posSrcDibWidth - srcX;
}
INT srcBottom;
srcBottom = srcY + srcHeight;
if (srcBottom < 0)
{
WARNING(("Bad srcWidth or srcX value"));
srcHeight = -srcY;
}
if (srcBottom > posSrcDibHeight)
{
WARNING(("Bad srcWidth or srcX value"));
srcHeight = posSrcDibHeight - srcY;
}
// This also catches the case where
// (posSrcDibWidth == 0) || (posSrcDibHeight == 0)
if ((posSrcWidth <= 0) || (posSrcHeight <= 0))
{
return;
}
// If we are drawing into an 8Bpp surface and we have a different ROP then
// srcCopy.
if (Is8Bpp && rop != SRCCOPY)
{
BOOL freeDibInfo = FALSE;
UINT size;
BITMAPINFO * alignedDibInfo = NULL;
if (GetDibNumPalEntries(TRUE,
dibInfo->biSize,
dibInfo->biBitCount,
dibInfo->biCompression,
dibInfo->biClrUsed,
&size))
{
if (IsDwordAligned(dibInfo))
{
alignedDibInfo = (BITMAPINFO*) dibInfo;
}
else
{
// Mutliply the number of entries by the size of each entry
size *= ((usage==DIB_RGB_COLORS)?sizeof(RGBQUAD):sizeof(WORD));
// WMF's can't use System Palette
alignedDibInfo = (BITMAPINFO*) GpMalloc(dibInfo->biSize + size);
if (alignedDibInfo != NULL)
{
memcpy((void*)&alignedDibInfo, dibInfo, dibInfo->biSize + size);
freeDibInfo = TRUE;
}
}
if (alignedDibInfo != NULL)
{
if (GpBitmap::DrawAndHalftoneForStretchBlt(hdc, alignedDibInfo, bits, srcX, srcY,
posSrcWidth, posSrcHeight,
posDstWidth, posDstHeight,
&dibBmpInfo, &bmpBits, &hBitmap,
Interpolation) == Ok)
{
deleteDIBSection = TRUE;
srcX = 0;
srcY = 0;
srcWidth = posDstWidth;
srcHeight = posDstHeight;
dibInfo = (BITMAPINFOHEADER*) dibBmpInfo;
bits = bmpBits;
if (freeDibInfo)
{
GpFree(alignedDibInfo);
}
goto DoGdiStretch;
}
if (freeDibInfo)
{
GpFree(alignedDibInfo);
}
}
}
}
// if not stretching, let GDI do the blt
if ((posSrcWidth == posDstWidth) && (posSrcHeight == posDstHeight))
{
goto DoGdiStretch;
}
InterpolationMode interpolationMode;
interpolationMode = Interpolation;
// if not going to the screen or to a bitmap, use GDI to do the stretch
// otherwise, use GDI+ to do the stretch (but let GDI do the blt)
// if going to printer on Win98 and it is RLE8 compressed bitmap, then
// always decode and blit from GDI+. The reason is that print drivers
// commonly don't support RLEx encoding and punt to GDI. In this
// context it creates a compatible printer dc and bitmap and then does a
// StretchBlt, but it only does this at 1bpp, the result is black and white.
if ((IsPrinter() && !Globals::IsNt &&
dibInfo->biCompression == BI_RLE8) ||
((IsScreen() || IsBitmap()) &&
(interpolationMode != InterpolationModeNearestNeighbor) &&
(posSrcWidth > 1) && (posSrcHeight > 1) &&
((posDstWidth * posDstHeight) < GDI_INTERPOLATION_MAX)))
{
GpStatus status = GenericError;
destBitmap = new GpBitmap(posDstWidth, posDstHeight, PIXFMT_24BPP_RGB);
if (destBitmap != NULL)
{
if (destBitmap->IsValid())
{
BITMAPINFO * alignedDibInfo = NULL;
UINT size;
BOOL freeDibInfo = FALSE;
if (GetDibNumPalEntries(TRUE,
dibInfo->biSize,
dibInfo->biBitCount,
dibInfo->biCompression,
dibInfo->biClrUsed,
&size))
{
if (IsDwordAligned(dibInfo))
{
alignedDibInfo = (BITMAPINFO*) dibInfo;
}
else
{
// Mutliply the number of entries by the size of each entry
size *= ((usage==DIB_RGB_COLORS)?sizeof(RGBQUAD):sizeof(WORD));
// WMF's can't use System Palette
alignedDibInfo = (BITMAPINFO*) GpMalloc(dibInfo->biSize + size);
if (alignedDibInfo != NULL)
{
memcpy((void*)&alignedDibInfo, dibInfo, dibInfo->biSize + size);
freeDibInfo = TRUE;
}
}
if (alignedDibInfo != NULL)
{
GpBitmap * srcBitmap = new GpBitmap(alignedDibInfo,
bits, FALSE);
if (srcBitmap != NULL)
{
if (srcBitmap->IsValid())
{
GpGraphics * destGraphics = destBitmap->GetGraphicsContext();
if (destGraphics != NULL)
{
if (destGraphics->IsValid())
{
// we have to lock the graphics so the driver doesn't assert
GpLock lockGraphics(destGraphics->GetObjectLock());
ASSERT(lockGraphics.IsValid());
GpRectF dstRect(0.0f, 0.0f, (REAL)posDstWidth, (REAL)posDstHeight);
GpRectF srcRect;
// StretchDIBits takes a srcY parameter relative to the lower-left
// (bottom) corner of the bitmap, not the top-left corner,
// like DrawImage does
srcRect.Y = (REAL)(posSrcDibHeight - srcY - srcHeight);
srcRect.X = (REAL)srcX;
// !!! We have to subtract one to keep the
// filter from blending black into the image
// on the right and bottom.
srcRect.Width = (REAL)(srcWidth - (srcWidthSign));
srcRect.Height = (REAL)(srcHeight - (srcHeightSign));
// don't do any blending as part of the stretch
destGraphics->SetCompositingMode(CompositingModeSourceCopy);
destGraphics->SetInterpolationMode(interpolationMode);
// Set the image attributes to Wrap since we don't want to
// use black pixels for the edges
GpImageAttributes imgAttr;
imgAttr.SetWrapMode(WrapModeTileFlipXY);
// now draw the source into the dest bitmap
status = destGraphics->DrawImage(srcBitmap,
dstRect,
srcRect,
UnitPixel,
&imgAttr);
}
else
{
WARNING(("destGraphics not valid"));
}
delete destGraphics;
}
else
{
WARNING(("Could not construct destGraphics"));
}
}
else
{
WARNING(("srcGraphics not valid"));
}
srcBitmap->Dispose(); // doesn't delete the source data
}
else
{
WARNING(("Could not allocate a new BitmapInfoHeader"));
}
if (freeDibInfo)
{
GpFree(alignedDibInfo);
}
}
else
{
WARNING(("Could not construct srcGraphics"));
}
}
else
{
WARNING(("Could not clone the bitmap header"));
}
if ((status == Ok) &&
(destBitmap->LockBits(NULL, IMGLOCK_READ, PIXFMT_24BPP_RGB,
&bmpData) == Ok))
{
ASSERT((bmpData.Stride & 3) == 0);
GpMemset(dibHeaderBuffer, 0, sizeof(BITMAPINFO));
bmpDataPtr = &bmpData;
bits = (BYTE *)bmpData.Scan0;
srcX = 0;
srcY = 0;
srcWidth = posDstWidth;
srcHeight = posDstHeight;
usage = DIB_RGB_COLORS;
dibInfo = (BITMAPINFOHEADER *)dibHeaderBuffer;
dibInfo->biSize = sizeof(BITMAPINFOHEADER);
dibInfo->biWidth = posDstWidth;
dibInfo->biHeight = -posDstHeight;
dibInfo->biPlanes = 1;
dibInfo->biBitCount = 24;
// We don't want to set the StretchBltMode to COLORONCOLOR here
// because we might draw this into non 8Bpp surface and we still
// have to halftone in this case
}
}
else
{
WARNING(("destBitmap not valid"));
}
}
else
{
WARNING(("Could not construct destBitmap"));
}
}
DoGdiStretch:
// Halftoning on NT4 with 8bpp does not work very well -- it gets
// the wrong color hue.
// We cannot halftone to metafile for that same reason
if ((rop != SRCCOPY) ||
(Is8Bpp && Globals::IsNt && (Globals::OsVer.dwMajorVersion <= 4)) ||
IsMetafile())
{
// don't let halftoning mess up some kind of masking or other effect
stretchBltMode = COLORONCOLOR;
}
::SetStretchBltMode(hdc, stretchBltMode);
// There is a bug in Win9x that some StretchDIBits calls don't work
// so we need to create an actual StretchDIBits record to play.
// Also, NT4 postscript printing can't handle anything but SRCCOPY,
// so change any ROPs that are not source copy.
BOOL processed = FALSE;
if (!Globals::IsNt)
{
processed = CreateAndPlayOutputDIBRecord(hdc, bounds, dstX, dstY, dstWidth,
dstHeight, srcX, srcY, srcWidth, srcHeight, dibInfo, bits, usage,
rop);
}
else if (rop != SRCCOPY &&
Globals::OsVer.dwMajorVersion <= 4 &&
IsPostscript())
{
rop = SRCCOPY;
}
// In MSO, at this point they would check if this is NT running
// on a non-true-color surface. If so, they would set the
// color adjustment gamma to be 20000. The comment said that
// this was needed for NT 3.5. I'm assuming we don't need to
// worry about that anymore.
if (!processed)
{
::StretchDIBits(hdc,
dstX, dstY, dstWidth, dstHeight,
srcX, srcY, srcWidth, srcHeight,
bits, (BITMAPINFO *)dibInfo, usage, rop);
}
if (destBitmap)
{
if (bmpDataPtr != NULL)
{
destBitmap->UnlockBits(bmpDataPtr);
}
destBitmap->Dispose();
}
if (restoreColors)
{
::SetBkColor(hdc, oldBkColor);
::SetTextColor(hdc, oldTextColor);
}
if (deleteDIBSection)
{
// This will get rid of the Bitmap and it's bits
::DeleteObject(hBitmap);
GpFree(dibBmpInfo);
}
}
// Also handles META_DIBSTRETCHBLT
// There is not a usage parameter with these records -- the
// usage is always DIB_RGB_COLORS.
VOID
WmfEnumState::DIBBitBlt(
)
{
DWORD rop = *((DWORD UNALIGNED *)RecordData);
// If No-Op ROP, do nothing; just return
if ((rop & 0xFFFF0000) == (GDIP_NOOP_ROP3 & 0xFFFF0000))
{
return;
}
if (rop != SRCCOPY &&
rop != NOTSRCCOPY &&
rop != PATCOPY &&
rop != BLACKNESS &&
rop != WHITENESS)
{
RopUsed = TRUE;
}
INT paramIndex = 7;
if (RecordType != WmfRecordTypeDIBBitBlt)
{
paramIndex = 9; // META_DIBSTRETCHBLT
}
INT dibIndex = paramIndex + 1;
if (!IsSourceInRop3(rop))
{
if (((GetCurrentRecordSize() / 2) - 3) ==
(GDIP_EMFPLUS_RECORD_TO_WMF(RecordType) >> 8))
{
paramIndex++;
}
INT dstX = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
INT dstY = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
INT dstWidth = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
INT dstHeight = (INT)((INT16)((WORD *)RecordData)[paramIndex]);
// We know that this call will succeed because we have a 2K buffer
// by default
CreateRecordToModify(20);
// For some strange reason we need to play the record on both
// Win2K and Win9x. So create a WMF record for PatBlt and play it
ModifiedWmfRecord->rdFunction = GDIP_EMFPLUS_RECORD_TO_WMF(WmfRecordTypePatBlt);
ModifiedWmfRecord->rdSize = 10;
ModifiedWmfRecord->rdParm[5] = (WORD) dstX;
ModifiedWmfRecord->rdParm[4] = (WORD) dstY;
ModifiedWmfRecord->rdParm[3] = (WORD) dstWidth;
ModifiedWmfRecord->rdParm[2] = (WORD) dstHeight;
if (rop != PATCOPY && SrcCopyOnly)
{
*((DWORD*) ModifiedWmfRecord->rdParm) = PATCOPY;
}
else
{
*((DWORD*) ModifiedWmfRecord->rdParm) = rop;
}
// Now play the record (below)
}
else
{
BITMAPINFOHEADER UNALIGNED * srcDibInfo = (BITMAPINFOHEADER UNALIGNED *)(((WORD *)RecordData) + dibIndex);
UINT numPalEntries;
UINT dibBitsSize;
if ((srcDibInfo->biSize >= sizeof(BITMAPINFOHEADER)) &&
GetDibNumPalEntries(TRUE,
srcDibInfo->biSize,
srcDibInfo->biBitCount,
srcDibInfo->biCompression,
srcDibInfo->biClrUsed,
&numPalEntries) &&
((dibBitsSize = GetDibBitsSize(srcDibInfo)) > 0))
{
if ((srcDibInfo->biBitCount == 1) && (srcDibInfo->biPlanes == 1))
{
DWORD UNALIGNED * rgb = (DWORD UNALIGNED *)GetDibColorTable(srcDibInfo);
if ((rgb[0] == 0x00000000) &&
(rgb[1] == 0x00FFFFFF))
{
if (SrcCopyOnly && (rop != SRCCOPY) && CreateCopyOfCurrentRecord())
{
*((DWORD UNALIGNED *)ModifiedWmfRecord->rdParm) = SRCCOPY;
goto PlayTheRecord;
}
else
{
// It is a compatible monochrome bitmap, which means it
// will use the TextColor and BkColor, so no recoloring
// is needed. Since we are not using SrcCopy that means
// that it's a mask
COLORREF oldBkColor = ::SetBkColor(Hdc, BkColor);
COLORREF oldTextColor = ::SetTextColor(Hdc, TextColor);
this->PlayRecord();
::SetBkColor(Hdc, oldBkColor);
::SetTextColor(Hdc, oldTextColor);
return;
}
}
}
UINT usage = DIB_RGB_COLORS;
INT dstDibSize = GetModifiedDibSize(srcDibInfo, numPalEntries, dibBitsSize, usage);
if (IsMetafile())
{
if (dstDibSize > 0)
{
INT size = SIZEOF_METARECORDHEADER + (dibIndex * sizeof(WORD)) + dstDibSize;
if (CreateRecordToModify(size))
{
ModifiedWmfRecord->rdFunction = GDIP_EMFPLUS_RECORD_TO_WMF(RecordType);
ModifiedWmfRecord->rdSize = size / 2;
GpMemcpy(ModifiedWmfRecord->rdParm, RecordData, (dibIndex * sizeof(WORD)));
ModifyDib(DIB_RGB_COLORS, srcDibInfo, NULL,
(BITMAPINFOHEADER UNALIGNED *)(ModifiedWmfRecord->rdParm + dibIndex),
numPalEntries, dibBitsSize, ColorAdjustTypeBitmap);
}
}
goto PlayTheRecord;
}
// At this point, we are not going to play the record. We're
// going to call StretchDIBits. One reason is because we want
// to set the StretchBltMode, which is only used if a stretching
// method is called.
// Also, it avoids us doing an allocation/copy and then GDI doing
// another allocation/copy (GDI has to do this to align the
// DIB on a DWORD boundary).
BITMAPINFOHEADER UNALIGNED * dstDibInfo = srcDibInfo;
if (dstDibSize > 0)
{
if (CreateRecordToModify(dstDibSize))
{
// ModifiedRecord is Aligned
dstDibInfo = (BITMAPINFOHEADER UNALIGNED *)ModifiedRecord;
ModifyDib(DIB_RGB_COLORS, srcDibInfo, NULL, dstDibInfo,
numPalEntries, dibBitsSize, ColorAdjustTypeBitmap);
}
}
else if (!IsDwordAligned(dstDibInfo))
{
// The srcDibInfo may not aligned properly, so we make
// a copy of it, so that it will be aligned.
dstDibSize = GetCurrentRecordSize() - (SIZEOF_METARECORDHEADER + (dibIndex * sizeof(WORD)));
if (CreateRecordToModify(dstDibSize))
{
dstDibInfo = (BITMAPINFOHEADER *)ModifiedRecord;
GpMemcpy(dstDibInfo, srcDibInfo, dstDibSize);
}
}
if (SrcCopyOnly)
{
rop = SRCCOPY;
}
INT srcX, srcY;
INT dstX, dstY;
INT srcWidth, srcHeight;
INT dstWidth, dstHeight;
dstX = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
dstY = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
dstWidth = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
dstHeight = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
srcX = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
srcY = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
if (RecordType != WmfRecordTypeDIBBitBlt)
{
// META_DIBSTRETCHBLT
srcWidth = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
srcHeight = (INT)((INT16)((WORD *)RecordData)[paramIndex--]);
}
else
{
srcWidth = dstWidth;
srcHeight = dstHeight;
}
// Need to flip the source y coordinate to call StretchDIBits.
srcY = dstDibInfo->biHeight - srcY - srcHeight;
OutputDIB(Hdc,
NULL,
dstX, dstY, dstWidth, dstHeight,
srcX, srcY, srcWidth, srcHeight,
dstDibInfo, NULL, DIB_RGB_COLORS, rop, TRUE);
return;
}
}
PlayTheRecord:
this->PlayRecord();
}
VOID
WmfEnumState::StretchDIBits(
)
{
DWORD rop = *((DWORD UNALIGNED *)RecordData);
// If No-Op ROP, do nothing; just return
if ((rop & 0xFFFF0000) == (GDIP_NOOP_ROP3 & 0xFFFF0000))
{
return;
}
if (rop != SRCCOPY &&
rop != NOTSRCCOPY &&
rop != PATCOPY &&
rop != BLACKNESS &&
rop != WHITENESS)
{
RopUsed = TRUE;
}
if (IsSourceInRop3(rop))
{
if(((GetCurrentRecordSize() / 2) > (SIZEOF_METARECORDHEADER / sizeof(WORD)) + 11))
{
BITMAPINFOHEADER UNALIGNED * srcDibInfo = (BITMAPINFOHEADER UNALIGNED *)(((WORD *)RecordData) + 11);
UINT numPalEntries;
UINT dibBitsSize;
if ((srcDibInfo->biSize >= sizeof(BITMAPINFOHEADER)) &&
GetDibNumPalEntries(TRUE,
srcDibInfo->biSize,
srcDibInfo->biBitCount,
srcDibInfo->biCompression,
srcDibInfo->biClrUsed,
&numPalEntries) &&
((dibBitsSize = GetDibBitsSize(srcDibInfo)) > 0))
{
UINT usage = ((WORD *)RecordData)[2];
UINT oldUsage = usage;
INT dstDibSize = GetModifiedDibSize(srcDibInfo, numPalEntries, dibBitsSize, usage);
BITMAPINFOHEADER UNALIGNED * dstDibInfo = srcDibInfo;
if (dstDibSize > 0)
{
if ((srcDibInfo->biBitCount == 1) && (srcDibInfo->biPlanes == 1))
{
DWORD UNALIGNED * rgb = (DWORD UNALIGNED *)GetDibColorTable(srcDibInfo);
if ((rgb[0] == 0x00000000) &&
(rgb[1] == 0x00FFFFFF))
{
if (SrcCopyOnly && (rop != SRCCOPY) && CreateCopyOfCurrentRecord())
{
*((DWORD UNALIGNED *)ModifiedWmfRecord->rdParm) = SRCCOPY;
goto PlayTheRecord;
}
else
{
// It is a compatible monochrome bitmap, which means it
// will use the TextColor and BkColor, so no recoloring
// is needed. Since we are not using SrcCopy that means
// that it's a mask
COLORREF oldBkColor = ::SetBkColor(Hdc, BkColor);
COLORREF oldTextColor = ::SetTextColor(Hdc, TextColor);
this->PlayRecord();
::SetBkColor(Hdc, oldBkColor);
::SetTextColor(Hdc, oldTextColor);
return;
}
}
}
INT size = SIZEOF_METARECORDHEADER + (11 * sizeof(WORD)) + dstDibSize;
if (CreateRecordToModify(size))
{
ModifiedWmfRecord->rdFunction = GDIP_EMFPLUS_RECORD_TO_WMF(RecordType);
ModifiedWmfRecord->rdSize = size / 2;
GpMemcpy(ModifiedWmfRecord->rdParm, RecordData, (11 * sizeof(WORD)));
// This will be aligned.... Do we want to take a chance?
dstDibInfo = (BITMAPINFOHEADER UNALIGNED *)(ModifiedWmfRecord->rdParm + 11);
ModifyDib(oldUsage, srcDibInfo, NULL, dstDibInfo,
numPalEntries, dibBitsSize, ColorAdjustTypeBitmap);
}
}
if (!IsMetafile())
{
if ((dstDibInfo == srcDibInfo) && (!IsDwordAligned(dstDibInfo)))
{
// The srcDibInfo may not aligned properly, so we make
// a copy of it, so that it will be aligned.
dstDibSize = GetCurrentRecordSize() - (SIZEOF_METARECORDHEADER + (11 * sizeof(WORD)));
if (CreateRecordToModify(dstDibSize))
{
dstDibInfo = (BITMAPINFOHEADER UNALIGNED *)ModifiedRecord;
GpMemcpy(dstDibInfo, srcDibInfo, dstDibSize);
}
}
if (SrcCopyOnly)
{
rop = SRCCOPY;
}
INT dstX = (INT)((INT16)((WORD *)RecordData)[10]);
INT dstY = (INT)((INT16)((WORD *)RecordData)[9]);
INT dstWidth = (INT)((INT16)((WORD *)RecordData)[8]);
INT dstHeight = (INT)((INT16)((WORD *)RecordData)[7]);
INT srcX = (INT)((INT16)((WORD *)RecordData)[6]);
INT srcY = (INT)((INT16)((WORD *)RecordData)[5]);
INT srcWidth = (INT)((INT16)((WORD *)RecordData)[4]);
INT srcHeight = (INT)((INT16)((WORD *)RecordData)[3]);
OutputDIB(Hdc,
NULL,
dstX, dstY, dstWidth, dstHeight,
srcX, srcY, srcWidth, srcHeight,
dstDibInfo, NULL, usage, rop, TRUE);
return;
}
}
}
}
else // !IsSourceRop3
{
if (rop != PATCOPY && SrcCopyOnly && CreateCopyOfCurrentRecord())
{
*((DWORD UNALIGNED *)ModifiedWmfRecord->rdParm) = PATCOPY;
}
}
PlayTheRecord:
this->PlayRecord();
}
BOOL
WmfEnumState::CreateAndPlayOutputDIBRecord(
HDC hdc,
const RECTL * bounds,
INT dstX,
INT dstY,
INT dstWidth,
INT dstHeight,
INT srcX,
INT srcY,
INT srcWidth,
INT srcHeight,
BITMAPINFOHEADER UNALIGNED * dibInfo,
BYTE * bits, // if NULL, this is a packed DIB
UINT usage,
DWORD rop
)
{
INT bitsSize = GetDibBitsSize(dibInfo);
UINT sizePalEntries;
if (GetDibNumPalEntries(TRUE,
dibInfo->biSize,
dibInfo->biBitCount,
dibInfo->biCompression,
dibInfo->biClrUsed,
&sizePalEntries))
{
// We need to get the palette size that corresponds to the type
// If we have a DIB_PAL_COLORS then each entry is 16bits
sizePalEntries *= ((usage == DIB_PAL_COLORS)?2:sizeof(RGBQUAD));
}
else
{
sizePalEntries = 0 ;
}
// We need at least a BITMAPINFO structure in there, but if there is a
// palette, calculate the full size of the structure including the
// palette
INT bitmapHeaderSize = sizeof(BITMAPINFOHEADER) + sizePalEntries;
INT size = SIZEOF_METARECORDHEADER + (11 * sizeof(WORD)) + bitmapHeaderSize + bitsSize ;
// We cannot use the CreateRecordToModify because the record has already
// been modified
size = (size + 1) & ~1;
METARECORD* metaRecord = (METARECORD*) GpMalloc(size);
if (metaRecord != NULL)
{
metaRecord->rdFunction = GDIP_EMFPLUS_RECORD_TO_WMF(WmfRecordTypeStretchDIB);
metaRecord->rdSize = size / 2;
metaRecord->rdParm[10] = (WORD) dstX;
metaRecord->rdParm[9] = (WORD) dstY;
metaRecord->rdParm[8] = (WORD) dstWidth;
metaRecord->rdParm[7] = (WORD) dstHeight;
metaRecord->rdParm[6] = (WORD) srcX;
metaRecord->rdParm[5] = (WORD) srcY;
metaRecord->rdParm[4] = (WORD) srcWidth;
metaRecord->rdParm[3] = (WORD) srcHeight;
metaRecord->rdParm[2] = (WORD) usage;
*(DWORD UNALIGNED *)(&(metaRecord->rdParm[0])) = rop;
GpMemcpy((BYTE*)(&(metaRecord->rdParm[11])), dibInfo, bitmapHeaderSize);
GpMemcpy((BYTE*)(&(metaRecord->rdParm[11])) + bitmapHeaderSize, bits, bitsSize);
::PlayMetaFileRecord(hdc, HandleTable, metaRecord, NumObjects);
GpFree(metaRecord);
return TRUE;
}
return FALSE;
}
VOID
WmfEnumState::ModifyRecordColor(
INT paramIndex,
ColorAdjustType adjustType
)
{
COLORREF origColor = *((COLORREF UNALIGNED *)&(((WORD *)RecordData)[paramIndex]));
COLORREF modifiedColor = ModifyColor(origColor, adjustType);
if (modifiedColor != origColor)
{
if (CreateCopyOfCurrentRecord())
{
*((COLORREF UNALIGNED *)&(ModifiedWmfRecord->rdParm[paramIndex])) = modifiedColor;
}
}
}
COLORREF
MfEnumState::ModifyColor(
COLORREF color,
ColorAdjustType adjustType
)
{
if (AdjustType != ColorAdjustTypeDefault)
{
adjustType = AdjustType;
}
switch (color & 0xFF000000)
{
case 0x00000000:
break;
case 0x01000000: // Palette Index
{
PALETTEENTRY palEntry;
if (::GetPaletteEntries(CurrentPalette, color & 0x000000FF, 1, &palEntry) == 1)
{
color = RGB(palEntry.peRed, palEntry.peGreen, palEntry.peBlue);
}
else
{
color = RGB(0, 0, 0);
WARNING(("Failed to get palette entry"));
}
}
break;
case 0x02000000: // Palette RGB
default:
color &= 0x00FFFFFF;
break;
}
// Possible perfomance improvement: recolor the SelectedPalette so only
// RGB values need to be recolored here.
if (Recolor != NULL)
{
Recolor->ColorAdjustCOLORREF(&color, adjustType);
}
// Palette RGB values don't get dithered (at least not on NT), so we
// only want to make it a PaletteRGB value if it is a solid color in
// the palette.
if (Is8Bpp)
{
COLORREF matchingColor;
matchingColor = (::GetNearestColor(Hdc, color | 0x02000000) & 0x00FFFFFF);
// Pens and Text don't get Dithered so match them to the logical palette
// the other adjustTypes do so they will get halftoned
if ((matchingColor == color) ||
(adjustType == ColorAdjustTypePen) ||
(adjustType == ColorAdjustTypeText))
{
return color | 0x02000000;
}
}
return color;
}
BOOL
MfEnumState::CreateRecordToModify(
INT size
)
{
if (size <= 0)
{
size = this->GetCurrentRecordSize();
}
// add a little padding to help insure we don't read past the end of the buffer
size += 16;
if (ModifiedRecordSize < size)
{
ASSERT(ModifiedRecordSize == 0);
if (size <= GDIP_RECORDBUFFER_SIZE)
{
ModifiedRecord = RecordBuffer;
}
else if (size <= SizeAllocedRecord)
{
ModifiedRecord = AllocedRecord;
}
else
{
VOID * newRecord;
INT allocSize;
// alloc in increments of 1K
allocSize = (size + 1023) & (~1023);
ModifiedRecord = NULL;
newRecord = GpRealloc(AllocedRecord, allocSize);
if (newRecord != NULL)
{
ModifiedRecord = newRecord;
AllocedRecord = newRecord;
SizeAllocedRecord = allocSize;
}
}
}
else
{
ASSERT(ModifiedRecord != NULL);
ASSERT(ModifiedRecordSize == size);
}
if (ModifiedRecord != NULL)
{
ModifiedRecordSize = size;
return TRUE;
}
WARNING(("Failed to create ModifiedRecord"));
return FALSE;
}
BOOL
WmfEnumState::CreateCopyOfCurrentRecord()
{
if (ModifiedRecordSize > 0)
{
// We already made a modified record. Don't do it again.
ASSERT(ModifiedRecord != NULL);
return TRUE;
}
INT size = this->GetCurrentRecordSize();
if (CreateRecordToModify(size))
{
METARECORD * modifiedRecord = (METARECORD *)ModifiedRecord;
modifiedRecord->rdFunction = GDIP_EMFPLUS_RECORD_TO_WMF(RecordType);
modifiedRecord->rdSize = size / 2;
if (RecordDataSize > 0)
{
GpMemcpy(modifiedRecord->rdParm, RecordData, RecordDataSize);
}
return TRUE;
}
WARNING(("Failed to create copy of current record"));
return FALSE;
}
VOID
WmfEnumState::MakeSolidBlackBrush()
{
INT size = SIZEOF_METARECORDHEADER + sizeof(LOGBRUSH16);
CreateRecordToModify(size);
ModifiedWmfRecord->rdSize = size / 2;
ModifiedWmfRecord->rdFunction = META_CREATEBRUSHINDIRECT;
LOGBRUSH16 * logBrush = (LOGBRUSH16 *)(ModifiedWmfRecord->rdParm);
logBrush->lbStyle = BS_SOLID;
logBrush->lbColor = PALETTERGB(0,0,0);
logBrush->lbHatch = 0;
}
VOID
WmfEnumState::CalculateViewportMatrix()
{
GpRectF destViewport((REAL)DstViewportOrg.x, (REAL)DstViewportOrg.y,
(REAL)DstViewportExt.cx, (REAL)DstViewportExt.cy);
GpRectF srcViewport((REAL)ImgViewportOrg.x, (REAL)ImgViewportOrg.y,
(REAL)ImgViewportExt.cx, (REAL)ImgViewportExt.cy);
Status status = ViewportXForm.InferAffineMatrix(destViewport, srcViewport);
if (status != Ok)
{
ViewportXForm.Reset();
}
}
VOID
WmfEnumState::SetViewportOrg()
{
// If this is the first SetViewportOrg then we need to save that value and
// calculate a transform from out viewport to this viewport
ImgViewportOrg.x = (INT)((INT16)((WORD *)RecordData)[1]);
ImgViewportOrg.y = (INT)((INT16)((WORD *)RecordData)[0]);
if (FirstViewportOrg || FirstViewportExt)
{
FirstViewportOrg = FALSE;
// If we have processed the first ViewportExt call then we can calculate
// the transform from our current viewport to the new viewport
if (!FirstViewportExt)
{
CalculateViewportMatrix();
}
}
else
{
// We need to keep the new Viewport origin to be able to calculate
// the viewport bottom right corner before passing it through a
// transform
GpPointF newOrg((REAL) ImgViewportOrg.x,
(REAL) ImgViewportOrg.y);
// Transform the new viewport with our viewport transformation
ViewportXForm.Transform(&newOrg);
DstViewportOrg.x = GpRound(newOrg.X);
DstViewportOrg.y = GpRound(newOrg.Y);
if(CreateRecordToModify())
{
ModifiedWmfRecord->rdFunction = CurrentWmfRecord->rdFunction;
ModifiedWmfRecord->rdSize = CurrentWmfRecord->rdSize;
ModifiedWmfRecord->rdParm[0] = (WORD)GpRound(newOrg.Y);
ModifiedWmfRecord->rdParm[1] = (WORD)GpRound(newOrg.X);
}
this->PlayRecord();
}
}
VOID
WmfEnumState::SetViewportExt()
{
// If this is the first SetViewportOrg then we need to save that value and
// calculate a transform from out viewport to this viewport
ImgViewportExt.cx = (INT)((INT16)((WORD *)RecordData)[1]);
ImgViewportExt.cy = (INT)((INT16)((WORD *)RecordData)[0]);
if (FirstViewportOrg || FirstViewportExt)
{
FirstViewportExt = FALSE;
// If we have processed the first ViewportExt call then we can calculate
// the transform from our current viewport to the new viewport
if (!FirstViewportOrg)
{
CalculateViewportMatrix();
}
}
else
{
// We need to transform the point, so add the current origin
// of the Viewport
GpPointF newExt((REAL) ImgViewportExt.cx,
(REAL) ImgViewportExt.cy);
// Transform the new viewport with our viewport transformation
ViewportXForm.VectorTransform(&newExt);
if(CreateRecordToModify())
{
ModifiedWmfRecord->rdFunction = CurrentWmfRecord->rdFunction;
ModifiedWmfRecord->rdSize = CurrentWmfRecord->rdSize;
ModifiedWmfRecord->rdParm[0] = (WORD)GpRound(newExt.Y);
ModifiedWmfRecord->rdParm[1] = (WORD)GpRound(newExt.X);
}
this->PlayRecord();
}
}
VOID
WmfEnumState::CreateRegion()
{
// Check if the region it too big when mapped to device space.
if (!Globals::IsNt)
{
// There is a bug in Win9x GDI where the code which plays METACREATEREGION doesn't copy the
// entire region data, it is off by 8 bytes. This seems to have been introduced to allow
// for compatibility with an older header format, WIN2OBJECT. We get around this by increasing
// the size of the record by 8 bytes. No other harm done.
if (CreateCopyOfCurrentRecord())
{
// When we create a copy of the record, we add 16 bytes of padding so we know this
// won't overflow into other memory.
ModifiedWmfRecord->rdSize += 4;
}
}
this->PlayRecord();
}
HFONT CreateTrueTypeFont(
HFONT hFont
);
VOID
WmfEnumState::CreateFontIndirect(
)
{
LOGFONT16 * logFont = (LOGFONT16 *)RecordData;
BOOL recordCopied = FALSE;
if (!Globals::IsNt)
{
// We have a bug in Win9x that the OUT_TT_ONLY_PRECIS flag is
// not always respected so if the font name is MS SANS SERIF
// change it to Times New Roman
// Since we don't have a string compare in ASCII do it in UNICODE
WCHAR faceName[32];
if (AnsiToUnicodeStr((char*)(logFont->lfFaceName), faceName, sizeof(faceName)/sizeof(WCHAR)) &&
(UnicodeStringCompareCI(faceName, L"MS SANS SERIF") == 0))
{
if (CreateCopyOfCurrentRecord())
{
GpMemcpy(((LOGFONT16 *)(ModifiedWmfRecord->rdParm))->lfFaceName,
"Times New Roman", sizeof("Times New Roman"));
recordCopied = TRUE;
}
}
}
if (logFont->lfOutPrecision != OUT_TT_ONLY_PRECIS)
{
// Instruct GDI to use only True Type fonts, since bitmap fonts
// are not scalable.
if (recordCopied || CreateCopyOfCurrentRecord())
{
((LOGFONT16 *)(ModifiedWmfRecord->rdParm))->lfOutPrecision = OUT_TT_ONLY_PRECIS;
}
}
this->PlayRecord();
}
VOID WmfEnumState::SelectObject()
{
this->PlayRecord();
// In case we selected a region on Win9x, we need to intersect
if (!Globals::IsNt)
{
DWORD index = CurrentWmfRecord->rdParm[0];
if (GetObjectTypeInternal((*HandleTable).objectHandle[index]) == OBJ_REGION)
{
this->IntersectDestRect();
}
}
}
VOID WmfEnumState::IntersectDestRect()
{
if (!IsMetafile())
{
POINT windowOrg;
SIZE windowExt;
::SetWindowOrgEx(Hdc, DstViewportOrg.x, DstViewportOrg.y, &windowOrg);
::SetWindowExtEx(Hdc, DstViewportExt.cx, DstViewportExt.cy, &windowExt);
// We are always in device units
::IntersectClipRect(Hdc, DestRectDevice.left, DestRectDevice.top,
DestRectDevice.right, DestRectDevice.bottom);
::SetWindowOrgEx(Hdc, windowOrg.x, windowOrg.y, NULL);
::SetWindowExtEx(Hdc, windowExt.cx, windowExt.cy, NULL);
}
}
VOID WmfEnumState::SetROP2()
{
INT rop = (INT)((INT16)(((WORD *)RecordData)[0]));
if (rop != R2_BLACK &&
rop != R2_COPYPEN &&
rop != R2_NOTCOPYPEN &&
rop != R2_WHITE )
{
RopUsed = TRUE;
}
this->PlayRecord();
}
BOOL
WmfEnumState::ProcessRecord(
EmfPlusRecordType recordType,
UINT recordDataSize,
const BYTE * recordData
)
{
BOOL forceCallback = FALSE;
MfFsmState nextState = MfFsmStart;
if (IsFirstRecord)
{
// Bitmap fonts are not good for playing metafiles because they
// don't scale well, so use a true type font instead as the default font.
HFONT hFont = CreateTrueTypeFont((HFONT)GetCurrentObject(Hdc, OBJ_FONT));
if (hFont != NULL)
{
DefaultFont = hFont;
::SelectObject(Hdc, hFont);
}
IsFirstRecord = FALSE;
}
// See if we're doing enumeration for an external app
if (ExternalEnumeration)
{
if (recordData == NULL)
{
recordDataSize = 0;
}
else if (recordDataSize == 0)
{
recordData = NULL;
}
// make sure it's an EMF enum type
recordType = GDIP_WMF_RECORD_TO_EMFPLUS(recordType);
// See if the app changed the record at all.
if ((recordType != RecordType) ||
(recordDataSize != RecordDataSize) ||
((recordDataSize > 0) &&
((CurrentWmfRecord == NULL) ||
(recordData != (const BYTE *)CurrentWmfRecord->rdParm))))
{
// Yes, we need to override what happened in StartRecord
CurrentWmfRecord = NULL;
RecordType = recordType;
RecordData = recordData;
RecordDataSize = recordDataSize;
}
}
// Ignore all non-escape records if IgnorePostcript is TRUE
if (recordType == WmfRecordTypeEscape || !IgnorePostscript)
{
GDIP_TRY
switch (recordType)
{
// According to NT playback code, this is a EOF record, but it
// is just skipped by the NT player.
case GDIP_WMF_RECORD_TO_EMFPLUS(0x0000): // End of metafile record
break;
// These records are not played back (at least in Win2000).
// Apparently they haven't been supported since before Win3.1!
case WmfRecordTypeSetRelAbs:
case WmfRecordTypeDrawText:
case WmfRecordTypeResetDC:
case WmfRecordTypeStartDoc:
case WmfRecordTypeStartPage:
case WmfRecordTypeEndPage:
case WmfRecordTypeAbortDoc:
case WmfRecordTypeEndDoc:
case WmfRecordTypeCreateBrush:
case WmfRecordTypeCreateBitmapIndirect:
case WmfRecordTypeCreateBitmap:
ONCE(WARNING1("Unsupported WMF record"));
break;
default:
// unknown record -- ignore it
WARNING1("Unknown WMF Record");
break;
case WmfRecordTypeSetBkMode:
case WmfRecordTypeSetMapMode:
case WmfRecordTypeSetPolyFillMode:
case WmfRecordTypeSetStretchBltMode:
case WmfRecordTypeSetTextCharExtra:
case WmfRecordTypeSetTextJustification:
case WmfRecordTypeSetWindowOrg:
case WmfRecordTypeSetWindowExt:
case WmfRecordTypeOffsetWindowOrg:
case WmfRecordTypeScaleWindowExt:
case WmfRecordTypeOffsetViewportOrg:
case WmfRecordTypeScaleViewportExt:
case WmfRecordTypeLineTo:
case WmfRecordTypeMoveTo:
case WmfRecordTypeExcludeClipRect:
case WmfRecordTypeIntersectClipRect:
case WmfRecordTypeArc:
case WmfRecordTypeEllipse:
case WmfRecordTypePie:
case WmfRecordTypeRoundRect:
case WmfRecordTypePatBlt:
case WmfRecordTypeTextOut:
case WmfRecordTypePolygon:
case WmfRecordTypePolyline:
case WmfRecordTypeFillRegion:
case WmfRecordTypeFrameRegion:
case WmfRecordTypeInvertRegion:
case WmfRecordTypePaintRegion:
case WmfRecordTypeSetTextAlign:
case WmfRecordTypeChord:
case WmfRecordTypeSetMapperFlags:
case WmfRecordTypeExtTextOut:
case WmfRecordTypeAnimatePalette:
case WmfRecordTypeSetPalEntries:
case WmfRecordTypeResizePalette:
case WmfRecordTypeSetLayout:
case WmfRecordTypeDeleteObject:
case WmfRecordTypeCreatePalette:
// Play the current record.
// Even if it fails, we keep playing the rest of the metafile.
// There is a case that GdiComment with EPS may fail.
this->PlayRecord();
break;
case WmfRecordTypeCreateRegion:
this->CreateRegion();
break;
case WmfRecordTypeCreateFontIndirect:
this->CreateFontIndirect();
break;
case WmfRecordTypeSetBkColor:
this->SetBkColor();
break;
case WmfRecordTypeSetTextColor:
this->SetTextColor();
break;
case WmfRecordTypeFloodFill:
this->FloodFill();
break;
case WmfRecordTypeExtFloodFill:
this->ExtFloodFill();
break;
case WmfRecordTypeSaveDC:
this->SaveHdc(); // plays the record
break;
case WmfRecordTypeSetPixel:
this->SetPixel();
break;
case WmfRecordTypeDIBCreatePatternBrush:
this->DibCreatePatternBrush();
break;
case WmfRecordTypeCreatePatternBrush: // Obsolete but still played back
this->CreatePatternBrush();
break;
case WmfRecordTypeCreatePenIndirect:
this->CreatePenIndirect();
if (FsmState == MfFsmSelectBrush)
{
nextState = MfFsmCreatePenIndirect;
}
break;
case WmfRecordTypeCreateBrushIndirect:
this->CreateBrushIndirect();
if (FsmState == MfFsmPSData)
{
nextState = MfFsmCreateBrushIndirect;
}
break;
case WmfRecordTypeSelectObject:
// What if we break out of the FSM, we do want to Create the appropriate
// brush and pens right?!
if (FsmState == MfFsmCreateBrushIndirect)
{
nextState = MfFsmSelectBrush;
break;
}
else if (FsmState == MfFsmSelectBrush ||
FsmState == MfFsmCreatePenIndirect)
{
nextState = MfFsmSelectPen;
break;
}
this->SelectObject();
break;
case WmfRecordTypeRectangle:
this->Rectangle();
break;
case WmfRecordTypeSetROP2:
{
INT rdParm = (INT)((INT16)(((WORD *)RecordData)[0]));
if (FsmState == MfFsmSelectPen &&
(INT)(rdParm == R2_NOP))
{
nextState = MfFsmSetROP;
}
this->SetROP2();
}
break;
case WmfRecordTypeBitBlt: // Obsolete but still played back
this->BitBlt();
forceCallback = TRUE;
break;
case WmfRecordTypeStretchBlt: // Obsolete but still played back
this->StretchBlt();
forceCallback = TRUE;
break;
case WmfRecordTypeEscape:
{
INT escapeCode = (INT)((INT16)(((WORD *)RecordData)[0]));
this->Escape(); // optionally plays the record
if (FsmState == MfFsmStart && escapeCode == POSTSCRIPT_DATA &&
Globals::IsNt && IsPostscriptPrinter() &&
GdiCentricMode && SoftekFilter)
{
nextState = MfFsmPSData;
}
// Comments do not change the current state
if (escapeCode == MFCOMMENT)
{
nextState = FsmState;
}
}
break;
case WmfRecordTypeRestoreDC:
this->RestoreHdc(); // optionally plays the record
break;
case WmfRecordTypeSetDIBToDev:
this->SetDIBitsToDevice();
forceCallback = TRUE;
break;
case WmfRecordTypeSelectPalette:
// We don't select in any palettes when playing the metafile,
// because we don't want to invalidate our halftoning palette.
// Keep track of the palette so we can map from PALETTEINDEXes
// to RGB values.
this->SelectPalette((INT)((INT16)(((WORD *)recordData)[0])));
break;
case WmfRecordTypeRealizePalette:
// We don't want to invalidate our halftoning palette by realizing one
// from a metafile.
break;
case WmfRecordTypePolyPolygon:
this->PolyPolygon();
break;
case WmfRecordTypeDIBBitBlt:
this->DIBBitBlt();
forceCallback = TRUE;
break;
case WmfRecordTypeDIBStretchBlt:
this->DIBStretchBlt();
forceCallback = TRUE;
break;
case WmfRecordTypeStretchDIB:
this->StretchDIBits();
forceCallback = TRUE;
break;
case WmfRecordTypeSetViewportOrg:
this->SetViewportOrg();
break;
case WmfRecordTypeSetViewportExt:
this->SetViewportExt();
break;
case WmfRecordTypeSelectClipRegion:
case WmfRecordTypeOffsetClipRgn:
this->PlayRecord();
if (!Globals::IsNt)
{
this->IntersectDestRect();
}
break;
}
GDIP_CATCH
forceCallback = TRUE;
GDIP_ENDCATCH
}
FsmState = nextState;
this->EndRecord();
return forceCallback;
}