/*************************************************************************\
* Module Name: Lines.c
*
* Contains most of the required GDI line support.  Supports drawing
* lines in short 'strips' when clipping is complex or coordinates
* are too large to be drawn by the line hardware.
*
* Copyright (c) 1990-1995 Microsoft Corporation
\**************************************************************************/

#include "precomp.h"

///////////////////////////////////////////////////////////////////////

// We have to be careful of arithmetic overflow in a number of places.
// Fortunately, the compiler is guaranteed to natively support 64-bit
// signed LONGLONGs and 64-bit unsigned DWORDLONGs.
//
// UUInt32x32To64(a, b) is a macro defined in 'winnt.h' that multiplies
//      two 32-bit ULONGs to produce a 64-bit DWORDLONG result.
//
// UInt64By32To32 is our own macro to divide a 64-bit DWORDLONG by
//      a 32-bit ULONG to produce a 32-bit ULONG result.
//
// UInt64Mod32To32 is our own macro to modulus a 64-bit DWORDLONG by
//      a 32-bit ULONG to produce a 32-bit ULONG result.
//
// 64 bit divides are usually very expensive.  Since it's very rare
// that we'll get lines where the upper 32 bits of the 64 bit result
// are used, we can almost always use 32-bit ULONG divides.  We still
// must correctly handle the larger cases:

#define UInt64Div32To32(a, b)                   \
    ((((DWORDLONG)(a)) > ULONG_MAX)          ?  \
        (ULONG)((DWORDLONG)(a) / (ULONG)(b)) :  \
        (ULONG)((ULONG)(a) / (ULONG)(b)))

#define UInt64Mod32To32(a, b)                   \
    ((((DWORDLONG)(a)) > ULONG_MAX)          ?  \
        (ULONG)((DWORDLONG)(a) % (ULONG)(b)) :  \
        (ULONG)((ULONG)(a) % (ULONG)(b)))

#define SWAPL(x,y,t)        {t = x; x = y; y = t;}

FLONG gaflRound[] = {
    FL_H_ROUND_DOWN | FL_V_ROUND_DOWN, // no flips
    FL_H_ROUND_DOWN | FL_V_ROUND_DOWN, // FL_FLIP_D
    FL_H_ROUND_DOWN,                   // FL_FLIP_V
    FL_V_ROUND_DOWN,                   // FL_FLIP_V | FL_FLIP_D
    FL_V_ROUND_DOWN,                   // FL_FLIP_SLOPE_ONE
    0xbaadf00d,                        // FL_FLIP_SLOPE_ONE | FL_FLIP_D
    FL_H_ROUND_DOWN,                   // FL_FLIP_SLOPE_ONE | FL_FLIP_V
    0xbaadf00d                         // FL_FLIP_SLOPE_ONE | FL_FLIP_V | FL_FLIP_D
};

/******************************Public*Routine******************************\
* BOOL bLines(ppdev, pptfxFirst, pptfxBuf, cptfx, pls,
*                   prclClip, apfn[], flStart)
*
* Computes the DDA for the line and gets ready to draw it.  Puts the
* pixel data into an array of strips, and calls a strip routine to
* do the actual drawing.
*
\**************************************************************************/

BOOL bLines(
PDEV*      ppdev,
POINTFIX*  pptfxFirst,  // Start of first line
POINTFIX*  pptfxBuf,    // Pointer to buffer of all remaining lines
RUN*       prun,        // Pointer to runs if doing complex clipping
ULONG      cptfx,       // Number of points in pptfxBuf or number of runs
                        // in prun
LINESTATE* pls,         // Colour and style info
RECTL*     prclClip,    // Pointer to clip rectangle if doing simple clipping
PFNSTRIP*  apfn,        // Array of strip functions
FLONG      flStart,     // Flags for each line, which is a combination of:
                        //      FL_SIMPLE_CLIP
                        //      FL_COMPLEX_CLIP
                        //      FL_STYLED
                        //      FL_LAST_PEL_INCLUSIVE
                        //        - Should be set only for all integer lines,
                        //          and can't be used with FL_COMPLEX_CLIP
ULONG      ulHwMix)
{
    ULONG     M0;
    ULONG     dM;
    ULONG     N0;
    ULONG     dN;
    ULONG     dN_Original;
    FLONG     fl;
    LONG      x;
    LONG      y;

    LONGLONG  llBeta;
    LONGLONG  llGamma;
    LONGLONG  dl;
    LONGLONG  ll;

    ULONG     ulDelta;

    ULONG     x0;
    ULONG     y0;
    ULONG     x1;
    ULONG     cStylePels;    // Major length of line in pixels for styling
    ULONG     xStart;
    POINTL    ptlStart;
    STRIP     strip;
    PFNSTRIP  pfn;
    LONG      cPels;
    LONG*     plStrip;
    LONG*     plStripEnd;
    LONG      cStripsInNextRun;

    POINTFIX* pptfxBufEnd = pptfxBuf + cptfx; // Last point in path record
    STYLEPOS  spThis;                         // Style pos for this line
    BYTE*     pjBase;

    pjBase = ppdev->pjBase;

    do {

/***********************************************************************\
* Start the DDA calculations.                                           *
\***********************************************************************/

        M0 = (LONG) pptfxFirst->x;
        dM = (LONG) pptfxBuf->x;

        N0 = (LONG) pptfxFirst->y;
        dN = (LONG) pptfxBuf->y;

        fl = flStart;

        // Check for non-clipped, non-styled integer endpoint lines

        if ((fl & (FL_CLIP | FL_STYLED)) == 0)
        {
            // Special-case integer end-point lines:

            if (((M0 | dM | N0 | dN) & (F - 1)) == 0)
            {
                LONG x0;
                LONG y0;
                LONG x1;
                LONG y1;

                x0 = M0 >> FLOG2;
                x1 = dM >> FLOG2;
                y0 = N0 >> FLOG2;
                y1 = dN >> FLOG2;

                // Unfortunately, we can only use the Weitek's point-
                // to-point capability for perfectly horizontal and
                // vertical lines, because for other lines the tie-
                // breaker rule comes into play, and the Weitek has
                // exactly the wrong tie-breaker convention.

                if (y0 == y1)
                {
                    // Horizontal integer line.  Do last-pel exclusion:

                    if (x0 < x1)
                        x1--;
                    else if (x0 > x1)
                        x1++;
                    else
                        goto Next_Line;         // Zero-pel line

                    CP_METALINE(ppdev, pjBase, x0, y0);
                    CP_METALINE(ppdev, pjBase, x1, y1);
                    CP_START_QUAD_WAIT(ppdev, pjBase);
                    goto Next_Line;
                }
                else if (x0 == x1)
                {
                    // Vertical integer line.  Do last-pel exclusion:

                    if (y0 < y1)
                        y1--;
                    else
                        y1++;

                    CP_METALINE(ppdev, pjBase, x0, y0);
                    CP_METALINE(ppdev, pjBase, x1, y1);
                    CP_START_QUAD_WAIT(ppdev, pjBase);
                    goto Next_Line;
                }
            }
        }

        if ((LONG) M0 > (LONG) dM)
        {
        // Ensure that we run left-to-right:

            register ULONG ulTmp;
            SWAPL(M0, dM, ulTmp);
            SWAPL(N0, dN, ulTmp);
            fl |= FL_FLIP_H;
        }

    // Compute the delta dx.  The DDI says we can never have a valid delta
    // with a magnitued more than 2^31 - 1, but GDI never actually checks
    // its transforms.  So we have to check for this case to avoid overflow:

        dM -= M0;
        if ((LONG) dM < 0)
        {
            goto Next_Line;
        }

        if ((LONG) dN < (LONG) N0)
        {
        // Line runs from bottom to top, so flip across y = 0:

            N0 = -(LONG) N0;
            dN = -(LONG) dN;
            fl |= FL_FLIP_V;
        }

        dN -= N0;

        if ((LONG) dN < 0)
        {
            goto Next_Line;
        }

    // We now have a line running left-to-right, top-to-bottom from (M0, N0)
    // to (M0 + dM, N0 + dN):

        if (dN >= dM)
        {
            if (dN == dM)
            {
            // Have to special case slopes of one:

                fl |= FL_FLIP_SLOPE_ONE;
            }
            else
            {
            // Since line has slope greater than 1, flip across x = y:

                register ULONG ulTmp;
                SWAPL(dM, dN, ulTmp);
                SWAPL(M0, N0, ulTmp);
                fl |= FL_FLIP_D;
            }
        }

        fl |= gaflRound[(fl & FL_ROUND_MASK) >> FL_ROUND_SHIFT];

        x = LFLOOR((LONG) M0);
        y = LFLOOR((LONG) N0);

        M0 = FXFRAC(M0);
        N0 = FXFRAC(N0);

    // Calculate the remainder term [ dM * (N0 + F/2) - M0 * dN ]:

        llGamma = UInt32x32To64(dM, N0 + F/2) - UInt32x32To64(M0, dN);
        if (fl & FL_V_ROUND_DOWN)   // Adjust so y = 1/2 rounds down
        {
            llGamma--;
        }

        llGamma >>= FLOG2;
        llBeta = ~llGamma;

/***********************************************************************\
* Figure out which pixels are at the ends of the line.                  *
\***********************************************************************/

    // The toughest part of GIQ is determining the start and end pels.
    //
    // Our approach here is to calculate x0 and x1 (the inclusive start
    // and end columns of the line respectively, relative to our normalized
    // origin).  Then x1 - x0 + 1 is the number of pels in the line.  The
    // start point is easily calculated by plugging x0 into our line equation
    // (which takes care of whether y = 1/2 rounds up or down in value)
    // getting y0, and then undoing the normalizing flips to get back
    // into device space.
    //
    // We look at the fractional parts of the coordinates of the start and
    // end points, and call them (M0, N0) and (M1, N1) respectively, where
    // 0 <= M0, N0, M1, N1 < 16.  We plot (M0, N0) on the following grid
    // to determine x0:
    //
    //   +-----------------------> +x
    //   |
    //   | 0                     1
    //   |     0123456789abcdef
    //   |
    //   |   0 ........?xxxxxxx
    //   |   1 ..........xxxxxx
    //   |   2 ...........xxxxx
    //   |   3 ............xxxx
    //   |   4 .............xxx
    //   |   5 ..............xx
    //   |   6 ...............x
    //   |   7 ................
    //   |   8 ................
    //   |   9 ......**........
    //   |   a ........****...x
    //   |   b ............****
    //   |   c .............xxx****
    //   |   d ............xxxx    ****
    //   |   e ...........xxxxx        ****
    //   |   f ..........xxxxxx
    //   |
    //   | 2                     3
    //   v
    //
    //   +y
    //
    // This grid accounts for the appropriate rounding of GIQ and last-pel
    // exclusion.  If (M0, N0) lands on an 'x', x0 = 2.  If (M0, N0) lands
    // on a '.', x0 = 1.  If (M0, N0) lands on a '?', x0 rounds up or down,
    // depending on what flips have been done to normalize the line.
    //
    // For the end point, if (M1, N1) lands on an 'x', x1 =
    // floor((M0 + dM) / 16) + 1.  If (M1, N1) lands on a '.', x1 =
    // floor((M0 + dM)).  If (M1, N1) lands on a '?', x1 rounds up or down,
    // depending on what flips have been done to normalize the line.
    //
    // Lines of exactly slope one require a special case for both the start
    // and end.  For example, if the line ends such that (M1, N1) is (9, 1),
    // the line has gone exactly through (8, 0) -- which may be considered
    // to be part of 'x' because of rounding!  So slopes of exactly slope
    // one going through (8, 0) must also be considered as belonging in 'x'.
    //
    // For lines that go left-to-right, we have the following grid:
    //
    //   +-----------------------> +x
    //   |
    //   | 0                     1
    //   |     0123456789abcdef
    //   |
    //   |   0 xxxxxxxx?.......
    //   |   1 xxxxxxx.........
    //   |   2 xxxxxx..........
    //   |   3 xxxxx...........
    //   |   4 xxxx............
    //   |   5 xxx.............
    //   |   6 xx..............
    //   |   7 x...............
    //   |   8 x...............
    //   |   9 x.....**........
    //   |   a xx......****....
    //   |   b xxx.........****
    //   |   c xxxx............****
    //   |   d xxxxx...........    ****
    //   |   e xxxxxx..........        ****
    //   |   f xxxxxxx.........
    //   |
    //   | 2                     3
    //   v
    //
    //   +y
    //
    // This grid accounts for the appropriate rounding of GIQ and last-pel
    // exclusion.  If (M0, N0) lands on an 'x', x0 = 0.  If (M0, N0) lands
    // on a '.', x0 = 1.  If (M0, N0) lands on a '?', x0 rounds up or down,
    // depending on what flips have been done to normalize the line.
    //
    // For the end point, if (M1, N1) lands on an 'x', x1 =
    // floor((M0 + dM) / 16) - 1.  If (M1, N1) lands on a '.', x1 =
    // floor((M0 + dM)).  If (M1, N1) lands on a '?', x1 rounds up or down,
    // depending on what flips have been done to normalize the line.
    //
    // Lines of exactly slope one must be handled similarly to the right-to-
    // left case.

        {

        // Calculate x0, x1

            ULONG N1 = FXFRAC(N0 + dN);
            ULONG M1 = FXFRAC(M0 + dM);

            x1 = LFLOOR(M0 + dM);

            if (fl & FL_LAST_PEL_INCLUSIVE)
            {
            // It sure is easy to compute the first pel when lines have only
            // integer coordinates and are last-pel inclusive:

                x0 = 0;
                y0 = 0;

            // Last-pel inclusive lines that are exactly one pixel long
            // have a 'delta-x' and 'delta-y' equal to zero.  The problem is
            // that our clip code assumes that 'delta-x' is always non-zero
            // (since it never happens with last-pel exclusive lines).  As
            // an inelegant solution, we simply modify 'delta-x' in this
            // case -- because the line is exactly one pixel long, changing
            // the slope will obviously have no effect on rasterization.

                if (x1 == 0)
                {
                    dM      = 1;
                    llGamma = 0;
                    llBeta  = ~llGamma;
                }
            }
            else
            {
                if (fl & FL_FLIP_H)
                {
                // ---------------------------------------------------------------
                // Line runs right-to-left:  <----

                // Compute x1:

                    if (N1 == 0)
                    {
                        if (LROUND(M1, fl & FL_H_ROUND_DOWN))
                        {
                            x1++;
                        }
                    }
                    else if (abs((LONG) (N1 - F/2)) + M1 > F)
                    {
                        x1++;
                    }

                    if ((fl & (FL_FLIP_SLOPE_ONE | FL_H_ROUND_DOWN))
                           == (FL_FLIP_SLOPE_ONE))
                    {
                    // Have to special-case diagonal lines going through our
                    // the point exactly equidistant between two horizontal
                    // pixels, if we're supposed to round x=1/2 down:

                        if ((N1 > 0) && (M1 == N1 + 8))
                            x1++;

                    // Don't you love special cases?  Is this a rhetorical question?

                        if ((N0 > 0) && (M0 == N0 + 8))
                        {
                            x0      = 2;
                            ulDelta = dN;
                            goto right_to_left_compute_y0;
                        }
                    }

                // Compute x0:

                    x0      = 1;
                    ulDelta = 0;
                    if (N0 == 0)
                    {
                        if (LROUND(M0, fl & FL_H_ROUND_DOWN))
                        {
                            x0      = 2;
                            ulDelta = dN;
                        }
                    }
                    else if (abs((LONG) (N0 - F/2)) + M0 > F)
                    {
                        x0      = 2;
                        ulDelta = dN;
                    }


                // Compute y0:

                right_to_left_compute_y0:

                    y0 = 0;
                    ll = llGamma + (LONGLONG) ulDelta;

                    if (ll >= (LONGLONG) (2 * dM - dN))
                        y0 = 2;
                    else if (ll >= (LONGLONG) (dM - dN))
                        y0 = 1;
                }
                else
                {
                // ---------------------------------------------------------------
                // Line runs left-to-right:  ---->

                // Compute x1:

                    if (!(fl & FL_LAST_PEL_INCLUSIVE))
                        x1--;

                    if (M1 > 0)
                    {
                        if (N1 == 0)
                        {
                            if (LROUND(M1, fl & FL_H_ROUND_DOWN))
                                x1++;
                        }
                        else if (abs((LONG) (N1 - F/2)) <= (LONG) M1)
                        {
                            x1++;
                        }
                    }

                    if ((fl & (FL_FLIP_SLOPE_ONE | FL_H_ROUND_DOWN))
                           == (FL_FLIP_SLOPE_ONE | FL_H_ROUND_DOWN))
                    {
                    // Have to special-case diagonal lines going through our
                    // the point exactly equidistant between two horizontal
                    // pixels, if we're supposed to round x=1/2 down:

                        if ((M1 > 0) && (N1 == M1 + 8))
                            x1--;

                        if ((M0 > 0) && (N0 == M0 + 8))
                        {
                            x0 = 0;
                            goto left_to_right_compute_y0;
                        }
                    }

                // Compute x0:

                    x0 = 0;
                    if (M0 > 0)
                    {
                        if (N0 == 0)
                        {
                            if (LROUND(M0, fl & FL_H_ROUND_DOWN))
                                x0 = 1;
                        }
                        else if (abs((LONG) (N0 - F/2)) <= (LONG) M0)
                        {
                            x0 = 1;
                        }
                    }

                // Compute y0:

                left_to_right_compute_y0:

                    y0 = 0;
                    if (llGamma >= (LONGLONG) (dM - (dN & (-(LONG) x0))))
                    {
                        y0 = 1;
                    }
                }
            }
        }

        cStylePels = x1 - x0 + 1;
        if ((LONG) cStylePels <= 0)
            goto Next_Line;

        xStart = x0;

/***********************************************************************\
* Complex clipping.                                                     *
\***********************************************************************/

        if (fl & FL_COMPLEX_CLIP)
        {
            dN_Original = dN;

        Continue_Complex_Clipping:

            if (fl & FL_FLIP_H)
            {
            // Line runs right-to-left <-----

                x0 = xStart + cStylePels - prun->iStop - 1;
                x1 = xStart + cStylePels - prun->iStart - 1;
            }
            else
            {
            // Line runs left-to-right ----->

                x0 = xStart + prun->iStart;
                x1 = xStart + prun->iStop;
            }

            prun++;

        // Reset some variables we'll nuke a little later:

            dN          = dN_Original;
            pls->spNext = pls->spComplex;

        // No overflow since large integer math is used.  Both values
        // will be positive:

            dl = UInt32x32To64(x0, dN) + llGamma;

        // y0 = dl / dM:

            y0 = UInt64Div32To32(dl, dM);

            ASSERTDD((LONG) y0 >= 0, "y0 weird: Goofed up end pel calc?");
        }

/***********************************************************************\
* Simple rectangular clipping.                                          *
\***********************************************************************/

        if (fl & FL_SIMPLE_CLIP)
        {
            ULONG y1;
            LONG  xRight;
            LONG  xLeft;
            LONG  yBottom;
            LONG  yTop;

        // Note that y0 and y1 are actually the lower and upper bounds,
        // respectively, of the y coordinates of the line (the line may
        // have actually shrunk due to first/last pel clipping).
        //
        // Also note that x0, y0 are not necessarily zero.

            RECTL* prcl = &prclClip[(fl & FL_RECTLCLIP_MASK) >>
                                    FL_RECTLCLIP_SHIFT];

        // Normalize to the same point we've normalized for the DDA
        // calculations:

            xRight  = prcl->right  - x;
            xLeft   = prcl->left   - x;
            yBottom = prcl->bottom - y;
            yTop    = prcl->top    - y;

            if (yBottom <= (LONG) y0 ||
                xRight  <= (LONG) x0 ||
                xLeft   >  (LONG) x1)
            {
            Totally_Clipped:

                if (fl & FL_STYLED)
                {
                    pls->spNext += cStylePels;
                    if (pls->spNext >= pls->spTotal2)
                        pls->spNext %= pls->spTotal2;
                }

                goto Next_Line;
            }

            if ((LONG) x1 >= xRight)
                x1 = xRight - 1;

        // We have to know the correct y1, which we haven't bothered to
        // calculate up until now.  This multiply and divide is quite
        // expensive; we could replace it with code similar to that which
        // we used for computing y0.
        //
        // The reason why we need the actual value, and not an upper
        // bounds guess like y1 = LFLOOR(dM) + 2 is that we have to be
        // careful when calculating x(y) that y0 <= y <= y1, otherwise
        // we can overflow on the divide (which, needless to say, is very
        // bad).

            dl = UInt32x32To64(x1, dN) + llGamma;

        // y1 = dl / dM:

            y1 = UInt64Div32To32(dl, dM);

            if (yTop > (LONG) y1)
                goto Totally_Clipped;

            if (yBottom <= (LONG) y1)
            {
                y1 = yBottom;
                dl = UInt32x32To64(y1, dM) + llBeta;

            // x1 = dl / dN:

                x1 = UInt64Div32To32(dl, dN);
            }

        // At this point, we've taken care of calculating the intercepts
        // with the right and bottom edges.  Now we work on the left and
        // top edges:

            if (xLeft > (LONG) x0)
            {
                x0 = xLeft;
                dl = UInt32x32To64(x0, dN) + llGamma;

            // y0 = dl / dM;

                y0 = UInt64Div32To32(dl, dM);

                if (yBottom <= (LONG) y0)
                    goto Totally_Clipped;
            }

            if (yTop > (LONG) y0)
            {
                y0 = yTop;
                dl = UInt32x32To64(y0, dM) + llBeta;

            // x0 = dl / dN + 1;

                x0 = UInt64Div32To32(dl, dN) + 1;

                if (xRight <= (LONG) x0)
                    goto Totally_Clipped;
            }

            ASSERTDD(x0 <= x1, "Improper rectangle clip");
        }

/***********************************************************************\
* Done clipping.  Unflip if necessary.                                 *
\***********************************************************************/

        ptlStart.x = x + x0;
        ptlStart.y = y + y0;

        if (fl & FL_FLIP_D)
        {
            register LONG lTmp;
            SWAPL(ptlStart.x, ptlStart.y, lTmp);
        }


        if (fl & FL_FLIP_V)
        {
            ptlStart.y = -ptlStart.y;
        }

        cPels = x1 - x0 + 1;

/***********************************************************************\
* Style calculations.                                                   *
\***********************************************************************/

        if (fl & FL_STYLED)
        {
            STYLEPOS sp;

            spThis       = pls->spNext;
            pls->spNext += cStylePels;

            {
                if (pls->spNext >= pls->spTotal2)
                    pls->spNext %= pls->spTotal2;

                if (fl & FL_FLIP_H)
                    sp = pls->spNext - x0 + xStart;
                else
                    sp = spThis + x0 - xStart;

                ASSERTDD(fl & FL_STYLED, "Oops");

            // Normalize our target style position:

                if ((sp < 0) || (sp >= pls->spTotal2))
                {
                    sp %= pls->spTotal2;

                // The modulus of a negative number is not well-defined
                // in C -- if it's negative we'll adjust it so that it's
                // back in the range [0, spTotal2):

                    if (sp < 0)
                        sp += pls->spTotal2;
                }

            // Since we always draw the line left-to-right, but styling is
            // always done in the direction of the original line, we have
            // to figure out where we are in the style array for the left
            // edge of this line.

                if (fl & FL_FLIP_H)
                {
                // Line originally ran right-to-left:

                    sp = -sp;
                    if (sp < 0)
                        sp += pls->spTotal2;

                    pls->ulStyleMask = ~pls->ulStartMask;
                    pls->pspStart    = &pls->aspRtoL[0];
                    pls->pspEnd      = &pls->aspRtoL[pls->cStyle - 1];
                }
                else
                {
                // Line originally ran left-to-right:

                    pls->ulStyleMask = pls->ulStartMask;
                    pls->pspStart    = &pls->aspLtoR[0];
                    pls->pspEnd      = &pls->aspLtoR[pls->cStyle - 1];
                }

                if (sp >= pls->spTotal)
                {
                    sp -= pls->spTotal;
                    if (pls->cStyle & 1)
                        pls->ulStyleMask = ~pls->ulStyleMask;
                }

                pls->psp = pls->pspStart;
                while (sp >= *pls->psp)
                    sp -= *pls->psp++;

                ASSERTDD(pls->psp <= pls->pspEnd,
                        "Flew off into NeverNeverLand");

                pls->spRemaining = *pls->psp - sp;
                if ((pls->psp - pls->pspStart) & 1)
                    pls->ulStyleMask = ~pls->ulStyleMask;
            }
        }

        plStrip    = &strip.alStrips[0];
        plStripEnd = &strip.alStrips[STRIP_MAX];    // Is exclusive
        cStripsInNextRun   = 0x7fffffff;

        strip.ptlStart = ptlStart;

        if (2 * dN > dM &&
            !(fl & FL_STYLED))
        {
        // Do a half flip!  Remember that we may doing this on the
        // same line multiple times for complex clipping (meaning the
        // affected variables should be reset for every clip run):

            fl |= FL_FLIP_HALF;

            llBeta  = llGamma - (LONGLONG) ((LONG) dM);
            dN = dM - dN;
            y0 = x0 - y0;       // Note this may overflow, but that's okay
        }

    // Now, run the DDA starting at (ptlStart.x, ptlStart.y)!

        strip.flFlips = fl;
        pfn           = apfn[(fl & FL_STRIP_MASK) >> FL_STRIP_SHIFT];

    // Now calculate the DDA variables needed to figure out how many pixels
    // go in the very first strip:

        {
            register LONG  i;
            register ULONG dI;
            register ULONG dR;
                     ULONG r;

            if (dN == 0)
                i = 0x7fffffff;
            else
            {
                dl = UInt32x32To64(y0 + 1, dM) + llBeta;

                ASSERTDD(dl >= 0, "Oops!");

            // i = (dl / dN) - x0 + 1;
            // r = (dl % dN);

                i = UInt64Div32To32(dl, dN);
                r = UInt64Mod32To32(dl, dN);
                i = i - x0 + 1;

                dI = dM / dN;
                dR = dM % dN;               // 0 <= dR < dN

                ASSERTDD(dI > 0, "Weird dI");
            }

            ASSERTDD(i > 0 && i <= 0x7fffffff, "Weird initial strip length");
            ASSERTDD(cPels > 0, "Zero pel line");

/***********************************************************************\
* Run the DDA!                                                          *
\***********************************************************************/

            while(TRUE)
            {
                cPels -= i;
                if (cPels <= 0)
                    break;

                *plStrip++ = i;

                if (plStrip == plStripEnd)
                {
                    strip.cStrips = (LONG)(plStrip - &strip.alStrips[0]);
                    (*pfn)(ppdev, &strip, pls);
                    plStrip = &strip.alStrips[0];
                }

                i = dI;
                r += dR;

                if (r >= dN)
                {
                    r -= dN;
                    i++;
                }
            }

            *plStrip++ = cPels + i;

            strip.cStrips = (ULONG)(plStrip - &strip.alStrips[0]);
            (*pfn)(ppdev, &strip, pls);


        }

    Next_Line:

        if (fl & FL_COMPLEX_CLIP)
        {
            cptfx--;
            if (cptfx != 0)
                goto Continue_Complex_Clipping;

            break;
        }
        else
        {
            pptfxFirst = pptfxBuf;
            pptfxBuf++;
        }

    } while (pptfxBuf < pptfxBufEnd);

    return(TRUE);

}

/******************************Public*Routine******************************\
* BOOL bIntegerUnclippedLines
*
* Draws lines using the Weitek's point-to-point capabilities.
* Unfortunately, the Weitek has exactly the wrong rounding convention
* for tie-breakers, and GDI is very picky about this.
*
* Consequently, we can only use the line hardware when we know there
* will be no tie-breakers.  Fortunately, this is pretty easy to detect,
* and the odds are that 3 out of 4 lines will not have tie breakers.  For
* those cases where there are tie-breakers, we can still usually draw the
* lines using the hardware, this time by doing a one-wide trapezoid.
* Unfortunately, this works for only 6 of the 8 octants, so for the final
* case we punt to our strips routine.
*
* Additional complications include the fact that lines have to be last-pel
* exclusive, and that we try to optimize horizontal and vertical lines.
*
\**************************************************************************/

BOOL bIntegerUnclippedLines(
PDEV*      ppdev,
POINTFIX*  pptfxFirst,
POINTFIX*  pptfxBuf,
RUN*       prun,
ULONG      cptfx,
LINESTATE* pls,
RECTL*     prclClip,
PFNSTRIP*  apfn,
FLONG      flStart,
ULONG      ulHwMix)
{
    BYTE*   pjBase;
    BOOL    bClippingSet;
    ULONG   ulLineMix;
    ULONG   ulTrapezoidMix;
    LONG    x0;
    LONG    y0;
    LONG    x1;
    LONG    y1;
    LONG    xLeft;
    LONG    xRight;
    LONG    yTop;
    LONG    yBottom;
    LONG    dx;
    LONG    dy;
    LONG    lOr;
    LONG    lBit;
    LONG    iShift;
    LONG    xDir;
    LONG    yDir;

    pjBase       = ppdev->pjBase;
    bClippingSet = FALSE;

    if (P9000(ppdev))
    {
        ulTrapezoidMix = ulHwMix;
        ulLineMix      = ulTrapezoidMix | P9000_OVERSIZED;
    }
    else
    {
        ulTrapezoidMix = ulHwMix & 0xff;
        ulLineMix      = ulTrapezoidMix | P9100_OVERSIZED;
    }

    while (TRUE)
    {
        x0 = pptfxFirst->x;
        y0 = pptfxFirst->y;
        x1 = pptfxBuf->x;
        y1 = pptfxBuf->y;

        // First, check to see if the line is has all-integer coordinates:

        if (((x0 | y0 | x1 | y1) & 0xf) != 0)
        {
            // Ack, this line has non-integer coordinates.  The rest of the
            // lines in this batch likely have non-integer coordinates
            // as well, so punt the entire batch to our strips routine:

            if (bClippingSet)
            {
                CP_WAIT(ppdev, pjBase);
                CP_RASTER(ppdev, pjBase, ulLineMix);
                CP_ABS_WMIN(ppdev, pjBase, 0, 0);
                CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD);
            }

            return(bLines(ppdev, pptfxFirst, pptfxBuf, prun, cptfx, pls,
                          prclClip, apfn, flStart, ulHwMix));
        }
        else
        {
            x0 >>= 4;
            x1 >>= 4;
            y0 >>= 4;
            y1 >>= 4;

            if ((y0 == y1) && (!bClippingSet))
            {
                // We special case horizontal lines:

                if (x0 < x1)
                    x1--;
                else if (x0 > x1)
                    x1++;
                else
                    goto Next_Line;     // Zero-length line

                CP_METALINE(ppdev, pjBase, x0, y0);
                CP_METALINE(ppdev, pjBase, x1, y0);
                CP_START_QUAD_WAIT(ppdev, pjBase);
                goto Next_Line;
            }
            else if (y0 < y1)
            {
                yTop    = y0;
                yBottom = y1;
            }
            else
            {
                yBottom = y0;
                yTop    = y1;
            }

            if ((x0 == x1) && (!bClippingSet))
            {
                // We special case vertical lines:

                if (y0 < y1)
                    y1--;
                else
                    y1++;

                CP_METALINE(ppdev, pjBase, x0, y0);
                CP_METALINE(ppdev, pjBase, x0, y1);
                CP_START_QUAD_WAIT(ppdev, pjBase);
                goto Next_Line;
            }
            else if (x0 < x1)
            {
                xLeft   = x0;
                xRight  = x1;
            }
            else
            {
                xRight  = x0;
                xLeft   = x1;
            }

            dx = xRight - xLeft;
            dy = yBottom - yTop;

            if (dx >= dy)
            {
                if (dx == 0)
                    goto Next_Line;     // Get rid of zero-length line case

                // We have an x-major line.  Adjust the clip box to
                // account for last-pel exclusion:

                if (x0 < x1)
                    xRight--;
                else
                    xLeft++;

                lOr    = (dx | dy);
                lBit   = 1;
                iShift = 1;
                while (!(lOr & lBit))
                {
                    lBit <<= 1;
                    iShift++;
                }

                if (dx & lBit)
                {

                Output_Simple_Line:

                    CP_METALINE(ppdev, pjBase, x0, y0);
                    CP_METALINE(ppdev, pjBase, x1, y1);

                    CP_WAIT(ppdev, pjBase);
                    CP_RASTER(ppdev, pjBase, ulLineMix);
                    CP_WMIN(ppdev, pjBase, xLeft, yTop);
                    CP_WMAX(ppdev, pjBase, xRight, yBottom);
                    CP_START_QUAD_WAIT(ppdev, pjBase);
                    bClippingSet = TRUE;
                    goto Next_Line;
                }
                else
                {
                    if ((dx ^ dy) > 0)
                        goto Punt_Line;

                    // Ick, this x-major line has tie-breaker cases.

                    xDir = 0;
                    yDir = 1;
                    dy >>= iShift;
                    if (y0 > y1)
                    {
                        dy = -dy;
                        yDir = -1;
                    }

                    y0 -= dy;
                    y1 += dy;

                    dx >>= iShift;
                    if (x0 > x1)
                        dx = -dx;

                    x0 -= dx;
                    x1 += dx;

                Output_Trapezoid_Line:

                    CP_METAQUAD(ppdev, pjBase, x0, y0);
                    CP_METAQUAD(ppdev, pjBase, x1 + xDir, y1 - yDir);
                    CP_METAQUAD(ppdev, pjBase, x1, y1);
                    CP_METAQUAD(ppdev, pjBase, x0 - xDir, y0 + yDir);

                    CP_WAIT(ppdev, pjBase);
                    CP_RASTER(ppdev, pjBase, ulTrapezoidMix);
                    CP_WMIN(ppdev, pjBase, xLeft, yTop);
                    CP_WMAX(ppdev, pjBase, xRight, yBottom);
                    CP_START_QUAD_WAIT(ppdev, pjBase);
                    bClippingSet = TRUE;
                    goto Next_Line;
                }
            }
            else
            {
                // We have a y-major line.  Adjust the clip box to
                // account for last-pel exclusion:

                if (y0 < y1)
                    yBottom--;
                else
                    yTop++;

                lOr    = (dx | dy);
                lBit   = 1;
                iShift = 1;
                while (!(lOr & lBit))
                {
                    lBit <<= 1;
                    iShift++;
                }

                if (dy & lBit)
                {
                    goto Output_Simple_Line;
                }
                else
                {
                    // Ick, this y-major line has tie-breaker cases.

                    yDir = 0;
                    xDir = 1;
                    dx >>= iShift;
                    if (x0 > x1)
                    {
                        dx = -dx;
                        xDir = -1;
                    }

                    x0 -= dx;
                    x1 += dx;

                    dy >>= iShift;
                    if (y0 > y1)
                        dy = -dy;

                    y0 -= dy;
                    y1 += dy;

                    goto Output_Trapezoid_Line;
                }
            }
        }

    Punt_Line:

        if (bClippingSet)
        {
            CP_WAIT(ppdev, pjBase);
            CP_RASTER(ppdev, pjBase, ulLineMix);
            CP_ABS_WMIN(ppdev, pjBase, 0, 0);
            CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD);
            bClippingSet = FALSE;
        }

        bLines(ppdev, pptfxFirst, pptfxBuf, NULL, 1, pls,
               prclClip, apfn, flStart, ulHwMix);

    Next_Line:

        --cptfx;
        if (cptfx == 0)
            break;

        pptfxFirst = pptfxBuf;
        pptfxBuf++;
    }

    if (bClippingSet)
    {
        CP_WAIT(ppdev, pjBase);
        CP_RASTER(ppdev, pjBase, ulLineMix);    // Might need for next batch
        CP_ABS_WMIN(ppdev, pjBase, 0, 0);
        CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD);
    }

    return(TRUE);
}

/******************************Public*Routine******************************\
* BOOL bIntegerClippedLines
*
* Draws lines using the hardware when there is a single clipping rectangle.
* See 'bIntegerUnclippedLines' above for more details.
*
\**************************************************************************/

BOOL bIntegerClippedLines(
PDEV*      ppdev,
POINTFIX*  pptfxFirst,
POINTFIX*  pptfxBuf,
RUN*       prun,
ULONG      cptfx,
LINESTATE* pls,
RECTL*     prclClip,
PFNSTRIP*  apfn,
FLONG      flStart,
ULONG      ulHwMix)
{
    BYTE*   pjBase;
    BOOL    bClippingSet;
    ULONG   ulLineMix;
    ULONG   ulTrapezoidMix;
    LONG    x0;
    LONG    y0;
    LONG    x1;
    LONG    y1;
    LONG    xLeft;
    LONG    xRight;
    LONG    yTop;
    LONG    yBottom;
    LONG    dx;
    LONG    dy;
    LONG    lOr;
    LONG    lBit;
    LONG    iShift;
    LONG    xDir;
    LONG    yDir;

    ASSERTDD(flStart & FL_SIMPLE_CLIP, "Expected only simple clipping");

    pjBase       = ppdev->pjBase;
    bClippingSet = FALSE;

    if (P9000(ppdev))
    {
        ulTrapezoidMix = ulHwMix;
        ulLineMix      = ulTrapezoidMix | P9000_OVERSIZED;
    }
    else
    {
        ulTrapezoidMix = ulHwMix & 0xff;
        ulLineMix      = ulTrapezoidMix | P9100_OVERSIZED;
    }

    while (TRUE)
    {
        x0 = pptfxFirst->x;
        y0 = pptfxFirst->y;
        x1 = pptfxBuf->x;
        y1 = pptfxBuf->y;

        // First, check to see if the line is has all-integer coordinates:

        if (((x0 | y0 | x1 | y1) & 0xf) != 0)
        {
            // Ack, this line has non-integer coordinates.  The rest of the
            // lines in this batch likely have non-integer coordinates
            // as well, so punt the entire batch to our strips routine:

            if (bClippingSet)
            {
                CP_WAIT(ppdev, pjBase);
                CP_RASTER(ppdev, pjBase, ulLineMix);
                CP_ABS_WMIN(ppdev, pjBase, 0, 0);
                CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD);
            }

            return(bLines(ppdev, pptfxFirst, pptfxBuf, prun, cptfx, pls,
                          prclClip, apfn, flStart, ulHwMix));
        }
        else
        {
            x0 >>= 4;
            x1 >>= 4;
            y0 >>= 4;
            y1 >>= 4;

            if (y0 < y1)
            {
                yTop    = y0;
                yBottom = y1;
            }
            else
            {
                yBottom = y0;
                yTop    = y1;
            }

            if (x0 < x1)
            {
                xLeft   = x0;
                xRight  = x1;
            }
            else
            {
                xRight  = x0;
                xLeft   = x1;
            }

            // Do a trivial rejection test, remembering that the bound box
            // we just computed is lower-right inclusive:

            if ((xLeft   >= prclClip->right) ||
                (yTop    >= prclClip->bottom) ||
                (xRight  <  prclClip->left) ||
                (yBottom <  prclClip->top))
            {
                goto Next_Line;
            }
            else
            {
                dx = xRight - xLeft;
                dy = yBottom - yTop;

                if (dx >= dy)
                {
                    if (dx == 0)
                        goto Next_Line;     // Get rid of zero-length line case

                    // We have an x-major line.  Adjust the clip box to
                    // account for last-pel exclusion:

                    if (x0 < x1)
                        xRight--;
                    else
                        xLeft++;

                    lOr    = (dx | dy);
                    lBit   = 1;
                    iShift = 1;
                    while (!(lOr & lBit))
                    {
                        lBit <<= 1;
                        iShift++;
                    }

                    // The Weitek's clip registers are inclusive, and
                    // are expected to be well-ordered:

                    xLeft   = max(xLeft,   prclClip->left);
                    yTop    = max(yTop,    prclClip->top);
                    xRight  = min(xRight,  prclClip->right - 1);
                    yBottom = min(yBottom, prclClip->bottom - 1);

                    if ((xLeft <= xRight) && (yTop <= yBottom))
                    {
                        if (dx & lBit)
                        {

                        Output_Simple_Line:

                            CP_METALINE(ppdev, pjBase, x0, y0);
                            CP_METALINE(ppdev, pjBase, x1, y1);

                            CP_WAIT(ppdev, pjBase);
                            CP_RASTER(ppdev, pjBase, ulLineMix);
                            CP_WMIN(ppdev, pjBase, xLeft, yTop);
                            CP_WMAX(ppdev, pjBase, xRight, yBottom);
                            CP_START_QUAD_WAIT(ppdev, pjBase);
                            bClippingSet = TRUE;
                            goto Next_Line;
                        }
                        else
                        {
                            if ((dx ^ dy) > 0)
                                goto Punt_Line;

                            // Ick, this x-major line has tie-breaker cases.

                            xDir = 0;
                            yDir = 1;
                            dy >>= iShift;
                            if (y0 > y1)
                            {
                                dy = -dy;
                                yDir = -1;
                            }

                            y0 -= dy;
                            y1 += dy;

                            dx >>= iShift;
                            if (x0 > x1)
                                dx = -dx;

                            x0 -= dx;
                            x1 += dx;

                        Output_Trapezoid_Line:

                            CP_METAQUAD(ppdev, pjBase, x0, y0);
                            CP_METAQUAD(ppdev, pjBase, x1 + xDir, y1 - yDir);
                            CP_METAQUAD(ppdev, pjBase, x1, y1);
                            CP_METAQUAD(ppdev, pjBase, x0 - xDir, y0 + yDir);

                            CP_WAIT(ppdev, pjBase);
                            CP_RASTER(ppdev, pjBase, ulTrapezoidMix);
                            CP_WMIN(ppdev, pjBase, xLeft, yTop);
                            CP_WMAX(ppdev, pjBase, xRight, yBottom);
                            CP_START_QUAD_WAIT(ppdev, pjBase);
                            bClippingSet = TRUE;
                            goto Next_Line;
                        }
                    }
                }
                else
                {
                    // We have a y-major line.  Adjust the clip box to
                    // account for last-pel exclusion:

                    if (y0 < y1)
                        yBottom--;
                    else
                        yTop++;

                    lOr    = (dx | dy);
                    lBit   = 1;
                    iShift = 1;
                    while (!(lOr & lBit))
                    {
                        lBit <<= 1;
                        iShift++;
                    }

                    // The Weitek's clip registers are inclusive, and
                    // are expected to be well-ordered:

                    xLeft   = max(xLeft,   prclClip->left);
                    yTop    = max(yTop,    prclClip->top);
                    xRight  = min(xRight,  prclClip->right - 1);
                    yBottom = min(yBottom, prclClip->bottom - 1);

                    if ((xLeft <= xRight) && (yTop <= yBottom))
                    {
                        if (dy & lBit)
                        {
                            goto Output_Simple_Line;
                        }
                        else
                        {
                            // Ick, this y-major line has tie-breaker cases.

                            yDir = 0;
                            xDir = 1;
                            dx >>= iShift;
                            if (x0 > x1)
                            {
                                dx = -dx;
                                xDir = -1;
                            }

                            x0 -= dx;
                            x1 += dx;

                            dy >>= iShift;
                            if (y0 > y1)
                                dy = -dy;

                            y0 -= dy;
                            y1 += dy;

                            goto Output_Trapezoid_Line;
                        }
                    }
                }
            }
        }

    Punt_Line:

        if (bClippingSet)
        {
            CP_WAIT(ppdev, pjBase);
            CP_RASTER(ppdev, pjBase, ulLineMix);
            CP_ABS_WMIN(ppdev, pjBase, 0, 0);
            CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD);
            bClippingSet = FALSE;
        }

        bLines(ppdev, pptfxFirst, pptfxBuf, NULL, 1, pls,
               prclClip, apfn, flStart, ulHwMix);

    Next_Line:

        --cptfx;
        if (cptfx == 0)
            break;

        pptfxFirst = pptfxBuf;
        pptfxBuf++;
    }

    if (bClippingSet)
    {
        CP_WAIT(ppdev, pjBase);
        CP_RASTER(ppdev, pjBase, ulLineMix);    // Might need for next batch
        CP_ABS_WMIN(ppdev, pjBase, 0, 0);
        CP_ABS_WMAX(ppdev, pjBase, MAX_COORD, MAX_COORD);
    }

    return(TRUE);
}

/******************************Public*Routine******************************\
* BOOL bCacheCircle(ppdev, ppo, pco, pbo, bStroke, pla)
*
\**************************************************************************/

BOOL bCacheCircle(
PDEV*       ppdev,
PATHOBJ*    ppo,
CLIPOBJ*    pco,
BRUSHOBJ*   pbo,
BOOL        bStroke,        // TRUE if stroke, FALSE if fill
LINEATTRS*  pla)            // Used for strokes only
{
    RECTFX          rcfx;
    LONG            xCircle;
    LONG            yCircle;
    LONG            xCached;
    LONG            yCached;
    LONG            cx;
    LONG            cy;
    CIRCLEENTRY*    pce;
    LONG            i;
    BYTE*           pjBase;
    RECTL           rclDst;
    POINTL          ptlSrc;
    ULONG           ulHwMix;
    RECTL           rclTmp;
    CLIPENUM        ce;
    LONG            c;
    LONG            bMore;
    LONG            iCircleCache;
    SURFOBJ*        pso;
    CLIPOBJ         co;
    BRUSHOBJ        bo;

    if (!(ppdev->flStat & STAT_CIRCLE_CACHE))
        return(FALSE);

    PATHOBJ_vGetBounds(ppo, &rcfx);

    // Normalize bounds to upper-left corner:

    xCircle = rcfx.xLeft & ~0xfL;
    yCircle = rcfx.yTop  & ~0xfL;

    rcfx.xLeft   -= xCircle;
    rcfx.xRight  -= xCircle;
    rcfx.yTop    -= yCircle;
    rcfx.yBottom -= yCircle;

    // Convert to pixel units:

    xCircle >>= 4;
    yCircle >>= 4;

    cx = (rcfx.xRight >> 4) + 2;
    cy = (rcfx.yBottom >> 4) + 2;

    if ((cx > CIRCLE_DIMENSION) || (cy > CIRCLE_DIMENSION))
    {
        // This circle is too big to cache, so decline it:

        return(FALSE);
    }

    pjBase = ppdev->pjBase;

    pce = &ppdev->ace[0];
    for (i = TOTAL_CIRCLE_COUNT; i != 0; i--)
    {
        if ((pce->bStroke == bStroke)                &&
            (pce->rcfxCircle.xLeft   == rcfx.xLeft)  &&
            (pce->rcfxCircle.yTop    == rcfx.yTop)   &&
            (pce->rcfxCircle.xRight  == rcfx.xRight) &&
            (pce->rcfxCircle.yBottom == rcfx.yBottom))
        {
Draw_It:
            // We got a hit!  Colour-expand from our off-screen
            // cache to the screen:

            rclDst.left   = xCircle;
            rclDst.right  = xCircle + cx;
            rclDst.top    = yCircle;
            rclDst.bottom = yCircle + cy;

            // 'ptlSrc' has to be in relative coordinates:

            ptlSrc.x = pce->xCached - ppdev->xOffset;
            ptlSrc.y = pce->yCached - ppdev->yOffset;

            CP_WAIT(ppdev, pjBase);
            if (P9000(ppdev))
            {
                CP_FOREGROUND(ppdev, pjBase, pbo->iSolidColor);
                ulHwMix = 0xee22;
            }
            else
            {
                CP_COLOR0(ppdev, pjBase, pbo->iSolidColor);
                ulHwMix = 0xe2e2;
            }

            if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
            {
                ppdev->pfnCopyBlt(ppdev, 1, &rclDst, ulHwMix, &ptlSrc, &rclDst);
            }
            else if (pco->iDComplexity == DC_RECT)
            {
                if (bIntersect(&rclDst, &pco->rclBounds, &rclTmp))
                    ppdev->pfnCopyBlt(ppdev, 1, &rclTmp, ulHwMix, &ptlSrc, &rclDst);
            }
            else
            {
                CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

                do {
                    bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (ULONG*) &ce);

                    c = cIntersect(&rclDst, ce.arcl, ce.c);

                    if (c != 0)
                        ppdev->pfnCopyBlt(ppdev, c, ce.arcl, ulHwMix, &ptlSrc, &rclDst);

                } while (bMore);
            }

            return(TRUE);
        }
    }

    // Make an entry in our cache:

    iCircleCache = ppdev->iCircleCache;
    if (++iCircleCache >= TOTAL_CIRCLE_COUNT)
        iCircleCache = 0;
    ppdev->iCircleCache = iCircleCache;

    pce = &ppdev->ace[iCircleCache];

    // We must place the circle in off-screen memory with the same dword
    // alignment as the one we've been asked to draw, because we're
    // going to have GDI draw there instead:

    xCached = pce->x + (xCircle & 3);
    yCached = pce->y;

    // Store all the relevant information about the circle:

    pce->xCached = xCached;
    pce->yCached = yCached;
    pce->bStroke = bStroke;

    pce->rcfxCircle.xLeft   = rcfx.xLeft;
    pce->rcfxCircle.yTop    = rcfx.yTop;
    pce->rcfxCircle.xRight  = rcfx.xRight;
    pce->rcfxCircle.yBottom = rcfx.yBottom;

    // Fudge up some parameters for the GDI call:

    pso = ppdev->psoPunt;
    pso->pvScan0 = ppdev->pjScreen
                 + ((yCached - yCircle) * ppdev->lDelta)
                 + ((xCached - xCircle) * ppdev->cjPel);

    ASSERTDD((((ULONG_PTR) pso->pvScan0) & 0x3) == 0,
             "Surface must have dword alignment");

    co.iDComplexity = DC_TRIVIAL;
    bo.iSolidColor  = ppdev->ulWhite;

    // Erase old thing:

    CP_ABS_METARECT(ppdev, pjBase, xCached, yCached);
    CP_ABS_METARECT(ppdev, pjBase, xCached + CIRCLE_DIMENSION,
                                   yCached + CIRCLE_DIMENSION);

    CP_WAIT(ppdev, pjBase);
    CP_RASTER(ppdev, pjBase, 0);    // Same on both P9000 and P9100
    CP_START_QUAD(ppdev, pjBase);

    // Get GDI to draw the circle in our off-screen cache:

    if (bStroke)
    {
        EngStrokePath(pso, ppo, &co, NULL, &bo, NULL, pla, 0x0d0d);
    }
    else
    {
        EngFillPath(pso, ppo, &co, &bo, NULL, 0x0d0d, FP_ALTERNATEMODE);
    }

    goto Draw_It;
}

VOID (*gapfnStrip[])(PDEV*, STRIP*, LINESTATE*) = {
    vStripSolidHorizontal,
    vStripSolidVertical,
    vStripSolidDiagonalHorizontal,
    vStripSolidDiagonalVertical,

    vStripStyledHorizontal,
    vStripStyledVertical,
    vStripStyledVertical,         // Diagonal goes here
    vStripStyledVertical,         // Diagonal goes here
};

// Style array for alternate style (alternates one pixel on, one pixel off):

STYLEPOS gaspAlternateStyle[] = { 1 };

/******************************Public*Routine******************************\
* BOOL DrvStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix)
*
* Strokes the path.
*
\**************************************************************************/

BOOL DrvStrokePath(
    SURFOBJ*   pso,
    PATHOBJ*   ppo,
    CLIPOBJ*   pco,
    XFORMOBJ*  pxo,
    BRUSHOBJ*  pbo,
    POINTL*    pptlBrush,
    LINEATTRS* pla,
    MIX        mix)
{
    STYLEPOS  aspLtoR[STYLE_MAX_COUNT];
    STYLEPOS  aspRtoL[STYLE_MAX_COUNT];
    LINESTATE ls;
    PFNSTRIP* apfn;
    PFNLINES  pfnLines;
    FLONG     fl;
    PDEV*     ppdev;
    DSURF*    pdsurf;
    OH*       poh;
    RECTL     arclClip[4];                  // For rectangular clipping
    BYTE*     pjBase;
    RECTL*    prclClip;
    ULONG     ulHwMix;

    ASSERTDD(((mix >> 8) & 0xff) == (mix & 0xff),
             "GDI gave us an improper mix");

// Pass the surface off to GDI if it's a device bitmap that we've
// converted to a DIB:

    pdsurf = (DSURF*) pso->dhsurf;
    if (pdsurf->dt == DT_DIB)
    {
        return(EngStrokePath(pdsurf->pso, ppo, pco, pxo, pbo, pptlBrush,
                             pla, mix));
    }

// We'll be drawing to the screen or an off-screen DFB; copy the surface's
// offset now so that we won't need to refer to the DSURF again:

    poh   = pdsurf->poh;
    ppdev = (PDEV*) pso->dhpdev;
    ppdev->xOffset = poh->x;
    ppdev->yOffset = poh->y;

    // Because we set GCAPS_BEZIERS, we have to watch out for Beziers:

    if (ppo->fl & PO_BEZIERS)
    {
        // We only try to cache solid-styled COPYPEN ellipses:

        if ((ppo->fl & PO_ELLIPSE)    &&
            (mix == 0x0d0d)           &&
            !(pla->fl & LA_ALTERNATE) &&
            (pla->pstyle == NULL))
        {
            if (bCacheCircle(ppdev, ppo, pco, pbo, TRUE, pla))
                return(TRUE);
        }

        // Get GDI to break the Beziers into lines before calling us
        // again:

        return(FALSE);
    }

    pfnLines = bLines;
    if ((pla->pstyle == NULL) && !(pla->fl & LA_ALTERNATE))
    {
    // We can accelerate solid lines:

        if (pco->iDComplexity == DC_TRIVIAL)
        {
            pfnLines = bIntegerUnclippedLines;
        }
        else if (pco->iDComplexity == DC_RECT)
        {
            RECTFX rcfxBounds;

        // We have to be sure that we don't overflow the hardware registers
        // for current position, line length, or DDA terms.  We check
        // here to make sure that the current position and line length
        // values won't overflow:

            PATHOBJ_vGetBounds(ppo, &rcfxBounds);

            if (rcfxBounds.xLeft   + (ppdev->xOffset * F)
                                                >= (MIN_INTEGER_BOUND * F) &&
                rcfxBounds.xRight  + (ppdev->xOffset * F)
                                                <= (MAX_INTEGER_BOUND * F) &&
                rcfxBounds.yTop    + (ppdev->yOffset * F)
                                                >= (MIN_INTEGER_BOUND * F) &&
                rcfxBounds.yBottom + (ppdev->yOffset * F)
                                                <= (MAX_INTEGER_BOUND * F))
            {
                pfnLines = bIntegerClippedLines;
            }
        }
    }

    pjBase   = ppdev->pjBase;
    prclClip = NULL;
    fl       = 0;

// Look after styling initialization:

    if (pla->fl & LA_ALTERNATE)
    {
        ls.cStyle      = 1;
        ls.spTotal     = 1;
        ls.spTotal2    = 2;
        ls.spRemaining = 1;
        ls.aspRtoL     = &gaspAlternateStyle[0];
        ls.aspLtoR     = &gaspAlternateStyle[0];
        ls.spNext      = HIWORD(pla->elStyleState.l);
        ls.xyDensity   = 1;
        fl            |= FL_STYLED;
        ls.ulStartMask = 0L;
    }
    else if (pla->pstyle != (FLOAT_LONG*) NULL)
    {
        PFLOAT_LONG pstyle;
        STYLEPOS*   pspDown;
        STYLEPOS*   pspUp;

        pstyle = &pla->pstyle[pla->cstyle];

        ls.xyDensity = STYLE_DENSITY;
        ls.spTotal   = 0;
        while (pstyle-- > pla->pstyle)
        {
            ls.spTotal += pstyle->l;
        }
        ls.spTotal *= STYLE_DENSITY;
        ls.spTotal2 = 2 * ls.spTotal;

    // Compute starting style position (this is guaranteed not to overflow):

        ls.spNext = HIWORD(pla->elStyleState.l) * STYLE_DENSITY +
                    LOWORD(pla->elStyleState.l);

        fl        |= FL_STYLED;
        ls.cStyle  = pla->cstyle;
        ls.aspRtoL = aspRtoL;
        ls.aspLtoR = aspLtoR;

        if (pla->fl & LA_STARTGAP)
            ls.ulStartMask = 0xffffffffL;
        else
            ls.ulStartMask = 0L;

        pstyle  = pla->pstyle;
        pspDown = &ls.aspRtoL[ls.cStyle - 1];
        pspUp   = &ls.aspLtoR[0];

        while (pspDown >= &ls.aspRtoL[0])
        {
            *pspDown = pstyle->l * STYLE_DENSITY;
            *pspUp   = *pspDown;

            pspUp++;
            pspDown--;
            pstyle++;
        }
    }

    if (pco->iDComplexity == DC_RECT)
    {
        fl |= FL_SIMPLE_CLIP;

        arclClip[0]        =  pco->rclBounds;

    // FL_FLIP_D:

        arclClip[1].top    =  pco->rclBounds.left;
        arclClip[1].left   =  pco->rclBounds.top;
        arclClip[1].bottom =  pco->rclBounds.right;
        arclClip[1].right  =  pco->rclBounds.bottom;

    // FL_FLIP_V:

        arclClip[2].top    = -pco->rclBounds.bottom + 1;
        arclClip[2].left   =  pco->rclBounds.left;
        arclClip[2].bottom = -pco->rclBounds.top + 1;
        arclClip[2].right  =  pco->rclBounds.right;

    // FL_FLIP_V | FL_FLIP_D:

        arclClip[3].top    =  pco->rclBounds.left;
        arclClip[3].left   = -pco->rclBounds.bottom + 1;
        arclClip[3].bottom =  pco->rclBounds.right;
        arclClip[3].right  = -pco->rclBounds.top + 1;

        prclClip = arclClip;
    }

    apfn = &gapfnStrip[4 * ((fl & FL_STYLE_MASK) >> FL_STYLE_SHIFT)];

// Get the device ready:

    ulHwMix = gaRop3FromMix[mix & 0xF];
    ulHwMix = (ulHwMix << 8) | (ulHwMix);

    CP_WAIT(ppdev, pjBase);
    if (P9000(ppdev))
    {
        CP_RASTER(ppdev, pjBase, P9000_OVERSIZED | ulHwMix);
        CP_BACKGROUND(ppdev, pjBase, pbo->iSolidColor);
    }
    else
    {
        CP_RASTER(ppdev, pjBase, P9100_OVERSIZED | (ulHwMix & 0xff));
        CP_COLOR0(ppdev, pjBase, pbo->iSolidColor);
    }

// Set up to enumerate the path:

    if (pco->iDComplexity != DC_COMPLEX)
    {
        PATHDATA  pd;
        BOOL      bMore;
        ULONG     cptfx;
        POINTFIX  ptfxStartFigure;
        POINTFIX  ptfxLast;
        POINTFIX* pptfxFirst;
        POINTFIX* pptfxBuf;

        pd.flags = 0;

        do {
            bMore = PATHOBJ_bEnum(ppo, &pd);

            cptfx = pd.count;
            if (cptfx == 0)
                break;

            if (pd.flags & PD_BEGINSUBPATH)
            {
                ptfxStartFigure  = *pd.pptfx;
                pptfxFirst       = pd.pptfx;
                pptfxBuf         = pd.pptfx + 1;
                cptfx--;
            }
            else
            {
                pptfxFirst       = &ptfxLast;
                pptfxBuf         = pd.pptfx;
            }

            if (pd.flags & PD_RESETSTYLE)
                ls.spNext = 0;

            if (cptfx > 0)
            {
                if (!pfnLines(ppdev,
                              pptfxFirst,
                              pptfxBuf,
                              (RUN*) NULL,
                              cptfx,
                              &ls,
                              prclClip,
                              apfn,
                              fl,
                              ulHwMix))
                {
                    return(FALSE);
                }
            }

            ptfxLast = pd.pptfx[pd.count - 1];

            if (pd.flags & PD_CLOSEFIGURE)
            {
                if (!pfnLines(ppdev,
                              &ptfxLast,
                              &ptfxStartFigure,
                              (RUN*) NULL,
                              1,
                              &ls,
                              prclClip,
                              apfn,
                              fl,
                              ulHwMix))
                {
                    return(FALSE);
                }
            }
        } while (bMore);

        if (fl & FL_STYLED)
        {
        // Save the style state:

            ULONG ulHigh;
            ULONG ulLow;

        // Masked styles don't normalize the style state.  It's a good
        // thing to do, so let's do it now:

            if ((ULONG) ls.spNext >= (ULONG) ls.spTotal2)
                ls.spNext = (ULONG) ls.spNext % (ULONG) ls.spTotal2;

            ulHigh = ls.spNext / ls.xyDensity;
            ulLow  = ls.spNext % ls.xyDensity;

            pla->elStyleState.l = MAKELONG(ulLow, ulHigh);
        }
    }
    else
    {
    // Local state for path enumeration:

        BOOL bMore;
        union {
            BYTE     aj[offsetof(CLIPLINE, arun) + RUN_MAX * sizeof(RUN)];
            CLIPLINE cl;
        } cl;

        fl |= FL_COMPLEX_CLIP;

    // We use the clip object when non-simple clipping is involved:

        PATHOBJ_vEnumStartClipLines(ppo, pco, pso, pla);

        do {
            bMore = PATHOBJ_bEnumClipLines(ppo, sizeof(cl), &cl.cl);
            if (cl.cl.c != 0)
            {
                if (fl & FL_STYLED)
                {
                    ls.spComplex = HIWORD(cl.cl.lStyleState) * ls.xyDensity
                                 + LOWORD(cl.cl.lStyleState);
                }
                if (!pfnLines(ppdev,
                              &cl.cl.ptfxA,
                              &cl.cl.ptfxB,
                              &cl.cl.arun[0],
                              cl.cl.c,
                              &ls,
                              (RECTL*) NULL,
                              apfn,
                              fl,
                              ulHwMix))
                {
                    return(FALSE);
                }
            }
        } while (bMore);
    }

    return(TRUE);
}