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.
5171 lines
144 KiB
5171 lines
144 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
*
|
|
* DpRegion.cpp
|
|
*
|
|
* Abstract:
|
|
*
|
|
* DpRegion class operates on scan-converted Y spans of rects
|
|
*
|
|
* Created:
|
|
*
|
|
* 12/17/1998 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
//#define DEBUG_REGION 1
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Do a binary search of the horizontal tessellation structure to find
|
|
* the Y Span containing y. If y is inside a Y Span, that span will be
|
|
* returned. If y is inside the region extent but not inside a Y Span,
|
|
* the span whose yMin > y will be returned. If y is less than the region
|
|
* extent, the first Y span will be returned. If y is greater than the
|
|
* region extent, the last Y Span will be returned.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] y - y value to search for
|
|
* [OUT] ySpanFound - y span pointer found by the search
|
|
* [OUT] spanIndexFound - y span index found by the search
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE - found a y span that includes the y value
|
|
* FALSE - the y span does not include the y value
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
BOOL
|
|
DpComplexRegion::YSpanSearch(
|
|
INT y,
|
|
INT ** ySpanFound,
|
|
INT * spanIndexFound
|
|
)
|
|
{
|
|
INT indexMin = 0;
|
|
INT indexMax = NumYSpans - 1;
|
|
INT indexMiddle = YSearchIndex;
|
|
INT * ySpan = GetYSpan (indexMiddle);
|
|
|
|
ASSERT((indexMiddle >= indexMin) && (indexMiddle <= indexMax));
|
|
|
|
for (;;)
|
|
{
|
|
if (y >= ySpan[YSPAN_YMIN])
|
|
{
|
|
if (y < ySpan[YSPAN_YMAX])
|
|
{
|
|
*ySpanFound = ySpan;
|
|
*spanIndexFound = indexMiddle;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// If only 1 span, this could go past the indexMax
|
|
indexMin = indexMiddle + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
indexMax = indexMiddle;
|
|
}
|
|
|
|
if (indexMin >= indexMax)
|
|
{
|
|
ySpan = GetYSpan (indexMax);
|
|
*ySpanFound = ySpan;
|
|
*spanIndexFound = indexMax;
|
|
return (y >= ySpan[YSPAN_YMIN]) && (y < ySpan[YSPAN_YMAX]);
|
|
}
|
|
|
|
// If there are only 2 elements left to check, then
|
|
// indexMiddle is set to indexMin.
|
|
|
|
indexMiddle = (indexMin + indexMax) >> 1;
|
|
ySpan = GetYSpan (indexMiddle);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Determine if the specified point is inside this region.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] x - x coordinate of point
|
|
* [IN] y - y coordinate of point
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE - point is inside the region
|
|
* FALSE - point is not inside the region
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
BOOL
|
|
DpRegion::PointInside(
|
|
INT x,
|
|
INT y
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
#if 0
|
|
if (Infinite)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (Empty)
|
|
{
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
return ((x >= XMin) && (x < XMax) &&
|
|
(y >= YMin) && (y < YMax));
|
|
}
|
|
else
|
|
{
|
|
INT * ySpan;
|
|
INT ySpanIndex;
|
|
|
|
ComplexData->ResetSearchIndex();
|
|
if (ComplexData->YSpanSearch (y, &ySpan, &ySpanIndex))
|
|
{
|
|
INT * xSpan;
|
|
INT numXCoords;
|
|
|
|
xSpan = ComplexData->XCoords + ySpan[YSPAN_XOFFSET];
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
|
|
for (;;)
|
|
{
|
|
if (x < xSpan[1])
|
|
{
|
|
if (x >= xSpan[0])
|
|
{
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
if ((numXCoords -= 2) <= 0)
|
|
{
|
|
break;
|
|
}
|
|
xSpan += 2;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Determine the visibility of the specified rectangle.
|
|
*
|
|
* Note that some rects that could return ClippedVisible end up returning
|
|
* PartiallyVisible (for performance reasons).
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] xMin - minimum x of rect
|
|
* [IN] yMin - minimum y of rect
|
|
* [IN] xMax - maximum x of rect (exclusive)
|
|
* [IN] yMax - maximum y of rect (exclusive)
|
|
* [OUT] rectClipped - used when ClippedVisible is returned (can be NULL)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Visibility
|
|
* Invisible - rect is completely outside all region rects
|
|
* TotallyVisible - rect is completely inside a region rect
|
|
* ClippedVisible - rect intersects 1 and only 1 region rect
|
|
* PartiallyVisible - rect partially intersects at least 1 region rect
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::Visibility
|
|
DpRegion::GetRectVisibility(
|
|
INT xMin,
|
|
INT yMin,
|
|
INT xMax, // exclusive
|
|
INT yMax, // exclusive
|
|
GpRect * rectClipped
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((xMin < xMax) && (yMin < yMax));
|
|
|
|
Visibility visibility = ClippedVisible;
|
|
|
|
if (Infinite)
|
|
{
|
|
IsTotallyVisible:
|
|
if (rectClipped != NULL)
|
|
{
|
|
rectClipped->X = xMin;
|
|
rectClipped->Y = yMin;
|
|
rectClipped->Width = xMax - xMin;
|
|
rectClipped->Height = yMax - yMin;
|
|
}
|
|
return TotallyVisible;
|
|
}
|
|
|
|
BOOL simpleRegion = (ComplexData == NULL);
|
|
|
|
if (!Empty)
|
|
{
|
|
// If it is a simple region (only 1 rect) and the specified rect is
|
|
// completely inside the region, then trivially accept it
|
|
if (simpleRegion &&
|
|
(xMin >= XMin) && (yMin >= YMin) &&
|
|
(xMax <= XMax) && (yMax <= YMax))
|
|
{
|
|
goto IsTotallyVisible;
|
|
}
|
|
|
|
// Try to trivially reject rectangle.
|
|
if ((xMax > XMin) && (xMin < XMax) &&
|
|
(yMax > YMin) && (yMin < YMax))
|
|
{
|
|
// Couldn't trivially reject
|
|
// If simple region, clip it against the region
|
|
if (simpleRegion)
|
|
{
|
|
ClipToRgnExtent:
|
|
if (rectClipped)
|
|
{
|
|
INT xMinTmp = (xMin > XMin) ? xMin : XMin;
|
|
INT xMaxTmp = (xMax < XMax) ? xMax : XMax;
|
|
INT yMinTmp = (yMin > YMin) ? yMin : YMin;
|
|
INT yMaxTmp = (yMax < YMax) ? yMax : YMax;
|
|
|
|
rectClipped->X = xMinTmp;
|
|
rectClipped->Y = yMinTmp;
|
|
rectClipped->Width = xMaxTmp - xMinTmp;
|
|
rectClipped->Height = yMaxTmp - yMinTmp;
|
|
}
|
|
return visibility;
|
|
}
|
|
|
|
// else not a simple region -- see if the rect falls
|
|
// within one of the region rects
|
|
|
|
INT * ySpanYMin;
|
|
INT * ySpanYMax;
|
|
INT * xSpan;
|
|
INT numXCoords;
|
|
INT ySpanIndex;
|
|
BOOL yMinInside;
|
|
BOOL yMaxInside;
|
|
|
|
// Don't resetSearchIndex() here cause if we're calling this
|
|
// from regionOverlaps, we want to search from where we left off
|
|
// the last time.
|
|
yMinInside = ComplexData->YSpanSearch(
|
|
yMin,
|
|
&ySpanYMin,
|
|
&ySpanIndex);
|
|
|
|
ComplexData->YSearchIndex = ySpanIndex;
|
|
yMaxInside = ComplexData->YSpanSearch(
|
|
yMax - 1,
|
|
&ySpanYMax,
|
|
&ySpanIndex);
|
|
|
|
// See if both values are inside the same Y span
|
|
// so that we are totally visible in Y
|
|
if (yMinInside && yMaxInside && (ySpanYMin == ySpanYMax))
|
|
{
|
|
xSpan = ComplexData->XCoords + ySpanYMin[YSPAN_XOFFSET];
|
|
numXCoords = ySpanYMin[YSPAN_XCOUNT];
|
|
|
|
for (;;)
|
|
{
|
|
if (xMax <= xSpan[0])
|
|
{
|
|
goto IsInvisible;
|
|
}
|
|
if (xMin < xSpan[1])
|
|
{
|
|
// we found an intersection!
|
|
if (xMax <= xSpan[1])
|
|
{
|
|
if (xMin >= xSpan[0])
|
|
{
|
|
goto IsTotallyVisible;
|
|
}
|
|
if (rectClipped != NULL)
|
|
{
|
|
rectClipped->X = xSpan[0];
|
|
rectClipped->Width = xMax - xSpan[0];
|
|
rectClipped->Y = yMin;
|
|
rectClipped->Height = yMax - yMin;
|
|
}
|
|
return ClippedVisible;
|
|
}
|
|
// we could look ahead to see if we are clipped visible
|
|
visibility = PartiallyVisible;
|
|
goto ClipToRgnExtent;
|
|
}
|
|
// continue on with loop through x spans
|
|
|
|
if ((numXCoords -= 2) <= 0)
|
|
{
|
|
break;
|
|
}
|
|
xSpan += 2;
|
|
}
|
|
goto IsInvisible;
|
|
}
|
|
|
|
// See if the rect intersects with at least one X Span
|
|
// within the set of Y Spans it crosses
|
|
|
|
// If yMax was not inside a span, ySpanYMax could be
|
|
// one span too far
|
|
if (yMax <= ySpanYMax[YSPAN_YMIN])
|
|
{
|
|
ySpanYMax -= YSPAN_SIZE;
|
|
}
|
|
INT * ySpan = ySpanYMin;
|
|
|
|
for (;;)
|
|
{
|
|
xSpan = ComplexData->XCoords + ySpan[YSPAN_XOFFSET];
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
|
|
for (;;)
|
|
{
|
|
if (xMax <= xSpan[0])
|
|
{
|
|
break;
|
|
}
|
|
if (xMin < xSpan[1])
|
|
{
|
|
visibility = PartiallyVisible;
|
|
goto ClipToRgnExtent;
|
|
}
|
|
// continue on with loop through x spans
|
|
|
|
if ((numXCoords -= 2) <= 0)
|
|
{
|
|
break;
|
|
}
|
|
xSpan += 2;
|
|
}
|
|
|
|
if (ySpan >= ySpanYMax)
|
|
{
|
|
break;
|
|
}
|
|
ySpan += YSPAN_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
IsInvisible:
|
|
// couldn't find a span that it intersected
|
|
if (rectClipped)
|
|
{
|
|
rectClipped->X = 0;
|
|
rectClipped->Y = 0;
|
|
rectClipped->Width = 0;
|
|
rectClipped->Height = 0;
|
|
}
|
|
return Invisible;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Determine if the specified region overlaps (intersects) this
|
|
* region at all.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - region to test visibility of
|
|
*
|
|
* Return Value:
|
|
*
|
|
* BOOL - whether region is at least partially visible or not
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
BOOL
|
|
DpRegion::RegionVisible(
|
|
DpRegion * region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (Empty || region->Empty)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (Infinite || region->Infinite)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else // neither is empty or infinite
|
|
{
|
|
Visibility visibility = GetRectVisibility(
|
|
region->XMin,
|
|
region->YMin,
|
|
region->XMax,
|
|
region->YMax);
|
|
|
|
if (visibility == TotallyVisible)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (visibility == Invisible)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ((ComplexData == NULL) && (region->ComplexData == NULL))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
INT * ySpan;
|
|
INT * ySpanLast;
|
|
INT * xSpan;
|
|
INT * xCoords;
|
|
INT numXCoords;
|
|
INT yMin;
|
|
INT yMax;
|
|
INT ySpanTmp[YSPAN_SIZE];
|
|
INT xCoordsTmp[2];
|
|
|
|
if (region->ComplexData == NULL)
|
|
{
|
|
ySpan = ySpanTmp;
|
|
ySpanLast = ySpanTmp;
|
|
|
|
ySpan[YSPAN_YMIN] = region->YMin;
|
|
ySpan[YSPAN_YMAX] = region->YMax;
|
|
ySpan[YSPAN_XOFFSET] = 0;
|
|
ySpan[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords = xCoordsTmp;
|
|
xCoords[0] = region->XMin;
|
|
xCoords[1] = region->XMax;
|
|
}
|
|
else
|
|
{
|
|
DpComplexRegion * complexData = region->ComplexData;
|
|
ySpan = complexData->YSpans;
|
|
ySpanLast = ySpan + ((complexData->NumYSpans - 1) * YSPAN_SIZE);
|
|
xCoords = complexData->XCoords;
|
|
}
|
|
|
|
if (ComplexData != NULL)
|
|
{
|
|
ComplexData->ResetSearchIndex();
|
|
}
|
|
|
|
do
|
|
{
|
|
yMin = ySpan[YSPAN_YMIN];
|
|
yMax = ySpan[YSPAN_YMAX];
|
|
|
|
if (yMin >= YMax)
|
|
{
|
|
break; // doesn't overlap
|
|
}
|
|
if (yMax > YMin)
|
|
{
|
|
xSpan = xCoords + ySpan[YSPAN_XOFFSET];
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
|
|
for (;;)
|
|
{
|
|
if (GetRectVisibility(xSpan[0], yMin, xSpan[1], yMax) !=
|
|
Invisible)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if ((numXCoords -= 2) <= 0)
|
|
{
|
|
break;
|
|
}
|
|
xSpan += 2;
|
|
}
|
|
}
|
|
|
|
} while ((ySpan += YSPAN_SIZE) <= ySpanLast);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Determine if the specified rect intersects this region.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] xMin - minimum x coordinate of rect
|
|
* [IN] yMin - minimum y coordinate of rect
|
|
* [IN] xMax - maximum x coordinate of rect
|
|
* [IN] yMax - maximum y coordinate of rect
|
|
*
|
|
* Return Value:
|
|
*
|
|
* BOOL
|
|
* TRUE - rect intersects this region
|
|
* FALSE - rect does not intersect this region
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
BOOL
|
|
DpRegion::RectVisible(
|
|
INT xMin,
|
|
INT yMin,
|
|
INT xMax,
|
|
INT yMax
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
// Do Trivial Rejection Test
|
|
if ((xMin >= XMax) || (xMax <= XMin) ||
|
|
(yMin >= YMax) || (yMax <= YMin) ||
|
|
(xMin >= xMax) || (yMin >= yMax)) // must test for empty rect too
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
ComplexData->ResetSearchIndex();
|
|
|
|
return (GetRectVisibility(xMin, yMin, xMax, yMax) != Invisible);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Determine if the specified rect intersects this region.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] xMin - minimum x coordinate of rect
|
|
* [IN] yMin - minimum y coordinate of rect
|
|
* [IN] xMax - maximum x coordinate of rect
|
|
* [IN] yMax - maximum y coordinate of rect
|
|
*
|
|
* Return Value:
|
|
*
|
|
* BOOL
|
|
* TRUE - rect intersects this region
|
|
* FALSE - rect does not intersect this region
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
BOOL
|
|
DpRegion::RectInside(
|
|
INT xMin,
|
|
INT yMin,
|
|
INT xMax,
|
|
INT yMax
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
// Do Trivial Rejection Test
|
|
if ((xMin < XMin) || (xMax > XMax) ||
|
|
(yMin < YMin) || (yMax > YMax))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
ComplexData->ResetSearchIndex();
|
|
|
|
return (GetRectVisibility(xMin, yMin, xMax, yMax) == TotallyVisible);
|
|
}
|
|
|
|
#define YSPAN_INC 16
|
|
|
|
GpStatus
|
|
DpRegionBuilder::InitComplexData(
|
|
INT ySpans // estimate of number of y spans required for region
|
|
)
|
|
{
|
|
if (ySpans < YSPAN_INC)
|
|
{
|
|
ySpans = YSPAN_INC;
|
|
}
|
|
|
|
INT xCoordsCapacity;
|
|
|
|
for (;;)
|
|
{
|
|
xCoordsCapacity = ySpans * 4;
|
|
|
|
ComplexData = static_cast<DpComplexRegion *>(
|
|
GpMalloc(sizeof(DpComplexRegion) +
|
|
(ySpans * (YSPAN_SIZE * sizeof(*(ComplexData->YSpans)))) +
|
|
(xCoordsCapacity * sizeof(*(ComplexData->XCoords)))));
|
|
if (ComplexData != NULL)
|
|
{
|
|
break;
|
|
}
|
|
ySpans >>= 1;
|
|
if (ySpans <= (YSPAN_INC / 2))
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
}
|
|
|
|
DpComplexRegion * complexData = ComplexData;
|
|
|
|
complexData->XCoordsCapacity = xCoordsCapacity;
|
|
complexData->XCoordsCount = 0;
|
|
complexData->YSpansCapacity = ySpans;
|
|
complexData->NumYSpans = 0;
|
|
complexData->YSearchIndex = 0;
|
|
complexData->XCoords = reinterpret_cast<INT *>(complexData + 1);
|
|
complexData->YSpans = complexData->XCoords + xCoordsCapacity;
|
|
XMin = XMax = YMin = YMax = 0;
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Traverse through the region, clipping as you go, doing a fill using
|
|
* the specified output object.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] output - the object used to output the region spans
|
|
* [IN] clipBounds - the bounds to clip to (if any)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 02/25/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Fill(
|
|
DpOutputSpan * output,
|
|
GpRect * clipBounds
|
|
) const
|
|
{
|
|
GpStatus status = Ok;
|
|
|
|
if (!Empty)
|
|
{
|
|
INT y;
|
|
INT xMin;
|
|
INT yMin;
|
|
INT xMax;
|
|
INT yMax;
|
|
|
|
if (Infinite)
|
|
{
|
|
ASSERT(clipBounds != NULL);
|
|
if (clipBounds != NULL)
|
|
{
|
|
xMin = clipBounds->X;
|
|
xMax = clipBounds->GetRight();
|
|
yMax = clipBounds->GetBottom();
|
|
|
|
for (y = clipBounds->Y; (y < yMax) && (status == Ok); y++)
|
|
{
|
|
status = output->OutputSpan(y, xMin, xMax);
|
|
}
|
|
}
|
|
}
|
|
else if (ComplexData == NULL)
|
|
{
|
|
xMin = XMin;
|
|
yMin = YMin;
|
|
xMax = XMax;
|
|
yMax = YMax;
|
|
|
|
if (clipBounds != NULL)
|
|
{
|
|
if (xMin < clipBounds->X)
|
|
{
|
|
xMin = clipBounds->X;
|
|
}
|
|
if (yMin < clipBounds->Y)
|
|
{
|
|
yMin = clipBounds->Y;
|
|
}
|
|
if (xMax > clipBounds->GetRight())
|
|
{
|
|
xMax = clipBounds->GetRight();
|
|
}
|
|
if (yMax > clipBounds->GetBottom())
|
|
{
|
|
yMax = clipBounds->GetBottom();
|
|
}
|
|
}
|
|
for (y = yMin; (y < yMax) && (status == Ok); y++)
|
|
{
|
|
status = output->OutputSpan(y, xMin, xMax);
|
|
}
|
|
}
|
|
else // complex region
|
|
{
|
|
DpComplexRegion * complexData = ComplexData;
|
|
INT * ySpan = complexData->YSpans;
|
|
INT * ySpanLast = ySpan +
|
|
((complexData->NumYSpans - 1) * YSPAN_SIZE);
|
|
INT * xCoords;
|
|
INT * xSpan;
|
|
INT numXCoords;
|
|
INT numXSpan;
|
|
|
|
if (clipBounds != NULL)
|
|
{
|
|
INT ySpanIndex;
|
|
|
|
complexData->ResetSearchIndex();
|
|
if (YMin < clipBounds->Y)
|
|
{
|
|
complexData->YSpanSearch(clipBounds->Y,
|
|
&ySpan, &ySpanIndex);
|
|
}
|
|
if (YMax > clipBounds->GetBottom())
|
|
{
|
|
complexData->YSpanSearch(clipBounds->GetBottom(),
|
|
&ySpanLast, &ySpanIndex);
|
|
}
|
|
}
|
|
xCoords = complexData->XCoords + ySpan[YSPAN_XOFFSET];
|
|
for (;;)
|
|
{
|
|
yMin = ySpan[YSPAN_YMIN];
|
|
yMax = ySpan[YSPAN_YMAX];
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
|
|
// [agodfrey] We must clip our output range to the clip region.
|
|
// Bug #122789 showed that, otherwise, we can take inordinately
|
|
// long to execute. In the bug's specific case, the loop below
|
|
// executed 67 million iterations before getting to the
|
|
// first unclipped scan.
|
|
|
|
if (clipBounds != NULL)
|
|
{
|
|
if (yMin < clipBounds->Y)
|
|
{
|
|
yMin = clipBounds->Y;
|
|
}
|
|
|
|
if (yMax > clipBounds->GetBottom())
|
|
{
|
|
yMax = clipBounds->GetBottom();
|
|
}
|
|
}
|
|
|
|
// The code below assumes that yMax > yMin. We think this should
|
|
// be satisfied because the clipBounds and yspan should both
|
|
// be non-empty, and yMax >= clipBounds->Y since the code
|
|
// above searched for a yspan which intersects the clip bounds.
|
|
|
|
// NTRAID#NTBUG9-393985-2001/05/16-asecchia
|
|
// This ASSERT is overactive and fires when there is no real
|
|
// crash problem, however there is a performance issue that
|
|
// should be addressed - see RAID bug.
|
|
|
|
// ASSERT(yMax > yMin);
|
|
|
|
if (numXCoords == 2)
|
|
{
|
|
xMin = *xCoords++;
|
|
xMax = *xCoords++;
|
|
do
|
|
{
|
|
status = output->OutputSpan(yMin, xMin, xMax);
|
|
|
|
} while ((++yMin < yMax) && (status == Ok));
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
for (xSpan = xCoords, numXSpan = numXCoords;;)
|
|
{
|
|
numXSpan -= 2;
|
|
status = output->OutputSpan(yMin,xSpan[0],xSpan[1]);
|
|
if (status != Ok)
|
|
{
|
|
goto Done;
|
|
}
|
|
if (numXSpan < 2)
|
|
{
|
|
break;
|
|
}
|
|
xSpan += 2;
|
|
}
|
|
|
|
} while (++yMin < yMax);
|
|
|
|
xCoords += numXCoords;
|
|
}
|
|
if (ySpan >= ySpanLast)
|
|
{
|
|
break;
|
|
}
|
|
ySpan += YSPAN_SIZE;
|
|
}
|
|
}
|
|
}
|
|
Done:
|
|
return status;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Impelement GpOutputYSpan interface to create the region
|
|
* data from the path data, using the rasterizer.
|
|
* Exclusive in yMax and in the xMax'es.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] yMin - min y of this span
|
|
* [IN] yMax - max y of this span
|
|
* [IN] xCoords - array of x coordinates (in pairs of x min, x max)
|
|
* [IN] numXCoords - number of x coordinates in xCoords array
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegionBuilder::OutputYSpan(
|
|
INT yMin,
|
|
INT yMax,
|
|
INT * xCoords, // even number of X coordinates
|
|
INT numXCoords // must be a multiple of 2
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
DpComplexRegion * complexData = ComplexData;
|
|
INT numYSpans = complexData->NumYSpans;
|
|
|
|
#ifdef USE_YSPAN_BUILDER
|
|
if (numYSpans > 0)
|
|
{
|
|
// Try to add this row to the previous row,
|
|
// if the scans are the same and the y's match up
|
|
INT * ySpanPrev;
|
|
INT * xSpanPrev;
|
|
INT numXCoordsPrev;
|
|
|
|
ySpanPrev = complexData->GetYSpan(numYSpans - 1);
|
|
xSpanPrev = complexData->XCoords + ySpanPrev[YSPAN_XOFFSET];
|
|
numXCoordsPrev = ySpanPrev[YSPAN_XCOUNT];
|
|
|
|
if ((numXCoordsPrev == numXCoords) &&
|
|
(ySpanPrev[YSPAN_YMAX] >= yMin) &&
|
|
(GpMemcmp (xSpanPrev, xCoords, numXCoords * sizeof(INT)) == 0))
|
|
{
|
|
// Yes, it did match -- just set the new yMax and return
|
|
YMax = yMax;
|
|
ySpanPrev[YSPAN_YMAX] = yMax;
|
|
return Ok;
|
|
}
|
|
}
|
|
|
|
// no previous spans or doesn't match previous spans
|
|
#endif
|
|
|
|
INT xCount = complexData->XCoordsCount;
|
|
INT * ySpan;
|
|
INT * xArray;
|
|
|
|
if ((complexData->YSpansCapacity > numYSpans) &&
|
|
(complexData->XCoordsCapacity >= (xCount + numXCoords)))
|
|
{
|
|
complexData->NumYSpans++;
|
|
complexData->XCoordsCount += numXCoords;
|
|
}
|
|
else // need more capacity
|
|
{
|
|
// We want to have YSPAN_INC Y spans available and
|
|
// to have YSPAN_INC * 4 X coords available after we
|
|
// add this data.
|
|
INT newYSpansCapacity = numYSpans + (YSPAN_INC + 1);
|
|
INT newXCoordsCapacity = xCount + numXCoords + (YSPAN_INC * 4);
|
|
DpComplexRegion * oldData = complexData;
|
|
|
|
complexData = static_cast<DpComplexRegion *>(
|
|
GpMalloc(sizeof(DpComplexRegion) +
|
|
(newYSpansCapacity * (YSPAN_SIZE * sizeof(*(oldData->YSpans)))) +
|
|
(newXCoordsCapacity * sizeof(*(oldData->XCoords)))));
|
|
|
|
if (complexData == NULL)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
ComplexData = complexData;
|
|
|
|
complexData->XCoordsCapacity = newXCoordsCapacity;
|
|
complexData->XCoordsCount = xCount + numXCoords;
|
|
complexData->YSpansCapacity = newYSpansCapacity;
|
|
complexData->NumYSpans = numYSpans + 1;
|
|
complexData->YSearchIndex = 0;
|
|
complexData->XCoords = reinterpret_cast<INT *>(complexData + 1);
|
|
complexData->YSpans = complexData->XCoords +
|
|
newXCoordsCapacity;
|
|
|
|
GpMemcpy(complexData->XCoords, oldData->XCoords,
|
|
xCount * sizeof(*(complexData->XCoords)));
|
|
|
|
GpMemcpy(complexData->YSpans, oldData->YSpans,
|
|
numYSpans * (YSPAN_SIZE * sizeof(*(complexData->YSpans))));
|
|
|
|
GpFree (oldData);
|
|
}
|
|
xArray = complexData->XCoords + xCount;
|
|
ySpan = complexData->YSpans + (numYSpans * YSPAN_SIZE);
|
|
|
|
ySpan[YSPAN_YMIN] = yMin; // y Start (Min)
|
|
ySpan[YSPAN_YMAX] = yMax; // y End (Max)
|
|
ySpan[YSPAN_XOFFSET] = xCount; // XCoords index
|
|
ySpan[YSPAN_XCOUNT] = numXCoords; // number of X's
|
|
|
|
GpMemcpy (xArray, xCoords, numXCoords * sizeof(xArray[0]));
|
|
|
|
if (numYSpans == 0)
|
|
{
|
|
YMin = yMin;
|
|
XMin = xCoords[0];
|
|
XMax = xCoords[numXCoords - 1];
|
|
}
|
|
else
|
|
{
|
|
if (XMin > xCoords[0])
|
|
{
|
|
XMin = xCoords[0];
|
|
}
|
|
if (XMax < xCoords[numXCoords - 1])
|
|
{
|
|
XMax = xCoords[numXCoords - 1];
|
|
}
|
|
}
|
|
|
|
YMax = yMax;
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* constructor
|
|
* Set to either empty or to infinite, depending on the value of empty.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] empty - if non-zero, initialize to empty else to infinite
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::DpRegion(
|
|
BOOL empty
|
|
)
|
|
{
|
|
ComplexData = NULL;
|
|
Lazy = FALSE;
|
|
|
|
if (!empty)
|
|
{
|
|
// set to infinite
|
|
Infinite = TRUE;
|
|
Empty = FALSE;
|
|
|
|
XMin = INFINITE_MIN;
|
|
YMin = INFINITE_MIN;
|
|
XMax = INFINITE_MAX;
|
|
YMax = INFINITE_MAX;
|
|
}
|
|
else
|
|
{
|
|
// set to empty
|
|
Infinite = FALSE;
|
|
Empty = TRUE;
|
|
|
|
XMin = 0;
|
|
YMin = 0;
|
|
XMax = 0;
|
|
YMax = 0;
|
|
}
|
|
|
|
SetValid(TRUE);
|
|
UpdateUID();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* constructor
|
|
* Set to the specified rect.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] rect - rect to use for the region coverage area
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::DpRegion(
|
|
const GpRect * rect
|
|
)
|
|
{
|
|
ASSERT (rect != NULL);
|
|
|
|
ComplexData = NULL;
|
|
Lazy = FALSE;
|
|
SetValid(TRUE);
|
|
UpdateUID();
|
|
|
|
Set(rect->X, rect->Y, rect->Width, rect->Height);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* constructor
|
|
* Set to the specified list of rects, which must be in the same order
|
|
* as our YSpan data.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] rects - rects to use for the region coverage area
|
|
* [IN] count - number of rects
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 05/04/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::DpRegion(
|
|
const RECT * rects,
|
|
INT count
|
|
)
|
|
{
|
|
ComplexData = NULL;
|
|
Lazy = FALSE;
|
|
SetValid(TRUE);
|
|
UpdateUID();
|
|
|
|
if (Set(rects, count) == Ok)
|
|
{
|
|
return;
|
|
}
|
|
SetValid(FALSE);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Set to the specified list of rects, which must be in the same order
|
|
* as our YSpan data.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] rects - rects to use for the region coverage area
|
|
* [IN] count - number of rects
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 05/04/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Set(
|
|
const RECT * rects,
|
|
INT count
|
|
)
|
|
{
|
|
if ((rects == NULL) || (count <= 0))
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
|
|
if (count == 1)
|
|
{
|
|
OneRect:
|
|
Set(rects->left, rects->top,
|
|
rects->right - rects->left,
|
|
rects->bottom - rects->top);
|
|
return Ok;
|
|
}
|
|
|
|
// Verify the first rect(s) to make sure they're not empty
|
|
for (;;)
|
|
{
|
|
// Ignore any empty rects at the beginning of the list
|
|
if ((rects->top < rects->bottom) &&
|
|
(rects->left < rects->right))
|
|
{
|
|
break;
|
|
}
|
|
|
|
WARNING1("Empty or Invalid Rect");
|
|
rects++;
|
|
if (--count == 1)
|
|
{
|
|
goto OneRect;
|
|
}
|
|
}
|
|
|
|
{
|
|
DpRegionBuilder regionBuilder(count);
|
|
|
|
if (regionBuilder.IsValid())
|
|
{
|
|
INT yMin = rects->top;
|
|
INT yMax = rects->bottom;
|
|
BOOL failed = FALSE;
|
|
|
|
DynArrayIA<INT,32> xCoords;
|
|
|
|
if(xCoords.ReserveSpace(count * 2) != Ok)
|
|
goto ErrorExit;
|
|
|
|
xCoords.Add(rects->left);
|
|
xCoords.Add(rects->right);
|
|
|
|
for (INT i = 1; i < count; i++)
|
|
{
|
|
// Ignore empty rects
|
|
if ((rects[i].top < rects[i].bottom) &&
|
|
(rects[i].left < rects[i].right))
|
|
{
|
|
if (rects[i].top != yMin)
|
|
{
|
|
if (regionBuilder.OutputYSpan(yMin, yMax,
|
|
xCoords.GetDataBuffer(), xCoords.GetCount()) != Ok)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ASSERT(rects[i].top >= yMax);
|
|
|
|
xCoords.SetCount(0);
|
|
yMin = rects[i].top;
|
|
yMax = rects[i].bottom;
|
|
}
|
|
|
|
xCoords.Add(rects[i].left);
|
|
xCoords.Add(rects[i].right);
|
|
}
|
|
else
|
|
{
|
|
WARNING1("Empty or Invalid Rect");
|
|
}
|
|
}
|
|
|
|
if (xCoords.GetCount() > 0)
|
|
{
|
|
if (regionBuilder.OutputYSpan(yMin, yMax,
|
|
xCoords.GetDataBuffer(), xCoords.GetCount()) != Ok)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
return Set(regionBuilder);
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
SetEmpty();
|
|
SetValid(FALSE);
|
|
return GenericError;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* constructor
|
|
* Make this region be the specified rect.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] x - starting x coordinate of rect
|
|
* [IN] y - starting y coordinate of rect
|
|
* [IN] width - width of rect
|
|
* [IN] height - height of rect
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::DpRegion(
|
|
INT x,
|
|
INT y,
|
|
INT width,
|
|
INT height
|
|
)
|
|
{
|
|
ComplexData = NULL;
|
|
Lazy = FALSE;
|
|
SetValid(TRUE);
|
|
UpdateUID();
|
|
|
|
Set(x, y, width, height);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Make this region be the specified rect.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] x - starting x coordinate of rect
|
|
* [IN] y - starting y coordinate of rect
|
|
* [IN] width - width of rect
|
|
* [IN] height - height of rect
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
VOID
|
|
DpRegion::Set(
|
|
INT x,
|
|
INT y,
|
|
INT width,
|
|
INT height
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((width >= 0) && (height >= 0));
|
|
|
|
// 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 > 0) && (width < INFINITE_SIZE) &&
|
|
(height > 0) && (height < INFINITE_SIZE))
|
|
{
|
|
FreeData();
|
|
|
|
SetValid(TRUE);
|
|
Infinite = FALSE;
|
|
Empty = FALSE;
|
|
UpdateUID();
|
|
|
|
XMin = x;
|
|
YMin = y;
|
|
XMax = x + width;
|
|
YMax = y + height;
|
|
}
|
|
else if ((width <= 0) || (height <= 0))
|
|
{
|
|
SetEmpty();
|
|
}
|
|
else
|
|
{
|
|
SetInfinite();
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Initialize the region to a cleared state with an empty coverage area.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
VOID
|
|
DpRegion::SetEmpty()
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
FreeData();
|
|
|
|
SetValid(TRUE);
|
|
Infinite = FALSE;
|
|
Empty = TRUE;
|
|
UpdateUID();
|
|
|
|
|
|
XMin = 0;
|
|
YMin = 0;
|
|
XMax = 0;
|
|
YMax = 0;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Initialize the region to contain an infinite coverage area.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
VOID
|
|
DpRegion::SetInfinite()
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
FreeData();
|
|
|
|
SetValid(TRUE);
|
|
Infinite = TRUE;
|
|
Empty = FALSE;
|
|
UpdateUID();
|
|
|
|
XMin = INFINITE_MIN;
|
|
YMin = INFINITE_MIN;
|
|
XMax = INFINITE_MAX;
|
|
YMax = INFINITE_MAX;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Constructor.
|
|
* Make this region cover the area specified by the path.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] path - specifies the coverage area in world units
|
|
* [IN] matrix - matrix to apply to the path
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::DpRegion(
|
|
const DpPath * path,
|
|
const GpMatrix * matrix
|
|
)
|
|
{
|
|
ComplexData = NULL;
|
|
Lazy = FALSE;
|
|
SetValid(TRUE);
|
|
UpdateUID();
|
|
|
|
if (Set(path, matrix) == Ok)
|
|
{
|
|
return;
|
|
}
|
|
SetValid(FALSE);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Make this region cover the area specified by the path.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] path - specifies the coverage area in world units
|
|
* [IN] matrix - matrix to apply to the path
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Set(
|
|
const DpPath * path,
|
|
const GpMatrix * matrix
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT ((path != NULL) && (matrix != NULL));
|
|
|
|
GpRect bounds;
|
|
|
|
path->GetBounds(&bounds, matrix);
|
|
|
|
DpRegionBuilder regionBuilder(bounds.Height);
|
|
|
|
if (regionBuilder.IsValid())
|
|
{
|
|
#ifndef USE_YSPAN_BUILDER
|
|
|
|
GpRectBuilder rectBuilder(®ionBuilder);
|
|
|
|
if (rectBuilder.IsValid() &&
|
|
(Rasterizer(path, matrix, path->GetFillMode(), &rectBuilder) == Ok))
|
|
{
|
|
return Set(regionBuilder);
|
|
}
|
|
#else
|
|
GpYSpanBuilder ySpanBuilder(®ionBuilder);
|
|
|
|
if (ySpanBuilder.IsValid() &&
|
|
(Rasterizer(path, matrix, path->GetFillMode(), &ySpanBuilder) ==Ok))
|
|
{
|
|
return Set(regionBuilder);
|
|
}
|
|
#endif // USE_YSPAN_BUILDER
|
|
}
|
|
return GenericError;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Make this region be a copy of the data in the specified region builder.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] regionBuilder - contains the simple or complex region data
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Set(
|
|
DpRegionBuilder & regionBuilder
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
DpComplexRegion * srcComplexData = regionBuilder.ComplexData;
|
|
|
|
if (srcComplexData != NULL)
|
|
{
|
|
if ((srcComplexData->NumYSpans == 1) &&
|
|
(srcComplexData->XCoordsCount == 2))
|
|
{
|
|
Set(regionBuilder.XMin, regionBuilder.YMin,
|
|
regionBuilder.XMax - regionBuilder.XMin,
|
|
regionBuilder.YMax - regionBuilder.YMin);
|
|
return Ok;
|
|
}
|
|
if (srcComplexData->NumYSpans >= 1)
|
|
{
|
|
ASSERT(srcComplexData->XCoordsCount >= 2);
|
|
|
|
DpComplexRegion * destComplexData = NULL;
|
|
|
|
FreeData();
|
|
|
|
SetValid(TRUE);
|
|
Infinite = FALSE;
|
|
Empty = FALSE;
|
|
UpdateUID();
|
|
|
|
|
|
XMin = regionBuilder.XMin;
|
|
YMin = regionBuilder.YMin;
|
|
XMax = regionBuilder.XMax;
|
|
YMax = regionBuilder.YMax;
|
|
|
|
if ((srcComplexData->YSpansCapacity - srcComplexData->NumYSpans) >=
|
|
YSPAN_INC)
|
|
{
|
|
destComplexData = static_cast<DpComplexRegion *>(
|
|
GpMalloc(sizeof(DpComplexRegion) +
|
|
(srcComplexData->NumYSpans *
|
|
(YSPAN_SIZE * sizeof(*(srcComplexData->YSpans)))) +
|
|
(srcComplexData->XCoordsCount *
|
|
sizeof(*(srcComplexData->XCoords)))));
|
|
}
|
|
|
|
if (destComplexData != NULL)
|
|
{
|
|
destComplexData->XCoordsCapacity = srcComplexData->XCoordsCount;
|
|
destComplexData->XCoordsCount = srcComplexData->XCoordsCount;
|
|
|
|
destComplexData->YSpansCapacity = srcComplexData->NumYSpans;
|
|
destComplexData->NumYSpans = srcComplexData->NumYSpans;
|
|
|
|
destComplexData->XCoords =
|
|
reinterpret_cast<INT*>(destComplexData + 1);
|
|
destComplexData->YSpans = destComplexData->XCoords +
|
|
destComplexData->XCoordsCapacity;
|
|
|
|
GpMemcpy (destComplexData->XCoords,
|
|
srcComplexData->XCoords,
|
|
srcComplexData->XCoordsCount *
|
|
sizeof(*(destComplexData->XCoords)));
|
|
|
|
GpMemcpy (destComplexData->YSpans,
|
|
srcComplexData->YSpans,
|
|
srcComplexData->NumYSpans *
|
|
(YSPAN_SIZE * sizeof(*(destComplexData->YSpans))));
|
|
}
|
|
else
|
|
{
|
|
destComplexData = srcComplexData;
|
|
regionBuilder.ComplexData = NULL;
|
|
}
|
|
destComplexData->ResetSearchIndex();
|
|
ComplexData = destComplexData;
|
|
return Ok;
|
|
}
|
|
}
|
|
|
|
// else empty
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Constructor - makes a copy of the specified region
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - region to copy
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::DpRegion(
|
|
const DpRegion * region,
|
|
BOOL lazy
|
|
)
|
|
{
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
ComplexData = NULL;
|
|
Lazy = FALSE;
|
|
SetValid(TRUE);
|
|
UpdateUID();
|
|
|
|
if (Set(region, lazy) == Ok)
|
|
{
|
|
return;
|
|
}
|
|
SetValid(FALSE);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Copy constructor
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - region to copy
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion::DpRegion(
|
|
DpRegion & region
|
|
)
|
|
{
|
|
ASSERT(region.IsValid());
|
|
|
|
ComplexData = NULL;
|
|
Lazy = FALSE;
|
|
SetValid(TRUE);
|
|
UpdateUID();
|
|
|
|
if (Set(®ion) == Ok)
|
|
{
|
|
return;
|
|
}
|
|
SetValid(FALSE);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Makes this region be a copy of another source region.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - region to copy.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Set(
|
|
const DpRegion * region,
|
|
BOOL lazy
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (region != NULL)
|
|
{
|
|
GpStatus status = Ok;
|
|
DpComplexRegion * srcComplexData = region->ComplexData;
|
|
|
|
if (srcComplexData == NULL)
|
|
{
|
|
Set (region->XMin, region->YMin,
|
|
region->XMax - region->XMin,
|
|
region->YMax - region->YMin);
|
|
return Ok;
|
|
}
|
|
|
|
if ((region == this) && !Lazy)
|
|
{
|
|
return Ok;
|
|
}
|
|
|
|
FreeData();
|
|
|
|
if (!lazy)
|
|
{
|
|
ComplexData = static_cast<DpComplexRegion*>(
|
|
GpMalloc(sizeof(DpComplexRegion) +
|
|
(srcComplexData->NumYSpans *
|
|
(YSPAN_SIZE * sizeof(*(ComplexData->YSpans)))) +
|
|
(srcComplexData->XCoordsCount *
|
|
sizeof(*(ComplexData->XCoords)))));
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
ASSERT(0);
|
|
SetValid(FALSE);
|
|
return OutOfMemory;
|
|
}
|
|
|
|
DpComplexRegion * destComplexData = ComplexData;
|
|
|
|
destComplexData->XCoordsCapacity = srcComplexData->XCoordsCount;
|
|
destComplexData->XCoordsCount = srcComplexData->XCoordsCount;
|
|
|
|
destComplexData->YSpansCapacity = srcComplexData->NumYSpans;
|
|
destComplexData->NumYSpans = srcComplexData->NumYSpans;
|
|
|
|
destComplexData->ResetSearchIndex();
|
|
|
|
destComplexData->XCoords= reinterpret_cast<INT*>(destComplexData+1);
|
|
destComplexData->YSpans = destComplexData->XCoords +
|
|
destComplexData->XCoordsCapacity;
|
|
|
|
GpMemcpy (destComplexData->XCoords,
|
|
srcComplexData->XCoords,
|
|
srcComplexData->XCoordsCount *
|
|
sizeof(*(destComplexData->XCoords)));
|
|
|
|
GpMemcpy (destComplexData->YSpans,
|
|
srcComplexData->YSpans,
|
|
srcComplexData->NumYSpans *
|
|
(YSPAN_SIZE * sizeof(*(destComplexData->YSpans))));
|
|
}
|
|
else
|
|
{
|
|
ComplexData = srcComplexData;
|
|
Lazy = TRUE;
|
|
}
|
|
|
|
SetValid(TRUE);
|
|
Empty = FALSE;
|
|
Infinite = FALSE;
|
|
UpdateUID();
|
|
|
|
XMin = region->XMin;
|
|
YMin = region->YMin;
|
|
XMax = region->XMax;
|
|
YMax = region->YMax;
|
|
|
|
return Ok;
|
|
}
|
|
|
|
return InvalidParameter;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* assignment operator.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - region to copy
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
DpRegion &
|
|
DpRegion::operator=(
|
|
DpRegion & region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT(region.IsValid());
|
|
|
|
Set (®ion); // what do we do if this fails?
|
|
return *this; // Assignment operator returns left side.
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Offset (translate) the region by the specified offset values
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] xOffset - x offset (delta) value
|
|
* [IN] yOffset - y offset (delta) value
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Offset(
|
|
INT xOffset,
|
|
INT yOffset
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
if ((xOffset | yOffset) != 0)
|
|
{
|
|
// copy the data, so it's not a lazy copy
|
|
if (Lazy && (Set(this) != Ok))
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
if (Infinite || Empty)
|
|
{
|
|
return Ok;
|
|
}
|
|
|
|
if (xOffset != 0)
|
|
{
|
|
XMin += xOffset;
|
|
XMax += xOffset;
|
|
|
|
// !!! Handle this for non-debug case
|
|
ASSERT((XMin >= INFINITE_MIN) && (XMin <= INFINITE_MAX));
|
|
ASSERT((XMax >= INFINITE_MIN) && (XMax <= INFINITE_MAX));
|
|
|
|
if (ComplexData != NULL)
|
|
{
|
|
INT * xCoords = ComplexData->XCoords;
|
|
INT count = ComplexData->XCoordsCount;
|
|
|
|
while (count >= 2)
|
|
{
|
|
xCoords[0] += xOffset;
|
|
xCoords[1] += xOffset;
|
|
xCoords += 2;
|
|
count -= 2;
|
|
}
|
|
}
|
|
}
|
|
if (yOffset != 0)
|
|
{
|
|
YMin += yOffset;
|
|
YMax += yOffset;
|
|
|
|
// !!! Handle this for non-debug case
|
|
ASSERT((YMin >= INFINITE_MIN) && (YMin <= INFINITE_MAX));
|
|
ASSERT((YMax >= INFINITE_MIN) && (YMax <= INFINITE_MAX));
|
|
|
|
if (ComplexData != NULL)
|
|
{
|
|
INT * ySpans = ComplexData->YSpans;
|
|
INT count = ComplexData->NumYSpans;
|
|
|
|
while (count > 0)
|
|
{
|
|
ySpans[YSPAN_YMIN] += yOffset;
|
|
ySpans[YSPAN_YMAX] += yOffset;
|
|
ySpans += YSPAN_SIZE;
|
|
count--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* See if another region is identical in coverage to this one.
|
|
* For this to work, infinite regions must all have the same data.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - region to compare with
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE - regions cover the same area
|
|
* FALSE - regions not identical
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
BOOL
|
|
DpRegion::IsEqual(
|
|
DpRegion * region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (!Empty)
|
|
{
|
|
if ((XMin == region->XMin) &&
|
|
(YMin == region->YMin) &&
|
|
(XMax == region->XMax) &&
|
|
(YMax == region->YMax))
|
|
{
|
|
if (ComplexData == NULL)
|
|
{
|
|
return (region->ComplexData == NULL);
|
|
}
|
|
if (region->ComplexData == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (ComplexData->NumYSpans == region->ComplexData->NumYSpans)
|
|
{
|
|
// If the ySpans are the same, then the size of the
|
|
// xCoords buffers should also be the same.
|
|
return ((GpMemcmp (ComplexData->YSpans,
|
|
region->ComplexData->YSpans,
|
|
ComplexData->NumYSpans *
|
|
YSPAN_SIZE * sizeof(INT)) == 0) &&
|
|
(GpMemcmp (ComplexData->XCoords,
|
|
region->ComplexData->XCoords,
|
|
ComplexData->XCoordsCount *
|
|
sizeof(INT)) == 0));
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
return region->Empty;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Add Y Span data to a regionBuilder, compacting the data as we go.
|
|
* Used by the region combine methods.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] yMin - min y of this span
|
|
* [IN] yMax - max y of this span
|
|
* [IN] xCoords - array of x coordinates (in pairs of x min, x max)
|
|
* [IN] numXCoords - number of x coordinates in xCoords array
|
|
* [IN] regionBuilder - the region builder that stores the data
|
|
* [IN] combineCoords - used to compact the data
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::CompactAndOutput(
|
|
INT yMin,
|
|
INT yMax,
|
|
INT * xCoords,
|
|
INT numXCoords,
|
|
DpRegionBuilder * regionBuilder,
|
|
DynIntArray * combineCoords
|
|
)
|
|
{
|
|
// numEdgeCoords could be 0 when this is called from the combine code
|
|
if (numXCoords > 0)
|
|
{
|
|
if (numXCoords > 2)
|
|
{
|
|
// Try to compact the X Span data.
|
|
// First, make a copy of the data if we need to
|
|
// so we can compact it in place.
|
|
if (combineCoords != NULL)
|
|
{
|
|
combineCoords->Reset(FALSE);
|
|
if (combineCoords->AddMultiple(xCoords, numXCoords) != Ok)
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
xCoords = combineCoords->GetDataBuffer();
|
|
}
|
|
|
|
INT indexDest = 0;
|
|
INT index = 2;
|
|
INT indexLast = numXCoords - 2;
|
|
|
|
numXCoords = 2;
|
|
do
|
|
{
|
|
if ((xCoords[indexDest + 1]) >= xCoords[index])
|
|
{
|
|
xCoords[indexDest + 1] = xCoords[index + 1];
|
|
index += 2;
|
|
}
|
|
else
|
|
{
|
|
indexDest += 2;
|
|
if (indexDest != index)
|
|
{
|
|
xCoords[indexDest] = xCoords[index];
|
|
xCoords[indexDest+1] = xCoords[index+1];
|
|
}
|
|
index += 2;
|
|
numXCoords += 2;
|
|
}
|
|
} while (index <= indexLast);
|
|
}
|
|
|
|
#ifndef USE_YSPAN_BUILDER
|
|
DpComplexRegion * complexData = regionBuilder->ComplexData;
|
|
|
|
if (complexData->NumYSpans > 0)
|
|
{
|
|
// Try to add this row to the previous row,
|
|
// if the scans are the same and the y's match up
|
|
INT * ySpanPrev;
|
|
INT * xSpanPrev;
|
|
INT numXCoordsPrev;
|
|
|
|
ySpanPrev = complexData->GetYSpan(complexData->NumYSpans - 1);
|
|
xSpanPrev = complexData->XCoords + ySpanPrev[YSPAN_XOFFSET];
|
|
numXCoordsPrev = ySpanPrev[YSPAN_XCOUNT];
|
|
|
|
if ((numXCoordsPrev == numXCoords) &&
|
|
(ySpanPrev[YSPAN_YMAX] >= yMin) &&
|
|
(GpMemcmp (xSpanPrev, xCoords, numXCoords * sizeof(INT)) == 0))
|
|
{
|
|
// Yes, it did match -- just set the new yMax and return
|
|
regionBuilder->YMax = yMax;
|
|
ySpanPrev[YSPAN_YMAX] = yMax;
|
|
return Ok;
|
|
}
|
|
}
|
|
#endif // USE_YSPAN_BUILDER
|
|
|
|
return regionBuilder->OutputYSpan(yMin, yMax, xCoords, numXCoords);
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine another region with this one using the AND operator.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - the region to combine with this one
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::And(
|
|
const DpRegion * region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (Empty || (region->Infinite) || (region == this))
|
|
{
|
|
return Ok;
|
|
}
|
|
if (Infinite)
|
|
{
|
|
return Set(region);
|
|
}
|
|
if (region->Empty)
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
// check if the region totally encompasses this
|
|
if ((region->ComplexData == NULL) &&
|
|
(region->XMin <= XMin) &&
|
|
(region->YMin <= YMin) &&
|
|
(region->XMax >= XMax) &&
|
|
(region->YMax >= YMax))
|
|
{
|
|
return Ok;
|
|
}
|
|
// check if this totally encompasses the region
|
|
if ((ComplexData == NULL) &&
|
|
(XMin <= region->XMin) &&
|
|
(YMin <= region->YMin) &&
|
|
(XMax >= region->XMax) &&
|
|
(YMax >= region->YMax))
|
|
{
|
|
return Set(region);
|
|
}
|
|
// check for no intersection
|
|
if ((XMin >= region->XMax) ||
|
|
(region->XMax <= XMin) ||
|
|
(XMax <= region->XMin) ||
|
|
(region->XMin >= XMax) ||
|
|
(YMin >= region->YMax) ||
|
|
(region->YMax <= YMin) ||
|
|
(YMax <= region->YMin) ||
|
|
(region->YMin >= YMax))
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
else
|
|
{
|
|
INT * ySpan1;
|
|
INT * ySpan2;
|
|
INT * ySpan1Last;
|
|
INT * ySpan2Last;
|
|
INT * xCoords1;
|
|
INT * xCoords2;
|
|
INT ySpan1Tmp[YSPAN_SIZE];
|
|
INT ySpan2Tmp[YSPAN_SIZE];
|
|
INT xCoords1Tmp[2];
|
|
INT xCoords2Tmp[2];
|
|
INT yMin1 = YMin;
|
|
INT yMax1;
|
|
INT yMin2 = region->YMin;
|
|
INT yMax2;
|
|
INT numYSpans1;
|
|
INT numYSpans2;
|
|
INT combineTmp[4];
|
|
DynIntArray combineCoords(combineTmp, 4);
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
numYSpans1 = 1;
|
|
ySpan1 = ySpan1Tmp;
|
|
ySpan1Last = ySpan1Tmp;
|
|
yMax1 = YMax;
|
|
|
|
ySpan1[YSPAN_YMIN] = yMin1;
|
|
ySpan1[YSPAN_YMAX] = yMax1;
|
|
ySpan1[YSPAN_XOFFSET] = 0;
|
|
ySpan1[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords1 = xCoords1Tmp;
|
|
xCoords1[0] = XMin;
|
|
xCoords1[1] = XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans1 = ComplexData->NumYSpans;
|
|
ySpan1 = ComplexData->YSpans;
|
|
ySpan1Last = ySpan1 + ((numYSpans1 - 1) * YSPAN_SIZE);
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
xCoords1 = ComplexData->XCoords;
|
|
}
|
|
if (region->ComplexData == NULL)
|
|
{
|
|
numYSpans2 = 1;
|
|
ySpan2 = ySpan2Tmp;
|
|
ySpan2Last = ySpan2Tmp;
|
|
yMax2 = region->YMax;
|
|
|
|
ySpan2[YSPAN_YMIN] = yMin2;
|
|
ySpan2[YSPAN_YMAX] = yMax2;
|
|
ySpan2[YSPAN_XOFFSET] = 0;
|
|
ySpan2[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords2 = xCoords2Tmp;
|
|
xCoords2[0] = region->XMin;
|
|
xCoords2[1] = region->XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans2 = region->ComplexData->NumYSpans;
|
|
ySpan2 = region->ComplexData->YSpans;
|
|
ySpan2Last = ySpan2 + ((numYSpans2 - 1) * YSPAN_SIZE);
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
xCoords2 = region->ComplexData->XCoords;
|
|
}
|
|
|
|
DpRegionBuilder regionBuilder(numYSpans1 + numYSpans2);
|
|
|
|
if (!regionBuilder.IsValid())
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (yMin1 <= yMin2)
|
|
{
|
|
if (yMax1 > yMin2)
|
|
{
|
|
if (XSpansAND(
|
|
&combineCoords,
|
|
xCoords1 + ySpan1[YSPAN_XOFFSET],
|
|
ySpan1[YSPAN_XCOUNT],
|
|
xCoords2 + ySpan2[YSPAN_XOFFSET],
|
|
ySpan2[YSPAN_XCOUNT]) == Ok)
|
|
{
|
|
if (yMax1 <= yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMax1,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
goto AndIncYSpan1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMax2,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
goto AndIncYSpan2;
|
|
}
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
goto AndIncYSpan1;
|
|
}
|
|
if (yMax2 > yMin1)
|
|
{
|
|
if (XSpansAND(
|
|
&combineCoords,
|
|
xCoords1 + ySpan1[YSPAN_XOFFSET],
|
|
ySpan1[YSPAN_XCOUNT],
|
|
xCoords2 + ySpan2[YSPAN_XOFFSET],
|
|
ySpan2[YSPAN_XCOUNT]) == Ok)
|
|
{
|
|
if (yMax2 <= yMax1)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax2,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
goto AndIncYSpan2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
goto AndIncYSpan1;
|
|
}
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
// else goto AndIncYSpan2
|
|
|
|
AndIncYSpan2:
|
|
if ((ySpan2 += YSPAN_SIZE) > ySpan2Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
continue;
|
|
|
|
AndIncYSpan1:
|
|
if ((ySpan1 += YSPAN_SIZE) > ySpan1Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
return Set(regionBuilder);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine a set of X spans from each region with the AND operator.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] combineCoords - where to put the combined coordinates
|
|
* [IN] xSpan1 - x spans from this region
|
|
* [IN] numXCoords1 - number of xSpan1 coordinates
|
|
* [IN] xSpan2 - x spans from the other region
|
|
* [IN] numXCoords2 - number of xSpan2 coordinates
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::XSpansAND(
|
|
DynIntArray * combineCoords,
|
|
INT * xSpan1,
|
|
INT numXCoords1,
|
|
INT * xSpan2,
|
|
INT numXCoords2)
|
|
{
|
|
INT * XCoords;
|
|
INT count = 0;
|
|
|
|
combineCoords->Reset(FALSE);
|
|
XCoords = combineCoords->AddMultiple(numXCoords1 + numXCoords2);
|
|
|
|
if (XCoords != NULL)
|
|
{
|
|
INT xMin1 = xSpan1[0];
|
|
INT xMax1 = xSpan1[1];
|
|
INT xMin2 = xSpan2[0];
|
|
INT xMax2 = xSpan2[1];
|
|
|
|
for (;;)
|
|
{
|
|
if (xMin1 <= xMin2)
|
|
{
|
|
if (xMax1 > xMin2)
|
|
{
|
|
XCoords[count++] = xMin2; // left
|
|
if (xMax1 <= xMax2)
|
|
{
|
|
XCoords[count++] = xMax1; // right
|
|
goto AndIncXSpan1;
|
|
}
|
|
XCoords[count++] = xMax2; // right
|
|
goto AndIncXSpan2;
|
|
}
|
|
goto AndIncXSpan1;
|
|
}
|
|
if (xMax2 > xMin1)
|
|
{
|
|
XCoords[count++] = xMin1; // left
|
|
if (xMax2 <= xMax1)
|
|
{
|
|
XCoords[count++] = xMax2; // right
|
|
goto AndIncXSpan2;
|
|
}
|
|
XCoords[count++] = xMax1; // right
|
|
goto AndIncXSpan1;
|
|
}
|
|
// else goto AndIncXSpan2;
|
|
|
|
AndIncXSpan2:
|
|
if ((numXCoords2 -= 2) < 2)
|
|
{
|
|
break;
|
|
}
|
|
xSpan2 += 2;
|
|
xMin2 = xSpan2[0];
|
|
xMax2 = xSpan2[1];
|
|
continue;
|
|
|
|
AndIncXSpan1:
|
|
if ((numXCoords1 -= 2) < 2)
|
|
{
|
|
break;
|
|
}
|
|
xSpan1 += 2;
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
combineCoords->SetCount(count);
|
|
return Ok;
|
|
}
|
|
return OutOfMemory;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine another region with this one using the OR operator.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - the region to combine with this one
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Or(
|
|
const DpRegion * region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (Infinite || region->Empty || (region == this))
|
|
{
|
|
return Ok;
|
|
}
|
|
if (region->Infinite)
|
|
{
|
|
SetInfinite();
|
|
return Ok;
|
|
}
|
|
if (Empty)
|
|
{
|
|
return Set(region);
|
|
}
|
|
// check if the region totally encompasses this
|
|
if ((region->ComplexData == NULL) &&
|
|
(region->XMin <= XMin) &&
|
|
(region->YMin <= YMin) &&
|
|
(region->XMax >= XMax) &&
|
|
(region->YMax >= YMax))
|
|
{
|
|
Set(region->XMin, region->YMin,
|
|
region->XMax - region->XMin,
|
|
region->YMax - region->YMin);
|
|
return Ok;
|
|
}
|
|
// check if this totally encompasses the region
|
|
if ((ComplexData == NULL) &&
|
|
(XMin <= region->XMin) &&
|
|
(YMin <= region->YMin) &&
|
|
(XMax >= region->XMax) &&
|
|
(YMax >= region->YMax))
|
|
{
|
|
return Ok;
|
|
}
|
|
else
|
|
{
|
|
INT * ySpan1;
|
|
INT * ySpan2;
|
|
INT * ySpan1Last;
|
|
INT * ySpan2Last;
|
|
INT * xCoords1;
|
|
INT * xCoords2;
|
|
INT ySpan1Tmp[YSPAN_SIZE];
|
|
INT ySpan2Tmp[YSPAN_SIZE];
|
|
INT xCoords1Tmp[2];
|
|
INT xCoords2Tmp[2];
|
|
INT yMin1 = YMin;
|
|
INT yMax1;
|
|
INT yMin2 = region->YMin;
|
|
INT yMax2;
|
|
INT numYSpans1;
|
|
INT numYSpans2;
|
|
INT combineTmp[4];
|
|
DynIntArray combineCoords(combineTmp, 4);
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
numYSpans1 = 1;
|
|
ySpan1 = ySpan1Tmp;
|
|
ySpan1Last = ySpan1Tmp;
|
|
yMax1 = YMax;
|
|
|
|
ySpan1[YSPAN_YMIN] = yMin1;
|
|
ySpan1[YSPAN_YMAX] = yMax1;
|
|
ySpan1[YSPAN_XOFFSET] = 0;
|
|
ySpan1[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords1 = xCoords1Tmp;
|
|
xCoords1[0] = XMin;
|
|
xCoords1[1] = XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans1 = ComplexData->NumYSpans;
|
|
ySpan1 = ComplexData->YSpans;
|
|
ySpan1Last = ySpan1 + ((numYSpans1 - 1) * YSPAN_SIZE);
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
xCoords1 = ComplexData->XCoords;
|
|
}
|
|
if (region->ComplexData == NULL)
|
|
{
|
|
numYSpans2 = 1;
|
|
ySpan2 = ySpan2Tmp;
|
|
ySpan2Last = ySpan2Tmp;
|
|
yMax2 = region->YMax;
|
|
|
|
ySpan2[YSPAN_YMIN] = yMin2;
|
|
ySpan2[YSPAN_YMAX] = yMax2;
|
|
ySpan2[YSPAN_XOFFSET] = 0;
|
|
ySpan2[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords2 = xCoords2Tmp;
|
|
xCoords2[0] = region->XMin;
|
|
xCoords2[1] = region->XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans2 = region->ComplexData->NumYSpans;
|
|
ySpan2 = region->ComplexData->YSpans;
|
|
ySpan2Last = ySpan2 + ((numYSpans2 - 1) * YSPAN_SIZE);
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
xCoords2 = region->ComplexData->XCoords;
|
|
}
|
|
|
|
DpRegionBuilder regionBuilder(numYSpans1 + numYSpans2);
|
|
BOOL done = FALSE;
|
|
INT numXCoords;
|
|
INT * xSpan;
|
|
|
|
if (!regionBuilder.IsValid())
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (yMin1 < yMin2)
|
|
{
|
|
xSpan = xCoords1 + ySpan1[YSPAN_XOFFSET];
|
|
numXCoords = ySpan1[YSPAN_XCOUNT];
|
|
|
|
if (yMax1 <= yMin2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
goto OrIncYSpan1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMin2,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
yMin1 = yMin2;
|
|
continue; // no increment
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
else if (yMin1 > yMin2)
|
|
{
|
|
xSpan = xCoords2 + ySpan2[YSPAN_XOFFSET];
|
|
numXCoords = ySpan2[YSPAN_XCOUNT];
|
|
|
|
if (yMax2 <= yMin1)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMax2,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
goto OrIncYSpan2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMin1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
yMin2 = yMin1;
|
|
continue; // no increment
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
// else if (yMin1 == yMin2)
|
|
if (XSpansOR (
|
|
&combineCoords,
|
|
xCoords1 + ySpan1[YSPAN_XOFFSET],
|
|
ySpan1[YSPAN_XCOUNT],
|
|
xCoords2 + ySpan2[YSPAN_XOFFSET],
|
|
ySpan2[YSPAN_XCOUNT]) == Ok)
|
|
{
|
|
if (yMax1 < yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
yMin2 = yMax1;
|
|
goto OrIncYSpan1;
|
|
}
|
|
}
|
|
else if (yMax1 > yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax2,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
yMin1 = yMax2;
|
|
goto OrIncYSpan2;
|
|
}
|
|
}
|
|
else // if (yMax1 == yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
goto OrIncYSpanBoth;
|
|
}
|
|
}
|
|
}
|
|
return GenericError;
|
|
|
|
OrIncYSpanBoth:
|
|
if ((ySpan2 += YSPAN_SIZE) > ySpan2Last)
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
}
|
|
|
|
if ((ySpan1 += YSPAN_SIZE) > ySpan1Last)
|
|
{
|
|
goto OrCheckMoreY2Spans;
|
|
}
|
|
else
|
|
{
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
|
|
OrIncYSpan2:
|
|
if ((ySpan2 += YSPAN_SIZE) > ySpan2Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
continue;
|
|
|
|
OrIncYSpan1:
|
|
if ((ySpan1 += YSPAN_SIZE) > ySpan1Last)
|
|
{
|
|
goto OrCheckMoreY2Spans;
|
|
}
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
|
|
if (ySpan1 <= ySpan1Last)
|
|
{
|
|
for (;;)
|
|
{
|
|
xSpan = xCoords1 + ySpan1[YSPAN_XOFFSET];
|
|
numXCoords = ySpan1[YSPAN_XCOUNT];
|
|
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) != Ok)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
ySpan1 += YSPAN_SIZE;
|
|
if (ySpan1 > ySpan1Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
}
|
|
|
|
OrCheckMoreY2Spans:
|
|
|
|
if (ySpan2 <= ySpan2Last)
|
|
{
|
|
for (;;)
|
|
{
|
|
xSpan = xCoords2 + ySpan2[YSPAN_XOFFSET];
|
|
numXCoords = ySpan2[YSPAN_XCOUNT];
|
|
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMax2,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) != Ok)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
ySpan2 += YSPAN_SIZE;
|
|
if (ySpan2 > ySpan2Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
}
|
|
}
|
|
return Set(regionBuilder);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine a set of X spans from each region with the OR operator.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] combineCoords - where to put the combined coordinates
|
|
* [IN] xSpan1 - x spans from this region
|
|
* [IN] numXCoords1 - number of xSpan1 coordinates
|
|
* [IN] xSpan2 - x spans from the other region
|
|
* [IN] numXCoords2 - number of xSpan2 coordinates
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::XSpansOR (
|
|
DynIntArray * combineCoords,
|
|
INT * xSpan1,
|
|
INT numXCoords1,
|
|
INT * xSpan2,
|
|
INT numXCoords2)
|
|
{
|
|
INT * XCoords;
|
|
INT count = 0;
|
|
|
|
combineCoords->Reset(FALSE);
|
|
XCoords = combineCoords->AddMultiple(numXCoords1 + numXCoords2);
|
|
|
|
if (XCoords != NULL)
|
|
{
|
|
INT xMin1 = xSpan1[0];
|
|
INT xMax1 = xSpan1[1];
|
|
INT xMin2 = xSpan2[0];
|
|
INT xMax2 = xSpan2[1];
|
|
BOOL done = FALSE;
|
|
|
|
for (;;)
|
|
{
|
|
if (xMin1 <= xMin2)
|
|
{
|
|
XCoords[count++] = xMin1;
|
|
if (xMax1 <= xMin2)
|
|
{
|
|
XCoords[count++] = xMax1;
|
|
goto OrIncXSpan1;
|
|
}
|
|
XCoords[count++] = (xMax1 <= xMax2) ? xMax2 : xMax1;
|
|
goto OrIncXSpanBoth;
|
|
}
|
|
XCoords[count++] = xMin2;
|
|
if (xMax2 <= xMin1)
|
|
{
|
|
XCoords[count++] = xMax2;
|
|
goto OrIncXSpan2;
|
|
}
|
|
XCoords[count++] = (xMax2 <= xMax1) ? xMax1 : xMax2;
|
|
// goto OrIncXSpanBoth;
|
|
|
|
OrIncXSpanBoth:
|
|
xSpan2 += 2;
|
|
if ((numXCoords2 -= 2) < 2)
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
xMin2 = xSpan2[0];
|
|
xMax2 = xSpan2[1];
|
|
}
|
|
|
|
xSpan1 += 2;
|
|
if ((numXCoords1 -= 2) < 2)
|
|
{
|
|
goto OrCheckMoreX2Spans;
|
|
}
|
|
else
|
|
{
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
|
|
OrIncXSpan2:
|
|
xSpan2 += 2;
|
|
if ((numXCoords2 -= 2) < 2)
|
|
{
|
|
break;
|
|
}
|
|
xMin2 = xSpan2[0];
|
|
xMax2 = xSpan2[1];
|
|
continue;
|
|
|
|
OrIncXSpan1:
|
|
xSpan1 += 2;
|
|
if ((numXCoords1 -= 2) < 2)
|
|
{
|
|
goto OrCheckMoreX2Spans;
|
|
}
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
|
|
while (numXCoords1 >= 2)
|
|
{
|
|
XCoords[count++] = xSpan1[0];
|
|
XCoords[count++] = xSpan1[1];
|
|
|
|
numXCoords1 -= 2;
|
|
xSpan1 += 2;
|
|
}
|
|
|
|
OrCheckMoreX2Spans:
|
|
|
|
while (numXCoords2 >= 2)
|
|
{
|
|
XCoords[count++] = xSpan2[0];
|
|
XCoords[count++] = xSpan2[1];
|
|
|
|
numXCoords2 -= 2;
|
|
xSpan2 += 2;
|
|
}
|
|
combineCoords->SetCount(count);
|
|
return Ok;
|
|
}
|
|
return GenericError;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine another region with this one using the XOR operator.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - the region to combine with this one
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Xor(
|
|
const DpRegion * region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (region == this)
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
if (region->Empty)
|
|
{
|
|
return Ok;
|
|
}
|
|
if (Empty)
|
|
{
|
|
return Set(region);
|
|
}
|
|
if (Infinite)
|
|
{
|
|
if (region->Infinite)
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
return Exclude(region);
|
|
}
|
|
if (region->Infinite)
|
|
{
|
|
return Complement(region);
|
|
}
|
|
else
|
|
{
|
|
INT * ySpan1;
|
|
INT * ySpan2;
|
|
INT * ySpan1Last;
|
|
INT * ySpan2Last;
|
|
INT * xCoords1;
|
|
INT * xCoords2;
|
|
INT ySpan1Tmp[YSPAN_SIZE];
|
|
INT ySpan2Tmp[YSPAN_SIZE];
|
|
INT xCoords1Tmp[2];
|
|
INT xCoords2Tmp[2];
|
|
INT yMin1 = YMin;
|
|
INT yMax1;
|
|
INT yMin2 = region->YMin;
|
|
INT yMax2;
|
|
INT numYSpans1;
|
|
INT numYSpans2;
|
|
INT combineTmp[4];
|
|
DynIntArray combineCoords(combineTmp, 4);
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
numYSpans1 = 1;
|
|
ySpan1 = ySpan1Tmp;
|
|
ySpan1Last = ySpan1Tmp;
|
|
yMax1 = YMax;
|
|
|
|
ySpan1[YSPAN_YMIN] = yMin1;
|
|
ySpan1[YSPAN_YMAX] = yMax1;
|
|
ySpan1[YSPAN_XOFFSET] = 0;
|
|
ySpan1[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords1 = xCoords1Tmp;
|
|
xCoords1[0] = XMin;
|
|
xCoords1[1] = XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans1 = ComplexData->NumYSpans;
|
|
ySpan1 = ComplexData->YSpans;
|
|
ySpan1Last = ySpan1 + ((numYSpans1 - 1) * YSPAN_SIZE);
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
xCoords1 = ComplexData->XCoords;
|
|
}
|
|
if (region->ComplexData == NULL)
|
|
{
|
|
numYSpans2 = 1;
|
|
ySpan2 = ySpan2Tmp;
|
|
ySpan2Last = ySpan2Tmp;
|
|
yMax2 = region->YMax;
|
|
|
|
ySpan2[YSPAN_YMIN] = yMin2;
|
|
ySpan2[YSPAN_YMAX] = yMax2;
|
|
ySpan2[YSPAN_XOFFSET] = 0;
|
|
ySpan2[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords2 = xCoords2Tmp;
|
|
xCoords2[0] = region->XMin;
|
|
xCoords2[1] = region->XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans2 = region->ComplexData->NumYSpans;
|
|
ySpan2 = region->ComplexData->YSpans;
|
|
ySpan2Last = ySpan2 + ((numYSpans2 - 1) * YSPAN_SIZE);
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
xCoords2 = region->ComplexData->XCoords;
|
|
}
|
|
|
|
DpRegionBuilder regionBuilder(2 * (numYSpans1 + numYSpans2));
|
|
BOOL done = FALSE;
|
|
INT numXCoords;
|
|
INT * xSpan;
|
|
|
|
if (!regionBuilder.IsValid())
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (yMin1 < yMin2)
|
|
{
|
|
xSpan = xCoords1 + ySpan1[YSPAN_XOFFSET];
|
|
numXCoords = ySpan1[YSPAN_XCOUNT];
|
|
|
|
if (yMax1 <= yMin2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
goto XorIncYSpan1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMin2,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
yMin1 = yMin2;
|
|
continue; // no increment
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
else if (yMin1 > yMin2)
|
|
{
|
|
xSpan = xCoords2 + ySpan2[YSPAN_XOFFSET];
|
|
numXCoords = ySpan2[YSPAN_XCOUNT];
|
|
|
|
if (yMax2 <= yMin1)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMax2,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
goto XorIncYSpan2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMin1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
yMin2 = yMin1;
|
|
continue; // no increment
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
// else if (yMin1 == yMin2)
|
|
if (XSpansXOR (
|
|
&combineCoords,
|
|
xCoords1 + ySpan1[YSPAN_XOFFSET],
|
|
ySpan1[YSPAN_XCOUNT],
|
|
xCoords2 + ySpan2[YSPAN_XOFFSET],
|
|
ySpan2[YSPAN_XCOUNT]) == Ok)
|
|
{
|
|
if (yMax1 < yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
yMin2 = yMax1;
|
|
goto XorIncYSpan1;
|
|
}
|
|
}
|
|
else if (yMax1 > yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax2,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
yMin1 = yMax2;
|
|
goto XorIncYSpan2;
|
|
}
|
|
}
|
|
else // if (yMax1 == yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
goto XorIncYSpanBoth;
|
|
}
|
|
}
|
|
}
|
|
return GenericError;
|
|
|
|
XorIncYSpanBoth:
|
|
if ((ySpan2 += YSPAN_SIZE) > ySpan2Last)
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
}
|
|
|
|
if ((ySpan1 += YSPAN_SIZE) > ySpan1Last)
|
|
{
|
|
goto XorCheckMoreY2Spans;
|
|
}
|
|
else
|
|
{
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
|
|
XorIncYSpan2:
|
|
if ((ySpan2 += YSPAN_SIZE) > ySpan2Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
continue;
|
|
|
|
XorIncYSpan1:
|
|
if ((ySpan1 += YSPAN_SIZE) > ySpan1Last)
|
|
{
|
|
goto XorCheckMoreY2Spans;
|
|
}
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
|
|
if (ySpan1 <= ySpan1Last)
|
|
{
|
|
for (;;)
|
|
{
|
|
xSpan = xCoords1 + ySpan1[YSPAN_XOFFSET];
|
|
numXCoords = ySpan1[YSPAN_XCOUNT];
|
|
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) != Ok)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
ySpan1 += YSPAN_SIZE;
|
|
if (ySpan1 > ySpan1Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
}
|
|
|
|
XorCheckMoreY2Spans:
|
|
|
|
if (ySpan2 <= ySpan2Last)
|
|
{
|
|
for (;;)
|
|
{
|
|
xSpan = xCoords2 + ySpan2[YSPAN_XOFFSET];
|
|
numXCoords = ySpan2[YSPAN_XCOUNT];
|
|
|
|
if (CompactAndOutput(
|
|
yMin2,
|
|
yMax2,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) != Ok)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
ySpan2 += YSPAN_SIZE;
|
|
if (ySpan2 > ySpan2Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
}
|
|
}
|
|
return Set(regionBuilder);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine a set of X spans from each region with the XOR operator.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] combineCoords - where to put the combined coordinates
|
|
* [IN] xSpan1 - x spans from this region
|
|
* [IN] numXCoords1 - number of xSpan1 coordinates
|
|
* [IN] xSpan2 - x spans from the other region
|
|
* [IN] numXCoords2 - number of xSpan2 coordinates
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::XSpansXOR(
|
|
DynIntArray * combineCoords,
|
|
INT * xSpan1,
|
|
INT numXCoords1,
|
|
INT * xSpan2,
|
|
INT numXCoords2)
|
|
{
|
|
INT * XCoords;
|
|
INT count = 0;
|
|
|
|
combineCoords->Reset(FALSE);
|
|
XCoords = combineCoords->AddMultiple(numXCoords1 + numXCoords2);
|
|
|
|
if (XCoords != NULL)
|
|
{
|
|
INT xMin1 = xSpan1[0];
|
|
INT xMax1 = xSpan1[1];
|
|
INT xMin2 = xSpan2[0];
|
|
INT xMax2 = xSpan2[1];
|
|
BOOL done = FALSE;
|
|
|
|
for (;;)
|
|
{
|
|
if (xMin1 < xMin2)
|
|
{
|
|
XCoords[count++] = xMin1; // left
|
|
if (xMax1 <= xMin2)
|
|
{
|
|
XCoords[count++] = xMax1; // right
|
|
goto XorIncXSpan1;
|
|
}
|
|
XCoords[count++] = xMin2; // right
|
|
if (xMax1 < xMax2)
|
|
{
|
|
xMin2 = xMax1;
|
|
goto XorIncXSpan1;
|
|
}
|
|
else if (xMax1 > xMax2)
|
|
{
|
|
xMin1 = xMax2;
|
|
goto XorIncXSpan2;
|
|
}
|
|
// else if (xMax1 == xMax2)
|
|
goto XorIncXSpanBoth;
|
|
}
|
|
else if (xMin1 > xMin2)
|
|
{
|
|
XCoords[count++] = xMin2; // left
|
|
if (xMax2 <= xMin1)
|
|
{
|
|
XCoords[count++] = xMax2; // right
|
|
goto XorIncXSpan2;
|
|
}
|
|
XCoords[count++] = xMin1; // right
|
|
if (xMax1 < xMax2)
|
|
{
|
|
xMin2 = xMax1;
|
|
goto XorIncXSpan1;
|
|
}
|
|
else if (xMax1 > xMax2)
|
|
{
|
|
xMin1 = xMax2;
|
|
goto XorIncXSpan2;
|
|
}
|
|
// else if (xMax1 == xMax2)
|
|
goto XorIncXSpanBoth;
|
|
}
|
|
// else if (xMin1 == xMin2)
|
|
if (xMax1 < xMax2)
|
|
{
|
|
xMin2 = xMax1;
|
|
goto XorIncXSpan1;
|
|
}
|
|
else if (xMax1 > xMax2)
|
|
{
|
|
xMin1 = xMax2;
|
|
goto XorIncXSpan2;
|
|
}
|
|
// else if (xMax1 == xMax2)
|
|
// goto XorIncXSpanBoth;
|
|
|
|
XorIncXSpanBoth:
|
|
xSpan2 += 2;
|
|
if ((numXCoords2 -= 2) < 2)
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
xMin2 = xSpan2[0];
|
|
xMax2 = xSpan2[1];
|
|
}
|
|
|
|
xSpan1 += 2;
|
|
if ((numXCoords1 -= 2) < 2)
|
|
{
|
|
goto XorCheckMoreX2Spans;
|
|
}
|
|
else
|
|
{
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
|
|
XorIncXSpan2:
|
|
xSpan2 += 2;
|
|
if ((numXCoords2 -= 2) < 2)
|
|
{
|
|
break;
|
|
}
|
|
xMin2 = xSpan2[0];
|
|
xMax2 = xSpan2[1];
|
|
continue;
|
|
|
|
XorIncXSpan1:
|
|
xSpan1 += 2;
|
|
if ((numXCoords1 -= 2) < 2)
|
|
{
|
|
goto XorCheckMoreX2Spans;
|
|
}
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
|
|
if (numXCoords1 >= 2)
|
|
{
|
|
for (;;)
|
|
{
|
|
XCoords[count++] = xMin1;
|
|
XCoords[count++] = xMax1;
|
|
|
|
numXCoords1 -= 2;
|
|
if (numXCoords1 < 2)
|
|
{
|
|
break;
|
|
}
|
|
xSpan1 += 2;
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
}
|
|
|
|
XorCheckMoreX2Spans:
|
|
|
|
if (numXCoords2 >= 2)
|
|
{
|
|
for (;;)
|
|
{
|
|
XCoords[count++] = xMin2;
|
|
XCoords[count++] = xMax2;
|
|
|
|
numXCoords2 -= 2;
|
|
if (numXCoords2 < 2)
|
|
{
|
|
break;
|
|
}
|
|
xSpan2 += 2;
|
|
xMin2 = xSpan2[0];
|
|
xMax2 = xSpan2[1];
|
|
}
|
|
}
|
|
combineCoords->SetCount(count);
|
|
return Ok;
|
|
}
|
|
return GenericError;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine another region with this one using the Complement operator.
|
|
* i.e. this = region - this
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - the region to combine with this one
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Complement(
|
|
const DpRegion * region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (region->Empty || (region == this) || Infinite)
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
if (Empty)
|
|
{
|
|
return Set(region);
|
|
}
|
|
// check if this totally encompasses the region
|
|
if ((ComplexData == NULL) &&
|
|
(XMin <= region->XMin) &&
|
|
(YMin <= region->YMin) &&
|
|
(XMax >= region->XMax) &&
|
|
(YMax >= region->YMax))
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
// check for no intersection
|
|
if ((XMin >= region->XMax) ||
|
|
(region->XMax <= XMin) ||
|
|
(XMax <= region->XMin) ||
|
|
(region->XMin >= XMax) ||
|
|
(YMin >= region->YMax) ||
|
|
(region->YMax <= YMin) ||
|
|
(YMax <= region->YMin) ||
|
|
(region->YMin >= YMax))
|
|
{
|
|
return Set(region);
|
|
}
|
|
return Diff(const_cast<DpRegion *>(region), this, FALSE);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine another region with this one using the Exclude operator.
|
|
* i.e. this = this - region
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region - the region to combine with this one
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Exclude(
|
|
const DpRegion * region
|
|
)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT((region != NULL) && region->IsValid());
|
|
|
|
if (Empty || region->Empty)
|
|
{
|
|
return Ok;
|
|
}
|
|
if ((region == this) || region->Infinite)
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
// check if the region totally encompasses this
|
|
if ((region->ComplexData == NULL) &&
|
|
(region->XMin <= XMin) &&
|
|
(region->YMin <= YMin) &&
|
|
(region->XMax >= XMax) &&
|
|
(region->YMax >= YMax))
|
|
{
|
|
SetEmpty();
|
|
return Ok;
|
|
}
|
|
// check for no intersection
|
|
if ((XMin >= region->XMax) ||
|
|
(region->XMax <= XMin) ||
|
|
(XMax <= region->XMin) ||
|
|
(region->XMin >= XMax) ||
|
|
(YMin >= region->YMax) ||
|
|
(region->YMax <= YMin) ||
|
|
(YMax <= region->YMin) ||
|
|
(region->YMin >= YMax))
|
|
{
|
|
return Ok;
|
|
}
|
|
return Diff(this, const_cast<DpRegion *>(region), TRUE);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Subtract region2 from region1. If set1, then region1 gets the result;
|
|
* otherwise region2 gets the result.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] region1 - the 1st region
|
|
* [IN] region2 - the 2nd region
|
|
* [IN] set1 - if TRUE, region1 gets result, else region2 does
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::Diff(
|
|
DpRegion * region1,
|
|
DpRegion * region2,
|
|
BOOL set1
|
|
)
|
|
{
|
|
INT * ySpan1;
|
|
INT * ySpan2;
|
|
INT * ySpan1Last;
|
|
INT * ySpan2Last;
|
|
INT * xCoords1;
|
|
INT * xCoords2;
|
|
INT ySpan1Tmp[YSPAN_SIZE];
|
|
INT ySpan2Tmp[YSPAN_SIZE];
|
|
INT xCoords1Tmp[2];
|
|
INT xCoords2Tmp[2];
|
|
INT yMin1 = region1->YMin;
|
|
INT yMax1;
|
|
INT yMin2 = region2->YMin;
|
|
INT yMax2;
|
|
INT numYSpans1;
|
|
INT numYSpans2;
|
|
INT combineTmp[4];
|
|
DynIntArray combineCoords(combineTmp, 4);
|
|
|
|
if (region1->ComplexData == NULL)
|
|
{
|
|
numYSpans1 = 1;
|
|
ySpan1 = ySpan1Tmp;
|
|
ySpan1Last = ySpan1Tmp;
|
|
yMax1 = region1->YMax;
|
|
|
|
ySpan1[YSPAN_YMIN] = yMin1;
|
|
ySpan1[YSPAN_YMAX] = yMax1;
|
|
ySpan1[YSPAN_XOFFSET] = 0;
|
|
ySpan1[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords1 = xCoords1Tmp;
|
|
xCoords1[0] = region1->XMin;
|
|
xCoords1[1] = region1->XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans1 = region1->ComplexData->NumYSpans;
|
|
ySpan1 = region1->ComplexData->YSpans;
|
|
ySpan1Last = ySpan1 + ((numYSpans1 - 1) * YSPAN_SIZE);
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
xCoords1 = region1->ComplexData->XCoords;
|
|
}
|
|
if (region2->ComplexData == NULL)
|
|
{
|
|
numYSpans2 = 1;
|
|
ySpan2 = ySpan2Tmp;
|
|
ySpan2Last = ySpan2Tmp;
|
|
yMax2 = region2->YMax;
|
|
|
|
ySpan2[YSPAN_YMIN] = yMin2;
|
|
ySpan2[YSPAN_YMAX] = yMax2;
|
|
ySpan2[YSPAN_XOFFSET] = 0;
|
|
ySpan2[YSPAN_XCOUNT] = 2;
|
|
|
|
xCoords2 = xCoords2Tmp;
|
|
xCoords2[0] = region2->XMin;
|
|
xCoords2[1] = region2->XMax;
|
|
}
|
|
else
|
|
{
|
|
numYSpans2 = region2->ComplexData->NumYSpans;
|
|
ySpan2 = region2->ComplexData->YSpans;
|
|
ySpan2Last = ySpan2 + ((numYSpans2 - 1) * YSPAN_SIZE);
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
xCoords2 = region2->ComplexData->XCoords;
|
|
}
|
|
|
|
DpRegionBuilder regionBuilder(numYSpans1 + (2 * numYSpans2));
|
|
INT numXCoords;
|
|
INT * xSpan;
|
|
|
|
if (!regionBuilder.IsValid())
|
|
{
|
|
return OutOfMemory;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (yMin1 < yMin2)
|
|
{
|
|
xSpan = xCoords1 + ySpan1[YSPAN_XOFFSET];
|
|
numXCoords = ySpan1[YSPAN_XCOUNT];
|
|
|
|
if (yMax1 <= yMin2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
goto DiffIncYSpan1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMin2,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) == Ok)
|
|
{
|
|
yMin1 = yMin2;
|
|
continue; // no increment
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
else if (yMin1 < yMax2)
|
|
{
|
|
if (XSpansDIFF(
|
|
&combineCoords,
|
|
xCoords1 + ySpan1[YSPAN_XOFFSET],
|
|
ySpan1[YSPAN_XCOUNT],
|
|
xCoords2 + ySpan2[YSPAN_XOFFSET],
|
|
ySpan2[YSPAN_XCOUNT]) == Ok)
|
|
{
|
|
if (yMax1 <= yMax2)
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
goto DiffIncYSpan1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax2,
|
|
combineCoords.GetDataBuffer(),
|
|
combineCoords.GetCount(),
|
|
®ionBuilder, NULL) == Ok)
|
|
{
|
|
yMin1 = yMax2;
|
|
goto DiffIncYSpan2;
|
|
}
|
|
}
|
|
}
|
|
return GenericError;
|
|
}
|
|
// else goto DiffIncYSpan2;
|
|
|
|
DiffIncYSpan2:
|
|
if ((ySpan2 += YSPAN_SIZE) > ySpan2Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin2 = ySpan2[YSPAN_YMIN];
|
|
yMax2 = ySpan2[YSPAN_YMAX];
|
|
continue;
|
|
|
|
DiffIncYSpan1:
|
|
if ((ySpan1 += YSPAN_SIZE) > ySpan1Last)
|
|
{
|
|
goto DiffDone;
|
|
}
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
|
|
if (ySpan1 <= ySpan1Last)
|
|
{
|
|
for (;;)
|
|
{
|
|
xSpan = xCoords1 + ySpan1[YSPAN_XOFFSET];
|
|
numXCoords = ySpan1[YSPAN_XCOUNT];
|
|
|
|
if (CompactAndOutput(
|
|
yMin1,
|
|
yMax1,
|
|
xSpan,
|
|
numXCoords,
|
|
®ionBuilder,
|
|
&combineCoords) != Ok)
|
|
{
|
|
return GenericError;
|
|
}
|
|
|
|
ySpan1 += YSPAN_SIZE;
|
|
if (ySpan1 > ySpan1Last)
|
|
{
|
|
break;
|
|
}
|
|
yMin1 = ySpan1[YSPAN_YMIN];
|
|
yMax1 = ySpan1[YSPAN_YMAX];
|
|
}
|
|
}
|
|
DiffDone:
|
|
if (set1)
|
|
{
|
|
return region1->Set(regionBuilder);
|
|
}
|
|
return region2->Set(regionBuilder);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Combine a set of X spans from each region with the DIFF operator.
|
|
* i.e. xSpan1 - xSpan2
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] combineCoords - where to put the combined coordinates
|
|
* [IN] xSpan1 - x spans from this region
|
|
* [IN] numXCoords1 - number of xSpan1 coordinates
|
|
* [IN] xSpan2 - x spans from the other region
|
|
* [IN] numXCoords2 - number of xSpan2 coordinates
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/06/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpRegion::XSpansDIFF(
|
|
DynIntArray * combineCoords,
|
|
INT * xSpan1,
|
|
INT numXCoords1,
|
|
INT * xSpan2,
|
|
INT numXCoords2)
|
|
{
|
|
INT * XCoords;
|
|
INT count = 0;
|
|
|
|
combineCoords->Reset(FALSE);
|
|
XCoords = combineCoords->AddMultiple(numXCoords1 + numXCoords2);
|
|
|
|
if (XCoords != NULL)
|
|
{
|
|
INT xMin1 = xSpan1[0];
|
|
INT xMax1 = xSpan1[1];
|
|
INT xMin2 = xSpan2[0];
|
|
INT xMax2 = xSpan2[1];
|
|
|
|
for (;;)
|
|
{
|
|
if (xMin1 < xMin2)
|
|
{
|
|
XCoords[count++] = xMin1; // left
|
|
if (xMax1 <= xMin2)
|
|
{
|
|
XCoords[count++] = xMax1; // right
|
|
goto DiffIncXSpan1;
|
|
}
|
|
XCoords[count++] = xMin2; // right
|
|
xMin1 = xMin2;
|
|
continue; // no increment
|
|
}
|
|
else if (xMin1 < xMax2)
|
|
{
|
|
if (xMax1 <= xMax2)
|
|
{
|
|
goto DiffIncXSpan1;
|
|
}
|
|
xMin1 = xMax2;
|
|
// goto DiffIncXSpan2;
|
|
}
|
|
// else goto DiffIncXSpan2;
|
|
|
|
// DiffIncXSpan2:
|
|
xSpan2 += 2;
|
|
if ((numXCoords2 -= 2) < 2)
|
|
{
|
|
break;
|
|
}
|
|
xMin2 = xSpan2[0];
|
|
xMax2 = xSpan2[1];
|
|
continue;
|
|
|
|
DiffIncXSpan1:
|
|
xSpan1 += 2;
|
|
if ((numXCoords1 -= 2) < 2)
|
|
{
|
|
goto DiffDone;
|
|
}
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
|
|
if (numXCoords1 >= 2)
|
|
{
|
|
for (;;)
|
|
{
|
|
XCoords[count++] = xMin1;
|
|
XCoords[count++] = xMax1;
|
|
|
|
numXCoords1 -= 2;
|
|
if (numXCoords1 < 2)
|
|
{
|
|
break;
|
|
}
|
|
xSpan1 += 2;
|
|
xMin1 = xSpan1[0];
|
|
xMax1 = xSpan1[1];
|
|
}
|
|
}
|
|
DiffDone:
|
|
combineCoords->SetCount(count);
|
|
return Ok;
|
|
}
|
|
return GenericError;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Initialize the clipper by setting the output method and setting the
|
|
* starting y span search index.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] outputClippedSpan - The output class for clipped spans
|
|
* [IN] yMin - The starting y value of the object to clip
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/12/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
VOID
|
|
DpClipRegion::InitClipping(
|
|
DpOutputSpan * outputClippedSpan,
|
|
INT yMin
|
|
)
|
|
{
|
|
OutputClippedSpan = outputClippedSpan;
|
|
|
|
// init search index if appropriate
|
|
if (ComplexData != NULL)
|
|
{
|
|
INT * ySpan;
|
|
INT ySpanIndex;
|
|
|
|
ComplexData->ResetSearchIndex();
|
|
ComplexData->YSpanSearch (yMin, &ySpan, &ySpanIndex);
|
|
ComplexData->YSearchIndex = ySpanIndex;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* The method called from the rasterizer during the rasterization when a
|
|
* horizontal span has been identified. We clip it and if not clipped out,
|
|
* we send the clipped data to the output method.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] y - the Y value of the raster being output
|
|
* [IN] xMin - the X value of the left edge
|
|
* [IN] xMax - the X value of the right edge (exclusive)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/12/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
GpStatus
|
|
DpClipRegion::OutputSpan(
|
|
INT y,
|
|
INT xMin,
|
|
INT xMax // xMax is exclusive
|
|
)
|
|
{
|
|
ASSERT(!Empty && !Infinite);
|
|
ASSERT(OutputClippedSpan != NULL);
|
|
INT xMinCur = xMin;
|
|
INT xMaxCur = xMax;
|
|
|
|
// do simple clip test to bounding rectangle
|
|
if ((xMin < XMax) && (xMax > XMin) &&
|
|
(y >= YMin) && (y < YMax))
|
|
{
|
|
if (xMin < XMin)
|
|
{
|
|
xMinCur = XMin;
|
|
}
|
|
if (xMax > XMax)
|
|
{
|
|
xMaxCur = XMax;
|
|
}
|
|
}
|
|
else
|
|
return Ok;
|
|
|
|
if (ComplexData == NULL)
|
|
{
|
|
return OutputClippedSpan->OutputSpan(y, xMinCur, xMaxCur);
|
|
}
|
|
else // not a simple region
|
|
{
|
|
// find the Y span that includes the line (if any)
|
|
INT ySpanIndex = ComplexData->YSearchIndex;
|
|
INT * ySpan = ComplexData->GetYSpan (ySpanIndex);
|
|
|
|
if (y >= ySpan[YSPAN_YMIN])
|
|
{
|
|
if (y >= ySpan[YSPAN_YMAX])
|
|
{
|
|
// do forward linear search from previous point
|
|
for (;;)
|
|
{
|
|
// see if we're past the end of the tessellation
|
|
if (++ySpanIndex >= ComplexData->NumYSpans)
|
|
{
|
|
ComplexData->YSearchIndex = ComplexData->NumYSpans - 1;
|
|
return Ok; // nothing to draw
|
|
}
|
|
ySpan += YSPAN_SIZE;
|
|
if (y < ySpan[YSPAN_YMAX])
|
|
{
|
|
ComplexData->YSearchIndex = ySpanIndex;
|
|
if (y >= ySpan[YSPAN_YMIN])
|
|
{
|
|
break;
|
|
}
|
|
return Ok; // nothing to draw
|
|
}
|
|
}
|
|
}
|
|
// else yMin is inside this ySpan
|
|
}
|
|
else // need to search backward (shouldn't happen when rasterizing)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (ySpanIndex == 0)
|
|
{
|
|
ComplexData->YSearchIndex = 0;
|
|
return Ok; // nothing to draw
|
|
}
|
|
ySpanIndex--;
|
|
ySpan -= YSPAN_SIZE;
|
|
|
|
if (y >= ySpan[YSPAN_YMIN])
|
|
{
|
|
ComplexData->YSearchIndex = ySpanIndex;
|
|
if (y < ySpan[YSPAN_YMAX])
|
|
{
|
|
break;
|
|
}
|
|
return Ok; // nothing to draw
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we get here, we know there y is within a Y span and that
|
|
// ySpan points to the correct Y span
|
|
|
|
GpStatus status = Ok;
|
|
INT * xSpan;
|
|
INT numXCoords;
|
|
|
|
xSpan = ComplexData->XCoords + ySpan[YSPAN_XOFFSET];
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
|
|
for (;;)
|
|
{
|
|
if (xMax <= xSpan[0])
|
|
{
|
|
break;
|
|
}
|
|
if (xMin < xSpan[1])
|
|
{
|
|
xMinCur = (xMin < xSpan[0]) ? xSpan[0] : xMin;
|
|
xMaxCur = (xMax > xSpan[1]) ? xSpan[1] : xMax;
|
|
status = OutputClippedSpan->OutputSpan(y, xMinCur, xMaxCur);
|
|
}
|
|
// continue on with loop through x spans
|
|
|
|
if (((numXCoords -= 2) <= 0) || (status != Ok))
|
|
{
|
|
break;
|
|
}
|
|
xSpan += 2;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
INT
|
|
DpRegion::GetRects(
|
|
GpRect * rects
|
|
) const
|
|
{
|
|
if (Empty)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (Infinite)
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
rects->X = INFINITE_MIN;
|
|
rects->Y = INFINITE_MIN;
|
|
rects->Width = INFINITE_SIZE;
|
|
rects->Height = INFINITE_SIZE;
|
|
}
|
|
return 1;
|
|
}
|
|
else if (ComplexData == NULL)
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
rects->X = XMin;
|
|
rects->Y = YMin;
|
|
rects->Width = XMax - XMin;
|
|
rects->Height = YMax - YMin;
|
|
}
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
DpComplexRegion * complexData = ComplexData;
|
|
INT * xCoords = complexData->XCoords;
|
|
INT * ySpan = complexData->YSpans;
|
|
INT * ySpanLast = ySpan +
|
|
((complexData->NumYSpans - 1) * YSPAN_SIZE);
|
|
INT numXCoords;
|
|
INT yMin;
|
|
INT height;
|
|
INT xMin;
|
|
INT xMax;
|
|
|
|
do
|
|
{
|
|
yMin = ySpan[YSPAN_YMIN];
|
|
height = ySpan[YSPAN_YMAX] - yMin;
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
do
|
|
{
|
|
xMin = *xCoords++;
|
|
xMax = *xCoords++;
|
|
|
|
rects->X = xMin;
|
|
rects->Y = yMin;
|
|
rects->Width = xMax - xMin;
|
|
rects->Height = height;
|
|
|
|
rects++;
|
|
numXCoords -= 2;
|
|
} while (numXCoords >= 2);
|
|
|
|
ySpan += YSPAN_SIZE;
|
|
} while (ySpan <= ySpanLast);
|
|
}
|
|
return ComplexData->XCoordsCount / 2;
|
|
}
|
|
}
|
|
|
|
INT
|
|
DpRegion::GetRects(
|
|
GpRectF * rects
|
|
) const
|
|
{
|
|
if (Empty)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (Infinite)
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
rects->X = (REAL)INFINITE_MIN;
|
|
rects->Y = (REAL)INFINITE_MIN;
|
|
rects->Width = (REAL)INFINITE_SIZE;
|
|
rects->Height = (REAL)INFINITE_SIZE;
|
|
}
|
|
return 1;
|
|
}
|
|
else if (ComplexData == NULL)
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
rects->X = (REAL)XMin;
|
|
rects->Y = (REAL)YMin;
|
|
rects->Width = (REAL)(XMax - XMin);
|
|
rects->Height = (REAL)(YMax - YMin);
|
|
}
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
DpComplexRegion * complexData = ComplexData;
|
|
INT * xCoords = complexData->XCoords;
|
|
INT * ySpan = complexData->YSpans;
|
|
INT * ySpanLast = ySpan +
|
|
((complexData->NumYSpans - 1) * YSPAN_SIZE);
|
|
INT numXCoords;
|
|
INT yMin;
|
|
INT height;
|
|
INT xMin;
|
|
INT xMax;
|
|
|
|
do
|
|
{
|
|
yMin = ySpan[YSPAN_YMIN];
|
|
height = ySpan[YSPAN_YMAX] - yMin;
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
do
|
|
{
|
|
xMin = *xCoords++;
|
|
xMax = *xCoords++;
|
|
|
|
rects->X = (REAL)xMin;
|
|
rects->Y = (REAL)yMin;
|
|
rects->Width = (REAL)(xMax - xMin);
|
|
rects->Height = (REAL)height;
|
|
|
|
rects++;
|
|
numXCoords -= 2;
|
|
} while (numXCoords >= 2);
|
|
|
|
ySpan += YSPAN_SIZE;
|
|
} while (ySpan <= ySpanLast);
|
|
}
|
|
return ComplexData->XCoordsCount / 2;
|
|
}
|
|
}
|
|
|
|
// The WIN9x infinite max and min values are set up to be the greatest
|
|
// values that will interop with GDI HRGNs on Win9x successfully.
|
|
#define INFINITE_MIN_WIN9X -16384
|
|
#define INFINITE_MAX_WIN9X 16383
|
|
|
|
INT
|
|
DpRegion::GetRects(
|
|
RECT * rects,
|
|
BOOL clampToWin9xSize
|
|
) const
|
|
{
|
|
if (Empty)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (Infinite)
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
if (!clampToWin9xSize)
|
|
{
|
|
rects->left = INFINITE_MIN;
|
|
rects->top = INFINITE_MIN;
|
|
rects->right = INFINITE_MAX;
|
|
rects->bottom = INFINITE_MAX;
|
|
}
|
|
else
|
|
{
|
|
rects->left = INFINITE_MIN_WIN9X;
|
|
rects->top = INFINITE_MIN_WIN9X;
|
|
rects->right = INFINITE_MAX_WIN9X;
|
|
rects->bottom = INFINITE_MAX_WIN9X;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
else if (ComplexData == NULL)
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
rects->left = XMin;
|
|
rects->top = YMin;
|
|
rects->right = XMax;
|
|
rects->bottom = YMax;
|
|
|
|
if (clampToWin9xSize)
|
|
{
|
|
if (rects->left < INFINITE_MIN_WIN9X)
|
|
{
|
|
rects->left = INFINITE_MIN_WIN9X;
|
|
}
|
|
if (rects->top < INFINITE_MIN_WIN9X)
|
|
{
|
|
rects->top = INFINITE_MIN_WIN9X;
|
|
}
|
|
if (rects->right > INFINITE_MAX_WIN9X)
|
|
{
|
|
rects->right = INFINITE_MAX_WIN9X;
|
|
}
|
|
if (rects->bottom > INFINITE_MAX_WIN9X)
|
|
{
|
|
rects->bottom = INFINITE_MAX_WIN9X;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (rects != NULL)
|
|
{
|
|
DpComplexRegion * complexData = ComplexData;
|
|
INT * xCoords = complexData->XCoords;
|
|
INT * ySpan = complexData->YSpans;
|
|
INT * ySpanLast = ySpan +
|
|
((complexData->NumYSpans - 1) * YSPAN_SIZE);
|
|
INT numXCoords;
|
|
INT yMin;
|
|
INT height;
|
|
INT xMin;
|
|
INT xMax;
|
|
|
|
do
|
|
{
|
|
yMin = ySpan[YSPAN_YMIN];
|
|
height = ySpan[YSPAN_YMAX] - yMin;
|
|
numXCoords = ySpan[YSPAN_XCOUNT];
|
|
do
|
|
{
|
|
xMin = *xCoords++;
|
|
xMax = *xCoords++;
|
|
|
|
rects->left = xMin;
|
|
rects->top = yMin;
|
|
rects->right = xMax;
|
|
rects->bottom = yMin + height;
|
|
|
|
if (clampToWin9xSize)
|
|
{
|
|
// In this case, this could invalidate the region,
|
|
// but hopefully ExtCreateRegion will catch this.
|
|
if (rects->left < INFINITE_MIN_WIN9X)
|
|
{
|
|
rects->left = INFINITE_MIN_WIN9X;
|
|
}
|
|
if (rects->top < INFINITE_MIN_WIN9X)
|
|
{
|
|
rects->top = INFINITE_MIN_WIN9X;
|
|
}
|
|
if (rects->right > INFINITE_MAX_WIN9X)
|
|
{
|
|
rects->right = INFINITE_MAX_WIN9X;
|
|
}
|
|
if (rects->bottom > INFINITE_MAX_WIN9X)
|
|
{
|
|
rects->bottom = INFINITE_MAX_WIN9X;
|
|
}
|
|
}
|
|
|
|
rects++;
|
|
numXCoords -= 2;
|
|
} while (numXCoords >= 2);
|
|
|
|
ySpan += YSPAN_SIZE;
|
|
} while (ySpan <= ySpanLast);
|
|
}
|
|
return ComplexData->XCoordsCount / 2;
|
|
}
|
|
}
|
|
|
|
// If error, returns INVALID_HANDLE_VALUE
|
|
HRGN
|
|
DpRegion::GetHRgn() const
|
|
{
|
|
HRGN hRgn = NULL;
|
|
|
|
if (Infinite)
|
|
{
|
|
return NULL;
|
|
}
|
|
else if (Empty)
|
|
{
|
|
hRgn = CreateRectRgn(0, 0, 0, 0);
|
|
}
|
|
else if (ComplexData == NULL)
|
|
{
|
|
hRgn = CreateRectRgn(XMin, YMin, XMax, YMax);
|
|
}
|
|
else
|
|
{
|
|
INT numRects = GetRects((RECT *)NULL);
|
|
|
|
ASSERT(numRects > 1);
|
|
|
|
// Allocate memory to hold RGNDATA structure
|
|
|
|
INT rgnDataSize = numRects * sizeof(RECT);
|
|
RGNDATA * rgnData = (RGNDATA*)GpMalloc(sizeof(RGNDATAHEADER) +
|
|
rgnDataSize);
|
|
|
|
if (rgnData != NULL)
|
|
{
|
|
RECT* rects = (RECT*)rgnData->Buffer;
|
|
RECT bounds;
|
|
|
|
bounds.left = XMin;
|
|
bounds.top = YMin;
|
|
bounds.right = XMax;
|
|
bounds.bottom = YMax;
|
|
|
|
rgnData->rdh.dwSize = sizeof(RGNDATAHEADER);
|
|
rgnData->rdh.iType = RDH_RECTANGLES;
|
|
rgnData->rdh.nCount = numRects;
|
|
rgnData->rdh.nRgnSize = rgnDataSize;
|
|
rgnData->rdh.rcBound = bounds;
|
|
|
|
GetRects(rects, !Globals::IsNt);
|
|
|
|
hRgn = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + rgnDataSize,
|
|
rgnData);
|
|
GpFree(rgnData);
|
|
}
|
|
}
|
|
if (hRgn != NULL)
|
|
{
|
|
return hRgn;
|
|
}
|
|
|
|
WARNING(("Couldn't create win32 HRGN"));
|
|
return (HRGN)INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
VOID
|
|
DpClipRegion::StartEnumeration (
|
|
INT yMin,
|
|
Direction direction
|
|
)
|
|
{
|
|
INT * ySpan;
|
|
EnumDirection = direction;
|
|
|
|
if (ComplexData != NULL)
|
|
{
|
|
DpComplexRegion * complexData = ComplexData;
|
|
INT ySpanIndex;
|
|
|
|
complexData->ResetSearchIndex();
|
|
complexData->YSpanSearch (yMin, &ySpan, &ySpanIndex);
|
|
complexData->YSearchIndex = ySpanIndex;
|
|
|
|
if(EnumDirection == TopLeftToBottomRight ||
|
|
EnumDirection == BottomLeftToTopRight )
|
|
{
|
|
EnumSpan = 0;
|
|
}
|
|
else
|
|
{
|
|
EnumSpan = ySpan[YSPAN_XCOUNT] - 2;
|
|
}
|
|
|
|
if(EnumDirection == BottomLeftToTopRight ||
|
|
EnumDirection == BottomRightToTopLeft)
|
|
{
|
|
//If the enumeration is from bottom to top,
|
|
//and the suplied y is not inside a span, we
|
|
//want to return the span with the next smaller
|
|
//y, instead of the one with the next largest y.
|
|
//Or better, the next span in the enumeration order.
|
|
|
|
if(yMin < ySpan[YSPAN_YMIN])
|
|
{
|
|
//Get the previous span
|
|
complexData->YSearchIndex--;
|
|
|
|
if(complexData->YSearchIndex < 0)
|
|
{
|
|
complexData->YSearchIndex = 0;
|
|
EnumDirection = NotEnumerating;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if(yMin > ySpan[YSPAN_YMAX])
|
|
{
|
|
// This situation can only happen if there
|
|
// are no more spans.
|
|
EnumDirection = NotEnumerating;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DpClipRegion::Enumerate (
|
|
GpRect * rects,
|
|
INT & numRects
|
|
)
|
|
{
|
|
INT numOut = 0;
|
|
|
|
INT *ySpan = ComplexData->YSpans +
|
|
ComplexData->YSearchIndex*YSPAN_SIZE;
|
|
|
|
if(EnumDirection == NotEnumerating)
|
|
{
|
|
numRects = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
while(numOut < numRects)
|
|
{
|
|
// Return the current rectangle
|
|
|
|
INT *xCoords = ComplexData->XCoords + ySpan[YSPAN_XOFFSET] + EnumSpan;
|
|
|
|
INT xMin = *xCoords++;
|
|
INT xMax = *xCoords;
|
|
|
|
rects->X = xMin;
|
|
rects->Y = ySpan[YSPAN_YMIN];
|
|
rects->Width = xMax - xMin;
|
|
rects->Height = ySpan[YSPAN_YMAX] - rects->Y;
|
|
|
|
rects++;
|
|
|
|
numOut++;
|
|
|
|
// Update the indices
|
|
|
|
switch(EnumDirection)
|
|
{
|
|
case TopLeftToBottomRight:
|
|
EnumSpan += 2;
|
|
if(EnumSpan == ySpan[YSPAN_XCOUNT])
|
|
{
|
|
if(ComplexData->YSearchIndex == ComplexData->NumYSpans - 1)
|
|
{
|
|
goto enumeration_finished;
|
|
}
|
|
ComplexData->YSearchIndex++;
|
|
ySpan += YSPAN_SIZE;
|
|
EnumSpan = 0;
|
|
}
|
|
break;
|
|
case BottomLeftToTopRight:
|
|
EnumSpan += 2;
|
|
if(EnumSpan == ySpan[YSPAN_XCOUNT])
|
|
{
|
|
if(ComplexData->YSearchIndex == 0)
|
|
{
|
|
goto enumeration_finished;
|
|
}
|
|
ComplexData->YSearchIndex--;
|
|
ySpan -= YSPAN_SIZE;
|
|
EnumSpan = 0;
|
|
}
|
|
break;
|
|
case TopRightToBottomLeft:
|
|
EnumSpan -= 2;
|
|
if(EnumSpan < 0)
|
|
{
|
|
if(ComplexData->YSearchIndex == ComplexData->NumYSpans - 1)
|
|
{
|
|
goto enumeration_finished;
|
|
}
|
|
ComplexData->YSearchIndex++;
|
|
ySpan += YSPAN_SIZE;
|
|
EnumSpan = ySpan[YSPAN_XCOUNT] - 2;
|
|
}
|
|
break;
|
|
case BottomRightToTopLeft:
|
|
EnumSpan -= 2;
|
|
if(EnumSpan < 0)
|
|
{
|
|
if(ComplexData->YSearchIndex == 0)
|
|
{
|
|
goto enumeration_finished;
|
|
}
|
|
ComplexData->YSearchIndex--;
|
|
ySpan -= YSPAN_SIZE;
|
|
EnumSpan = ySpan[YSPAN_XCOUNT] - 2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
numRects = numOut;
|
|
return TRUE;
|
|
|
|
enumeration_finished:
|
|
|
|
EnumDirection = NotEnumerating;
|
|
numRects = numOut;
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* The method called to convert the region scans to an outline path. The
|
|
* path can be quite large and contains only points at righ angles with
|
|
* each other.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN,OUT] points - array of points to output
|
|
* [IN,OUT] types - array of types for output
|
|
*
|
|
* We do not return a path because we work on GpPoint's not GpPointF's.
|
|
*
|
|
* NOTE: This alorithm was copied from GDI's RGNOBJ::bOutline by
|
|
* J. Andrew Goossen.
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/12/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
|
|
// Because the XCoords may be negative, the high bit is reserved. Instead we
|
|
// use a bit in the Types byte array since we know the
|
|
// Types.Count >= XCoordsCount. We also know that points generated by this
|
|
// code aren't in dash mode, so we reuse the dash mode bit for marking a
|
|
// visited wall. We clear all bits on exit.
|
|
|
|
const UINT MarkWallBit = PathPointTypeDashMode; // 0x10, currently in dash mode.
|
|
|
|
#define XOFFSET(span,index) (INT)(XCoords[*(span + YSPAN_XOFFSET) + index])
|
|
|
|
#define XCOUNT(span) *(span + YSPAN_XCOUNT)
|
|
|
|
#define MARKXOFFSET(span,index) MarkWallPtr[*(span + YSPAN_XOFFSET) + index] \
|
|
|= MarkWallBit
|
|
|
|
// This macro adds a type to the type array. If the current count exceeds
|
|
// the capacity, then we grow the structure by 256 bytes. Then we continue
|
|
// adding the new type to array.
|
|
#define ADDTYPE(pointtype) if ((UINT)types.GetCount() >= types.GetCapacity()) { \
|
|
if (types.ReserveSpace(512) == Ok) \
|
|
{ \
|
|
Types = (INT)(Types - MarkWallPtr) + types.GetDataBuffer(); \
|
|
GpMemset(Types, 0, types.GetCapacity() - types.GetCount()); \
|
|
MarkWallPtr = types.GetDataBuffer(); \
|
|
} else { \
|
|
return FALSE; \
|
|
} \
|
|
} \
|
|
types.AdjustCount(1); \
|
|
*Types++ |= pointtype;
|
|
|
|
DpRegion::GetOutlinePoints(DynPointArray& points,
|
|
DynByteArray& types) const
|
|
{
|
|
if (IsSimple())
|
|
{
|
|
GpRect rect;
|
|
|
|
GpPoint newPoints[4];
|
|
BYTE newTypes[4];
|
|
|
|
GetBounds(&rect);
|
|
|
|
newPoints[0] = GpPoint(rect.X, rect.Y);
|
|
newPoints[1] = GpPoint(rect.X + rect.Width, rect.Y);
|
|
newPoints[2] = GpPoint(rect.X + rect.Width, rect.Y + rect.Height);
|
|
newPoints[3] = GpPoint(rect.X, rect.Y + rect.Height);
|
|
|
|
newTypes[0] = PathPointTypeStart;
|
|
newTypes[1] = PathPointTypeLine;
|
|
newTypes[2] = PathPointTypeLine;
|
|
newTypes[3] = PathPointTypeLine | PathPointTypeCloseSubpath;
|
|
|
|
points.AddMultiple(&newPoints[0], 4);
|
|
types.AddMultiple(&newTypes[0], 4);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// to avoid too many reallocations, we grow the array by the total number
|
|
// or x,y pairs in the reigon.
|
|
|
|
points.ReserveSpace(ComplexData->XCoordsCount+10);
|
|
types.ReserveSpace(ComplexData->XCoordsCount+10);
|
|
|
|
BYTE* MarkWallPtr = types.GetDataBuffer();
|
|
BYTE* Types = types.GetDataBuffer();
|
|
|
|
// Clear all bits in the Types array
|
|
GpMemset(MarkWallPtr, 0, types.GetCapacity());
|
|
|
|
// complicated case.
|
|
|
|
GpPoint pt[2];
|
|
INT NumYScans;
|
|
|
|
INT* CurYScan;
|
|
INT* XCoords;
|
|
INT* LastYScan;
|
|
INT* FirstYScan;
|
|
|
|
INT XOffset;
|
|
INT XIndex;
|
|
INT XCount;
|
|
|
|
// Now compute the outline:
|
|
|
|
CurYScan = ComplexData->YSpans;
|
|
NumYScans = ComplexData->NumYSpans;
|
|
XCoords = ComplexData->XCoords;
|
|
LastYScan = CurYScan + NumYScans * YSPAN_SIZE;
|
|
FirstYScan = CurYScan;
|
|
|
|
while (NumYScans--)
|
|
{
|
|
XCount = *(CurYScan + YSPAN_XCOUNT);
|
|
XOffset = *(CurYScan + YSPAN_XOFFSET);
|
|
|
|
for (XIndex = 0; XIndex < XCount; XIndex++)
|
|
{
|
|
// Only start at unvisited walls:
|
|
|
|
if ((MarkWallPtr[XOffset + XIndex] & MarkWallBit) == 0)
|
|
{
|
|
|
|
INT* YScan = CurYScan;
|
|
INT IndexWall = XIndex;
|
|
LONG Turn;
|
|
|
|
pt[0].X = XCoords[XOffset + XIndex];
|
|
pt[0].Y = *(CurYScan + YSPAN_YMIN);
|
|
|
|
points.Add(pt[0]);
|
|
|
|
ADDTYPE(PathPointTypeStart);
|
|
#ifdef DEBUG_REGION
|
|
DbgPrint("Point: (%d,%d)\n",pt[0].X, pt[0].Y);
|
|
#endif
|
|
INT* YSearch = CurYScan + YSPAN_SIZE;
|
|
BOOL Inside = (BOOL) (XIndex & 1);
|
|
|
|
// Mark that we've visited this wall:
|
|
|
|
MarkWallPtr[XOffset + IndexWall] |= MarkWallBit;
|
|
|
|
// Loop until the path closes on itself:
|
|
|
|
GoDown:
|
|
// YSPAN_YMAX is exclusive, YSPAN_YMIN is inclusive so
|
|
// vertically adjacent spans have YSPAN_YMIN==YSPAN_YMAX
|
|
Turn = +1;
|
|
while (
|
|
(YSearch >= CurYScan) &&
|
|
(YSearch < LastYScan) &&
|
|
(YScan[YSPAN_YMAX] == YSearch[YSPAN_YMIN])
|
|
)
|
|
{
|
|
INT Wall = XOFFSET(YScan, IndexWall);
|
|
INT IndexNewWall;
|
|
INT NewWall;
|
|
|
|
INT Left = Inside;
|
|
INT Right = XCOUNT(YSearch) - 1 - Inside;
|
|
|
|
// It would be nice if the first wall in the region structure
|
|
// was minus infinity, but it isn't, so we do this check:
|
|
|
|
if (XOFFSET(YSearch, Left) > Wall)
|
|
IndexNewWall = Left;
|
|
else
|
|
{
|
|
// Check if it's possible to find a wall with the
|
|
// minimum x-value > xWall:
|
|
|
|
if (XOFFSET(YSearch, Right) <= Wall)
|
|
break; // =====>
|
|
|
|
// Do a binary search to find it:
|
|
|
|
while (TRUE)
|
|
{
|
|
INT IndexSearch = (Left + Right) >> 1;
|
|
if (IndexSearch == Left)
|
|
break; // =====>
|
|
|
|
INT Search = XOFFSET(YSearch, IndexSearch);
|
|
|
|
if (Search > Wall)
|
|
Right = IndexSearch;
|
|
else
|
|
Left = IndexSearch;
|
|
}
|
|
|
|
IndexNewWall = Right;
|
|
}
|
|
|
|
if ((IndexNewWall & 1) != Inside)
|
|
{
|
|
// There is a region directly below xWall. We can't
|
|
// move down if its left side is < the left
|
|
// side of our space:
|
|
|
|
if (IndexWall > 0 &&
|
|
XOFFSET(YSearch, IndexNewWall - 1) <
|
|
XOFFSET(YScan, IndexWall - 1))
|
|
{
|
|
Turn = -1;
|
|
break; // =====>
|
|
}
|
|
|
|
IndexNewWall--;
|
|
}
|
|
else
|
|
{
|
|
// There is a space directly below xWall. We can't
|
|
// move down if its right side is more than the
|
|
// right side of our region:
|
|
|
|
if (XOFFSET(YSearch, IndexNewWall) >=
|
|
XOFFSET(YScan, IndexWall + 1))
|
|
break; // =====>
|
|
}
|
|
|
|
NewWall = XOFFSET(YSearch, IndexNewWall);
|
|
|
|
// Don't bother outputing multiple in-line straight lines:
|
|
|
|
if (Wall != NewWall ||
|
|
XOFFSET(YScan, IndexWall) != NewWall ||
|
|
XOFFSET(YSearch, IndexNewWall) != NewWall)
|
|
{
|
|
pt[0].X = Wall;
|
|
pt[0].Y = *(YScan + YSPAN_YMAX);
|
|
pt[1].Y = *(YScan + YSPAN_YMAX);
|
|
pt[1].X = NewWall;
|
|
|
|
points.Add(pt[0]);
|
|
points.Add(pt[1]);
|
|
|
|
ADDTYPE(PathPointTypeLine);
|
|
ADDTYPE(PathPointTypeLine);
|
|
|
|
#ifdef DEBUG_REGION
|
|
DbgPrint("Points: (%d,%d), (%d,%d)\n",
|
|
pt[0].X, pt[0].Y, pt[1].X, pt[1].Y);
|
|
#endif
|
|
}
|
|
|
|
YScan = YSearch;
|
|
IndexWall = IndexNewWall;
|
|
YSearch = YScan + YSPAN_SIZE;
|
|
|
|
MARKXOFFSET(YScan, IndexWall);
|
|
}
|
|
|
|
// Setup to go up other side:
|
|
|
|
pt[0].X = XOFFSET(YScan, IndexWall);
|
|
pt[0].Y = *(YScan + YSPAN_YMAX);
|
|
pt[1].Y = *(YScan + YSPAN_YMAX);
|
|
pt[1].X = XOFFSET(YScan, IndexWall + Turn);
|
|
|
|
points.Add(pt[0]);
|
|
points.Add(pt[1]);
|
|
|
|
ADDTYPE(PathPointTypeLine);
|
|
ADDTYPE(PathPointTypeLine);
|
|
#ifdef DEBUG_REGION
|
|
DbgPrint("Points: (%d,%d), (%d,%d)\n",
|
|
pt[0].X, pt[0].Y, pt[1].X, pt[1].Y);
|
|
#endif
|
|
|
|
YSearch = YScan - YSPAN_SIZE;
|
|
IndexWall += Turn;
|
|
MARKXOFFSET(YScan, IndexWall);
|
|
|
|
// Go up:
|
|
|
|
// YSPAN_YMAX is exclusive, YSPAN_YMIN is inclusive so
|
|
// vertically adjacent spans have YSPAN_YMIN==YSPAN_YMAX
|
|
Turn = -1;
|
|
while (
|
|
(YSearch >= CurYScan) &&
|
|
(YSearch < LastYScan) &&
|
|
(YScan[YSPAN_YMIN] == YSearch[YSPAN_YMAX])
|
|
)
|
|
{
|
|
INT Wall = XOFFSET(YScan, IndexWall);
|
|
INT IndexNewWall;
|
|
INT NewWall;
|
|
|
|
INT Left = Inside;
|
|
INT Right = XCOUNT(YSearch) - 1 - Inside;
|
|
|
|
// It would be nice if the last wall in the region structure
|
|
// was plus infinity, but it isn't, so we do this check:
|
|
|
|
if (XOFFSET(YSearch, Right) < Wall)
|
|
IndexNewWall = Right;
|
|
else
|
|
{
|
|
// Check if it's possible to find a wall with the
|
|
// maximum x-value < xWall:
|
|
|
|
if (XOFFSET(YSearch, Left) >= Wall)
|
|
break; // =====>
|
|
|
|
// Binary search to find it:
|
|
|
|
while (TRUE)
|
|
{
|
|
INT IndexSearch = (Left + Right) >> 1;
|
|
if (IndexSearch == Left)
|
|
break; // =====>
|
|
|
|
INT Search = XOFFSET(YSearch, IndexSearch);
|
|
|
|
if (Search >= Wall)
|
|
Right = IndexSearch;
|
|
else
|
|
Left = IndexSearch;
|
|
}
|
|
|
|
IndexNewWall = Left;
|
|
}
|
|
|
|
if ((IndexNewWall & 1) == Inside)
|
|
{
|
|
// There is a region directly above xWall. We can't
|
|
// move up if its right side is more than the right
|
|
// side of our space:
|
|
|
|
if ((IndexWall < (XCOUNT(YScan) - 1)) &&
|
|
(XOFFSET(YSearch, IndexNewWall + 1) >
|
|
XOFFSET(YScan, IndexWall + 1)) )
|
|
{
|
|
Turn = +1;
|
|
break; // =====>
|
|
}
|
|
|
|
IndexNewWall++;
|
|
}
|
|
else
|
|
{
|
|
// There is a space directly above xWall. We can't
|
|
// move up if its left side is <= the left side
|
|
// of our region:
|
|
|
|
if (XOFFSET(YSearch, IndexNewWall) <=
|
|
XOFFSET(YScan, IndexWall - 1))
|
|
break; // =====>
|
|
}
|
|
|
|
NewWall = XOFFSET(YSearch, IndexNewWall);
|
|
|
|
// Don't bother outputing multiple in-line straight lines:
|
|
|
|
if (Wall != NewWall ||
|
|
XOFFSET(YScan, IndexWall) != NewWall ||
|
|
XOFFSET(YSearch, IndexNewWall) != NewWall)
|
|
{
|
|
pt[0].X = Wall;
|
|
pt[0].Y = *(YScan + YSPAN_YMIN);
|
|
pt[1].Y = *(YScan + YSPAN_YMIN);
|
|
pt[1].X = NewWall;
|
|
|
|
points.Add(pt[0]);
|
|
points.Add(pt[1]);
|
|
|
|
ADDTYPE(PathPointTypeLine);
|
|
ADDTYPE(PathPointTypeLine);
|
|
#ifdef DEBUG_REGION
|
|
DbgPrint("Points: (%d,%d), (%d,%d)\n",
|
|
pt[0].X, pt[0].Y, pt[1].X, pt[1].Y);
|
|
#endif
|
|
}
|
|
|
|
YScan = YSearch;
|
|
IndexWall = IndexNewWall;
|
|
YSearch = YScan - YSPAN_SIZE;
|
|
|
|
MARKXOFFSET(YScan, IndexWall);
|
|
}
|
|
|
|
// Check if we've returned to where we started from:
|
|
|
|
if ((CurYScan != YScan) || (XIndex != IndexWall - 1))
|
|
{
|
|
// Setup to go down other side:
|
|
|
|
pt[0].X = XOFFSET(YScan, IndexWall);
|
|
pt[0].Y = *(YScan + YSPAN_YMIN);
|
|
pt[1].Y = *(YScan + YSPAN_YMIN);
|
|
pt[1].X = XOFFSET(YScan, IndexWall + Turn);
|
|
|
|
points.Add(pt[0]);
|
|
points.Add(pt[1]);
|
|
|
|
ADDTYPE(PathPointTypeLine);
|
|
ADDTYPE(PathPointTypeLine);
|
|
#ifdef DEBUG_REGION
|
|
DbgPrint("Points: (%d,%d), (%d,%d)\n",
|
|
pt[0].X, pt[0].Y, pt[1].X, pt[1].Y);
|
|
#endif
|
|
YSearch = YScan + YSPAN_SIZE;
|
|
|
|
IndexWall += Turn;
|
|
MARKXOFFSET(YScan, IndexWall);
|
|
|
|
goto GoDown; // =====>
|
|
}
|
|
|
|
// We're all done with this outline!
|
|
|
|
pt[0].X = XOFFSET(YScan, IndexWall);
|
|
pt[0].Y = *(YScan + YSPAN_YMIN);
|
|
|
|
points.Add(pt[0]);
|
|
|
|
ADDTYPE(PathPointTypeLine | PathPointTypeCloseSubpath);
|
|
#ifdef DEBUG_REGION
|
|
DbgPrint("Point: (%d,%d)\n", pt[0].X, pt[0].Y);
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
CurYScan = CurYScan + YSPAN_SIZE;
|
|
}
|
|
|
|
// we must untrash the region by removing our MarkWallBits
|
|
|
|
BYTE* TypesPtr = types.GetDataBuffer();
|
|
INT XCnt = ComplexData->XCoordsCount;
|
|
|
|
while (XCnt--)
|
|
{
|
|
*TypesPtr++ &= ~MarkWallBit;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|