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
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, ®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;
|
|
}
|
|
}
|