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.
303 lines
9.4 KiB
303 lines
9.4 KiB
/**************************************************************************\
|
|
*
|
|
* 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);
|
|
}
|
|
}
|