/**************************************************************************\
* 
* 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;
}