/******************************Module*Header***********************************\
 *
 *                           *******************
 *                           * GDI SAMPLE CODE *
 *                           *******************
 *
 * Module Name: Stroke.c
 *
 * Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
 * Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
 ******************************************************************************/
#include "precomp.h"
#include "gdi.h"
#include "log.h"

PFNSTRIP gapfnStrip[] =
{
    vSolidHorizontalLine,
    vSolidVerticalLine,
    vSolidDiagonalHorizontalLine,
    vSolidDiagonalVerticalLine,

    // Should be NUM_STRIP_DRAW_DIRECTIONS = 4 strip drawers in every group

    vSolidHorizontalLine,
    vSolidVerticalLine,
    vSolidDiagonalHorizontalLine,
    vSolidDiagonalVerticalLine,

    // Should be NUM_STRIP_DRAW_STYLES = 8 strip drawers in total for doing
    // solid lines, and the same number for non-solid lines:

    vStyledHorizontalLine,
    vStyledVerticalLine,
    vStyledVerticalLine,  // Diagonal goes here
    vStyledVerticalLine,  // Diagonal goes here

    vStyledHorizontalLine,
    vStyledVerticalLine,
    vStyledVerticalLine,  // Diagonal goes here
    vStyledVerticalLine,  // Diagonal goes here
};

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

STYLEPOS gaspAlternateStyle[] = { 1 };

//-----------------------------------------------------------------------------
// BOOL DrvStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix)
//
// DrvStrokePath strokes a path when called by GDI. If the driver has hooked the
// function, and if the appropriate GCAPs are set, GDI calls DrvStrokePath when
// GDI draws a line or curve with any set of attributes.
//
// Parameters
// pso----------Identifies the surface on which to draw. 
// ppo----------Points to a PATHOBJ structure. GDI PATHOBJ_Xxx service routines
//              are provided to enumerate the lines, Bezier curves, and other
//              data that make up the path. This indicates what is to be drawn. 
// pco----------Points to a CLIPOBJ structure. GDI CLIPOBJ_Xxx service routines
//              are provided to enumerate the clip region as a set of
//              rectangles. Optionally, all the lines in the path may be
//              enumerated preclipped by CLIPOBJ. This means that drivers can
//              have all their line clipping calculations done for them. 
// pxo----------Points to a XFORMOBJ. This is only needed when a geometric wide
//              line is to be drawn. It specifies the transform that maps world
//              coordinates to device coordinates. This is needed because the
//              path is provided in device coordinates but a geometric wide line
//              is actually widened in world coordinates. 
//              The XFORMOBJ can be queried to find the transform. 
// pbo----------Specifies the brush to be used when drawing the path. 
// pptlBrushOrg-Points to the brush origin used to align the brush pattern on
//              the device. 
// pLineAttrs---Points to a LINEATTRS structure. Note that the elStyleState
//              member must be updated as part of this function if the line is
//              styled. Also note that the ptlLastPel member must be updated if
//              a single pixel width cosmetic line is being drawn. 
// mix----------Specifies how to combine the brush with the destination. 
//
// Return Value
//  The return value is TRUE if the driver is able to stroke the path. If GDI
//  should stroke the path, the return value is FALSE, and an error code is not
//  logged. If the driver encounters an error, the return value is DDI_ERROR,
//  and an error code is reported.
//
// Comments
//  If a driver supports this entry point, it should also support the drawing of
//  cosmetic wide lines with arbitrary clipping. Using the provided GDI
//  functions, the call can be broken down into a set of single-pixel-width
//  lines with precomputed clipping.
//
//  This function is required if any drawing is to be done on a device-managed
//  surface.
//
//  Drivers for advanced devices can optionally receive this call to draw paths
//  containing Bezier curves and geometric wide lines. GDI will test the
//  GCAPS_BEZIERS and GCAPS_GEOMETRICWIDE flags of the flGraphicsCaps member of
//  the DEVINFO structure to decide whether it should call. (The four
//  combinations of the bits determine the four levels of functionality for
//  this call.) If the driver gets an advanced call containing Bezier curves or
//  geometric wide lines, it can decide not to handle the call, returning FALSE.
//  This might happen if the path or clipping is too complex for the device to
//  process. If the call does return FALSE, GDI breaks the call down into
//  simpler calls that can be handled easily.
//
//  For device-managed surfaces, the function must minimally support
//  single-pixel-wide solid and styled cosmetic lines using a solid-colored
//  brush. The device can return FALSE if the line is geometric and the engine
//  will convert those calls to DrvFillPath or DrvPaint calls.
//
//  The mix mode defines how the incoming pattern should be mixed with the data
//  already on the device surface. The MIX data type consists of two ROP2 values
//  packed into a single ULONG. The low-order byte defines the foreground raster
//  operation; the next byte defines the background raster operation. For more
//  information about raster operation codes, see the Platform SDK.
//
//-----------------------------------------------------------------------------
BOOL
DrvStrokePath(SURFOBJ*   pso,
              PATHOBJ*   ppo,
              CLIPOBJ*   pco,
              XFORMOBJ*  pxo,
              BRUSHOBJ*  pbo,
              POINTL*    pptlBrush,
              LINEATTRS* pLineAttrs,
              MIX        mix)
{
    STYLEPOS  aspLtoR[STYLE_MAX_COUNT];
    STYLEPOS  aspRtoL[STYLE_MAX_COUNT];
    LINESTATE lineState;
    PFNSTRIP* apfn;
    FLONG     fl;
    PDev*     ppdev;
    Surf*     psurfDst;
    RECTL     arclClip[4];                  // For rectangular clipping
    BOOL      bResetHW;
    BOOL      bRet;
    DWORD     logicOp;

    DBG_GDI((6, "DrvStrokePath called with mix =%x", mix));

    psurfDst = (Surf*)pso->dhsurf;

    //
    // Punt to engine if surface to draw is not in video memory
    //
    if ( psurfDst->flags & SF_SM )
    {
        goto puntIt;
    }

    ppdev = (PDev*)pso->dhpdev;

    
    vCheckGdiContext(ppdev);

    ppdev->psurf = psurfDst;

    fl = 0;

    //
    // Look after styling initialization:
    //
    if ( pLineAttrs->fl & LA_ALTERNATE )
    {
        //
        // LA_ALTERNATE: A special cosmetic line style; every other pixel is on
        //
        lineState.cStyle      = 1;
        lineState.spTotal     = 1;
        lineState.spTotal2    = 2;
        lineState.spRemaining = 1;
        lineState.aspRtoL     = &gaspAlternateStyle[0];
        lineState.aspLtoR     = &gaspAlternateStyle[0];
        lineState.spNext      = HIWORD(pLineAttrs->elStyleState.l);
        lineState.xyDensity   = 1;
        lineState.ulStartMask = 0L;
        fl                   |= FL_ARBITRARYSTYLED;
    }// Cosmetic line
    else if ( pLineAttrs->pstyle != (FLOAT_LONG*)NULL )
    {
        //
        // "pLineAttrs->pstyle" points to the style array. If this member is
        // null, the line style is solid
        //
        PFLOAT_LONG pStyle;
        STYLEPOS*   pspDown;
        STYLEPOS*   pspUp;

        //
        // "pLineAttrs->cstyle" specifies the number of entries in the style
        // array. So here we get the address of the last array first
        //
        pStyle = &pLineAttrs->pstyle[pLineAttrs->cstyle];

        lineState.xyDensity = STYLE_DENSITY;
        lineState.spTotal   = 0;

        //
        // Loop through all the data array backworfds to get the sum of style
        // array
        //
        while ( pStyle-- > pLineAttrs->pstyle )
        {
            lineState.spTotal += pStyle->l;
        }

        lineState.spTotal *= STYLE_DENSITY;
        lineState.spTotal2 = 2 * lineState.spTotal;

        //
        // Compute starting style position (this is guaranteed not to overflow)
        // Note: "pLineAttrs->elStyleState" is a pair of 16-bit values supplied
        // by GDI whenever the driver calls PATHOBJ_bEnumClipLines. These two
        // values, packed into a LONG, specify where in the styling array
        // (at which pixel) to start the first subpath. This value must be
        // updated as part of the output routine if the line is not solid.
        // This member applies to cosmetic lines only.
        //
        lineState.spNext = HIWORD(pLineAttrs->elStyleState.l) * STYLE_DENSITY
                         + LOWORD(pLineAttrs->elStyleState.l);

        fl |= FL_ARBITRARYSTYLED;
        lineState.cStyle  = pLineAttrs->cstyle;
        lineState.aspRtoL = aspRtoL;
        lineState.aspLtoR = aspLtoR;

        if ( pLineAttrs->fl & LA_STARTGAP )
        {
            //
            // The first entry in the style array specifies the length of the
            // first gap. set "ulStartMask" to mark it as a GAP
            //
            lineState.ulStartMask = 0xffffffffL;
        }
        else
        {
            //
            // It must be LA_GEOMETRIC which specifies a geometric wide line. Mark
            // it as not a GAP
            //
            lineState.ulStartMask = 0L;
        }

        //
        // Let "pStyle" points to the 1st style array, "pspDown" ponits to the
        // "cStyle"th array in aspRtoL and "pspUp" points to the 1st array in
        // aspLtoR
        //
        pStyle  = pLineAttrs->pstyle;
        pspDown = &lineState.aspRtoL[lineState.cStyle - 1];
        pspUp   = &lineState.aspLtoR[0];

        //
        // Move backwards to assign all the style data
        //
        while ( pspDown >= &lineState.aspRtoL[0] )
        {
            //
            // Let the last style data in "pspDown" = the 1st in "pspUp", 2 to
            // last in "pspDown" = 2nd in "pspUp".....
            // pspDown [n][n-1]...[2][1]
            // pspUp [1][2]...[n-1][n]
            //
            *pspDown = pStyle->l * STYLE_DENSITY;
            *pspUp   = *pspDown;

            pspUp++;
            pspDown--;
            
            pStyle++;
        }
    }// Non-solid line

    bRet = TRUE;
    apfn = &gapfnStrip[NUM_STRIP_DRAW_STYLES *
                       ((fl & FL_STYLE_MASK) >> FL_STYLE_SHIFT)];

    //
    // Set up to enumerate the path:
    //
    if ( pco->iDComplexity != DC_COMPLEX )
    {
        PATHDATA  pd;
        RECTL*    prclClip = (RECTL*)NULL;
        BOOL      bMore;
        ULONG     lPtFix;
        POINTFIX  ptfxStartFigure;
        POINTFIX  ptfxLast;
        POINTFIX* pptfxFirst;
        POINTFIX* pptfxBuf;

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

            //
            // This is the only clip region of importance to Permedia2
            //
            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;
        }// if ( pco->iDComplexity == DC_RECT )

        pd.flags = 0;

        //
        // Get the logic op and set up the flag to indicate reads from the frame
        // buffer will occur.
        //
        logicOp = ulRop3ToLogicop(gaMix[mix & 0xff]);
        DBG_GDI((7, "logicop is %d", logicOp));

        if ( LogicopReadDest[logicOp] )
        {
            fl |= FL_READ;
        }

        //
        // Need to set up Permedia2 modes and colors appropriately for the lines
        //
        bResetHW = bInitializeStrips(ppdev, pbo->iSolidColor,
                                     logicOp, prclClip);

        PATHOBJ_vEnumStart(ppo);

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

            lPtFix = pd.count;
            if ( lPtFix == 0 )
            {
                //
                // If the pathdata contains no data, finish
                //
                break;
            }

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

            if ( pd.flags & PD_RESETSTYLE )
            {
                lineState.spNext = 0;
            }

            if ( lPtFix > 0 )
            {
                if ( !bLines(ppdev,
                             pptfxFirst,
                             pptfxBuf,
                             (RUN*)NULL,
                             lPtFix,
                             &lineState,
                             prclClip,
                             apfn,
                             fl) )
                {
                    bRet = FALSE;
                    goto ResetReturn;
                }
            }

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

            if ( pd.flags & PD_CLOSEFIGURE )
            {
                if ( !bLines(ppdev,
                             &ptfxLast,
                             &ptfxStartFigure,
                             (RUN*)NULL,
                             1,
                             &lineState,
                             prclClip,
                             apfn,
                             fl) )
                {
                    bRet = FALSE;
                    goto ResetReturn;
                }
            }
        } 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)lineState.spNext >= (ULONG)lineState.spTotal2 )
            {
                lineState.spNext = (ULONG)lineState.spNext
                                 % (ULONG)lineState.spTotal2;
            }

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

            pLineAttrs->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;

        //
        // Need to set up hardware modes and colors appropriately for the lines.
        // NOTE, with a complex clip,we can not yet use permedia2 for fast lines
        //
        bResetHW = bInitializeStrips(ppdev, pbo->iSolidColor,
                                     ulRop3ToLogicop(gaMix[mix&0xff]),
                                     NULL);

        //
        // We use the clip object when non-simple clipping is involved:
        //
        PATHOBJ_vEnumStartClipLines(ppo, pco, pso, pLineAttrs);

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

ResetReturn:

    if ( bResetHW )
    {
        vResetStrips(ppdev);
    }

    DBG_GDI((6, "DrvStrokePath done it"));

    InputBufferFlush(ppdev);


    return (bRet);

puntIt:

    bRet = EngStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush,
                         pLineAttrs, mix);


    DBG_GDI((6, "DrvStrokePath punt it"));
    return bRet;
}// DrvStrokePath()



