|
|
/**************************************************************************\
* * Copyright (c) 1999 Microsoft Corporation * * Module Name: * * GraphicsClip.cpp * * Abstract: * * Clipping methods of Graphics class * * Created: * * 02/05/1999 DCurtis * \**************************************************************************/
#include "precomp.hpp"
/**************************************************************************\
* * Function Description: * * Get a copy of the current clipping region. Transform it through the * inverse of the current world-to-device matrix, so that if this region * was immediately set as the clipping, then the clipping wouldn't change. * * Arguments: * * NONE * * Return Value: * * GpRegion * region - a copy of the current clipping region; must be * deleted by the application. * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpRegion* GpGraphics::GetClip() const { ASSERT(this->IsValid());
GpRegion * region = new GpRegion(&(Context->AppClip));
if (region != NULL) { if (region->IsValid()) { GpMatrix deviceToWorld;
if ((GetDeviceToWorldTransform(&deviceToWorld) == Ok) && (region->Transform(&deviceToWorld) == Ok)) { return region; } } delete region; } return NULL; }
/**************************************************************************\
* * Function Description: * * Get a copy of the current clipping region. Transform it through the * inverse of the current world-to-device matrix, so that if this region * was immediately set as the clipping, then the clipping wouldn't change. * * Arguments: * * NONE * * Return Value: * * GpRegion * region - an already created region, we set the contents of it. * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::GetClip(GpRegion* region) const { ASSERT(this->IsValid());
region->Set(&(Context->AppClip));
if (region->IsValid()) { GpMatrix deviceToWorld;
if ((GetDeviceToWorldTransform(&deviceToWorld) == Ok) && (region->Transform(&deviceToWorld) == Ok)) { return Ok; } } return GenericError; }
/**************************************************************************\
* * Function Description: * * Reset the clipping back to its default state. * * Arguments: * * NONE * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::ResetClip() { ASSERT(this->IsValid());
GpStatus status = Ok;
if (IsRecording()) { status = Metafile->RecordResetClip(); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } }
DoResetClip(); return status; }
/**************************************************************************\
* * Function Description: * * Set the clipping in the graphics context to be the specified rect. * * Arguments: * * [IN] rect - the rectangle, in world units * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/05/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::SetClip( const GpRectF& rect, CombineMode combineMode ) { ASSERT(this->IsValid());
GpStatus status = Ok;
GpRectF tmpRect = rect; // handle flipped rects
if (tmpRect.Width < 0) { tmpRect.X += tmpRect.Width; tmpRect.Width = -tmpRect.Width; } if (tmpRect.Height < 0) { tmpRect.Y += tmpRect.Height; tmpRect.Height = -tmpRect.Height; }
// crop to infinity
if (tmpRect.X < INFINITE_MIN) { if (tmpRect.Width < INFINITE_SIZE) { tmpRect.Width -= (INFINITE_MIN - tmpRect.X); } tmpRect.X = INFINITE_MIN; } if (tmpRect.Y < INFINITE_MIN) { if (tmpRect.Height < INFINITE_SIZE) { tmpRect.Height -= (INFINITE_MIN - tmpRect.Y); } tmpRect.Y = INFINITE_MIN; }
if ((tmpRect.Width <= REAL_EPSILON) || (tmpRect.Height <= REAL_EPSILON)) { GpRegion emptyRegion; emptyRegion.SetEmpty(); return this->SetClip(&emptyRegion, combineMode); }
if (tmpRect.Width >= INFINITE_SIZE) { if (tmpRect.Height >= INFINITE_SIZE) { GpRegion infiniteRegion; return this->SetClip(&infiniteRegion, combineMode); } tmpRect.Width = INFINITE_SIZE; // crop to infinite
} else if (tmpRect.Height > INFINITE_SIZE) { tmpRect.Height = INFINITE_SIZE; // crop to infinite
} if (IsRecording()) { status = Metafile->RecordSetClip(rect, combineMode); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } }
if (combineMode != CombineModeReplace) { return this->CombineClip(rect, combineMode); } if (Context->WorldToDevice.IsTranslateScale()) { GpRectF transformedRect = rect;
Context->WorldToDevice.TransformRect(transformedRect); Context->AppClip.Set(transformedRect.X, transformedRect.Y, transformedRect.Width, transformedRect.Height);
// Try to match the GDI+ rasterizer
// In theory, this could cause a floating point exception, but
// the transform would have to be a very big scaling transform to do it.
INT left = RasterizerCeiling(transformedRect.X); INT top = RasterizerCeiling(transformedRect.Y); INT right = RasterizerCeiling(transformedRect.GetRight()); INT bottom = RasterizerCeiling(transformedRect.GetBottom());
Context->VisibleClip.Set(left, top, right - left, bottom - top); goto AndClip; } else { GpPointF points[4]; REAL left = rect.X; REAL top = rect.Y; REAL right = rect.X + rect.Width; REAL bottom = rect.Y + rect.Height;
points[0].X = left; points[0].Y = top; points[1].X = right; points[1].Y = top; points[2].X = right; points[2].Y = bottom; points[3].X = left; points[3].Y = bottom;
// Transform the points now so we only have to do it once
Context->WorldToDevice.Transform(points, 4);
GpPath path;
path.AddLines(points, 4);
if (path.IsValid()) { GpMatrix identityMatrix;
if ((Context->AppClip.Set(&path) == Ok) && (Context->VisibleClip.Set(&path, &identityMatrix) == Ok)) { goto AndClip; } } }
ErrorExit: DoResetClip(); return GenericError;
AndClip: if (AndVisibleClip() == Ok) { return status; } goto ErrorExit; }
/**************************************************************************\
* * Function Description: * * Set the clipping in the graphics context to be the specified region. * * Arguments: * * [IN] region - the region to clip to * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/05/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::SetClip( GpRegion * region, CombineMode combineMode ) { ASSERT(this->IsValid()); ASSERT((region != NULL) && (region->IsValid()));
GpStatus status = Ok;
if (IsRecording()) { status = Metafile->RecordSetClip(region, combineMode); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } }
if (combineMode != CombineModeReplace) { return this->CombineClip(region, combineMode); } if ((Context->AppClip.Set(region) == Ok) && (Context->AppClip.Transform(&(Context->WorldToDevice)) == Ok)) { GpMatrix identityMatrix;
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) && (Context->VisibleClip.Set(&(Context->AppClip.DeviceRegion)) == Ok)&& (AndVisibleClip() == Ok)) { return status; } }
DoResetClip(); return GenericError; }
/**************************************************************************\
* * Function Description: * * Set the clipping in the graphics context to be the specified region. * * Arguments: * * [IN] hRgn - the region to clip to (already in device units) * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/05/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::SetClip( HRGN hRgn, CombineMode combineMode ) { ASSERT(this->IsValid());
GpPath path(hRgn); if (path.IsValid()) { return this->SetClip(&path, combineMode, TRUE/*isDevicePath*/); } return OutOfMemory; }
/**************************************************************************\
* * Function Description: * * Set the clipping in the graphics context to be the same as what * the specified graphics has. * * Currently, this only works if the other graphics has the same * resolution as this one does. * * Arguments: * * [IN] g - the graphics to copy the clipping from * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::SetClip( GpGraphics* g, CombineMode combineMode ) { ASSERT(this->IsValid() && (g != NULL) && g->IsValid());
GpStatus status = GenericError; GpRegion * region = new GpRegion(&(g->Context->AppClip));
if (region != NULL) { if (region->IsValid()) { GpMatrix deviceToWorld;
if ((GetDeviceToWorldTransform(&deviceToWorld) == Ok) && (region->Transform(&deviceToWorld) == Ok)) { status = this->SetClip(region, combineMode); } } delete region; } return status; }
/**************************************************************************\
* * Function Description: * * Set the clipping in the graphics context to be the specified path. * * Arguments: * * [IN] path - the path to clip to * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * [IN] isDevicePath- if path is already in device units * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::SetClip( GpPath* path, CombineMode combineMode, BOOL isDevicePath // if path is already in device units
) { ASSERT(this->IsValid() && (path != NULL) && path->IsValid());
GpStatus status = Ok;
if (IsRecording()) { status = Metafile->RecordSetClip(path, combineMode, isDevicePath); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } }
if (combineMode != CombineModeReplace) { return this->CombineClip(path, combineMode, isDevicePath); } if ((Context->AppClip.Set(path) == Ok) && (isDevicePath || (Context->AppClip.Transform(&(Context->WorldToDevice)) == Ok))) { GpMatrix identityMatrix;
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) && (Context->VisibleClip.Set(&(Context->AppClip.DeviceRegion)) == Ok)&& (AndVisibleClip() == Ok)) { return status; } }
DoResetClip(); return GenericError; }
/**************************************************************************\
* * Function Description: * * Combine the region with the current clipping using the specified * combine type. * * Arguments: * * [IN] region - the region to combine the clipping with. * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::CombineClip( GpRegion * region, CombineMode combineMode ) { ASSERT(this->IsValid()); ASSERT((region != NULL) && (region->IsValid())); ASSERT(CombineModeIsValid(combineMode));
GpRegion regionCopy;
if (!Context->WorldToDevice.IsIdentity()) { regionCopy.Set(region);
if ((!regionCopy.IsValid()) || (regionCopy.Transform(&(Context->WorldToDevice)) != Ok)) { return GenericError; } region = ®ionCopy; }
if (Context->AppClip.Combine(region, combineMode) == Ok) { GpMatrix identityMatrix;
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) && (Context->VisibleClip.Set(&(Context->AppClip.DeviceRegion)) == Ok)&& (AndVisibleClip() == Ok)) { return Ok; } }
DoResetClip(); return GenericError; }
/**************************************************************************\
* * Function Description: * * Combine the rect with the current clipping using the specified * combine type. * * Arguments: * * [IN] path - the path to combine the clipping with. * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::CombineClip( const GpPath * path, CombineMode combineMode, BOOL isDevicePath // if path is already in device units
) { ASSERT(this->IsValid()); ASSERT((path != NULL) && (path->IsValid())); ASSERT(CombineModeIsValid(combineMode));
GpPath * pathCopy = NULL;
if (!isDevicePath && (!Context->WorldToDevice.IsIdentity())) { pathCopy = path->Clone();
if (!CheckValid(pathCopy)) { return OutOfMemory; } pathCopy->Transform(&(Context->WorldToDevice)); path = pathCopy; }
GpStatus status = Context->AppClip.Combine(path, combineMode);
delete pathCopy;
if (status == Ok) { GpMatrix identityMatrix;
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) && (Context->VisibleClip.Set(&(Context->AppClip.DeviceRegion)) == Ok)&& (AndVisibleClip() == Ok)) { return Ok; } }
DoResetClip(); return GenericError; }
/**************************************************************************\
* * Function Description: * * Combine the rect with the current clipping using the specified * combine type. * * Arguments: * * [IN] rect - the rect to combine the clipping with. * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::CombineClip( const GpRectF& rect, CombineMode combineMode ) { ASSERT(this->IsValid()); ASSERT(CombineModeIsValid(combineMode));
if (Context->WorldToDevice.IsTranslateScale()) { GpRectF transformedRect = rect;
Context->WorldToDevice.TransformRect(transformedRect);
if (Context->AppClip.Combine(&transformedRect, combineMode) == Ok) { goto SetVisibleClip; } } else { GpPointF points[4]; REAL left = rect.X; REAL top = rect.Y; REAL right = rect.X + rect.Width; REAL bottom = rect.Y + rect.Height;
points[0].X = left; points[0].Y = top; points[1].X = right; points[1].Y = top; points[2].X = right; points[2].Y = bottom; points[3].X = left; points[3].Y = bottom;
Context->WorldToDevice.Transform(points, 4);
GpPath path;
path.AddLines(points, 4);
if (path.IsValid()) { if ( Context->AppClip.Combine(&path, combineMode) == Ok) { goto SetVisibleClip; } } }
ErrorExit: DoResetClip(); return GenericError;
SetVisibleClip: { GpMatrix identityMatrix;
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) && (Context->VisibleClip.Set(&(Context->AppClip.DeviceRegion)) == Ok)&& (AndVisibleClip() == Ok)) { return Ok; } goto ErrorExit; } }
/**************************************************************************\
* * Function Description: * * Offset (translate) the current clipping region by the specified * world unit amounts. * * Arguments: * * [IN] dx - the amount of X to offset the region by, in world units * [IN] dy - the amount of Y to offset the region by, in world units * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ GpStatus GpGraphics::OffsetClip( REAL dx, REAL dy ) { ASSERT(this->IsValid());
GpStatus status = Ok;
if (IsRecording()) { status = Metafile->RecordOffsetClip(dx, dy); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } }
GpPointF offset(dx, dy);
Context->WorldToDevice.VectorTransform(&offset);
if (Context->AppClip.Offset(offset.X, offset.Y) == Ok) { GpMatrix identityMatrix;
if ((Context->AppClip.UpdateDeviceRegion(&identityMatrix) == Ok) && (Context->VisibleClip.Set(&(Context->AppClip.DeviceRegion)) == Ok)&& (AndVisibleClip() == Ok)) { return status; } }
DoResetClip(); return GenericError; }
/**************************************************************************\
* * Function Description: * * Determine if the specified rect is completely outside the current * clipping region. * * Arguments: * * [IN] rect - the rect to check, in device units * * Return Value: * * TRUE - the rect is completely outside the current clipping region * FALSE - the rect is at least partially visible * * Created: * * 02/05/1999 DCurtis * \**************************************************************************/ BOOL GpGraphics::IsTotallyClipped( GpRect * rect // rect in device units
) const { ASSERT(rect != NULL);
return !(Context->VisibleClip.RectVisible(rect)); }
/**************************************************************************\
* * Function Description: * * Determine if the current clipping is empty or not * * Arguments: * * NONE * * Return Value: * * BOOL - whether or not the current clipping area is empty. * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ BOOL GpGraphics::IsClipEmpty() const { ASSERT(this->IsValid());
GpMatrix identityMatrix; BOOL isEmpty = FALSE;
Context->AppClip.IsEmpty(&identityMatrix, &isEmpty);
return isEmpty; }
/**************************************************************************\
* * Function Description: * * Return a rect with the bounds (in world units) of the current clip region. * * Arguments: * * [OUT] rect - the bounds of the current clip region, in world units * * Return Value: * * NONE * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ VOID GpGraphics::GetClipBounds( GpRectF& rect ) const { ASSERT(this->IsValid());
GpRect deviceRect; GpMatrix identityMatrix;
// We keep AppClip in device units
Context->AppClip.GetBounds(&identityMatrix, &deviceRect);
DeviceToWorldTransformRect(deviceRect, rect); }
/**************************************************************************\
* * Function Description: * * Transform a device units rect to a world units rect. * * Arguments: * * [IN] deviceRect - the bounds in device units * [OUT] rect - the bounds, in world units * * Return Value: * * NONE * * Created: * * 04/07/1999 DCurtis * \**************************************************************************/ VOID GpGraphics::DeviceToWorldTransformRect( const GpRect & deviceRect, GpRectF & rect ) const { if (Context->WorldToDevice.IsIdentity()) { rect.X = LTOF(deviceRect.X); rect.Y = LTOF(deviceRect.Y); rect.Width = LTOF(deviceRect.Width); rect.Height = LTOF(deviceRect.Height); } else { GpMatrix deviceToWorld;
if (GetDeviceToWorldTransform(&deviceToWorld) != Ok) { rect.X = rect.Y = rect.Width = rect.Height = 0; return; }
if (deviceToWorld.IsTranslateScale()) { rect.X = LTOF(deviceRect.X); rect.Y = LTOF(deviceRect.Y); rect.Width = LTOF(deviceRect.Width); rect.Height = LTOF(deviceRect.Height);
deviceToWorld.TransformRect(rect); } else { GpPointF points[4]; REAL left = LTOF(deviceRect.X); REAL top = LTOF(deviceRect.Y); REAL right = LTOF(deviceRect.X + deviceRect.Width); REAL bottom = LTOF(deviceRect.Y + deviceRect.Height);
points[0].X = left; points[0].Y = top; points[1].X = right; points[1].Y = top; points[2].X = right; points[2].Y = bottom; points[3].X = left; points[3].Y = bottom;
deviceToWorld.Transform(points, 4);
REAL value;
left = points[0].X; right = left; top = points[0].Y; bottom = top;
INT count = 3;
do { value = points[count].X;
if (value < left) { left = value; } else if (value > right) { right = value; }
value = points[count].Y;
if (value < top) { top = value; } else if (value > bottom) { bottom = value; } } while (--count > 0);
rect.X = left; rect.Y = top; rect.Width = right - left; rect.Height = bottom - top; } } }
/**************************************************************************\
* * Function Description: * * Return a rect with the bounds (in world units) of the current * visible clip region. * * Arguments: * * [OUT] rect - the bounds of the current clip region, in world units * * Return Value: * * NONE * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ VOID GpGraphics::GetVisibleClipBounds( GpRectF& rect ) const { ASSERT(this->IsValid());
GpRect deviceRect; Context->VisibleClip.GetBounds(&deviceRect);
DeviceToWorldTransformRect(deviceRect, rect); }
/**************************************************************************\
* * Function Description: * * Determine if the current visible clipping is empty or not * * Arguments: * * NONE * * Return Value: * * BOOL - whether or not the current clipping area is empty. * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ BOOL GpGraphics::IsVisibleClipEmpty() const { ASSERT(this->IsValid());
return Context->VisibleClip.IsEmpty(); }
/**************************************************************************\
* * Function Description: * * Determine if the specified point is visible within the current clip region. * * Arguments: * * point - the point to test, in world units. * * Return Value: * * BOOL - whether or not the point is inside the current clipping. * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ BOOL GpGraphics::IsVisible( const GpPointF& point ) const { ASSERT(this->IsValid());
GpPointF pointCopy = point;
Context->WorldToDevice.Transform(&pointCopy);
return Context->VisibleClip.PointInside(GpRound(pointCopy.X), GpRound(pointCopy.Y)); }
/**************************************************************************\
* * Function Description: * * Determine if the specified rect is visible within the current clip region. * * Arguments: * * rect - the rect to test, in world units. * * Return Value: * * BOOL - whether or not the rect is inside/overlaps the current clipping. * * Created: * * 02/09/1999 DCurtis * \**************************************************************************/ BOOL GpGraphics::IsVisible( const GpRectF& rect ) const { ASSERT(this->IsValid());
if (Context->WorldToDevice.IsTranslateScale()) { GpRectF transformedRect = rect;
Context->WorldToDevice.TransformRect(transformedRect);
// use ceiling to match rasterizer
return Context->VisibleClip.RectVisible( GpCeiling(transformedRect.X), GpCeiling(transformedRect.Y), GpCeiling(transformedRect.GetRight()), GpCeiling(transformedRect.GetBottom())); } else { GpRectF bounds; GpRect deviceBounds; GpRect clipBounds;
TransformBounds(&(Context->WorldToDevice), rect.X, rect.Y, rect.GetRight(), rect.GetBottom(), &bounds);
GpStatus status = BoundsFToRect(&bounds, &deviceBounds); Context->VisibleClip.GetBounds(&clipBounds);
// try trivial reject
if (status == Ok && clipBounds.IntersectsWith(deviceBounds)) { // couldn't reject, so do full test
GpRegion region(&rect);
if (region.IsValid() && (region.UpdateDeviceRegion(&(Context->WorldToDevice)) == Ok)) { return Context->VisibleClip.RegionVisible(®ion.DeviceRegion); } } } return FALSE; }
|