|
|
/**************************************************************************\
* * 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; }
|