/**************************************************************************\ * * Copyright (c) 1998 Microsoft Corporation * * Module Name: * * * * Abstract: * * * * Notes: * * * * Created: * * //1999 agodfrey * \**************************************************************************/ #include "precomp.hpp" DpDriver::DpDriver() { Internal = new DpDriverInternal; Device = NULL; } DpDriver::~DpDriver() { delete Internal; SetValid(FALSE); // so we don't use a deleted object } // If we let the clipping region height get too large, then GDI will allocate // tons of memory when we select the clip path in. #define GDI_MAX_REGION_HEIGHT_FOR_GDI 65536 // Returns TRUE if we succeed in setting the clipping using // path clipping. static BOOL SetupPathClipping( HDC hdc, const DpContext * context, BOOL doSaveDC, const GpRect * drawBounds ) { // We can use the actual clip path to set up the clipping // under the following circumstances: // 1) the application clipping has only one path // 2) the container clip is simple (a single rect) // which either fully encompasses the application clipping // or else the application clipping is also a single rect // and the intersection of the 2 rects can be used. // We could expand this criteria to include more cases, but for // now, this is sufficient. const GpRegion * appClip = &(context->AppClip); const DpRegion * containerClip = &(context->ContainerClip); if (appClip->IsOnePath() && containerClip->IsSimple()) { // ContainerClip is a single rect // It may be inifinte, but it shouldn't be empty at this point. GpRect pathRect; GpRect containerBounds; GpRect appBounds; GpMatrix identityMatrix; containerClip->GetBounds(&containerBounds); appClip->GetBounds(&identityMatrix, &appBounds); if (appClip->IsRect()) { GpRect::IntersectRect(pathRect, appBounds, containerBounds); if (doSaveDC) { ::SaveDC(hdc); } // Use IntersectClipRect (not BeginPath, Rectangle, EndPath), // because mf3216.dll assumes that // path clipping means do XOR, black, XOR technique, and // we don't want that if we are playing a metafile into // another metafile. ::IntersectClipRect(hdc, pathRect.GetLeft(), pathRect.GetTop(), pathRect.GetRight(), pathRect.GetBottom()); return TRUE; } else // use the AppClip as the clip path { ConvertPathToGdi gdiPath(appClip->GetPath(), &identityMatrix, ForFill); if (gdiPath.IsValid()) { if (doSaveDC) { ::SaveDC(hdc); } gdiPath.AndClip(hdc); if ((appBounds.GetLeft() < containerBounds.GetLeft()) || (appBounds.GetRight() > containerBounds.GetRight()) || (appBounds.GetTop() < containerBounds.GetTop()) || (appBounds.GetBottom() > containerBounds.GetBottom())) { ::IntersectClipRect(hdc, containerBounds.GetLeft(), containerBounds.GetTop(), containerBounds.GetRight(), containerBounds.GetBottom()); } return TRUE; } } } else { const DpClipRegion * clipRegion = &(context->VisibleClip); DpClipRegion intersectClip(drawBounds); GpRect clipBounds; if (intersectClip.IsValid()) { clipRegion->GetBounds(&clipBounds); // GDI doesn't handle large clip regions very well -- it uses // the height of the region to decide how much memory to allocate, // so can end up allocating huge amounts of memory. An example // of this is to take an infinite region and exclude a rect from // it and then clip to that region. To solve this problem, // intersect the clip region with the drawBounds (which are hopefully // a reasonable size). if (clipBounds.Height >= GDI_MAX_REGION_HEIGHT_FOR_GDI) { intersectClip.And(clipRegion); if (intersectClip.IsValid()) { clipRegion = &intersectClip; } } GpPath path(clipRegion); if (path.IsValid()) { GpMatrix identityMatrix; ConvertPathToGdi gdiPath(&path, &identityMatrix, ForFill); if (gdiPath.IsValid()) { if (doSaveDC) { ::SaveDC(hdc); } gdiPath.AndClip(hdc); return TRUE; } } } } return FALSE; } /**************************************************************************\ * * Function Description: * * Set up the clipping in the HDC for the GDI primitive * * Arguments: * * [IN] hdc - The device to set the clipping in * [IN] clipRegion - The region to clip to * [IN] drawBounds - The bounds of the object being drawn * [OUT] isClip - Whether or not we are clipping the object * * Return Value: * * N/A * * Created: * * 9/15/1999 DCurtis * \**************************************************************************/ VOID DpDriver::SetupClipping( HDC hdc, DpContext * context, const GpRect * drawBounds, BOOL & isClip, BOOL & usePathClipping, BOOL forceClipping ) { // VisibleClip is the combination of the AppClip and the ContainerClip. // The ContainerClip is always intersected with the WindowClip. DpClipRegion * clipRegion = &(context->VisibleClip); // We set wantPathClipping to be what the user wants to do. This way // when we return usePathClipping is true only if we did indeed setup a // path clipping BOOL wantPathClipping = usePathClipping; usePathClipping = FALSE; isClip = FALSE; if (forceClipping || (clipRegion->GetRectVisibility( drawBounds->X, drawBounds->Y, drawBounds->GetRight(), drawBounds->GetBottom()) != DpRegion::TotallyVisible)) { if (clipRegion->IsSimple()) { isClip = TRUE; GpRect clipRect; clipRegion->GetBounds(&clipRect); ::SaveDC(hdc); // If we have an infinite region don't intersect if (!clipRegion->IsInfinite()) { ::IntersectClipRect(hdc, clipRect.X, clipRect.Y, clipRect.GetRight(), clipRect.GetBottom()); } return; } // I'm assuming that by now we've already decided that the // drawBounds is at least partially visible. Otherwise, we're // going to a lot of trouble here for nothing. // When writing a metafile, we always want to use path clipping // so that the clipping scales properly when being played back. if (wantPathClipping) { if (SetupPathClipping(hdc, context, TRUE, drawBounds)) { isClip = TRUE; usePathClipping = TRUE; return; } } // Either we're not supposed to use path clipping, or else // path clipping failed for some reason, so use the region // to clip. // Since this might get saved out to a metafile, we need // to Save the DC, and restore it once the clipping is back // so that we don't overwrite the application's clipping HRGN hRgn = clipRegion->GetHRgn(); if (hRgn != (HRGN)0) { ::SaveDC(hdc); ::ExtSelectClipRgn(hdc, hRgn, RGN_AND); ::DeleteObject(hRgn); isClip = TRUE; } } } /**************************************************************************\ * * Function Description: * * Restore the clipping state in the HDC * * Arguments: * * [IN] hdc - The device to set the clipping in * [IN] isClip - If clipping was turned on or not * * Return Value: * * N/A * * Created: * * 9/15/1999 DCurtis * \**************************************************************************/ VOID DpDriver::RestoreClipping( HDC hdc, BOOL isClip, BOOL usePathClipping ) { if (isClip) { // Restore the DC in both cases for PathClipping or // for region clipping ::RestoreDC(hdc, -1); } }