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.
7225 lines
197 KiB
7225 lines
197 KiB
//
|
|
// OE.C
|
|
// Order Encoder
|
|
//
|
|
// Copyright(c) Microsoft 1997-
|
|
//
|
|
|
|
#include <as16.h>
|
|
|
|
|
|
//
|
|
// Define entries in the Font Alias table. This table is used to convert
|
|
// non-existant fonts (used by certain widely used applications) into
|
|
// something we can use as a local font.
|
|
//
|
|
// The font names that we alias are:
|
|
//
|
|
// "Helv"
|
|
// This is used by Excel. It is mapped directly onto "MS Sans Serif".
|
|
//
|
|
// "MS Dialog"
|
|
// This is used by Word. It is the same as an 8pt bold MS Sans Serif.
|
|
// We actually map it to a "MS Sans Serif" font that is one pel narrower
|
|
// than the metrics specify (because all matching is done on non-bold
|
|
// fonts) - hence the 1 value in the charWidthAdjustment field.
|
|
//
|
|
// "MS Dialog Light"
|
|
// Added as part of the Win95 performance enhancements...Presumably for
|
|
// MS-Word...
|
|
//
|
|
//
|
|
#define NUM_ALIAS_FONTS 3
|
|
|
|
char CODESEG g_szMsSansSerif[] = "MS Sans Serif";
|
|
char CODESEG g_szHelv[] = "Helv";
|
|
char CODESEG g_szMsDialog[] = "MS Dialog";
|
|
char CODESEG g_szMsDialogLight[] = "MS Dialog Light";
|
|
|
|
FONT_ALIAS_TABLE CODESEG g_oeFontAliasTable[NUM_ALIAS_FONTS] =
|
|
{
|
|
{ g_szHelv, g_szMsSansSerif, 0 },
|
|
{ g_szMsDialog, g_szMsSansSerif, 1 },
|
|
{ g_szMsDialogLight, g_szMsSansSerif, 0 }
|
|
};
|
|
|
|
|
|
//
|
|
// OE_DDProcessRequest()
|
|
// Handles OE escapes
|
|
//
|
|
|
|
BOOL OE_DDProcessRequest
|
|
(
|
|
UINT fnEscape,
|
|
LPOSI_ESCAPE_HEADER pResult,
|
|
DWORD cbResult
|
|
)
|
|
{
|
|
BOOL rc = TRUE;
|
|
|
|
DebugEntry(OE_DDProcessRequest);
|
|
|
|
switch (fnEscape)
|
|
{
|
|
case OE_ESC_NEW_FONTS:
|
|
{
|
|
ASSERT(cbResult == sizeof(OE_NEW_FONTS));
|
|
|
|
OEDDSetNewFonts((LPOE_NEW_FONTS)pResult);
|
|
}
|
|
break;
|
|
|
|
case OE_ESC_NEW_CAPABILITIES:
|
|
{
|
|
ASSERT(cbResult == sizeof(OE_NEW_CAPABILITIES));
|
|
|
|
OEDDSetNewCapabilities((LPOE_NEW_CAPABILITIES)pResult);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
ERROR_OUT(("Unrecognized OE escape"));
|
|
rc = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
DebugExitBOOL(OE_DDProcessRequest, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
//
|
|
// OE_DDInit()
|
|
// This creates the patches we need.
|
|
//
|
|
BOOL OE_DDInit(void)
|
|
{
|
|
BOOL rc = FALSE;
|
|
HGLOBAL hMem;
|
|
UINT uSel;
|
|
DDI_PATCH iPatch;
|
|
|
|
DebugEntry(OE_DDInit);
|
|
|
|
//
|
|
// lstrcmp(), like strcmp(), works numerically for US/Eng code page.
|
|
// But it's lexographic like Win32 lstrcmp() is all the time for non
|
|
// US.
|
|
//
|
|
// So we use MyStrcmp()
|
|
//
|
|
ASSERT(MyStrcmp("Symbol", "SYmbol") > 0);
|
|
|
|
//
|
|
// Allocate a cached selector. We use it when reading from swapped-out
|
|
// DCs. Therefore base it off of GDI's data segement, so it has the
|
|
// same access rights and limit.
|
|
//
|
|
g_oeSelDst = AllocSelector((UINT)g_hInstGdi16);
|
|
g_oeSelSrc = AllocSelector((UINT)g_hInstGdi16);
|
|
if (!g_oeSelDst || !g_oeSelSrc)
|
|
{
|
|
ERROR_OUT(("Out of selectors"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Allocate g_poeLocalFonts--it's too big for our DS. We make it
|
|
// a very small size since on new fonts, we will realloc it.
|
|
//
|
|
hMem = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT | GMEM_SHARE,
|
|
sizeof(LOCALFONT));
|
|
if (!hMem)
|
|
{
|
|
ERROR_OUT(("OE_DDInit: Couldn't allocate font matching array"));
|
|
DC_QUIT;
|
|
}
|
|
g_poeLocalFonts = MAKELP(hMem, 0);
|
|
|
|
|
|
//
|
|
// Create two patches for ChangeDisplaySettings/Ex and ENABLE them right
|
|
// away. We don't want you to be able to change your display when
|
|
// NetMeeting is running, regardless of whether you are in a share yet.
|
|
//
|
|
uSel = CreateFnPatch(ChangeDisplaySettings, DrvChangeDisplaySettings,
|
|
&g_oeDisplaySettingsPatch, 0);
|
|
if (!uSel)
|
|
{
|
|
ERROR_OUT(("CDS patch failed to create"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
EnableFnPatch(&g_oeDisplaySettingsPatch, PATCH_ACTIVATE);
|
|
|
|
if (SELECTOROF(g_lpfnCDSEx))
|
|
{
|
|
if (!CreateFnPatch(g_lpfnCDSEx, DrvChangeDisplaySettingsEx,
|
|
&g_oeDisplaySettingsExPatch, uSel))
|
|
{
|
|
ERROR_OUT(("CDSEx patch failed to create"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
EnableFnPatch(&g_oeDisplaySettingsExPatch, PATCH_ACTIVATE);
|
|
}
|
|
|
|
//
|
|
// Create patches.
|
|
// NOTE this code assumes that various groups of functions are in
|
|
// the same segment. CreateFnPatch has asserts to verify this.
|
|
//
|
|
// Rather than check each for failure (low on selectors), we try to
|
|
// create all the patches, then loop through looking for any that
|
|
// didn't succeed.
|
|
//
|
|
// Why do we do this? Because allocating 50 different selectors is
|
|
// not so hot when 16-bit selectors are the most precious resource on
|
|
// Win95 (most out-of-memory conditions that aren't blatant app errors
|
|
// are caused by a lack of selectors, not logical memory).
|
|
//
|
|
|
|
// _ARCDDA
|
|
uSel = CreateFnPatch(Arc, DrvArc, &g_oeDDPatches[DDI_ARC], 0);
|
|
CreateFnPatch(Chord, DrvChord, &g_oeDDPatches[DDI_CHORD], uSel);
|
|
CreateFnPatch(Ellipse, DrvEllipse, &g_oeDDPatches[DDI_ELLIPSE], uSel);
|
|
CreateFnPatch(Pie, DrvPie, &g_oeDDPatches[DDI_PIE], uSel);
|
|
CreateFnPatch(RoundRect, DrvRoundRect, &g_oeDDPatches[DDI_ROUNDRECT], uSel);
|
|
|
|
// IGROUP
|
|
uSel = CreateFnPatch(BitBlt, DrvBitBlt, &g_oeDDPatches[DDI_BITBLT], 0);
|
|
CreateFnPatch(ExtTextOut, DrvExtTextOutA, &g_oeDDPatches[DDI_EXTTEXTOUTA], uSel);
|
|
CreateFnPatch(InvertRgn, DrvInvertRgn, &g_oeDDPatches[DDI_INVERTRGN], uSel);
|
|
CreateFnPatch(DeleteObject, DrvDeleteObject, &g_oeDDPatches[DDI_DELETEOBJECT], uSel);
|
|
CreateFnPatch(Death, DrvDeath, &g_oeDDPatches[DDI_DEATH], uSel);
|
|
CreateFnPatch(Resurrection, DrvResurrection, &g_oeDDPatches[DDI_RESURRECTION], uSel);
|
|
|
|
|
|
//
|
|
// Note: PatBlt and IPatBlt (internal PatBlt) jump to RealPatBlt, which
|
|
// is 3 bytes past PatBlt. So patch RealPatBlt, or we'll (1) fault with
|
|
// misaligned instructions and (2) miss many PatBlt calls. But our
|
|
// function needs to preserve CX since those two routines pass 0 for
|
|
// internal calls (EMF) and -1 for external calls.
|
|
//
|
|
g_lpfnRealPatBlt = (REALPATBLTPROC)((LPBYTE)PatBlt+3);
|
|
CreateFnPatch(g_lpfnRealPatBlt, DrvPatBlt, &g_oeDDPatches[DDI_PATBLT], uSel);
|
|
CreateFnPatch(StretchBlt, DrvStretchBlt, &g_oeDDPatches[DDI_STRETCHBLT], uSel);
|
|
CreateFnPatch(TextOut, DrvTextOutA, &g_oeDDPatches[DDI_TEXTOUTA], uSel);
|
|
|
|
// _FLOODFILL
|
|
uSel = CreateFnPatch(ExtFloodFill, DrvExtFloodFill, &g_oeDDPatches[DDI_EXTFLOODFILL], 0);
|
|
CreateFnPatch(FloodFill, DrvFloodFill, &g_oeDDPatches[DDI_FLOODFILL], uSel);
|
|
|
|
// _FONTLOAD
|
|
uSel = CreateFnPatch(g_lpfnExtTextOutW, DrvExtTextOutW, &g_oeDDPatches[DDI_EXTTEXTOUTW], 0);
|
|
CreateFnPatch(g_lpfnTextOutW, DrvTextOutW, &g_oeDDPatches[DDI_TEXTOUTW], uSel);
|
|
|
|
// _PATH
|
|
uSel = CreateFnPatch(FillPath, DrvFillPath, &g_oeDDPatches[DDI_FILLPATH], 0);
|
|
CreateFnPatch(StrokeAndFillPath, DrvStrokeAndFillPath, &g_oeDDPatches[DDI_STROKEANDFILLPATH], uSel);
|
|
CreateFnPatch(StrokePath, DrvStrokePath, &g_oeDDPatches[DDI_STROKEPATH], uSel);
|
|
|
|
// _RGOUT
|
|
uSel = CreateFnPatch(FillRgn, DrvFillRgn, &g_oeDDPatches[DDI_FILLRGN], 0);
|
|
CreateFnPatch(FrameRgn, DrvFrameRgn, &g_oeDDPatches[DDI_FRAMERGN], uSel);
|
|
CreateFnPatch(PaintRgn, DrvPaintRgn, &g_oeDDPatches[DDI_PAINTRGN], uSel);
|
|
|
|
// _OUTMAN
|
|
uSel = CreateFnPatch(LineTo, DrvLineTo, &g_oeDDPatches[DDI_LINETO], 0);
|
|
CreateFnPatch(Polyline, DrvPolyline, &g_oeDDPatches[DDI_POLYLINE], uSel);
|
|
CreateFnPatch(g_lpfnPolylineTo, DrvPolylineTo, &g_oeDDPatches[DDI_POLYLINETO], uSel);
|
|
|
|
// EMF
|
|
uSel = CreateFnPatch(PlayEnhMetaFileRecord, DrvPlayEnhMetaFileRecord, &g_oeDDPatches[DDI_PLAYENHMETAFILERECORD], 0);
|
|
|
|
// METAPLAY
|
|
uSel = CreateFnPatch(PlayMetaFile, DrvPlayMetaFile, &g_oeDDPatches[DDI_PLAYMETAFILE], 0);
|
|
CreateFnPatch(PlayMetaFileRecord, DrvPlayMetaFileRecord, &g_oeDDPatches[DDI_PLAYMETAFILERECORD], uSel);
|
|
|
|
// _POLYGON
|
|
uSel = CreateFnPatch(Polygon, DrvPolygon, &g_oeDDPatches[DDI_POLYGON], 0);
|
|
CreateFnPatch(PolyPolygon, DrvPolyPolygon, &g_oeDDPatches[DDI_POLYPOLYGON], uSel);
|
|
|
|
// _BEZIER
|
|
uSel = CreateFnPatch(PolyBezier, DrvPolyBezier, &g_oeDDPatches[DDI_POLYBEZIER], 0);
|
|
CreateFnPatch(PolyBezierTo, DrvPolyBezierTo, &g_oeDDPatches[DDI_POLYBEZIERTO], uSel);
|
|
|
|
// _WIN32
|
|
uSel = CreateFnPatch(g_lpfnPolyPolyline, DrvPolyPolyline, &g_oeDDPatches[DDI_POLYPOLYLINE], 0);
|
|
|
|
// _RECT
|
|
uSel = CreateFnPatch(Rectangle, DrvRectangle, &g_oeDDPatches[DDI_RECTANGLE], 0);
|
|
|
|
// _DIBITMAP
|
|
uSel = CreateFnPatch(SetDIBitsToDevice, DrvSetDIBitsToDevice, &g_oeDDPatches[DDI_SETDIBITSTODEVICE], 0);
|
|
CreateFnPatch(StretchDIBits, DrvStretchDIBits, &g_oeDDPatches[DDI_STRETCHDIBITS], uSel);
|
|
|
|
// _DCSTUFF
|
|
uSel = CreateFnPatch(CreateSpb, DrvCreateSpb, &g_oeDDPatches[DDI_CREATESPB], 0);
|
|
|
|
// _PIXDDA
|
|
uSel = CreateFnPatch(SetPixel, DrvSetPixel, &g_oeDDPatches[DDI_SETPIXEL], 0);
|
|
|
|
// _PALETTE
|
|
uSel = CreateFnPatch(UpdateColors, DrvUpdateColors, &g_oeDDPatches[DDI_UPDATECOLORS], 0);
|
|
CreateFnPatch(GDIRealizePalette, DrvGDIRealizePalette, &g_oeDDPatches[DDI_GDIREALIZEPALETTE], uSel);
|
|
CreateFnPatch(RealizeDefaultPalette, DrvRealizeDefaultPalette, &g_oeDDPatches[DDI_REALIZEDEFAULTPALETTE], uSel);
|
|
|
|
// (User WINRARE)
|
|
uSel = CreateFnPatch(WinOldAppHackoMatic, DrvWinOldAppHackoMatic, &g_oeDDPatches[DDI_WINOLDAPPHACKOMATIC], 0);
|
|
|
|
//
|
|
// Loop through our patches and check for failure
|
|
//
|
|
for (iPatch = DDI_FIRST; iPatch < DDI_MAX; iPatch++)
|
|
{
|
|
if (!SELECTOROF(g_oeDDPatches[iPatch].lpCodeAlias))
|
|
{
|
|
ERROR_OUT(("Patch %u failed to create", iPatch));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(OE_DDInit, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OE_DDTerm()
|
|
// This destroys the patches we created.
|
|
//
|
|
void OE_DDTerm(void)
|
|
{
|
|
DDI_PATCH iPatch;
|
|
|
|
DebugEntry(OE_DDTerm);
|
|
|
|
//
|
|
// Destroying patches will also disable any still active.
|
|
//
|
|
for (iPatch = DDI_FIRST; iPatch < DDI_MAX; iPatch++)
|
|
{
|
|
// destroy patches
|
|
DestroyFnPatch(&g_oeDDPatches[iPatch]);
|
|
}
|
|
|
|
//
|
|
// Destroy ChangeDisplaySettings patches
|
|
//
|
|
if (SELECTOROF(g_lpfnCDSEx))
|
|
DestroyFnPatch(&g_oeDisplaySettingsExPatch);
|
|
DestroyFnPatch(&g_oeDisplaySettingsPatch);
|
|
|
|
//
|
|
// Free font memory
|
|
//
|
|
if (SELECTOROF(g_poeLocalFonts))
|
|
{
|
|
GlobalFree((HGLOBAL)SELECTOROF(g_poeLocalFonts));
|
|
g_poeLocalFonts = NULL;
|
|
}
|
|
|
|
//
|
|
// Free cached selectors
|
|
//
|
|
if (g_oeSelSrc)
|
|
{
|
|
FreeSelector(g_oeSelSrc);
|
|
g_oeSelSrc = 0;
|
|
}
|
|
|
|
if (g_oeSelDst)
|
|
{
|
|
FreeSelector(g_oeSelDst);
|
|
g_oeSelDst = 0;
|
|
}
|
|
|
|
DebugExitVOID(OE_DDTerm);
|
|
}
|
|
|
|
|
|
//
|
|
// OE_DDViewing()
|
|
//
|
|
// Turns on/off patches for trapping graphic output.
|
|
//
|
|
void OE_DDViewing(BOOL fViewers)
|
|
{
|
|
DDI_PATCH patch;
|
|
|
|
DebugEntry(OE_DDViewing);
|
|
|
|
//
|
|
// Clear window and font caches
|
|
//
|
|
g_oeLastWindow = NULL;
|
|
g_oeFhLast.fontIndex = 0xFFFF;
|
|
|
|
//
|
|
// Enable or disable GDI patches
|
|
//
|
|
for (patch = DDI_FIRST; patch < DDI_MAX; patch++)
|
|
{
|
|
EnableFnPatch(&g_oeDDPatches[patch], (fViewers ? PATCH_ACTIVATE :
|
|
PATCH_DEACTIVATE));
|
|
}
|
|
|
|
//
|
|
// Do save bits & cursor patches too
|
|
//
|
|
SSI_DDViewing(fViewers);
|
|
CM_DDViewing(fViewers);
|
|
|
|
if (fViewers)
|
|
{
|
|
//
|
|
// Our palette color array starts out as all black on each share.
|
|
// So force PMUpdateSystemColors() to do something.
|
|
//
|
|
ASSERT(g_asSharedMemory);
|
|
g_asSharedMemory->pmPaletteChanged = TRUE;
|
|
}
|
|
|
|
DebugExitVOID(OE_DDViewing);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: OEDDSetNewCapabilities
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Set the new OE related capabilities
|
|
//
|
|
// RETURNS:
|
|
//
|
|
// NONE
|
|
//
|
|
// PARAMETERS:
|
|
//
|
|
// pDataIn - pointer to the input buffer
|
|
//
|
|
//
|
|
void OEDDSetNewCapabilities(LPOE_NEW_CAPABILITIES pCapabilities)
|
|
{
|
|
LPBYTE lpos16;
|
|
|
|
DebugEntry(OEDDSetNewCapabilities);
|
|
|
|
//
|
|
// Copy the data from the Share Core.
|
|
//
|
|
g_oeBaselineTextEnabled = pCapabilities->baselineTextEnabled;
|
|
|
|
g_oeSendOrders = pCapabilities->sendOrders;
|
|
|
|
g_oeTextEnabled = pCapabilities->textEnabled;
|
|
|
|
//
|
|
// The share core has passed down a pointer to it's copy of the order
|
|
// support array. We take a copy for the kernel here.
|
|
//
|
|
lpos16 = MapLS(pCapabilities->orderSupported);
|
|
if (SELECTOROF(lpos16))
|
|
{
|
|
hmemcpy(g_oeOrderSupported, lpos16, sizeof(g_oeOrderSupported));
|
|
UnMapLS(lpos16);
|
|
}
|
|
else
|
|
{
|
|
UINT i;
|
|
|
|
ERROR_OUT(("OEDDSetNewCaps: can't save new order array"));
|
|
|
|
for (i = 0; i < sizeof(g_oeOrderSupported); i++)
|
|
g_oeOrderSupported[i] = FALSE;
|
|
}
|
|
|
|
TRACE_OUT(( "OE caps: BLT %c Orders %c Text %c",
|
|
g_oeBaselineTextEnabled ? 'Y': 'N',
|
|
g_oeSendOrders ? 'Y': 'N',
|
|
g_oeTextEnabled ? 'Y': 'N'));
|
|
|
|
DebugExitVOID(OEDDSetNewCapabilities);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: OEDDSetNewFonts
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Set the new font handling information to be used by the display driver.
|
|
//
|
|
// RETURNS:
|
|
//
|
|
// NONE
|
|
//
|
|
//
|
|
void OEDDSetNewFonts(LPOE_NEW_FONTS pRequest)
|
|
{
|
|
HGLOBAL hMem;
|
|
UINT cbNewSize;
|
|
LPVOID lpFontData;
|
|
LPVOID lpFontIndex;
|
|
|
|
DebugEntry(OEDDSetNewFonts);
|
|
|
|
TRACE_OUT(( "New fonts %d", pRequest->countFonts));
|
|
|
|
//
|
|
// Initialize new number of fonts to zero in case an error happens.
|
|
// We don't want to use stale font info if so. And clear the font
|
|
// cache.
|
|
//
|
|
g_oeNumFonts = 0;
|
|
g_oeFhLast.fontIndex = 0xFFFF;
|
|
|
|
g_oeFontCaps = pRequest->fontCaps;
|
|
|
|
//
|
|
// Can we get 16:16 addresses for font info?
|
|
//
|
|
lpFontData = MapLS(pRequest->fontData);
|
|
lpFontIndex = MapLS(pRequest->fontIndex);
|
|
if (!lpFontData || !lpFontIndex)
|
|
{
|
|
ERROR_OUT(("OEDDSetNewFonts: couldn't map flat addresses to 16-bit"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Realloc our current font block if we need to. Always shrink it
|
|
// too, this thing can get large!
|
|
//
|
|
ASSERT(pRequest->countFonts <= (0xFFFF / sizeof(LOCALFONT)));
|
|
cbNewSize = pRequest->countFonts * sizeof(LOCALFONT);
|
|
|
|
hMem = (HGLOBAL)SELECTOROF(g_poeLocalFonts);
|
|
|
|
hMem = GlobalReAlloc(hMem, cbNewSize, GMEM_MOVEABLE | GMEM_SHARE);
|
|
if (!hMem)
|
|
{
|
|
ERROR_OUT(("OEDDSetNewFonts: can't allocate space for font info"));
|
|
DC_QUIT;
|
|
}
|
|
else
|
|
{
|
|
g_poeLocalFonts = MAKELP(hMem, 0);
|
|
}
|
|
|
|
//
|
|
// We got here, so everything is OK. Update the font info we have.
|
|
//
|
|
g_oeNumFonts = pRequest->countFonts;
|
|
|
|
hmemcpy(g_poeLocalFonts, lpFontData, cbNewSize);
|
|
|
|
hmemcpy(g_oeLocalFontIndex, lpFontIndex,
|
|
sizeof(g_oeLocalFontIndex[0]) * FH_LOCAL_INDEX_SIZE);
|
|
|
|
DC_EXIT_POINT:
|
|
if (lpFontData)
|
|
UnMapLS(lpFontData);
|
|
|
|
if (lpFontIndex)
|
|
UnMapLS(lpFontIndex);
|
|
|
|
DebugExitVOID(OEDDSetNewFonts);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// UTILITY ROUTINES
|
|
//
|
|
|
|
|
|
//
|
|
// OEGetPolarity()
|
|
// Gets the axes polarity signs.
|
|
//
|
|
// NOTE that we fill in the ptPolarity field of our OESTATE global, to
|
|
// save on stack.
|
|
//
|
|
void OEGetPolarity(void)
|
|
{
|
|
SIZE WindowExtent;
|
|
SIZE ViewportExtent;
|
|
|
|
DebugEntry(OEGetPolarity);
|
|
|
|
switch (GetMapMode(g_oeState.hdc))
|
|
{
|
|
case MM_ANISOTROPIC:
|
|
case MM_ISOTROPIC:
|
|
GetWindowExtEx(g_oeState.hdc, &WindowExtent);
|
|
GetViewportExtEx(g_oeState.hdc, &ViewportExtent);
|
|
|
|
if ((ViewportExtent.cx < 0) == (WindowExtent.cx < 0))
|
|
g_oeState.ptPolarity.x = 1;
|
|
else
|
|
g_oeState.ptPolarity.x = -1;
|
|
|
|
if ((ViewportExtent.cy < 0) == (WindowExtent.cy < 0))
|
|
g_oeState.ptPolarity.y = 1;
|
|
else
|
|
g_oeState.ptPolarity.y = -1;
|
|
break;
|
|
|
|
case MM_HIENGLISH:
|
|
case MM_HIMETRIC:
|
|
case MM_LOENGLISH:
|
|
case MM_LOMETRIC:
|
|
case MM_TWIPS:
|
|
g_oeState.ptPolarity.x = 1;
|
|
g_oeState.ptPolarity.y = -1;
|
|
break;
|
|
|
|
default:
|
|
g_oeState.ptPolarity.x = 1;
|
|
g_oeState.ptPolarity.y = 1;
|
|
break;
|
|
}
|
|
|
|
DebugExitVOID(OEGetPolarity);
|
|
}
|
|
|
|
|
|
//
|
|
// OEGetState()
|
|
// This sets up the fields in the g_oeState global, depending on what
|
|
// a particular DDI needs. That is conveyed via the flags.
|
|
//
|
|
void OEGetState
|
|
(
|
|
UINT uFlags
|
|
)
|
|
{
|
|
DWORD dwOrg;
|
|
|
|
DebugEntry(OEGetState);
|
|
|
|
if (uFlags & OESTATE_COORDS)
|
|
{
|
|
dwOrg = GetDCOrg(g_oeState.hdc);
|
|
g_oeState.ptDCOrg.x = LOWORD(dwOrg);
|
|
g_oeState.ptDCOrg.y = HIWORD(dwOrg);
|
|
|
|
OEGetPolarity();
|
|
}
|
|
|
|
if (uFlags & OESTATE_PEN)
|
|
{
|
|
// Try to get the pen data
|
|
if (!GetObject(g_oeState.lpdc->hPen, sizeof(g_oeState.logPen),
|
|
&g_oeState.logPen))
|
|
{
|
|
ERROR_OUT(("Couldn't get pen info"));
|
|
g_oeState.logPen.lopnWidth.x = 1;
|
|
g_oeState.logPen.lopnWidth.y = 1;
|
|
g_oeState.logPen.lopnStyle = PS_NULL;
|
|
uFlags &= ~OESTATE_PEN;
|
|
}
|
|
}
|
|
|
|
if (uFlags & OESTATE_BRUSH)
|
|
{
|
|
// Try to get the brush data
|
|
if (!GetObject(g_oeState.lpdc->hBrush, sizeof(g_oeState.logBrush),
|
|
&g_oeState.logBrush))
|
|
{
|
|
ERROR_OUT(("Couldn't get brush info"));
|
|
g_oeState.logBrush.lbStyle = BS_NULL;
|
|
uFlags &= ~OESTATE_BRUSH;
|
|
}
|
|
}
|
|
|
|
if (uFlags & OESTATE_FONT)
|
|
{
|
|
// Try to get the logfont data
|
|
if (!GetObject(g_oeState.lpdc->hFont, sizeof(g_oeState.logFont),
|
|
&g_oeState.logFont))
|
|
{
|
|
ERROR_OUT(("Gouldn't get font info"));
|
|
|
|
//
|
|
// Fill in an empty face name
|
|
//
|
|
g_oeState.logFont.lfFaceName[0] = 0;
|
|
uFlags &= ~OESTATE_FONT;
|
|
}
|
|
else
|
|
{
|
|
GetTextMetrics(g_oeState.hdc, &g_oeState.tmFont);
|
|
g_oeState.tmAlign = GetTextAlign(g_oeState.hdc);
|
|
}
|
|
}
|
|
|
|
if (uFlags & OESTATE_REGION)
|
|
{
|
|
DWORD cbSize;
|
|
|
|
cbSize = GetRegionData(g_oeState.lpdc->hRaoClip,
|
|
sizeof(g_oeState.rgnData), (LPRGNDATA)&g_oeState.rgnData);
|
|
if (cbSize > sizeof(g_oeState.rgnData))
|
|
{
|
|
WARNING_OUT(("Clip region %04x is too big, unclipped drawing may result"));
|
|
}
|
|
|
|
if (!cbSize || (cbSize > sizeof(g_oeState.rgnData)))
|
|
{
|
|
// Bound box is best we can do.
|
|
RECT rcBound;
|
|
|
|
if (GetRgnBox(g_oeState.lpdc->hRaoClip, &rcBound) <= NULLREGION)
|
|
{
|
|
WARNING_OUT(("Couldn't even get bounding box of Clip region"));
|
|
SetRectEmpty(&rcBound);
|
|
}
|
|
|
|
g_oeState.rgnData.rdh.iType = SIMPLEREGION;
|
|
g_oeState.rgnData.rdh.nRgnSize = sizeof(RDH) + sizeof(RECTL);
|
|
g_oeState.rgnData.rdh.nRectL = 1;
|
|
RECT_TO_RECTL(&rcBound, &g_oeState.rgnData.rdh.arclBounds);
|
|
RECT_TO_RECTL(&rcBound, g_oeState.rgnData.arclPieces);
|
|
}
|
|
}
|
|
|
|
g_oeState.uFlags |= uFlags;
|
|
|
|
DebugExitVOID(OEGetState);
|
|
}
|
|
|
|
|
|
//
|
|
// OEPolarityAdjust()
|
|
// This swaps the coordinates of a rectangle based on the sign polarity.
|
|
//
|
|
// NOTE: We use the g_oeState polarity field. So this function assumes
|
|
// polarity is setup already.
|
|
//
|
|
void OEPolarityAdjust
|
|
(
|
|
LPRECT aRects,
|
|
UINT cRects
|
|
)
|
|
{
|
|
int tmp;
|
|
|
|
DebugEntry(OEPolarityAdjust);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_COORDS);
|
|
|
|
while (cRects > 0)
|
|
{
|
|
if (g_oeState.ptPolarity.x < 0)
|
|
{
|
|
// Swap left & right
|
|
tmp = aRects->left;
|
|
aRects->left = aRects->right;
|
|
aRects->right = tmp;
|
|
}
|
|
|
|
if (g_oeState.ptPolarity.y < 0)
|
|
{
|
|
// Swap top & bottom
|
|
tmp = aRects->top;
|
|
aRects->top = aRects->bottom;
|
|
aRects->bottom = tmp;
|
|
}
|
|
|
|
cRects--;
|
|
aRects++;
|
|
}
|
|
|
|
DebugExitVOID(OEPolarityAdjust);
|
|
}
|
|
|
|
|
|
//
|
|
// OECheckOrder()
|
|
// This checks for the common stuff that all the DDIs do before deciding
|
|
// to send an order or accumulate screen data.
|
|
//
|
|
BOOL OECheckOrder
|
|
(
|
|
DWORD order,
|
|
UINT flags
|
|
)
|
|
{
|
|
if (!OE_SendAsOrder(order))
|
|
return(FALSE);
|
|
|
|
if ((flags & OECHECK_PEN) && !OECheckPenIsSimple())
|
|
return(FALSE);
|
|
|
|
if ((flags & OECHECK_BRUSH) && !OECheckBrushIsSimple())
|
|
return(FALSE);
|
|
|
|
if ((flags & OECHECK_CLIPPING) && OEClippingIsComplex())
|
|
return(FALSE);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
//
|
|
// OELPtoVirtual()
|
|
// Converts coords from logical to device (pixels). This does map mode
|
|
// then translation offsets.
|
|
//
|
|
void OELPtoVirtual
|
|
(
|
|
HDC hdc,
|
|
LPPOINT aPts,
|
|
UINT cPts
|
|
)
|
|
{
|
|
LONG l;
|
|
int s;
|
|
|
|
DebugEntry(OELPtoVirtual);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_COORDS);
|
|
|
|
ASSERT(hdc == g_oeState.hdc);
|
|
|
|
//
|
|
// Convert to pixels
|
|
//
|
|
LPtoDP(hdc, aPts, cPts);
|
|
|
|
//
|
|
// Use the device origin, so we can convert from DC-relative to screen
|
|
// coords.
|
|
//
|
|
|
|
while (cPts > 0)
|
|
{
|
|
//
|
|
// Prevent overflow
|
|
//
|
|
l = (LONG)aPts->x + (LONG)g_oeState.ptDCOrg.x;
|
|
s = (int)l;
|
|
|
|
if (l == (LONG)s)
|
|
{
|
|
aPts->x = s;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// HIWORD(l) will be 1 for positive overflow, 0xFFFF for
|
|
// negative overflow. Therefore we will get 0x7FFE or 0x8000
|
|
// (+32766 or -32768).
|
|
//
|
|
aPts->x = 0x7FFF - HIWORD(l);
|
|
TRACE_OUT(("adjusted X from %ld to %d", l, aPts->x));
|
|
}
|
|
|
|
//
|
|
// Look for int overflow in the Y coordinate
|
|
//
|
|
l = (LONG)aPts->y + (LONG)g_oeState.ptDCOrg.y;
|
|
s = (int)l;
|
|
|
|
if (l == (LONG)s)
|
|
{
|
|
aPts->y = s;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// HIWORD(l) will be 1 for positive overflow, 0xFFFF for
|
|
// negative overflow. Therefore we will get 0x7FFE or 0x8000
|
|
// (+32766 or -32768).
|
|
//
|
|
aPts->y = 0x7FFF - HIWORD(l);
|
|
TRACE_OUT(("adjusted Y from %ld to %d", l, aPts->y));
|
|
}
|
|
|
|
//
|
|
// Move on to the next point
|
|
//
|
|
--cPts;
|
|
++aPts;
|
|
}
|
|
|
|
DebugExitVOID(OELPtoVirtual);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OELRtoVirtual
|
|
//
|
|
// Adjusts RECT in window coordinates to virtual coordinates. Clips the
|
|
// result to [+32766, -32768] which is near enough to [+32767, -32768]
|
|
//
|
|
// NB. This function takes a Windows rectangle (Exclusive coords) and
|
|
// returns a DC-Share rectangle (inclusive coords).
|
|
// This means that any calling function can safely convert to inclusive
|
|
// without having to worry above overflowing.
|
|
//
|
|
void OELRtoVirtual
|
|
(
|
|
HDC hdc,
|
|
LPRECT aRects,
|
|
UINT cRects
|
|
)
|
|
{
|
|
int temp;
|
|
|
|
DebugEntry(OELRtoVirtual);
|
|
|
|
//
|
|
// Convert the points to screen coords, clipping to INT16s
|
|
//
|
|
OELPtoVirtual(hdc, (LPPOINT)aRects, 2 * cRects);
|
|
|
|
//
|
|
// Make each rectangle inclusive
|
|
//
|
|
while (cRects > 0)
|
|
{
|
|
//
|
|
// LAURABU BOGUS!
|
|
// Use OEPolarityAdjust() instead, this is safer.
|
|
//
|
|
|
|
//
|
|
// If the rect is bad then flip the edges. This will be the case
|
|
// if the LP coordinate system is running in a different direction
|
|
// than the device coordinate system.
|
|
//
|
|
if (aRects->left > aRects->right)
|
|
{
|
|
TRACE_OUT(("Flipping x coords"));
|
|
|
|
temp = aRects->left;
|
|
aRects->left = aRects->right;
|
|
aRects->right = temp;
|
|
}
|
|
|
|
if (aRects->top > aRects->bottom)
|
|
{
|
|
TRACE_OUT(("Flipping y coords"));
|
|
|
|
temp = aRects->top;
|
|
aRects->top = aRects->bottom;
|
|
aRects->bottom = temp;
|
|
}
|
|
|
|
aRects->right--;
|
|
aRects->bottom--;
|
|
|
|
//
|
|
// Move on to the next rect
|
|
//
|
|
cRects--;
|
|
aRects++;
|
|
}
|
|
|
|
DebugExitVOID(OELRtoVirtual);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OE_SendAsOrder()
|
|
//
|
|
BOOL OE_SendAsOrder(DWORD order)
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
DebugEntry(OE_SendAsOrder);
|
|
|
|
//
|
|
// Only check the order if we are allowed to send orders in the first
|
|
// place!
|
|
//
|
|
if (g_oeSendOrders)
|
|
{
|
|
TRACE_OUT(("Orders enabled"));
|
|
|
|
//
|
|
// We are sending some orders, so check individual flags.
|
|
//
|
|
rc = (BOOL)g_oeOrderSupported[HIWORD(order)];
|
|
TRACE_OUT(("Send order %lx HIWORD %u", order, HIWORD(order)));
|
|
}
|
|
|
|
DebugExitDWORD(OE_SendAsOrder, rc);
|
|
return(rc);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: OESendRop3AsOrder.
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Checks to see if the rop uses the destination bits. If it does then
|
|
// returns FALSE unless the "send all rops" property flag is set.
|
|
//
|
|
// PARAMETERS: The rop3 to be checked (in protocol format ie a byte).
|
|
//
|
|
// RETURNS: TRUE if the rop3 should be sent as an order.
|
|
//
|
|
//
|
|
BOOL OESendRop3AsOrder(BYTE rop3)
|
|
{
|
|
BOOL rc = TRUE;
|
|
|
|
DebugEntry(OESendRop3AsOrder);
|
|
|
|
//
|
|
// Rop 0x5F is used by MSDN to highlight search keywords. This XORs
|
|
// a pattern with the destination, producing markedly different (and
|
|
// sometimes unreadable) shadow output. We special-case no-encoding for
|
|
// it.
|
|
//
|
|
if (rop3 == 0x5F)
|
|
{
|
|
WARNING_OUT(("Rop3 0x5F never encoded"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
DebugExitBOOL(OESendRop3AsOrder, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
//
|
|
// OEPenWidthAdjust()
|
|
//
|
|
// Adjusts a rectangle to allow for the current pen width divided by
|
|
// the divisor, rounding up.
|
|
//
|
|
// NOTE: This routine uses the logPen and ptPolarity fields of g_oeState.
|
|
//
|
|
void OEPenWidthAdjust
|
|
(
|
|
LPRECT lprc,
|
|
UINT divisor
|
|
)
|
|
{
|
|
UINT width;
|
|
UINT roundingFactor = divisor - 1;
|
|
|
|
DebugEntry(OEPenWidthAdjust);
|
|
|
|
width = max(g_oeState.logPen.lopnWidth.x, g_oeState.logPen.lopnWidth.y);
|
|
|
|
InflateRect(lprc,
|
|
((g_oeState.ptPolarity.x * width) +
|
|
(g_oeState.ptPolarity.x * roundingFactor)) / divisor,
|
|
((g_oeState.ptPolarity.y * width) +
|
|
(g_oeState.ptPolarity.x * roundingFactor)) / divisor);
|
|
|
|
DebugExitVOID(OEPenWidthAdjust);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Function: OEExpandColor
|
|
//
|
|
// Description: Converts a generic bitwise representation of an RGB color
|
|
// index into an 8-bit color index as used by the line
|
|
// protocol.
|
|
//
|
|
void OEExpandColor
|
|
(
|
|
LPBYTE lpField,
|
|
DWORD srcColor,
|
|
DWORD mask
|
|
)
|
|
{
|
|
DWORD colorTmp;
|
|
|
|
DebugEntry(OEExpandColor);
|
|
|
|
//
|
|
// Different example bit masks:
|
|
//
|
|
// Normal 24-bit:
|
|
// 0x000000FF (red)
|
|
// 0x0000FF00 (green)
|
|
// 0x00FF0000 (blue)
|
|
//
|
|
// True color 32-bits:
|
|
// 0xFF000000 (red)
|
|
// 0x00FF0000 (green)
|
|
// 0x0000FF00 (blue)
|
|
//
|
|
// 5-5-5 16-bits
|
|
// 0x0000001F (red)
|
|
// 0x000003E0 (green)
|
|
// 0x00007C00 (blue)
|
|
//
|
|
// 5-6-5 16-bits
|
|
// 0x0000001F (red)
|
|
// 0x000007E0 (green)
|
|
// 0x0000F800 (blue)
|
|
//
|
|
//
|
|
// Convert the color using the following algorithm.
|
|
//
|
|
// <new color> = <old color> * <new bpp mask> / <old bpp mask>
|
|
//
|
|
// where:
|
|
//
|
|
// new bpp mask = mask for all bits at new setting (0xFF for 8bpp)
|
|
//
|
|
// This way maximal (eg. 0x1F) and minimal (eg. 0x00) settings are
|
|
// converted into the correct 8-bit maximum and minimum.
|
|
//
|
|
// Rearranging the above equation we get:
|
|
//
|
|
// <new color> = (<old color> & <old bpp mask>) * 0xFF / <old bpp mask>
|
|
//
|
|
// where:
|
|
//
|
|
// <old bpp mask> = mask for the color
|
|
//
|
|
|
|
//
|
|
// LAURABU BOGUS:
|
|
// We need to avoid overflow caused by the multiply. NOTE: in theory
|
|
// we should use a double, but that's painfully slow. So for now hack
|
|
// it. If the HIBYTE is set, just right shift 24 bits.
|
|
//
|
|
colorTmp = srcColor & mask;
|
|
if (colorTmp & 0xFF000000)
|
|
colorTmp >>= 24;
|
|
else
|
|
colorTmp = (colorTmp * 0xFF) / mask;
|
|
*lpField = (BYTE)colorTmp;
|
|
|
|
TRACE_OUT(( "0x%lX -> 0x%X", srcColor, (WORD)*lpField));
|
|
|
|
DebugExitVOID(OEExpandColor);
|
|
}
|
|
|
|
|
|
//
|
|
// OEConvertColor()
|
|
// Converts a PHYSICAL color to a real RGB
|
|
//
|
|
void OEConvertColor
|
|
(
|
|
DWORD rgb,
|
|
LPTSHR_COLOR lptshrDst,
|
|
BOOL fAllowDither
|
|
)
|
|
{
|
|
DWORD rgbConverted;
|
|
PALETTEENTRY pe;
|
|
int pal;
|
|
DWORD numColors;
|
|
|
|
DebugEntry(OEConvertColor);
|
|
|
|
rgbConverted = rgb;
|
|
|
|
//
|
|
// Get the current palette size.
|
|
//
|
|
GetObject(g_oeState.lpdc->hPal, sizeof(pal), &pal);
|
|
if (pal == 0)
|
|
{
|
|
//
|
|
// GDI has a bug. It allows a ResizePalette() call to set a new
|
|
// size of zero for the palette. If you subsequently make
|
|
// certain palette manager calls on such a palette, GDI will fault.
|
|
//
|
|
// To avoid this problem, as seen in 3D Kitchen by Books that Work,
|
|
// we check for this case and simply return the input color.
|
|
//
|
|
WARNING_OUT(("Zero-sized palette"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (g_oeState.lpdc->hPal == g_oeStockPalette)
|
|
{
|
|
//
|
|
// Quattro Pro and others put junk in the high bits of their colors.
|
|
// We need to mask it out.
|
|
//
|
|
if (rgb & 0xFC000000)
|
|
{
|
|
rgb &= 0x00FFFFFF;
|
|
}
|
|
else
|
|
{
|
|
if (rgb & PALETTERGB_FLAG)
|
|
{
|
|
//
|
|
// Using PALETTERGB is just like using an RGB, turn it off.
|
|
// The color will be dithered, if necessary, using the
|
|
// default system colors.
|
|
//
|
|
rgb &= 0x01FFFFFF;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rgb & COLOR_FLAGS)
|
|
{
|
|
if (rgb & PALETTERGB_FLAG)
|
|
{
|
|
pal = GetNearestPaletteIndex(g_oeState.lpdc->hPal, rgb);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(rgb & PALETTEINDEX_FLAG);
|
|
pal = LOWORD(rgb);
|
|
}
|
|
|
|
//
|
|
// Look up entry in palette.
|
|
//
|
|
if (!GetPaletteEntries(g_oeState.lpdc->hPal, pal, 1, &pe))
|
|
{
|
|
ERROR_OUT(("GetPaletteEntries failed for index %d", pal));
|
|
*((LPDWORD)&pe) = 0L;
|
|
}
|
|
else if (pe.peFlags & PC_EXPLICIT)
|
|
{
|
|
//
|
|
// If this is PC_EXPLICIT, it's an index into the system
|
|
// palette.
|
|
//
|
|
pal = LOWORD(*((LPDWORD)&pe));
|
|
|
|
if (g_osiScreenBPP < 32)
|
|
{
|
|
numColors = 1L << g_osiScreenBPP;
|
|
}
|
|
else
|
|
{
|
|
numColors = 0xFFFFFFFF;
|
|
}
|
|
|
|
if (numColors > 256)
|
|
{
|
|
//
|
|
// We are on a direct color device. What does explicit
|
|
// mean in this case? The answer is, use the VGA color
|
|
// palette.
|
|
//
|
|
pe = g_osiVgaPalette[pal % 16];
|
|
}
|
|
else
|
|
{
|
|
pal %= numColors;
|
|
|
|
GetSystemPaletteEntries(g_oeState.hdc, pal, 1, &pe);
|
|
}
|
|
}
|
|
|
|
rgbConverted = *((LPDWORD)&pe);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
//
|
|
// To get the correct results for any RGBs we send to true color systems,
|
|
// we need to normalize the RGB to an exact palette match on the local
|
|
// system. This is because we aren't guaranteed that the RGB on the
|
|
// local will have an exact match to the current system palette. If
|
|
// not, then GDI will convert them locally, but the orders will send
|
|
// to remotes will be displayed exactly, resulting in a mismatch.
|
|
//
|
|
if ((g_osiScreenBPP == 8) &&
|
|
!(rgb & COLOR_FLAGS) &&
|
|
(!fAllowDither || (g_oeState.lpdc->hPal != g_oeStockPalette)))
|
|
{
|
|
TSHR_RGBQUAD rgq;
|
|
|
|
rgbConverted &= 0x00FFFFFF;
|
|
|
|
//
|
|
// Common cases.
|
|
//
|
|
if ((rgbConverted == RGB(0, 0, 0)) ||
|
|
(rgbConverted == RGB(0xFF, 0xFF, 0xFF)))
|
|
{
|
|
goto ReallyConverted;
|
|
}
|
|
|
|
//
|
|
// g_osiScreenBMI.bmiHeader is already filled in.
|
|
//
|
|
|
|
//
|
|
// NOTE:
|
|
// We don't need or want to realize any palettes. We want color
|
|
// mapping based on the current screen palette contents.
|
|
//
|
|
// We disable SetPixel() patch, or our trap will trash the
|
|
// variables for this call.
|
|
//
|
|
|
|
//
|
|
// g_osiMemoryDC() always has our 1x1 color bitmap g_osiMemoryBMP
|
|
// selected into it.
|
|
//
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_SETPIXEL], PATCH_DISABLE);
|
|
SetPixel(g_osiMemoryDC, 0, 0, rgbConverted);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_SETPIXEL], PATCH_ENABLE);
|
|
|
|
//
|
|
// Get mapped color index
|
|
//
|
|
GetDIBits(g_osiMemoryDC, g_osiMemoryBMP, 0, 1, &pal,
|
|
(LPBITMAPINFO)&g_osiScreenBMI, DIB_RGB_COLORS);
|
|
|
|
rgq = g_osiScreenBMI.bmiColors[LOBYTE(pal)];
|
|
|
|
OTRACE(("Mapped color %08lx to %08lx", rgbConverted,
|
|
RGB(rgq.rgbRed, rgq.rgbGreen, rgq.rgbBlue)));
|
|
|
|
rgbConverted = RGB(rgq.rgbRed, rgq.rgbGreen, rgq.rgbBlue);
|
|
}
|
|
|
|
ReallyConverted:
|
|
lptshrDst->red = GetRValue(rgbConverted);
|
|
lptshrDst->green = GetGValue(rgbConverted);
|
|
lptshrDst->blue = GetBValue(rgbConverted);
|
|
|
|
DebugExitVOID(OEConvertColor);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OEGetBrushInfo()
|
|
// Standard brush goop
|
|
//
|
|
void OEGetBrushInfo
|
|
(
|
|
LPTSHR_COLOR pBack,
|
|
LPTSHR_COLOR pFore,
|
|
LPTSHR_UINT32 pStyle,
|
|
LPTSHR_UINT32 pHatch,
|
|
LPBYTE pExtra
|
|
)
|
|
{
|
|
int iRow;
|
|
|
|
DebugEntry(OEGetBrushInfo);
|
|
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.bkColorL, pBack, FALSE);
|
|
|
|
*pStyle = g_oeState.logBrush.lbStyle;
|
|
|
|
if (g_oeState.logBrush.lbStyle == BS_PATTERN)
|
|
{
|
|
//
|
|
// We only track mono patterns, so the foreground color is the
|
|
// brush color.
|
|
//
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.txColorL, pFore, FALSE);
|
|
|
|
// For pattern brushes, the hatch stores the 1st pattern byte,
|
|
// the Extra field the remaining 7 pattern bytes
|
|
*pHatch = g_oeState.logBrushExtra[0];
|
|
hmemcpy(pExtra, g_oeState.logBrushExtra+1, TRACKED_BRUSH_SIZE-1);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(g_oeState.logBrush.lbStyle != BS_DIBPATTERN);
|
|
|
|
OEConvertColor(g_oeState.logBrush.lbColor, pFore, TRUE);
|
|
|
|
// The hatch is the hatch style
|
|
*pHatch = g_oeState.logBrush.lbHatch;
|
|
|
|
// Extra info is empty
|
|
for (iRow = 0; iRow < TRACKED_BRUSH_SIZE-1; iRow++)
|
|
{
|
|
pExtra[iRow] = 0;
|
|
}
|
|
}
|
|
|
|
DebugExitVOID(OEGetBrushInfo);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OEClippingIsSimple()
|
|
//
|
|
BOOL OEClippingIsSimple(void)
|
|
{
|
|
BOOL fSimple;
|
|
RECT rc;
|
|
|
|
DebugEntry(OEClippingIsSimple);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_REGION);
|
|
|
|
fSimple = (g_oeState.rgnData.rdh.nRectL <= 1);
|
|
|
|
DebugExitBOOL(OEClippingIsSimple, fSimple);
|
|
return(fSimple);
|
|
}
|
|
|
|
//
|
|
// OEClippingIsComplex()
|
|
//
|
|
BOOL OEClippingIsComplex(void)
|
|
{
|
|
BOOL fComplex;
|
|
|
|
DebugEntry(OEClippingIsComplex);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_REGION);
|
|
|
|
fComplex = (g_oeState.rgnData.rdh.nRgnSize >=
|
|
sizeof(RDH) + CRECTS_COMPLEX*sizeof(RECTL));
|
|
|
|
DebugExitBOOL(OEClippingIsComplex, fComplex);
|
|
return(fComplex);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OECheckPenIsSimple()
|
|
//
|
|
BOOL OECheckPenIsSimple(void)
|
|
{
|
|
POINT ptArr[2];
|
|
BOOL fSimple;
|
|
|
|
DebugEntry(OECheckPenIsSimple);
|
|
|
|
if (g_oeState.uFlags & OESTATE_PEN)
|
|
{
|
|
ptArr[0].x = ptArr[0].y = 0;
|
|
ptArr[1].x = g_oeState.logPen.lopnWidth.x;
|
|
ptArr[1].y = 0;
|
|
|
|
LPtoDP(g_oeState.hdc, ptArr, 2);
|
|
|
|
fSimple = ((ptArr[1].x - ptArr[0].x) <= 1);
|
|
}
|
|
else
|
|
{
|
|
// The current pen in the DC is invalid
|
|
WARNING_OUT(("Invalid pen selected into DC"));
|
|
fSimple = FALSE;
|
|
}
|
|
|
|
DebugExitBOOL(OECheckPenIsSimple, fSimple);
|
|
return(fSimple);
|
|
}
|
|
|
|
|
|
//
|
|
// OECheckBrushIsSimple()
|
|
//
|
|
BOOL OECheckBrushIsSimple(void)
|
|
{
|
|
BOOL fSimple;
|
|
|
|
DebugEntry(OECheckBrushIsSimple);
|
|
|
|
// Assume not simple
|
|
fSimple = FALSE;
|
|
|
|
if (g_oeState.uFlags & OESTATE_BRUSH)
|
|
{
|
|
//
|
|
// If the brush is a pattern, it's OK if one of standard pattern
|
|
// brushes. If it comes from a DIB, it's never OK. All other
|
|
// brushes are OK.
|
|
//
|
|
if (g_oeState.logBrush.lbStyle == BS_PATTERN)
|
|
{
|
|
LPGDIHANDLE lpgh;
|
|
LPBRUSH lpBrush;
|
|
LPBITMAP lpPattern;
|
|
|
|
//
|
|
// For pattern brushes, the lbHatch field of the ilBrushOverhead
|
|
// item in the GDI local BRUSH object is a global handle to
|
|
// a memory block that is the BITMAP of the thing.
|
|
//
|
|
|
|
//
|
|
// BOGUS LAURABU:
|
|
// NM 2.0 Win95 went to a lot more work to check if a color bitmap
|
|
// pattern brush had only 2 colors and therefore was orderable. But
|
|
// I can't find a single that uses such a thing. So for now, we just
|
|
// care if the pattern bitmap is monochrome and the pattern is between 8x8 and
|
|
// 16x8.
|
|
//
|
|
|
|
// Get a pointer to the brush data
|
|
lpgh = MAKELP(g_hInstGdi16, g_oeState.lpdc->hBrush);
|
|
ASSERT(!IsBadReadPtr(lpgh, sizeof(DWORD)));
|
|
ASSERT(!(lpgh->objFlags & OBJFLAGS_SWAPPEDOUT));
|
|
|
|
lpBrush = MAKELP(g_hInstGdi16, lpgh->pGdiObj);
|
|
ASSERT(!IsBadReadPtr(lpBrush, sizeof(BRUSH)));
|
|
|
|
// Get the bitmapinfo handle -- it's the lbHatch field
|
|
lpPattern = MAKELP(lpBrush->ilBrushOverhead.lbHatch, 0);
|
|
|
|
//
|
|
// Macromedia Director among others creates pattern brushes
|
|
// with no pattern. We therefore consider these objects to
|
|
// be too complex to send in an order
|
|
//
|
|
|
|
//
|
|
// Is this monochrome with a pattern between 8 and 16 pels?
|
|
// We save the left 8 pixel grid if so.
|
|
//
|
|
if (!IsBadReadPtr(lpPattern, sizeof(BITMAP)) &&
|
|
(lpPattern->bmWidth >= MIN_BRUSH_WIDTH) &&
|
|
(lpPattern->bmWidth <= MAX_BRUSH_WIDTH) &&
|
|
(lpPattern->bmHeight == TRACKED_BRUSH_HEIGHT) &&
|
|
(lpPattern->bmPlanes == 1) && (lpPattern->bmBitsPixel == 1))
|
|
{
|
|
LPUINT lpRow;
|
|
int iRow;
|
|
|
|
// Save the pattern away in logBrushExtra
|
|
lpRow = lpPattern->bmBits;
|
|
ASSERT(!IsBadReadPtr(lpRow, TRACKED_BRUSH_HEIGHT*sizeof(UINT)));
|
|
|
|
//
|
|
// The pattern is always WORD aligned. But only the
|
|
// LOBYTE has meaning.
|
|
//
|
|
// NOTE:
|
|
// We fill the pattern in DIB order, namely bottom to
|
|
// top.
|
|
//
|
|
ASSERT(lpPattern->bmWidthBytes == 2);
|
|
for (iRow = 0; iRow < TRACKED_BRUSH_HEIGHT; iRow++, lpRow++)
|
|
{
|
|
g_oeState.logBrushExtra[TRACKED_BRUSH_HEIGHT - 1 - iRow] =
|
|
(BYTE)*lpRow;
|
|
}
|
|
|
|
fSimple = TRUE;
|
|
}
|
|
}
|
|
else if (g_oeState.logBrush.lbStyle != BS_DIBPATTERN)
|
|
{
|
|
fSimple = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("Invalid brush selected into DC"));
|
|
}
|
|
|
|
DebugExitBOOL(OECheckBrushIsSimple, fSimple);
|
|
return(fSimple);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// OEAddLine()
|
|
// This calculates the bounds of a line output call, and either adds an
|
|
// order or gets set for screen data accum.
|
|
//
|
|
void OEAddLine
|
|
(
|
|
POINT ptStart,
|
|
POINT ptEnd
|
|
)
|
|
{
|
|
LPINT_ORDER pOrder;
|
|
LPLINETO_ORDER pLineTo;
|
|
|
|
DebugEntry(OEAddLine);
|
|
|
|
//
|
|
// Get the bounds
|
|
//
|
|
g_oeState.rc.left = min(ptStart.x, ptEnd.x);
|
|
g_oeState.rc.top = min(ptStart.y, ptEnd.y);
|
|
g_oeState.rc.right = max(ptStart.x, ptEnd.x);
|
|
g_oeState.rc.bottom = max(ptStart.y, ptEnd.y);
|
|
|
|
//
|
|
// Adjust for axes polarity and pen dimensions
|
|
//
|
|
ASSERT(g_oeState.uFlags & OESTATE_COORDS);
|
|
|
|
OEPolarityAdjust(&g_oeState.rc, 1);
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
|
|
//
|
|
// OEPenWidthAdjust returns an inclusive rect. But OELRtoVirtual
|
|
// expects an exclusive. After it returns, we need to add back
|
|
// the extra subtraction.
|
|
//
|
|
// NOTE that OELRtoVirtual also adjusts for virtual desktop origin.
|
|
//
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
g_oeState.rc.right++;
|
|
g_oeState.rc.bottom++;
|
|
|
|
//
|
|
// Now we have the true draw bounds. Can we send this as an order?
|
|
//
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_LINETO, OECHECK_PEN | OECHECK_CLIPPING))
|
|
{
|
|
//
|
|
// We can send an order.
|
|
//
|
|
pOrder = OA_DDAllocOrderMem(sizeof(LINETO_ORDER), 0);
|
|
if (!pOrder)
|
|
DC_QUIT;
|
|
|
|
pLineTo = (LPLINETO_ORDER)pOrder->abOrderData;
|
|
|
|
pLineTo->type = LOWORD(ORD_LINETO);
|
|
|
|
//
|
|
// Must do this first: oords in the LINETO order are 32-bit
|
|
//
|
|
OELPtoVirtual(g_oeState.hdc, &ptStart, 1);
|
|
OELPtoVirtual(g_oeState.hdc, &ptEnd, 1);
|
|
|
|
pLineTo->nXStart = ptStart.x;
|
|
pLineTo->nYStart = ptStart.y;
|
|
pLineTo->nXEnd = ptEnd.x;
|
|
pLineTo->nYEnd = ptEnd.y;
|
|
|
|
//
|
|
// This is a physical color
|
|
//
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.bkColorL,
|
|
&pLineTo->BackColor, FALSE);
|
|
|
|
pLineTo->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pLineTo->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
pLineTo->PenStyle = g_oeState.logPen.lopnStyle;
|
|
|
|
//
|
|
// Currently only pen withs of 1 are supported. Unfortunately
|
|
// GDI left it up to the driver to decide on how to stroke the
|
|
// line, so we can't predict what pixels will be on or off for
|
|
// pen widths bigger.
|
|
//
|
|
pLineTo->PenWidth = 1;
|
|
|
|
//
|
|
// This is a logical color
|
|
//
|
|
OEConvertColor(g_oeState.logPen.lopnColor, &pLineTo->PenColor,
|
|
FALSE);
|
|
|
|
//
|
|
// Store the general order data.
|
|
//
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
//
|
|
// This will add in OESTATE_SENTORDER if it succeeded.
|
|
// Then OEDDPostStopAccum() will ignore screen data, or
|
|
// will add our nicely calculated bounds above in instead.
|
|
//
|
|
OTRACE(("Line: Start {%d, %d}, End {%d, %d}", ptStart.x, ptStart.y,
|
|
ptEnd.x, ptEnd.y));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("Line: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
DebugExitVOID(OEAddLine);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// OEValidateDC()
|
|
// This makes sure the thing passed in is a valid DC and gets a pointer to
|
|
// the DC data structure in GDI if so. We need to handle the (rare) case
|
|
// of the DC being swapped out to GDI's extended flat memory space as well
|
|
// as the HDC being prsent in GDI's 16-bit dataseg
|
|
//
|
|
// NOTE:
|
|
// It is NOT valid to hang on to a LPDC around a GDI call. Something may
|
|
// be swapped out before the call, then get swapped in after the call.
|
|
// In which case the original based32 ptr gets freed. And vice-versa, the
|
|
// original GDI dc-16 localptr may get realloced small.
|
|
//
|
|
// In normal usage, this is very fast. Only in low memory (or when
|
|
// parameters are invalid) does doing this twice even matter.
|
|
//
|
|
LPDC OEValidateDC
|
|
(
|
|
HDC hdc,
|
|
BOOL fSrc
|
|
)
|
|
{
|
|
LPDC lpdc = NULL;
|
|
LPGDIHANDLE lpgh;
|
|
DWORD dwBase;
|
|
|
|
DebugEntry(OEDDValidateDC);
|
|
|
|
if (IsGDIObject(hdc) != GDIOBJ_DC)
|
|
{
|
|
//
|
|
// This is a metafile HDC, an IC, or just a plain old bad param.
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// OK. The HDC is a local handle to two words in GDI's DS:
|
|
// * 1st is actual ptr of DC (or local32 handle if swapped out)
|
|
// * 2nd is flags
|
|
//
|
|
// NOTE:
|
|
// Gdi's data segment is already GlobalFixed(). So we don't have to
|
|
// worry about it moving.
|
|
//
|
|
lpgh = MAKELP(g_hInstGdi16, hdc);
|
|
if (lpgh->objFlags & OBJFLAGS_SWAPPEDOUT)
|
|
{
|
|
UINT uSel;
|
|
|
|
//
|
|
// This is an error only so we can actually stop when we hit this
|
|
// rare case and make sure our code is working!
|
|
//
|
|
WARNING_OUT(("DC is swapped out, getting at far heap info"));
|
|
|
|
//
|
|
// Need to make our cached selector point at this thing. NOTE that
|
|
// in OEDDStopAccum, we need to reget lpdc since it will have been
|
|
// swapped in during the output call.
|
|
//
|
|
|
|
dwBase = GetSelectorBase((UINT)g_hInstGdi16);
|
|
ASSERT(dwBase);
|
|
|
|
uSel = (fSrc ? g_oeSelSrc : g_oeSelDst);
|
|
SetSelectorBase(uSel, dwBase + 0x10000);
|
|
|
|
//
|
|
// The pGdiObj is the local32 handle. GDI:10000+pGdiObj has a DWORD
|
|
// which is the based32 address, relative to GDI's dataseg, of the DC.
|
|
// We've set the base of our selector 64K higher than GDI, so we can
|
|
// use it as an offset directly.
|
|
//
|
|
ASSERT(!IsBadReadPtr(MAKELP(uSel, lpgh->pGdiObj), sizeof(DWORD)));
|
|
dwBase = *(LPDWORD)MAKELP(uSel, lpgh->pGdiObj);
|
|
|
|
//
|
|
// The 16-bit base is the nearest 64K less than this 32-bit pointer,
|
|
// above GDI's ds.
|
|
//
|
|
SetSelectorBase(uSel, GetSelectorBase((UINT)g_hInstGdi16) +
|
|
(dwBase & 0xFFFF0000));
|
|
|
|
//
|
|
// Remainder is slop past 64K.
|
|
//
|
|
lpdc = MAKELP(uSel, LOWORD(dwBase));
|
|
}
|
|
else
|
|
{
|
|
lpdc = MAKELP(g_hInstGdi16, lpgh->pGdiObj);
|
|
}
|
|
|
|
ASSERT(!IsBadReadPtr(lpdc, sizeof(DC)));
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(OEDDValidateDC, (DWORD)lpdc);
|
|
return(lpdc);
|
|
}
|
|
|
|
|
|
//
|
|
// OEBeforeDDI()
|
|
//
|
|
// This does all the common stuff at the start of an intercepted DDI call:
|
|
// * Increment the reentrancy count
|
|
// * Disable the patch
|
|
// * Get a ptr to the DC structure (if valid)
|
|
// * Get some attributes about the DC (if valid)
|
|
// * Set up to get the drawing bounds calculated in GDI
|
|
//
|
|
BOOL OEBeforeDDI
|
|
(
|
|
DDI_PATCH ddiType,
|
|
HDC hdcDst,
|
|
UINT uFlags
|
|
)
|
|
{
|
|
LPDC lpdc;
|
|
BOOL fWeCare = FALSE;
|
|
|
|
DebugEntry(OEBeforeDDI);
|
|
|
|
EnableFnPatch(&g_oeDDPatches[ddiType], PATCH_DISABLE);
|
|
if (++g_oeEnterCount > 1)
|
|
{
|
|
TRACE_OUT(("Skipping nested output call"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the destination DC. Since we may have an output
|
|
// call where both the source and dest are swapped out, we may need to
|
|
// use both our cached selectors. Thus, we must to tell OEValidateDC()
|
|
// which DC this is to avoid collision.
|
|
//
|
|
lpdc = OEValidateDC(hdcDst, FALSE);
|
|
if (!SELECTOROF(lpdc))
|
|
{
|
|
TRACE_OUT(("Bogus DC"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Is this a screen DC w/o an active path? When a path is active, the
|
|
// output is being recorded into a path, which is like a region. Then
|
|
// stroking/filling the path can cause output.
|
|
//
|
|
if (!(lpdc->DCFlags & DC_IS_DISPLAY) ||
|
|
(lpdc->fwPath & DCPATH_ACTIVE))
|
|
{
|
|
TRACE_OUT(("Not screen DC"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Only if this is a screen DC do we care about where the output is
|
|
// going to happen. For memory DCs,
|
|
//
|
|
// If this is a bitmap DC or a path is active, we want to mess with
|
|
// the bitmap cache.
|
|
if (lpdc->DCFlags & DC_IS_MEMORY)
|
|
{
|
|
//
|
|
// No screen data or other goop accumulated for non-output calls
|
|
// We just want to do stuff in OEAfterDDI.
|
|
//
|
|
uFlags &= ~OESTATE_DDISTUFF;
|
|
goto WeCareWeReallyCare;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Is this a DC we care about? Our algorithm is:
|
|
// * If sharing the desktop, yes.
|
|
// * If no window associated with DC or window is desktop, maybe.
|
|
// * If window is ancestor of shared window, yes. Else no.
|
|
//
|
|
|
|
if (!g_hetDDDesktopIsShared)
|
|
{
|
|
HWND hwnd;
|
|
HWND hwndP;
|
|
|
|
hwnd = WindowFromDC(hdcDst);
|
|
|
|
//
|
|
// LAURABU:
|
|
// Should we blow off painting into the desktop window? It's
|
|
// either clipped, in which case it's the shell background
|
|
// painting, or it's not, in which case it's the non-full drag
|
|
// dotted lines.
|
|
//
|
|
if (hwnd && (hwnd != g_osiDesktopWindow))
|
|
{
|
|
//
|
|
// If this is our cache, the result is g_oeLastWindowShared.
|
|
// Otherwise, compute it.
|
|
//
|
|
// Note that the HET code clears the cache when the cached
|
|
// window
|
|
// goes away, or any window changes its sharing status since in
|
|
// that case this window may be a descendant and hence not shared.
|
|
//
|
|
if (hwnd != g_oeLastWindow)
|
|
{
|
|
TRACE_OUT(("oeLastWindow cache miss: %04x, now %04x", g_oeLastWindow, hwnd));
|
|
|
|
//
|
|
// Cache this dude. Note that we don't care about
|
|
// visibility, since we know we won't get real painting
|
|
// into an invisible window (it has an empty visrgn).
|
|
//
|
|
g_oeLastWindow = hwnd;
|
|
g_oeLastWindowShared = HET_WindowIsHosted(hwnd);
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("oeLastWindow cache hit: %04x", g_oeLastWindow));
|
|
}
|
|
|
|
//
|
|
// This window isn't shared.
|
|
//
|
|
if (!g_oeLastWindowShared)
|
|
{
|
|
TRACE_OUT(("Output in window %04x: don't care", g_oeLastWindow));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Code from here to WeCareWeReallyCare() is only for screen DCs
|
|
//
|
|
|
|
//
|
|
// For the *TextOut* apis, we want to accumulate DCBs if the font is too
|
|
// complex.
|
|
//
|
|
if (uFlags & OESTATE_SDA_FONTCOMPLEX)
|
|
{
|
|
BOOL fComplex;
|
|
POINT aptCheck[2];
|
|
|
|
fComplex = TRUE;
|
|
|
|
// Get the logfont info
|
|
if (!GetObject(lpdc->hFont, sizeof(g_oeState.logFont), &g_oeState.logFont) ||
|
|
(g_oeState.logFont.lfEscapement != 0))
|
|
goto FontCheckDone;
|
|
|
|
//
|
|
// The font is too complex if it has escapement or the logical units
|
|
// are bigger than pixels.
|
|
//
|
|
// NOTE that NM 2.0 had a bug--it used one point only for non
|
|
// MM_TEXT mode. They did this because they wouldn't get back
|
|
// the same thing passed in, forgetting that LPtoDP takes into
|
|
// account viewport and window origins in addition to scaling.
|
|
//
|
|
// So we do this the right way, using two points and looking at
|
|
// the difference.
|
|
//
|
|
aptCheck[0].x = 0;
|
|
aptCheck[0].y = 0;
|
|
aptCheck[1].x = 1000;
|
|
aptCheck[1].y = 1000;
|
|
|
|
LPtoDP(hdcDst, aptCheck, 2);
|
|
|
|
if ((aptCheck[1].x - aptCheck[0].x <= 1000) ||
|
|
(aptCheck[1].y - aptCheck[0].y <= 1000))
|
|
{
|
|
fComplex = FALSE;
|
|
}
|
|
|
|
FontCheckDone:
|
|
if (fComplex)
|
|
{
|
|
TRACE_OUT(("Font too complex for text order"));
|
|
uFlags |= OESTATE_SDA_DCB;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Some DDIs calculate their own bound rects, which is faster than
|
|
// GDI's BoundsRect() services. But some don't because it's too
|
|
// complicated. In that case, we do it for 'em.
|
|
//
|
|
if (uFlags & OESTATE_SDA_DCB)
|
|
{
|
|
//
|
|
// We don't have to worry about the mapping mode when getting the
|
|
// bounds. The only thing to note is that the return rect is
|
|
// relative to the window org of the DC, and visrgn/clipping occurs
|
|
//
|
|
g_oeState.uGetDCB = GetBoundsRect(hdcDst, &g_oeState.rcDCB, 0);
|
|
g_oeState.uSetDCB = SetBoundsRect(hdcDst, NULL, DCB_ENABLE | DCB_RESET)
|
|
& (DCB_ENABLE | DCB_DISABLE);
|
|
|
|
// No curpos needed if going as screen data, not order
|
|
uFlags &= ~OESTATE_CURPOS;
|
|
}
|
|
|
|
if (uFlags & OESTATE_CURPOS)
|
|
{
|
|
GetCurrentPositionEx(hdcDst, &g_oeState.ptCurPos);
|
|
}
|
|
|
|
WeCareWeReallyCare:
|
|
fWeCare = TRUE;
|
|
g_oeState.uFlags = uFlags;
|
|
g_oeState.hdc = hdcDst;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(OEBeforeDDI, fWeCare);
|
|
return(fWeCare);
|
|
}
|
|
|
|
|
|
//
|
|
// OEAfterDDI()
|
|
//
|
|
// This does all the common things right after a DDI call. It returns TRUE
|
|
// if output happened into a screen DC that we care about.
|
|
//
|
|
BOOL OEAfterDDI
|
|
(
|
|
DDI_PATCH ddiType,
|
|
BOOL fWeCare,
|
|
BOOL fOutput
|
|
)
|
|
{
|
|
DebugEntry(OEAfterDDI);
|
|
|
|
//
|
|
// Reenable patch
|
|
//
|
|
EnableFnPatch(&g_oeDDPatches[ddiType], PATCH_ENABLE);
|
|
--g_oeEnterCount;
|
|
|
|
if (!fWeCare)
|
|
{
|
|
//
|
|
// This was reentrant, we don't care about output into this
|
|
// DC, or something went wrong, bail out.
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
g_oeState.lpdc = OEValidateDC(g_oeState.hdc, FALSE);
|
|
if (!SELECTOROF(g_oeState.lpdc))
|
|
{
|
|
ERROR_OUT(("Bogus DC"));
|
|
DC_QUIT;
|
|
}
|
|
ASSERT(g_oeState.lpdc->DCFlags & DC_IS_DISPLAY);
|
|
ASSERT(!(g_oeState.lpdc->fwPath & DCPATH_ACTIVE));
|
|
|
|
//
|
|
// If this output happened into a memory bitmap, see if it affects
|
|
// SPBs or our sent bitmap cache
|
|
//
|
|
if (g_oeState.lpdc->DCFlags & DC_IS_MEMORY)
|
|
{
|
|
//
|
|
// Don't set fOutput to FALSE for SPB operations, we want
|
|
// BitBlt to look at it.
|
|
//
|
|
if (fOutput)
|
|
{
|
|
// If this is BitBlt, check for SPB creation
|
|
if ((ddiType != DDI_BITBLT) ||
|
|
(g_oeState.lpdc->hBitmap != g_ssiLastSpbBitmap))
|
|
{
|
|
fOutput = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Drawing on the screen that isn't going to be handled in the DDI
|
|
// call.
|
|
//
|
|
if (fOutput && (g_oeState.uFlags & OESTATE_SDA_MASK))
|
|
{
|
|
//
|
|
// We do some common tasks that several DDIs would have to do
|
|
// * take the screen bounds and add as SD
|
|
// * take the draw bounds and add as SD
|
|
//
|
|
OEGetState(OESTATE_COORDS | OESTATE_REGION);
|
|
|
|
if (g_oeState.uFlags & OESTATE_SDA_DCB)
|
|
{
|
|
//
|
|
// Get the drawing bounds
|
|
//
|
|
int mmMode;
|
|
SIZE ptWindowExt;
|
|
SIZE ptViewportExt;
|
|
int uBoundsNew;
|
|
|
|
mmMode = GetMapMode(g_oeState.hdc);
|
|
if (mmMode != MM_TEXT)
|
|
{
|
|
//
|
|
// Changing the map mode whacks the window/view exts
|
|
// So save them so we can replace them when done.
|
|
//
|
|
GetWindowExtEx(g_oeState.hdc, &ptWindowExt);
|
|
GetViewportExtEx(g_oeState.hdc, &ptViewportExt);
|
|
|
|
SetMapMode(g_oeState.hdc, MM_TEXT);
|
|
}
|
|
|
|
//
|
|
// Get the drawing bounds and update them.
|
|
//
|
|
uBoundsNew = GetBoundsRect(g_oeState.hdc, &g_oeState.rc, DCB_RESET);
|
|
|
|
//
|
|
// If no drawing bounds updated, act like no output happened.
|
|
//
|
|
if ((uBoundsNew & DCB_SET) == DCB_RESET)
|
|
{
|
|
fOutput = FALSE;
|
|
}
|
|
else
|
|
{
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
}
|
|
|
|
if (mmMode != MM_TEXT)
|
|
{
|
|
SetMapMode(g_oeState.hdc, mmMode);
|
|
|
|
// Put back the window, viewport exts; SetMapMode wipes them out
|
|
SetWindowExt(g_oeState.hdc, ptWindowExt.cx, ptWindowExt.cy);
|
|
SetViewportExt(g_oeState.hdc, ptViewportExt.cx, ptViewportExt.cy);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(g_oeState.uFlags & OESTATE_SDA_SCREEN);
|
|
|
|
g_oeState.rc.left = g_osiScreenRect.left;
|
|
g_oeState.rc.top = g_osiScreenRect.top;
|
|
g_oeState.rc.right = g_osiScreenRect.right - 1;
|
|
g_oeState.rc.bottom = g_osiScreenRect.bottom - 1;
|
|
}
|
|
|
|
if (fOutput)
|
|
{
|
|
if (g_oeState.uFlags & OESTATE_OFFBYONEHACK)
|
|
g_oeState.rc.bottom++;
|
|
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
|
|
// This way caller won't do anything else.
|
|
fOutput = FALSE;
|
|
}
|
|
|
|
//
|
|
// Put back the draw bounds if we'd turned them on.
|
|
//
|
|
if (g_oeState.uFlags & OESTATE_SDA_DCB)
|
|
{
|
|
if (g_oeState.uGetDCB == DCB_SET)
|
|
{
|
|
SetBoundsRect(g_oeState.hdc, &g_oeState.rcDCB,
|
|
g_oeState.uSetDCB | DCB_ACCUMULATE);
|
|
}
|
|
else
|
|
{
|
|
SetBoundsRect(g_oeState.hdc, NULL,
|
|
g_oeState.uSetDCB | DCB_RESET);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(OEAfterDDI, (fWeCare && fOutput));
|
|
return(fWeCare && fOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OEClipAndAddScreenData()
|
|
//
|
|
void OEClipAndAddScreenData
|
|
(
|
|
LPRECT lprcAdd
|
|
)
|
|
{
|
|
RECT rcSDA;
|
|
RECT rcClipped;
|
|
LPRECTL pClip;
|
|
UINT iClip;
|
|
|
|
DebugEntry(OEClipAndAddScreenData);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_REGION);
|
|
|
|
//
|
|
// The rect passed is in virtual desktop inclusive coords. Convert to
|
|
// Windows screen coords
|
|
//
|
|
rcSDA.left = lprcAdd->left;
|
|
rcSDA.top = lprcAdd->top;
|
|
rcSDA.right = lprcAdd->right + 1;
|
|
rcSDA.bottom = lprcAdd->bottom + 1;
|
|
|
|
//
|
|
// We've got our region data. In the case of a region that has more
|
|
// than 64 pieces, we just use the bound box (one piece), that's been
|
|
// set up for us already.
|
|
//
|
|
|
|
//
|
|
// Intersect each piece with the total bounds to product an SDA rect
|
|
// clipped appropriately.
|
|
//
|
|
for (iClip = 0, pClip = g_oeState.rgnData.arclPieces;
|
|
iClip < g_oeState.rgnData.rdh.nRectL; iClip++, pClip++)
|
|
{
|
|
RECTL_TO_RECT(pClip, &rcClipped);
|
|
|
|
if (IntersectRect(&rcClipped, &rcClipped, &rcSDA))
|
|
{
|
|
//
|
|
// Convert to virtual desktop inclusive coords
|
|
//
|
|
rcClipped.right -= 1;
|
|
rcClipped.bottom -= 1;
|
|
|
|
BA_AddScreenData(&rcClipped);
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(OEClipAndAddScreenData);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: OEClipAndAddOrder
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Clips the supplied order to the current clip region in the DC. If this
|
|
// results in more than one clipped rectangle then the order is duplicated
|
|
// and multiple copies are added to the Order List (with the only
|
|
// difference between the orders being the destination rectangle).
|
|
//
|
|
// PARAMETERS: pOrder - a pointer to the order
|
|
//
|
|
// RETURNS: VOID
|
|
//
|
|
//
|
|
void OEClipAndAddOrder
|
|
(
|
|
LPINT_ORDER pOrder,
|
|
void FAR* lpExtraInfo
|
|
)
|
|
{
|
|
RECT rcOrder;
|
|
RECT rcPiece;
|
|
RECT rcClipped;
|
|
LPRECTL pPiece;
|
|
UINT iClip;
|
|
BOOL fOrderClipped;
|
|
LPINT_ORDER pNewOrder;
|
|
LPINT_ORDER pLastOrder;
|
|
|
|
DebugEntry(OEClipAndAddOrder);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_REGION);
|
|
|
|
//
|
|
// If this fails somewhere, we accumulate screen data in the same place
|
|
// to spoil the order(s).
|
|
//
|
|
|
|
//
|
|
// NOTE:
|
|
// There are some VERY important things about the way this function
|
|
// works that you should be aware of:
|
|
//
|
|
// (1) Every time an order is allocated, it is added to the end of
|
|
// the order heap linked list
|
|
// (2) Appending an order commits it, that updates some total byte info.
|
|
// If the order is a spoiler, the append code will walk backwards from
|
|
// the order being appended and will wipe out orders whose bounds are
|
|
// completely contained within the rect of the current one.
|
|
//
|
|
// THEREFORE, it is important to append orders in the order they are
|
|
// allocated it. When we come into this function, one order is already
|
|
// allocated. Its rcsDst bound rect is uninitialized. When a second
|
|
// intersection with the visrgn occurs, we must allocate a new order,
|
|
// but append the previously allocated block with the previous rect
|
|
// info.
|
|
//
|
|
// Otherwise you will encounter the bug that took me a while to figure
|
|
// out:
|
|
// * Laura allocates an order in say PatBlt with a spoiler ROP
|
|
// * Laura calls OEClipAndAddOrder and of course the rcsDst field
|
|
// hasn't been initialized yet.
|
|
// * The order intersects two pieces of the visrgn. On the first
|
|
// intersection, we save that info away.
|
|
// * On the second, we allocate a new order block, fill in the NEW
|
|
// order's info by copying from the old, setting up the rect
|
|
// with the first intersection, and call OA_DDAddOrder.
|
|
// * This, as a spoiler, causes the OA_ code to walk backwards in
|
|
// the linked list looking for orders whose bounds are
|
|
// completely enclosed by this one.
|
|
// * It comes to the original order allocated, whose bounds are
|
|
// currently NOT initialized
|
|
// * It may find that these uninitialized values describe a rect
|
|
// contained within the new order's bounds
|
|
// * It frees this order but the order was not yet committed
|
|
// * The heap sizes and heap info no longer match, causing an
|
|
// error about the "List head wrong", the list to get reinited,
|
|
// and orders to be lost.
|
|
//
|
|
|
|
rcOrder.left = g_oeState.rc.left;
|
|
rcOrder.top = g_oeState.rc.top;
|
|
rcOrder.right = g_oeState.rc.right + 1;
|
|
rcOrder.bottom = g_oeState.rc.bottom + 1;
|
|
|
|
pNewOrder = pOrder;
|
|
fOrderClipped = FALSE;
|
|
g_oaPurgeAllowed = FALSE;
|
|
|
|
//
|
|
// Intersect each piece rect with the draw bounds
|
|
//
|
|
for (iClip = 0, pPiece = g_oeState.rgnData.arclPieces;
|
|
iClip < g_oeState.rgnData.rdh.nRectL; iClip++, pPiece++)
|
|
{
|
|
RECTL_TO_RECT(pPiece, &rcPiece);
|
|
|
|
if (!IntersectRect(&rcPiece, &rcPiece, &rcOrder))
|
|
continue;
|
|
|
|
if (fOrderClipped)
|
|
{
|
|
//
|
|
// This adds a clipped order for the LAST intersection, not
|
|
// the current one. We do this to avoid allocating an extra
|
|
// order when only ONE intersection occurs.
|
|
//
|
|
|
|
//
|
|
// The order has already been clipped once, so it actually
|
|
// intersects more than one clip rect. We cope with this
|
|
// by duplicating the order and clipping it again.
|
|
//
|
|
pNewOrder = OA_DDAllocOrderMem(
|
|
pLastOrder->OrderHeader.Common.cbOrderDataLength, 0);
|
|
if (pNewOrder == NULL)
|
|
{
|
|
WARNING_OUT(("OA alloc failed"));
|
|
|
|
//
|
|
// BOGUS LAURABU:
|
|
// If some order in the middle fails to be
|
|
// allocated, we need the previous order + the remaining
|
|
// intersections to be added as screen data!
|
|
//
|
|
// NT's code is bogus, it will miss some output.
|
|
//
|
|
|
|
//
|
|
// Allocation of memory for a duplicate order failed.
|
|
// Just add the original order as screen data, and free
|
|
// the original's memory. Note that g_oeState.rc has
|
|
// the proper bounds, so we can just call OEClipAndAddScreenData().
|
|
//
|
|
OA_DDFreeOrderMem(pLastOrder);
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Copy the header & data from the original order to this
|
|
// new one. Don't overwrite the list info at the start.
|
|
//
|
|
hmemcpy((LPBYTE)pNewOrder + FIELD_SIZE(INT_ORDER, OrderHeader.list),
|
|
(LPBYTE)pLastOrder + FIELD_SIZE(INT_ORDER, OrderHeader.list),
|
|
pLastOrder->OrderHeader.Common.cbOrderDataLength +
|
|
sizeof(INT_ORDER_HEADER) -
|
|
FIELD_SIZE(INT_ORDER, OrderHeader.list));
|
|
|
|
//
|
|
// Set the clip rect. NOTE: This is the clipped rect from
|
|
// LAST time.
|
|
//
|
|
pLastOrder->OrderHeader.Common.rcsDst.left =
|
|
rcClipped.left;
|
|
pLastOrder->OrderHeader.Common.rcsDst.top =
|
|
rcClipped.top;
|
|
pLastOrder->OrderHeader.Common.rcsDst.right =
|
|
rcClipped.right - 1;
|
|
pLastOrder->OrderHeader.Common.rcsDst.bottom =
|
|
rcClipped.bottom - 1;
|
|
|
|
OTRACE(("Duplicate clipped order %08lx at {%d, %d, %d, %d}",
|
|
pLastOrder,
|
|
pLastOrder->OrderHeader.Common.rcsDst.left,
|
|
pLastOrder->OrderHeader.Common.rcsDst.top,
|
|
pLastOrder->OrderHeader.Common.rcsDst.right,
|
|
pLastOrder->OrderHeader.Common.rcsDst.bottom));
|
|
|
|
OA_DDAddOrder(pLastOrder, lpExtraInfo);
|
|
}
|
|
|
|
//
|
|
// Save the clipping rect for the NEXT dude.
|
|
//
|
|
CopyRect(&rcClipped, &rcPiece);
|
|
fOrderClipped = TRUE;
|
|
pLastOrder = pNewOrder;
|
|
}
|
|
|
|
|
|
//
|
|
// We're out of the loop now.
|
|
//
|
|
if (fOrderClipped)
|
|
{
|
|
pLastOrder->OrderHeader.Common.rcsDst.left =
|
|
rcClipped.left;
|
|
pLastOrder->OrderHeader.Common.rcsDst.top =
|
|
rcClipped.top;
|
|
pLastOrder->OrderHeader.Common.rcsDst.right =
|
|
rcClipped.right - 1;
|
|
pLastOrder->OrderHeader.Common.rcsDst.bottom =
|
|
rcClipped.bottom - 1;
|
|
|
|
OTRACE(("Clipped order %08lx at {%d, %d, %d, %d}",
|
|
pLastOrder,
|
|
pLastOrder->OrderHeader.Common.rcsDst.left,
|
|
pLastOrder->OrderHeader.Common.rcsDst.top,
|
|
pLastOrder->OrderHeader.Common.rcsDst.right,
|
|
pLastOrder->OrderHeader.Common.rcsDst.bottom));
|
|
|
|
OA_DDAddOrder(pLastOrder, lpExtraInfo);
|
|
}
|
|
else
|
|
{
|
|
OTRACE(("Order clipped completely"));
|
|
OA_DDFreeOrderMem(pOrder);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
g_oaPurgeAllowed = TRUE;
|
|
|
|
DebugExitVOID(OEClipAndAddOrder);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// DDI PATCHES
|
|
//
|
|
|
|
//
|
|
// DrvArc()
|
|
//
|
|
BOOL WINAPI DrvArc
|
|
(
|
|
HDC hdcDst,
|
|
int xLeft,
|
|
int yTop,
|
|
int xRight,
|
|
int yBottom,
|
|
int xStartArc,
|
|
int yStartArc,
|
|
int xEndArc,
|
|
int yEndArc
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
LPINT_ORDER pOrder;
|
|
LPARC_ORDER pArc;
|
|
POINT ptStart;
|
|
POINT ptEnd;
|
|
|
|
DebugEntry(DrvArc);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_ARC, hdcDst, 0);
|
|
|
|
fOutput = Arc(hdcDst, xLeft, yTop, xRight, yBottom, xStartArc,
|
|
yStartArc, xEndArc, yEndArc);
|
|
|
|
if (OEAfterDDI(DDI_ARC, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_REGION);
|
|
|
|
//
|
|
// Get the bound rect
|
|
//
|
|
g_oeState.rc.left = xLeft;
|
|
g_oeState.rc.top = yTop;
|
|
g_oeState.rc.right = xRight;
|
|
g_oeState.rc.bottom = yBottom;
|
|
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// Can we send an ARC order?
|
|
//
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_ARC, OECHECK_PEN | OECHECK_CLIPPING))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(ARC_ORDER), 0);
|
|
if (!pOrder)
|
|
goto NoArcOrder;
|
|
|
|
pArc = (LPARC_ORDER)pOrder->abOrderData;
|
|
pArc->type = LOWORD(ORD_ARC);
|
|
|
|
//
|
|
// Note that order coordinates are 32-bits, but we're 16-bits.
|
|
// So we need intermediate variables to do conversions on.
|
|
//
|
|
pArc->nLeftRect = g_oeState.rc.left;
|
|
pArc->nTopRect = g_oeState.rc.top;
|
|
pArc->nRightRect = g_oeState.rc.right;
|
|
pArc->nBottomRect = g_oeState.rc.bottom;
|
|
|
|
ptStart.x = xStartArc;
|
|
ptStart.y = yStartArc;
|
|
OELPtoVirtual(g_oeState.hdc, &ptStart, 1);
|
|
pArc->nXStart = ptStart.x;
|
|
pArc->nYStart = ptStart.y;
|
|
|
|
ptEnd.x = xEndArc;
|
|
ptEnd.y = yEndArc;
|
|
OELPtoVirtual(g_oeState.hdc, &ptEnd, 1);
|
|
pArc->nXEnd = ptEnd.x;
|
|
pArc->nYEnd = ptEnd.y;
|
|
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.bkColorL,
|
|
&pArc->BackColor, FALSE);
|
|
pArc->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pArc->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
pArc->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pArc->PenWidth = 1;
|
|
OEConvertColor(g_oeState.logPen.lopnColor,
|
|
&pArc->PenColor, FALSE);
|
|
|
|
//
|
|
// Get the arc direction (counter-clockwise or clockwise)
|
|
//
|
|
if (g_oeState.lpdc->fwPath & DCPATH_CLOCKWISE)
|
|
pArc->ArcDirection = ORD_ARC_CLOCKWISE;
|
|
else
|
|
pArc->ArcDirection = ORD_ARC_COUNTERCLOCKWISE;
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
OTRACE(("Arc: Order %08lx, Rect {%d, %d, %d, %d}, Start {%d, %d}, End {%d, %d}",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
NoArcOrder:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("Arc: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvArc, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// DrvChord()
|
|
//
|
|
BOOL WINAPI DrvChord
|
|
(
|
|
HDC hdcDst,
|
|
int xLeft,
|
|
int yTop,
|
|
int xRight,
|
|
int yBottom,
|
|
int xStartChord,
|
|
int yStartChord,
|
|
int xEndChord,
|
|
int yEndChord
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
LPINT_ORDER pOrder;
|
|
LPCHORD_ORDER pChord;
|
|
POINT ptStart;
|
|
POINT ptEnd;
|
|
POINT ptBrushOrg;
|
|
|
|
DebugEntry(DrvChord);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_CHORD, hdcDst, 0);
|
|
|
|
fOutput = Chord(hdcDst, xLeft, yTop, xRight, yBottom,
|
|
xStartChord, yStartChord, xEndChord, yEndChord);
|
|
|
|
if (OEAfterDDI(DDI_CHORD, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
//
|
|
// Get the bound rect
|
|
//
|
|
g_oeState.rc.left = xLeft;
|
|
g_oeState.rc.top = yTop;
|
|
g_oeState.rc.right = xRight;
|
|
g_oeState.rc.bottom = yBottom;
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// Can we send a CHORD order?
|
|
//
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_CHORD, OECHECK_PEN | OECHECK_BRUSH | OECHECK_CLIPPING))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(CHORD_ORDER), 0);
|
|
if (!pOrder)
|
|
goto NoChordOrder;
|
|
|
|
pChord = (LPCHORD_ORDER)pOrder->abOrderData;
|
|
pChord->type = LOWORD(ORD_CHORD);
|
|
|
|
pChord->nLeftRect = g_oeState.rc.left;
|
|
pChord->nTopRect = g_oeState.rc.top;
|
|
pChord->nRightRect = g_oeState.rc.right;
|
|
pChord->nBottomRect = g_oeState.rc.bottom;
|
|
|
|
ptStart.x = xStartChord;
|
|
ptStart.y = yStartChord;
|
|
OELPtoVirtual(g_oeState.hdc, &ptStart, 1);
|
|
pChord->nXStart = ptStart.x;
|
|
pChord->nYStart = ptStart.y;
|
|
|
|
ptEnd.x = xEndChord;
|
|
ptEnd.y = yEndChord;
|
|
OELPtoVirtual(g_oeState.hdc, &ptEnd, 1);
|
|
pChord->nXEnd = ptEnd.x;
|
|
pChord->nYEnd = ptEnd.y;
|
|
|
|
OEGetBrushInfo(&pChord->BackColor, &pChord->ForeColor,
|
|
&pChord->BrushStyle, &pChord->BrushHatch, pChord->BrushExtra);
|
|
|
|
GetBrushOrgEx(g_oeState.hdc, &ptBrushOrg);
|
|
pChord->BrushOrgX = (BYTE)ptBrushOrg.x;
|
|
pChord->BrushOrgY = (BYTE)ptBrushOrg.y;
|
|
|
|
pChord->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pChord->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
pChord->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pChord->PenWidth = 1;
|
|
OEConvertColor(g_oeState.logPen.lopnColor,
|
|
&pChord->PenColor, FALSE);
|
|
|
|
if (g_oeState.lpdc->fwPath & DCPATH_CLOCKWISE)
|
|
pChord->ArcDirection = ORD_ARC_CLOCKWISE;
|
|
else
|
|
pChord->ArcDirection = ORD_ARC_COUNTERCLOCKWISE;
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
OTRACE(("Chord: Order %08lx, Rect {%d, %d, %d, %d}, Start {%d, %d}, End {%d, %d}",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
NoChordOrder:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("Chord: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvChord, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// DrvEllipse()
|
|
//
|
|
BOOL WINAPI DrvEllipse
|
|
(
|
|
HDC hdcDst,
|
|
int xLeft,
|
|
int yTop,
|
|
int xRight,
|
|
int yBottom
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
LPINT_ORDER pOrder;
|
|
LPELLIPSE_ORDER pEllipse;
|
|
POINT ptBrushOrg;
|
|
|
|
DebugEntry(DrvEllipse);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_ELLIPSE, hdcDst, 0);
|
|
|
|
fOutput = Ellipse(hdcDst, xLeft, yTop, xRight, yBottom);
|
|
|
|
if (OEAfterDDI(DDI_ELLIPSE, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
//
|
|
// Calc bound rect
|
|
//
|
|
g_oeState.rc.left = xLeft;
|
|
g_oeState.rc.top = yTop;
|
|
g_oeState.rc.right = xRight;
|
|
g_oeState.rc.bottom = yBottom;
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// Can we send ELLIPSE order?
|
|
//
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_ELLIPSE, OECHECK_PEN | OECHECK_BRUSH | OECHECK_CLIPPING))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(ELLIPSE_ORDER), 0);
|
|
if (!pOrder)
|
|
goto NoEllipseOrder;
|
|
|
|
pEllipse = (LPELLIPSE_ORDER)pOrder->abOrderData;
|
|
pEllipse->type = LOWORD(ORD_ELLIPSE);
|
|
|
|
pEllipse->nLeftRect = g_oeState.rc.left;
|
|
pEllipse->nTopRect = g_oeState.rc.top;
|
|
pEllipse->nRightRect = g_oeState.rc.right;
|
|
pEllipse->nBottomRect = g_oeState.rc.bottom;
|
|
|
|
OEGetBrushInfo(&pEllipse->BackColor, &pEllipse->ForeColor,
|
|
&pEllipse->BrushStyle, &pEllipse->BrushHatch,
|
|
pEllipse->BrushExtra);
|
|
|
|
GetBrushOrgEx(g_oeState.hdc, &ptBrushOrg);
|
|
pEllipse->BrushOrgX = (BYTE)ptBrushOrg.x;
|
|
pEllipse->BrushOrgY = (BYTE)ptBrushOrg.y;
|
|
|
|
pEllipse->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pEllipse->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
pEllipse->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pEllipse->PenWidth = 1;
|
|
|
|
OEConvertColor(g_oeState.logPen.lopnColor, &pEllipse->PenColor,
|
|
FALSE);
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
OTRACE(("Ellipse: Order %08lx, Rect {%d, %d, %d, %d}",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
NoEllipseOrder:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("Ellipse: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvEllipse, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// DrvPie()
|
|
//
|
|
BOOL WINAPI DrvPie
|
|
(
|
|
HDC hdcDst,
|
|
int xLeft,
|
|
int yTop,
|
|
int xRight,
|
|
int yBottom,
|
|
int xStartArc,
|
|
int yStartArc,
|
|
int xEndArc,
|
|
int yEndArc
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
LPINT_ORDER pOrder;
|
|
LPPIE_ORDER pPie;
|
|
POINT ptStart;
|
|
POINT ptEnd;
|
|
POINT ptBrushOrg;
|
|
|
|
DebugEntry(DrvPie);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_PIE, hdcDst, 0);
|
|
|
|
fOutput = Pie(hdcDst, xLeft, yTop, xRight, yBottom, xStartArc, yStartArc,
|
|
xEndArc, yEndArc);
|
|
|
|
if (OEAfterDDI(DDI_PIE, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
//
|
|
// Get bound rect
|
|
//
|
|
g_oeState.rc.left = xLeft;
|
|
g_oeState.rc.top = yTop;
|
|
g_oeState.rc.right = xRight;
|
|
g_oeState.rc.bottom = yBottom;
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// Can we send PIE order?
|
|
//
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_PIE, OECHECK_PEN | OECHECK_BRUSH | OECHECK_CLIPPING))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(PIE_ORDER), 0);
|
|
if (!pOrder)
|
|
goto NoPieOrder;
|
|
|
|
pPie = (LPPIE_ORDER)pOrder->abOrderData;
|
|
pPie->type = LOWORD(ORD_PIE);
|
|
|
|
pPie->nLeftRect = g_oeState.rc.left;
|
|
pPie->nTopRect = g_oeState.rc.top;
|
|
pPie->nRightRect = g_oeState.rc.right;
|
|
pPie->nBottomRect = g_oeState.rc.bottom;
|
|
|
|
ptStart.x = xStartArc;
|
|
ptStart.y = yStartArc;
|
|
OELPtoVirtual(g_oeState.hdc, &ptStart, 1);
|
|
pPie->nXStart = ptStart.x;
|
|
pPie->nYStart = ptStart.y;
|
|
|
|
ptEnd.x = xEndArc;
|
|
ptEnd.y = yEndArc;
|
|
OELPtoVirtual(g_oeState.hdc, &ptEnd, 1);
|
|
pPie->nXEnd = ptEnd.x;
|
|
pPie->nYEnd = ptEnd.y;
|
|
|
|
OEGetBrushInfo(&pPie->BackColor, &pPie->ForeColor,
|
|
&pPie->BrushStyle, &pPie->BrushHatch, pPie->BrushExtra);
|
|
|
|
GetBrushOrgEx(g_oeState.hdc, &ptBrushOrg);
|
|
pPie->BrushOrgX = (BYTE)ptBrushOrg.x;
|
|
pPie->BrushOrgY = (BYTE)ptBrushOrg.y;
|
|
|
|
pPie->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pPie->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
pPie->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pPie->PenWidth = 1;
|
|
OEConvertColor(g_oeState.logPen.lopnColor, &pPie->PenColor,
|
|
FALSE);
|
|
|
|
if (g_oeState.lpdc->fwPath & DCPATH_CLOCKWISE)
|
|
pPie->ArcDirection = ORD_ARC_CLOCKWISE;
|
|
else
|
|
pPie->ArcDirection = ORD_ARC_COUNTERCLOCKWISE;
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
OTRACE(("Pie: Order %08lx, Rect {%d, %d, %d, %d}, Start {%d, %d}, End {%d, %d}",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
NoPieOrder:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("PieOrder: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPie, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvRoundRect()
|
|
//
|
|
BOOL WINAPI DrvRoundRect
|
|
(
|
|
HDC hdcDst,
|
|
int xLeft,
|
|
int yTop,
|
|
int xRight,
|
|
int yBottom,
|
|
int cxEllipse,
|
|
int cyEllipse
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
LPINT_ORDER pOrder;
|
|
LPROUNDRECT_ORDER pRoundRect;
|
|
POINT ptBrushOrg;
|
|
|
|
DebugEntry(DrvRoundRect);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_ROUNDRECT, hdcDst, 0);
|
|
|
|
fOutput = RoundRect(hdcDst, xLeft, yTop, xRight, yBottom, cxEllipse, cyEllipse);
|
|
|
|
if (OEAfterDDI(DDI_ROUNDRECT, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
//
|
|
// Get bound rect
|
|
//
|
|
g_oeState.rc.left = xLeft;
|
|
g_oeState.rc.top = yTop;
|
|
g_oeState.rc.right = xRight;
|
|
g_oeState.rc.bottom = yBottom;
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// Can we send ROUNDRECT order?
|
|
//
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_ROUNDRECT, OECHECK_PEN | OECHECK_BRUSH | OECHECK_CLIPPING) &&
|
|
(GetMapMode(hdcDst) == MM_TEXT))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(ROUNDRECT_ORDER), 0);
|
|
if (!pOrder)
|
|
goto NoRoundRectOrder;
|
|
|
|
pRoundRect = (LPROUNDRECT_ORDER)pOrder->abOrderData;
|
|
pRoundRect->type = LOWORD(ORD_ROUNDRECT);
|
|
|
|
pRoundRect->nLeftRect = g_oeState.rc.left;
|
|
pRoundRect->nTopRect = g_oeState.rc.top;
|
|
pRoundRect->nRightRect = g_oeState.rc.right;
|
|
pRoundRect->nBottomRect = g_oeState.rc.bottom;
|
|
|
|
//
|
|
// It's too difficult to do the mapping of the ellipse
|
|
// dimensions when not MM_TEXT. Therefore we don't. If we
|
|
// are here, we just pass the sizes straight through.
|
|
//
|
|
pRoundRect->nEllipseWidth = cxEllipse;
|
|
pRoundRect->nEllipseHeight = cyEllipse;
|
|
|
|
OEGetBrushInfo(&pRoundRect->BackColor, &pRoundRect->ForeColor,
|
|
&pRoundRect->BrushStyle, &pRoundRect->BrushHatch,
|
|
pRoundRect->BrushExtra);
|
|
|
|
GetBrushOrgEx(g_oeState.hdc, &ptBrushOrg);
|
|
pRoundRect->BrushOrgX = ptBrushOrg.x;
|
|
pRoundRect->BrushOrgY = ptBrushOrg.y;
|
|
|
|
pRoundRect->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pRoundRect->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
pRoundRect->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pRoundRect->PenWidth = 1;
|
|
OEConvertColor(g_oeState.logPen.lopnColor,
|
|
&pRoundRect->PenColor, FALSE);
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
OTRACE(("RoundRect: Order %08lx, Rect {%d, %d, %d, %d}, Curve {%d, %d}",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom, cxEllipse, cyEllipse));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
NoRoundRectOrder:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("RoundRect: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvRoundRect, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
//
|
|
// DrvBitBlt
|
|
//
|
|
BOOL WINAPI DrvBitBlt
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
int cxDst,
|
|
int cyDst,
|
|
HDC hdcSrc,
|
|
int xSrc,
|
|
int ySrc,
|
|
DWORD dwRop
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
BYTE bRop;
|
|
LPDC lpdcSrc;
|
|
LPINT_ORDER pOrder;
|
|
LPSCRBLT_ORDER pScrBlt;
|
|
POINT ptT;
|
|
RECT rcT;
|
|
|
|
DebugEntry(DrvBitBlt);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_BITBLT, hdcDst, 0);
|
|
|
|
fOutput = BitBlt(hdcDst, xDst, yDst, cxDst, cyDst, hdcSrc, xSrc, ySrc, dwRop);
|
|
|
|
if (OEAfterDDI(DDI_BITBLT, fWeCare, fOutput && cxDst && cyDst))
|
|
{
|
|
//
|
|
// Is this really PatBlt?
|
|
//
|
|
bRop = LOBYTE(HIWORD(dwRop));
|
|
|
|
if (((bRop & 0x33) << 2) == (bRop & 0xCC))
|
|
{
|
|
TRACE_OUT(("BitBlt used for PatBlt"));
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
//
|
|
// Get bound rect
|
|
//
|
|
g_oeState.rc.left = xDst;
|
|
g_oeState.rc.top = yDst;
|
|
g_oeState.rc.right = xDst + cxDst;
|
|
g_oeState.rc.bottom = yDst + cyDst;
|
|
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
OEAddBlt(dwRop);
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// SPB goop
|
|
//
|
|
if (g_oeState.lpdc->hBitmap == g_ssiLastSpbBitmap)
|
|
{
|
|
//
|
|
// This is an SPB operation. The source is in screen coords.
|
|
//
|
|
ASSERT(g_ssiLastSpbBitmap);
|
|
ASSERT(g_oeState.lpdc->DCFlags & DC_IS_MEMORY);
|
|
ASSERT(dwRop == SRCCOPY);
|
|
|
|
g_oeState.rc.left = xSrc;
|
|
g_oeState.rc.top = ySrc;
|
|
g_oeState.rc.right = xSrc + cxDst;
|
|
g_oeState.rc.bottom = ySrc + cyDst;
|
|
|
|
SSISaveBits(g_ssiLastSpbBitmap, &g_oeState.rc);
|
|
g_ssiLastSpbBitmap = NULL;
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
ASSERT(!(g_oeState.lpdc->DCFlags & DC_IS_MEMORY));
|
|
|
|
//
|
|
// Is this a memory to screen blt for SPB restoration?
|
|
//
|
|
lpdcSrc = OEValidateDC(hdcSrc, TRUE);
|
|
if (SELECTOROF(lpdcSrc) &&
|
|
(lpdcSrc->DCFlags & DC_IS_DISPLAY) &&
|
|
(lpdcSrc->DCFlags & DC_IS_MEMORY) &&
|
|
(dwRop == SRCCOPY) &&
|
|
SSIRestoreBits(lpdcSrc->hBitmap))
|
|
{
|
|
OTRACE(("BitBlt: SPB restored"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Now, we accumulate orders for screen-to-screen blts
|
|
//
|
|
OEGetState(OESTATE_COORDS | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
g_oeState.rc.left = xDst;
|
|
g_oeState.rc.top = yDst;
|
|
g_oeState.rc.right = xDst + cxDst;
|
|
g_oeState.rc.bottom = yDst + cyDst;
|
|
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
pOrder = NULL;
|
|
|
|
if (hdcSrc == hdcDst)
|
|
{
|
|
if (!OECheckOrder(ORD_SCRBLT, OECHECK_CLIPPING) ||
|
|
!OESendRop3AsOrder(bRop) ||
|
|
!ROP3_NO_PATTERN(bRop))
|
|
{
|
|
goto NoBitBltOrder;
|
|
}
|
|
|
|
//
|
|
// Get source coords
|
|
//
|
|
ptT.x = xSrc;
|
|
ptT.y = ySrc;
|
|
OELPtoVirtual(hdcSrc, &ptT, 1);
|
|
|
|
//
|
|
// If the clipping isn't simple and the source overlaps the dest,
|
|
// send as screen data. It's too complicated for an order.
|
|
//
|
|
if (!OEClippingIsSimple())
|
|
{
|
|
//
|
|
// NOTE:
|
|
// The NM 2.0 code was really messed up, the source rect
|
|
// calcs were bogus.
|
|
//
|
|
rcT.left = max(g_oeState.rc.left, ptT.x);
|
|
rcT.right = min(g_oeState.rc.right,
|
|
ptT.x + (g_oeState.rc.right - g_oeState.rc.left));
|
|
|
|
rcT.top = max(g_oeState.rc.top, ptT.y);
|
|
rcT.bottom = min(g_oeState.rc.bottom,
|
|
ptT.y + (g_oeState.rc.bottom - g_oeState.rc.top));
|
|
|
|
if ((rcT.left <= rcT.right) &&
|
|
(rcT.top <= rcT.bottom))
|
|
{
|
|
TRACE_OUT(("No SCRBLT order; non-rect clipping and Src/Dst intersect"));
|
|
goto NoBitBltOrder;
|
|
}
|
|
}
|
|
|
|
pOrder = OA_DDAllocOrderMem(sizeof(SCRBLT_ORDER), 0);
|
|
if (!pOrder)
|
|
goto NoBitBltOrder;
|
|
|
|
pScrBlt = (LPSCRBLT_ORDER)pOrder->abOrderData;
|
|
pScrBlt->type = LOWORD(ORD_SCRBLT);
|
|
|
|
pScrBlt->nLeftRect = g_oeState.rc.left;
|
|
pScrBlt->nTopRect = g_oeState.rc.top;
|
|
pScrBlt->nWidth = g_oeState.rc.right - g_oeState.rc.left + 1;
|
|
pScrBlt->nHeight = g_oeState.rc.bottom - g_oeState.rc.top + 1;
|
|
pScrBlt->bRop = bRop;
|
|
|
|
pScrBlt->nXSrc = ptT.x;
|
|
pScrBlt->nYSrc = ptT.y;
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_BLOCKER | OF_SPOILABLE;
|
|
|
|
OTRACE(("ScrBlt: From {%d, %d}, To {%d, %d}, Size {%d, %d}",
|
|
ptT.x, ptT.y, g_oeState.rc.left, g_oeState.rc.top,
|
|
g_oeState.rc.right - g_oeState.rc.left + 1,
|
|
g_oeState.rc.bottom - g_oeState.rc.top + 1));
|
|
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
NoBitBltOrder:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("BitBlt: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvBitBlt, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvExtTextOutA()
|
|
//
|
|
BOOL WINAPI DrvExtTextOutA
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
UINT uOptions,
|
|
LPRECT lprcClip,
|
|
LPSTR lpszText,
|
|
UINT cchText,
|
|
LPINT lpdxCharSpacing
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
UINT uFlags;
|
|
|
|
DebugEntry(DrvExtTextOutA);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// Is this really just opaquing?
|
|
//
|
|
if ((cchText == 0) &&
|
|
SELECTOROF(lprcClip) &&
|
|
!IsBadReadPtr(lprcClip, sizeof(RECT)) &&
|
|
(uOptions & ETO_OPAQUE))
|
|
{
|
|
uFlags = 0;
|
|
}
|
|
else
|
|
{
|
|
uFlags = OESTATE_SDA_FONTCOMPLEX | OESTATE_CURPOS;
|
|
}
|
|
|
|
fWeCare = OEBeforeDDI(DDI_EXTTEXTOUTA, hdcDst, uFlags);
|
|
|
|
fOutput = ExtTextOut(hdcDst, xDst, yDst, uOptions, lprcClip, lpszText, cchText, lpdxCharSpacing);
|
|
|
|
if (OEAfterDDI(DDI_EXTTEXTOUTA, fWeCare, fOutput))
|
|
{
|
|
//
|
|
// Is this a simple OPAQUE rect, or a textout call?
|
|
// NOTE that OEAfterDDI() returns FALSE if fOutput is TRUE but
|
|
// we used DCBs to add it as screen data.
|
|
//
|
|
if (uFlags & OESTATE_SDA_FONTCOMPLEX)
|
|
{
|
|
if (cchText)
|
|
{
|
|
POINT ptStart = {xDst, yDst};
|
|
|
|
OEAddText(ptStart, uOptions, lprcClip, lpszText, cchText, lpdxCharSpacing);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OEAddOpaqueRect(lprcClip);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvExtTextOutA, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
#pragma optimize("gle", off)
|
|
//
|
|
// DrvPatBlt()
|
|
//
|
|
BOOL WINAPI DrvPatBlt
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
int cxDst,
|
|
int cyDst,
|
|
DWORD rop
|
|
)
|
|
{
|
|
UINT cxSave;
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
LPINT_ORDER pOrder;
|
|
|
|
// Save CX
|
|
_asm mov cxSave, cx
|
|
|
|
DebugEntry(DrvPatBlt);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_PATBLT, hdcDst, 0);
|
|
|
|
// Restore CX for RealPatBlt
|
|
_asm mov cx, cxSave
|
|
fOutput = g_lpfnRealPatBlt(hdcDst, xDst, yDst, cxDst, cyDst, rop);
|
|
|
|
if (OEAfterDDI(DDI_PATBLT, fWeCare, fOutput && (cxSave != 0)))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
//
|
|
// Get bound rect
|
|
//
|
|
g_oeState.rc.left = xDst;
|
|
g_oeState.rc.top = yDst;
|
|
g_oeState.rc.right = xDst + cxDst;
|
|
g_oeState.rc.bottom = yDst + cyDst;
|
|
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
OEAddBlt(rop);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPatBlt, fOutput);
|
|
return(fOutput);
|
|
}
|
|
#pragma optimize("", on)
|
|
|
|
|
|
|
|
//
|
|
// OEAddBlt()
|
|
// Used for simple destination ROP blts
|
|
//
|
|
void OEAddBlt
|
|
(
|
|
DWORD dwRop
|
|
)
|
|
{
|
|
LPINT_ORDER pOrder;
|
|
DWORD type;
|
|
POINT ptBrushOrg;
|
|
BYTE bRop;
|
|
|
|
DebugEntry(OEAddBlt);
|
|
|
|
pOrder = NULL;
|
|
|
|
//
|
|
// Is this a full PATBLT_ORDER or a simple DSTBLT_ORDER? If the top
|
|
// nibble of the ROP is equal to the bottom nibble, no pattern is
|
|
// required. WHITENESS for example.
|
|
//
|
|
bRop = LOBYTE(HIWORD(dwRop));
|
|
if ((bRop >> 4) == (bRop & 0x0F))
|
|
{
|
|
type = ORD_DSTBLT;
|
|
}
|
|
else
|
|
{
|
|
type = ORD_PATBLT;
|
|
|
|
if (!OECheckBrushIsSimple())
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
if ((dwRop == PATCOPY) && (g_oeState.logBrush.lbStyle == BS_NULL))
|
|
{
|
|
// No output happens in this scenario at all, no screen data even
|
|
goto NothingAtAll;
|
|
}
|
|
}
|
|
|
|
if (OE_SendAsOrder(type) &&
|
|
OESendRop3AsOrder(bRop) &&
|
|
!OEClippingIsComplex())
|
|
{
|
|
if (type == ORD_PATBLT)
|
|
{
|
|
LPPATBLT_ORDER pPatBlt;
|
|
|
|
pOrder = OA_DDAllocOrderMem(sizeof(PATBLT_ORDER), 0);
|
|
if (!pOrder)
|
|
DC_QUIT;
|
|
|
|
pPatBlt = (LPPATBLT_ORDER)pOrder->abOrderData;
|
|
pPatBlt->type = LOWORD(ORD_PATBLT);
|
|
|
|
pPatBlt->nLeftRect = g_oeState.rc.left;
|
|
pPatBlt->nTopRect = g_oeState.rc.top;
|
|
pPatBlt->nWidth = g_oeState.rc.right - g_oeState.rc.left + 1;
|
|
pPatBlt->nHeight = g_oeState.rc.bottom - g_oeState.rc.top + 1;
|
|
|
|
pPatBlt->bRop = bRop;
|
|
|
|
OEGetBrushInfo(&pPatBlt->BackColor, &pPatBlt->ForeColor,
|
|
&pPatBlt->BrushStyle, &pPatBlt->BrushHatch, pPatBlt->BrushExtra);
|
|
|
|
GetBrushOrgEx(g_oeState.hdc, &ptBrushOrg);
|
|
pPatBlt->BrushOrgX = (BYTE)ptBrushOrg.x;
|
|
pPatBlt->BrushOrgY = (BYTE)ptBrushOrg.y;
|
|
|
|
OTRACE(("PatBlt: Order %08lx, Rect {%d, %d, %d, %d}",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.right));
|
|
}
|
|
else
|
|
{
|
|
LPDSTBLT_ORDER pDstBlt;
|
|
|
|
ASSERT(type == ORD_DSTBLT);
|
|
|
|
pOrder = OA_DDAllocOrderMem(sizeof(DSTBLT_ORDER), 0);
|
|
if (!pOrder)
|
|
DC_QUIT;
|
|
|
|
pDstBlt = (LPDSTBLT_ORDER)pOrder->abOrderData;
|
|
pDstBlt->type = LOWORD(ORD_DSTBLT);
|
|
|
|
pDstBlt->nLeftRect = g_oeState.rc.left;
|
|
pDstBlt->nTopRect = g_oeState.rc.top;
|
|
pDstBlt->nWidth = g_oeState.rc.right - g_oeState.rc.left + 1;
|
|
pDstBlt->nHeight = g_oeState.rc.bottom - g_oeState.rc.top + 1;
|
|
|
|
pDstBlt->bRop = bRop;
|
|
|
|
OTRACE(("DstBlt: Order %08lx, Rect {%d, %d, %d, %d}",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
}
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
if (ROP3_IS_OPAQUE(bRop))
|
|
pOrder->OrderHeader.Common.fOrderFlags |= OF_SPOILER;
|
|
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("PatBlt: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
NothingAtAll:
|
|
DebugExitVOID(OEAddBlt);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvStretchBlt()
|
|
//
|
|
BOOL WINAPI DrvStretchBlt
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
int cxDst,
|
|
int cyDst,
|
|
HDC hdcSrc,
|
|
int xSrc,
|
|
int ySrc,
|
|
int cxSrc,
|
|
int cySrc,
|
|
DWORD rop
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvStretchBlt);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_STRETCHBLT, hdcDst, 0);
|
|
|
|
fOutput = StretchBlt(hdcDst, xDst, yDst, cxDst, cyDst, hdcSrc, xSrc, ySrc, cxSrc, cySrc, rop);
|
|
|
|
if (OEAfterDDI(DDI_STRETCHBLT, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_REGION);
|
|
|
|
g_oeState.rc.left = xDst;
|
|
g_oeState.rc.top = yDst;
|
|
g_oeState.rc.right = xDst + cxDst;
|
|
g_oeState.rc.bottom = yDst + cyDst;
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
OTRACE(("StretchBlt: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvStretchBlt, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// TextOutA()
|
|
//
|
|
BOOL WINAPI DrvTextOutA
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
LPSTR lpszText,
|
|
int cchText
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvTextOutA);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_TEXTOUTA, hdcDst, OESTATE_SDA_FONTCOMPLEX | OESTATE_CURPOS);
|
|
|
|
fOutput = TextOut(hdcDst, xDst, yDst, lpszText, cchText);
|
|
|
|
if (OEAfterDDI(DDI_TEXTOUTA, fWeCare, fOutput && cchText))
|
|
{
|
|
POINT ptStart = {xDst, yDst};
|
|
OEAddText(ptStart, 0, NULL, lpszText, cchText, NULL);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvTextOutA, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvExtFloodFill()
|
|
//
|
|
// This just gets added as screen data. Too darned complicated to
|
|
// calculate the result.
|
|
//
|
|
BOOL WINAPI DrvExtFloodFill
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
COLORREF clrFill,
|
|
UINT uFillType
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvExtFloodFill);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// GDI's draw bounds has an off-by-one bug in ExtFloodFill and FloodFill
|
|
//
|
|
fWeCare = OEBeforeDDI(DDI_EXTFLOODFILL, hdcDst, OESTATE_SDA_DCB |
|
|
OESTATE_OFFBYONEHACK);
|
|
|
|
fOutput = ExtFloodFill(hdcDst, xDst, yDst, clrFill, uFillType);
|
|
|
|
OEAfterDDI(DDI_EXTFLOODFILL, fWeCare, fOutput);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvExtFloodFill, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvFloodFill()
|
|
//
|
|
BOOL WINAPI DrvFloodFill
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
COLORREF clrFill
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvFloodFill);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// GDI's draw bounds has an off-by-one bug in ExtFloodFill and FloodFill
|
|
//
|
|
fWeCare = OEBeforeDDI(DDI_FLOODFILL, hdcDst, OESTATE_SDA_DCB |
|
|
OESTATE_OFFBYONEHACK);
|
|
|
|
fOutput = FloodFill(hdcDst, xDst, yDst, clrFill);
|
|
|
|
OEAfterDDI(DDI_FLOODFILL, fWeCare, fOutput);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvFloodFill, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvExtTextOut()
|
|
//
|
|
BOOL WINAPI DrvExtTextOutW
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
UINT uOptions,
|
|
LPRECT lprcClip,
|
|
LPWSTR lpwszText,
|
|
UINT cchText,
|
|
LPINT lpdxCharSpacing
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
UINT uFlags;
|
|
|
|
//
|
|
// NOTE:
|
|
// ExtTextOutW and TextOutW are only called on 32-bit app threads. So
|
|
// chewing up stack space isn't a problem.
|
|
//
|
|
UINT cchAnsi = 0;
|
|
char szAnsi[ORD_MAX_STRING_LEN_WITHOUT_DELTAS+1];
|
|
|
|
DebugEntry(DrvExtTextOutW);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
if ((cchText == 0) &&
|
|
SELECTOROF(lprcClip) &&
|
|
!IsBadReadPtr(lprcClip, sizeof(RECT)) &&
|
|
(uOptions & ETO_OPAQUE))
|
|
{
|
|
uFlags = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Is this order-able? It is if we can convert the unicode string
|
|
// to ansi then back to unicode, and end up where we started.
|
|
//
|
|
uFlags = OESTATE_SDA_DCB;
|
|
|
|
if (cchText &&
|
|
(cchText <= ORD_MAX_STRING_LEN_WITHOUT_DELTAS) &&
|
|
!IsBadReadPtr(lpwszText, cchText*sizeof(WCHAR)))
|
|
{
|
|
int cchUni;
|
|
|
|
//
|
|
// NOTE:
|
|
// UniToAnsi() returns ONE LESS than the # of chars converted
|
|
//
|
|
cchAnsi = UniToAnsi(lpwszText, szAnsi, cchText) + 1;
|
|
cchUni = AnsiToUni(szAnsi, cchAnsi, g_oeTempString, ORD_MAX_STRING_LEN_WITHOUT_DELTAS);
|
|
|
|
if (cchUni == cchText)
|
|
{
|
|
//
|
|
// Verify these strings are the same
|
|
//
|
|
UINT ich;
|
|
|
|
for (ich = 0; ich < cchText; ich++)
|
|
{
|
|
if (lpwszText[ich] != g_oeTempString[ich])
|
|
break;
|
|
}
|
|
|
|
if (ich == cchText)
|
|
{
|
|
//
|
|
// We made it to the end; everything matched.
|
|
//
|
|
uFlags = OESTATE_SDA_FONTCOMPLEX | OESTATE_CURPOS;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (uFlags == OESTATE_SDA_DCB)
|
|
{
|
|
WARNING_OUT(("Can't encode ExtTextOutW"));
|
|
}
|
|
#endif // DEBUG
|
|
}
|
|
}
|
|
|
|
fWeCare = OEBeforeDDI(DDI_EXTTEXTOUTW, hdcDst, uFlags);
|
|
|
|
fOutput = g_lpfnExtTextOutW(hdcDst, xDst, yDst, uOptions, lprcClip,
|
|
lpwszText, cchText, lpdxCharSpacing);
|
|
|
|
if (OEAfterDDI(DDI_EXTTEXTOUTW, fWeCare, fOutput))
|
|
{
|
|
//
|
|
// Is this a simple OPAQUE rect, or a textout call we can order?
|
|
// NOTE that OEAfterDDI() returns FALSE even if fOutput but we
|
|
// used DCBs to add as screen data.
|
|
//
|
|
if (uFlags & OESTATE_SDA_FONTCOMPLEX)
|
|
{
|
|
POINT ptStart = {xDst, yDst};
|
|
|
|
ASSERT(cchAnsi);
|
|
OEAddText(ptStart, uOptions, lprcClip, szAnsi, cchAnsi, lpdxCharSpacing);
|
|
}
|
|
else
|
|
{
|
|
OEAddOpaqueRect(lprcClip);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvExtTextOutW, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvTextOutW()
|
|
//
|
|
BOOL WINAPI DrvTextOutW
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
LPWSTR lpwszText,
|
|
int cchText
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
UINT uFlags;
|
|
|
|
//
|
|
// NOTE:
|
|
// ExtTextOutW and TextOutW are only called on 32-bit app threads. So
|
|
// chewing up stack space isn't a problem.
|
|
//
|
|
UINT cchAnsi = 0;
|
|
char szAnsi[ORD_MAX_STRING_LEN_WITHOUT_DELTAS+1];
|
|
|
|
DebugEntry(DrvTextOutW);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// Is this order-able? It is if we can convert the unicode string to
|
|
// ansi then back to unicode, and end up where we started.
|
|
//
|
|
uFlags = OESTATE_SDA_DCB;
|
|
|
|
if (cchText &&
|
|
(cchText <= ORD_MAX_STRING_LEN_WITHOUT_DELTAS) &&
|
|
!IsBadReadPtr(lpwszText, cchText*sizeof(WCHAR)))
|
|
{
|
|
int cchUni;
|
|
|
|
//
|
|
// NOTE:
|
|
// UniToAnsi() returns one LESS than the # of chars converted
|
|
//
|
|
cchAnsi = UniToAnsi(lpwszText, szAnsi, cchText) + 1;
|
|
cchUni = AnsiToUni(szAnsi, cchAnsi, g_oeTempString, cchText);
|
|
|
|
if (cchUni == cchText)
|
|
{
|
|
//
|
|
// Verify these strings are the same
|
|
//
|
|
UINT ich;
|
|
|
|
for (ich = 0; ich < cchText; ich++)
|
|
{
|
|
if (lpwszText[ich] != g_oeTempString[ich])
|
|
break;
|
|
}
|
|
|
|
if (ich == cchText)
|
|
{
|
|
//
|
|
// We made it to the end; everything matched.
|
|
//
|
|
uFlags = OESTATE_SDA_FONTCOMPLEX | OESTATE_CURPOS;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (uFlags == OESTATE_SDA_DCB)
|
|
{
|
|
WARNING_OUT(("Can't encode TextOutW"));
|
|
}
|
|
#endif // DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
fWeCare = OEBeforeDDI(DDI_TEXTOUTW, hdcDst, uFlags);
|
|
|
|
fOutput = g_lpfnTextOutW(hdcDst, xDst, yDst, lpwszText, cchText);
|
|
|
|
if (OEAfterDDI(DDI_TEXTOUTW, fWeCare, fOutput && cchText))
|
|
{
|
|
POINT ptStart = {xDst, yDst};
|
|
OEAddText(ptStart, 0, NULL, szAnsi, cchAnsi, NULL);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvTextOutW, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
//
|
|
// OEAddOpaqueRect()
|
|
// Adds a simple opaque rect order, used for "erasing" ExtTextOutA/W
|
|
// calls. The most common examples are in Office.
|
|
//
|
|
void OEAddOpaqueRect(LPRECT lprcOpaque)
|
|
{
|
|
LPINT_ORDER pOrder;
|
|
LPOPAQUERECT_ORDER pOpaqueRect;
|
|
|
|
DebugEntry(OEAddOpaqueRect);
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_REGION);
|
|
|
|
g_oeState.rc = *lprcOpaque;
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_OPAQUERECT, OECHECK_CLIPPING))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(OPAQUERECT_ORDER), 0);
|
|
if (!pOrder)
|
|
DC_QUIT;
|
|
|
|
pOpaqueRect = (LPOPAQUERECT_ORDER)pOrder->abOrderData;
|
|
pOpaqueRect->type = LOWORD(ORD_OPAQUERECT);
|
|
|
|
pOpaqueRect->nLeftRect = g_oeState.rc.left;
|
|
pOpaqueRect->nTopRect = g_oeState.rc.top;
|
|
pOpaqueRect->nWidth = g_oeState.rc.right - g_oeState.rc.left + 1;
|
|
pOpaqueRect->nHeight = g_oeState.rc.bottom - g_oeState.rc.top + 1;
|
|
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.bkColorL,
|
|
&pOpaqueRect->Color, FALSE);
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILER | OF_SPOILABLE;
|
|
|
|
OTRACE(("OpaqueRect: Order %08lx, Rect {%d, %d, %d, %d}, Color %08lx",
|
|
pOrder,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom, pOpaqueRect->Color));
|
|
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("OpaqueRect: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
DebugExitVOID(OEAddOpaqueRect);
|
|
}
|
|
|
|
|
|
//
|
|
// OEAddText()
|
|
// Big monster routine that handles TextOutA/ExtTextOutA
|
|
//
|
|
// In general, we care about:
|
|
// * Clip rect--if none, and no text, it's an OpaqueRect instead
|
|
// * The font
|
|
// * Whether it's too complicated to send as an order
|
|
// * If it needs a deltaX array
|
|
//
|
|
void OEAddText
|
|
(
|
|
POINT ptStart,
|
|
UINT uOptions,
|
|
LPRECT lprcClip,
|
|
LPSTR lpszText,
|
|
UINT cchText,
|
|
LPINT lpdxCharSpacing
|
|
)
|
|
{
|
|
RECT rcT;
|
|
int overhang;
|
|
int width;
|
|
UINT fOrderFlags;
|
|
int cchMax;
|
|
DWORD order;
|
|
LPINT_ORDER pOrder;
|
|
LPEXTTEXTOUT_ORDER pExtTextOut;
|
|
LPTEXTOUT_ORDER pTextOut;
|
|
LPCOMMON_TEXTORDER pCommon;
|
|
UINT fontHeight;
|
|
UINT fontWidth;
|
|
UINT fontWeight;
|
|
UINT fontFlags;
|
|
UINT fontIndex;
|
|
BOOL fSendDeltaX;
|
|
POINT ptT;
|
|
|
|
DebugEntry(OEAddText);
|
|
|
|
//
|
|
// NOTE:
|
|
// Do NOT convert ptStart. It is needed in logical form for several
|
|
// different things.
|
|
//
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_FONT | OESTATE_REGION);
|
|
|
|
//
|
|
// We need to apply the same validation to the flags that GDI does.
|
|
// This bit massaging is for various app compatibility things.
|
|
//
|
|
if (uOptions & ~(ETO_CLIPPED | ETO_OPAQUE | ETO_GLYPH_INDEX | ETO_RTLREADING))
|
|
{
|
|
uOptions &= (ETO_CLIPPED | ETO_OPAQUE);
|
|
}
|
|
if (!(uOptions & (ETO_CLIPPED | ETO_OPAQUE)))
|
|
{
|
|
// No opaquing/clipping, no clip rect
|
|
lprcClip = NULL;
|
|
}
|
|
if (!SELECTOROF(lprcClip))
|
|
{
|
|
// No clip rect, no opaquing/clipping
|
|
uOptions &= ~(ETO_CLIPPED | ETO_OPAQUE);
|
|
}
|
|
|
|
pOrder = NULL;
|
|
|
|
fOrderFlags = OF_SPOILABLE;
|
|
|
|
//
|
|
// Calculate the real starting position of the text
|
|
//
|
|
if (g_oeState.tmAlign & TA_UPDATECP)
|
|
{
|
|
ASSERT(g_oeState.uFlags & OESTATE_CURPOS);
|
|
ptStart = g_oeState.ptCurPos;
|
|
}
|
|
|
|
overhang = OEGetStringExtent(lpszText, cchText, lpdxCharSpacing, &rcT);
|
|
|
|
width = rcT.right - overhang - rcT.left;
|
|
switch (g_oeState.tmAlign & (TA_CENTER | TA_LEFT | TA_RIGHT))
|
|
{
|
|
case TA_CENTER:
|
|
// The original x coord is the MIDPOINT
|
|
TRACE_OUT(("TextOut HORZ center"));
|
|
ptStart.x -= (width * g_oeState.ptPolarity.x / 2);
|
|
break;
|
|
|
|
case TA_RIGHT:
|
|
// The original x coord is the RIGHT SIDE
|
|
TRACE_OUT(("TextOut HORZ right"));
|
|
ptStart.x -= (width * g_oeState.ptPolarity.x);
|
|
break;
|
|
|
|
case TA_LEFT:
|
|
break;
|
|
}
|
|
|
|
switch (g_oeState.tmAlign & (TA_BASELINE | TA_BOTTOM | TA_TOP))
|
|
{
|
|
case TA_BASELINE:
|
|
// The original y coord is the BASELINE
|
|
TRACE_OUT(("TextOut VERT baseline"));
|
|
ptStart.y -= (g_oeState.tmFont.tmAscent * g_oeState.ptPolarity.y);
|
|
break;
|
|
|
|
case TA_BOTTOM:
|
|
// The original y coord is the BOTTOM SIDE
|
|
TRACE_OUT(("TextOut VERT bottom"));
|
|
ptStart.y -= ((rcT.bottom - rcT.top) * g_oeState.ptPolarity.y);
|
|
break;
|
|
|
|
case TA_TOP:
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Calculate extent rect for order
|
|
//
|
|
if (uOptions & ETO_CLIPPED)
|
|
{
|
|
// Because of CopyRect() validation layer bug, do this directly
|
|
g_oeState.rc = *lprcClip;
|
|
|
|
if (uOptions & ETO_OPAQUE)
|
|
fOrderFlags |= OF_SPOILER;
|
|
}
|
|
else
|
|
{
|
|
g_oeState.rc.left = ptStart.x + (g_oeState.ptPolarity.x * rcT.left);
|
|
g_oeState.rc.top = ptStart.y + (g_oeState.ptPolarity.y * rcT.top);
|
|
g_oeState.rc.right = ptStart.x + (g_oeState.ptPolarity.x * rcT.right);
|
|
g_oeState.rc.bottom = ptStart.y + (g_oeState.ptPolarity.y * rcT.bottom);
|
|
|
|
if (uOptions & ETO_OPAQUE)
|
|
{
|
|
//
|
|
// Set the SPOILER flag in the order header. However, if the
|
|
// text extends outside the opaque rect, then the order isn't
|
|
// really opaque, and we have to clear this flag.
|
|
//
|
|
|
|
fOrderFlags |= OF_SPOILER;
|
|
|
|
if (g_oeState.ptPolarity.x == 1)
|
|
{
|
|
if ((g_oeState.rc.left < lprcClip->left) ||
|
|
(g_oeState.rc.right > lprcClip->right))
|
|
{
|
|
fOrderFlags &= ~OF_SPOILER;
|
|
}
|
|
|
|
g_oeState.rc.left = min(g_oeState.rc.left, lprcClip->left);
|
|
g_oeState.rc.right = max(g_oeState.rc.right, lprcClip->right);
|
|
}
|
|
else
|
|
{
|
|
if ((g_oeState.rc.left > lprcClip->left) ||
|
|
(g_oeState.rc.right < lprcClip->right))
|
|
{
|
|
fOrderFlags &= ~OF_SPOILER;
|
|
}
|
|
|
|
g_oeState.rc.left = max(g_oeState.rc.left, lprcClip->left);
|
|
g_oeState.rc.right = min(g_oeState.rc.right, lprcClip->right);
|
|
}
|
|
|
|
if (g_oeState.ptPolarity.y == 1)
|
|
{
|
|
if ((g_oeState.rc.top < lprcClip->top) ||
|
|
(g_oeState.rc.bottom > lprcClip->bottom))
|
|
{
|
|
fOrderFlags &= ~OF_SPOILER;
|
|
}
|
|
|
|
g_oeState.rc.top = min(g_oeState.rc.top, lprcClip->top);
|
|
g_oeState.rc.bottom = max(g_oeState.rc.bottom, lprcClip->bottom);
|
|
}
|
|
else
|
|
{
|
|
if ((g_oeState.rc.top > lprcClip->top) ||
|
|
(g_oeState.rc.bottom < lprcClip->bottom))
|
|
{
|
|
fOrderFlags &= ~OF_SPOILER;
|
|
}
|
|
|
|
g_oeState.rc.top = max(g_oeState.rc.top, lprcClip->top);
|
|
g_oeState.rc.bottom = min(g_oeState.rc.bottom, lprcClip->bottom);
|
|
}
|
|
|
|
//
|
|
// After all this, if the text is OPAQUE, then it is a spoiler
|
|
//
|
|
if (g_oeState.lpdc->DrawMode.bkMode == OPAQUE)
|
|
fOrderFlags |= OF_SPOILER;
|
|
}
|
|
}
|
|
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// Is the font supported?
|
|
//
|
|
if (!OECheckFontIsSupported(lpszText, cchText, &fontHeight,
|
|
&fontWidth, &fontWeight, &fontFlags, &fontIndex, &fSendDeltaX))
|
|
DC_QUIT;
|
|
|
|
//
|
|
// What type of order are we sending? And therefore what is the max
|
|
// # of chars we can encode?
|
|
//
|
|
if (fSendDeltaX || SELECTOROF(lpdxCharSpacing) || uOptions)
|
|
{
|
|
order = ORD_EXTTEXTOUT;
|
|
cchMax = ORD_MAX_STRING_LEN_WITH_DELTAS;
|
|
}
|
|
else
|
|
{
|
|
order = ORD_TEXTOUT;
|
|
cchMax = ORD_MAX_STRING_LEN_WITHOUT_DELTAS;
|
|
}
|
|
|
|
|
|
if (OECheckOrder(order, OECHECK_CLIPPING) &&
|
|
(cchText <= cchMax))
|
|
{
|
|
if (order == ORD_TEXTOUT)
|
|
{
|
|
pOrder = OA_DDAllocOrderMem((sizeof(TEXTOUT_ORDER)
|
|
- ORD_MAX_STRING_LEN_WITHOUT_DELTAS
|
|
+ cchText),
|
|
0);
|
|
if (!pOrder)
|
|
DC_QUIT;
|
|
|
|
pTextOut = (LPTEXTOUT_ORDER)pOrder->abOrderData;
|
|
pTextOut->type = LOWORD(order);
|
|
|
|
pCommon = &pTextOut->common;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// BOGUS LAURABU
|
|
// This allocates space for a deltax array whether or not one is
|
|
// needed.
|
|
//
|
|
pOrder = OA_DDAllocOrderMem((sizeof(EXTTEXTOUT_ORDER)
|
|
- ORD_MAX_STRING_LEN_WITHOUT_DELTAS
|
|
- (ORD_MAX_STRING_LEN_WITH_DELTAS * sizeof(TSHR_INT32))
|
|
+ cchText
|
|
+ (cchText * sizeof(TSHR_INT32))
|
|
+ 4), 0); // 4 is for dword alignment padding
|
|
if (!pOrder)
|
|
DC_QUIT;
|
|
|
|
pExtTextOut = (LPEXTTEXTOUT_ORDER)pOrder->abOrderData;
|
|
pExtTextOut->type = LOWORD(order);
|
|
|
|
pCommon = &pExtTextOut->common;
|
|
}
|
|
|
|
//
|
|
// The order coords are TSHR_INT32s
|
|
//
|
|
ptT = ptStart;
|
|
OELPtoVirtual(g_oeState.hdc, &ptT, 1);
|
|
pCommon->nXStart = ptT.x;
|
|
pCommon->nYStart = ptT.y;
|
|
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.bkColorL,
|
|
&pCommon->BackColor, FALSE);
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.txColorL,
|
|
&pCommon->ForeColor, FALSE);
|
|
|
|
pCommon->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pCommon->CharExtra = g_oeState.lpdc->DrawMode.CharExtra;
|
|
pCommon->BreakExtra = g_oeState.lpdc->DrawMode.TBreakExtra;
|
|
pCommon->BreakCount = g_oeState.lpdc->DrawMode.BreakCount;
|
|
|
|
//
|
|
// Font details
|
|
//
|
|
pCommon->FontHeight = fontHeight;
|
|
pCommon->FontWidth = fontWidth;
|
|
pCommon->FontWeight = fontWeight;
|
|
pCommon->FontFlags = fontFlags;
|
|
pCommon->FontIndex = fontIndex;
|
|
|
|
if (order == ORD_TEXTOUT)
|
|
{
|
|
//
|
|
// Copy the string
|
|
//
|
|
pTextOut->variableString.len = cchText;
|
|
hmemcpy(pTextOut->variableString.string, lpszText, cchText);
|
|
}
|
|
else
|
|
{
|
|
pExtTextOut->fuOptions = uOptions & (ETO_OPAQUE | ETO_CLIPPED);
|
|
|
|
//
|
|
// If there is a clipping rect, set it up. Otherwise use the
|
|
// last ETO's clip rect. This makes OE2 encoding more efficient.
|
|
//
|
|
// NOTE that this is not the same as the drawing bounds--the
|
|
// text may extend outside the clip area.
|
|
//
|
|
if (SELECTOROF(lprcClip))
|
|
{
|
|
ASSERT(uOptions & (ETO_OPAQUE | ETO_CLIPPED));
|
|
|
|
rcT = *lprcClip;
|
|
OELRtoVirtual(g_oeState.hdc, &rcT, 1);
|
|
|
|
|
|
//
|
|
// This is a TSHR_RECT32, so we can't just copy
|
|
//
|
|
pExtTextOut->rectangle.left = rcT.left;
|
|
pExtTextOut->rectangle.top = rcT.top;
|
|
pExtTextOut->rectangle.right = rcT.right;
|
|
pExtTextOut->rectangle.bottom = rcT.bottom;
|
|
|
|
g_oeLastETORect = pExtTextOut->rectangle;
|
|
}
|
|
else
|
|
{
|
|
pExtTextOut->rectangle = g_oeLastETORect;
|
|
}
|
|
|
|
//
|
|
// Copy the string
|
|
//
|
|
pExtTextOut->variableString.len = cchText;
|
|
hmemcpy(pExtTextOut->variableString.string, lpszText, cchText);
|
|
|
|
//
|
|
// Copy the deltax array
|
|
//
|
|
// Although we have a defined fixed length structure for
|
|
// storing ExtTextOut orders, we don't send the full structure
|
|
// over the network as the text will only be, say, 10 chars while
|
|
// the structure contains room for 127.
|
|
//
|
|
// Hence we pack the structure now to remove all the blank data
|
|
// BUT we must maintain the natural alignment of the variables.
|
|
//
|
|
// So we know the length of the string which we can use to
|
|
// start the new delta structure at the next 4-byte boundary.
|
|
//
|
|
if (!OEAddDeltaX(pExtTextOut, lpszText, cchText, lpdxCharSpacing, fSendDeltaX, ptStart))
|
|
{
|
|
WARNING_OUT(("Couldn't add delta-x array to EXTTEXTOUT order"));
|
|
OA_DDFreeOrderMem(pOrder);
|
|
pOrder = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
if (pOrder)
|
|
{
|
|
//
|
|
// Call OEMaybeSimulateDeltaX to add a deltax array to the order
|
|
// if needed to correctly position the text. This happens when
|
|
// the font in use doesn't exist on other machines.
|
|
//
|
|
pOrder->OrderHeader.Common.fOrderFlags = fOrderFlags;
|
|
|
|
OTRACE(("TextOut: Type %08lx, Order %08lx, Rect {%d, %d, %d, %d}, Length %d",
|
|
pOrder, order,
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom, cchText));
|
|
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
else
|
|
{
|
|
OTRACE(("OEAddText: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
DebugExitVOID(OEAddText);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OECheckFontIsSupported()
|
|
//
|
|
// We check if we can send this font. If we haven't received the negotiated
|
|
// packet caps yet, forget it.
|
|
//
|
|
// It returns:
|
|
// font height in points
|
|
// font ascender in points
|
|
// average font width in points
|
|
// font weight
|
|
// font style flags
|
|
// font handle
|
|
// do we need to send delta x
|
|
//
|
|
|
|
BOOL OECheckFontIsSupported
|
|
(
|
|
LPSTR lpszText,
|
|
UINT cchText,
|
|
LPUINT pFontHeight,
|
|
LPUINT pFontWidth,
|
|
LPUINT pFontWeight,
|
|
LPUINT pFontFlags,
|
|
LPUINT pFontIndex,
|
|
LPBOOL pSendDeltaX
|
|
)
|
|
{
|
|
BOOL fFontSupported;
|
|
UINT codePage;
|
|
UINT i;
|
|
UINT iLocal;
|
|
TSHR_UINT32 matchQuality;
|
|
UINT charWidthAdjustment;
|
|
int fontNameLen;
|
|
int compareResult;
|
|
POINT xformSize[2];
|
|
|
|
DebugEntry(OECheckFontIsSupported);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_FONT);
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
fFontSupported = FALSE;
|
|
*pSendDeltaX = FALSE;
|
|
|
|
//
|
|
// Do we have our list yet?
|
|
//
|
|
if (!g_oeTextEnabled)
|
|
DC_QUIT;
|
|
|
|
//
|
|
// Get the font facename
|
|
//
|
|
GetTextFace(g_oeState.hdc, LF_FACESIZE, g_oeState.logFont.lfFaceName);
|
|
|
|
//
|
|
// Search our Font Alias Table for the font name. If we find it,
|
|
// replace it with the aliased name.
|
|
//
|
|
charWidthAdjustment = 0;
|
|
for (i = 0; i < NUM_ALIAS_FONTS; i++)
|
|
{
|
|
if (!lstrcmp(g_oeState.logFont.lfFaceName,
|
|
g_oeFontAliasTable[i].pszOriginalFontName))
|
|
{
|
|
TRACE_OUT(("Alias name: %s -> %s", g_oeState.logFont.lfFaceName,
|
|
g_oeFontAliasTable[i].pszAliasFontName));
|
|
lstrcpy(g_oeState.logFont.lfFaceName,
|
|
g_oeFontAliasTable[i].pszAliasFontName);
|
|
charWidthAdjustment = g_oeFontAliasTable[i].charWidthAdjustment;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the current font code page
|
|
//
|
|
switch (g_oeState.tmFont.tmCharSet)
|
|
{
|
|
case ANSI_CHARSET:
|
|
codePage = NF_CP_WIN_ANSI;
|
|
break;
|
|
|
|
case OEM_CHARSET:
|
|
codePage = NF_CP_WIN_OEM;
|
|
break;
|
|
|
|
//
|
|
// LAURABU BUGBUG
|
|
// This wasn't in NM 2.0 -- does this cause problems in int'l?
|
|
//
|
|
case SYMBOL_CHARSET:
|
|
codePage = NF_CP_WIN_SYMBOL;
|
|
break;
|
|
|
|
default:
|
|
codePage = NF_CP_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// We have a font name to match with those we know to be available
|
|
// remotely. Try to jump straight to the first entry in the local font
|
|
// table starting with the same char as this font. If this index slot
|
|
// is empty (has 0xFFFF in it), then bail out immediately.
|
|
//
|
|
for (iLocal = g_oeLocalFontIndex[(BYTE)g_oeState.logFont.lfFaceName[0]];
|
|
iLocal < g_oeNumFonts;
|
|
iLocal++)
|
|
{
|
|
//
|
|
// If this font isn't supported remotely, skip it.
|
|
//
|
|
matchQuality = g_poeLocalFonts[iLocal].SupportCode;
|
|
if (matchQuality == FH_SC_NO_MATCH)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this facename is different than ours, skip it. WE MUST
|
|
// CALL STRCMP(), because lstrcmp and strcmp() do different things
|
|
// for case. lstrcmp is lexi, and strcmp is alphi.
|
|
//
|
|
compareResult = MyStrcmp(g_poeLocalFonts[iLocal].Details.nfFaceName,
|
|
g_oeState.logFont.lfFaceName);
|
|
|
|
//
|
|
// If this font is alphabetically before the one we're searching for,
|
|
// skip it and continue looking.
|
|
//
|
|
if (compareResult < 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this font is alphabetically after the one we're searching for,
|
|
// then an entry for ours doesn't exist since the table is sorted
|
|
// alphabetically. Bail out.
|
|
//
|
|
if (compareResult > 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This looks promising, a font with the right name is supported on
|
|
// the remote system. Let's look at the metrics.
|
|
//
|
|
*pFontFlags = 0;
|
|
*pFontIndex = iLocal;
|
|
*pFontWeight = g_oeState.tmFont.tmWeight;
|
|
|
|
//
|
|
// Check for a fixed pitch font (NOT present means fixed)
|
|
//
|
|
if (!(g_oeState.tmFont.tmPitchAndFamily & FIXED_PITCH))
|
|
{
|
|
*pFontFlags |= NF_FIXED_PITCH;
|
|
}
|
|
|
|
//
|
|
// Check for a truetype font
|
|
//
|
|
if (g_oeState.tmFont.tmPitchAndFamily & TMPF_TRUETYPE)
|
|
{
|
|
*pFontFlags |= NF_TRUE_TYPE;
|
|
}
|
|
|
|
//
|
|
// Convert the font dimensions into pixel values. We use the
|
|
// average font width and the character height
|
|
//
|
|
xformSize[0].x = 0;
|
|
xformSize[0].y = 0;
|
|
xformSize[1].x = g_oeState.tmFont.tmAveCharWidth;
|
|
xformSize[1].y = g_oeState.tmFont.tmHeight -
|
|
g_oeState.tmFont.tmInternalLeading;
|
|
|
|
//
|
|
// For non-truetype simulated bold/italic fonts only:
|
|
//
|
|
// If the font is bold, the overhang field indicates the extra
|
|
// space a char takes up. Since our internal table contains the
|
|
// size of normal (non-bold) chars for simulated bold, we adjust
|
|
// for that here.
|
|
//
|
|
// If the font is italic, the overhang field indicates the number
|
|
// of pixels the char is skewed. We don't want to make adjustments
|
|
// in this case.
|
|
//
|
|
if (!(g_oeState.tmFont.tmPitchAndFamily & TMPF_TRUETYPE) &&
|
|
!g_oeState.tmFont.tmItalic)
|
|
{
|
|
xformSize[1].x -= g_oeState.tmFont.tmOverhang;
|
|
}
|
|
|
|
//
|
|
// LAURABU BOGUS
|
|
// For baseline text orders
|
|
//
|
|
// xformSize[2].x = 0;
|
|
// xformSize[2].y = g_oeState.tmFont.tmAscent;
|
|
//
|
|
|
|
LPtoDP(g_oeState.hdc, xformSize, 2);
|
|
|
|
//
|
|
// Calculate the font width & height
|
|
//
|
|
*pFontHeight = abs(xformSize[1].y - xformSize[0].y);
|
|
*pFontWidth = abs(xformSize[1].x - xformSize[0].x)
|
|
- charWidthAdjustment;
|
|
|
|
//
|
|
// LAURABU BOGUS
|
|
// For baseline text orders
|
|
//
|
|
// Get the offset to the start of the text cell
|
|
//
|
|
// *pFontAscender = abs(xformSize[2].y - xformSize[0].y);
|
|
//
|
|
|
|
|
|
//
|
|
// Check that we have a matching pair -- where we require that the
|
|
// fonts (i.e., the one being used by the app and the one we've
|
|
// matched with the remot system) are the same pitch and use the
|
|
// same technology.
|
|
//
|
|
if ((g_poeLocalFonts[iLocal].Details.nfFontFlags & NF_FIXED_PITCH) !=
|
|
(*pFontFlags & NF_FIXED_PITCH))
|
|
{
|
|
OTRACE(("Fixed pitch mismatch"));
|
|
continue;
|
|
}
|
|
if ((g_poeLocalFonts[iLocal].Details.nfFontFlags & NF_TRUE_TYPE) !=
|
|
(*pFontFlags & NF_TRUE_TYPE))
|
|
{
|
|
OTRACE(("True type mismatch"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We have a pair of fonts with the same attributes, both fixed or
|
|
// variable pitch, and using the same font technology.
|
|
//
|
|
// If the font is fixed pitch, then we need to check that the size
|
|
// matches also.
|
|
//
|
|
// If not, assume it's always matchable.
|
|
//
|
|
if (g_poeLocalFonts[iLocal].Details.nfFontFlags & NF_FIXED_SIZE)
|
|
{
|
|
//
|
|
// The font is fixed size, so we must check that this
|
|
// particular size is matchable.
|
|
//
|
|
if ( (*pFontHeight != g_poeLocalFonts[iLocal].Details.nfAveHeight) ||
|
|
(*pFontWidth != g_poeLocalFonts[iLocal].Details.nfAveWidth) )
|
|
{
|
|
//
|
|
// The sizes differ, so we must fail this match.
|
|
//
|
|
TRACE_OUT(("Font size mismatch: want {%d, %d}, found {%d, %d}",
|
|
*pFontHeight, *pFontWidth, g_poeLocalFonts[iLocal].Details.nfAveHeight,
|
|
g_poeLocalFonts[iLocal].Details.nfAveWidth));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally, we've got a matched pair.
|
|
//
|
|
fFontSupported = TRUE;
|
|
break;
|
|
}
|
|
|
|
|
|
if (!fFontSupported)
|
|
{
|
|
TRACE_OUT(("Couldn't find matching font for %s in table",
|
|
g_oeState.logFont.lfFaceName));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Build up the rest of the font flags. We've got pitch already.
|
|
//
|
|
if (g_oeState.tmFont.tmItalic)
|
|
{
|
|
*pFontFlags |= NF_ITALIC;
|
|
}
|
|
if (g_oeState.tmFont.tmUnderlined)
|
|
{
|
|
*pFontFlags |= NF_UNDERLINE;
|
|
}
|
|
if (g_oeState.tmFont.tmStruckOut)
|
|
{
|
|
*pFontFlags |= NF_STRIKEOUT;
|
|
}
|
|
|
|
//
|
|
// LAURABU BOGUS
|
|
// On NT, here's where simulated bold fonts are handled. Note that we,
|
|
// like NM 2.0, handle it above with the overhang.
|
|
//
|
|
#if 0
|
|
//
|
|
// It is possible to have a font made bold by Windows, i.e. the
|
|
// standard font definition is not bold, but windows manipulates the
|
|
// font data to create a bold effect. This is marked by the
|
|
// FO_SIM_BOLD flag.
|
|
//
|
|
// In this case we need to ensure that the font flags are marked as
|
|
// bold according to the weight.
|
|
//
|
|
if ( ((pfo->flFontType & FO_SIM_BOLD) != 0) &&
|
|
( pFontMetrics->usWinWeight < FW_BOLD) )
|
|
{
|
|
TRACE_OUT(( "Upgrading weight for a bold font"));
|
|
*pFontWeight = FW_BOLD;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Should we check the chars in the string itself? Use matchQuality
|
|
// to decide.
|
|
//
|
|
// If the font is an exact match, or if it's an approx match for its
|
|
// entire range (0x00 to 0xFF), then send it happily. If not, only
|
|
// send chars within the range 0x20-0x7F (real ASCII)
|
|
//
|
|
if (codePage != g_poeLocalFonts[iLocal].Details.nfCodePage)
|
|
{
|
|
TRACE_OUT(( "Using different CP: downgrade to APPROX_ASC"));
|
|
matchQuality = FH_SC_APPROX_ASCII_MATCH;
|
|
}
|
|
|
|
//
|
|
// If we don't have an exact match, check the individual characters.
|
|
//
|
|
if ( (matchQuality != FH_SC_EXACT_MATCH ) &&
|
|
(matchQuality != FH_SC_APPROX_MATCH) )
|
|
{
|
|
//
|
|
// LAURABU BOGUS!
|
|
// NT does approximate matching only if the font supports the
|
|
// ANSI charset. NM 2.0 never did this, so we won't either.
|
|
//
|
|
|
|
//
|
|
// This font is not a good match across its entire range. Check
|
|
// that all chars are within the desired range.
|
|
//
|
|
for (i = 0; i < cchText; i++)
|
|
{
|
|
if ( (lpszText[i] == 0) ||
|
|
( (lpszText[i] >= NF_ASCII_FIRST) &&
|
|
(lpszText[i] <= NF_ASCII_LAST) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Can only get here by finding a char outside our acceptable
|
|
// range.
|
|
//
|
|
OTRACE(("Found non ASCII char %c", lpszText[i]));
|
|
fFontSupported = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (fFontSupported)
|
|
{
|
|
//
|
|
// We still have to check that this is ANSI text. Consider a
|
|
// string written in symbol font where all the chars in
|
|
// the string are in the range 0x20-0x7F, but none of them
|
|
// are ASCII.
|
|
//
|
|
OemToAnsiBuff(lpszText, g_oeAnsiString, cchText);
|
|
|
|
//
|
|
// BOGUS LAURABU
|
|
// This is our own inline MEMCMP to avoid pulling in the CRT.
|
|
// If any other place needs it, we should make this a function
|
|
//
|
|
for (i = 0; i < cchText; i++)
|
|
{
|
|
if (lpszText[i] != g_oeAnsiString[i])
|
|
{
|
|
OTRACE(("Found non ANSI char %c", lpszText[i]));
|
|
fFontSupported = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We have a valid font. Now sort out deltaX issues
|
|
//
|
|
if (!(g_oeFontCaps & CAPS_FONT_NEED_X_ALWAYS))
|
|
{
|
|
if (!(g_oeFontCaps & CAPS_FONT_NEED_X_SOMETIMES))
|
|
{
|
|
//
|
|
// CAPS_FONT_NEED_X_SOMETIMES and CAPS_FONT_NEED_X_ALWAYS are
|
|
// both not set so we can exit now. (We do not need a delta X
|
|
// array).
|
|
//
|
|
TRACE_OUT(( "Capabilities eliminated delta X"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// CAPS_FONT_NEED_X_SOMETIMES is set and CAPS_FONT_NEED_X_ALWAYS is
|
|
// not set. In this case whether we need a delta X is determined
|
|
// by whether the font is an exact match or an approximate match
|
|
// (because of either approximation of name, signature, or aspect
|
|
// ratio). We can only find this out after we have extracted the
|
|
// font handle from the existing order.
|
|
//
|
|
}
|
|
|
|
//
|
|
// If the string is a single character (or less) then we can just
|
|
// return.
|
|
//
|
|
if (cchText <= 1)
|
|
{
|
|
TRACE_OUT(( "String only %u long", cchText));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Capabilities allow us to ignore delta X position if we have an exact
|
|
// match.
|
|
//
|
|
if (matchQuality & FH_SC_EXACT)
|
|
{
|
|
//
|
|
// Exit immediately, providing that there is no override to always
|
|
// send increments.
|
|
//
|
|
if (!(g_oeFontCaps & CAPS_FONT_NEED_X_ALWAYS))
|
|
{
|
|
TRACE_OUT(( "Font has exact match"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We must send a deltaX array
|
|
//
|
|
*pSendDeltaX = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(OECheckFontIsSupported, fFontSupported);
|
|
return(fFontSupported);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OEAddDeltaX()
|
|
//
|
|
// This fills in the allocated deltaX array if one is needed, either because
|
|
// the app passed one in ExtTextOut, or we need to simulate a font that
|
|
// isn't available remotely.
|
|
//
|
|
BOOL OEAddDeltaX
|
|
(
|
|
LPEXTTEXTOUT_ORDER pExtTextOut,
|
|
LPSTR lpszText,
|
|
UINT cchText,
|
|
LPINT lpdxCharSpacing,
|
|
BOOL fDeltaX,
|
|
POINT ptStart
|
|
)
|
|
{
|
|
BOOL fSuccess;
|
|
LPBYTE lpVariable;
|
|
LPVARIABLE_DELTAX lpDeltaPos;
|
|
UINT i;
|
|
int charWidth;
|
|
int xLastLP;
|
|
int xLastDP;
|
|
|
|
DebugEntry(OEAddDeltaX);
|
|
|
|
lpVariable = ((LPBYTE)&pExtTextOut->variableString) +
|
|
sizeof(pExtTextOut->variableString.len) + cchText;
|
|
lpDeltaPos = (LPVARIABLE_DELTAX)DC_ROUND_UP_4((DWORD)lpVariable);
|
|
|
|
fSuccess = FALSE;
|
|
|
|
if (SELECTOROF(lpdxCharSpacing))
|
|
{
|
|
//
|
|
// We must translate the LPDX increments into device units.
|
|
// We have to do this a single point at a time to preserve
|
|
// accuracy and because the order field isn't the same size.
|
|
//
|
|
// We preserve accuracy by calculating the position of the
|
|
// point in the current coords, and converting this before
|
|
// subtracting the original point to get the delta.
|
|
// Otherwise, we'd hit rounding errors very often. 4 chars
|
|
// is the limit in TWIPs e.g.
|
|
//
|
|
|
|
lpDeltaPos->len = cchText * sizeof(TSHR_INT32);
|
|
|
|
xLastLP = ptStart.x;
|
|
ptStart.y = 0;
|
|
LPtoDP(g_oeState.hdc, &ptStart, 1);
|
|
xLastDP = ptStart.x;
|
|
|
|
for (i = 0; i < cchText; i++)
|
|
{
|
|
xLastLP += lpdxCharSpacing[i];
|
|
|
|
ptStart.x = xLastLP;
|
|
ptStart.y = 0;
|
|
LPtoDP(g_oeState.hdc, &ptStart, 1);
|
|
|
|
lpDeltaPos->deltaX[i] = ptStart.x - xLastDP;
|
|
xLastDP = ptStart.x;
|
|
}
|
|
|
|
//
|
|
// Remember we have a deltax array
|
|
//
|
|
pExtTextOut->fuOptions |= ETO_LPDX;
|
|
fSuccess = TRUE;
|
|
}
|
|
else if (fDeltaX)
|
|
{
|
|
//
|
|
// Simulate deltax.
|
|
//
|
|
lpDeltaPos->len = cchText * sizeof(TSHR_INT32);
|
|
|
|
//
|
|
// Is this the same font as last time? If so, we have the
|
|
// generated character width table cached.
|
|
//
|
|
// NOTE that when the capabilities chage, we clear the cache to
|
|
// avoid matching a font based on a stale index. And when starting
|
|
// to share.
|
|
//
|
|
if ((g_oeFhLast.fontIndex != pExtTextOut->common.FontIndex) ||
|
|
(g_oeFhLast.fontHeight != pExtTextOut->common.FontHeight) ||
|
|
(g_oeFhLast.fontWidth != pExtTextOut->common.FontWidth) ||
|
|
(g_oeFhLast.fontWeight != pExtTextOut->common.FontWeight) ||
|
|
(g_oeFhLast.fontFlags != pExtTextOut->common.FontFlags))
|
|
{
|
|
LPLOCALFONT lpFont;
|
|
HFONT hFontSim;
|
|
HFONT hFontOld;
|
|
TEXTMETRIC tmNew;
|
|
int width;
|
|
ABC abc;
|
|
BYTE italic;
|
|
BYTE underline;
|
|
BYTE strikeout;
|
|
BYTE pitch;
|
|
BYTE charset;
|
|
BYTE precis;
|
|
TSHR_UINT32 FontFlags;
|
|
|
|
//
|
|
// Generate a new table and cache the info
|
|
//
|
|
// We can not use the ACTUAL font selected in. We must
|
|
// create a new logical font from our table info.
|
|
//
|
|
|
|
ASSERT(g_poeLocalFonts);
|
|
lpFont = g_poeLocalFonts + pExtTextOut->common.FontIndex;
|
|
FontFlags = pExtTextOut->common.FontFlags;
|
|
|
|
//
|
|
// What are the logical attributes of this desired font?
|
|
//
|
|
|
|
italic = (BYTE)(FontFlags & NF_ITALIC);
|
|
underline = (BYTE)(FontFlags & NF_UNDERLINE);
|
|
strikeout = (BYTE)(FontFlags & NF_STRIKEOUT);
|
|
|
|
if (FontFlags & NF_FIXED_PITCH)
|
|
{
|
|
pitch = FF_DONTCARE | FIXED_PITCH;
|
|
}
|
|
else
|
|
{
|
|
pitch = FF_DONTCARE | VARIABLE_PITCH;
|
|
}
|
|
|
|
//
|
|
// Is this a TrueType font? The windows Font Mapper biases
|
|
// towards non-TrueType fonts.
|
|
//
|
|
if (FontFlags & NF_TRUE_TYPE)
|
|
{
|
|
pitch |= TMPF_TRUETYPE;
|
|
precis = OUT_TT_ONLY_PRECIS;
|
|
}
|
|
else
|
|
{
|
|
precis = OUT_RASTER_PRECIS;
|
|
}
|
|
|
|
//
|
|
// The given height is the char height, not the cell height.
|
|
// So pass it as a negative value below...
|
|
//
|
|
|
|
//
|
|
// Use the codepage (misleadingly named) to figure out the
|
|
// charset to ask for.
|
|
//
|
|
if (lpFont->Details.nfCodePage == NF_CP_WIN_ANSI)
|
|
{
|
|
charset = ANSI_CHARSET;
|
|
}
|
|
else if (lpFont->Details.nfCodePage == NF_CP_WIN_OEM)
|
|
{
|
|
charset = OEM_CHARSET;
|
|
}
|
|
else if (lpFont->Details.nfCodePage == NF_CP_WIN_SYMBOL)
|
|
{
|
|
charset = SYMBOL_CHARSET;
|
|
}
|
|
else
|
|
{
|
|
charset = DEFAULT_CHARSET;
|
|
}
|
|
|
|
hFontSim = CreateFont(-(int)pExtTextOut->common.FontHeight,
|
|
(int)pExtTextOut->common.FontWidth, 0, 0,
|
|
(int)pExtTextOut->common.FontWeight, italic, underline,
|
|
strikeout, charset, precis, CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY, pitch, (LPSTR)lpFont->Details.nfFaceName);
|
|
if (!hFontSim)
|
|
{
|
|
ERROR_OUT(("Couldn't create simulated font for metrics"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
hFontOld = SelectFont(g_osiScreenDC, hFontSim);
|
|
if (!hFontOld)
|
|
{
|
|
ERROR_OUT(("Couldn't select simulated font for metrics"));
|
|
DeleteFont(hFontSim);
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get the character dimensions
|
|
//
|
|
GetTextMetrics(g_osiScreenDC, &tmNew);
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
if (tmNew.tmPitchAndFamily & TMPF_TRUETYPE)
|
|
{
|
|
//
|
|
// Use ABC spacing for truetype
|
|
//
|
|
GetCharABCWidths(g_osiScreenDC, i, i, &abc);
|
|
|
|
width = abc.abcA + abc.abcB + abc.abcC;
|
|
}
|
|
else if (!(tmNew.tmPitchAndFamily & FIXED_PITCH))
|
|
{
|
|
//
|
|
// Note that the name of FIXED_PITCH is not what you'd
|
|
// expect, its ABSENCE means it's fixed.
|
|
//
|
|
// In any case, for fixed pitch fonts, each char is the
|
|
// same size.
|
|
//
|
|
width = tmNew.tmAveCharWidth - tmNew.tmOverhang;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Query the width of the char
|
|
//
|
|
GetCharWidth(g_osiScreenDC, i, i, &width);
|
|
width -= tmNew.tmOverhang;
|
|
}
|
|
|
|
g_oeFhLast.charWidths[i] = width;
|
|
}
|
|
|
|
//
|
|
// We've successfully generated the width info for this font,
|
|
// update our cache.
|
|
//
|
|
g_oeFhLast.fontIndex = pExtTextOut->common.FontIndex;
|
|
g_oeFhLast.fontHeight = pExtTextOut->common.FontHeight;
|
|
g_oeFhLast.fontWidth = pExtTextOut->common.FontWidth;
|
|
g_oeFhLast.fontWeight = pExtTextOut->common.FontWeight;
|
|
g_oeFhLast.fontFlags = pExtTextOut->common.FontFlags;
|
|
|
|
//
|
|
// Select back in old font and delete new one
|
|
//
|
|
SelectFont(g_osiScreenDC, hFontOld);
|
|
DeleteFont(hFontSim);
|
|
}
|
|
|
|
//
|
|
// Now calculate the width of each character in the string.
|
|
// This includes the last char because it is needed to correctly
|
|
// define the extent of the string.
|
|
//
|
|
for (i = 0; i < cchText; i++)
|
|
{
|
|
//
|
|
// The width is that in the width table for the current char.
|
|
//
|
|
lpDeltaPos->deltaX[i] = g_oeFhLast.charWidths[lpszText[i]];
|
|
}
|
|
|
|
//
|
|
// Remember we have a deltax array
|
|
//
|
|
pExtTextOut->fuOptions |= ETO_LPDX;
|
|
fSuccess = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No deltax array
|
|
//
|
|
lpDeltaPos->len = 0;
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(OEAddDeltaX, fSuccess);
|
|
return(fSuccess);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OEGetStringExtent()
|
|
//
|
|
int OEGetStringExtent
|
|
(
|
|
LPSTR lpszText,
|
|
UINT cchText,
|
|
LPINT lpdxCharSpacing,
|
|
LPRECT lprcExtent
|
|
)
|
|
{
|
|
DWORD textExtent;
|
|
UINT i;
|
|
int thisX;
|
|
int minX;
|
|
int maxX;
|
|
ABC abcSpace;
|
|
int overhang = 0;
|
|
|
|
DebugEntry(OEGetStringExtent);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_FONT);
|
|
ASSERT(g_oeState.uFlags & OESTATE_COORDS);
|
|
|
|
//
|
|
// With no characters, return a NULL rect
|
|
//
|
|
if (cchText == 0)
|
|
{
|
|
lprcExtent->left = 1;
|
|
lprcExtent->top = 0;
|
|
lprcExtent->right = 0;
|
|
lprcExtent->bottom = 0;
|
|
}
|
|
else if (!SELECTOROF(lpdxCharSpacing))
|
|
{
|
|
//
|
|
// Get the simple text extent from GDI
|
|
//
|
|
textExtent = GetTextExtent(g_oeState.hdc, lpszText, cchText);
|
|
|
|
lprcExtent->left = 0;
|
|
lprcExtent->top = 0;
|
|
lprcExtent->right = LOWORD(textExtent);
|
|
lprcExtent->bottom = HIWORD(textExtent);
|
|
|
|
//
|
|
// We now have the the advance distance for the string. However,
|
|
// some fonts like TrueType with C widths, or Italic, may extend
|
|
// beyond this. Add in extra space here if necessary
|
|
//
|
|
if (g_oeState.tmFont.tmPitchAndFamily & TMPF_TRUETYPE)
|
|
{
|
|
//
|
|
// Get the A-B-C widths of the last character
|
|
//
|
|
GetCharABCWidths(g_oeState.hdc, lpszText[cchText-1],
|
|
lpszText[cchText-1], &abcSpace);
|
|
|
|
//
|
|
// Add on the C width (the right side extra) of the last char
|
|
//
|
|
overhang = abcSpace.abcC;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use global overhang, this is an old font (like simulated Italic)
|
|
//
|
|
overhang = g_oeState.tmFont.tmOverhang;
|
|
}
|
|
|
|
lprcExtent->right += overhang;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Delta positions were given. In this case, the text extent is
|
|
// the sum of the delta values + the width of the last char
|
|
//
|
|
|
|
// Get the dimensions of the chars one by one, starting with 1st char
|
|
textExtent = GetTextExtent(g_oeState.hdc, lpszText, 1);
|
|
|
|
thisX = 0;
|
|
minX = 0;
|
|
maxX = LOWORD(textExtent);
|
|
|
|
for (i = 1; i < cchText; i++)
|
|
{
|
|
thisX += g_oeState.ptPolarity.x * lpdxCharSpacing[i-1];
|
|
textExtent = GetTextExtent(g_oeState.hdc, lpszText+i, 1);
|
|
|
|
minX = min(minX, thisX);
|
|
maxX = max(maxX, thisX + (int)LOWORD(textExtent));
|
|
}
|
|
|
|
thisX += g_oeState.ptPolarity.x * lpdxCharSpacing[cchText-1];
|
|
maxX = max(maxX, thisX);
|
|
|
|
lprcExtent->left = minX;
|
|
lprcExtent->top = 0;
|
|
lprcExtent->right = maxX;
|
|
lprcExtent->bottom = HIWORD(textExtent);
|
|
}
|
|
|
|
DebugExitDWORD(OEGetStringExtent, (DWORD)(LONG)overhang);
|
|
return(overhang);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvFillPath()
|
|
//
|
|
BOOL WINAPI DrvFillPath
|
|
(
|
|
HDC hdcDst
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvFillPath);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// The Path() apis don't set the drawing bounds. We assume the whole
|
|
// screen (device coords) instead.
|
|
//
|
|
// NOTE that NM 2.0 had a bug--it didn't account for the virtual
|
|
// screen origin when setting up the rect to accum as screen data.
|
|
// It just passed (0, 0, 32765, 32765) in.
|
|
//
|
|
fWeCare = OEBeforeDDI(DDI_FILLPATH, hdcDst, OESTATE_SDA_SCREEN);
|
|
|
|
fOutput = FillPath(hdcDst);
|
|
|
|
OEAfterDDI(DDI_FILLPATH, fWeCare, fOutput);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvFillPath, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
//
|
|
// DrvStrokeAndFillPath()
|
|
//
|
|
BOOL WINAPI DrvStrokeAndFillPath
|
|
(
|
|
HDC hdcDst
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvStrokeAndFillPath);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// The Path() apis don't set the drawing bounds. We assume the whole
|
|
// screen (device coords) instead.
|
|
//
|
|
// NOTE that NM 2.0 had a bug--it didn't account for the virtual
|
|
// screen origin when setting up the rect to accum as screen data.
|
|
// It just passed (0, 0, 32765, 32765) in.
|
|
//
|
|
|
|
fWeCare = OEBeforeDDI(DDI_STROKEANDFILLPATH, hdcDst, OESTATE_SDA_SCREEN);
|
|
|
|
fOutput = StrokeAndFillPath(hdcDst);
|
|
|
|
OEAfterDDI(DDI_STROKEANDFILLPATH, fWeCare, fOutput);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvStrokeAndFillPath, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
//
|
|
// DrvStrokePath()
|
|
//
|
|
BOOL WINAPI DrvStrokePath
|
|
(
|
|
HDC hdcDst
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvStrokePath);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// The Path() apis don't set the drawing bounds. We assume the whole
|
|
// screen (device coords) instead.
|
|
//
|
|
// NOTE that NM 2.0 had a bug--it didn't account for the virtual
|
|
// screen origin when setting up the rect to accum as screen data.
|
|
// It just passed (0, 0, 32765, 32765) in.
|
|
//
|
|
fWeCare = OEBeforeDDI(DDI_STROKEPATH, hdcDst, OESTATE_SDA_SCREEN);
|
|
|
|
fOutput = StrokePath(hdcDst);
|
|
|
|
OEAfterDDI(DDI_STROKEPATH, fWeCare, fOutput);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvStrokePath, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvFillRgn()
|
|
//
|
|
BOOL WINAPI DrvFillRgn
|
|
(
|
|
HDC hdcDst,
|
|
HRGN hrgnFill,
|
|
HBRUSH hbrFill
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvFillRgn);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// We can't use Rgn apis if the map mode isn't MM_TEXT. So we use DCBs
|
|
// instead.
|
|
//
|
|
fWeCare = OEBeforeDDI(DDI_FILLRGN, hdcDst, 0);
|
|
|
|
fOutput = FillRgn(hdcDst, hrgnFill, hbrFill);
|
|
|
|
if (OEAfterDDI(DDI_FILLRGN, fWeCare, fOutput))
|
|
{
|
|
//
|
|
// NOTE that OEAfterDDI() returns FALSE even if fOutput if we used
|
|
// DCBs to send as screen data. In other words, OEAfterDDI() returns
|
|
// TRUE IFF output happened into a DC we care about and it needs
|
|
// processing still.
|
|
//
|
|
OEAddRgnPaint(hrgnFill, hbrFill, g_oeState.lpdc->DrawMode.Rop2);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvFillRgn, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
//
|
|
// OETwoWayRopToThree()
|
|
// Gets the 3-way ROP equivalent of a 2-way ROP.
|
|
//
|
|
BOOL OETwoWayRopToThree
|
|
(
|
|
int rop2,
|
|
LPDWORD lpdwRop3
|
|
)
|
|
{
|
|
BOOL fConverted = TRUE;
|
|
|
|
DebugEntry(OETwoWayRopToThree);
|
|
|
|
switch (rop2)
|
|
{
|
|
case R2_BLACK:
|
|
*lpdwRop3 = BLACKNESS;
|
|
break;
|
|
|
|
case R2_NOT:
|
|
*lpdwRop3 = DSTINVERT;
|
|
break;
|
|
|
|
case R2_XORPEN:
|
|
*lpdwRop3 = PATINVERT;
|
|
break;
|
|
|
|
case R2_COPYPEN:
|
|
*lpdwRop3 = PATCOPY;
|
|
break;
|
|
|
|
case R2_WHITE:
|
|
*lpdwRop3 = WHITENESS;
|
|
break;
|
|
|
|
default:
|
|
fConverted = FALSE;
|
|
break;
|
|
}
|
|
|
|
DebugExitBOOL(OETwoWayRopToThree, fConverted);
|
|
return(fConverted);
|
|
}
|
|
|
|
//
|
|
// OEAddRgnPaint()
|
|
// This will set up a modified region (vis intersect param) and brush, and
|
|
// if possible will fake a PatBlt. If not, screen data.
|
|
//
|
|
// NOTE:
|
|
// (1) hrgnPaint is in DC coords
|
|
// (2) GetClipRgn() returns a region in screen coords
|
|
// (3) SelectClipRgn() takes a region in DC coords
|
|
//
|
|
void OEAddRgnPaint
|
|
(
|
|
HRGN hrgnPaint,
|
|
HBRUSH hbrPaint,
|
|
UINT rop2
|
|
)
|
|
{
|
|
BOOL fScreenData = TRUE;
|
|
HRGN hrgnClip;
|
|
HRGN hrgnNewClip;
|
|
HRGN hrgnOldClip;
|
|
POINT ptXlation;
|
|
DWORD dwRop3;
|
|
|
|
DebugEntry(OEAddRgnPaint);
|
|
|
|
//
|
|
// Get the original visrgn.
|
|
//
|
|
OEGetState(OESTATE_COORDS | OESTATE_REGION);
|
|
|
|
//
|
|
// Get the bounding box and convert the bounding box to our coords.
|
|
//
|
|
if (GetRgnBox(hrgnPaint, &g_oeState.rc) <= NULLREGION)
|
|
{
|
|
// Nothing to do.
|
|
TRACE_OUT(("OEAddRgnPaint: empty region"));
|
|
goto DC_EMPTY_REGION;
|
|
}
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// We can't continue if we aren't MM_TEXT--clip rgn APIs only work
|
|
// in that mode. So send as screen data instead.
|
|
//
|
|
if (GetMapMode(g_oeState.hdc) != MM_TEXT)
|
|
{
|
|
TRACE_OUT(("OEAddRgnPaint: map mode not MM_TEXT, send as screen data"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Save a copy of the current cliprgn
|
|
//
|
|
hrgnNewClip = CreateRectRgn(0, 0, 0, 0);
|
|
if (!hrgnNewClip)
|
|
DC_QUIT;
|
|
|
|
//
|
|
// Get app LP xlation factor; SelectClipRgn() expects DP units
|
|
//
|
|
ptXlation.x = 0;
|
|
ptXlation.y = 0;
|
|
DPtoLP(g_oeState.hdc, &ptXlation, 1);
|
|
|
|
hrgnOldClip = NULL;
|
|
if (hrgnClip = GetClipRgn(g_oeState.hdc))
|
|
{
|
|
hrgnOldClip = CreateRectRgn(0, 0, 0, 0);
|
|
if (!hrgnOldClip)
|
|
{
|
|
DeleteRgn(hrgnNewClip);
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// This is in screen coords. Convert to DC coords
|
|
// * Subtract the DC origin
|
|
// * Get the DP-LP xlation and subtract
|
|
//
|
|
CopyRgn(hrgnOldClip, hrgnClip);
|
|
OffsetRgn(hrgnOldClip,
|
|
-g_oeState.ptDCOrg.x + ptXlation.x,
|
|
-g_oeState.ptDCOrg.y + ptXlation.y);
|
|
|
|
//
|
|
// Intersect the current clip with the paint region (already in
|
|
// DC coords)
|
|
//
|
|
IntersectRgn(hrgnNewClip, hrgnOldClip, hrgnPaint);
|
|
|
|
//
|
|
// Convert the old LP region back to DP units to select back in
|
|
// when done.
|
|
//
|
|
OffsetRgn(hrgnOldClip, -ptXlation.x, -ptXlation.y);
|
|
}
|
|
else
|
|
{
|
|
CopyRgn(hrgnNewClip, hrgnPaint);
|
|
}
|
|
|
|
//
|
|
// Convert LP paint region to DP clip region
|
|
//
|
|
OffsetRgn(hrgnNewClip, -ptXlation.x, -ptXlation.y);
|
|
|
|
//
|
|
// Select in new clip region (expected to be in device coords).
|
|
//
|
|
SelectClipRgn(g_oeState.hdc, hrgnNewClip);
|
|
DeleteRgn(hrgnNewClip);
|
|
|
|
//
|
|
// Reget the RAO (intersect of vis/clip)
|
|
//
|
|
OEGetState(OESTATE_REGION);
|
|
|
|
//
|
|
// Get brush info
|
|
//
|
|
if (hbrPaint)
|
|
{
|
|
if (GetObject(hbrPaint, sizeof(g_oeState.logBrush), &g_oeState.logBrush))
|
|
{
|
|
g_oeState.uFlags |= OESTATE_BRUSH;
|
|
}
|
|
else
|
|
{
|
|
g_oeState.logBrush.lbStyle = BS_NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fake a patblt
|
|
//
|
|
if (OETwoWayRopToThree(rop2, &dwRop3))
|
|
{
|
|
fScreenData = FALSE;
|
|
OEAddBlt(dwRop3);
|
|
}
|
|
|
|
//
|
|
// Select back in the previous clip rgn
|
|
//
|
|
SelectClipRgn(g_oeState.hdc, hrgnOldClip);
|
|
if (hrgnOldClip)
|
|
DeleteRgn(hrgnOldClip);
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
if (fScreenData)
|
|
{
|
|
OTRACE(("OEAddRgnPaint: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
DC_EMPTY_REGION:
|
|
DebugExitVOID(OEAddRgnPaint);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvFrameRgn()
|
|
//
|
|
BOOL WINAPI DrvFrameRgn
|
|
(
|
|
HDC hdcDst,
|
|
HRGN hrgnFrameArea,
|
|
HBRUSH hbrFramePattern,
|
|
int cxFrame,
|
|
int cyFrame
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvFrameRgn);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_FRAMERGN, hdcDst, 0);
|
|
|
|
fOutput = FrameRgn(hdcDst, hrgnFrameArea, hbrFramePattern,
|
|
cxFrame, cyFrame);
|
|
|
|
if (OEAfterDDI(DDI_FRAMERGN, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_REGION);
|
|
|
|
if (GetRgnBox(hrgnFrameArea, &g_oeState.rc) > NULLREGION)
|
|
{
|
|
InflateRect(&g_oeState.rc,
|
|
g_oeState.ptPolarity.x * cxFrame,
|
|
g_oeState.ptPolarity.y * cyFrame);
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
OTRACE(("FrameRgn: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvFrameRgn, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvInvertRgn()
|
|
//
|
|
BOOL WINAPI DrvInvertRgn
|
|
(
|
|
HDC hdcDst,
|
|
HRGN hrgnInvert
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvInvertRgn);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_INVERTRGN, hdcDst, 0);
|
|
|
|
fOutput = InvertRgn(hdcDst, hrgnInvert);
|
|
|
|
if (OEAfterDDI(DDI_INVERTRGN, fWeCare, fOutput))
|
|
{
|
|
OEAddRgnPaint(hrgnInvert, NULL, R2_NOT);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvInvertRgn, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPaintRgn()
|
|
//
|
|
BOOL WINAPI DrvPaintRgn
|
|
(
|
|
HDC hdcDst,
|
|
HRGN hrgnPaint
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvPaintRgn);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_PAINTRGN, hdcDst, 0);
|
|
|
|
fOutput = PaintRgn(hdcDst, hrgnPaint);
|
|
|
|
if (OEAfterDDI(DDI_PAINTRGN, fWeCare, fOutput))
|
|
{
|
|
OEAddRgnPaint(hrgnPaint, g_oeState.lpdc->hBrush, g_oeState.lpdc->DrawMode.Rop2);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPaintRgn, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvLineTo()
|
|
//
|
|
BOOL WINAPI DrvLineTo
|
|
(
|
|
HDC hdcDst,
|
|
int xTo,
|
|
int yTo
|
|
)
|
|
{
|
|
POINT ptEnd;
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvLineTo);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_LINETO, hdcDst, OESTATE_CURPOS);
|
|
|
|
fOutput = LineTo(hdcDst, xTo, yTo);
|
|
|
|
//
|
|
// OEAfterDDI returns TRUE if the DC is a screen DC and output happened
|
|
// and we aren't skipping due to reentrancy.
|
|
//
|
|
if (OEAfterDDI(DDI_LINETO, fWeCare, fOutput))
|
|
{
|
|
//
|
|
// OEAddLine() will calculate extents, and if an order can't be sent,
|
|
// OEDoneDDI will add the bounds as screen data.
|
|
//
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_REGION);
|
|
|
|
ptEnd.x = xTo;
|
|
ptEnd.y = yTo;
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_CURPOS);
|
|
OEAddLine(g_oeState.ptCurPos, ptEnd);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvLineTo, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPolyline()
|
|
//
|
|
// NOTE:
|
|
// The differences between Polyline() and PolylineTo() are
|
|
// (1) PolylineTo moves the current position to the end coords of the
|
|
// last point; Polyline preserves the current position
|
|
// (2) Polyline uses the first point in the array as the starting coord
|
|
// of the first point; PolylineTo() uses the current position.
|
|
//
|
|
BOOL WINAPI DrvPolyline
|
|
(
|
|
HDC hdcDst,
|
|
LPPOINT aPoints,
|
|
int cPoints
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvPolyline);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_POLYLINE, hdcDst, 0);
|
|
|
|
fOutput = Polyline(hdcDst, aPoints, cPoints);
|
|
|
|
if (OEAfterDDI(DDI_POLYLINE, fWeCare, fOutput && cPoints > 1))
|
|
{
|
|
//
|
|
// GDI should NEVER return success if the aPoints parameter is
|
|
// bogus.
|
|
//
|
|
// NOTE LAURABU:
|
|
// This implementation is better than NM 2.0. That one would turn
|
|
// this GDI call actually into separate MoveTo/LineTo calls, which
|
|
// whacks out metafiles etc. Instead, we call through to the org
|
|
// Polyline, then add LineTo orders.
|
|
//
|
|
ASSERT(!IsBadReadPtr(aPoints, cPoints*sizeof(POINT)));
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_REGION);
|
|
|
|
OEAddPolyline(aPoints[0], aPoints+1, cPoints-1);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPolyline, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPolylineTo()
|
|
//
|
|
BOOL WINAPI DrvPolylineTo
|
|
(
|
|
HDC hdcDst,
|
|
LPPOINT aPoints,
|
|
int cPoints
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvPolylineTo);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_POLYLINETO, hdcDst, OESTATE_CURPOS);
|
|
|
|
fOutput = g_lpfnPolylineTo(hdcDst, aPoints, cPoints);
|
|
|
|
if (OEAfterDDI(DDI_POLYLINETO, fWeCare, fOutput && cPoints))
|
|
{
|
|
//
|
|
// GDI should NEVER return success if the aPoints parameter is
|
|
// bogus.
|
|
//
|
|
// NOTE LAURABU:
|
|
// This implementation is better than NM 2.0. That one would turn
|
|
// this GDI call actually into separate LineTo calls, which whacks
|
|
// out metafiles etc. Instead, we call through to the original
|
|
// PolylineTo, then add LineTo orders.
|
|
//
|
|
ASSERT(!IsBadReadPtr(aPoints, cPoints*sizeof(POINT)));
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_REGION);
|
|
ASSERT(g_oeState.uFlags & OESTATE_CURPOS);
|
|
|
|
OEAddPolyline(g_oeState.ptCurPos, aPoints, cPoints);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPolylineTo, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OEAddPolyline
|
|
// Used by Polyline(), PolylineTo(), and PolyPolyline()
|
|
//
|
|
void OEAddPolyline
|
|
(
|
|
POINT ptStart,
|
|
LPPOINT aPoints,
|
|
UINT cPoints
|
|
)
|
|
{
|
|
DebugEntry(OEAddPolyline);
|
|
|
|
ASSERT(g_oeState.uFlags & OESTATE_COORDS);
|
|
ASSERT(g_oeState.uFlags & OESTATE_REGION);
|
|
|
|
while (cPoints-- > 0)
|
|
{
|
|
OEAddLine(ptStart, *aPoints);
|
|
|
|
//
|
|
// The start point of the next line is the end point of the
|
|
// current one.
|
|
//
|
|
ptStart = *aPoints;
|
|
|
|
aPoints++;
|
|
}
|
|
|
|
DebugExitVOID(OEAddPolyline);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPlayEnhMetaFileRecord()
|
|
//
|
|
BOOL WINAPI DrvPlayEnhMetaFileRecord
|
|
(
|
|
HDC hdcDst,
|
|
LPHANDLETABLE lpEMFHandles,
|
|
LPENHMETARECORD lpEMFRecord,
|
|
DWORD cEMFHandles
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvPlayEnhMetaFileRecord);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_PLAYENHMETAFILERECORD, hdcDst, OESTATE_SDA_DCB);
|
|
|
|
fOutput = PlayEnhMetaFileRecord(hdcDst, lpEMFHandles, lpEMFRecord, cEMFHandles);
|
|
|
|
OEAfterDDI(DDI_PLAYENHMETAFILERECORD, fWeCare, fOutput);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPlayEnhMetaFileRecord, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPlayMetaFile()
|
|
//
|
|
BOOL WINAPI DrvPlayMetaFile
|
|
(
|
|
HDC hdcDst,
|
|
HMETAFILE hmf
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvPlayMetaFile);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_PLAYMETAFILE, hdcDst, OESTATE_SDA_DCB);
|
|
|
|
fOutput = PlayMetaFile(hdcDst, hmf);
|
|
|
|
OEAfterDDI(DDI_PLAYMETAFILE, fWeCare, fOutput);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPlayMetaFile, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPlayMetaFileRecord()
|
|
//
|
|
void WINAPI DrvPlayMetaFileRecord
|
|
(
|
|
HDC hdcDst,
|
|
LPHANDLETABLE lpMFHandles,
|
|
LPMETARECORD lpMFRecord,
|
|
UINT cMFHandles
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
|
|
DebugEntry(DrvPlayMetaFileRecord);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_PLAYMETAFILERECORD, hdcDst, OESTATE_SDA_DCB);
|
|
|
|
PlayMetaFileRecord(hdcDst, lpMFHandles, lpMFRecord, cMFHandles);
|
|
|
|
OEAfterDDI(DDI_PLAYMETAFILERECORD, fWeCare, TRUE);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitVOID(DrvPlayMetaFileRecord);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPolyBezier()
|
|
//
|
|
BOOL WINAPI DrvPolyBezier
|
|
(
|
|
HDC hdcDst,
|
|
LPPOINT aPoints,
|
|
UINT cPoints
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvPolyBezier);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_POLYBEZIER, hdcDst, 0);
|
|
|
|
fOutput = PolyBezier(hdcDst, aPoints, cPoints);
|
|
|
|
if (OEAfterDDI(DDI_POLYBEZIER, fWeCare, fOutput && (cPoints > 1)))
|
|
{
|
|
ASSERT(!IsBadReadPtr(aPoints, cPoints*sizeof(POINT)));
|
|
|
|
OEAddPolyBezier(aPoints[0], aPoints+1, cPoints-1);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPolyBezier, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPolyBezierTo()
|
|
//
|
|
BOOL WINAPI DrvPolyBezierTo
|
|
(
|
|
HDC hdcDst,
|
|
LPPOINT aPoints,
|
|
UINT cPoints
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvPolyBezierTo);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_POLYBEZIERTO, hdcDst, OESTATE_CURPOS);
|
|
|
|
fOutput = PolyBezierTo(hdcDst, aPoints, cPoints);
|
|
|
|
if (OEAfterDDI(DDI_POLYBEZIERTO, fWeCare, fOutput && cPoints))
|
|
{
|
|
ASSERT(!IsBadReadPtr(aPoints, cPoints*sizeof(POINT)));
|
|
ASSERT(g_oeState.uFlags & OESTATE_CURPOS);
|
|
|
|
OEAddPolyBezier(g_oeState.ptCurPos, aPoints, cPoints);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPolyBezierTo, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// OEAddPolyBezier()
|
|
//
|
|
// Adds poly bezier order for both PolyBezier() and PolyBezierTo().
|
|
//
|
|
void OEAddPolyBezier
|
|
(
|
|
POINT ptStart,
|
|
LPPOINT aPoints,
|
|
UINT cPoints
|
|
)
|
|
{
|
|
UINT iPoint;
|
|
LPINT_ORDER pOrder;
|
|
LPPOLYBEZIER_ORDER pPolyBezier;
|
|
|
|
DebugEntry(OEAddPolyBezier);
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_REGION);
|
|
|
|
//
|
|
// Calculate the bounds
|
|
//
|
|
g_oeState.rc.left = ptStart.x;
|
|
g_oeState.rc.top = ptStart.y;
|
|
g_oeState.rc.right = ptStart.x;
|
|
g_oeState.rc.bottom = ptStart.y;
|
|
|
|
for (iPoint = 0; iPoint < cPoints; iPoint++)
|
|
{
|
|
g_oeState.rc.left = min(g_oeState.rc.left, aPoints[iPoint].x);
|
|
g_oeState.rc.right = max(g_oeState.rc.right, aPoints[iPoint].x);
|
|
g_oeState.rc.top = min(g_oeState.rc.top, aPoints[iPoint].y);
|
|
g_oeState.rc.bottom = max(g_oeState.rc.bottom, aPoints[iPoint].y);
|
|
}
|
|
|
|
OEPolarityAdjust(&g_oeState.rc, 1);
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// OELRtoVirtual takes an exclusive rect and returns an inclusive one.
|
|
// But we passed it an inclusive already rect, so we need to account
|
|
// for that.
|
|
//
|
|
g_oeState.rc.right++;
|
|
g_oeState.rc.bottom++;
|
|
|
|
pOrder = NULL;
|
|
|
|
// Account for starting point also
|
|
if (OECheckOrder(ORD_POLYBEZIER, OECHECK_PEN | OECHECK_CLIPPING) &&
|
|
(cPoints < ORD_MAX_POLYBEZIER_POINTS))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(POLYBEZIER_ORDER) -
|
|
((ORD_MAX_POLYBEZIER_POINTS - cPoints - 1) *
|
|
sizeof(pPolyBezier->variablePoints.aPoints[0])), 0);
|
|
if (!pOrder)
|
|
DC_QUIT;
|
|
|
|
pPolyBezier = (LPPOLYBEZIER_ORDER)pOrder->abOrderData;
|
|
pPolyBezier->type = LOWORD(ORD_POLYBEZIER);
|
|
|
|
//
|
|
// Copy them into the order array
|
|
//
|
|
pPolyBezier->variablePoints.len =
|
|
((cPoints+1) * sizeof(pPolyBezier->variablePoints.aPoints[0]));
|
|
|
|
pPolyBezier->variablePoints.aPoints[0].x = ptStart.x;
|
|
pPolyBezier->variablePoints.aPoints[0].y = ptStart.y;
|
|
hmemcpy(pPolyBezier->variablePoints.aPoints+1, aPoints,
|
|
cPoints*sizeof(pPolyBezier->variablePoints.aPoints[0]));
|
|
|
|
//
|
|
// Convert points to virtual
|
|
//
|
|
// NOTE that this works because aPoints[] holds TSHR_POINT16s, which
|
|
// are natively the same size as POINT structures.
|
|
//
|
|
OELPtoVirtual(g_oeState.hdc, (LPPOINT)pPolyBezier->variablePoints.aPoints,
|
|
cPoints+1);
|
|
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.bkColorL,
|
|
&pPolyBezier->BackColor, FALSE);
|
|
OEConvertColor(g_oeState.lpdc->DrawMode.txColorL,
|
|
&pPolyBezier->ForeColor, FALSE);
|
|
|
|
pPolyBezier->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pPolyBezier->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
pPolyBezier->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pPolyBezier->PenWidth = 1;
|
|
OEConvertColor(g_oeState.logPen.lopnColor, &pPolyBezier->PenColor,
|
|
FALSE);
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
OTRACE(("PolyBezier: Order %08lx, Rect {%d, %d, %d, %d} with %d points",
|
|
pOrder, g_oeState.rc.left, g_oeState.rc.top,
|
|
g_oeState.rc.right, g_oeState.rc.bottom, cPoints+1));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("PolyBezier: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
DebugExitVOID(OEAddPolyBezier);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPolygon()
|
|
//
|
|
BOOL WINAPI DrvPolygon
|
|
(
|
|
HDC hdcDst,
|
|
LPPOINT aPoints,
|
|
int cPoints
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
LPINT_ORDER pOrder;
|
|
LPPOLYGON_ORDER pPolygon;
|
|
int iPoint;
|
|
POINT ptBrushOrg;
|
|
|
|
DebugEntry(DrvPolygon);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_POLYGON, hdcDst, 0);
|
|
|
|
fOutput = Polygon(hdcDst, aPoints, cPoints);
|
|
|
|
if (OEAfterDDI(DDI_POLYGON, fWeCare, fOutput && (cPoints > 1)))
|
|
{
|
|
ASSERT(!IsBadReadPtr(aPoints, cPoints*sizeof(POINT)));
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
//
|
|
// Compute the bounds
|
|
//
|
|
g_oeState.rc.left = aPoints[0].x;
|
|
g_oeState.rc.top = aPoints[0].y;
|
|
g_oeState.rc.right = aPoints[0].x;
|
|
g_oeState.rc.bottom = aPoints[0].y;
|
|
|
|
for (iPoint = 1; iPoint < cPoints; iPoint++)
|
|
{
|
|
g_oeState.rc.left = min(g_oeState.rc.left, aPoints[iPoint].x);
|
|
g_oeState.rc.top = min(g_oeState.rc.top, aPoints[iPoint].y);
|
|
g_oeState.rc.right = max(g_oeState.rc.right, aPoints[iPoint].x);
|
|
g_oeState.rc.bottom = max(g_oeState.rc.bottom, aPoints[iPoint].y);
|
|
}
|
|
|
|
OEPolarityAdjust(&g_oeState.rc, 1);
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
|
|
//
|
|
// The rect is in inclusive coords already, OELRtoVirtual thinks
|
|
// it's exclusive. So we need to add one back on to the right
|
|
// and bottom to end up with the real inclusive rect.
|
|
//
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
g_oeState.rc.right++;
|
|
g_oeState.rc.bottom++;
|
|
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_POLYGON, OECHECK_PEN | OECHECK_BRUSH | OECHECK_CLIPPING) &&
|
|
(cPoints <= ORD_MAX_POLYGON_POINTS))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(POLYGON_ORDER) -
|
|
((ORD_MAX_POLYGON_POINTS - cPoints) *
|
|
sizeof(pPolygon->variablePoints.aPoints[0])), 0);
|
|
if (!pOrder)
|
|
goto NoPolygonOrder;
|
|
|
|
pPolygon = (LPPOLYGON_ORDER)pOrder->abOrderData;
|
|
pPolygon->type = LOWORD(ORD_POLYGON);
|
|
|
|
pPolygon->variablePoints.len =
|
|
cPoints * sizeof(pPolygon->variablePoints.aPoints[0]);
|
|
hmemcpy(pPolygon->variablePoints.aPoints, aPoints,
|
|
pPolygon->variablePoints.len);
|
|
|
|
//
|
|
// Convert all the points to virtual
|
|
//
|
|
// NOTE that this works because aPoints[] hols TSHR_POINT16s,
|
|
// which are natively the same size as POINT structures.
|
|
//
|
|
OELPtoVirtual(g_oeState.hdc, (LPPOINT)pPolygon->variablePoints.aPoints,
|
|
cPoints);
|
|
|
|
OEGetBrushInfo(&pPolygon->BackColor, &pPolygon->ForeColor,
|
|
&pPolygon->BrushStyle, &pPolygon->BrushHatch,
|
|
pPolygon->BrushExtra);
|
|
|
|
GetBrushOrgEx(g_oeState.hdc, &ptBrushOrg);
|
|
pPolygon->BrushOrgX = (BYTE)ptBrushOrg.x;
|
|
pPolygon->BrushOrgY = (BYTE)ptBrushOrg.y;
|
|
|
|
pPolygon->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pPolygon->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
//
|
|
// Pen info
|
|
//
|
|
pPolygon->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pPolygon->PenWidth = 1;
|
|
OEConvertColor(g_oeState.logPen.lopnColor, &pPolygon->PenColor,
|
|
FALSE);
|
|
pPolygon->FillMode = GetPolyFillMode(g_oeState.hdc);
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
|
|
OTRACE(("Polygon: Order %08lx, Rect {%d, %d, %d, %d} with %d points",
|
|
pOrder, g_oeState.rc.left, g_oeState.rc.top,
|
|
g_oeState.rc.right, g_oeState.rc.bottom, cPoints));
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
|
|
}
|
|
|
|
NoPolygonOrder:
|
|
if (!pOrder)
|
|
{
|
|
OTRACE(("Polygon: Sending %d points as screen data {%d, %d, %d, %d}",
|
|
cPoints, g_oeState.rc.left, g_oeState.rc.top,
|
|
g_oeState.rc.right, g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPolygon, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvPolyPolygon()
|
|
//
|
|
BOOL WINAPI DrvPolyPolygon
|
|
(
|
|
HDC hdcDst,
|
|
LPPOINT aPoints,
|
|
LPINT aPolygonPoints,
|
|
int cPolygons
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
int iPolygon;
|
|
int iPoint;
|
|
|
|
DebugEntry(DrvPolyPolygon);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_POLYPOLYGON, hdcDst, 0);
|
|
|
|
fOutput = PolyPolygon(hdcDst, aPoints, aPolygonPoints, cPolygons);
|
|
|
|
if (OEAfterDDI(DDI_POLYPOLYGON, fWeCare, fOutput && cPolygons))
|
|
{
|
|
ASSERT(!IsBadReadPtr(aPolygonPoints, cPolygons*sizeof(int)));
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// How many points total are there?
|
|
//
|
|
iPoint = 0;
|
|
for (iPolygon = 0; iPolygon < cPolygons; iPolygon++)
|
|
{
|
|
iPoint += aPolygonPoints[iPolygon];
|
|
}
|
|
|
|
ASSERT(!IsBadReadPtr(aPoints, iPoint*sizeof(POINT)));
|
|
#endif
|
|
|
|
//
|
|
// Like LineTo, we need to juggle the coords for polarity.
|
|
//
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_REGION);
|
|
|
|
for (iPolygon = 0; iPolygon < cPolygons; iPolygon++, aPolygonPoints++)
|
|
{
|
|
//
|
|
// No points for this polygon, nothing to do.
|
|
//
|
|
if (*aPolygonPoints < 2)
|
|
{
|
|
aPoints += *aPolygonPoints;
|
|
continue;
|
|
}
|
|
|
|
g_oeState.rc.left = aPoints[0].x;
|
|
g_oeState.rc.top = aPoints[0].y;
|
|
g_oeState.rc.right = aPoints[0].x;
|
|
g_oeState.rc.bottom = aPoints[0].y;
|
|
|
|
aPoints++;
|
|
|
|
for (iPoint = 1; iPoint < *aPolygonPoints; iPoint++, aPoints++)
|
|
{
|
|
g_oeState.rc.left = min(g_oeState.rc.left, aPoints[0].x);
|
|
g_oeState.rc.top = min(g_oeState.rc.top, aPoints[0].y);
|
|
g_oeState.rc.right = max(g_oeState.rc.right, aPoints[0].x);
|
|
g_oeState.rc.bottom = max(g_oeState.rc.bottom, aPoints[0].y);
|
|
}
|
|
|
|
OEPolarityAdjust(&g_oeState.rc, 1);
|
|
OEPenWidthAdjust(&g_oeState.rc, 1);
|
|
|
|
//
|
|
// Our rectangle is already inclusive, and OELRtoVirtual() will
|
|
// treat it like it's exclusive. So after we return add one back
|
|
// to the right & bottom to end up with the real inclusive
|
|
// rectangle.
|
|
//
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
g_oeState.rc.right++;
|
|
g_oeState.rc.bottom++;
|
|
|
|
OTRACE(("PolyPolygon: Sending piece %d as screen data {%d, %d, %d, %d}",
|
|
iPolygon, g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPolyPolygon, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// PolyPolyline()
|
|
//
|
|
BOOL WINAPI DrvPolyPolyline
|
|
(
|
|
DWORD cPtTotal,
|
|
HDC hdcDst,
|
|
LPPOINT aPoints,
|
|
LPINT acPolylinePoints,
|
|
int cPolylines
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
UINT cPoints;
|
|
|
|
DebugEntry(DrvPolyPolyline);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// LAURABU NOTE:
|
|
// This code is better than 2.0. 2.0 would simulate the actual GDI
|
|
// call by repeated Polyline calls. We accumulate orders the same way
|
|
// that would have happened, but let GDI do the drawing, which is much
|
|
// more metafile friendly, among other things.
|
|
//
|
|
fWeCare = OEBeforeDDI(DDI_POLYPOLYLINE, hdcDst, 0);
|
|
|
|
fOutput = g_lpfnPolyPolyline(cPtTotal, hdcDst, aPoints, acPolylinePoints,
|
|
cPolylines);
|
|
|
|
if (OEAfterDDI(DDI_POLYPOLYLINE, fWeCare, fOutput && cPolylines))
|
|
{
|
|
ASSERT(!IsBadReadPtr(acPolylinePoints, cPolylines*sizeof(int)));
|
|
ASSERT(!IsBadReadPtr(aPoints, (UINT)cPtTotal*sizeof(POINT)));
|
|
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_REGION);
|
|
|
|
while (cPolylines-- > 0)
|
|
{
|
|
cPoints = *(acPolylinePoints++);
|
|
|
|
if (cPoints > 1)
|
|
{
|
|
OEAddPolyline(aPoints[0], aPoints+1, cPoints-1);
|
|
}
|
|
|
|
aPoints += cPoints;
|
|
}
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvPolyPolyline, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvRectangle()
|
|
//
|
|
BOOL WINAPI DrvRectangle
|
|
(
|
|
HDC hdcDst,
|
|
int xLeft,
|
|
int yTop,
|
|
int xRight,
|
|
int yBottom
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
RECT rcAdjusted;
|
|
LPINT_ORDER pOrder;
|
|
LPRECTANGLE_ORDER pRectangle;
|
|
POINT ptBrushOrg;
|
|
LPRECT pRect;
|
|
int sideWidth;
|
|
|
|
DebugEntry(DrvRectangle);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_RECTANGLE, hdcDst, 0);
|
|
|
|
fOutput = Rectangle(hdcDst, xLeft, yTop, xRight, yBottom);
|
|
|
|
if (OEAfterDDI(DDI_RECTANGLE, fWeCare, fOutput && (xLeft != xRight) && (yTop != yBottom)))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_PEN | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
g_oeState.rc.left = xLeft;
|
|
g_oeState.rc.top = yTop;
|
|
g_oeState.rc.right = xRight;
|
|
g_oeState.rc.bottom = yBottom;
|
|
|
|
CopyRect(&rcAdjusted, &g_oeState.rc);
|
|
|
|
if ((g_oeState.logPen.lopnStyle == PS_SOLID) ||
|
|
(g_oeState.logPen.lopnStyle == PS_INSIDEFRAME))
|
|
{
|
|
OEPenWidthAdjust(&rcAdjusted, 2);
|
|
}
|
|
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
OELRtoVirtual(g_oeState.hdc, &rcAdjusted, 1);
|
|
|
|
rcAdjusted.right--;
|
|
rcAdjusted.bottom--;
|
|
|
|
pOrder = NULL;
|
|
|
|
if (OECheckOrder(ORD_RECTANGLE, OECHECK_PEN | OECHECK_BRUSH | OECHECK_CLIPPING))
|
|
{
|
|
pOrder = OA_DDAllocOrderMem(sizeof(RECTANGLE_ORDER), 0);
|
|
if (!pOrder)
|
|
goto NoRectOrder;
|
|
|
|
pRectangle = (LPRECTANGLE_ORDER)pOrder->abOrderData;
|
|
pRectangle->type = LOWORD(ORD_RECTANGLE);
|
|
|
|
pRectangle->nLeftRect = g_oeState.rc.left;
|
|
pRectangle->nTopRect = g_oeState.rc.top;
|
|
pRectangle->nRightRect = g_oeState.rc.right;
|
|
pRectangle->nBottomRect = g_oeState.rc.bottom;
|
|
|
|
OEGetBrushInfo(&pRectangle->BackColor, &pRectangle->ForeColor,
|
|
&pRectangle->BrushStyle, &pRectangle->BrushHatch,
|
|
pRectangle->BrushExtra);
|
|
|
|
GetBrushOrgEx(g_oeState.hdc, &ptBrushOrg);
|
|
pRectangle->BrushOrgX = (BYTE)ptBrushOrg.x;
|
|
pRectangle->BrushOrgY = (BYTE)ptBrushOrg.y;
|
|
|
|
pRectangle->BackMode = g_oeState.lpdc->DrawMode.bkMode;
|
|
pRectangle->ROP2 = g_oeState.lpdc->DrawMode.Rop2;
|
|
|
|
pRectangle->PenStyle = g_oeState.logPen.lopnStyle;
|
|
pRectangle->PenWidth = 1;
|
|
|
|
OEConvertColor(g_oeState.logPen.lopnColor, &pRectangle->PenColor,
|
|
FALSE);
|
|
|
|
pOrder->OrderHeader.Common.fOrderFlags = OF_SPOILABLE;
|
|
if ((g_oeState.logBrush.lbStyle == BS_SOLID) ||
|
|
((pRectangle->BackMode == OPAQUE) &&
|
|
(g_oeState.logBrush.lbStyle != BS_NULL)))
|
|
{
|
|
pOrder->OrderHeader.Common.fOrderFlags |= OF_SPOILER;
|
|
}
|
|
|
|
//
|
|
// Since we only encode orders of width 1, the bounding rect
|
|
// stuff is simple.
|
|
//
|
|
OTRACE(("Rectangle: Order %08lx, pOrder, Rect {%d, %d, %d, %d}",
|
|
pOrder, g_oeState.rc.left,
|
|
g_oeState.rc.top, g_oeState.rc.right, g_oeState.rc.bottom));
|
|
|
|
OEClipAndAddOrder(pOrder, NULL);
|
|
}
|
|
NoRectOrder:
|
|
if (!pOrder)
|
|
{
|
|
//
|
|
// This is more complicated. We accumulate screen data for
|
|
// pens of different sizes.
|
|
//
|
|
|
|
//
|
|
// If the interior is drawn, then we need to send all the screen
|
|
// area enclosed by the rect. Otherwise, we can just send the
|
|
// four rectangles describing the border.
|
|
//
|
|
if (g_oeState.logBrush.lbStyle == BS_NULL)
|
|
{
|
|
pRect = NULL;
|
|
|
|
//
|
|
// Use the pen width to determine the width of each rect
|
|
// to add as screen data
|
|
//
|
|
switch (g_oeState.logPen.lopnStyle)
|
|
{
|
|
case PS_NULL:
|
|
// Nothing to do.
|
|
break;
|
|
|
|
case PS_SOLID:
|
|
//
|
|
// The difference between the adjusted and normal
|
|
// rects is half the pen width, so double this up
|
|
// to get the width of each piece to send.
|
|
//
|
|
pRect = &rcAdjusted;
|
|
sideWidth = 2*(g_oeState.rc.left - rcAdjusted.left)
|
|
- 1;
|
|
break;
|
|
|
|
case PS_INSIDEFRAME:
|
|
//
|
|
// The pen is contained entirely within the corner
|
|
// pts passed to this function.
|
|
//
|
|
pRect = &g_oeState.rc;
|
|
sideWidth = 2*(g_oeState.rc.left - rcAdjusted.left)
|
|
- 1;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// All other pens have width of 1 and are inside the
|
|
// frame.
|
|
//
|
|
pRect = &g_oeState.rc;
|
|
sideWidth = 0;
|
|
break;
|
|
}
|
|
|
|
if (pRect)
|
|
{
|
|
RECT rcT;
|
|
|
|
//
|
|
// Left
|
|
//
|
|
CopyRect(&rcT, pRect);
|
|
rcT.right = rcT.left + sideWidth;
|
|
rcT.bottom -= sideWidth + 1;
|
|
|
|
OTRACE(("Rectangle left: Sending screen data {%d, %d, %d, %d}",
|
|
rcT.left, rcT.top, rcT.right, rcT.bottom));
|
|
OEClipAndAddScreenData(&rcT);
|
|
|
|
//
|
|
// Top
|
|
//
|
|
CopyRect(&rcT, pRect);
|
|
rcT.left += sideWidth + 1;
|
|
rcT.bottom = rcT.top + sideWidth;
|
|
|
|
OTRACE(("Rectangle top: Sending screen data {%d, %d, %d, %d}",
|
|
rcT.left, rcT.top, rcT.right, rcT.bottom));
|
|
OEClipAndAddScreenData(&rcT);
|
|
|
|
//
|
|
// Right
|
|
//
|
|
CopyRect(&rcT, pRect);
|
|
rcT.left = rcT.right - sideWidth;
|
|
rcT.top += sideWidth + 1;
|
|
|
|
OTRACE(("Rectangle right: Sending screen data {%d, %d, %d, %d}",
|
|
rcT.left, rcT.top, rcT.right, rcT.bottom));
|
|
OEClipAndAddScreenData(&rcT);
|
|
|
|
//
|
|
// Bottom
|
|
//
|
|
CopyRect(&rcT, pRect);
|
|
rcT.right -= sideWidth + 1;
|
|
rcT.top = rcT.bottom - sideWidth;
|
|
|
|
OTRACE(("Rectangle bottom: Sending screen data {%d, %d, %d, %d}",
|
|
rcT.left, rcT.top, rcT.right, rcT.bottom));
|
|
OEClipAndAddScreenData(&rcT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_oeState.logPen.lopnStyle == PS_SOLID)
|
|
pRect = &rcAdjusted;
|
|
else
|
|
pRect = &g_oeState.rc;
|
|
|
|
OTRACE(("Rectangle: Sending as screen data {%d, %d, %d, %d}",
|
|
pRect->left, pRect->top, pRect->right, pRect->bottom));
|
|
OEClipAndAddScreenData(pRect);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvRectangle, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvSetDIBitsToDevice()
|
|
//
|
|
int WINAPI DrvSetDIBitsToDevice
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
int cxDst,
|
|
int cyDst,
|
|
int xSrc,
|
|
int ySrc,
|
|
UINT uStartScan,
|
|
UINT cScanLines,
|
|
LPVOID lpvBits,
|
|
LPBITMAPINFO lpbmi,
|
|
UINT fuColorUse
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
|
|
DebugEntry(DrvSetDIBitsToDevice);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_SETDIBITSTODEVICE, hdcDst, 0);
|
|
|
|
fOutput = SetDIBitsToDevice(hdcDst, xDst, yDst, cxDst, cyDst,
|
|
xSrc, ySrc, uStartScan, cScanLines, lpvBits, lpbmi, fuColorUse);
|
|
|
|
if (OEAfterDDI(DDI_SETDIBITSTODEVICE, fWeCare, fOutput))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_REGION);
|
|
|
|
g_oeState.rc.left = xDst;
|
|
g_oeState.rc.top = yDst;
|
|
OELPtoVirtual(g_oeState.hdc, (LPPOINT)&g_oeState.rc.left, 1);
|
|
g_oeState.rc.right = g_oeState.rc.left + cxDst;
|
|
g_oeState.rc.bottom = g_oeState.rc.top + cyDst;
|
|
|
|
OTRACE(("SetDIBitsToDevice: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvSetDIBitsToDevice, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvSetPixel()
|
|
//
|
|
COLORREF WINAPI DrvSetPixel
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
COLORREF crPixel
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
COLORREF rgbOld;
|
|
|
|
DebugEntry(DrvSetPixel);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_SETPIXEL, hdcDst, 0);
|
|
|
|
rgbOld = SetPixel(hdcDst, xDst, yDst, crPixel);
|
|
|
|
if (OEAfterDDI(DDI_SETPIXEL, fWeCare, (rgbOld != (COLORREF)-1)))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_REGION);
|
|
|
|
g_oeState.rc.left = xDst;
|
|
g_oeState.rc.top = yDst;
|
|
g_oeState.rc.right = xDst;
|
|
g_oeState.rc.bottom = yDst;
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
g_oeState.rc.right++;
|
|
g_oeState.rc.bottom++;
|
|
|
|
OTRACE(("SetPixel: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitDWORD(DrvSetPxel, rgbOld);
|
|
return(rgbOld);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvStretchDIBits()
|
|
//
|
|
int WINAPI DrvStretchDIBits
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
int cxDst,
|
|
int cyDst,
|
|
int xSrc,
|
|
int ySrc,
|
|
int cxSrc,
|
|
int cySrc,
|
|
LPVOID lpvBits,
|
|
LPBITMAPINFO lpbmi,
|
|
UINT fuColorUse,
|
|
DWORD dwRop
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
BOOL fOutput;
|
|
BYTE bRop;
|
|
|
|
DebugEntry(DrvStretchDIBits);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
fWeCare = OEBeforeDDI(DDI_STRETCHDIBITS, hdcDst, 0);
|
|
|
|
fOutput = StretchDIBits(hdcDst, xDst, yDst, cxDst, cyDst,
|
|
xSrc, ySrc, cxSrc, cySrc, lpvBits, lpbmi, fuColorUse, dwRop);
|
|
|
|
if (OEAfterDDI(DDI_STRETCHDIBITS, fWeCare, fOutput && cxDst && cyDst))
|
|
{
|
|
OEGetState(OESTATE_COORDS | OESTATE_BRUSH | OESTATE_REGION);
|
|
|
|
g_oeState.rc.left = xDst;
|
|
g_oeState.rc.top = yDst;
|
|
g_oeState.rc.right = xDst + cxDst;
|
|
g_oeState.rc.bottom = yDst + cyDst;
|
|
|
|
OELRtoVirtual(g_oeState.hdc, &g_oeState.rc, 1);
|
|
|
|
//
|
|
// If this is a PatBlt really, do that instead.
|
|
//
|
|
bRop = LOBYTE(HIWORD(dwRop));
|
|
if (((bRop & 0x33) << 2) == (bRop & 0xCC))
|
|
{
|
|
OEAddBlt(dwRop);
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Do tile bitblt order stuff...
|
|
//
|
|
|
|
OTRACE(("StretchDIBits: Sending as screen data {%d, %d, %d, %d}",
|
|
g_oeState.rc.left, g_oeState.rc.top, g_oeState.rc.right,
|
|
g_oeState.rc.bottom));
|
|
OEClipAndAddScreenData(&g_oeState.rc);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitBOOL(DrvStretchDIBits, fOutput);
|
|
return(fOutput);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvUpdateColors()
|
|
//
|
|
int WINAPI DrvUpdateColors
|
|
(
|
|
HDC hdcDst
|
|
)
|
|
{
|
|
BOOL fWeCare;
|
|
int ret;
|
|
|
|
DebugEntry(DrvUpdateColors);
|
|
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// This doesn't reset the drawing bounds. So we just assume the whole
|
|
// DC changed. And the return value is meaningless. We can't assume
|
|
// that zero means failure.
|
|
//
|
|
fWeCare = OEBeforeDDI(DDI_UPDATECOLORS, hdcDst, OESTATE_SDA_SCREEN);
|
|
|
|
ret = UpdateColors(hdcDst);
|
|
|
|
OEAfterDDI(DDI_UPDATECOLORS, fWeCare, TRUE);
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
|
|
DebugExitDWORD(DrvUpdateColors, (DWORD)(UINT)ret);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SETTINGS/MODE FUNCTIONS
|
|
// For full screen dos boxes, resolution/color depth changes
|
|
//
|
|
|
|
|
|
//
|
|
// DrvGDIRealizePalette()
|
|
//
|
|
// The WM_PALETTE* messages in Win95 are unreliable. So, like NM 2.0, we
|
|
// patch two GDI APIs instead and update a shared variable
|
|
//
|
|
DWORD WINAPI DrvGDIRealizePalette(HDC hdc)
|
|
{
|
|
DWORD dwRet;
|
|
|
|
DebugEntry(DrvGDIRealizePalette);
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_GDIREALIZEPALETTE], PATCH_DISABLE);
|
|
dwRet = GDIRealizePalette(hdc);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_GDIREALIZEPALETTE], PATCH_ENABLE);
|
|
|
|
ASSERT(g_asSharedMemory);
|
|
g_asSharedMemory->pmPaletteChanged = TRUE;
|
|
|
|
DebugExitDWORD(DrvGDIRealizePalette, dwRet);
|
|
return(dwRet);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvRealizeDefaultPalette()
|
|
//
|
|
// The WM_PALETTE* messages in Win95 are unreliable. So, like NM 2.0, we
|
|
// patch two GDI APIs instead and update a shared variable
|
|
//
|
|
void WINAPI DrvRealizeDefaultPalette(HDC hdc)
|
|
{
|
|
DebugEntry(DrvRealizeDefaultPalette);
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_REALIZEDEFAULTPALETTE], PATCH_DISABLE);
|
|
RealizeDefaultPalette(hdc);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_REALIZEDEFAULTPALETTE], PATCH_ENABLE);
|
|
|
|
ASSERT(g_asSharedMemory);
|
|
g_asSharedMemory->pmPaletteChanged = TRUE;
|
|
|
|
DebugExitVOID(DrvRealizeDefaultPalette);
|
|
}
|
|
|
|
|
|
//
|
|
// This is called when a blue screen fault is coming up, or an app calls
|
|
// Disable() in USER.
|
|
//
|
|
UINT WINAPI DrvDeath
|
|
(
|
|
HDC hdc
|
|
)
|
|
{
|
|
UINT uResult;
|
|
|
|
g_asSharedMemory->fullScreen = TRUE;
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_DEATH], PATCH_DISABLE);
|
|
uResult = Death(hdc);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_DEATH], PATCH_ENABLE);
|
|
|
|
return(uResult);
|
|
}
|
|
|
|
|
|
//
|
|
// This is called when a blue screen fault is going away, or an app calls
|
|
// Enable() in USER.
|
|
//
|
|
UINT WINAPI DrvResurrection
|
|
(
|
|
HDC hdc,
|
|
DWORD dwParam1,
|
|
DWORD dwParam2,
|
|
DWORD dwParam3
|
|
)
|
|
{
|
|
UINT uResult;
|
|
|
|
g_asSharedMemory->fullScreen = FALSE;
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_RESURRECTION], PATCH_DISABLE);
|
|
uResult = Resurrection(hdc, dwParam1, dwParam2, dwParam3);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_RESURRECTION], PATCH_ENABLE);
|
|
|
|
return(uResult);
|
|
}
|
|
|
|
|
|
//
|
|
// This is called by a dosbox when going to or coming out of full screen
|
|
// mode. DirectX calls it also.
|
|
//
|
|
LONG WINAPI DrvWinOldAppHackoMatic
|
|
(
|
|
LONG lFlags
|
|
)
|
|
{
|
|
LONG lResult;
|
|
|
|
if (lFlags == WOAHACK_LOSINGDISPLAYFOCUS)
|
|
{
|
|
//
|
|
// DOS box is going to full screen from windowed
|
|
//
|
|
g_asSharedMemory->fullScreen = TRUE;
|
|
}
|
|
else if (lFlags == WOAHACK_GAININGDISPLAYFOCUS)
|
|
{
|
|
//
|
|
// DOS box is going from windowed to full screen
|
|
//
|
|
g_asSharedMemory->fullScreen = FALSE;
|
|
}
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_WINOLDAPPHACKOMATIC], PATCH_DISABLE);
|
|
lResult = WinOldAppHackoMatic(lFlags);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_WINOLDAPPHACKOMATIC], PATCH_ENABLE);
|
|
|
|
return(lResult);
|
|
}
|
|
|
|
|
|
//
|
|
// ChangeDisplaySettings() WIN95
|
|
// ChangeDisplaySettingsEx() MEMPHIS
|
|
//
|
|
// This is called in 3 circumstances:
|
|
// * By the control to change your screen
|
|
// * By the shell when warm-docking
|
|
// * By 3rd party games to change the settings silently.
|
|
//
|
|
// Easiest thing to do is just to fail this completely.
|
|
//
|
|
|
|
LONG WINAPI DrvChangeDisplaySettings
|
|
(
|
|
LPDEVMODE lpDevMode,
|
|
DWORD flags
|
|
)
|
|
{
|
|
return(DISP_CHANGE_FAILED);
|
|
}
|
|
|
|
|
|
LONG WINAPI DrvChangeDisplaySettingsEx
|
|
(
|
|
LPCSTR lpszDeviceName,
|
|
LPDEVMODE lpDevMode,
|
|
HWND hwnd,
|
|
DWORD flags,
|
|
LPVOID lParam
|
|
)
|
|
{
|
|
return(DISP_CHANGE_FAILED);
|
|
}
|
|
|
|
|
|
//
|
|
// OBJECT FUNCTIONS
|
|
// For bitmaps (SPBs and cache) and brushes
|
|
//
|
|
|
|
|
|
//
|
|
// DrvCreateSpb()
|
|
//
|
|
// This watches for SPB bitmaps being created.
|
|
//
|
|
UINT WINAPI DrvCreateSpb
|
|
(
|
|
HDC hdcCompat,
|
|
int cxWidth,
|
|
int cyHeight
|
|
)
|
|
{
|
|
HBITMAP hbmpRet;
|
|
|
|
DebugEntry(DrvCreateSpb);
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_CREATESPB], PATCH_DISABLE);
|
|
hbmpRet = (HBITMAP)CreateSpb(hdcCompat, cxWidth, cyHeight);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_CREATESPB], PATCH_ENABLE);
|
|
|
|
if (hbmpRet)
|
|
{
|
|
//
|
|
// Save in our "next SPB" bitmap list
|
|
//
|
|
g_ssiLastSpbBitmap = hbmpRet;
|
|
}
|
|
|
|
DebugExitDWORD(DrvCreateSpb, (DWORD)(UINT)hbmpRet);
|
|
return((UINT)hbmpRet);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DrvDeleteObject()
|
|
//
|
|
// This and DrvSysDeleteObject() watch for bitmaps being destroyed.
|
|
//
|
|
BOOL WINAPI DrvDeleteObject
|
|
(
|
|
HGDIOBJ hobj
|
|
)
|
|
{
|
|
BOOL fReturn;
|
|
int gdiType;
|
|
|
|
DebugEntry(DrvDeleteObject);
|
|
|
|
gdiType = IsGDIObject(hobj);
|
|
if (gdiType == GDIOBJ_BITMAP)
|
|
{
|
|
OE_SHM_START_WRITING;
|
|
|
|
//
|
|
// If SPB, toss it. Else if cached bitmap, kill cache entry.
|
|
//
|
|
if ((HBITMAP)hobj == g_ssiLastSpbBitmap)
|
|
{
|
|
g_ssiLastSpbBitmap = NULL;
|
|
}
|
|
else if (!SSIDiscardBits((HBITMAP)hobj))
|
|
{
|
|
}
|
|
|
|
OE_SHM_STOP_WRITING;
|
|
}
|
|
|
|
EnableFnPatch(&g_oeDDPatches[DDI_DELETEOBJECT], PATCH_DISABLE);
|
|
fReturn = DeleteObject(hobj);
|
|
EnableFnPatch(&g_oeDDPatches[DDI_DELETEOBJECT], PATCH_ENABLE);
|
|
|
|
DebugExitBOOL(DrvDeleteObject, fReturn);
|
|
return(fReturn);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// OE_RectIntersectsSDA()
|
|
//
|
|
// Used by SSI and BLT orders
|
|
//
|
|
BOOL OE_RectIntersectsSDA(LPRECT pRect)
|
|
{
|
|
RECT rectVD;
|
|
BOOL fIntersection = FALSE;
|
|
UINT i;
|
|
|
|
DebugEntry(OE_RectIntersectsSDA);
|
|
|
|
//
|
|
// Copy the supplied rectangle, converting to inclusive Virtual
|
|
// Desktop coords.
|
|
//
|
|
rectVD.left = pRect->left;
|
|
rectVD.top = pRect->top;
|
|
rectVD.right = pRect->right - 1;
|
|
rectVD.bottom = pRect->bottom - 1;
|
|
|
|
//
|
|
// Loop through each of the bounding rectangles checking for
|
|
// an intersection with the supplied rectangle.
|
|
//
|
|
for (i = 0; i <= BA_NUM_RECTS; i++)
|
|
{
|
|
if ( (g_baBounds[i].InUse) &&
|
|
(g_baBounds[i].Coord.left <= rectVD.right) &&
|
|
(g_baBounds[i].Coord.top <= rectVD.bottom) &&
|
|
(g_baBounds[i].Coord.right >= rectVD.left) &&
|
|
(g_baBounds[i].Coord.bottom >= rectVD.top) )
|
|
{
|
|
OTRACE(("Rect {%d, %d, %d, %d} intersects SDA {%d, %d, %d, %d}",
|
|
rectVD.left, rectVD.top, rectVD.right, rectVD.bottom,
|
|
g_baBounds[i].Coord.left, g_baBounds[i].Coord.top,
|
|
g_baBounds[i].Coord.right, g_baBounds[i].Coord.bottom));
|
|
fIntersection = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DebugExitBOOL(OE_RectIntersectsSDA, fIntersection);
|
|
return(fIntersection);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// MyStrcmp()
|
|
// Real strcmp() algorithm.
|
|
//
|
|
int MyStrcmp(LPCSTR lp1, LPCSTR lp2)
|
|
{
|
|
ASSERT(lp1);
|
|
ASSERT(lp2);
|
|
|
|
while (*lp1 == *lp2)
|
|
{
|
|
//
|
|
// The two strings are identical
|
|
//
|
|
if (!*lp1)
|
|
return(0);
|
|
|
|
lp1++;
|
|
lp2++;
|
|
}
|
|
|
|
//
|
|
// String1 is numerically > String2, or <
|
|
//
|
|
return((*lp1 > *lp2) ? 1 : -1);
|
|
}
|