|
|
/**************************************************************************\
* * Copyright (c) 1999 Microsoft Corporation * * Module Name: * * Region.cpp * * Abstract: * * Implementation of GpRegion class * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/
#include "precomp.hpp"
#define COMBINE_STEP_SIZE 4 // takes 2 for each combine operation
LONG_PTR GpObject::Uniqueness = (0xdbc - 1); // for setting Uid of Objects
/**************************************************************************\
* * Function Description: * * Default constructor. Sets the default state of the region to * be infinite. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpRegion::GpRegion() { SetValid(TRUE); // default is valid
// Default is infinite
RegionOk = TRUE;
Type = TypeInfinite; }
/**************************************************************************\
* * Function Description: * * Constructor. Sets the region to the specified rect. * * Arguments: * * [IN] rect - rect to initialize the region to * * Return Value: * * NONE * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpRegion::GpRegion( const GpRectF * rect ) { ASSERT(rect != NULL);
SetValid(TRUE); // default is valid
RegionOk = FALSE;
X = rect->X; Y = rect->Y; Width = rect->Width; Height = rect->Height; Type = TypeRect; }
/**************************************************************************\
* * Function Description: * * Constructor. Sets the region to a copy of the specified path. * * Arguments: * * [IN] path - path to initialize the region to * * Return Value: * * NONE * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpRegion::GpRegion( const GpPath * path ) { ASSERT(path != NULL);
SetValid(TRUE); // default is valid
RegionOk = FALSE;
Lazy = FALSE; Path = path->Clone(); Type = (Path != NULL) ? TypePath : TypeNotValid; }
/**************************************************************************\
* * Function Description: * * Constructor. Sets the region using the specified region data buffer. * * Arguments: * * [IN] regionDataBuffer - should contain data that describes the region * * Return Value: * * NONE * * Created: * * 9/3/1999 DCurtis * \**************************************************************************/ GpRegion::GpRegion( const BYTE * regionDataBuffer, UINT size ) { ASSERT(regionDataBuffer != NULL);
SetValid(TRUE); // default is valid
RegionOk = FALSE; Type = TypeEmpty; // so FreePathData works correctly
if (this->SetExternalData(regionDataBuffer, size) != Ok) { Type = TypeNotValid; } }
/**************************************************************************\
* * Function Description: * * Constructor. Sets the region to a copy of the specified path. * * Arguments: * * [IN] region - region to initialize the region to * * Return Value: * * NONE * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpRegion::GpRegion( const GpRegion * region, BOOL lazy ) { SetValid(TRUE); // default is valid
RegionOk = FALSE;
// We set the type here to avoid the assert in GpRegion::Set when the
// uninitialized Type is equal to TypeNotValid
Type = TypeEmpty;
Set(region, lazy); }
/**************************************************************************\
* * Function Description: * * Destructor. Frees any copied path data associated with the region. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpRegion::~GpRegion() { FreePathData(); }
/**************************************************************************\
* * Function Description: * * When a region is created from a path, a copy of that path is stored in * the region. This method frees up any of those copies that have been * saved in the region. * * It also resets the CombineData back to having no children. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ VOID GpRegion::FreePathData() { if (Type == TypePath) { if (!Lazy) { delete Path; } } else { INT count = CombineData.GetCount();
if (count > 0) { RegionData * data = CombineData.GetDataBuffer(); ASSERT (data != NULL);
do { if ((data->Type == TypePath) && (!data->Lazy)) { delete data->Path; } data++;
} while (--count > 0); } CombineData.Reset(); } }
/**************************************************************************\
* * Function Description: * * Set the region to the specified rectangle. * * Arguments: * * [IN] rect - the rect, in world units * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ VOID GpRegion::Set( REAL x, REAL y, REAL width, REAL height ) { ASSERT(IsValid());
// handle flipped rects
if (width < 0) { x += width; width = -width; } if (height < 0) { y += height; height = -height; }
// crop to infinity
if (x < INFINITE_MIN) { if (width < INFINITE_SIZE) { width -= (INFINITE_MIN - x); } x = INFINITE_MIN; } if (y < INFINITE_MIN) { if (height < INFINITE_SIZE) { height -= (INFINITE_MIN - y); } y = INFINITE_MIN; }
if ((width > REAL_EPSILON) && (height > REAL_EPSILON)) { if (width >= INFINITE_SIZE) { if (height >= INFINITE_SIZE) { SetInfinite(); return; } width = INFINITE_SIZE; // crop to infinite
} else if (height > INFINITE_SIZE) { height = INFINITE_SIZE; // crop to infinite
}
UpdateUid(); if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); } FreePathData();
X = x; Y = y; Width = width; Height = height; Type = TypeRect;
return; } else { SetEmpty(); } }
/**************************************************************************\
* * Function Description: * * Set the region to be infinite. * * Arguments: * * NONE * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/9/1999 DCurtis * \**************************************************************************/ VOID GpRegion::SetInfinite() { ASSERT(IsValid());
UpdateUid(); DeviceRegion.SetInfinite(); RegionOk = TRUE;
FreePathData();
X = INFINITE_MIN; Y = INFINITE_MIN; Width = INFINITE_SIZE; Height = INFINITE_SIZE; Type = TypeInfinite;
return; }
/**************************************************************************\
* * Function Description: * * Set the region to be empty. * * Arguments: * * NONE * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/9/1999 DCurtis * \**************************************************************************/ VOID GpRegion::SetEmpty() { ASSERT(IsValid());
UpdateUid(); DeviceRegion.SetEmpty(); RegionOk = TRUE;
FreePathData();
X = 0; Y = 0; Width = 0; Height = 0; Type = TypeEmpty;
return; }
/**************************************************************************\
* * Function Description: * * Set the region to the specified path. * * Arguments: * * [IN] path - the path, in world units * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::Set( const GpPath * path ) { ASSERT(IsValid()); ASSERT(path != NULL);
UpdateUid(); if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); } FreePathData();
Lazy = FALSE; Path = path->Clone(); if (Path != NULL) { Type = TypePath; return Ok; } Type = TypeNotValid; return GenericError; }
/**************************************************************************\
* * Function Description: * * Set the region to be a copy of the specified region. * * Arguments: * * [IN] region - the region to copy * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::Set( const GpRegion * region, BOOL lazy ) { ASSERT(IsValid()); ASSERT((region != NULL) && (region->IsValid()));
if (region == this) { return Ok; }
UpdateUid(); if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); } FreePathData();
if ((region->Type & REGIONTYPE_LEAF) != 0) { *this = *(const_cast<GpRegion *>(region)); if (Type == TypePath) { if (!lazy) { Lazy = FALSE; if ((Path = Path->Clone()) == NULL) { Type = TypeNotValid; return GenericError; } } else // lazy copy
{ Lazy = TRUE; } } return Ok; } else { INT count = region->CombineData.GetCount();
ASSERT(count > 0);
Type = TypeNotValid;
RegionData * data = CombineData.AddMultiple(count); if (data != NULL) { BOOL error = FALSE;
GpMemcpy (data, region->CombineData.GetDataBuffer(), count * sizeof(*data));
while (count--) { if (data->Type == TypePath) { if (!lazy) { data->Lazy = FALSE; if ((data->Path = data->Path->Clone()) == NULL) { data->Type = TypeNotValid; error = TRUE; // don't break out or else FreePathData will free
// paths that don't belong to us.
} } else // lazy copy
{ data->Lazy = TRUE; } } data++; } if (!error) { Type = region->Type; Left = region->Left; Right = region->Right; return Ok; } FreePathData(); } } return GenericError; }
/**************************************************************************\
* * Function Description: * * Combine the region with the specified rect, using the boolean * operator specified by the type. * * Arguments: * * [IN] rect - the rect to combine with the current region * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::Combine( const GpRectF * rect, CombineMode combineMode ) { ASSERT(IsValid()); ASSERT(rect != NULL); ASSERT(CombineModeIsValid(combineMode));
if (combineMode == CombineModeReplace) { this->Set(rect); return Ok; }
if (Type == TypeInfinite) { if (combineMode == CombineModeIntersect) { this->Set(rect); return Ok; } else if (combineMode == CombineModeUnion) { return Ok; // nothing to do, already infinite
} else if (combineMode == CombineModeComplement) { this->SetEmpty(); return Ok; } } else if (Type == TypeEmpty) { if ((combineMode == CombineModeUnion) || (combineMode == CombineModeXor) || (combineMode == CombineModeComplement)) { this->Set(rect); } // if combineMode is Intersect or Exclude, just leave it empty
return Ok; }
// Now we know this region is not empty
REAL x = rect->X; REAL y = rect->Y; REAL width = rect->Width; REAL height = rect->Height; // handle flipped rects
if (width < 0) { x += width; width = -width; } if (height < 0) { y += height; height = -height; }
// crop to infinity
if (x < INFINITE_MIN) { if (width < INFINITE_SIZE) { width -= (INFINITE_MIN - x); } x = INFINITE_MIN; } if (y < INFINITE_MIN) { if (height < INFINITE_SIZE) { height -= (INFINITE_MIN - y); } y = INFINITE_MIN; }
BOOL isEmptyRect = ((width <= REAL_EPSILON) || (height <= REAL_EPSILON)); if (isEmptyRect) { if ((combineMode == CombineModeIntersect) || (combineMode == CombineModeComplement)) { SetEmpty(); } // if combineMode is Union or Xor or Exclude, just leave it alone
return Ok; }
// Now we know the rect is not empty
// See if the rect is infinite
if (width >= INFINITE_SIZE) { if (height >= INFINITE_SIZE) { GpRegion infiniteRegion; return this->Combine(&infiniteRegion, combineMode); } width = INFINITE_SIZE; // crop to infinite
} else if (height > INFINITE_SIZE) { height = INFINITE_SIZE; // crop to infinite
}
// The rect is neither infinite nor empty
UpdateUid(); if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); }
INT index = CombineData.GetCount(); RegionData * data = CombineData.AddMultiple(2);
if (data != NULL) { data[0] = *this;
data[1].Type = TypeRect; data[1].X = x; data[1].Y = y; data[1].Width = width; data[1].Height = height;
Type = (NodeType)combineMode; Left = index; Right = index + 1; return Ok; }
FreePathData(); Type = TypeNotValid; return GenericError; }
/**************************************************************************\
* * Function Description: * * Combine the region with the specified path, using the boolean * operator specified by the type. * * Arguments: * * [IN] path - the path to combine with the current region * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::Combine( const GpPath * path, CombineMode combineMode ) { ASSERT(IsValid()); ASSERT(path != NULL); ASSERT(CombineModeIsValid(combineMode));
if (combineMode == CombineModeReplace) { return this->Set(path); }
if (Type == TypeInfinite) { if (combineMode == CombineModeIntersect) { this->Set(path); return Ok; } else if (combineMode == CombineModeUnion) { return Ok; // nothing to do, already infinite
} else if (combineMode == CombineModeComplement) { this->SetEmpty(); return Ok; } } else if (Type == TypeEmpty) { if ((combineMode == CombineModeUnion) || (combineMode == CombineModeXor) || (combineMode == CombineModeComplement)) { this->Set(path); } // if combineMode is Intersect or Exclude, just leave it empty
return Ok; }
// Now we know this region is not empty
if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); }
GpPath * pathCopy = path->Clone(); if (pathCopy != NULL) { INT index = CombineData.GetCount(); RegionData * data = CombineData.AddMultiple(2);
if (data != NULL) { data[0] = *this;
data[1].Type = TypePath; data[1].Lazy = FALSE; data[1].Path = pathCopy;
Type = (NodeType)combineMode; Left = index; Right = index + 1; UpdateUid(); return Ok; } delete pathCopy; } FreePathData(); Type = TypeNotValid; return GenericError; }
/**************************************************************************\
* * Function Description: * * Combine the region with the specified region, using the boolean * operator specified by the type. * * Arguments: * * [IN] region - the region to combine with the current region * [IN] combineMode - the combine operator (and, or, xor, exclude, complement) * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::Combine( GpRegion * region, CombineMode combineMode ) { ASSERT(IsValid()); ASSERT((region != NULL) && region->IsValid()); ASSERT(CombineModeIsValid(combineMode));
if (combineMode == CombineModeReplace) { return this->Set(region); }
if (region->Type == TypeEmpty) { if ((combineMode == CombineModeIntersect) || (combineMode == CombineModeComplement)) { SetEmpty(); } // if combineMode is Union or Xor or Exclude, just leave it alone
return Ok; }
// Now we know the input region is not empty
if (region->Type == TypeInfinite) { if (combineMode == CombineModeIntersect) { return Ok; } else if (combineMode == CombineModeUnion) { SetInfinite(); return Ok; } else if ((combineMode == CombineModeXor) || (combineMode == CombineModeComplement)) { if (Type == TypeInfinite) { SetEmpty(); return Ok; } } if (combineMode == CombineModeExclude) { SetEmpty(); return Ok; } }
if (Type == TypeInfinite) { if (combineMode == CombineModeIntersect) { this->Set(region); return Ok; } else if (combineMode == CombineModeUnion) { return Ok; // nothing to do, already infinite
} else if (combineMode == CombineModeComplement) { this->SetEmpty(); return Ok; } } else if (Type == TypeEmpty) { if ((combineMode == CombineModeUnion) || (combineMode == CombineModeXor) || (combineMode == CombineModeComplement)) { this->Set(region); } // if combineMode is Intersect or Exclude, just leave it empty
return Ok; }
// Now we know this region is not empty
if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); }
INT regionCount = region->CombineData.GetCount(); INT index = CombineData.GetCount(); RegionData * data = CombineData.AddMultiple(2 + regionCount);
if (data != NULL) { data[regionCount] = *this; data[regionCount + 1] = *region;
if (regionCount > 0) { RegionData * srcData = region->CombineData.GetDataBuffer(); INT i = 0; BOOL error = FALSE; GpPath * path;
do { data[i] = srcData[i]; if ((data[i].Type & REGIONTYPE_LEAF) == 0) { data[i].Left += index; data[i].Right += index; } else if (data[i].Type == TypePath) { data[i].Lazy = FALSE; path = data[i].Path->Clone(); data[i].Path = path; if (path == NULL) { data[i].Type = TypeNotValid; error = TRUE; // don't break out
} }
} while (++i < regionCount); data[regionCount+1].Left += index; data[regionCount+1].Right += index; index += regionCount; if (error) { goto ErrorExit; } } else if (region->Type == TypePath) { data[1].Lazy = FALSE; data[1].Path = region->Path->Clone(); if (data[1].Path == NULL) { data[1].Type = TypeNotValid; goto ErrorExit; } }
Type = (NodeType)combineMode; Left = index; Right = index + 1; UpdateUid(); return Ok; } ErrorExit: FreePathData(); Type = TypeNotValid; return GenericError; }
GpStatus GpRegion::CreateLeafDeviceRegion( const RegionData * regionData, DpRegion * region ) const { GpStatus status = GenericError;
switch (regionData->Type) { case TypeRect: if ((regionData->Width > 0) && (regionData->Height > 0)) { // If the transform is a simple scaling transform, life is a
// little easier:
if (Matrix.IsTranslateScale()) { GpRectF rect(regionData->X, regionData->Y, regionData->Width, regionData->Height);
Matrix.TransformRect(rect);
// Use ceiling to stay compatible with rasterizer
// Don't take the ceiling of the width directly,
// because it introduces additional round-off error.
// For example, if rect.X is 1.7 and rect.Width is 47.2,
// then if we took the ceiling of the width, the right
// coordinate will end up being 50, instead of 49.
INT xMin = RasterizerCeiling(rect.X); INT yMin = RasterizerCeiling(rect.Y); INT xMax = RasterizerCeiling(rect.GetRight()); INT yMax = RasterizerCeiling(rect.GetBottom());
region->Set(xMin, yMin, xMax - xMin, yMax - yMin); status = Ok; } else { GpPointF points[4]; REAL left; REAL right; REAL top; REAL bottom;
left = regionData->X; top = regionData->Y; right = regionData->X + regionData->Width; bottom = regionData->Y + regionData->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;
const INT stackCount = 4; GpPointF stackPoints[stackCount]; BYTE stackTypes[stackCount];
GpPath path(points, 4, stackPoints, stackTypes, stackCount, FillModeAlternate, DpPath::Convex);
if (path.IsValid()) { status = region->Set(&path, &Matrix); } } } else { region->SetEmpty(); status = Ok; } break;
case TypePath: status = region->Set(regionData->Path, &Matrix); break;
case TypeEmpty: region->SetEmpty(); status = Ok; break;
case TypeInfinite: region->SetInfinite(); status = Ok; break;
default: ASSERT(0); break; } return status; }
/**************************************************************************\
* * Function Description: * * Creates a DpRegion (device coordinate region) using the data in the * specified RegionData node and using the current transformation matrix. * This may involve creating a region for children nodes and then combining * the children into a single device region. * * Arguments: * * [IN] regionData - the world coordinate region to convert to device region * [OUT] region - the created/combined device region * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::CreateDeviceRegion( const RegionData * regionData, DpRegion * region ) const { ASSERT(IsValid());
GpStatus status; RegionData * regionDataLeft;
regionDataLeft = &(CombineData[regionData->Left]);
if ((regionDataLeft->Type & REGIONTYPE_LEAF) != 0) { status = CreateLeafDeviceRegion(regionDataLeft, region); } else { status = CreateDeviceRegion(regionDataLeft, region); }
if (status == Ok) { DpRegion regionRight; RegionData * regionDataRight;
regionDataRight = &(CombineData[regionData->Right]);
if ((regionDataRight->Type & REGIONTYPE_LEAF) != 0) { status = CreateLeafDeviceRegion(regionDataRight, ®ionRight); } else { status = CreateDeviceRegion(regionDataRight, ®ionRight); }
if (status == Ok) { switch (regionData->Type) { case TypeAnd: status = region->And(®ionRight); break;
case TypeOr: status = region->Or(®ionRight); break;
case TypeXor: status = region->Xor(®ionRight); break;
case TypeExclude: status = region->Exclude(®ionRight); break;
case TypeComplement: status = region->Complement(®ionRight); break;
default: ASSERT(0); break; } } } return status; }
/**************************************************************************\
* * Function Description: * * Checks if the current DeviceRegion is up-to-date with the specified * matrix. If not, then it recreates the DeviceRegion using the matrix. * * Arguments: * * [IN] matrix - the world-to-device transformation matrix * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::UpdateDeviceRegion( GpMatrix * matrix ) const { ASSERT(IsValid());
if (RegionOk && matrix->IsEqual(&Matrix)) { return Ok; } Matrix = *matrix;
GpStatus status;
if ((this->Type & REGIONTYPE_LEAF) != 0) { status = CreateLeafDeviceRegion(this, &DeviceRegion); } else { status = CreateDeviceRegion(this, &DeviceRegion); } RegionOk = (status == Ok); return status; }
/**************************************************************************\
* * Function Description: * * Get the bounds of the region, in world units. * * Arguments: * * [IN] matrix - world-to-device transformation matrix * [OUT] bounds - bounding rect of region, in world units * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::GetBounds( GpGraphics * graphics, GpRectF * bounds, BOOL device ) const { ASSERT((graphics != NULL) && (bounds != NULL)); ASSERT(IsValid() && graphics->IsValid());
// Note we can't lock graphics, cause it gets locked in its calls
GpStatus status = Ok;
switch (Type) { case TypeRect: if (!device) { bounds->X = X; bounds->Y = Y; bounds->Width = Width; bounds->Height = Height; } else { GpMatrix worldToDevice;
graphics->GetWorldToDeviceTransform(&worldToDevice);
TransformBounds(&worldToDevice, X, Y, X + Width, Y + Height,bounds); } break;
case TypePath: { GpMatrix worldToDevice;
if (device) { graphics->GetWorldToDeviceTransform(&worldToDevice); } // else leave it as identity
Path->GetBounds(bounds, &worldToDevice); } break;
case TypeInfinite: bounds->X = INFINITE_MIN; bounds->Y = INFINITE_MIN; bounds->Width = INFINITE_SIZE; bounds->Height = INFINITE_SIZE; break;
case TypeAnd: case TypeOr: case TypeXor: case TypeExclude: case TypeComplement: { GpMatrix worldToDevice;
graphics->GetWorldToDeviceTransform(&worldToDevice);
if (UpdateDeviceRegion(&worldToDevice) == Ok) { GpRect deviceBounds;
DeviceRegion.GetBounds(&deviceBounds);
if (device) { bounds->X = TOREAL(deviceBounds.X); bounds->Y = TOREAL(deviceBounds.Y); bounds->Width = TOREAL(deviceBounds.Width); bounds->Height = TOREAL(deviceBounds.Height); break; } else { GpMatrix deviceToWorld;
if (graphics->GetDeviceToWorldTransform(&deviceToWorld)==Ok) { TransformBounds( &deviceToWorld, TOREAL(deviceBounds.X), TOREAL(deviceBounds.Y), TOREAL(deviceBounds.X + deviceBounds.Width), TOREAL(deviceBounds.Y + deviceBounds.Height), bounds); break; } } } } status = GenericError; // FALLTHRU
default: // TypeEmpty
bounds->X = 0; bounds->Y = 0; bounds->Width = 0; bounds->Height = 0; break; } return status; }
/**************************************************************************\
* * Function Description: * * Get the bounds of the region, in device units. * * Arguments: * * [IN] matrix - world-to-device transformation matrix * [OUT] bounds - bounding rect of region, in device units * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::GetBounds( GpMatrix * matrix, GpRect * bounds ) const { ASSERT(IsValid()); ASSERT((matrix != NULL) && (bounds != NULL));
GpStatus status = Ok;
switch (Type) { case TypeInfinite: bounds->X = INFINITE_MIN; bounds->Y = INFINITE_MIN; bounds->Width = INFINITE_SIZE; bounds->Height = INFINITE_SIZE; break;
default: if (UpdateDeviceRegion(matrix) == Ok) { DeviceRegion.GetBounds(bounds); break; } status = GenericError; // FALLTHRU
case TypeEmpty: bounds->X = 0; bounds->Y = 0; bounds->Width = 0; bounds->Height = 0; break; } return status; }
/**************************************************************************\
* * Function Description: * * Get the HRGN corresponding to the region * * Arguments: * * [IN] graphics - a reference graphics for conversion to device units * (can be NULL) * [OUT] hRgn - the GDI region * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 7/6/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::GetHRgn( GpGraphics * graphics, HRGN * hRgn ) const { ASSERT(IsValid()); ASSERT(hRgn != NULL);
GpMatrix worldToDevice;
if (graphics != NULL) { graphics->GetWorldToDeviceTransform(&worldToDevice); }
if (UpdateDeviceRegion(&worldToDevice) == Ok) { if ((*hRgn = DeviceRegion.GetHRgn()) != (HRGN)INVALID_HANDLE_VALUE) { return Ok; } } else { *hRgn = (HRGN)INVALID_HANDLE_VALUE; } return GenericError; }
GpStatus GpRegion::GetRegionScans( GpRect * rects, INT * count, const GpMatrix * matrix ) const { ASSERT(IsValid()); ASSERT(count != NULL); ASSERT(matrix != NULL);
if (UpdateDeviceRegion(const_cast<GpMatrix*>(matrix)) == Ok) { *count = DeviceRegion.GetRects(rects); return Ok; } else { *count = 0; } return GenericError; }
GpStatus GpRegion::GetRegionScans( GpRectF * rects, INT * count, const GpMatrix * matrix ) const { ASSERT(IsValid()); ASSERT(count != NULL); ASSERT(matrix != NULL);
if (UpdateDeviceRegion(const_cast<GpMatrix*>(matrix)) == Ok) { *count = DeviceRegion.GetRects(rects); return Ok; } else { *count = 0; } return GenericError; }
/**************************************************************************\
* * Function Description: * * Determine if the specified point is visible (inside) the region. * * Arguments: * * [IN] point - the point, in world units * [IN] matrix - the world-to-device transformation matrix to use * [OUT] isVisible - if the point is visible or not * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::IsVisible ( GpPointF * point, GpMatrix * matrix, BOOL * isVisible ) const { ASSERT(IsValid()); ASSERT(matrix != NULL); ASSERT(point != NULL); ASSERT(isVisible != NULL);
if (UpdateDeviceRegion(matrix) == Ok) { GpPointF transformedPoint = *point;
matrix->Transform(&transformedPoint);
*isVisible = DeviceRegion.PointInside(GpRound(transformedPoint.X), GpRound(transformedPoint.Y)); return Ok; }
*isVisible = FALSE; return GenericError; }
/**************************************************************************\
* * Function Description: * * Determine if the specified rect is inside or overlaps the region. * * Arguments: * * [IN] rect - the rect, in world units * [IN] matrix - the world-to-device transformation matrix to use * [OUT] isVisible - if the rect is visible or not * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::IsVisible( GpRectF * rect, GpMatrix * matrix, BOOL * isVisible ) const { ASSERT(IsValid()); ASSERT(matrix != NULL); ASSERT(rect != NULL);
if (UpdateDeviceRegion(matrix) == Ok) { // If the transform is a simple scaling transform, life is a
// little easier:
if (Matrix.IsTranslateScale()) { GpRectF transformRect(*rect);
Matrix.TransformRect(transformRect);
// Use ceiling to stay compatible with rasterizer
INT x = GpCeiling(transformRect.X); INT y = GpCeiling(transformRect.Y);
*isVisible = DeviceRegion.RectVisible( x, y, x + GpCeiling(transformRect.Width), y + GpCeiling(transformRect.Height)); return Ok; } else { REAL left = rect->X; REAL top = rect->Y; REAL right = rect->X + rect->Width; REAL bottom = rect->Y + rect->Height; GpRectF bounds; GpRect deviceBounds; GpRect regionBounds;
TransformBounds(matrix, left, top, right, bottom, &bounds); GpStatus status = BoundsFToRect(&bounds, &deviceBounds); DeviceRegion.GetBounds(®ionBounds);
// try trivial reject
if (status != Ok || !regionBounds.IntersectsWith(deviceBounds)) { *isVisible = FALSE; return status; }
// couldn't reject, so do full test
GpPointF points[4];
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;
const INT stackCount = 4; GpPointF stackPoints[stackCount]; BYTE stackTypes[stackCount];
GpPath path(points, 4, stackPoints, stackTypes, stackCount, FillModeAlternate, DpPath::Convex);
if (path.IsValid()) { DpRegion region(&path, matrix);
if (region.IsValid()) { *isVisible = DeviceRegion.RegionVisible(®ion); return Ok; } } } } *isVisible = FALSE; return GenericError; }
/**************************************************************************\
* * Function Description: * * Determine if the specified region is inside or overlaps the region. * * Arguments: * * [IN] region - the region * [IN] matrix - the world-to-device transformation matrix to use * [OUT] isVisible - if the region is visible or not * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::IsVisible( GpRegion * region, GpMatrix * matrix, BOOL * isVisible ) const { ASSERT(IsValid()); ASSERT(matrix != NULL); ASSERT((region != NULL) && (region->IsValid()));
if ((UpdateDeviceRegion(matrix) == Ok) && (region->UpdateDeviceRegion(matrix) == Ok)) { *isVisible = DeviceRegion.RegionVisible(&(region->DeviceRegion)); return Ok; } *isVisible = FALSE; return GenericError; }
/**************************************************************************\
* * Function Description: * * Determine if the region is empty, i.e. if it has no coverage area. * * Arguments: * * [IN] matrix - the world-to-device transformation matrix to use * [OUT] isEmpty - if the region is empty or not * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 01/06/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::IsEmpty( GpMatrix * matrix, BOOL * isEmpty ) const { ASSERT(IsValid()); ASSERT(matrix != NULL);
if (Type == TypeEmpty) { *isEmpty = TRUE; return Ok; }
if (UpdateDeviceRegion(matrix) == Ok) { *isEmpty = DeviceRegion.IsEmpty(); return Ok; } *isEmpty = FALSE; return GenericError; }
/**************************************************************************\
* * Function Description: * * Determine if the region is infinite, i.e. if it has infinite coverage area. * * Arguments: * * [IN] matrix - the world-to-device transformation matrix to use * [OUT] isInfinite - if the region is infinite or not * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 01/06/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::IsInfinite( GpMatrix * matrix, BOOL * isInfinite ) const { ASSERT(IsValid()); ASSERT(matrix != NULL);
if (Type == TypeInfinite) { *isInfinite = TRUE; return Ok; }
// We have this here for cases like the following:
// This region was OR'ed with another region that was infinite.
// We wouldn't know this region was infinite now without checking
// the device region.
if (UpdateDeviceRegion(matrix) == Ok) { *isInfinite = DeviceRegion.IsInfinite(); return Ok; } *isInfinite = FALSE; return GenericError; }
/**************************************************************************\
* * Function Description: * * Determine if the specified region is equal, in coverage area, to * this region. * * Arguments: * * [IN] region - the region to check equality with * [IN] matrix - the world-to-device transformation matrix to use * [OUT] isEqual - if the regions are equal or not * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 01/06/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::IsEqual( GpRegion * region, GpMatrix * matrix, BOOL * isEqual ) const { ASSERT(IsValid()); ASSERT(matrix != NULL); ASSERT((region != NULL) && (region->IsValid()));
if ((UpdateDeviceRegion(matrix) == Ok) && (region->UpdateDeviceRegion(matrix) == Ok)) { *isEqual = DeviceRegion.IsEqual(&(region->DeviceRegion)); return Ok; } *isEqual = FALSE; return GenericError; }
/**************************************************************************\
* * Function Description: * * Translate (offset) the region by the specified delta/offset values. * * Arguments: * * [IN] xOffset - amount to offset in X (world units) * [IN] yOffset - amount to offset in Y * * Return Value: * * NONE * * Created: * * 01/06/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::Offset( REAL xOffset, REAL yOffset ) { ASSERT(IsValid());
if ((xOffset == 0) && (yOffset == 0)) { return Ok; }
// Note that if performance is a problem, there's lots we could do here.
// For example, we could keep track of the offset, and only apply it
// when updating the device region. We could even avoid re-rasterizing
// the device region.
switch (Type) { case TypeEmpty: case TypeInfinite: return Ok; // do nothing
case TypeRect: UpdateUid(); X += xOffset; Y += yOffset; break;
case TypePath: UpdateUid(); if (Lazy) { Path = Path->Clone(); Lazy = FALSE; if (Path == NULL) { Type = TypeNotValid; return GenericError; } } Path->Offset(xOffset, yOffset); break;
default: UpdateUid(); { INT count = CombineData.GetCount(); RegionData * data = CombineData.GetDataBuffer(); NodeType type;
ASSERT ((count > 0) && (data != NULL));
do { type = data->Type;
if (type == TypeRect) { data->X += xOffset; data->Y += yOffset; } else if (type == TypePath) { if (data->Lazy) { data->Path = data->Path->Clone(); data->Lazy = FALSE; if (data->Path == NULL) { data->Type = TypeNotValid; FreePathData(); Type = TypeNotValid; return GenericError; } } data->Path->Offset(xOffset, yOffset); } data++;
} while (--count > 0); } break; }
if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); } return Ok; }
/**************************************************************************\
* * Function Description: * * Transform a leaf node by the specified matrix. This could result in a * rect being converted into a path. Ignores non-leaf nodes. No reason * to transform empty/infinite nodes. * * Arguments: * * [IN] matrix - the transformation matrix to apply * [IN/OUT] data - the node to transform * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/08/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::TransformLeaf( GpMatrix * matrix, RegionData * data ) { switch (data->Type) { // case TypeEmpty:
// case TypeInfinite:
// case TypeAnd, TypeOr, TypeXor, TypeExclude, TypeComplement:
default: return Ok; // do nothing
case TypeRect: { if (matrix->IsTranslateScale()) { GpRectF rect(data->X, data->Y, data->Width, data->Height); matrix->TransformRect(rect);
data->X = rect.X; data->Y = rect.Y; data->Width = rect.Width; data->Height = rect.Height;
return Ok; } else { GpPath * path = new GpPath(FillModeAlternate);
if (path != NULL) { if (path->IsValid()) { GpPointF points[4]; REAL left; REAL right; REAL top; REAL bottom;
left = data->X; top = data->Y; right = data->X + data->Width; bottom = data->Y + data->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;
matrix->Transform(points, 4);
if (path->AddLines(points, 4) == Ok) { data->Path = path; data->Lazy = FALSE; data->Type = TypePath; return Ok; } } delete path; } data->Type = TypeNotValid; } } return GenericError;
case TypePath: if (data->Lazy) { data->Path = data->Path->Clone(); data->Lazy = FALSE; if (data->Path == NULL) { data->Type = TypeNotValid; return GenericError; } } data->Path->Transform(matrix); return Ok; } }
/**************************************************************************\
* * Function Description: * * Transform a region with the specified matrix. * * Arguments: * * [IN] matrix - the transformation matrix to apply * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 02/08/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::Transform( GpMatrix * matrix ) { ASSERT(IsValid());
if (matrix->IsIdentity() || (Type == TypeInfinite) || (Type == TypeEmpty)) { return Ok; }
UpdateUid(); if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); }
if ((Type & REGIONTYPE_LEAF) != 0) { return TransformLeaf(matrix, this); } else { BOOL error = FALSE; INT count = CombineData.GetCount(); RegionData * data = CombineData.GetDataBuffer();
ASSERT((count > 0) && (data != NULL));
do { error |= (TransformLeaf(matrix, data++) != Ok);
} while (--count > 0);
if (!error) { return Ok; } }
FreePathData(); Type = TypeNotValid; return GenericError; }
class RegionRecordData : public ObjectData { public: INT32 NodeCount; };
GpStatus GpRegion::SetData( const BYTE * dataBuffer, UINT size ) { if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; }
return this->Set(dataBuffer, size); }
GpStatus GpRegion::Set( const BYTE * regionDataBuffer, // NULL means set to empty
UINT regionDataSize ) { GpStatus status = Ok;
if (regionDataBuffer != NULL) { if (regionDataSize < sizeof(RegionRecordData)) { WARNING(("size too small")); status = InsufficientBuffer; goto SetEmptyRegion; }
if (!((RegionRecordData *)regionDataBuffer)->MajorVersionMatches()) { WARNING(("Version number mismatch")); status = InvalidParameter; goto SetEmptyRegion; }
UpdateUid(); if (RegionOk) { RegionOk = FALSE; DeviceRegion.SetEmpty(); } FreePathData();
RegionData * regionDataArray = NULL; INT nodeCount = ((RegionRecordData *)regionDataBuffer)->NodeCount;
if (nodeCount > 0) { regionDataArray = CombineData.AddMultiple(nodeCount); if (regionDataArray == NULL) { Type = TypeNotValid; return OutOfMemory; } } regionDataBuffer += sizeof(RegionRecordData); regionDataSize -= sizeof(RegionRecordData);
INT nextArrayIndex = 0; status = SetRegionData(regionDataBuffer, regionDataSize, this, regionDataArray, nextArrayIndex, nodeCount); if (status == Ok) { ASSERT(nextArrayIndex == nodeCount); return Ok; } Type = TypeNotValid; return status; } SetEmptyRegion: SetEmpty(); return status; }
GpStatus GpRegion::SetRegionData( const BYTE * & regionDataBuffer, UINT & regionDataSize, RegionData * regionData, RegionData * regionDataArray, INT & nextArrayIndex, INT arraySize ) { for (;;) { if (regionDataSize < sizeof(INT32)) { WARNING(("size too small")); return InsufficientBuffer; }
regionData->Type = (NodeType)(((INT32 *)regionDataBuffer)[0]); regionDataBuffer += sizeof(INT32); regionDataSize -= sizeof(INT32);
if ((regionData->Type & REGIONTYPE_LEAF) != 0) { switch (regionData->Type) { case TypeRect: if (regionDataSize < (4 * sizeof(REAL))) { WARNING(("size too small")); return InsufficientBuffer; }
regionData->X = ((REAL *)regionDataBuffer)[0]; regionData->Y = ((REAL *)regionDataBuffer)[1]; regionData->Width = ((REAL *)regionDataBuffer)[2]; regionData->Height = ((REAL *)regionDataBuffer)[3];
regionDataBuffer += (4 * sizeof(REAL)); regionDataSize -= (4 * sizeof(REAL)); break;
case TypePath: { if (regionDataSize < sizeof(INT32)) { WARNING(("size too small")); return InsufficientBuffer; }
GpPath * path = new GpPath(); UINT pathSize = ((INT32 *)regionDataBuffer)[0];
regionDataBuffer += sizeof(INT32); regionDataSize -= sizeof(INT32);
if (path == NULL) { return OutOfMemory; }
UINT tmpPathSize = pathSize;
if ((path->SetData(regionDataBuffer, tmpPathSize) != Ok) || (!path->IsValid())) { delete path; return InvalidParameter; } regionDataBuffer += pathSize; regionDataSize -= pathSize;
regionData->Path = path; regionData->Lazy = FALSE; } break;
case TypeEmpty: case TypeInfinite: break;
default: ASSERT(0); break; } break; // get out of loop
} else // it's not a leaf node
{ if ((regionDataArray == NULL) || (nextArrayIndex >= arraySize)) { ASSERT(0); return InvalidParameter; } regionData->Left = nextArrayIndex++;
// traverse left
GpStatus status = SetRegionData(regionDataBuffer, regionDataSize, regionDataArray + regionData->Left, regionDataArray, nextArrayIndex, arraySize); if (status != Ok) { return status; }
if (nextArrayIndex >= arraySize) { ASSERT(0); return InvalidParameter; } regionData->Right = nextArrayIndex++;
// traverse right using tail-end recursion
regionData = regionDataArray + regionData->Right; } } return Ok; }
/**************************************************************************\
* * Function Description: * * Serialize all the region data into a single memory buffer. If the * buffer is NULL, just return the number of bytes required in the buffer. * * Arguments: * * [IN] regionDataBuffer - the memory buffer to fill with region data * * Return Value: * * INT - Num Bytes required (or used) to fill with region data * * Created: * * 09/01/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::GetData( IStream * stream ) const { ASSERT (stream != NULL);
RegionRecordData regionRecordData; regionRecordData.NodeCount = CombineData.GetCount(); stream->Write(®ionRecordData, sizeof(regionRecordData), NULL);
return this->GetRegionData(stream, this); }
UINT GpRegion::GetDataSize() const { return sizeof(RegionRecordData) + this->GetRegionDataSize(this); }
/**************************************************************************\
* * Function Description: * * Recurse through the region data structure to determine how many bytes * are required to hold all the region data. * * Arguments: * * [IN] regionData - the region data node to start with * * Return Value: * * INT - the size in bytes from this node down * * Created: * * 09/01/1999 DCurtis * \**************************************************************************/ INT GpRegion::GetRegionDataSize( const RegionData * regionData ) const { INT size = 0;
for (;;) { size += sizeof(INT32); // for the type of this node
if ((regionData->Type & REGIONTYPE_LEAF) != 0) { switch (regionData->Type) { case TypeRect: size += (4 * sizeof(REAL)); // for the rect data
break;
case TypePath: size += sizeof(INT32) + regionData->Path->GetDataSize(); ASSERT((size & 0x03) == 0); break;
case TypeEmpty: case TypeInfinite: break;
default: ASSERT(0); break; } break; // get out of loop
} else // it's not a leaf node
{ // traverse left
size += GetRegionDataSize(&(CombineData[regionData->Left]));
// traverse right using tail-end recursion
regionData = &(CombineData[regionData->Right]); } } return size; }
/**************************************************************************\
* * Function Description: * * Recurse through the region data structure writing each region data * node to the memory buffer. * * Arguments: * * [IN] regionData - the region data node to start with * [IN] regionDataBuffer - the memory buffer to write the data to * * Return Value: * * BYTE * - the next memory location to write to * * Created: * * 09/01/1999 DCurtis * \**************************************************************************/ GpStatus GpRegion::GetRegionData( IStream * stream, const RegionData * regionData ) const { ASSERT(stream != NULL);
GpStatus status = Ok; UINT pathSize; REAL rectBuffer[4];
for (;;) { stream->Write(®ionData->Type, sizeof(INT32), NULL);
if ((regionData->Type & REGIONTYPE_LEAF) != 0) { switch (regionData->Type) { case TypeRect: rectBuffer[0] = regionData->X; rectBuffer[1] = regionData->Y; rectBuffer[2] = regionData->Width; rectBuffer[3] = regionData->Height; stream->Write(rectBuffer, 4 * sizeof(rectBuffer[0]), NULL); break;
case TypePath: pathSize = regionData->Path->GetDataSize(); ASSERT((pathSize & 0x03) == 0); stream->Write(&pathSize, sizeof(INT32), NULL); status = regionData->Path->GetData(stream); break;
case TypeEmpty: case TypeInfinite: break;
default: ASSERT(0); break; } break; // get out of loop
} else // it's not a leaf node
{ // traverse left
status = GetRegionData(stream, &(CombineData[regionData->Left]));
// traverse right using tail-end recursion
regionData = &(CombineData[regionData->Right]); } if (status != Ok) { break; } } return status; }
/**************************************************************************\
* * Function Description: * * Constructor. Sets the region to a copy of the specified path. * * Arguments: * * [IN] path - path to initialize the region to * * Return Value: * * NONE * * Created: * * 2/3/1999 DCurtis * \**************************************************************************/ GpRegion::GpRegion( HRGN hRgn ) { ASSERT(hRgn != NULL);
SetValid(TRUE); // default is valid
RegionOk = FALSE; Lazy = FALSE; Type = TypeNotValid; Path = new GpPath(hRgn);
if (CheckValid(Path)) { Type = TypePath; } }
|