/*++

Copyright (c) 1990-1999  Microsoft Corporation


Module Name:

    polygon.c


Abstract:

    This module contains path forming code utilized by the rest of the
    driver. Path primitive functions (such as DrvStrokePath, DrvTextOut)
    use this code to generate and fill and stroke paths.
    Since this code is aware of all the combinations of complex paths and
    complex clipping regions and how to deal with them.

Author:

    15:30 on Wed 09 Mar 1993    -by-    KL
        Created it

    15-Nov-1993 Mon 19:42:05 updated  -by-  DC,V-JB
        clean up / fixed / add debugging information

    27-Jan-1994 Thu 23:40:57 updated  -by-  DC
        Add user defined pattern caching

    16-Mar-1994 Wed 11:21:02 updated  -by-  DC
        Add SetBrushOrigin() so we can align brush origins for filling
        correctly


[Environment:]

    GDI Device Driver - Plotter.


[Notes:]


Revision History:


--*/

#include "precomp.h"
#pragma hdrstop

//
// General debug flags for module, see dbgread.txt for overview.
//

#define DBG_PLOTFILENAME    DbgPolygon

#define DBG_GENPOLYGON      0x00000001
#define DBG_GENPOLYPATH     0x00000002
#define DBG_BEZIER          0x00000004
#define DBG_DORECT          0x00000008
#define DBG_FILL_CLIP       0x00000010
#define DBG_CHECK_FOR_WHITE 0x00000020
#define DBG_USERPAT         0x00000040
#define DBG_FILL_LOGIC      0x00000080
#define DBG_HANDLELINEATTR  0x00000100

DEFINE_DBGVAR(0);

//
// Derive new rect by offsetting the source rect
//

#define POLY_GEN_RECTFIX(dest, src, offset) { dest.x = src->x + offset.x;   \
                                              dest.y = src->y + offset.y; }

//
// Build table with HPGL2 commands for cursor movement, and path construction.
//

static BYTE __ER[]    = { 'E', 'R'      };
static BYTE __RR[]    = { 'R', 'R'      };
static BYTE __EP[]    = { 'E', 'P'      };
static BYTE __FP[]    = { 'F', 'P'      };
static BYTE __PM0[]   = { 'P', 'M', '0' };
static BYTE __PM1[]   = { 'P', 'M', '1' };
static BYTE __PM2[]   = { 'P', 'M', '2' };
static BYTE __TR0[]   = { 'T', 'R', '0' };
static BYTE __TR1[]   = { 'T', 'R', '1' };
static BYTE __SEMI[]  = { ';'           };
static BYTE __1SEMI[] = { '1', ';'      };
static BYTE __BR[]    = { 'B', 'R'      };
static BYTE __BZ[]    = { 'B', 'Z'      };
static BYTE __PE[]    = { 'P', 'E'      };
static BYTE __PD[]    = { 'P', 'D'      };
static BYTE __COMMA[] = { ','           };


//
// Make MACROS for sending out command streams to device
//

#define SEND_ER(pPDev)      OutputBytes(pPDev, __ER    , sizeof(__ER   ) );
#define SEND_RR(pPDev)      OutputBytes(pPDev, __RR    , sizeof(__RR   ) );
#define SEND_EP(pPDev)      OutputBytes(pPDev, __EP    , sizeof(__EP   ) );
#define SEND_FP(pPDev)      OutputBytes(pPDev, __FP    , sizeof(__FP   ) );
#define SEND_PM0(pPDev)     OutputBytes(pPDev, __PM0   , sizeof(__PM0  ) );
#define SEND_PM1(pPDev)     OutputBytes(pPDev, __PM1   , sizeof(__PM1  ) );
#define SEND_PM2(pPDev)     OutputBytes(pPDev, __PM2   , sizeof(__PM2  ) );
#define SEND_TR0(pPDev)     OutputBytes(pPDev, __TR0   , sizeof(__TR0  ) );
#define SEND_TR1(pPDev)     OutputBytes(pPDev, __TR1   , sizeof(__TR1  ) );
#define SEND_SEMI(pPDev)    OutputBytes(pPDev, __SEMI  , sizeof(__SEMI ) );
#define SEND_1SEMI(pPDev)   OutputBytes(pPDev, __1SEMI , sizeof(__1SEMI) );
#define SEND_BR(pPDev)      OutputBytes(pPDev, __BR    , sizeof(__BR   ) );
#define SEND_BZ(pPDev)      OutputBytes(pPDev, __BZ    , sizeof(__BZ   ) );
#define SEND_PE(pPDev)      OutputBytes(pPDev, __PE    , sizeof(__PE   ) );
#define SEND_PD(pPDev)      OutputBytes(pPDev, __PD    , sizeof(__PD   ) );
#define SEND_COMMA(pPDev)   OutputBytes(pPDev, __COMMA , sizeof(__COMMA) );


#define TERM_PE_MODE(pPDev, Mode)                                           \
{                                                                           \
    if (Mode == 'PE') {                                                     \
                                                                            \
        SEND_SEMI(pPDev);                                                   \
        Mode = 0;                                                           \
    }                                                                       \
}

#define SWITCH_TO_PE(pPDev, Mode, PenIsDown)                                \
{                                                                           \
    if (Mode != 'PE') {                                                     \
                                                                            \
        SEND_PE(pPDev);                                                     \
        Mode      = 'PE';                                                   \
        PenIsDown = TRUE;                                                   \
    }                                                                       \
}


#define SWITCH_TO_BR(pPDev, Mode, PenIsDown)                                \
{                                                                           \
    TERM_PE_MODE(pPDev, Mode)                                               \
                                                                            \
    if (Mode != 'BR') {                                                     \
                                                                            \
        if (!PenIsDown) {                                                   \
                                                                            \
            SEND_PD(pPDev);                                                 \
            PenIsDown = TRUE;                                               \
        }                                                                   \
                                                                            \
        SEND_BR(pPDev);                                                     \
        Mode = 'BR';                                                        \
                                                                            \
    } else {                                                                \
                                                                            \
        SEND_COMMA(pPDev);                                                  \
    }                                                                       \
}


#define PLOT_IS_WHITE(pdev, ulCol)  (ulCol == WHITE_INDEX)
#define TOGGLE_DASH(x)              ((x) ? FALSE : TRUE)

#define ROP4_USE_DEST(Rop4)         ((Rop4 & 0x5555) != ((Rop4 & 0xAAAA) >> 1))
#define SET_PP_WITH_ROP4(pPDev, Rop4)                                       \
    SetPixelPlacement(pPDev, (ROP4_USE_DEST(Rop4)) ? SPP_MODE_EDGE :        \
                                                     SPP_MODE_CENTER)



VOID
SetBrushOrigin(
    PPDEV   pPDev,
    PPOINTL pptlBrushOrg
    )

/*++

Routine Description:

    This function sets the brush origin onto the device for the next brush
    fill. Brush origins are used in order for paths being filled to line up
    correctly. In this way, if many different paths are filled, the patterns
    will line up based on the pattern being repeated starting at the correct
    origin.


Arguments:

    pPDev           - Pointer to our PDEV

    pptlBrushOrg    - Pointer to the brush origin to be set


Return Value:

    VOID

Author:

    16-Mar-1994 Wed 10:56:46 created  -by-  DC


Revision History:


--*/

{
    POINTL  ptlAC;


    if (pptlBrushOrg) {

        ptlAC = *pptlBrushOrg;

    } else {

        ptlAC.x =
        ptlAC.y = 0;
    }


    //
    // Check to see if the origin is different, and if it is output the
    // new origin to the device.
    //

    if ((ptlAC.x != pPDev->ptlAnchorCorner.x) ||
        (ptlAC.y != pPDev->ptlAnchorCorner.y)) {

        OutputString(pPDev, "AC");

        if ((ptlAC.x) || (ptlAC.y)) {

            OutputLONGParams(pPDev, (PLONG)&ptlAC, 2, 'd');
        }

        //
        // Save the current setting
        //

        pPDev->ptlAnchorCorner = ptlAC;
    }
}




BOOL
DoRect(
    PPDEV       pPDev,
    RECTL       *pRectl,
    BRUSHOBJ    *pBrushFill,
    BRUSHOBJ    *pBrushStroke,
    POINTL      *pptlBrush,
    ROP4        rop4,
    LINEATTRS   *plineattrs,
    ULONG       ulFlags
    )

/*++

Routine Description:

    This function will draw and optionally fill a rectangle. It uses seperate
    BRUSHOBJ's for the outline and interior of the rectangle. Since the stroke
    operation may include data for a styled line (dashes etc.) the LINEATTRS
    structure is included as well.

Arguments:

    pPDev           - Pointer to our PDEV

    pRectl          - rectangle area to fill

    pBrushFill      - Brush used to fill the rectangle

    pBrushStroke    - Brush used to stroke the rectangle

    pptlBrush       - brush origin

    rop4            - rop to be used

    plineattrs      - Pointer to the line attributes for a styled line

    ulFlags         - FPOLY_xxxx flags


Return Value:

    TRUE if sucessful and false if not


Author:

    15-Feb-1994 Tue 11:59:52 updated  -by-  DC
        We will do RR or RA now

    24-Mar-1994 Thu 19:37:05 updated  -by-  DC
        Do local MovePen and make sure we at least output ONE RASTER PEL

Revision History:


--*/

{
    POINTL      ptlPlot;
    SIZEL       szlRect;

    //
    // Check to see if we can short cut some of our work if its a pen plotter
    //

    if (PlotCheckForWhiteIfPenPlotter(pPDev,
                                      pBrushFill,
                                      pBrushStroke,
                                      rop4,
                                      &ulFlags))  {
        return(TRUE);
    }

    PLOTDBG(DBG_DORECT,
            ("DoRect: Passed In RECTL=(%ld, %ld)-(%ld, %ld)=%ld x %ld",
                pRectl->left,   pRectl->top,
                pRectl->right,  pRectl->bottom,
                pRectl->right -  pRectl->left,
                pRectl->bottom -  pRectl->top));

    ptlPlot.x  = LTODEVL(pPDev, pRectl->left);
    ptlPlot.y  = LTODEVL(pPDev, pRectl->top);
    szlRect.cx = LTODEVL(pPDev, pRectl->right)  - ptlPlot.x;
    szlRect.cy = LTODEVL(pPDev, pRectl->bottom) - ptlPlot.y;


    //
    // No need to fill an empty rectangle.
    //

    if ((szlRect.cx) && (szlRect.cy)) {

        SET_PP_WITH_ROP4(pPDev, rop4);

        //
        // If the rectangle is not of sufficient size to actually cause any
        // bits to appear on the target device, we grow the rectangle
        // to the correct amount. This is done because the target device
        // may after converting to physical units, decide there is no work to
        // do. In this case nothing would show up on the page at all. So we
        // opt to have at least a one pixel object show up.
        //

        if (szlRect.cx < (LONG)pPDev->MinLToDevL) {

            PLOTWARN(("DoRect: cxRect=%ld < MIN=%ld, Make it as MIN",
                            szlRect.cx, (LONG)pPDev->MinLToDevL));

            szlRect.cx = (LONG)pPDev->MinLToDevL;
        }

        if (szlRect.cy < (LONG)pPDev->MinLToDevL) {

            PLOTWARN(("DoRect: cyRect=%ld < MIN=%ld, Make it as MIN",
                            szlRect.cy, (LONG)pPDev->MinLToDevL));

            szlRect.cy = (LONG)pPDev->MinLToDevL;
        }

        //
        // Do the MOVE PEN.
        //

        OutputFormatStr(pPDev, "PE<=#D#D;", ptlPlot.x, ptlPlot.y);

        PLOTDBG(DBG_DORECT,
                ("DoRect: PLOTUNIT=%ld, MovePen=(%ld, %ld), RR=%ld x %ld",
                pPDev->pPlotGPC->PlotXDPI,
                ptlPlot.x, ptlPlot.y, szlRect.cx, szlRect.cy));

        //
        // Since all the parameters are set up correctly, call the core routine
        // for filling a rectangle.
        //

        DoFillLogic(pPDev,
                    pptlBrush,
                    pBrushFill,
                    pBrushStroke,
                    rop4,
                    plineattrs,
                    &szlRect,
                    ulFlags);

    } else {

        PLOTDBG(DBG_DORECT, ("DoRect: Pass a NULL Rectl, Do NOTHING"));
    }

    return(TRUE);
}



BOOL
DoFillByEnumingClipRects(
    PPDEV       pPDev,
    POINTL      *ppointlOffset,
    CLIPOBJ     *pco,
    POINTL      *pPointlBrushOrg,
    BRUSHOBJ    *pBrushFill,
    ROP4        Rop4,
    LINEATTRS   *plineattrs,
    ULONG       ulFlags
    )

/*++

Routine Description:

    This function, fills a CLIPOBJ by enurating the CLIPOBJ as seperate
    rectangles and filling each of them in turn. This is typically done when
    the CLIPOBJ is comprised of so many path objects, that the path cannot
    be described in HPGL2 (overfilling the path buffer in the target device).


Arguments:

    pPDev           - Pointer to our PDEV

    ppointlOffset   - Extra offset to the output polygon

    pClipObj        - clip object

    pPointlBrushOrg - brush origin for the fill brush.

    pBrushFill      - Brush used to fill the rectangle

    Rop4            - rop to be used

    plineattrs      - Pointer to the line attributes for a styled line

    ulFlags         - FPOLY_xxxx flags


Return Value:

    BOOL    TRUE  - Function succeded
            FALSE - Function failed.

Author:

    28-Nov-1993 created  -by-  JB

    18-Dec-1993 Sat 10:35:24 updated  -by-  DC
        use PRECTL rather RECTL *, and use INT rater than int, removed compiler
        warning which has unreferenced local variable

    16-Feb-1994 Wed 16:12:53 updated  -by-  DC
        Re-structure and make it Polyline encoded

    09-Apr-1994 Sat 16:38:16 updated  -by-  DC
        Fixed the ptlCur++ twice typo which make us Do the RECT crazy.

Revision History:


--*/
{
    PRECTL      prclCur;
    POINTFIX    ptsFix[4];
    HTENUMRCL   EnumRects;
    POINTL      ptlCur;
    DWORD       MaxRects;
    DWORD       cRects;
    BOOL        bMore;
    BOOL        NeedSendPM0;



    PLOTDBG(DBG_FILL_CLIP,
            ("DoFillByEnumingRects: Maximum polygon points = %d",
                        pPDev->pPlotGPC->MaxPolygonPts));

    PLOTASSERT(1, "DoFillByEnumingRects: Minimum must be 5 points [%ld]",
                pPDev->pPlotGPC->MaxPolygonPts >= 5,
                pPDev->pPlotGPC->MaxPolygonPts);

    //
    // In this mode we will enter polygon mode and try to batch based on the
    // number of points the device can handle in its polygon buffer.
    //

    bMore       = FALSE;
    EnumRects.c = 1;

    if ((!pco) || (pco->iDComplexity == DC_TRIVIAL)) {

        PLOTASSERT(1, "DoFillByEnumingClipRects: Invalid pco TRIVIAL passed (%08lx)",
                    (pco) && (pco->iDComplexity != DC_TRIVIAL), pco);

        return(FALSE);

    } else if (pco->iDComplexity == DC_RECT) {

        //
        // The visible area is one rectangle intersect with the destinaiton
        //

        PLOTDBG(DBG_FILL_CLIP, ("DoFillByEnumingClipRects: pco=DC_RECT"));

        EnumRects.rcl[0] = pco->rclBounds;

    } else {

        //
        // We have complex clipping region to be computed, call engine to start
        // enumerate the rectangles and set More = TRUE so we can get the first
        // batch of rectangles.
        //

        PLOTDBG(DBG_FILL_CLIP, ("DoFillByEnumingClipRects: pco=DC_COMPLEX, EnumRects now"));

        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
        bMore = TRUE;
    }


    //
    // Calculate how many rects we can do at a time, based on how large of
    // a polygon buffer the target device can hold. Make sure that value
    // is at least 1.
    //

    if (!(MaxRects = (DWORD)pPDev->pPlotGPC->MaxPolygonPts / 7)) {

        MaxRects = 1;
    }

    cRects      = MaxRects;
    NeedSendPM0 = TRUE;

    do {


        //
        // If the job was cancelled, break out now. This is typically done
        // anytime the code enters some looping that may take a while.
        //

        if (PLOT_CANCEL_JOB(pPDev)) {

            return(TRUE);
        }

        //
        // If More is true then we need to get next batch of rectangles
        //

        if (bMore) {

            bMore = CLIPOBJ_bEnum(pco, sizeof(EnumRects), (ULONG *)&EnumRects);

            if (!EnumRects.c) {

                PLOTWARN(("DoFillByEnumingClipRects: MORE CLIPOBJ_bEnum BUT Count=0"));
            }
        }


        PLOTDBG( DBG_FILL_CLIP,
                ("DoFillByEnumingClipRects: Doing batch of %ld clip rects",
                EnumRects.c));


        //
        // prclCur will point to the first enumerated rectangle
        //

        prclCur = (PRECTL)&EnumRects.rcl[0];

        while (EnumRects.c--) {

            ptsFix[3].x = LTOFX(prclCur->left);
            ptsFix[3].y = LTOFX(prclCur->top);

            MovePen(pPDev, &ptsFix[3], &ptlCur);

            if (NeedSendPM0) {

                SEND_PM0(pPDev);

                NeedSendPM0 = FALSE;
            }

            ptsFix[0].x = LTOFX(prclCur->right);
            ptsFix[0].y = ptsFix[3].y;

            ptsFix[1].x = ptsFix[0].x;
            ptsFix[1].y = LTOFX(prclCur->bottom);;

            ptsFix[2].x = ptsFix[3].x;
            ptsFix[2].y = ptsFix[1].y;

            SEND_PE(pPDev);

            OutputXYParams(pPDev,
                           (PPOINTL)ptsFix,
                           (PPOINTL)NULL,
                           (PPOINTL)&ptlCur,
                           (UINT)4,
                           (UINT)1,
                           'F');


            PLOTDBG(DBG_FILL_CLIP,
               ("DoFillByEnumingRects: Rect = (%ld, %ld) - (%ld, %ld)",
                 FXTOL(ptsFix[3].x), FXTOL( ptsFix[3].y),
                 FXTOL(ptsFix[1].x), FXTOL( ptsFix[1].y) ));

#if DBG
            if ((FXTODEVL(pPdev, ptsFix[1].x - ptsFix[3].x) >= (1016 * 34)) ||
                (FXTODEVL(pPdev, ptsFix[1].y - ptsFix[3].y) >= (1016 * 34)))  {

                PLOTWARN(("DoFillByEnumingClipRect: *** BIG RECT (%ld x %ld) *****",
                            FXTODEVL( pPDev, ptsFix[1].x - ptsFix[3].x),
                            FXTODEVL( pPDev, ptsFix[1].y - ptsFix[3].y)));
            }
#endif

            SEND_SEMI(pPDev);
            SEND_PM1(pPDev);
            SEND_SEMI(pPDev);

            //
            // 5 points per RECT polygon, so if we hit the limit then batch
            // it out first. we also calling the DoFillLogic when we are at
            // the very last enumeration of clipping rectangle.
            //

            --cRects;
            ++prclCur;

            if ((!cRects)     ||
                ((!EnumRects.c) && (!bMore))) {

                PLOTDBG(DBG_FILL_CLIP,
                        ("DoFillByEnumingRects: Hit MaxPolyPts limit"));

                //
                // We have hit the limit so close the polygon and do the fill
                // logic then continue till were done
                //

                SEND_PM2(pPDev);
                SETLINETYPESOLID(pPDev);

                DoFillLogic(pPDev,
                            pPointlBrushOrg,
                            pBrushFill,
                            NULL,
                            Rop4,
                            plineattrs,
                            NULL,
                            ulFlags);

                //
                // Reset the count of points generated thus far, and set the
                // flag to init polygon mode
                //

                cRects      = MaxRects;
                NeedSendPM0 = TRUE;
            }
        }

    } while (bMore);

    if (cRects != MaxRects) {

        PLOTWARN(("DoFillByEnumingRects: Why are we here?? Send Last Batch of =%ld",
                    MaxRects - cRects));

        SEND_PM2(pPDev);
        SETLINETYPESOLID(pPDev);

        DoFillLogic(pPDev,
                    pPointlBrushOrg,
                    pBrushFill,
                    NULL,
                    Rop4,
                    plineattrs,
                    NULL,
                    ulFlags);
    }

    return(TRUE);
}




BOOL
PlotCheckForWhiteIfPenPlotter(
    PPDEV       pPDev,
    BRUSHOBJ    *pBrushFill,
    BRUSHOBJ    *pBrushStroke,
    ROP4        rop4,
    PULONG      pulFlags
    )

/*++

Routine Description:

    This function checks to see if we can safely ignore a drawing command
    if it will cause only white to get rendered. Although this is legal on
    a raster device (white fill over some other previously rendered object),
    it doesn't make sense on a pen plotter.

Arguments:

    pPDev           - Pointer to our PDEV

    pBrushFill      - Brush used to fill the rectangle

    pBrushStroke    - Brush used to stroke the rectangle

    rop4            - rop to be used

    pulFlags        - FPOLY_xxxx flags, may be reset.


Return Value:

    BOOL    TRUE  - Bypass future operations
            FALSE - Operation needs to be completed

Author:

    28-Nov-1993 created  -by-  James Bratsanos (v-jimbr)

    15-Jan-1994 Sat 04:57:55 updated  -by-  DC
        Change GetColor() and make it tab 5


Revision History:


--*/
{

    ULONG   StrokeColor;
    ULONG   FillColor;


    //
    // Initially we do a quick check if were a PEN plotter to get rid of
    // either filling or stroking white. If we are a raster device, we
    // support filling white, so we cannot ignore the call.
    //

    if (!IS_RASTER(pPDev)) {

        //
        // Check to see if filling is enabled and if it is undo the fill flag
        // if the fill color is white.
        //

        if (*pulFlags & FPOLY_FILL ) {

            //
            // Get the fill color so we can look at it and decide if its a NOOP
            // on pen plotters. If it is, undo the FILL flag.
            //

            GetColor(pPDev, pBrushFill, &FillColor, NULL, rop4);

            if (PLOT_IS_WHITE( pPDev, FillColor)) {

                *pulFlags &= ~FPOLY_FILL;
            }
        }


        if (*pulFlags & FPOLY_STROKE) {

            //
            // Get the Stroke color so we can look at it and decide it its a
            // NOOP on pen plotters. If it is, undo the STROKE flag.
            //

            GetColor(pPDev, pBrushStroke, &StrokeColor, NULL, rop4);

            if (PLOT_IS_WHITE(pPDev, StrokeColor)) {

               *pulFlags &= ~FPOLY_STROKE;
            }
        }

        if (!(*pulFlags & (FPOLY_STROKE | FPOLY_FILL))) {

            //
            // Nothing left to do so simply return success
            //

            PLOTDBG(DBG_CHECK_FOR_WHITE,
                     ("PlotCheckForWhiteIfPen: ALL WHITE detected"));
            return(TRUE);
        }

        PLOTDBG(DBG_CHECK_FOR_WHITE,
                 ("PlotCheckForWhiteIfPen: Painting required!"));
    }

    return(FALSE);
}



BOOL
DoPolygon(
    PPDEV       pPDev,
    POINTL      *ppointlOffset,
    CLIPOBJ     *pClipObj,
    PATHOBJ     *pPathObj,
    POINTL      *pPointlBrushOrg,
    BRUSHOBJ    *pBrushFill,
    BRUSHOBJ    *pBrushStroke,
    ROP4        rop4,
    LINEATTRS   *plineattrs,
    ULONG       ulFlags
    )

/*++

Routine Description:

    This function is the core path handling function for the entire driver.
    The passed PATHOBJ and CLIPOBJ are looked at, and various logic is
    enabled to determine the correct sequence of events to get the target
    path filled. Since HPGL2 cannot handle complex clipping, this function
    must deal with the issue of having both a COMPLEX CLIPOBJ, and a
    COMPLEX PATHOBJ. When this function decides the work it needs to do
    is too complex, it fails this call, the NT graphics engine in turn will
    break down the work, most likely calling DrvPaint multiple times in
    order to get the object drawn.

Arguments:

    pPDev           - Pointer to our PDEV

    ppointlOffset   - Extra offset to the output polygon

    pClipObj        - clip object

    pPathObj        - The path object to be used

    pPointlBrushOrg - brush origin in the brush to be fill or stroke

    pBrushFill      - brush object to be used in the FILL

    pBrushStroke    - brush object to be used in the STROKE

    rop4            - Rop4 used in the fill

    plineattrs      - LINEATTRS for style lines stroke

    ulFlags         - polygon flags for stroke or fill


Return Value:

    BOOL    TRUE  - Function succeded
            FALSE - Function failed

Author:

    28-Nov-1993 created  -by-  James Bratsanos (v-jimbr)

    28-Jan-1994 Fri 00:58:25 updated  -by-  DC
        Style, commented, re-structure the loop and reduce code size.

    04-Aug-1994 Thu 20:00:23 updated  -by-  DC
        bug# 22348 which actually is a raster plotter firmware bug


Revision History:


--*/
{
    PRECTL      prclClip = NULL;
    POINTFIX    *pptfx;
    POINTFIX    ptOffsetFix;
    POINTFIX    ptStart;
    POINTL      ptlCur;
    PATHDATA    pd;
    DWORD       cptfx;
    DWORD       cptExtra;
    UINT        cCurPosSkips;
    WORD        PolyMode;
    BOOL        bPathCameFromClip = FALSE;
    BOOL        bStrokeOnTheFly    = FALSE;
    BOOL        bFirstSubPath;
    BOOL        bMore;
    BOOL        bRet;
    BOOL        PenIsDown;
    BYTE        NumType;


    //
    // Check to see if we can short cut some of our work if its a pen plotter
    //

    if (PlotCheckForWhiteIfPenPlotter(pPDev,
                                      pBrushFill,
                                      pBrushStroke,
                                      rop4,
                                      &ulFlags))  {
        return(TRUE);
    }

    //
    // There are a few different scenarios to deal with here when the
    // item in question is too complex and we need to fail. They are
    // catagorized as follows
    //
    //    1) The fill mode is unsupported, in which case we fail the call
    //       and it should come back in in a simpler format (DrvPaint)
    //
    //    2) We have a CLIPOBJ thats more complicated than a RECT and, a
    //       PATHOBJ, if we only have a clipobj we can enum it as a path
    //

    if ((ulFlags & FPOLY_WINDING) &&
        (!IS_WINDINGFILL(pPDev))) {

       //
       // The plotter cannot support WINDING Mode fills, all we can do
       // is fail the call and have it come back in a mode we can support
       //

       PLOTDBG(DBG_GENPOLYGON, ("DoPolygon: Can't do WINDING, return(FALSE)"));

       return(FALSE);
    }

    if (pClipObj != NULL) {

       //
       // We have a clipobj so decide what to do
       //

       if (pClipObj->iDComplexity == DC_COMPLEX) {

            //
            // Since the clipobj is complex we have two choices, either there is
            // no PATHOBJ, in which case we will enum the clipobj as a path, or
            // if there is a pathobj we must fail the call. HPGL2 doesn't
            // support COMPLEX clipping objects.
            //

            if (pPathObj != NULL) {

                //
                // We have a complex clip and a path? we cannot handle this so
                // fail the call, the NT graphics engine will simplify the
                // object by calling into other primitives (like DrvPaint).
                //

                PLOTDBG(DBG_GENPOLYGON,
                        ("DoPolygon: pco=COMPLEX, pPath != NULL, can handle, FALSE"));

                return(FALSE);
            }

            //
            // We have come this far, so we must have a CLIPOBJ that is complex
            // and we will go ahead and enum it as a path.
            //

            if ((pPathObj = CLIPOBJ_ppoGetPath(pClipObj)) == NULL) {

                PLOTRIP(("Engine path from clipobj returns NULL"));
                return(FALSE);
            }


            //
            // Record the fact that the PATHOBJ is really coming froma CLIPOBJ
            //

            bPathCameFromClip = TRUE;

       } else if (pClipObj->iDComplexity == DC_RECT) {

            //
            // We have a RECT CLIPOBJ, if we have no PATHOBJ we simply fill
            // the clipping rectangle. If we do have a PATHOBJ we need to set
            // the HPGL2 clip window before enumerating and filling the PATHOBJ.
            //

            if (pPathObj != NULL) {

                //
                // Some plotters have a firmware bug with clipping windows
                // when using styled lines that keep the styled lines from
                // being rendered, even though they fit inside the CLIPOBJ.
                //
                // We get around this limitation by failing this call. This in
                // turn will cause DoStrokePathByEnumingClipLines() to be used
                // instead.
                //

                if ((IS_RASTER(pPDev))                  &&
                    (ulFlags & FPOLY_STROKE)            &&
                    (plineattrs)                        &&
                    ((plineattrs->fl & LA_ALTERNATE)    ||
                     ((plineattrs->cstyle) &&
                      (plineattrs->pstyle)))) {

                    PLOTWARN(("DoPolygon: RASTER/Stroke/DC_RECT/PathObj/StyleLine: (Firmware BUG) FAILED and using EnumClipLine()"));

                    return(FALSE);
                }

                prclClip = &pClipObj->rclBounds;

            } else {

                //
                // Simply call the fill rect code and return, no more work
                // to do in this function.
                //

                return(DoRect(pPDev,
                              &pClipObj->rclBounds,
                              pBrushFill,
                              pBrushStroke,
                              pPointlBrushOrg,
                              rop4,
                              plineattrs,
                              ulFlags));

            }

        } else {

            //
            // CLIPOBJ is trivial so we simply ignore it and fill using the
            // passed PATHOBJ.
            //

            NULL;
        }

    } else {

        //
        // No CLIPOBJ so use the PATHOBJ passed in.
        //

        NULL;
    }

    //
    // Setup the offset coordinate data, in case were coming from
    // DrvTextOut. In this case there is an offset passed in that
    // must be applied to each point. This is used when the glyphs we
    // are painting, are actually interpreted as paths. In this case,
    // the paths are fixed based on the origin of the glyph. We must
    // add the current X/Y position in order to render the glyph in the
    // correct place on the page.
    //

    if (ppointlOffset) {

        ptOffsetFix.x = LTOFX(ppointlOffset->x);
        ptOffsetFix.y = LTOFX(ppointlOffset->y);

    } else {

        ptOffsetFix.x =
        ptOffsetFix.y = 0;
    }

    //
    // First we need to verify that we dont have more points than will fit
    // in our polygon buffer for this device. If this is the case we have two
    // choices. If the path did not come from a clip obj we fail this call,
    // if it did we handle this based on enuming the clipobj as rects and
    // filling. If we were also asked to stroke the PATHOBJ, we need to
    // enumerate the path yet another time.
    //

    cptfx = 0;
    cptExtra = 1;

    PATHOBJ_vEnumStart(pPathObj);

    do {

        bRet = PATHOBJ_bEnum(pPathObj, &pd);

        cptfx += pd.count;

        if ( pd.flags & PD_ENDSUBPATH ) {

            //
            // Count both the ENDSUBPATH and the PM1 as taking space...
            //

            cptExtra++;

            if (!(pd.flags & PD_CLOSEFIGURE)) {

                //
                // Since we were not asked to close the figure, we will generate
                // a move back to our starting point with the pen up, in order
                // eliminate problems with HPGL/2 closing the polygon for
                // us when we send the PM2

                cptExtra++;
            }
        }


    } while (bRet);


    PLOTDBG(DBG_GENPOLYGON,
                ("DoPolygon: Total points = %d, Extra %d",
                cptfx, cptExtra ));

    //
    // We will only do this if we have any points to do, first set bRet to
    // true in case we were not asked to do anything.
    //

    bRet = TRUE;

    if (cptfx) {

        SET_PP_WITH_ROP4(pPDev, rop4);

        //
        // Now add in the extra points that account for the PM0 and PM1
        // since we have some REAL points in the path.
        //

        cptfx += cptExtra;


        if (cptfx > pPDev->pPlotGPC->MaxPolygonPts) {

            PLOTWARN(("DoPolygon: Too many polygon points = %ld > PCD=%ld",
                            cptfx, pPDev->pPlotGPC->MaxPolygonPts));

            if (bPathCameFromClip) {

                PLOTWARN(("DoPolygon: Using DoFillByEnumingClipRects()"));

                //
                // The path the engine created for us to enum must be freed.
                //

                EngDeletePath(pPathObj);

                //
                // Since the path is to complex to fill with native  HPLG2
                // path code, we must do it the slower way.
                //

                return(DoFillByEnumingClipRects(pPDev,
                                                ppointlOffset,
                                                pClipObj,
                                                pPointlBrushOrg,
                                                pBrushFill,
                                                rop4,
                                                plineattrs,
                                                ulFlags));

            } else {

                //
                // If were dealing with a REAL PATHOBJ and there are too many
                // points in the polygon, and were being asked to FILL, all
                // we can do is fail the call and have the NT graphics engine
                // simplify the object.
                //

                if (ulFlags & FPOLY_FILL) {

                    PLOTERR(("DoPolygon: Too many POINTS, return FALSE"));
                    return(FALSE);

                } else if (ulFlags & FPOLY_STROKE) {

                    //
                    // Since were stroking we can go ahead and do it on the
                    // fly. Rather than building up a POLYGON object in the
                    // target device and asking the device to stroke it, we
                    // simply set up the correct stroke attributes, and request
                    // each path component to be stroked individually.
                    //

                    PLOTDBG(DBG_GENPOLYGON, ("DoPolygon: Is stroking manually"));


                    //
                    // At this point were ONLY being asked to stroke so we simply
                    // setup up the stroke color and set a flag to keep us from
                    // going into polygon mode.
                    //

                    DoSetupOfStrokeAttributes( pPDev,
                                               pPointlBrushOrg,
                                               pBrushStroke,
                                               rop4,
                                               plineattrs );

                    bStrokeOnTheFly = TRUE;
                }
            }
        }

        //
        // At this point were sure to actually do some real RENDERING so set
        // the clip window in the target device.
        //

        if (prclClip) {

            PLOTDBG(DBG_GENPOLYGON,
            ("DoPolygon: Setting Clip Window to: (%ld, %ld)-(%ld, %ld)=%ld x %ld",
                    prclClip->left,   prclClip->top,
                    prclClip->right,  prclClip->bottom,
                    prclClip->right -  prclClip->left,
                    prclClip->bottom -  prclClip->top));


            SetClipWindow( pPDev, prclClip);
        }

        //
        // Now setup to enumerate the PATHOBJ and output the points.
        //

        PATHOBJ_vEnumStart(pPathObj);

        PenIsDown     = FALSE;
        PolyMode      = 0;
        bFirstSubPath = TRUE;

        do {

            //
            // Check to see if the print job has been cancelled.
            //

            if (PLOT_CANCEL_JOB(pPDev)) {

                break;
            }

            bMore = PATHOBJ_bEnum(pPathObj, &pd);
            cptfx = pd.count;
            pptfx = pd.pptfx;

            //
            // Check the BEGINSUBPATH or if this is our first time here.
            //

            if ((pd.flags & PD_BEGINSUBPATH) || (bFirstSubPath)) {

                PLOTDBG(DBG_GENPOLYGON, ("DoPolygon: Getting PD_BEGINSUBPATH"));

                TERM_PE_MODE(pPDev, PolyMode);

                ptStart.x = pptfx->x + ptOffsetFix.x;
                ptStart.y = pptfx->y + ptOffsetFix.y;

                MovePen(pPDev, &ptStart, &ptlCur);
                PenIsDown = FALSE;

                pptfx++;
                cptfx--;

                if ((!bStrokeOnTheFly) && (bFirstSubPath)) {

                    SEND_PM0(pPDev);
                }

                bFirstSubPath = FALSE;
            }

            //
            // Now check if we are sending out Beziers.
            //

            if (pd.flags & PD_BEZIERS) {

                PLOTASSERT(1, "DoPolygon: PD_BEZIERS (count % 3) != 0 (%ld)",
                                                      (cptfx % 3) == 0, cptfx);

                SWITCH_TO_BR(pPDev, PolyMode, PenIsDown);

                NumType      = 'f';
                cCurPosSkips = 3;

            } else {

                SWITCH_TO_PE(pPDev, PolyMode, PenIsDown);

                NumType      = 'F';
                cCurPosSkips = 1;
            }

            PLOTDBG(DBG_GENPOLYGON, ("DoPolygon: OutputXYParam(%ld pts=%hs)",
                    cptfx, (pd.flags & PD_BEZIERS) ? "BEZIER" : "POLYGON"));

            OutputXYParams(pPDev,
                           (PPOINTL)pptfx,
                           (PPOINTL)&ptOffsetFix,
                           (PPOINTL)&ptlCur,
                           (UINT)cptfx,
                           (UINT)cCurPosSkips,
                           NumType);

            //
            // Check to see if we are ending the sub path.
            //

            if (pd.flags & PD_ENDSUBPATH) {

                PLOTDBG(DBG_GENPOLYGON,
                       ("DoPolygon: Getting PD_ENDSUBPATH   %hs",
                       (pd.flags & PD_CLOSEFIGURE) ? "PD_CLOSEFIGURE" : ""));

                //
                // If we are not closing the figure then move the pen to the
                // starting position so we do not have the plotter automatically
                // close the sub-path.
                //

                if (pd.flags & PD_CLOSEFIGURE) {

                    PLOTDBG(DBG_GENPOLYGON,
                            ("DoPolygon: OutputXYParam(1) to ptStart=(%ld, %ld)",
                                                ptStart.x, ptStart.y));

                    //
                    // We must not pass the ptOffsetFix, because we already
                    // added it into the ptStart at BEGSUBPATH.
                    //

                    SWITCH_TO_PE(pPDev, PolyMode, PenIsDown);

                    OutputXYParams(pPDev,
                                   (PPOINTL)&ptStart,
                                   (PPOINTL)NULL,
                                   (PPOINTL)&ptlCur,
                                   (UINT)1,
                                   (UINT)1,
                                   'F');
                }

                TERM_PE_MODE(pPDev, PolyMode);

                if (!(pd.flags & PD_CLOSEFIGURE)) {

                    MovePen(pPDev, &ptStart, &ptlCur);
                    PenIsDown = FALSE;
                }

                //
                // If we are not stroking on the fly, close the subpath.
                //

                if (!bStrokeOnTheFly) {

                    SEND_PM1(pPDev);
                }

            }

        } while (bMore);

        TERM_PE_MODE(pPDev, PolyMode);

        //
        // Now end polygon mode.
        //

        if ((bRet)                  &&
            (!bStrokeOnTheFly)      &&
            (!PLOT_CANCEL_JOB(pPDev))) {

            SEND_PM2(pPDev);
            SETLINETYPESOLID(pPDev);

            //
            // Now fill and/or stroke the current polygon.
            //

            DoFillLogic(pPDev,
                        pPointlBrushOrg,
                        pBrushFill,
                        pBrushStroke,
                        rop4,
                        plineattrs,
                        NULL,
                        ulFlags);
        }

        //
        // If we set a clip window, clear it.
        //

        if (prclClip) {

            ClearClipWindow(pPDev);
        }

    } else {

        PLOTDBG(DBG_GENPOLYGON, ("DoPolygon: PATHOBJ_bEnum=NO POINT"));
    }

    //
    // If the path was constructed from a complex clip object we need to
    // delete that path now.
    //

    if (bPathCameFromClip) {

       EngDeletePath(pPathObj);
    }

    return(bRet);
}





VOID
HandleLineAttributes(
    PPDEV       pPDev,
    LINEATTRS   *plineattrs,
    PLONG       pStyleToUse,
    LONG        lExtraStyle
    )

/*++

Routine Description:

    This function does any setup necessary to correctly handle stroking of
    a path. It does this by looking at the LINEATTRS structure passed in
    and setting up the HPGL2 plotter with the appropriate style info using
    HPGL2 styled line commands.

Arguments:

    pPDev           - Pointer to our PDEV

    plineattrs      - LINEATTRS for style lines stroke

    pStyleToUse     - The starting style offset to use, if this is NULL then
                      we use the starting member in plineatts.

    lExtraStyle     - Any extra style to use based on the current run

Return Value:

    VOID

Author:

    01-Feb-1994 created  -by-  James Bratsanos (v-jimbr)

Revision History:


--*/
{
    LONG        lTotLen = 0L;
    INT         i;
    LONG        lScaleVal;
    INT         iCount;
    PFLOAT_LONG pStartStyle;
    FLOAT_LONG  aAlternate[2];
    BOOL        bSolid = TRUE;
    LONG        lStyleState;
    PLONG       pArrayToUse;


    PLOTDBG( DBG_HANDLELINEATTR,
             ("HandleLineAttr: plineattrs = %hs",
             (plineattrs) ? "Exists" : "NULL" ));

    if (plineattrs) {

        PLOTASSERT(1,
                  "HandleLineAttrs: Getting a LA_GEOMETRIC and cannot handle %u",
                  (!(plineattrs->fl & LA_GEOMETRIC)),
                  plineattrs->fl);

        //
        // Set up the correct lStyleState to use, the passed one has precedence
        // over the one imbedded in the lineattributes structure.
        //

        if (pStyleToUse) {

            lStyleState = *pStyleToUse;

        } else {

            lStyleState = plineattrs->elStyleState.l;
        }

        if (plineattrs->fl & LA_ALTERNATE) {

            PLOTDBG( DBG_HANDLELINEATTR,
                    ("HandleLineAttr: plineattrs has LA_ALTERNATE bit set!"));
            //
            // This is a special case where every other pixel is on...
            //

            pStartStyle     = &aAlternate[0];
            iCount          = sizeof(aAlternate) / sizeof(aAlternate[0]);

            aAlternate[0].l = 1;
            aAlternate[1].l = 1;

        } else if ((plineattrs->cstyle != 0) &&
                   (plineattrs->pstyle != (PFLOAT_LONG)NULL)) {

           //
           // There is a user defined style passed in so set up for it
           //

            iCount      = plineattrs->cstyle;
            pStartStyle = plineattrs->pstyle;

            PLOTDBG(DBG_HANDLELINEATTR, ("HandleLineAttr: Count = %ld",
                                            plineattrs->cstyle));

        } else {

           //
           // This is a SOLID line, so simply set the number of points to 0
           //

           iCount = 0;
        }

        if (iCount) {

            PFLOAT_LONG pCurStyle;
            INT         idx;
            LONG        lTempValue;
            LONG        lValueToEnd;
            BOOL        bInDash;
            LONG        convArray[MAX_USER_POINTS];
            PLONG       pConverted;
            LONG        newArray[MAX_USER_POINTS+2];
            PLONG       pNewArray;


            PLOTASSERT(0,
                       "HandleLineAttributes: Getting more than 18 points (%ld)",
                       (iCount <= MAX_STYLE_ENTRIES) ,
                       iCount);

            //
            // Record our current DASH state, the line either starts with
            // a gap or a dash.
            //

            if (plineattrs->fl & LA_STARTGAP) {

                bInDash = FALSE;

            } else {

                bInDash = TRUE;
            }

            //
            // Since we know we can't handle more than 20 points sent to HPGL2
            // we limit it now to 18 in order to compensate for the up-to 2
            // additional points we may add later.
            //

            iCount = min(MAX_STYLE_ENTRIES, iCount);


            //
            // Get our scaling value, so we can convert style units to
            // our units.
            //

            lScaleVal = PLOT_STYLE_STEP(pPDev);


            //
            // Now convert to the new units, and store the result in the
            // new array. Also keep track of the total length of the style
            //

            for (i = 0, pConverted = &convArray[0], lTotLen = 0,
                                                pCurStyle = pStartStyle;
                 i < iCount ;
                 i++, pCurStyle++, pConverted++) {

                *pConverted = pCurStyle->l * lScaleVal;

                PLOTDBG( DBG_HANDLELINEATTR,
                         ("HandleLineAttr: Orig Array [%ld]= %ld becomes %ld",
                          i, pCurStyle->l, *pConverted ));

                lTotLen += *pConverted;
            }


            //
            // Now convert the passed style state and extra info into the
            // real final style state to use, we do this by taking the value of
            // interest which is packed into the HIWORD and LOWORD of
            // lstylestate based on the DDK definition, then we must add on
            // any additional distance (which may have come from enuming
            // a CLIPLINE structure).
            //

            lStyleState = (HIWORD(lStyleState) * PLOT_STYLE_STEP(pPDev) +
                           LOWORD(lStyleState) + lExtraStyle) % lTotLen ;

            PLOTDBG(DBG_HANDLELINEATTR,
                    ("HandleLineAttributes: Computed Style state = %ld, extra = %ld",
                    lStyleState, lExtraStyle));

            //
            // Set up our final pointer to the new array, since we may be done
            // based on the final computed stylestate being 0.
            //

            pNewArray = &newArray[0];


            if (lStyleState != 0) {

                lTempValue = 0;

                //
                // Since lStyleState has a value other than zero we must
                // construct a new style array to pass to HPGL2 that has been
                // rotated in order to take into account the style state.
                // the code below constructs the new array.
                //

                for (i=0, pConverted = &convArray[0];
                     i < iCount ;
                     i++, pConverted++) {

                    //
                    // At this point were looking for the entry which partially
                    // encompasses the style state derived. Based on this
                    // we can create a new array that is a transformation of the
                    // original array rotated the correct amount.
                    //

                    if (lStyleState  < lTempValue + *pConverted) {

                        //
                        // Here is the transition point.
                        //

                        *pNewArray++ = *pConverted - (lStyleState - lTempValue);

                        //
                        // Record the value that needs to be appended to the end
                        // of the array
                        //

                        lValueToEnd = lStyleState - lTempValue;


                        idx = i;

                        idx++;
                        pConverted++;

                        //
                        // Fill up the end
                        //

                        while (idx++ < iCount) {

                            *pNewArray++ = *pConverted++;
                        }

                        //
                        // Now fill up the beginning...
                        //

                        idx        = 0;
                        pConverted = &convArray[0];

                        //
                        // If there was an odd number we can add together
                        // the starting and ending one since they have the
                        // same state
                        //

                        if ((iCount % 2) == 1 ) {

                            pNewArray--;
                            *pNewArray += *pConverted++;

                            idx++;
                            pNewArray++;
                        }

                        while (idx++ < i) {

                            *pNewArray++ = *pConverted++;
                        }

                        *pNewArray++ = lValueToEnd;

                        break;
                    }

                    lTempValue += *pConverted;

                    bInDash = TOGGLE_DASH(bInDash);
                }

                pArrayToUse = &newArray[0];
                iCount = (INT)(pNewArray - &newArray[0]);

            } else {

                pArrayToUse = &convArray[0];
            }

            PLOTASSERT(0,
                       "HandleLineAttributes: Getting more than 20 points (%ld)",
                       (iCount <= MAX_USER_POINTS) ,
                       iCount);
            //
            // There is a style pattern so set up for it.
            //

            bSolid = FALSE;


            //
            // Begin the HPGL2 line command to define a custom style type
            //

            OutputString(pPDev, "UL1");

            //
            // If this flag is set, the first segment is a gap NOT a dash so
            // we trick HPGL2 into doing the right thing by having a zero
            // length dash in the begining.
            //

            if (!bInDash) {

               OutputString(pPDev, ",0");
            }

            //
            // Since we output the 0 len dash at the begining if the line
            // starts with a gap, the most additional points we send out
            // is decremented by 1.
            //

            iCount = min((bInDash ? MAX_USER_POINTS : MAX_USER_POINTS - 1) ,
                         iCount);

            //
            // Enum through the points in the style array, converting to our
            // Graphics units and send them to the plotter.
            //

            for (i = 0; i < iCount; i++, pArrayToUse++) {

                PLOTDBG(DBG_HANDLELINEATTR,
                         ("HandleLineAttr: New Array [%ld]= %ld",
                          i, *pArrayToUse));

                OutputFormatStr(pPDev, ",#l", *pArrayToUse);
            }

            //
            // Now output the linetype and specify the total lenght of the
            // pattern.
            //

            OutputFormatStr(pPDev, "LT1,#d,1",
                                ((lTotLen * 254) / pPDev->lCurResolution ) / 10 );

            //
            // Update our linetype in the pdev since we ALWAYS send out this
            // line type
            //

            pPDev->LastLineType = PLOT_LT_USERDEFINED;
        }
    }

    //
    // If it was SOLID just send out the SOLID (default command)
    //

    if (bSolid) {

        PLOTDBG(DBG_HANDLELINEATTR, ("HandleLineAttr: Line type is SOLID"));

        //
        // Send out the correct commands to the plotter
        //

        SETLINETYPESOLID(pPDev);
    }
}





VOID
DoFillLogic(
    PPDEV       pPDev,
    POINTL      *pPointlBrushOrg,
    BRUSHOBJ    *pBrushFill,
    BRUSHOBJ    *pBrushStroke,
    ROP4        Rop4,
    LINEATTRS   *plineattrs,
    SIZEL       *pszlRect,
    ULONG       ulFlags
    )

/*++

Routine Description:

    This routine has the core logic for filling and already established
    polygon, or a passed in segment.

Arguments:

    pPDev           - Pointer to our PDEV

    pptlBrushOrg    - Pointer to the brush origin to be set

    pBrushFill      - Brush used to fill the rectangle

    pBrushStroke    - Brush used to stroke the rectangle

    Rop4            - rop to be used

    plineattrs      - Pointer to the line attributes for a styled line

    pszlRect        - Pointer to a segment to stroke.

    ulFlags         - FPOLY_XXX, stroking and or filling flags.


Return Value:

    VOID

Author:

    30-Nov-1993 created  -by-  James Bratsanos (v-jimbr)

    15-Jan-1994 Sat 05:02:42 updated  -by-  DC
        Change GetColor() and tabify

    18-Jan-1994 Sat 05:02:42 updated  -by-  James Bratsanos (v-jimbr)

    16-Feb-1994 Wed 09:34:06 updated  -by-  DC
        Update for the rectangle polygon case to use RR/ER commands

Revision History:


--*/
{
    INTDECIW    PenWidth;


    //
    // Since a polygon must already be defined this code simply
    // looks at the passed data and sends out the appropriate codes to
    // fill/stroke this polygon correctly.
    //

    PenWidth.Integer =
    PenWidth.Decimal = 0;


    if (ulFlags & FPOLY_FILL) {

        DEVBRUSH    *pDevFill;
        DWORD       FillForeColor;
        LONG        HSType;
        LONG        HSParam;
        BOOL        bSetTransparent = FALSE;


        //
        // If we are filling, get the current color taking the ROP into
        // acount.
        //

        if (!GetColor(pPDev, pBrushFill, &FillForeColor, &pDevFill, Rop4)) {

            PLOTERR(("DoFillLogic: GetColor()=FALSE"));
            return;
        }

        HSType  = -1;
        HSParam = (LONG)((pDevFill) ? pDevFill->LineSpacing : 0);

        //
        // If the plotter cannot support tranparent mode there is no need
        // to wory about backgrounds. we will only ever care about foreground.
        //

        if (((IS_TRANSPARENT(pPDev)) || (!IS_RASTER(pPDev))) &&
            (pDevFill)) {

            //
            // Determine if we are using a Pre-defined pattern to fill with.
            //

            switch(pDevFill->PatIndex) {

            case HS_HORIZONTAL:
            case HS_VERTICAL:
            case HS_BDIAGONAL:
            case HS_FDIAGONAL:
            case HS_CROSS:
            case HS_DIAGCROSS:

                PenWidth.Integer = PW_HATCH_INT;
                PenWidth.Decimal = PW_HATCH_DECI;
                bSetTransparent  = (BOOL)IS_TRANSPARENT(pPDev);

                if ((Rop4 & 0xFF00) != 0xAA00) {

                    if (IS_RASTER(pPDev)) {

                       //
                       // Send out the Background Rop.
                       //

                       SetRopMode(pPDev, ROP4_BG_ROP(Rop4));

                       PLOTDBG(DBG_FILL_LOGIC,
                               ("DoFillLogic: BCK = MC=%02lx", ROP4_BG_ROP(Rop4)));
                    }

                    //
                    // We need to select the background color fill then
                    // select the foreground color back... ONLY if it is
                    // non white.
                    //

                    if ((IS_RASTER(pPDev)) ||
                        (!PLOT_IS_WHITE(pPDev, pDevFill->ColorBG))) {

                        HSType = HS_DDI_MAX;
                    }
                }

                break;

            default:

                //
                // If we are a pen plotter and have a user defined pattern.
                // Do a horizontal hatch for background color and a vertical
                // hatch for the foreground color.
                //

                if ((!IS_RASTER(pPDev)) &&
                    (pDevFill->PatIndex >= HS_DDI_MAX)) {

                    PLOTWARN(("DoFillLogic: PEN+USER PAT, Do HS_FDIAGONAL for BG [%ld]",
                                    pDevFill->ColorBG));

                    HSParam <<= 1;

                    if (!PLOT_IS_WHITE(pPDev, pDevFill->ColorBG)) {

                        HSType = HS_FDIAGONAL;

                    } else {

                        PLOTWARN(("DoFillLogic: PEN+USER PAT, Skip WHITE COLOR"));
                    }
                }

                break;
            }
        }


        //
        // Check for a valid pre-defined hatch type and send out the commands.
        //

        if (HSType != -1) {

            PLOTDBG(DBG_FILL_LOGIC, ("DoFillLogic: Fill BGColor = %08lx", pDevFill->ColorBG));

            SelectColor(pPDev, pDevFill->ColorBG, PenWidth);

            SetHSFillType(pPDev, (DWORD)HSType, HSParam);

            SetBrushOrigin(pPDev, pPointlBrushOrg);

            if (pszlRect) {

                SEND_RR(pPDev);
                OutputLONGParams(pPDev, (PLONG)pszlRect, 2, 'd');
                pszlRect = NULL;

            } else {

                SEND_FP(pPDev);

                //
                // Fill with the correct winding mode.
                //

                if (ulFlags & FPOLY_WINDING) {

                    SEND_1SEMI(pPDev);
                }
            }
        }

        //
        // Send out the foreground ROP.
        //

        if (IS_RASTER(pPDev)) {

            SetRopMode(pPDev, ROP4_FG_ROP(Rop4));
        }

        //
        // Now select the foreground color.
        //

        SelectColor(pPDev, FillForeColor, PenWidth);

        if (bSetTransparent) {

            PLOTDBG(DBG_FILL_LOGIC, ("DoFillLogic: TRANSPARENT MODE"));

            //
            // Set up for transparent.
            //

            SEND_TR1(pPDev);
        }

        if (pDevFill) {

            //
            // If the pattern to fill with is user defined, the convert it
            // to a user defined pattern in HPGL2. A user defined pattern
            // is where the client code passed a bitmap in to GDI that
            // it expects to fill with (with tileing). If its a pen plotter,
            // this won't work, so simulate it with a diagonal fill.
            //

            if (pDevFill->PatIndex >= HS_DDI_MAX) {

                if (IS_RASTER(pPDev)) {

                    DownloadUserDefinedPattern(pPDev, pDevFill);

                } else {

                    PLOTWARN(("DoFillLogic: PEN+USER PAT, Do HS_BDIAGONAL for FG [%ld]",
                                    FillForeColor));

                    SetHSFillType(pPDev, HS_BDIAGONAL, HSParam);
                }

            } else {

                //
                // The pattern is a predefined one, so convert it to an HPGL2
                // pattern type.
                //

                SetHSFillType(pPDev, pDevFill->PatIndex, pDevFill->LineSpacing);
            }

            //
            // Set the brush origin.
            //

            SetBrushOrigin(pPDev, pPointlBrushOrg);

        } else {

            SetHSFillType(pPDev, HS_DDI_MAX, 0);
        }

        //
        // If we were passed a segment, paint it now.
        //

        if (pszlRect) {

            SEND_RR(pPDev);
            OutputLONGParams(pPDev, (PLONG)pszlRect, 2, 'd');
            pszlRect = NULL;

        } else {

            //
            // Execute the command to paint the existing path using the current
            // parameters.
            //

            SEND_FP(pPDev);

            if (ulFlags & FPOLY_WINDING) {

                SEND_1SEMI(pPDev);
            }
        }

        //
        // If we used tranparent mode put it back
        //

        if (bSetTransparent) {

            SEND_TR0(pPDev);
        }
    }

    if (ulFlags & FPOLY_STROKE) {

        DoSetupOfStrokeAttributes(pPDev,
                                  pPointlBrushOrg,
                                  pBrushStroke,
                                  Rop4,
                                  plineattrs);

        //
        // give the plotter the command to stroke the polygon outline!
        //

        if (pszlRect) {

            SEND_ER(pPDev);
            OutputLONGParams(pPDev, (PLONG)pszlRect, 2, 'd');

        } else {

            SEND_EP(pPDev);
        }
    }
}





VOID
DoSetupOfStrokeAttributes(
    PPDEV       pPDev,
    POINTL      *pPointlBrushOrg,
    BRUSHOBJ    *pBrushStroke,
    ROP4        Rop4,
    LINEATTRS   *plineattrs
    )

/*++

Routine Description:

    This routine sets up the plotter in order to correctly handle stroking,
    based on the passed brush and lineattributes structures.

Arguments:

    pPDev                Pointer to our current PDEV with state info about
                         driver

    pPointlBrushOrg      Brush origin

    pBrushStroke         BRUSHOBJ to stroke with (should only be solid color)

    Rop4                 The rop to use when stroking

    plineattrs           LINEATTRS structure with the specified line styles


Return Value:

    VOID

Author:

    01-Feb-1994 Tue 05:02:42 created  -by-  James Bratsanos (v-jimbr)

Revision History:


--*/
{
    INTDECIW    PenWidth;
    DWORD       StrokeColor;


    GetColor(pPDev, pBrushStroke, &StrokeColor, NULL, Rop4);

    PenWidth.Integer =
    PenWidth.Decimal = 0;

    SelectColor(pPDev, StrokeColor, PenWidth);

    //
    // Send out the foreground Rop, if we are RASTER
    //

    if (IS_RASTER(pPDev)) {

        SetRopMode(pPDev, ROP4_FG_ROP(Rop4));
    }

    //
    // Handle the line attributes
    //

    HandleLineAttributes(pPDev, plineattrs, NULL, 0);
}




LONG
DownloadUserDefinedPattern(
    PPDEV       pPDev,
    PDEVBRUSH   pBrush
    )

/*++

Routine Description:

    This function defines a user pattern to the HPGL2 device. This is used
    when a client application passes a bitmap to GDI to use for filling
    polygons.

Arguments:

    pPDev   - Pointer to the PDEV

    pBrush  - Pointer to the cached device brush


Return Value:

    INT to indicate a pattern number downloaed/defined

Author:

    09-Feb-1994 Wed 13:11:01 updated  -by-  DC
        Remove 4bpp/1bpp, it always must have pbgr24

    08-Feb-1994 Tue 01:49:53 updated  -by-  DC
        make PalEntry.B = *pbgr++ as first color, since the order we have
        is PALENTRY and first color is B in the structure.

    27-Jan-1994 Thu 21:20:30 updated  -by-  DC
        Add the RF cache codes

    14-Jan-1994 Fri 15:23:40 updated  -by-  DC
        Added assert for compatible device pattern
        Added so it will take device compatible pattern (8x8,16x16,32x32,64x64)

    13-Jan-1994 Thu 19:04:04 created  -by-  DC
        Re-write

    16-Feb-1994 Wed 11:00:19 updated  -by-  DC
        Change return value to return the HSFillType, and fixed the bugs which
        if we found the cached but we do not set the fill type again

    05-Aug-1994 Fri 18:35:45 updated  -by-  DC
        Bug# 22381, we do FindCachedPen() for during the pattern downloading
        and this causing the problem if the pen is not in the cache then we
        will send the PEN DEFINITION at middle of pattern downloading.  If this
        happened then downloading sequence is broken.  We fixes this by

            1) Cache the pen indices if we have enough memory
            2) Run through FindCachePen() for all the RGB colors in the pattern
            3) Download cached pen indices if we have memory OR run through
               FindCachedPen() again to download the pen indices

        This may still have problem if we have

            1) No pen indices caching memory
            2) more color in the pattern then the max pens in the device

        BUT if this happens then we have no choice but to have the wrong output.


Revision History:


--*/

{
    LONG    HSFillType;
    LONG    RFIndex;

    //
    // Firs we must find the RFIndex
    //
    //

    HSFillType = HS_FT_USER_DEFINED;

    if ((RFIndex = FindDBCache(pPDev, pBrush->Uniq)) < 0) {

        LPBYTE  pbgr24;


        RFIndex = -RFIndex;

        //
        // We must download new pattern to the plotter now, make it positive
        //

        if (pbgr24 = pBrush->pbgr24) {

            PALENTRY    PalEntry;
            LPWORD      pwIdx;
            UINT        Idx;
            UINT        Size;


            Size = (UINT)pBrush->cxbgr24 * (UINT)pBrush->cybgr24;

            PLOTDBG(DBG_USERPAT,
                    ("PlotGenUserDefinedPattern: DOWNLOAD %ld x %ld=%ld USER PAT #%ld",
                    (LONG)pBrush->cxbgr24, (LONG)pBrush->cybgr24, Size, RFIndex));

            if (!(pwIdx = (LPWORD)LocalAlloc(LPTR, Size * sizeof(WORD)))) {

                //
                // Do not have memory to do it, so forget it
                //

                PLOTWARN(("Download User defined pattern NO Memory so REAL TIME RUN"));
            }

            //
            // We must first get all the pens cached so we have the indices to
            // use, otherwise we will download the pen color when the pen color
            // is defined.
            //

            PalEntry.Flags = 0;

            for (Idx = 0; Idx < Size; Idx++) {

                WORD    PenIdx;


                PalEntry.B = *pbgr24++;
                PalEntry.G = *pbgr24++;
                PalEntry.R = *pbgr24++;

                PenIdx = (WORD)FindCachedPen(pPDev, &PalEntry);

                if (pwIdx) {

                    pwIdx[Idx] = PenIdx;
                }
            }

            //
            // Now output the download header/size first
            //

            OutputFormatStr(pPDev, "RF#d,#d,#d", RFIndex,
                            (LONG)pBrush->cxbgr24, (LONG)pBrush->cybgr24);

            //
            // If we cached the indices, then use them. Otherwise, find the
            // cache again.
            //

            if (pwIdx) {

                for (Idx = 0; Idx < Size; Idx++) {

                    OutputFormatStr(pPDev, ",#d", pwIdx[Idx]);
                }

                //
                // Free the indices memory if we have one.
                //

                LocalFree((HLOCAL)pwIdx);

            } else {

                //
                // We do not have cached indices, so run through again.
                //

                pbgr24 = pBrush->pbgr24;

                for (Idx = 0; Idx < Size; Idx++) {

                    PalEntry.B = *pbgr24++;
                    PalEntry.G = *pbgr24++;
                    PalEntry.R = *pbgr24++;

                    OutputFormatStr(pPDev, ",#d", FindCachedPen(pPDev, &PalEntry));
                }
            }

            SEND_SEMI(pPDev);

        } else {

            PLOTERR(("PlotGenUserDefinedPattern: NO pbgr24??, set SOLID"));

            HSFillType = HS_DDI_MAX;
            RFIndex    = 0;
        }

    } else {

        PLOTDBG(DBG_USERPAT,
                ("PlotGenUserDefinedPattern: We have CACHED RFIndex=%ld",
                RFIndex));
    }

    SetHSFillType(pPDev, (DWORD)HSFillType, RFIndex);

    return(RFIndex);
}

