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
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;
|
|
}
|