Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1682 lines
51 KiB

/**************************************************************************\
*
* Copyright (c) 1998 Microsoft Corporation
*
* Module Name:
*
* Rasterizer.cpp
*
* Abstract:
*
* GpRasterizer class implementation (and supporting classes)
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
#include "precomp.hpp"
/**************************************************************************\
*
* Function Description:
*
* Initialize the vector's DDA info.
* Assumes y1 < y2.
*
* Arguments:
*
* [IN] x1 - starting fixed-point x coordinate of vector
* [IN] y1 - starting fixed-point y coordinate of vector
* [IN] x2 - ending fixed-point x coordinate of vector
* [IN] y2 - ending fixed-point y coordinate of vector
* [IN] direction - VectorGoingUp or VectorGoingDown
*
* Return Value:
*
* NONE
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
VOID
GpYDda::Init(
FIX4 x1,
FIX4 y1,
FIX4 x2,
FIX4 y2,
GpVectorDirection direction
)
{
ASSERT ((direction == VectorGoingUp) || (direction == VectorGoingDown));
FIX4 xDelta = x2 - x1;
FIX4 yDelta = y2 - y1;
ASSERT (yDelta > 0);
YDelta = yDelta;
// Set up x increment
INT quotient;
INT remainder;
// Error is initially 0, but we add yDelta - 1 for the ceiling,
// but then subtract off yDelta so that we can check the sign
// instead of comparing to yDelta.
Error = -1;
if (xDelta < 0)
{
xDelta = -xDelta;
if (xDelta < yDelta) // y-major
{
XInc = -1;
ErrorUp = yDelta - xDelta;
}
else // x-major
{
QUOTIENT_REMAINDER (xDelta, yDelta, quotient, remainder);
XInc = -quotient;
ErrorUp = remainder;
if (remainder != 0)
{
XInc--;
ErrorUp = yDelta - remainder;
}
}
}
else
{
if (xDelta < yDelta) // y-major
{
XInc = 0;
ErrorUp = xDelta;
}
else // x-major
{
QUOTIENT_REMAINDER (xDelta, yDelta, quotient, remainder);
XInc = quotient;
ErrorUp = remainder;
}
}
// See if we need to advance to an integer coordinate
if ((y1 & FIX4_MASK) != 0)
{
INT i;
for (i = FIX4_ONE - (y1 & FIX4_MASK); i > 0; i--)
{
x1 += XInc;
Error += ErrorUp;
if (Error >= 0)
{
Error -= yDelta;
x1++;
}
}
// y1 += FIX4_MASK;
}
if ((x1 & FIX4_MASK) != 0)
{
Error -= yDelta * (FIX4_ONE - (x1 & FIX4_MASK));
x1 += FIX4_MASK; // to get the ceiling
}
Error >>= FIX4_PRECISION;
Direction = direction;
XCur = x1 >> FIX4_PRECISION;
YMax = GpFix4Ceiling (y2) - 1;
} // End of GpYDda::Init()
/**************************************************************************\
*
* Function Description:
*
* Advance the DDA to the next raster.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* NONE
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
VOID
GpYDda::Advance()
{
XCur += XInc;
Error += ErrorUp;
if (Error >= 0)
{
Error -= YDelta;
XCur++;
}
}
#ifndef USE_YSPAN_BUILDER
/**************************************************************************\
*
* Function Description:
*
* Construct a GpRectBuilder object that will output 1 rect at a time
*
* Arguments:
*
* [IN] renderRect - a class that outputs a single rect at a time
*
* Return Value:
*
* NONE
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpRectBuilder::GpRectBuilder(
GpOutputRect * renderRect
)
{
ASSERT(renderRect);
SetValid(FALSE);
if ((renderRect != NULL) && (InitArrays() == Ok))
{
SetValid(TRUE);
FlushRects = this;
RenderRect = renderRect;
RectYMin = 0x7FFFFFFF;
RectHeight = 0;
}
}
/**************************************************************************\
*
* Function Description:
*
* Construct a GpRectBuilder object that will output a set of rects in a
* YSpan (YMin to YMax) at a time.
*
* Arguments:
*
* [IN] flushRects - a class that outputs a Y span at a time
*
* Return Value:
*
* NONE
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpRectBuilder::GpRectBuilder(
GpOutputYSpan * flushRects
)
{
ASSERT(flushRects);
SetValid(FALSE);
if ((flushRects != NULL) && (InitArrays() == Ok))
{
SetValid(TRUE);
FlushRects = flushRects;
RenderRect = NULL;
RectYMin = 0x7FFFFFFF;
RectHeight = 0;
}
}
/**************************************************************************\
*
* Function Description:
*
* Initialize the Rect and Raster arrays that will build up a set of
* X Coordinates within a Y span.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
GpRectBuilder::InitArrays()
{
GpStatus status;
status = RectXCoords.ReserveSpace(16);
if (status == Ok)
{
status = RasterXCoords.ReserveSpace(16);
}
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Outputs a single span within a raster. Is called by the rasterizer.
* These spans provide the input for the rect builder.
*
* 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:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
GpRectBuilder::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
ASSERT (xMin < xMax);
INT * xCoords;
RasterY = y;
// Test here if xMin == previous xMax and combine spans.
// Some polygons (like the letter W) could benefit from such a check.
// NT4 doesn't handle regions with adjacent spans that aren't combined.
if ((RasterXCoords.GetCount() == 0) || (RasterXCoords.Last() != xMin))
{
if ((xCoords = RasterXCoords.AddMultiple(2)) != NULL)
{
xCoords[0] = xMin;
xCoords[1] = xMax;
return Ok;
}
return OutOfMemory;
}
else
{
RasterXCoords.Last() = xMax;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Called by the rasterizer when all the spans of a raster (Y value)
* have been output.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
GpRectBuilder::EndRaster()
{
INT rectXCount = RectXCoords.GetCount();
INT rasterXCount = RasterXCoords.GetCount();
INT * rasterXCoords = RasterXCoords.GetDataBuffer();
GpStatus status = Ok;
if (rectXCount != 0)
{
INT * rectXCoords = RectXCoords.GetDataBuffer();
if ((RasterY == (RectYMin + RectHeight)) &&
(rasterXCount == rectXCount))
{
if (rasterXCount == 2)
{
if ((rasterXCoords[0] == rectXCoords[0]) &&
(rasterXCoords[1] == rectXCoords[1]))
{
FoundRectMatch:
RasterXCoords.Reset(FALSE);
RectHeight++;
return Ok;
}
}
else if (GpMemcmp(rasterXCoords, rectXCoords,
rectXCount * sizeof(INT)) == 0)
{
goto FoundRectMatch;
}
}
status = FlushRects->OutputYSpan(
RectYMin,
RectYMin + RectHeight,
rectXCoords,
rectXCount);
}
// if we get here, either the RectXCoords is empty or
// it has just been flushed
RectXCoords.Reset(FALSE);
if (rasterXCount > 0)
{
status = static_cast<GpStatus>
(status | RectXCoords.AddMultiple(rasterXCoords, rasterXCount));
RasterXCoords.Reset(FALSE);
RectHeight = 1;
RectYMin = RasterY;
}
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Called by the rasterizer when all the spans of all the rasters
* have been output.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
GpRectBuilder::End()
{
INT rectXCount = RectXCoords.GetCount();
if (rectXCount != 0)
{
return FlushRects->OutputYSpan(
RectYMin,
RectYMin + RectHeight,
RectXCoords.GetDataBuffer(),
rectXCount);
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* If the rect builder was constructed to output 1 rect at a time, then
* this method is called to do that after a Y span is completed.
*
* Arguments:
*
* [IN] yMin - top of the Y span
* [IN] yMax - bottom of the Y span
* [IN] xCoords - array of x coordinates
* [IN] numXCoords - number of x coordinates (>= 2, multiple of 2)
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
GpRectBuilder::OutputYSpan(
INT yMin,
INT yMax,
INT * xCoords, // even number of X coordinates
INT numXCoords // must be a multiple of 2
)
{
ASSERT (yMin < yMax);
ASSERT (xCoords);
ASSERT ((numXCoords >= 2) && ((numXCoords & 0x00000001) == 0))
GpStatus status;
INT i = 0;
do
{
status = RenderRect->OutputRect(
xCoords[i],
yMin,
xCoords[i+1],
yMax);
i += 2;
} while ((i < numXCoords) && (status == Ok));
return status;
}
#else
/**************************************************************************\
*
* Function Description:
*
* Construct a GpYSpanBuilder object that will output one YSpan
* (YMin to YMax) at a time.
*
* Arguments:
*
* [IN] output - a class that outputs a Y span at a time
*
* Return Value:
*
* NONE
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpYSpanBuilder::GpYSpanBuilder(
GpOutputYSpan * output
)
{
ASSERT(output);
SetValid(FALSE);
if (output != NULL)
{
Output = output;
SetValid(XCoords.ReserveSpace(16) == Ok);
}
}
/**************************************************************************\
*
* Function Description:
*
* Outputs a single span within a raster. Is called by the rasterizer.
* These spans provide the input for the rect builder.
*
* 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:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
GpYSpanBuilder::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
ASSERT (xMin < xMax);
INT * xCoords;
Y = y;
if ((xCoords = XCoords.AddMultiple(2)) != NULL)
{
xCoords[0] = xMin;
xCoords[1] = xMax;
return Ok;
}
return OutOfMemory;
}
/**************************************************************************\
*
* Function Description:
*
* Called by the rasterizer when all the spans of a raster (Y value)
* have been output.
*
* Arguments:
*
* NONE
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
GpYSpanBuilder::EndRaster()
{
INT count = XCoords.GetCount();
// count can be 0
if (count >= 2)
{
INT y = Y;
GpStatus status = Output->OutputYSpan(y, y + 1,
XCoords.GetDataBuffer(), count);
XCoords.Reset(FALSE);
return status;
}
return Ok;
}
#endif // USE_YSPAN_BUILDER
/**************************************************************************\
*
* Macro Description:
*
* Add a newly identified vector to the VectorList. Keep track of the
* direction of the vector, in case we're using the winding rule. Put
* the min y value in y1, so we can sort the vectors by min y. Keep track
* of the min and max y values for the entire list of vectors.
*
* Don't add the vector if this is a horizontal (or near-horizontal) line.
* We don't scan-convert horizontal lines. Note that this check will also
* fail if the two points are identical.
*
* Arguments:
*
* [IN] x1 - starting fixed-point x coordinate of vector
* [IN] y1 - starting fixed-point y coordinate of vector
* [IN] x2 - ending fixed-point x coordinate of vector
* [IN] y2 - ending fixed-point y coordinate of vector
*
* [OUT] yMin - set to min of yMin and least y of y1, y2
* [OUT] yMax - set to max of yMax and greatest y of y1, y2
* [OUT] numVectors - incremented by 1
* [OUT] vector - incremented by 1
*
* Return Value:
*
* NONE
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
#define ADDVECTOR_SETYBOUNDS(x1,y1,x2,y2,yMin,yMax,numVectors,vector,dir) \
if (GpFix4Ceiling(y1) != GpFix4Ceiling(y2)) \
{ \
if ((y1) <= (y2)) \
{ \
if ((y2) > (yMax)) \
{ \
(yMax) = (y2); \
} \
if ((y1) < (yMin)) \
{ \
(yMin) = (y1); \
} \
dir = VectorGoingDown; \
vector->Set((x1),(y1),(x2),(y2),VectorGoingDown); \
} \
else \
{ \
if ((y1) > (yMax)) \
{ \
(yMax) = (y1); \
} \
if ((y2) < (yMin)) \
{ \
(yMin) = (y2); \
} \
dir = VectorGoingUp; \
vector->Set((x2),(y2),(x1),(y1),VectorGoingUp); \
} \
(numVectors)++; \
(vector)++; \
}
// Used by Rasterizer to keep track of each vector in a path.
class RasterizeVector
{
public:
FIX4 X1;
FIX4 Y1; // Min Y
FIX4 X2;
FIX4 Y2; // Max Y
GpVectorDirection Direction; // VectorGoingUp or VectorGoingDown
VOID Set(FIX4 x1, FIX4 y1, FIX4 x2, FIX4 y2, GpVectorDirection direction)
{
X1 = x1;
Y1 = y1;
X2 = x2;
Y2 = y2;
Direction = direction;
}
};
typedef DynArray<RasterizeVector> DynVectorArray;
/**************************************************************************\
*
* Function Description:
*
* Rasterizer for non-convex polygons.
*
* Rasterize a path into a set of spans on each raster (Y value) that the
* path intersects. The specified output object is used to output the spans.
* The Y values of the specified clip bounds (if any) are used to speed the
* rasterization process by avoiding the processing of vectors that are
* outside those bounds. However, it is assumed that at least part of the
* path is visible in the vertical span of the clip bounds.
*
* This algorithm is based on the one documented in
* Foley & Van Dam, Version 1, pp. 456 - 460.
*
* Starting with the min y value and ending with the max y value, each
* raster (Y value) is rasterized (output) one at a time.
*
* (1) On each raster, check to see if new vectors need to be added to
* the ActiveVectorList. When an vector is added, DDA information about
* that vector is computed so that the intersection of that vector with
* each raster can be computed.
*
* (2) Sort the ActiveVectorList in order of increasing x.
*
* (3) Using either the alternate rule or the winding rule, output the span
* between the appropriate vectors.
*
* (4) Remove any vectors from the ActiveVectorList for which the current
* raster is the max y of the vector.
*
* (5) Calculate the intersection of the next raster with all the vectors
* that are still in the ActiveVectorList (i.e. advance their DDAs).
*
* Arguments:
*
* [IN] yMin - the min y value of the set of vectors
* [IN] yMax - the max y value of the set of vectors
* [IN] numVectors - the number of vectors in the vector list
* [IN] vectorList - the list of vectors to rasterize
* [IN] sortedVectorIndices - vector list sorted by increasing y
* [IN] left - a dda object to use
* [IN] right - a dda object to use
* [IN] output - used to output the spans of the rasterization
* [IN] clipBounds - clipbounds (if any) to use to speed the process
* [IN] useAlternate - whether or not to use the alternate rule
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
NonConvexRasterizer(
INT yMin, // min y of all vectors
INT yMax, // max y of all vectors
INT numVectors, // num vectors in VectorList
RasterizeVector * vectorList, // list of all vectors of path
INT * sortedVectorIndices, // sorted list of vector indices
GpYDda * left,
GpYDda * right,
DpOutputSpan * output,
const GpRect * clipBounds,
BOOL useAlternate
)
{
GpStatus status = Ok;
INT yMinClipped = yMin; // used for clipping
INT yMaxClipped = yMax; // used for clipping
INT y;
INT xMin;
INT xMax;
INT sortedVectorIndex = 0; // idx to sortedVectorIndices
RasterizeVector * vector;
INT i;
INT count;
INT numActiveVectors = 0; // num used in ActiveVectorList
DynArrayIA<GpYDda *, 16> activeVectorList; // active GpYDda vectors
GpYDda ** activeVectors;
GpYDda * activeVector;
activeVectors = reinterpret_cast<GpYDda **>
(activeVectorList.AddMultiple(2));
if (activeVectors == NULL)
{
delete left;
delete right;
return OutOfMemory;
}
activeVectors[0] = left;
activeVectors[1] = right;
if (clipBounds != NULL)
{
yMinClipped = clipBounds->Y;
yMaxClipped = clipBounds->GetBottom();
// There are a few cases where this can happen legitimately.
// For example, the transformed bounds of an object could be
// partially in the clip area although the object isn't.
// Also, a Bezier control point might be in the clip area while
// the actual curve isn't.
if ((yMin > yMaxClipped) || (yMax < yMinClipped))
{
goto DoneNotConvex;
}
if (yMaxClipped > yMax)
{
yMaxClipped = yMax;
}
if (yMinClipped > yMin)
{
while (sortedVectorIndex < numVectors)
{
vector = vectorList + sortedVectorIndices[sortedVectorIndex];
if (GpFix4Ceiling(vector->Y2) >= yMinClipped)
{
yMin = GpFix4Ceiling(vector->Y1);
break;
}
sortedVectorIndex++;
}
}
}
for (y = yMin; (y <= yMaxClipped); y++)
{
// Add any appropriate new vectors to the active vector list
while (sortedVectorIndex < numVectors)
{
vector = vectorList + sortedVectorIndices[sortedVectorIndex];
if (GpFix4Ceiling(vector->Y1) != y)
{
break;
}
// Clip the vector (but only against Y or
// we wouldn't get the right number of runs)
if (GpFix4Ceiling(vector->Y2) >= yMinClipped)
{
if (numActiveVectors < activeVectorList.GetCount())
{
activeVector = activeVectors[numActiveVectors];
}
else
{
activeVector = left->CreateYDda();
if ((activeVector == NULL) ||
(activeVectorList.Add(activeVector) != Ok))
{
delete activeVector;
status = OutOfMemory;
goto DoneNotConvex;
}
// adding the vector could change the data buffer
activeVectors = reinterpret_cast<GpYDda **>
(activeVectorList.GetDataBuffer());
}
activeVector->Init(
vector->X1,
vector->Y1,
vector->X2,
vector->Y2,
vector->Direction);
numActiveVectors++;
}
sortedVectorIndex++;
}
if (y >= yMinClipped)
{
// Make sure the activeVectorList is sorted in order of
// increasing x. We have to do this every time, even if
// we haven't added any new active vectors, because the
// slopes of the lines can be different, which means they
// could cross.
for (count = 1; count < numActiveVectors; count++)
{
i = count;
do
{
if (activeVectors[i]->GetX() >=
activeVectors[i-1]->GetX())
{
break;
}
activeVector = activeVectors[i-1];
activeVectors[i-1] = activeVectors[i];
activeVectors[i] = activeVector;
} while (--i > 0);
}
// fill the appropriate pixels on the current scan line
if (useAlternate)
{
// Use the alternating rule (also known as even/odd rule)
// to output the spans of a raster between the intersections
// of the vectors with the current scan line.
//
// The alternating rule means that a raster pattern is drawn
// between the 1st and 2nd points and between the 3rd and
// 4th points, etc., but not between the 2nd and 3rd points
// or between the 4th and 5th points, etc.
//
// There could be an odd number of points; for example:
//
// 9 /\
// 8 / \
// 7 / \
// 6 / \
// 5 / \
// 4 /----------\--------/
// 3 \ /
// 2 \ /
// 1 \ /
// 0 \/
//
// On raster y==4, there are 3 points, so the 2nd half of
// the raster does not get output.
for (i = 1; (i < numActiveVectors); i += 2)
{
xMin = activeVectors[i-1]->GetX();
xMax = activeVectors[i]->GetX();
// Make sure the X's aren't equal
if (xMin < xMax)
{
if (output->OutputSpan(y, xMin, xMax) != Ok)
{
status = GenericError;
goto DoneNotConvex;
}
}
}
}
else // Winding
{
GpYDda * leftEdge;
GpYDda * rightEdge;
INT j;
INT direction = 0; // num times going up and down
// There's got to be the same number of lines
// going up as going down before drawing the
// scan line.
for (count = 1; (count < numActiveVectors); count = j + 2)
{
leftEdge = activeVectors[count - 1];
direction += static_cast<INT>(leftEdge->GetDirection());
for (j = count; (j < numActiveVectors); j++)
{
rightEdge = activeVectors[j];
direction += static_cast<INT>
(rightEdge->GetDirection());
if (direction == 0)
{
xMin = leftEdge->GetX();
xMax = rightEdge->GetX();
// Make sure the X's aren't equal
if (xMin < xMax)
{
if (output->OutputSpan(y, xMin, xMax) != Ok)
{
status = GenericError;
goto DoneNotConvex;
}
}
break;
}
}
}
}
if (output->EndRaster() != Ok)
{
status = GenericError;
goto DoneNotConvex;
}
}
// remove any appropriate vectors from the active vector list
// and advance all the DDAs
{
INT activeIndex = 0;
VOID * removedActiveVector;
while (activeIndex < numActiveVectors)
{
activeVector = activeVectors[activeIndex];
if (activeVector->DoneWithVector(y))
{
numActiveVectors--;
if (numActiveVectors > activeIndex)
{
GpMemmove(
&(activeVectors[activeIndex]),
&(activeVectors[activeIndex + 1]),
(numActiveVectors - activeIndex) *
sizeof(activeVectors[0]));
}
activeVectors[numActiveVectors] = activeVector;
}
else
{
activeIndex++;
}
}
}
}
status = output->End();
DoneNotConvex:
numActiveVectors = activeVectorList.GetCount();
ASSERT(numActiveVectors > 0);
activeVectors = reinterpret_cast<GpYDda **>
(activeVectorList.GetDataBuffer());
do
{
delete activeVectors[--numActiveVectors];
} while (numActiveVectors > 0);
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Rasterizer for convex polygons.
*
* Uses basically the same algorithm to rasterize, but has a few optimizations
* for when we know the polygon is convex. We know there will only be 2
* active vectors at a time (and only 2). Even though they may cross each
* other, we don't bother sorting them. We just check the X values instead.
* These optimizations provide about a 15% increase in performance.
*
* Arguments:
*
* [IN] yMin - the min y value of the set of vectors
* [IN] yMax - the max y value of the set of vectors
* [IN] numVectors - the number of vectors in the vector list
* [IN] vectorList - the list of vectors to rasterize
* [IN] sortedVectorIndices - vector list sorted by increasing y
* [IN] left - a dda object to use
* [IN] right - a dda object to use
* [IN] output - used to output the spans of the rasterization
* [IN] clipBounds - clipbounds (if any) to use to speed the process
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 2/24/1999 DCurtis
*
\**************************************************************************/
GpStatus
ConvexRasterizer(
INT yMin, // min y of all vectors
INT yMax, // max y of all vectors
INT numVectors, // num vectors in VectorList
RasterizeVector * vectorList, // list of all vectors of path
INT * sortedVectorIndices, // sorted list of vector indices
GpYDda * dda1,
GpYDda * dda2,
DpOutputSpan * output,
const GpRect * clipBounds
)
{
GpStatus status = Ok;
INT yMinClipped = yMin; // used for clipping
INT yMaxClipped = yMax; // used for clipping
INT y;
INT x1;
INT x2;
INT sortedVectorIndex = 0; // idx to sortedVectorIndices
RasterizeVector * vector1;
RasterizeVector * vector2;
if (clipBounds != NULL)
{
yMinClipped = clipBounds->Y;
yMaxClipped = clipBounds->GetBottom();
// There are a few cases where this can happen legitimately.
// For example, the transformed bounds of an object could be
// partially in the clip area although the object isn't.
// Also, a Bezier control point might be in the clip area while
// the actual curve isn't.
if ((yMin > yMaxClipped) || (yMax < yMinClipped))
{
goto DoneConvex;
}
if (yMaxClipped > yMax)
{
yMaxClipped = yMax;
}
if (yMinClipped > yMin)
{
RasterizeVector * vector;
vector1 = NULL;
while (sortedVectorIndex < numVectors)
{
vector = vectorList+sortedVectorIndices[sortedVectorIndex++];
if (GpFix4Ceiling(vector->Y2) >= yMinClipped)
{
if (vector1 == NULL)
{
vector1 = vector;
}
else
{
vector2 = vector;
goto HaveVectors;
}
}
}
ASSERT(0);
status = InvalidParameter; // either not convex or clipped out
goto DoneConvex;
}
}
vector1 = vectorList + sortedVectorIndices[0];
vector2 = vectorList + sortedVectorIndices[1];
sortedVectorIndex = 2;
HaveVectors:
dda1->Init(vector1->X1, vector1->Y1,
vector1->X2, vector1->Y2,
VectorGoingUp);
dda2->Init(vector2->X1, vector2->Y1,
vector2->X2, vector2->Y2,
VectorGoingUp);
// For clipping case, we have to advance the first vector to catch up
// to the start of the 2nd vector.
{
yMin = GpFix4Ceiling(vector2->Y1);
for (INT yMinVector1 = GpFix4Ceiling(vector1->Y1);
yMinVector1 < yMin; yMinVector1++)
{
dda1->Advance();
}
}
for (y = yMin; (y <= yMaxClipped); y++)
{
if (y >= yMinClipped)
{
// fill the appropriate pixels on the current scan line
x1 = dda1->GetX();
x2 = dda2->GetX();
// Make sure the X's aren't equal before outputting span
if (x1 < x2)
{
if (output->OutputSpan(y, x1, x2) == Ok)
{
if (output->EndRaster() == Ok)
{
goto DoneCheck;
}
}
status = GenericError;
goto DoneConvex;
}
else if (x1 > x2)
{
if (output->OutputSpan(y, x2, x1) == Ok)
{
if (output->EndRaster() == Ok)
{
goto DoneCheck;
}
}
status = GenericError;
goto DoneConvex;
}
}
DoneCheck:
if (dda1->DoneWithVector(y))
{
if (sortedVectorIndex >= numVectors)
{
break;
}
vector1 = vectorList + sortedVectorIndices[sortedVectorIndex++];
ASSERT(GpFix4Ceiling(vector1->Y1) == (y + 1));
dda1->Init(vector1->X1, vector1->Y1,
vector1->X2, vector1->Y2,
VectorGoingUp);
}
if (dda2->DoneWithVector(y))
{
if (sortedVectorIndex >= numVectors)
{
ASSERT(0);
break;
}
vector2 = vectorList + sortedVectorIndices[sortedVectorIndex++];
ASSERT(GpFix4Ceiling(vector2->Y1) == (y + 1));
dda2->Init(vector2->X1, vector2->Y1,
vector2->X2, vector2->Y2,
VectorGoingUp);
}
}
status = output->End();
DoneConvex:
delete dda1;
delete dda2;
return status;
}
#define NUM_ENUMERATE_POINTS 32
/**************************************************************************\
*
* Function Description:
*
* Perform a simple partition sort on a list indexing into the vector list.
* The result is a sorted index array keyed on the vertex array Y1 coordinate.
* The index array is sorted in place and in ascending order.
*
* Arguments:
*
* v is the vertex list.
* F, L - First and Last pointer in the index array.
*
* Created:
*
* 09/16/2000 asecchia
*
\**************************************************************************/
void QuickSortIndex(
RasterizeVector *v,
int *F,
int *L
)
{
if(F < L)
{
// Find the median position.
int median = *(F + (L-F)/2);
int *i = F;
int *j = L;
while(i<j)
{
// seek for elements in the wrong partition.
while(v[*i].Y1 < v[median].Y1) {i++;}
while(v[*j].Y1 > v[median].Y1) {j--;}
if(i>=j) { break; }
// Swap.
int temp = *i;
*i = *j;
*j = temp;
// tie breaker - handle the case where [*i] == [*j] == [median], but
// i != j. Only possible with multiple copies of the same entry.
if(v[*i].Y1 == v[*j].Y1) { i++; }
}
// Call recursively for the two sub-partitions. The partitions don't
// include position i because it is correctly positioned.
QuickSortIndex(v, F, i-1);
QuickSortIndex(v, i+1, L);
}
}
/**************************************************************************\
*
* Function Description:
*
* Initialize the data needed for the rasterization and invoke the
* appropriate rasterizer (convex or non-convex).
*
* Arguments:
*
* [IN] path - the path to rasterize
* [IN] matrix - the matrix to transform the path points by
* [IN] fillMode - the fill mode to use (Alternate or Winding)
* [IN] output - used to output the spans of the rasterization
* [IN] clipBounds - clipbounds (if any) to use to speed the process
* [IN] yDda - instance of DDA class to use
* [IN] type - type of enumeration
* [IN] strokeWidth - for wide line rasterizing
*
* Return Value:
*
* NONE
*
* Created:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
Rasterizer(
const DpPath * path,
const GpMatrix * matrix,
GpFillMode fillMode,
DpOutputSpan * output,
REAL dpiX,
REAL dpiY,
const GpRect * clipBounds,
GpYDda * yDda,
DpEnumerationType type,
const DpPen * pen
)
{
ASSERT ((path != NULL) && (matrix != NULL) && (output != NULL));
if ((dpiX <= 0) || (dpiY <= 0))
{
dpiX = Globals::DesktopDpiX;
dpiY = Globals::DesktopDpiY;
}
GpStatus status = Ok;
FIX4 yMin; // min y of all vectors
FIX4 yMax; // max y of all vectors
INT numVectors; // num vectors in VectorList
RasterizeVector * vectorList = NULL; // list of all vectors of path
INT * sortedVectorIndices; // sorted list of vector indices
GpYDda * left;
GpYDda * right;
GpPointF pointsF[NUM_ENUMERATE_POINTS];
BYTE types[NUM_ENUMERATE_POINTS];
INT count;
INT estimateVectors;
GpPathPointType pointType;
FIX4 xFirst; // first point in sub-path
FIX4 yFirst;
FIX4 xStart; // start point of a vector
FIX4 yStart;
FIX4 xEnd; // end point of a vector
FIX4 yEnd;
RasterizeVector * vector;
INT i;
GpVectorDirection direction;
GpVectorDirection newDirection;
INT numDirections;
BOOL multipleSubPaths;
BOOL isAntiAliased = FALSE;
const DpPath* flattenedPath = path->GetFlattenedPath(
matrix,
type,
pen
);
if(!flattenedPath)
return OutOfMemory;
DpPathIterator enumerator(flattenedPath->GetPathPoints(),
flattenedPath->GetPathTypes(),
flattenedPath->GetPointCount());
if(!enumerator.IsValid())
{
// No need to rasterize. Exit gracefully.
goto Done;
}
estimateVectors = enumerator.GetCount() +
enumerator.GetSubpathCount() - 1;
// The estimate must be >= the actual number of points
if (estimateVectors < 2)
{
goto Done;
}
// Allocate vectorList and sortedVectorIndices together
vectorList = static_cast<RasterizeVector *>
(GpMalloc((estimateVectors * sizeof(RasterizeVector)) +
(estimateVectors * sizeof(INT))));
if (vectorList == NULL)
{
status = OutOfMemory;
goto Done;
}
sortedVectorIndices = reinterpret_cast<INT*>
(vectorList + estimateVectors);
// enumerate the first set of points from the path
count = enumerator.Enumerate(pointsF, types, NUM_ENUMERATE_POINTS);
ASSERT (count <= NUM_ENUMERATE_POINTS);
// We know we are done enumerating when we get back a 0 count
if (count <= 0)
{
goto Done;
}
// Keep track of direction changes as a way to know if we can use
// the convex rasterizer or not. As long as there is only 1 sub-path
// and there are at most 3 directions (e.g. down, up, down),
// we can use the convex rasterizer.
direction = VectorHorizontal;
newDirection = VectorHorizontal;
numDirections = 0;
multipleSubPaths = FALSE;
vector = vectorList;
numVectors = 0;
xFirst = GpRealToFix4(pointsF[0].X);
yFirst = GpRealToFix4(pointsF[0].Y);
xStart = xEnd = xFirst;
yStart = yEnd = yFirst;
yMin = yMax = yFirst;
// Add all the vectors to the vector list. The vector list keeps
// the coordinates as fixed point values (28.4). As each one is
// added, YMin and YMax are updated.
// Each sub-path is automatically closed, even if it was not
// specifically asked to be closed.
for (i = 1;; i++)
{
if (i == count)
{
count = enumerator.Enumerate(pointsF, types, 32);
ASSERT (count <= 32);
if (count <= 0)
{
// Close the last subpath, if necessary
ADDVECTOR_SETYBOUNDS(xEnd, yEnd, xFirst, yFirst, yMin, yMax,
numVectors, vector, newDirection);
ASSERT (numVectors <= estimateVectors);
if(newDirection != direction) // for convex test
{
numDirections++;
direction = newDirection;
}
break;
}
i = 0;
}
pointType = static_cast<GpPathPointType>
(types[i] & PathPointTypePathTypeMask);
if (pointType != PathPointTypeStart)
{
ASSERT(pointType == PathPointTypeLine);
xEnd = GpRealToFix4(pointsF[i].X);
yEnd = GpRealToFix4(pointsF[i].Y);
ADDVECTOR_SETYBOUNDS (xStart, yStart, xEnd, yEnd, yMin, yMax,
numVectors, vector, newDirection);
ASSERT (numVectors <= estimateVectors);
if(newDirection != direction) // for convex test
{
numDirections++;
direction = newDirection;
}
}
else // it is a start point
{
// Close the previous subpath, if necessary
ADDVECTOR_SETYBOUNDS(xEnd, yEnd, xFirst, yFirst, yMin, yMax,
numVectors, vector, newDirection);
ASSERT (numVectors <= estimateVectors);
xFirst = GpRealToFix4(pointsF[i].X);
yFirst = GpRealToFix4(pointsF[i].Y);
xEnd = xFirst;
yEnd = yFirst;
// Can't use convex rasterizer for more than one sub-path
// unless we've specifically been told it's Ok to do so.
multipleSubPaths = TRUE;
}
xStart = xEnd;
yStart = yEnd;
}
if (numVectors < 2)
{
goto Done;
}
yMin = GpFix4Ceiling (yMin); // convert to int
yMax = GpFix4Ceiling (yMax) - 1; // convert to int
// Initialize and sort the vector indices in order of
// increasing yMin values.
// All the vectors must be set up first in the VectorList.
// Initialize the index list.
for(INT count = 0; count < numVectors; count++)
{
sortedVectorIndices[count] = count;
}
// Sort the index list.
QuickSortIndex(
vectorList,
&sortedVectorIndices[0],
&sortedVectorIndices[numVectors-1]
);
left = yDda;
if (left == NULL)
{
left = new GpYDda();
if (left == NULL)
{
status = OutOfMemory;
goto Done;
}
}
right = left->CreateYDda();
if (right == NULL)
{
status = OutOfMemory;
delete left;
goto Done;
}
if ((flattenedPath->IsConvex()) ||
((!multipleSubPaths) && (numDirections <= 3)))
{
status = ConvexRasterizer(yMin, yMax, numVectors, vectorList,
sortedVectorIndices, left, right,
output, clipBounds);
}
else
{
status = NonConvexRasterizer(yMin, yMax, numVectors, vectorList,
sortedVectorIndices, left, right,
output, clipBounds,
(fillMode == FillModeAlternate));
}
Done:
GpFree(vectorList);
delete flattenedPath;
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Rasterize a path into a set of spans on each raster (Y value) that the
* path intersects. The specified output object is used to output the spans.
*
* First the clipping is looked at to determine if the path is visible.
* If not, we're done. If totally visible, no clipping needed. Otherwise,
* set up the clip region to handle the clipping of this path. The output
* method of the rasterizer is the clip region which then clips the span
* before calling the real output method.
*
* Arguments:
*
* [IN] path - the path to rasterize
* [IN] matrix - the matrix to transform the path points by
* [IN] fillMode - the fill mode to use (Alternate or Winding)
* [IN] output - the object used to output the spans of the rasterization
* [IN] clipRegion - clip region to clip against (or NULL)
* [IN] drawBounds - bounds of the path in device units
* [IN] yDda - instance of DDA class to use
* [IN] type - type of enumeration
* [IN] strokeWidth - for wide line rasterizing
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 01/12/1999 DCurtis
*
\**************************************************************************/
GpStatus
Rasterize(
const DpPath * path,
GpMatrix * matrix,
GpFillMode fillMode,
DpOutputSpan * output,
DpClipRegion * clipRegion,
const GpRect * drawBounds,
REAL dpiX,
REAL dpiY,
GpYDda * yDda,
DpEnumerationType type,
const DpPen * pen
)
{
ASSERT ((path != NULL) && (matrix != NULL) && (output != NULL));
ASSERT ((clipRegion != NULL) && (drawBounds != NULL));
GpRect clipBounds;
GpRect * clipBoundsPointer = NULL;
DpRegion::Visibility visibility;
visibility = clipRegion->GetRectVisibility(
drawBounds->X,
drawBounds->Y,
drawBounds->X + drawBounds->Width,
drawBounds->Y + drawBounds->Height);
switch (visibility)
{
case DpRegion::Invisible:
return Ok;
case DpRegion::TotallyVisible: // No clipping needed
break;
default: // Need to clip
clipRegion->GetBounds(&clipBounds);
clipBoundsPointer = &clipBounds;
clipRegion->InitClipping(output, drawBounds->Y);
output = clipRegion;
break;
}
GpStatus status = Rasterizer(path, matrix, fillMode, output, dpiX, dpiY,
clipBoundsPointer, yDda, type, pen);
if (clipRegion != NULL)
{
clipRegion->EndClipping();
}
return status;
}