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.
2884 lines
87 KiB
2884 lines
87 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998-1999 Microsoft Corporation
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Handle all the permutations of the creation and deletion of the
|
|
* GpGraphics class.
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 12/03/1998 andrewgo
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
#include "..\Render\HtTables.hpp"
|
|
|
|
#include "printer.hpp"
|
|
#include "winspool.h"
|
|
#include "winbase.h"
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Updates the draw bounds of the graphics. Resets the clipping.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] x, y, width, height - Specifies the client drawable boundaries
|
|
*
|
|
* History:
|
|
*
|
|
* 03/30/2000 agodfrey
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpGraphics::UpdateDrawBounds(
|
|
INT x,
|
|
INT y,
|
|
INT width,
|
|
INT height
|
|
)
|
|
{
|
|
DpContext *context = Context;
|
|
|
|
// Set up the surface bounds and the clip regions:
|
|
|
|
SurfaceBounds.X = x;
|
|
SurfaceBounds.Y = y;
|
|
SurfaceBounds.Width = width;
|
|
SurfaceBounds.Height = height;
|
|
|
|
WindowClip.Set(x, y, width, height);
|
|
context->VisibleClip.Set(x, y, width, height);
|
|
|
|
// ContainerClip always contains the clipping for the container,
|
|
// intersected with the WindowClip. Currently, the container is
|
|
// infinite, so just set it to the WindowClip.
|
|
context->ContainerClip.Set(x, y, width, height);
|
|
|
|
context->AppClip.SetInfinite();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Resets the graphics state to its defaults.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] x, y, width, height - Specifies the client drawable boundaries
|
|
*
|
|
* History:
|
|
*
|
|
* 12/04/1998 andrewgo
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpGraphics::ResetState(
|
|
INT x,
|
|
INT y,
|
|
INT width,
|
|
INT height
|
|
)
|
|
{
|
|
DpContext *context = Context;
|
|
|
|
context->CompositingMode = CompositingModeSourceOver;
|
|
context->CompositingQuality = CompositingQualityDefault;
|
|
context->AntiAliasMode = FALSE;
|
|
context->TextRenderHint = TextRenderingHintSystemDefault;
|
|
context->TextContrast = DEFAULT_TEXT_CONTRAST;
|
|
context->FilterType = InterpolationModeDefaultInternal;
|
|
context->PixelOffset = PixelOffsetModeDefault;
|
|
context->InverseOk = FALSE;
|
|
context->WorldToPage.Reset();
|
|
context->ContainerToDevice.Reset();
|
|
this->SetPageTransform(UnitDisplay, 1.0f); // updates the matrix
|
|
|
|
UpdateDrawBounds(x, y, width, height);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Get the drawing bounds of a DC. Only intended for use by
|
|
* GpGraphics::GpGraphics(HWND, HDC).
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] hdc - Specifies the DC
|
|
* [OUT] rect - The returned client rectangle.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status code
|
|
*
|
|
* Notes:
|
|
*
|
|
* See bug #93012. We used to just call GetClipBox, convert to device
|
|
* coordinates, then boost the rectangle by one pixel on each side to cover
|
|
* rounding error. But this was causing AV's - we really do need the exact
|
|
* client rectangle.
|
|
*
|
|
* But we need good perf in common cases. So we do a two-step process
|
|
* - check if the transform is such that there won't be rounding error
|
|
* (and simply use GetClipBox if so).
|
|
* Otherwise, save the DC, reset the transform, and then query.
|
|
*
|
|
* I tried an alternative - calling LPtoDP on 3 points to infer the transform
|
|
* (as we do in InheritAppClippingAndTransform).
|
|
* But because of rounding, and made worse by
|
|
* NT bug #133322 (in the old NT RAID), it's nearly impossible
|
|
* to infer the transform unambiguously. The particularly bad case is
|
|
* when the world-to-device transform is a shrink, but the scale factor
|
|
* is very close to 1. We'd decide it was a one-to-one transform, but it
|
|
* would be susceptible to bug #133322.
|
|
*
|
|
* So, to round off this novel: We're using a much simpler approach,
|
|
* which restricts the cases in which we can use the fast method, but
|
|
* should be ok.
|
|
*
|
|
* Notes:
|
|
*
|
|
* This should really be a member of DpContext (bug #98174).
|
|
*
|
|
* History:
|
|
*
|
|
* 03/28/2000 agodfrey
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#if 0 // not used
|
|
GpStatus
|
|
GpGraphics::GetDCDrawBounds(
|
|
HDC hdc,
|
|
RECT *rect
|
|
)
|
|
{
|
|
BOOL hackResetClipping = FALSE;
|
|
|
|
// Check if the transform is translation-only. If it is, we can avoid the
|
|
// expense of cleaning the DC. GetGraphicsMode and GetMapMode are both
|
|
// handled in user mode on NT.
|
|
|
|
if ( (GetGraphicsMode(hdc) != GM_COMPATIBLE)
|
|
|| (GetMapMode(hdc) != MM_TEXT))
|
|
{
|
|
// Clean the DC, to set the transform back to translation-only.
|
|
|
|
ASSERT(Context->SaveDc == 0);
|
|
|
|
Context->SaveDc = ::SaveDC(hdc);
|
|
if (!Context->SaveDc)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
// CleanTheHdc shouldn't be resetting the clipping, but it does,
|
|
// which messes up GetClipBox below.
|
|
// So until bug #99338 is resolved, we must work around it.
|
|
|
|
hackResetClipping = TRUE;
|
|
Context->CleanTheHdc(hdc, FALSE);
|
|
}
|
|
|
|
// The code above is necessary because GetClipBox returns
|
|
// logical coordinates, but we want device coordinates.
|
|
// By this point, we've made sure that the transform is translation-only.
|
|
|
|
if (GetClipBox(hdc, rect) == ERROR)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
// See bug #99338. We must reset the clipping, because that's what
|
|
// CleanTheHdc normally does, and apparently some of our code relies on it.
|
|
// If #99338 is resolved as suggested, this should go away.
|
|
|
|
if (hackResetClipping)
|
|
{
|
|
SelectClipRgn(hdc, NULL);
|
|
}
|
|
|
|
#if DBG
|
|
// Save the world-coordinate rectangle.
|
|
|
|
RECT checkRect = *rect;
|
|
#endif
|
|
|
|
// Convert to device coordinates.
|
|
if (!LPtoDP(hdc, reinterpret_cast<POINT *>(rect), 2))
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
// NT can sometimes return poorly-ordered rectangles,
|
|
// but I don't think this applies to translation-only tranforms.
|
|
|
|
ASSERT( (rect->left <= rect->right)
|
|
&& (rect->top <= rect->bottom));
|
|
|
|
// Verify that the transform was translation-only.
|
|
// Note that this sanity check could fail to catch some transforms
|
|
// which are 'almost' translation-only. But ask me if I care.
|
|
|
|
ASSERT( ( (rect->right - rect->left)
|
|
== (checkRect.right - checkRect.left))
|
|
&& ( (rect->bottom - rect->top)
|
|
== (checkRect.bottom - checkRect.top)));
|
|
|
|
return Ok;
|
|
}
|
|
#endif
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics class from a window handle.
|
|
*
|
|
* The advantage of this call over that of GetFromHdc is that
|
|
* it can avoid the (slow) process of cleaning the DC.
|
|
*
|
|
* NOTE: This does not provide BeginPaint/EndPaint functionality, so
|
|
* the app will still have to call BeginPaint/EndPaint in its
|
|
* WM_PAINT call.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] hwnd - Specifies the window
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NULL if failure (such as with an invalid hwnd).
|
|
*
|
|
* History:
|
|
*
|
|
* 12/04/1998 andrewgo
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics::GpGraphics(
|
|
HWND hwnd,
|
|
HDC hdc,
|
|
INT clientWidth,
|
|
INT clientHeight,
|
|
HdcIcmMode icmMode,
|
|
BOOL gdiLayered
|
|
) : BottomContext((hwnd != NULL) ||
|
|
(GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY))
|
|
{
|
|
ASSERT((hdc != NULL) || (hwnd != NULL));
|
|
|
|
//User doesn't have any protection against negative client areas so we
|
|
//should consider them to be valid although empty.
|
|
clientWidth = max(clientWidth, 0);
|
|
clientHeight = max(clientHeight, 0);
|
|
|
|
SetValid(TRUE);
|
|
|
|
Context = &BottomContext;
|
|
|
|
Type = GraphicsScreen;
|
|
Metafile = NULL;
|
|
DownLevel = FALSE;
|
|
Printer = FALSE;
|
|
LockedByGetDC = 0;
|
|
Driver = Globals::DesktopDriver;
|
|
Surface = Globals::DesktopSurface;
|
|
Device = Globals::DesktopDevice;
|
|
GdipBitmap = NULL;
|
|
CreatedDevice = FALSE;
|
|
|
|
// We don't do GetDC(hwnd) here and store that here because,
|
|
// among other things, we don't want to hit the cached DC
|
|
// limit on Win9x:
|
|
|
|
Context->Hdc = hdc;
|
|
Context->Hwnd = hwnd;
|
|
Context->IcmMode = icmMode;
|
|
Context->GdiLayered = gdiLayered;
|
|
|
|
HDC tempHdc = (hdc != NULL) ? hdc : Globals::DesktopDevice->DeviceHdc;
|
|
|
|
if (GetDeviceCaps(tempHdc, BITSPIXEL) <= 8)
|
|
{
|
|
Context->PaletteMap = new EpPaletteMap(tempHdc);
|
|
|
|
if (!Context->PaletteMap ||
|
|
!Context->PaletteMap->IsValid())
|
|
{
|
|
WARNING(("Unable to compute palette translation vector"));
|
|
SetValid(FALSE);
|
|
return;
|
|
}
|
|
|
|
Context->PaletteMap->SetUniqueness(Globals::PaletteChangeCount);
|
|
}
|
|
|
|
ResetState(0, 0, clientWidth, clientHeight);
|
|
|
|
// Now inherit state from the HDC:
|
|
|
|
if (hwnd == NULL)
|
|
{
|
|
// In addition to extracting the HDC's transform state, this
|
|
// will also extract app-specified clipping and combine it
|
|
// with our other clipping state:
|
|
|
|
if (IsValid())
|
|
{
|
|
SetValid(InheritAppClippingAndTransform(hdc) == Ok);
|
|
}
|
|
|
|
// Check the ICM Mode on the hdc - The ICM state in the HDC
|
|
// passed in should override the flag setting.
|
|
// IcmModeOn -> screen rendering will avoid using the
|
|
// DCI codepath and instead render using GDI with the ICM enabled
|
|
// HDC.
|
|
|
|
if(::SetICMMode(hdc, ICM_QUERY)==ICM_ON)
|
|
{
|
|
Context->IcmMode = IcmModeOn;
|
|
}
|
|
else
|
|
{
|
|
// If the ICM mode is off or we failed somehow to query
|
|
// the ICM mode, then set it to OFF.
|
|
|
|
Context->IcmMode = IcmModeOff;
|
|
}
|
|
}
|
|
else // non-NULL hwnd
|
|
{
|
|
// Since the window could have CS_OWNDC style, we still have to
|
|
// inherit from it.
|
|
HDC hdc = ::GetDC(hwnd);
|
|
|
|
if (hdc != NULL)
|
|
{
|
|
if (IsValid())
|
|
{
|
|
SetValid(InheritAppClippingAndTransform(hdc) == Ok);
|
|
}
|
|
|
|
::ReleaseDC(hwnd, hdc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics class from a DpBitmap.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] surface - Specifies the DpBitmap
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NULL if failure
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics::GpGraphics(DpBitmap * surface)
|
|
: BottomContext(surface->IsDisplay)
|
|
{
|
|
Surface = surface;
|
|
BottomContext.ContainerDpiX = surface->DpiX;
|
|
BottomContext.ContainerDpiY = surface->DpiY;
|
|
Context = &BottomContext;
|
|
Metafile = NULL;
|
|
DownLevel = FALSE;
|
|
Printer = FALSE;
|
|
PrintInit = NULL;
|
|
LockedByGetDC = 0;
|
|
CreatedDevice = FALSE;
|
|
GdipBitmap = NULL;
|
|
Device = Globals::DesktopDevice;
|
|
|
|
// Fail the creation of the destination if EpAlphaBlender
|
|
// cannot convert to the DpBitmap pixel format.
|
|
// The only reason to create a graphics around a bitmap is to be
|
|
// able to draw onto it. If we can't convert the format to it,
|
|
// we can't draw on it.
|
|
|
|
if( (surface->Type != DpBitmap::GPBITMAP) ||
|
|
(EpAlphaBlender::IsSupportedPixelFormat(surface->PixelFormat) &&
|
|
surface->PixelFormat != PixelFormat8bppIndexed))
|
|
{
|
|
SetValid(TRUE);
|
|
}
|
|
else
|
|
{
|
|
SetValid(FALSE);
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Check whether the HWND has windows layering set.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] hwnd - Specifies the HWND
|
|
*
|
|
* [OUT] isLayeredWindow - Points to BOOL that returns layering property
|
|
*
|
|
* Return Value:
|
|
*
|
|
* FALSE if failure.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL
|
|
CheckWindowsLayering(
|
|
HWND hwnd,
|
|
BOOL *isLayered
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
// Assume no layering.
|
|
|
|
*isLayered = FALSE;
|
|
|
|
// Layering is only supported on NT5 or better.
|
|
|
|
if ((Globals::IsNt) && (Globals::OsVer.dwMajorVersion >= 5)
|
|
&& (Globals::GetWindowInfoFunction))
|
|
{
|
|
WINDOWINFO wndInfo;
|
|
|
|
// Initialize the structure with the appropriate size.
|
|
|
|
GpMemset(&wndInfo, 0, sizeof(WINDOWINFO));
|
|
wndInfo.cbSize = sizeof(WINDOWINFO);
|
|
|
|
// NTRAID#NTBUG9-385929-2001/05/05-asecchia
|
|
// See JasonSch's comments in the bug report.
|
|
// Perf [agodfrey]: JStall pointed out that GetWindowInfo is very
|
|
// slow (he quoted 2,700,000 clocks). Much better would be
|
|
// GetWindowLong(hwnd, GWL_EXSTYLE).
|
|
|
|
if (Globals::GetWindowInfoFunction(hwnd, &wndInfo))
|
|
{
|
|
*isLayered = ((wndInfo.dwExStyle & WS_EX_LAYERED) != 0);
|
|
|
|
// An app using layered windows might only have the property set
|
|
// on the topmost or root window. So if we didn't find the
|
|
// layered property on the window itself, need to check the root
|
|
// window.
|
|
|
|
if ((!*isLayered) && (Globals::GetAncestorFunction))
|
|
{
|
|
HWND hwndRoot = Globals::GetAncestorFunction(hwnd, GA_ROOT);
|
|
|
|
// It's OK for GetAncestor to fail, which indicates that
|
|
// hwnd is already the top level window. If it succeeds,
|
|
// then hwnd is a child window and we need to check the
|
|
// root for layering.
|
|
|
|
if (hwndRoot)
|
|
{
|
|
// Perf [agodfrey]: Ditto here - GetWindowLong is better.
|
|
|
|
if (Globals::GetWindowInfoFunction(hwndRoot, &wndInfo))
|
|
{
|
|
*isLayered = ((wndInfo.dwExStyle & WS_EX_LAYERED) != 0);
|
|
}
|
|
else
|
|
{
|
|
WARNING(("GetWindowInfo failed"));
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(("GetWindowInfo failed"));
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics for a window.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] hwnd - Specifies the window
|
|
*
|
|
* [IN] icmMode - Specifies the GDI ICM mode associated with this
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NULL if failure.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromHwnd(
|
|
HWND hwnd,
|
|
HdcIcmMode icmMode
|
|
)
|
|
{
|
|
// If hwnd is NULL, caller really meant that desktop
|
|
// window should be used (Windows convention treats NULL hwnd
|
|
// as a reference to desktop window).
|
|
|
|
if (hwnd == NULL)
|
|
{
|
|
hwnd = GetDesktopWindow();
|
|
ASSERT(hwnd != NULL);
|
|
}
|
|
|
|
RECT rect;
|
|
|
|
// Check if hwnd has layering enabled. Need to let GpGraphics know
|
|
// about it. Only on NT5 or better. Also note that GetWindowInfo
|
|
// is only on NT4SP3 (or later) or Win98 (or later).
|
|
|
|
BOOL isLayeredWindow;
|
|
|
|
if (!CheckWindowsLayering(hwnd, &isLayeredWindow))
|
|
{
|
|
WARNING(("CheckWindowsLayering failed"));
|
|
return NULL;
|
|
}
|
|
|
|
// GetClientRect is nice and fast (entirely user-mode on NT).
|
|
|
|
if (::GetClientRect(hwnd, &rect))
|
|
{
|
|
ASSERT((rect.top == 0) && (rect.left == 0));
|
|
|
|
GpGraphics *g = new GpGraphics(hwnd, NULL, rect.right, rect.bottom,
|
|
icmMode, isLayeredWindow);
|
|
CheckValid(g);
|
|
return g;
|
|
}
|
|
else
|
|
{
|
|
WARNING(("GetClientRect failed"));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics for a screen DC.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] hdc - Specifies the DC
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NULL if failure.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromGdiScreenDC(
|
|
HDC hdc
|
|
)
|
|
{
|
|
// If hdc is NULL, caller really meant that desktop
|
|
// window should be used (Windows convention treats NULL hwnd
|
|
// as a reference to desktop window).
|
|
|
|
if (hdc == NULL)
|
|
{
|
|
return GpGraphics::GetFromHwnd(NULL);
|
|
}
|
|
|
|
ASSERT(GetDCType(hdc) == OBJ_DC);
|
|
|
|
HWND hwnd = WindowFromDC(hdc);
|
|
|
|
if (hwnd != NULL)
|
|
{
|
|
RECT windowRect;
|
|
POINT dcOrg;
|
|
|
|
// Check if hwnd has layering enabled. Need to let GpGraphics know
|
|
// about it. Only on NT5 or better. Also note that GetWindowInfo
|
|
// is only on NT4SP3 (or later) or Win98 (or later).
|
|
|
|
BOOL isLayeredWindow;
|
|
|
|
if (!CheckWindowsLayering(hwnd, &isLayeredWindow))
|
|
{
|
|
WARNING(("CheckWindowsLayering failed"));
|
|
return NULL;
|
|
}
|
|
|
|
// If the user did a GetWindowFromDC call, then they want to be
|
|
// able to draw to the entire window, not just to the client area.
|
|
// In that case we use the WindowRect for the surface size, instead
|
|
// of using the ClientRect. We determine this by seeing where the
|
|
// DC origin is (the window rect or the client rect).
|
|
|
|
if (::GetWindowRect(hwnd, &windowRect))
|
|
{
|
|
if (::GetDCOrgEx(hdc, &dcOrg))
|
|
{
|
|
if ((dcOrg.x == windowRect.left) && (dcOrg.y == windowRect.top))
|
|
{
|
|
windowRect.right -= windowRect.left;
|
|
windowRect.bottom -= windowRect.top;
|
|
|
|
GpGraphics *g = new GpGraphics(NULL,
|
|
hdc,
|
|
windowRect.right,
|
|
windowRect.bottom,
|
|
IcmModeOff,
|
|
isLayeredWindow);
|
|
|
|
CheckValid(g);
|
|
return g;
|
|
}
|
|
|
|
RECT clientRect;
|
|
|
|
// GetClientRect is nice and fast (entirely user-mode on NT).
|
|
if (::GetClientRect(hwnd, &clientRect))
|
|
{
|
|
ASSERT((clientRect.top == 0) && (clientRect.left == 0));
|
|
|
|
GpGraphics *g = new GpGraphics(NULL,
|
|
hdc,
|
|
clientRect.right,
|
|
clientRect.bottom,
|
|
IcmModeOff,
|
|
isLayeredWindow);
|
|
|
|
CheckValid(g);
|
|
return g;
|
|
}
|
|
else
|
|
{
|
|
WARNING(("GetClientRect failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(("GetDCOrgEx failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING(("GetWindowRect failed"));
|
|
}
|
|
}
|
|
else // WindowFromDC failed
|
|
{
|
|
// The client must have used CreateDC("DISPLAY") to get this hdc,
|
|
// so we'll use the full bounds of the screen to create the graphics.
|
|
|
|
INT screenWidth;
|
|
INT screenHeight;
|
|
|
|
screenWidth = ::GetDeviceCaps(hdc, HORZRES);
|
|
screenHeight = ::GetDeviceCaps(hdc, VERTRES);
|
|
|
|
if ((screenWidth > 0) && (screenHeight > 0))
|
|
{
|
|
GpGraphics *g = new GpGraphics(NULL, hdc, screenWidth, screenHeight);
|
|
CheckValid(g);
|
|
return g;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Set the Graphics container transform to copy the transform set in
|
|
* the DC.
|
|
*
|
|
* NOTE: This function will be called a lot, and is therefore rather
|
|
* performance critical. Do not add gratuitous GDI or GDI+
|
|
* calls!
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] hdc - Specifies the DC to be copied
|
|
*
|
|
* Notes:
|
|
*
|
|
* This should really be a member of DpContext (bug #98174).
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Ok if successful
|
|
*
|
|
* History:
|
|
*
|
|
* 12/04/1998 andrewgo
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpGraphics::InheritAppClippingAndTransform(
|
|
HDC hdc
|
|
)
|
|
{
|
|
POINT points[3];
|
|
GpPointF destPoints[3];
|
|
GpRectF srcRect;
|
|
GpRectF destRect;
|
|
GpStatus infer = GenericError;
|
|
GpStatus status;
|
|
BYTE stackBuffer[1024];
|
|
|
|
// It would take a lot of time to call all the Win32 APIs to query
|
|
// the transform: we would minimally have to call GetMapMode,
|
|
// GetWindowOrgEx, and GetViewportOrgEx; and maximally also have to
|
|
// call GetWorldTransform, GetViewportExtEx, and GetWindowExtEx.
|
|
//
|
|
// We cheat a little by making a single call to LPtoDP with a
|
|
// parallelogram, and then inferring the result. Note that we do
|
|
// run the risk of some error, and on Win9x of overflow, since Win9x
|
|
// only supports 16-bit coordinates. To counteract this, we try to
|
|
// choose large values that won't overflow.
|
|
|
|
// There is a common scenario when LPtoDP will overflow returning
|
|
// bad saturated values. In printing to high DPI devices to avoid
|
|
// overflow on Win9x, apps will use a large translate in the
|
|
// window org to reposition the graphics. In such cases, we do
|
|
// the expensive work of determining the real WorldToDevice.
|
|
if (!Globals::IsNt && Context->ContainerDpiX > 600.0f)
|
|
{
|
|
INT mapMode = GetMapMode(hdc);
|
|
|
|
if (mapMode == MM_ANISOTROPIC ||
|
|
mapMode == MM_ISOTROPIC)
|
|
{
|
|
POINT viewOrg, windOrg;
|
|
GetViewportOrgEx(hdc, &viewOrg);
|
|
GetWindowOrgEx(hdc, &windOrg);
|
|
|
|
SIZE viewExt, windExt;
|
|
GetViewportExtEx(hdc, &viewExt);
|
|
GetWindowExtEx(hdc, &windExt);
|
|
|
|
GpRectF windRect(TOREAL(windOrg.x), TOREAL(windOrg.y),
|
|
TOREAL(windExt.cx), TOREAL(windExt.cy));
|
|
GpRectF viewRect(TOREAL(viewOrg.x), TOREAL(viewOrg.y),
|
|
TOREAL(viewExt.cx), TOREAL(viewExt.cy));
|
|
|
|
infer = Context->ContainerToDevice.InferAffineMatrix(viewRect,
|
|
windRect);
|
|
}
|
|
}
|
|
|
|
if (infer != Ok)
|
|
{
|
|
points[0].x = 0;
|
|
points[0].y = 0;
|
|
points[1].x = 8192;
|
|
points[1].y = 0;
|
|
points[2].x = 0;
|
|
points[2].y = 8192;
|
|
|
|
if (!LPtoDP(hdc, points, 3))
|
|
return(GenericError);
|
|
|
|
srcRect.X = TOREAL(0.0);
|
|
srcRect.Y = TOREAL(0.0);
|
|
srcRect.Width = TOREAL(8192.0);
|
|
srcRect.Height = TOREAL(8192.0);
|
|
|
|
if ((points[0].x == points[2].x) && (points[0].y == points[1].y))
|
|
{
|
|
// Win9x doesn't support rotation, and even on NT it will be
|
|
// pretty rare. Having a special-case like this for scaling
|
|
// saves us some work in 'InferAffineMatrix':
|
|
|
|
destRect.X = LTOF(points[0].x);
|
|
destRect.Y = LTOF(points[0].y);
|
|
destRect.Width = LTOF(points[1].x - points[0].x);
|
|
destRect.Height = LTOF(points[2].y - points[0].y);
|
|
|
|
infer = Context->ContainerToDevice.InferAffineMatrix(destRect,
|
|
srcRect);
|
|
}
|
|
else
|
|
{
|
|
destPoints[0].X = LTOF(points[0].x);
|
|
destPoints[0].Y = LTOF(points[0].y);
|
|
destPoints[1].X = LTOF(points[1].x);
|
|
destPoints[1].Y = LTOF(points[1].y);
|
|
destPoints[2].X = LTOF(points[2].x);
|
|
destPoints[2].Y = LTOF(points[2].y);
|
|
|
|
infer = Context->ContainerToDevice.InferAffineMatrix(destPoints,
|
|
srcRect);
|
|
}
|
|
}
|
|
|
|
if (infer != Ok)
|
|
return(infer);
|
|
|
|
Context->UpdateWorldToDeviceMatrix();
|
|
|
|
// Quickly get a GDI region object:
|
|
|
|
HRGN regionHandle = GetCachedGdiRegion();
|
|
if (regionHandle == NULL)
|
|
return(OutOfMemory);
|
|
|
|
// Verify that our cache is working properly, and we have a valid region:
|
|
|
|
ASSERT(GetObjectTypeInternal(regionHandle) == OBJ_REGION);
|
|
|
|
// No early-outs from here-in, because we have to cleanup:
|
|
|
|
status = Ok;
|
|
|
|
// Query the application clip region, if there is one. The value of '1'
|
|
// as a parameter is a magic value used by the metafile code on both
|
|
// Win9x and NT to query the application clipping. If a value of zero
|
|
// is returned, there is no application-set clipping.
|
|
//
|
|
// Note that if we had passed in SYSRGN (a value of '4') instead of '1',
|
|
// the result does NOT include the application level clipping. (In other
|
|
// words, SYSRGN is not equivalent to the Rao region, which is why we have
|
|
// to explicitly query the application clipping here.)
|
|
|
|
INT getResult = GetRandomRgn(hdc, regionHandle, 1);
|
|
if (getResult == TRUE)
|
|
{
|
|
// If our stack buffer is big enough, get the clipping contents
|
|
// in one gulp:
|
|
|
|
INT newSize = GetRegionData(regionHandle,
|
|
sizeof(stackBuffer),
|
|
(RGNDATA*) &stackBuffer[0]);
|
|
RGNDATA *regionBuffer = (RGNDATA*) &stackBuffer[0];
|
|
|
|
// The spec says that GetRegionData returns '1' in the event of
|
|
// success, but NT returns the actual number of bytes written if
|
|
// successful, and returns '0' if the buffer wasn't large enough:
|
|
|
|
if ((newSize < 1) || (newSize > sizeof(stackBuffer)))
|
|
{
|
|
// Our stack buffer wasn't big enough. Figure out the required
|
|
// size:
|
|
|
|
newSize = GetRegionData(regionHandle, 0, NULL);
|
|
if (newSize > 1)
|
|
{
|
|
regionBuffer = (RGNDATA*) GpMalloc(newSize);
|
|
if (regionBuffer == NULL)
|
|
return OutOfMemory;
|
|
|
|
// Initialize to a decent result in the unlikely event of
|
|
// failure of GetRegionData:
|
|
|
|
regionBuffer->rdh.nCount = 0;
|
|
|
|
GetRegionData(regionHandle, newSize, regionBuffer);
|
|
}
|
|
}
|
|
|
|
// Set our GDI+ container clipping to be the same thing as the
|
|
// GDI application clipping:
|
|
|
|
status = Context->ContainerClip.Set((RECT*) ®ionBuffer->Buffer[0],
|
|
regionBuffer->rdh.nCount);
|
|
|
|
if (status == Ok)
|
|
{
|
|
// the ContainerClip must always be intersected with the WindowClip
|
|
status = Context->ContainerClip.And(&WindowClip);
|
|
}
|
|
|
|
if (status != Ok)
|
|
{
|
|
// use the best fall-back solution we can
|
|
|
|
// guaranteed to succeed
|
|
Context->ContainerClip.Set(&WindowClip);
|
|
}
|
|
|
|
// Now calculate the combined result:
|
|
status = this->AndVisibleClip();
|
|
|
|
// Free the temporary buffer if one was allocated:
|
|
|
|
if (regionBuffer != (RGNDATA*) &stackBuffer[0])
|
|
GpFree(regionBuffer);
|
|
}
|
|
|
|
ReleaseCachedGdiRegion(regionHandle);
|
|
return(status);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics class from a bitmap DC.
|
|
*
|
|
* History:
|
|
*
|
|
* 12/06/1998 andrewgo
|
|
* Created it.
|
|
*
|
|
* 11/21/2000 minliu
|
|
* Change the way GDI+ using the palette inside the DIBSection
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromGdiBitmap(
|
|
HDC hdc
|
|
)
|
|
{
|
|
ASSERT((hdc != NULL) && (GetDCType(hdc) == OBJ_MEMDC));
|
|
|
|
HBITMAP hbitmap = (HBITMAP) GetCurrentObject(hdc, OBJ_BITMAP);
|
|
if (hbitmap)
|
|
{
|
|
DpBitmap *bitmap = new DpBitmap(hdc); // initializes Dpi
|
|
if (CheckValid(bitmap))
|
|
{
|
|
INT bitmapWidth;
|
|
INT bitmapHeight;
|
|
EpPaletteMap* pPaletteMap = NULL;
|
|
DIBSECTION dibInfo;
|
|
INT infoSize = GetObjectA(hbitmap, sizeof(dibInfo),
|
|
&dibInfo);
|
|
BOOL initialized = FALSE;
|
|
BOOL isHalftoneDIB = FALSE;
|
|
DpDriver* driver = NULL;
|
|
ColorPalette* pPalette = NULL;
|
|
|
|
// WinNT/Win95 differences in GetObject:
|
|
//
|
|
// WinNT always returns the number of bytes filled, either
|
|
// sizeof(BITMAP) or sizeof(DIBSECTION).
|
|
//
|
|
// Win95 always returns the original requested size (filling the
|
|
// remainder with NULLs). So if it is a DIBSECTION, we expect
|
|
// dibInfo.dsBmih.biSize != 0; otherwise it is a BITMAP.
|
|
|
|
if ( (infoSize == sizeof(DIBSECTION) )
|
|
&&(Globals::IsNt || dibInfo.dsBmih.biSize != 0) )
|
|
{
|
|
// If this is an 8 bpp DIB, get its color palette and make a
|
|
// matching palette map from our halftone palette.
|
|
|
|
if ( dibInfo.dsBmih.biBitCount == 8 )
|
|
{
|
|
// Create a new EpPaletteMap object.
|
|
// Note: If the colorTable is exactly the same as our
|
|
// GDI+ halftone palette, we will have a 1 to 1 color
|
|
// translation table in the EpPaletteMap object. If the
|
|
// color palette doesn't match exactly with our GDI+
|
|
// halftone palette and also within a certain
|
|
// mismatching range, we will have a translation table
|
|
// in EpPaletteMap object.
|
|
// Also, EpPaletteMap object will set up a IsVGAOnly()
|
|
// to tell us if GDI+ can do the halftone dithering or
|
|
// not (if IsVGAOnly() returns FALSE, it means GDI+ can
|
|
// do it
|
|
|
|
// NOTE: EpPaletteMap may allocate storage for pPalette
|
|
// which must be freed with GpFree.
|
|
|
|
pPaletteMap = new EpPaletteMap(hdc, &pPalette, TRUE);
|
|
|
|
if ( pPaletteMap == NULL )
|
|
{
|
|
WARNING(("FromGdiBmp()-new EpPaletteMap failed"));
|
|
}
|
|
else if ( (pPaletteMap->IsValid() == TRUE)
|
|
&&(pPaletteMap->IsVGAOnly() == FALSE) )
|
|
{
|
|
ASSERT(pPalette != NULL);
|
|
|
|
// GDI+ can do the halftone dithering
|
|
|
|
isHalftoneDIB = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// The supplied palette has insufficient
|
|
// matching colors for our halftone dithering,
|
|
// but we can still do VGA dithering. However,
|
|
// we'll use the GDI bitmap path instead, to
|
|
// be safe, since this is what we were doing
|
|
// before.
|
|
|
|
if (pPaletteMap->IsValid())
|
|
{
|
|
ASSERT(pPalette != NULL);
|
|
|
|
GpFree(pPalette);
|
|
pPalette = NULL;
|
|
}
|
|
|
|
delete pPaletteMap;
|
|
pPaletteMap = NULL;
|
|
}
|
|
}// if ( dibInfo.dsBmih.biBitCount == 8 )
|
|
|
|
// Up to this point, we will either have isHalftoneDIB = TRUE,
|
|
// which means GDI+ can do the dithering or FALSE otherwise.
|
|
|
|
if ((dibInfo.dsBmih.biBitCount > 8) || (isHalftoneDIB == TRUE) )
|
|
{
|
|
initialized = bitmap->InitializeForDibsection(
|
|
hdc,
|
|
hbitmap,
|
|
Globals::DesktopDevice,
|
|
&dibInfo,
|
|
&bitmapWidth,
|
|
&bitmapHeight,
|
|
&driver
|
|
);
|
|
}
|
|
}// if it is a DIBSection
|
|
|
|
if ( initialized == FALSE )
|
|
{
|
|
// Use GDI code path
|
|
|
|
bitmapWidth = dibInfo.dsBm.bmWidth;
|
|
bitmapHeight = dibInfo.dsBm.bmHeight;
|
|
|
|
bitmap->InitializeForGdiBitmap(Globals::DesktopDevice,
|
|
bitmapWidth,
|
|
bitmapHeight);
|
|
|
|
driver = Globals::GdiDriver;
|
|
}
|
|
|
|
GpGraphics *g = new GpGraphics(bitmap);
|
|
|
|
if (g)
|
|
{
|
|
// NTRAID#NTBUG9-370409-2001/04/17-asecchia
|
|
// This is error-prone code. The GpGraphics and the DpContext
|
|
// objects should properly encapsulate their own construction.
|
|
|
|
g->Type = GraphicsBitmap;
|
|
g->Driver = driver;
|
|
g->Context->Hdc = hdc;
|
|
g->Context->PaletteMap = NULL;
|
|
g->Context->Palette = NULL;
|
|
|
|
g->ResetState(0, 0, bitmapWidth, bitmapHeight);
|
|
|
|
if (g->InheritAppClippingAndTransform(hdc) == Ok)
|
|
{
|
|
// If this is our special DIB, set the original palette in
|
|
// the Context so that later on when we doing alpha blend
|
|
// etc., we can use it to read pixel data from the
|
|
// DIBSection correctly
|
|
|
|
if ( isHalftoneDIB == TRUE )
|
|
{
|
|
g->Context->Palette = pPalette;
|
|
g->Context->PaletteMap = pPaletteMap;
|
|
|
|
return(g);
|
|
}
|
|
else if (GetDeviceCaps(hdc, BITSPIXEL) <= 8)
|
|
{
|
|
ASSERT(pPaletteMap == NULL);
|
|
|
|
pPaletteMap = new EpPaletteMap(hdc);
|
|
|
|
if ( NULL != pPaletteMap )
|
|
{
|
|
|
|
pPaletteMap->SetUniqueness(
|
|
Globals::PaletteChangeCount
|
|
);
|
|
|
|
if ( pPaletteMap->IsValid() )
|
|
{
|
|
// Now that we know that the pPaletteMap is
|
|
// valid and we're returning a valid GpGraphics
|
|
// we can give up ownership of the pPaletteMap
|
|
// to the GpGraphics and return without
|
|
// deleting it.
|
|
|
|
g->Context->PaletteMap = pPaletteMap;
|
|
return(g);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Higher than 8 bpp, this graphics object is fine
|
|
|
|
return(g);
|
|
}
|
|
}// if (g->InheritAppClippingAndTransform(hdc) == Ok)
|
|
|
|
delete g;
|
|
}// if (g)
|
|
else
|
|
{
|
|
delete bitmap;
|
|
}
|
|
|
|
// We fall into here only we failed to create the Graphics object
|
|
|
|
if ( NULL != pPaletteMap )
|
|
{
|
|
delete pPaletteMap;
|
|
}
|
|
|
|
if ( NULL != pPalette )
|
|
{
|
|
GpFree(pPalette);
|
|
}
|
|
}// if (CheckValid(bitmap))
|
|
}// if ( hbitmap )
|
|
else
|
|
{
|
|
RIP(("GetCurrentObject failed"));
|
|
}
|
|
|
|
return(NULL);
|
|
}// GetFromGdiBitmap()
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics class from a GpBitmap.
|
|
*
|
|
* History:
|
|
*
|
|
* 09/22/1999 gilmanw
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromGdipBitmap(
|
|
GpBitmap * bitmap,
|
|
ImageInfo * imageInfo,
|
|
EpScanBitmap * scanBitmap,
|
|
BOOL isDisplay
|
|
)
|
|
{
|
|
DpBitmap *surface = new DpBitmap();
|
|
|
|
if (CheckValid(surface))
|
|
{
|
|
// This call initializes the DPI and IsDisplay members
|
|
surface->InitializeForGdipBitmap(imageInfo->Width, imageInfo->Height, imageInfo, scanBitmap, isDisplay);
|
|
GpGraphics *g = new GpGraphics(surface);
|
|
if (g)
|
|
{
|
|
g->Type = GraphicsBitmap;
|
|
g->Driver = Globals::EngineDriver;
|
|
g->Context->Hdc = NULL;
|
|
g->GdipBitmap = bitmap;
|
|
|
|
g->ResetState(0, 0, imageInfo->Width, imageInfo->Height);
|
|
|
|
return g;
|
|
}
|
|
else
|
|
{
|
|
delete surface;
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics class from a direct draw surface.
|
|
*
|
|
* History:
|
|
*
|
|
* 10/06/1999 bhouse
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromDirectDrawSurface(
|
|
IDirectDrawSurface7 * surface
|
|
)
|
|
{
|
|
INT bitmapWidth;
|
|
INT bitmapHeight;
|
|
GpGraphics *g;
|
|
DpDriver *driver;
|
|
|
|
DpBitmap *bitmap = new DpBitmap();
|
|
|
|
if (CheckValid(bitmap))
|
|
{
|
|
// Leave bitmap->IsDisplay and Dpi params at their default values.
|
|
if( bitmap->InitializeForD3D(surface,
|
|
&bitmapWidth,
|
|
&bitmapHeight,
|
|
&driver))
|
|
{
|
|
GpGraphics *g = new GpGraphics(bitmap);
|
|
|
|
if (g)
|
|
{
|
|
g->Type = GraphicsBitmap;
|
|
g->Driver = driver;
|
|
g->Context->Hdc = NULL;
|
|
|
|
g->ResetState(0, 0, bitmapWidth, bitmapHeight);
|
|
|
|
return(g);
|
|
}
|
|
else
|
|
{
|
|
delete bitmap;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* This should only be called by the GetFromHdc() for a printer DC.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* History:
|
|
* 6/1/1999 ericvan Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpGraphics::StartPrinterEMF()
|
|
{
|
|
IStream *stream = NULL;
|
|
INT result;
|
|
|
|
// Send escape to printer to determine if this supports the UNIDRV
|
|
// escape codes.
|
|
|
|
GDIPPRINTINIT printInit;
|
|
printInit.dwSize = sizeof(GDIPPRINTINIT);
|
|
|
|
// !! Query whether escape is supported first.
|
|
|
|
result = ExtEscape(Context->Hdc,
|
|
GDIPLUS_UNI_INIT,
|
|
0,
|
|
NULL,
|
|
sizeof(GDIPPRINTINIT),
|
|
(LPSTR)&printInit);
|
|
|
|
if (result<=0)
|
|
return NotImplemented;
|
|
|
|
|
|
// save printer data in structure
|
|
|
|
PrintInit = new GDIPPRINTINIT;
|
|
|
|
if(!PrintInit)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
memcpy((LPVOID)PrintInit, (LPVOID)&printInit, sizeof(GDIPPRINTINIT));
|
|
|
|
PrinterEMF = GlobalAlloc(GMEM_MOVEABLE, 1);
|
|
|
|
if (!PrinterEMF)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
if (CreateStreamOnHGlobal(PrinterEMF, FALSE, &stream) != S_OK)
|
|
{
|
|
GlobalFree(PrinterEMF);
|
|
PrinterEMF = NULL;
|
|
return Win32Error;
|
|
}
|
|
|
|
FPUStateSaver fpuState;
|
|
|
|
PrinterMetafile = new GpMetafile(stream, Context->Hdc, EmfTypeEmfPlusOnly);
|
|
|
|
stream->Release();
|
|
|
|
if (!PrinterMetafile)
|
|
{
|
|
GlobalFree(PrinterEMF);
|
|
PrinterEMF = NULL;
|
|
return OutOfMemory;
|
|
}
|
|
|
|
PrinterGraphics = PrinterMetafile->GetGraphicsContext();
|
|
|
|
Metafile = PrinterGraphics->Metafile;
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* History:
|
|
* 6/1/1999 ericvan Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpGraphics::EndPrinterEMF()
|
|
{
|
|
LPVOID emfBlock;
|
|
INT result = -1;
|
|
GpStatus status;
|
|
|
|
if (PrinterGraphics)
|
|
{
|
|
// end recording to metafile graphics context
|
|
delete PrinterGraphics;
|
|
PrinterGraphics = NULL;
|
|
}
|
|
|
|
// Disposing of metafile also Release() stream interface.
|
|
if (PrinterMetafile)
|
|
{
|
|
PrinterMetafile->Dispose();
|
|
PrinterMetafile = NULL;
|
|
Metafile = NULL;
|
|
}
|
|
|
|
if (PrinterEMF)
|
|
{
|
|
emfBlock = GlobalLock(PrinterEMF);
|
|
|
|
if (emfBlock)
|
|
result = ExtEscape(Context->Hdc,
|
|
GDIPLUS_UNI_ESCAPE,
|
|
// This is a downcast on IA64, but I don't believe
|
|
// PrinterEMF will be bigger than MAXINT
|
|
(ULONG)GlobalSize(PrinterEMF),
|
|
(LPCSTR)emfBlock,
|
|
sizeof(GpStatus),
|
|
(LPSTR)&status);
|
|
|
|
GlobalUnlock(PrinterEMF);
|
|
GlobalFree(PrinterEMF);
|
|
PrinterEMF = NULL;
|
|
|
|
if (result>0)
|
|
return status;
|
|
else
|
|
return Win32Error;
|
|
|
|
}
|
|
else
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Uses HDC and HANDLE for printer to determine the postscript level. The
|
|
* caller must ensure this is a PS HDC so we don't waste our time.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* HDC - handle to device context for printer
|
|
* HANDLE - handle to printer device (may be NULL)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Postscript Level - (-1) if not found, must provide downlevel support
|
|
*
|
|
* History:
|
|
* 10/26/1999 ericvan Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
GpGraphics::GetPostscriptLevel(HDC hDC, HANDLE hPrinter)
|
|
{
|
|
// Place this code under the load library critical section. We make
|
|
// extensive use of Globals::variables and need protection.
|
|
LoadLibraryCriticalSection llcs;
|
|
|
|
INT feature = FEATURESETTING_PSLEVEL;
|
|
|
|
INT level;
|
|
|
|
if ((Globals::hCachedPrinter != 0) &&
|
|
(Globals::hCachedPrinter == hPrinter))
|
|
{
|
|
return Globals::CachedPSLevel;
|
|
}
|
|
|
|
// !! Re-examine this, Nolan said he would add this to the HP ps driver
|
|
// so we may have this working on Win9x
|
|
|
|
if (Globals::IsNt && Globals::OsVer.dwMajorVersion >= 5)
|
|
{
|
|
DWORD EscapeValue = POSTSCRIPT_IDENTIFY;
|
|
|
|
if (ExtEscape(hDC,
|
|
QUERYESCSUPPORT,
|
|
sizeof(DWORD),
|
|
(LPSTR)&EscapeValue,
|
|
0,
|
|
NULL) != 0)
|
|
{
|
|
|
|
// must be in GDI centric mode to get PS feature settings...
|
|
|
|
DWORD Mode = PSIDENT_GDICENTRIC;
|
|
|
|
if (ExtEscape(hDC,
|
|
POSTSCRIPT_IDENTIFY,
|
|
sizeof(DWORD),
|
|
(LPSTR)&Mode,
|
|
0,
|
|
NULL)>0)
|
|
{
|
|
if (ExtEscape(hDC,
|
|
GET_PS_FEATURESETTING,
|
|
sizeof(INT),
|
|
(LPSTR)&feature,
|
|
sizeof(INT),
|
|
(LPSTR)&level)>0)
|
|
{
|
|
Globals::hCachedPrinter = hPrinter;
|
|
Globals::CachedPSLevel = level;
|
|
|
|
return level;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hPrinter == NULL)
|
|
return -1;
|
|
|
|
// get name of the PPD file.
|
|
|
|
union
|
|
{
|
|
DRIVER_INFO_2A driverInfo;
|
|
CHAR buftmp[1024];
|
|
};
|
|
DWORD size;
|
|
|
|
// we require GetPrinterDriver() API to get the .PPD path+file.
|
|
// unfortunately this API is buried in winspool.drv (112KB), so we
|
|
// lazy load it here.
|
|
|
|
if (Globals::WinspoolHandle == NULL)
|
|
{
|
|
Globals::WinspoolHandle = LoadLibraryA("winspool.drv");
|
|
|
|
if (Globals::WinspoolHandle == NULL)
|
|
return -1;
|
|
|
|
Globals::GetPrinterDriverFunction = (WINSPOOLGETPRINTERDRIVERFUNCTION)
|
|
GetProcAddress(
|
|
Globals::WinspoolHandle,
|
|
"GetPrinterDriverA");
|
|
}
|
|
|
|
if (Globals::GetPrinterDriverFunction == NULL)
|
|
return -1;
|
|
|
|
if (Globals::GetPrinterDriverFunction(hPrinter,
|
|
NULL,
|
|
2,
|
|
(BYTE*)&driverInfo,
|
|
1024,
|
|
&size) == 0)
|
|
return -1;
|
|
|
|
HANDLE hFile;
|
|
|
|
hFile = CreateFileA(driverInfo.pDataFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return -1;
|
|
|
|
// Obtain the file size
|
|
// NOTE: We don't support files larger than 4GB.
|
|
|
|
DWORD sizeLow, sizeHigh;
|
|
sizeLow = GetFileSize(hFile, &sizeHigh);
|
|
|
|
// impose a 4GB limit (certainly reasonable)
|
|
if (sizeLow == 0xffffffff || sizeHigh != 0)
|
|
{
|
|
CloseHandle(hFile);
|
|
return -1;
|
|
}
|
|
|
|
// Map the file into memory
|
|
|
|
HANDLE hFilemap;
|
|
LPSTR fileview = NULL;
|
|
|
|
hFilemap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
|
|
if (hFilemap != NULL)
|
|
{
|
|
fileview = (LPSTR) MapViewOfFile(hFilemap, FILE_MAP_READ, 0, 0, 0);
|
|
CloseHandle(hFilemap);
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(hFile);
|
|
return -1;
|
|
}
|
|
|
|
LPSTR buf = fileview;
|
|
LPSTR topbuf = fileview + (sizeLow-16);
|
|
|
|
// we actually expect the LanguageLevel to be early
|
|
// in the file (likely in the first 2K of data).
|
|
|
|
// !! What if this appears in comments (read starting at carriage returns?!
|
|
|
|
level = -1;
|
|
|
|
while (buf < topbuf)
|
|
{
|
|
if (*buf == 'L' &&
|
|
GpMemcmp(buf, "LanguageLevel", 13) == 0)
|
|
{
|
|
while ((*buf < '0' || *buf > '9') && buf < topbuf)
|
|
buf++;
|
|
|
|
CHAR ch = *buf;
|
|
|
|
if (ch >= '0' && ch <= '9')
|
|
level = (INT)ch - (INT)'0';
|
|
|
|
break;
|
|
}
|
|
buf++;
|
|
}
|
|
|
|
UnmapViewOfFile((LPCVOID)fileview);
|
|
CloseHandle(hFile);
|
|
|
|
Globals::hCachedPrinter = hPrinter;
|
|
Globals::CachedPSLevel = level;
|
|
|
|
return level;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* History:
|
|
* 6/1/1999 ericvan Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromGdiPrinterDC(
|
|
HDC hdc,
|
|
HANDLE hPrinter
|
|
)
|
|
{
|
|
ASSERT((hdc != NULL) &&
|
|
((GetDCType(hdc) == OBJ_DC) ||
|
|
(GetDCType(hdc) == OBJ_ENHMETADC))&&
|
|
(GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASPRINTER));
|
|
|
|
// !! Change to useNewPrinterCode when we've fully switched
|
|
DriverPrint *driver = NULL;
|
|
|
|
DpBitmap *bitmap = new DpBitmap(hdc); // initializes Dpi
|
|
if (CheckValid(bitmap))
|
|
{
|
|
GpPrinterDevice *pdevice;
|
|
|
|
{ // FPU Sandbox for potentially unsafe FPU code.
|
|
FPUStateSandbox fpsb;
|
|
pdevice = new GpPrinterDevice(hdc);
|
|
} // FPU Sandbox for potentially unsafe FPU code.
|
|
|
|
if (CheckValid(pdevice))
|
|
{
|
|
// we defer creating driver until we know which to create
|
|
// change to use DIBSECTION instead of GDI operations
|
|
|
|
// Check if this is a postscript printer
|
|
CHAR strTech[30];
|
|
strTech[0] = '\0';
|
|
|
|
INT ScaleX; // ratio of device DPI : capped DPI
|
|
INT ScaleY;
|
|
|
|
// It is a PostScript printer if POSTSCRIPT_PASSTHROUGH or
|
|
// POSTSCRIPT_IGNORE is available. For some reason querying
|
|
// GETTECHNOLOGY for postscript fails in MS Publisher, it may
|
|
// be because we are in GDI centric mode.
|
|
|
|
int iWant1 = POSTSCRIPT_PASSTHROUGH;
|
|
int iWant2 = POSTSCRIPT_IGNORE;
|
|
|
|
BOOL postscript;
|
|
{ // FPU Sandbox for potentially unsafe FPU code.
|
|
FPUStateSandbox fpsb;
|
|
postscript = (
|
|
(Escape(hdc, QUERYESCSUPPORT, sizeof(iWant1), (LPCSTR)&iWant1, NULL) != 0) ||
|
|
(Escape(hdc, QUERYESCSUPPORT, sizeof(iWant2), (LPCSTR)&iWant2, NULL) != 0));
|
|
} // FPU Sandbox for potentially unsafe FPU code.
|
|
|
|
SIZEL szlDevice;
|
|
|
|
szlDevice.cx = GetDeviceCaps(hdc, HORZRES);
|
|
szlDevice.cy = GetDeviceCaps(hdc, VERTRES);
|
|
|
|
// ScaleX and ScaleY should be power of two (2, 4, 8)
|
|
|
|
if (bitmap->DpiX <= 100)
|
|
{
|
|
ScaleX = 1;
|
|
ScaleY = 1;
|
|
}
|
|
else
|
|
{
|
|
if (bitmap->DpiX >= 1200)
|
|
{
|
|
ScaleX = GpRound(TOREAL(bitmap->DpiX) / 200);
|
|
ScaleY = GpRound(TOREAL(bitmap->DpiY) / 200);
|
|
}
|
|
else
|
|
{
|
|
ScaleX = 3;
|
|
ScaleY = 3; // cap 600 to 200 dpi or 3:1
|
|
}
|
|
}
|
|
|
|
// We no longer keep capped dpi -- we use the device dpi as
|
|
// capped dpi so that the world to
|
|
// device transformation is correct for the clipped region and
|
|
// path transformation. ScaleX and ScaleY are used to scale
|
|
// the output rectangle region.
|
|
|
|
bitmap->InitializeForPrinter(pdevice,
|
|
szlDevice.cx,
|
|
szlDevice.cy);
|
|
|
|
GpGraphics *g = new GpGraphics(bitmap);
|
|
if (g)
|
|
{
|
|
g->Printer = TRUE;
|
|
g->Context->Hdc = hdc;
|
|
g->Context->IsPrinter = TRUE;
|
|
|
|
// Note: Both 'Device' and 'Driver' are freed at
|
|
// ~GpGraphics time when 'CreatedDevice' is set:
|
|
|
|
g->PrinterMetafile = NULL;
|
|
g->PrinterGraphics = NULL;
|
|
g->PrinterEMF = NULL;
|
|
|
|
if (postscript)
|
|
{
|
|
g->Type = GraphicsBitmap;
|
|
|
|
INT level = GetPostscriptLevel(hdc, hPrinter);
|
|
|
|
driver = new DriverPS(pdevice, level);
|
|
|
|
// !! Should this stuff be shifted into some driver
|
|
// initialization routine?!
|
|
|
|
// !! Interop - what about redefines or conflicts
|
|
// (conflicts aren't likely, but are
|
|
// theoretically possible with
|
|
// GetDC/ReleaseDC)
|
|
|
|
}
|
|
#if 0
|
|
else if (g->StartPrinterEMF() == Ok)
|
|
{
|
|
g->Type = GraphicsMetafile;
|
|
|
|
RIP(("Setting CreatedDevice will free Driver"));
|
|
|
|
driver = NULL; new DriverMeta(pdevice);
|
|
|
|
// GDI has some optimization code to check the page for color
|
|
// content, if none is found, on play back, it sets the device
|
|
// as being monochrome.
|
|
//
|
|
// Unfortunately, since our stuff is encoded in escapes, we end up
|
|
// playing back in monochrome. The work-around, is to call a GDI
|
|
// API that forces a color flag to be set in the EMF code. A
|
|
// simple one is SetTextColor().
|
|
|
|
// !!! Might want to remove this SetTextColor stuff for version one:
|
|
|
|
COLORREF colorRef = GetTextColor(hdc);
|
|
SetTextColor(hdc, 0x00808080);
|
|
SetTextColor(hdc, colorRef);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
// We can't use escapes for optimization,
|
|
// Map to GDI HDC operations.
|
|
|
|
g->Type = GraphicsBitmap;
|
|
|
|
driver = new DriverNonPS(pdevice);
|
|
}
|
|
|
|
if (CheckValid(driver))
|
|
{
|
|
g->Driver = driver;
|
|
g->Device = pdevice;
|
|
|
|
driver->ScaleX = ScaleX;
|
|
driver->ScaleY = ScaleY;
|
|
|
|
// check for supporting print clip escapes
|
|
// These may be supported on PCL as well as Postscript
|
|
DWORD EscapeValue1 = CLIP_TO_PATH;
|
|
DWORD EscapeValue2 = BEGIN_PATH;
|
|
DWORD EscapeValue3 = END_PATH;
|
|
|
|
// Although some PCL drivers support CLIP_TO_PATH we
|
|
// currently disable their use due to some outstanding
|
|
// bugs in HP and Lexmark PCL drivers. See bug #182972
|
|
|
|
{ // FPU Sandbox for potentially unsafe FPU code.
|
|
FPUStateSandbox fpsb;
|
|
|
|
driver->UseClipEscapes = postscript &&
|
|
(ExtEscape(hdc,
|
|
QUERYESCSUPPORT,
|
|
sizeof(DWORD),
|
|
(LPSTR)&EscapeValue1,
|
|
0,
|
|
NULL) != 0) &&
|
|
(ExtEscape(hdc,
|
|
QUERYESCSUPPORT,
|
|
sizeof(DWORD),
|
|
(LPSTR)&EscapeValue2,
|
|
0,
|
|
NULL) != 0) &&
|
|
(ExtEscape(hdc,
|
|
QUERYESCSUPPORT,
|
|
sizeof(DWORD),
|
|
(LPSTR)&EscapeValue3,
|
|
0,
|
|
NULL) != 0);
|
|
} // FPU Sandbox for potentially unsafe FPU code.
|
|
|
|
DWORD EscapeValue = CHECKJPEGFORMAT;
|
|
|
|
{ // FPU Sandbox for potentially unsafe FPU code.
|
|
FPUStateSandbox fpsb;
|
|
driver->SupportJPEGpassthrough = ExtEscape(
|
|
hdc,
|
|
QUERYESCSUPPORT,
|
|
sizeof(DWORD),
|
|
(LPSTR)&EscapeValue,
|
|
0,
|
|
NULL) != 0;
|
|
} // FPU Sandbox for potentially unsafe FPU code.
|
|
|
|
EscapeValue = CHECKPNGFORMAT;
|
|
{ // FPU Sandbox for potentially unsafe FPU code.
|
|
FPUStateSandbox fpsb;
|
|
driver->SupportPNGpassthrough = ExtEscape(
|
|
hdc,
|
|
QUERYESCSUPPORT,
|
|
sizeof(DWORD),
|
|
(LPSTR)&EscapeValue,
|
|
0,
|
|
NULL) != 0;
|
|
} // FPU Sandbox for potentially unsafe FPU code.
|
|
|
|
driver->NumColors = GetDeviceCaps(hdc, NUMCOLORS);
|
|
|
|
driver->UseVDP = FALSE;
|
|
|
|
// !! VDP not supported in v1
|
|
//VDP_GetFormSupport(hdc,
|
|
// (WORD*)&(driver->SupportVDP));
|
|
|
|
g->CreatedDevice = TRUE;
|
|
|
|
g->ResetState(0, 0, szlDevice.cx, szlDevice.cy);
|
|
|
|
if (g->InheritAppClippingAndTransform(hdc) == Ok)
|
|
{
|
|
return(g);
|
|
}
|
|
else
|
|
{
|
|
// ~GpGraphics implicitly deletes bitmap, device, and driver
|
|
delete g;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
delete g;
|
|
|
|
delete pdevice;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
delete pdevice;
|
|
}
|
|
|
|
delete bitmap;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* This constructor is used internally for printer callback routine.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* History:
|
|
* 6/1/1999 ericvan Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromHdcSurf(
|
|
HDC hdc,
|
|
SURFOBJ* surfObj,
|
|
RECTL* bandClip
|
|
)
|
|
{
|
|
static UINT16 PixelCount[] = { 1, 4, 8, 16, 24, 32 };
|
|
|
|
INT width;
|
|
INT height;
|
|
GpGraphics* g;
|
|
DpDriver *driver;
|
|
|
|
// This is a weird surface. It is a banded surface, so we set up a
|
|
// clip and direct bits pointer. We also have an HDC for it if we
|
|
// decide to punt to GDI.
|
|
|
|
DpBitmap *bitmap = new DpBitmap(hdc); // initializes Dpi
|
|
if (CheckValid(bitmap))
|
|
{
|
|
GpGraphics *g = new GpGraphics(bitmap);
|
|
if (g)
|
|
{
|
|
width = GetDeviceCaps(hdc, HORZRES);
|
|
height = GetDeviceCaps(hdc, VERTRES);
|
|
|
|
// create DIB section for direct rendering of bits
|
|
|
|
if (surfObj->iBitmapFormat < BMF_1BPP ||
|
|
surfObj->iBitmapFormat > BMF_32BPP)
|
|
{
|
|
|
|
InitializeHdcOnlyUse:
|
|
// we don't support direct rendering to this type of
|
|
// surface format. Do everything through GDI HDC.
|
|
|
|
driver = Globals::GdiDriver;
|
|
|
|
bitmap->InitializeForGdiBitmap(Globals::DesktopDevice,
|
|
width,
|
|
height);
|
|
|
|
|
|
g->Type = GraphicsBitmap;
|
|
g->Driver = driver;
|
|
g->Context->Hdc = hdc;
|
|
|
|
g->ResetState(0, 0, 1, 1);
|
|
}
|
|
else
|
|
{
|
|
DIBSECTION dibSec;
|
|
dibSec.dsBm.bmType = 0;
|
|
dibSec.dsBm.bmWidth = surfObj->sizlBitmap.cx;
|
|
|
|
if (surfObj->lDelta < 0)
|
|
{
|
|
// bits pointer at top of frame buffer (scans down)
|
|
|
|
dibSec.dsBm.bmWidthBytes = -surfObj->lDelta;
|
|
dibSec.dsBm.bmHeight = -surfObj->sizlBitmap.cy;
|
|
}
|
|
else
|
|
{
|
|
// bits pointer at base of frame buffer (scans up)
|
|
|
|
dibSec.dsBm.bmWidthBytes = surfObj->lDelta;
|
|
dibSec.dsBm.bmHeight = surfObj->sizlBitmap.cy;
|
|
}
|
|
|
|
dibSec.dsBm.bmPlanes = 1;
|
|
dibSec.dsBm.bmBitsPixel = PixelCount[surfObj->iBitmapFormat-1];
|
|
|
|
dibSec.dsBmih.biSize = sizeof(BITMAPINFOHEADER);
|
|
dibSec.dsBmih.biWidth = width;
|
|
dibSec.dsBmih.biHeight = height;
|
|
dibSec.dsBmih.biPlanes = 1;
|
|
dibSec.dsBmih.biBitCount = PixelCount[surfObj->iBitmapFormat-1];
|
|
dibSec.dsBmih.biCompression = BI_BITFIELDS;
|
|
dibSec.dsBmih.biSize = 0;
|
|
|
|
dibSec.dsBitfields[0] = 0x000000FF;
|
|
dibSec.dsBitfields[1] = 0x0000FF00;
|
|
dibSec.dsBitfields[2] = 0x00FF0000;
|
|
|
|
if (bitmap->InitializeForDibsection( hdc,
|
|
(HBITMAP) NULL,
|
|
Globals::DesktopDevice,
|
|
&dibSec,
|
|
&width,
|
|
&height,
|
|
&driver) == FALSE)
|
|
goto InitializeHdcOnlyUse;
|
|
|
|
// Init Valid now so later calls won't fail
|
|
|
|
g->Type = GraphicsBitmap;
|
|
g->Driver = driver;
|
|
g->Context->Hdc = hdc;
|
|
|
|
// How do we clip and map to correct band?
|
|
// GDI has set the WorldToContainer transform to translate the
|
|
// correct band to position (0,0) on the device surface. So we
|
|
// clip the size of the band relative to the surface. The image
|
|
// is mapped via the transform into this clipped region.
|
|
|
|
// set visible client clip region for surface
|
|
|
|
g->ResetState(0, 0, // bandClip->left, bandClip->top,
|
|
bandClip->right - bandClip->left,
|
|
bandClip->bottom - bandClip->top);
|
|
|
|
// Set the destination Graphics to represent device co-ordinates
|
|
|
|
g->SetPageTransform(UnitPixel);
|
|
|
|
if (g->InheritAppClippingAndTransform(hdc) == Ok)
|
|
{
|
|
return(g);
|
|
}
|
|
else
|
|
{
|
|
delete g;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromGdiEmfDC(
|
|
HDC hdc
|
|
)
|
|
{
|
|
ASSERT (hdc != NULL);
|
|
|
|
DpBitmap *bitmap = new DpBitmap(hdc); // initializes Dpi
|
|
if (CheckValid(bitmap))
|
|
{
|
|
bitmap->InitializeForMetafile(Globals::DesktopDevice);
|
|
|
|
GpGraphics *g = new GpGraphics(bitmap);
|
|
if (g)
|
|
{
|
|
g->Type = GraphicsMetafile;
|
|
g->DownLevel = TRUE;
|
|
g->Driver = Globals::MetaDriver;
|
|
g->Context->Hdc = hdc;
|
|
g->Context->IsEmfPlusHdc = TRUE;
|
|
|
|
g->ResetState(0, 0, 1, 1);
|
|
|
|
// Override some state, as we don't want anything to be
|
|
// clipped out of a metafile, unless there is clipping
|
|
// in the hdc.
|
|
|
|
g->WindowClip.SetInfinite();
|
|
g->Context->ContainerClip.SetInfinite();
|
|
g->Context->VisibleClip.SetInfinite();
|
|
|
|
if (g->InheritAppClippingAndTransform(hdc) == Ok)
|
|
{
|
|
return g;
|
|
}
|
|
delete g;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
* History:
|
|
*
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetForMetafile(
|
|
IMetafileRecord * metafile,
|
|
EmfType type,
|
|
HDC hdc
|
|
)
|
|
{
|
|
ASSERT ((metafile != NULL) && (hdc != NULL));
|
|
|
|
DpBitmap *bitmap = new DpBitmap(hdc); // initializes Dpi
|
|
if (CheckValid(bitmap))
|
|
{
|
|
bitmap->InitializeForMetafile(Globals::DesktopDevice);
|
|
|
|
GpGraphics *g = new GpGraphics(bitmap);
|
|
if (g)
|
|
{
|
|
g->Type = GraphicsMetafile;
|
|
g->Metafile = metafile;
|
|
g->DownLevel = (type != EmfTypeEmfPlusOnly);
|
|
g->Driver = Globals::MetaDriver;
|
|
g->Context->Hdc = hdc;
|
|
g->Context->IsEmfPlusHdc = TRUE;
|
|
|
|
g->ResetState(0, 0, 1, 1);
|
|
|
|
// Override some state, as we don't want anything to be
|
|
// clipped out of a metafile
|
|
|
|
g->WindowClip.SetInfinite();
|
|
g->Context->ContainerClip.SetInfinite();
|
|
g->Context->VisibleClip.SetInfinite();
|
|
|
|
return(g);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Create a GpGraphics class from a DC.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] hdc - Specifies the DC.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NULL if failure (such as with an invalid DC).
|
|
*
|
|
* History:
|
|
*
|
|
* 12/04/1998 andrewgo
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics*
|
|
GpGraphics::GetFromHdc(
|
|
HDC hdc,
|
|
HANDLE hDevice
|
|
)
|
|
{
|
|
GpGraphics *g = NULL;
|
|
|
|
// GetObjectType is nice and fast (entirely user-mode on NT):
|
|
|
|
switch (GetDCType(hdc))
|
|
{
|
|
case OBJ_DC:
|
|
if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASPRINTER )
|
|
{
|
|
g = GpGraphics::GetFromGdiPrinterDC(hdc, hDevice);
|
|
}
|
|
else
|
|
{
|
|
g = GpGraphics::GetFromGdiScreenDC(hdc);
|
|
}
|
|
|
|
break;
|
|
|
|
case OBJ_MEMDC:
|
|
g = GpGraphics::GetFromGdiBitmap(hdc);
|
|
break;
|
|
|
|
case OBJ_ENHMETADC:
|
|
// When metafile spooling, the printer DC will be of type
|
|
// OBJ_ENHMETADC on Win9x and NT4 (but not NT5 due to a fix
|
|
// to NT bug 98810). We need to do some more work to figure
|
|
// out whether it's really a printer DC or a true metafile
|
|
// DC:
|
|
|
|
BOOL printDC;
|
|
|
|
{ // FPU Sandbox for potentially unsafe FPU code.
|
|
FPUStateSandbox fpsb;
|
|
printDC = Globals::GdiIsMetaPrintDCFunction(hdc);
|
|
} // FPU Sandbox for potentially unsafe FPU code.
|
|
|
|
if (printDC)
|
|
{
|
|
g = GpGraphics::GetFromGdiPrinterDC(hdc, hDevice);
|
|
}
|
|
else
|
|
{
|
|
g = GpGraphics::GetFromGdiEmfDC(hdc);
|
|
}
|
|
break;
|
|
|
|
case OBJ_METADC:
|
|
TERSE(("16-bit metafile DC support not yet implemented"));
|
|
break;
|
|
}
|
|
|
|
return(g);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Dispose of a GpGraphics object
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpGraphics::~GpGraphics()
|
|
{
|
|
// How do we ensure that no one is using the graphics when we are here?
|
|
// Flush any pending drawing commands before we go out of scope:
|
|
|
|
Flush(FlushIntentionFlush);
|
|
|
|
if (IsPrinter())
|
|
{
|
|
EndPrinterEMF();
|
|
|
|
if (PrintInit)
|
|
delete PrintInit;
|
|
}
|
|
|
|
BOOL doResetHdc = TRUE;
|
|
|
|
// Handle Graphics-type specific functionality:
|
|
|
|
switch (Type)
|
|
{
|
|
case GraphicsMetafile:
|
|
if (Metafile != NULL)
|
|
{
|
|
Metafile->EndRecording();
|
|
doResetHdc = FALSE; // EndRecording closes the metafile HDC
|
|
}
|
|
// FALLTHRU
|
|
|
|
case GraphicsBitmap:
|
|
|
|
// if this is created on a GdipBitmap, dec the ref count
|
|
// delete the bitmap if ref count <= 0
|
|
|
|
if (GdipBitmap)
|
|
{
|
|
GdipBitmap->Dispose();
|
|
}
|
|
// We have to delete the temporary surface that we created:
|
|
|
|
delete Surface;
|
|
break;
|
|
}
|
|
|
|
// Restore the DC that we were derived from (if any).
|
|
// We must NOT do this before the call to EndRecording because
|
|
// EndRecording needs the context->Hdc to be in the saved state
|
|
// so that the transforms are still reset like GDI+ requires.
|
|
|
|
if (doResetHdc)
|
|
{
|
|
Context->ResetHdc();
|
|
}
|
|
|
|
// Free any device and driver objects that had to be created only for the
|
|
// lifespan of the Graphics object:
|
|
|
|
if (CreatedDevice)
|
|
{
|
|
delete Driver;
|
|
delete Device;
|
|
}
|
|
|
|
SetValid(FALSE); // so we don't use a deleted object
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Return a GDI DC handle associated with the current graphics context.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GDI DC handle associated with the current graphics context
|
|
* NULL if there is an error
|
|
*
|
|
* NOTE: We assume the caller has already obtained a lock on the
|
|
* current graphics context.
|
|
*
|
|
* NOTE: This function does not return a clean DC! That is, expect it
|
|
* to have a funky transform, weird ROP mode, etc. If you want
|
|
* to use this internally, you should probably call Context->GetHdc()
|
|
* directly.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HDC
|
|
GpGraphics::GetHdc()
|
|
{
|
|
// We must flush the output of the Graphics before returning an HDC.
|
|
this->Flush(FlushIntentionFlush);
|
|
|
|
HDC hdc = NULL;
|
|
|
|
if (Context->Hdc)
|
|
{
|
|
// If the Graphics was originally derived from an HDC, we simply
|
|
// return back the original HDC (this avoids some issues as to
|
|
// what to do about inherited transforms, etc.). We may have
|
|
// mucked with some of the DC state, though, so reset it back to
|
|
// what it was originally:
|
|
|
|
Context->ResetHdc();
|
|
|
|
hdc = Context->Hdc;
|
|
}
|
|
else if (Context->Hwnd)
|
|
{
|
|
// The Graphics was originally derived from an HWND:
|
|
|
|
hdc = GetDC(Context->Hwnd);
|
|
}
|
|
else if (Surface && (Surface->Type == DpBitmap::CreationType::GPBITMAP))
|
|
{
|
|
// The GpBitmap is accessible from the EpScanBitmap. It will
|
|
// create an HDC and GDI bitmap appropriate for interop.
|
|
|
|
EpScanBitmap *scan = static_cast<EpScanBitmap*>(Surface->Scan);
|
|
hdc = scan->GetBitmap()->GetHdc();
|
|
}
|
|
|
|
if (IsRecording() && (hdc != NULL))
|
|
{
|
|
if (IsPrinter())
|
|
{
|
|
EndPrinterEMF();
|
|
}
|
|
else
|
|
{
|
|
GpStatus status = Metafile->RecordGetDC();
|
|
}
|
|
}
|
|
|
|
return(hdc);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Release the GDI DC handle associated with the current graphics context.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* hdc - GDI DC handle returned by a previous GetHdc() call
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Notes:
|
|
*
|
|
* We assume the caller has already obtained a lock on the
|
|
* current graphics context.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpGraphics::ReleaseHdc(
|
|
HDC hdc
|
|
)
|
|
{
|
|
if (Context->Hdc)
|
|
{
|
|
// The Graphics was originally derived from an HDC. We don't
|
|
// have to do anything here; ResetHdc() already marked the
|
|
// DC as dirty.
|
|
}
|
|
else if (Context->Hwnd)
|
|
{
|
|
// The Graphics was originally derived from an HWND:
|
|
|
|
ReleaseDC(Context->Hwnd, hdc);
|
|
}
|
|
else if (Surface && (Surface->Type == DpBitmap::CreationType::GPBITMAP))
|
|
{
|
|
// The GpBitmap is accessible from the EpScanBitmap.
|
|
|
|
EpScanBitmap *scan = static_cast<EpScanBitmap*>(Surface->Scan);
|
|
scan->GetBitmap()->ReleaseHdc(hdc);
|
|
}
|
|
|
|
if (IsRecording() && IsPrinter())
|
|
{
|
|
StartPrinterEMF();
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Save (push) the graphics context state. Return the ID of the current
|
|
* state (before the push) for the app to restore to later on.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* gstate - the state to restore the context to later
|
|
*
|
|
* Created:
|
|
*
|
|
* 3/4/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
INT
|
|
GpGraphics::Save()
|
|
{
|
|
DpContext * newContext = new DpContext(Context);
|
|
|
|
if ((newContext != NULL) &&
|
|
(newContext->AppClip.Set(&(Context->AppClip), TRUE) == Ok) &&
|
|
(newContext->ContainerClip.Set(&(Context->ContainerClip), TRUE)
|
|
== Ok) &&
|
|
(newContext->VisibleClip.Set(&(Context->VisibleClip), TRUE) == Ok))
|
|
{
|
|
INT gstate = newContext->Id;
|
|
|
|
newContext->InverseOk = Context->InverseOk;
|
|
newContext->PageUnit = Context->PageUnit;
|
|
newContext->PageScale = Context->PageScale;
|
|
newContext->PageMultiplierX = Context->PageMultiplierX;
|
|
newContext->PageMultiplierY = Context->PageMultiplierY;
|
|
newContext->WorldToPage = Context->WorldToPage;
|
|
newContext->ContainerToDevice = Context->ContainerToDevice;
|
|
newContext->WorldToDevice = Context->WorldToDevice;
|
|
newContext->DeviceToWorld = Context->DeviceToWorld;
|
|
newContext->IcmMode = Context->IcmMode;
|
|
newContext->GdiLayered = Context->GdiLayered;
|
|
|
|
Context->Next = newContext;
|
|
Context = newContext;
|
|
|
|
if (IsRecording())
|
|
{
|
|
GpStatus status = Metafile->RecordSave(gstate);
|
|
if (status != Ok)
|
|
{
|
|
SetValid(FALSE); // Prevent any more recording
|
|
}
|
|
}
|
|
return gstate;
|
|
}
|
|
|
|
delete newContext;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define CONTAINER_ID 0x00008000
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Restore (pop) the context to the state before the specified one.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* gstate - the pushed state (restore to state before this)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 3/4/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
VOID
|
|
GpGraphics::Restore(
|
|
INT gstate
|
|
)
|
|
{
|
|
DpContext * cur = Context;
|
|
DpContext * prev;
|
|
|
|
for (;;)
|
|
{
|
|
if ((prev = cur->Prev) == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (cur->Id == (UINT)gstate)
|
|
{
|
|
// don't double record EndContainer calls
|
|
if (IsRecording() && ((gstate & CONTAINER_ID) == 0))
|
|
{
|
|
GpStatus status = Metafile->RecordRestore(gstate);
|
|
if (status != Ok)
|
|
{
|
|
SetValid(FALSE); // Prevent any more recording
|
|
}
|
|
}
|
|
prev->Next = NULL;
|
|
prev->SaveDc = cur->SaveDc;
|
|
Context = prev;
|
|
delete cur;
|
|
return;
|
|
}
|
|
cur = prev;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* End a container. Restores the state back to what it was before the
|
|
* container was started. The CONTAINER_ID bit is used to make sure that
|
|
* Restore is not used with BeginContainer and that EndContainer is not
|
|
* used with Save.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] containerState - the pushed container state
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 4/7/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
VOID
|
|
GpGraphics::EndContainer(
|
|
INT containerState
|
|
)
|
|
{
|
|
if (IsRecording())
|
|
{
|
|
GpStatus status = Metafile->RecordEndContainer(containerState);
|
|
if (status != Ok)
|
|
{
|
|
SetValid(FALSE); // Prevent any more recording
|
|
}
|
|
}
|
|
Restore(containerState | CONTAINER_ID);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Begin a container. This sets the container transform and the
|
|
* container clip based on the current transform and the current clip.
|
|
*
|
|
* We have to have a container transform for 2 reasons:
|
|
* 1) If we tried to do it in the world transform, then a call
|
|
* to (Re)SetWorldTransform would erase the container transform.
|
|
*
|
|
* 2) We have APIs for setting the text size and the line width that
|
|
* are based on the page units, so they are not affected by the
|
|
* world transform, but they are affected by the container transform.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* gstate - the state to restore the context to later
|
|
*
|
|
* Created:
|
|
*
|
|
* 3/9/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
GpGraphics::BeginContainer(
|
|
const GpRectF & destRect,
|
|
const GpRectF & srcRect,
|
|
GpPageUnit srcUnit,
|
|
REAL srcDpiX, // only set by metafile enumeration
|
|
REAL srcDpiY,
|
|
BOOL srcIsDisplay
|
|
)
|
|
{
|
|
GpMatrix identityMatrix;
|
|
DpContext * newContext = new DpContext(Context);
|
|
|
|
if (newContext == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// leave newContext->AppClip set to identity
|
|
|
|
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) &&
|
|
(newContext->ContainerClip.Set(&(Context->AppClip.DeviceRegion),
|
|
TRUE) == Ok) &&
|
|
(newContext->ContainerClip.And(&(Context->ContainerClip)) == Ok) &&
|
|
(newContext->VisibleClip.Set(&(Context->VisibleClip), TRUE) == Ok))
|
|
{
|
|
REAL unitMultiplierX;
|
|
REAL unitMultiplierY;
|
|
GpRectF deviceSrc;
|
|
|
|
newContext->GetPageMultipliers(&unitMultiplierX, &unitMultiplierY,
|
|
srcUnit);
|
|
|
|
deviceSrc.X = unitMultiplierX * srcRect.X;
|
|
deviceSrc.Y = unitMultiplierY * srcRect.Y;
|
|
deviceSrc.Width = unitMultiplierX * srcRect.Width;
|
|
deviceSrc.Height = unitMultiplierY * srcRect.Height;
|
|
|
|
if (newContext->ContainerToDevice.InferAffineMatrix(
|
|
destRect, deviceSrc) == Ok)
|
|
{
|
|
newContext->AntiAliasMode = 0;
|
|
newContext->TextRenderHint = TextRenderingHintSystemDefault;
|
|
newContext->TextContrast = DEFAULT_TEXT_CONTRAST;
|
|
newContext->CompositingMode = CompositingModeSourceOver;
|
|
newContext->CompositingQuality = CompositingQualityDefault;
|
|
newContext->FilterType = InterpolationModeDefaultInternal;
|
|
newContext->PixelOffset = PixelOffsetModeDefault;
|
|
|
|
// Note that the world to device transform includes the previous
|
|
// container to device transform.
|
|
newContext->ContainerToDevice.Append(Context->WorldToDevice);
|
|
newContext->InverseOk = FALSE;
|
|
newContext->PageUnit = UnitDisplay;
|
|
newContext->PageScale = 1.0f;
|
|
if ((srcDpiX > 0.0f) && (srcDpiY > 0.0f))
|
|
{
|
|
// When playing a metafile, we have to guarantee that
|
|
// a unit inch is played now as it would have been
|
|
// when it was recorded. For example, if we recorded
|
|
// the metafile at 300 dpi, then an inch was 300 pixels.
|
|
// Even if we're playing it back to a 96-dpi display,
|
|
// that metafile inch must still be transformed to
|
|
// 300 pixels before going throught the container
|
|
// transformation, so that all graphics are scaled
|
|
// the same, whether pixel units or some other units.
|
|
newContext->ContainerDpiX = srcDpiX;
|
|
newContext->ContainerDpiY = srcDpiY;
|
|
newContext->IsDisplay = srcIsDisplay;
|
|
}
|
|
newContext->GetPageMultipliers();
|
|
newContext->WorldToPage.Reset();
|
|
|
|
// Have to inherit the ICM and layering state:
|
|
|
|
newContext->IcmMode = Context->IcmMode;
|
|
newContext->GdiLayered = Context->GdiLayered;
|
|
|
|
INT containerState = newContext->Id;
|
|
newContext->Id |= CONTAINER_ID;
|
|
|
|
Context->Next = newContext;
|
|
Context = newContext;
|
|
|
|
if (IsRecording())
|
|
{
|
|
GpStatus status = Metafile->RecordBeginContainer(destRect,
|
|
srcRect, srcUnit, containerState);
|
|
if (status != Ok)
|
|
{
|
|
SetValid(FALSE); // Prevent any more recording
|
|
}
|
|
}
|
|
|
|
// Do this after switching over the context!
|
|
Context->UpdateWorldToDeviceMatrix();
|
|
|
|
return containerState;
|
|
}
|
|
}
|
|
|
|
delete newContext;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Begin a container. This sets the container transform and the
|
|
* container clip based on the current transform and the current clip.
|
|
*
|
|
* We have to have a container transform for 2 reasons:
|
|
* 1) If we tried to do it in the world transform, then a call
|
|
* to (Re)SetWorldTransform would erase the container transform.
|
|
*
|
|
* 2) We have APIs for setting the text size and the line width that
|
|
* are based on the page units, so they are not affected by the
|
|
* world transform, but they are affected by the container transform.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* gstate - the state to restore the context to later
|
|
*
|
|
* Created:
|
|
*
|
|
* 3/9/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
GpGraphics::BeginContainer(
|
|
BOOL forceIdentityTransform, // only set by metafile player
|
|
REAL srcDpiX,
|
|
REAL srcDpiY,
|
|
BOOL srcIsDisplay
|
|
)
|
|
{
|
|
GpMatrix identityMatrix;
|
|
DpContext * newContext = new DpContext(Context);
|
|
|
|
if (newContext == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// leave newContext->AppClip set to identity
|
|
|
|
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) &&
|
|
(newContext->ContainerClip.Set(&(Context->AppClip.DeviceRegion),
|
|
TRUE) == Ok) &&
|
|
(newContext->ContainerClip.And(&(Context->ContainerClip)) == Ok) &&
|
|
(newContext->VisibleClip.Set(&(Context->VisibleClip), TRUE) == Ok))
|
|
{
|
|
// Note that the world to device transform includes the previous
|
|
// container to device transform.
|
|
GpMatrix worldToDevice = Context->WorldToDevice;
|
|
|
|
// We append the world to device transform below, which already
|
|
// has the container transform in it. We don't want to apply
|
|
// the same transform twice, so we need to reset the container
|
|
// transform here.
|
|
newContext->ContainerToDevice.Reset();
|
|
|
|
// When playing a GDI+ metafile into another metafile, we have to guarantee
|
|
// that the transform is the identity so that the GDI+ records don't get
|
|
// transformed by GDI+ and then get transformed again by GDI.
|
|
if (forceIdentityTransform)
|
|
{
|
|
worldToDevice.Reset();
|
|
}
|
|
else
|
|
{
|
|
// The coordinates that the container transform is expecting are
|
|
// world coordinates, but they will already have gone through
|
|
// the new page to device transform, so convert from device
|
|
// units back to page units before running them through the
|
|
// the container to device transform.
|
|
// In the routine above, this is done through an inferred transform
|
|
// between device unit src rect and world unit dest rect.
|
|
newContext->ContainerToDevice.Scale(1.0f / Context->PageMultiplierX,
|
|
1.0f / Context->PageMultiplierY);
|
|
}
|
|
|
|
newContext->AntiAliasMode = 0;
|
|
newContext->TextRenderHint = TextRenderingHintSystemDefault;
|
|
newContext->TextContrast = DEFAULT_TEXT_CONTRAST;
|
|
newContext->CompositingMode = CompositingModeSourceOver;
|
|
newContext->CompositingQuality = CompositingQualityDefault;
|
|
newContext->FilterType = InterpolationModeDefaultInternal;
|
|
newContext->PixelOffset = PixelOffsetModeDefault;
|
|
newContext->ContainerToDevice.Append(worldToDevice);
|
|
newContext->InverseOk = FALSE;
|
|
newContext->PageUnit = UnitDisplay;
|
|
newContext->PageScale = 1.0f;
|
|
|
|
if ((srcDpiX > 0.0f) && (srcDpiY > 0.0f))
|
|
{
|
|
// When playing a metafile, we have to guarantee that
|
|
// a unit inch is played now as it would have been
|
|
// when it was recorded. For example, if we recorded
|
|
// the metafile at 300 dpi, then an inch was 300 pixels.
|
|
// Even if we're playing it back to a 96-dpi display,
|
|
// that metafile inch must still be transformed to
|
|
// 300 pixels before going throught the container
|
|
// transformation, so that all graphics are scaled
|
|
// the same, whether pixel units or some other units.
|
|
newContext->ContainerDpiX = srcDpiX;
|
|
newContext->ContainerDpiY = srcDpiY;
|
|
newContext->IsDisplay = srcIsDisplay;
|
|
}
|
|
|
|
newContext->GetPageMultipliers();
|
|
newContext->WorldToPage.Reset();
|
|
|
|
// Have to inherit the ICM and layering state:
|
|
|
|
newContext->IcmMode = Context->IcmMode;
|
|
newContext->GdiLayered = Context->GdiLayered;
|
|
|
|
INT containerState = newContext->Id;
|
|
newContext->Id |= CONTAINER_ID;
|
|
|
|
Context->Next = newContext;
|
|
Context = newContext;
|
|
|
|
if (IsRecording())
|
|
{
|
|
GpStatus status = Metafile->RecordBeginContainer(containerState);
|
|
if (status != Ok)
|
|
{
|
|
SetValid(FALSE); // Prevent any more recording
|
|
}
|
|
}
|
|
|
|
// Do this after switching over the context!
|
|
Context->UpdateWorldToDeviceMatrix();
|
|
|
|
return containerState;
|
|
}
|
|
|
|
delete newContext;
|
|
|
|
return 0;
|
|
}
|