Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2661 lines
64 KiB

/**************************************************************************\
*
* 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, &regionRight);
}
else
{
status = CreateDeviceRegion(regionDataRight, &regionRight);
}
if (status == Ok)
{
switch (regionData->Type)
{
case TypeAnd:
status = region->And(&regionRight);
break;
case TypeOr:
status = region->Or(&regionRight);
break;
case TypeXor:
status = region->Xor(&regionRight);
break;
case TypeExclude:
status = region->Exclude(&regionRight);
break;
case TypeComplement:
status = region->Complement(&regionRight);
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(&regionBounds);
// 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(&region);
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(&regionRecordData, 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(&regionData->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;
}
}