/*++

Copyright (c) 1990-1999  Microsoft Corporation


Module Name:

    bitblt.c


Abstract:

    This module contains functions which implement bitmap handling for the
    plotter driver.

Author:

    19:15 on Mon 15 Apr 1991    -by-    SC
        Created it

    15-Nov-1993 Mon 19:24:36 updated  -by-  DC
        fixed, clean up

    18-Dec-1993 Sat 10:52:07 updated  -by-  DC
        Move some functions from bitbltp.c and move others to htblt.c and
        bitmap.c.   This file mainly has DrvXXXXX() which related to the
        bitblt or drawing.

    27-Jan-1994 Thu 23:41:23 updated  -by-  DC
        Revised bitblt so it will handle better ROP3/Rop4 support, also it
        will check the PCD file's ROP caps

[Environment:]

    GDI Device Driver - Plotter.


[Notes:]


Revision History:


--*/

#include "precomp.h"
#pragma hdrstop


#define DBG_PLOTFILENAME    DbgBitBlt


#define DBG_COPYBITS            0x00000001
#define DBG_BITBLT              0x00000002
#define DBG_DRVPAINT            0x00000004
#define DBG_DRVFILLPATH         0x00000008
#define DBG_DRVSTROKEANDFILL    0x00000010
#define DBG_MIXTOROP4           0x00000020
#define DBG_TEMPSRC             0x00000040
#define DBG_STRETCHBLT          0x00000080
#define DBG_BANDINGHTBLT        0x00000100
#define DBG_DOFILL              0x00000200
#define DBG_CSI                 0x00000400


DEFINE_DBGVAR(0);


//
// This is the default BANDING size (2MB) for the DrvStretchBlt()
//

#if DBG

LPSTR   pCSIName[] = { "SRC", "PAT", "TMP" };


DWORD   MAX_STRETCH_BLT_SIZE = (2 * 1024 * 1024);
#else
#define MAX_STRETCH_BLT_SIZE    (2 * 1024 * 1024)
#endif

//
// This table converts MIX-1 to ROP3 value
//
static BYTE amixToRop4[] = {
         0x00,             // R2_BLACK             0
         0x05,             // R2_NOTMERGEPEN      DPon
         0x0a,             // R2_MASKNOTPEN       DPna
         0x0f,             // R2_NOTCOPYPEN       PN
         0x50,             // R2_MASKPENNOT       PDna
         0x55,             // R2_NOT              Dn
         0x5a,             // R2_XORPEN           DPx
         0x5f,             // R2_NOTMASKPEN       DPan
         0xa0,             // R2_MASKPEN          DPa
         0xa5,             // R2_NOTXORPEN        DPxn
         0xaa,             // R2_NOP              D
         0xaf,             // R2_MERGENOTPEN      DPno
         0xf0,             // R2_COPYPEN          P
         0xf5,             // R2_MERGEPENNOT      PDno
         0xfa,             // R2_MERGEPEN         DPo
         0xff,             // R2_WHITE             1
};

extern const POINTL ptlZeroOrigin;





ROP4
MixToRop4(
   MIX  mix
   )

/*++

Routine Description:

    This function converts a MIX value to a ROP4 value

Arguments:

    mix      - MIX value to convert, this is defined in wingdi.h and represents
               one of 16 different ROP2 values
Return Value:

    ROP4     - the converted value


Author:

    18-Dec-1993 Sat 09:34:06 created  -by-  JB


Revision History:


--*/
{
   ROP4 rop4Return;

   //
   // Now pack the two new values by looking up the correct rop codes in our
   // table.
   //


   rop4Return =  amixToRop4[((mix & 0xff) - 1)];

   rop4Return |= ( amixToRop4[((( mix >> 8) & 0xff ) - 1 )] << 8 );


   PLOTDBG(DBG_MIXTOROP4, ("MixToRop4 before %x after %x", (int) mix,(int) rop4Return));

   return(rop4Return);
}





BOOL
BandingHTBlt(
    PPDEV           pPDev,
    SURFOBJ         *psoDst,
    SURFOBJ         *psoSrc,
    SURFOBJ         *psoMask,
    CLIPOBJ         *pco,
    XLATEOBJ        *pxlo,
    COLORADJUSTMENT *pca,
    POINTL          *pptlBrushOrg,
    PRECTL          prclDst,
    PRECTL          prclSrc,
    PPOINTL         pptlMask,
    WORD            HTRop3,
    BOOL            InvertMask
    )

/*++

Routine Description:

    This is our internal version of StretchBlt() which always does halftoning
    and banding if the destination bitmap is too large. Since the target
    surface can pottentially be 3 feet by 3 feet, we don't want to
    have a bitmap created that large because of memory requirements. So
    we review the memory requirements and if they are too large we simply
    band, by setting a clip region that moves down the page via a loop. This
    effectively has the halftoning engine simply working on much smaller
    more manageable bitmaps, and we end up sending out virtually the same
    number of bytes.

Arguments:

    pPDev       - Pointer to our PDEV

    psoDst      - This is a pointer to a SURFOBJ.    It identifies the surface
                  on which to draw.

    psoSrc      - This SURFOBJ defines the source for the Blt operation.  The
                  driver must call GDI Services to find out if this is a device
                  managed  surface or a bitmap managed by GDI.

    psoMask     - This optional surface provides a mask for the source.  It is
                  defined by a logic map, i.e. a bitmap with one bit per pel.

                  The mask is used to limit the area of the source that is
                  copied.  When a mask is provided there is an implicit rop4 of
                  0xCCAA, which means that the source should be copied wherever
                  the mask is 1, but the destination should be left alone
                  wherever the mask is 0.

                  When this argument is NULL there is an implicit rop4 of
                  0xCCCC, which means that the source should be copied
                  everywhere in the source rectangle.

                  The mask will always be large enough to contain the source
                  rectangle, tiling does not need to be done.

    pco         - This is a pointer to a CLIPOBJ.    GDI Services are provided
                  to enumerate the clipping region as a set of rectangles or
                  trapezoids. This limits the area of the destination that will
                  be modified.

                  Whenever possible, GDI will simplify the clipping involved.
                  However, unlike DrvBitBlt, DrvStretchBlt may be called with a
                  single clipping rectangle.  This is necessary to prevent
                  roundoff errors in clipping the output.

    pxlo        - This is a pointer to an XLATEOBJ.  It tells how color indices
                  should be translated between the source and target surfaces.

                  The XLATEOBJ can also be queried to find the RGB color for
                  any source index.  A high quality stretching Blt will need
                  to interpolate colors in some cases.

    pca         - This is a pointer to COLORADJUSTMENT structure, if NULL it
                  specifies that appiclation did not set any color adjustment
                  for this DC, and it is up to the driver to provide a default
                  adjustment

    pptlBrushOrg- Pointer to the POINT structure which specifies the location
                  where the halftone brush should alignment to, if this pointer
                  is NULL, then we assume that (0, 0) is the brush origin.

    prclDst     - This RECTL defines the area in the coordinate system of the
                  destination surface that should be modified.

                  The rectangle is defined by two points.    These points are
                  not well ordered, i.e. the coordinates of the second point
                  are not necessarily larger than those of the first point.
                  The rectangle they describe does not include the lower and
                  right edges.  DrvStretchBlt will never be called with an
                  empty destination rectangle.

                  DrvStretchBlt can do inversions in both x and y, this happens
                  when the destination rectangle is not well ordered.

    prclSrc     - This RECTL defines the area in the coordinate system of the
                  source surface that will be copied.  The rectangle is defined
                  by two points, and will map onto the rectangle defined by
                  prclDst.  The points of the source rectangle are well
                  ordered.  DrvStretch will never be given an empty source
                  rectangle.

                  Note that the mapping to be done is defined by prclSrc and
                  prclDsst. To be precise, the given points in prclDst and
                  prclSrc lie on integer coordinates, which we consider to
                  correspond to pel centers.  A rectangle defined by two such
                  points should be considered a geometric rectangle with two
                  vertices whose coordinates are the given points, but with 0.5
                  subtracted from each coordinate.  (The POINTLs should just be
                  considered a shorthand notation for specifying these
                  fractional coordinate vertices.)  Note thate the edges of any
                  such rectangle never intersect a pel, but go around a set of
                  pels.  Note also that the pels that are inside the rectangle
                  are just what you would expect for a "bottom-right exclusive"
                  rectangle.  The mapping to be done by DrvStretchBlt will map
                  the geometric source rectangle exactly onto the geometric
                  destination rectangle.

    pptlMask    - This POINTL specifies which pel in the given mask corresponds
                  to the upper left pel in the source rectangle.  Ignore this
                  argument if there is no mask.

    HTRop3      - HIBYTE(HTRop3) when psoMask is not NULL and
                  LOBYTE(HTRop3) when psoMask is NULL

    InvertMask  - TRUE if the mask must be inverted


Return Value:


    TRUE if sucessful FALSE if failed

Author:

    07-Mar-1994 Mon 12:52:41 created  -by-  DC

Revision History:

    16-Mar-1994 Wed 15:20:42 updated  -by-  DC
        Updated for banding the mask so it will works correcly for the engine
        problem.

    04-May-1994 Wed 11:27:39 updated  -by-  DC
        Make rotate type (landscape mode) banding from right to left rather
        than top to bottom

    29-Nov-1995 Wed 13:00:30 updated  -by-  DC
        Mark not reentratable for the same PDEV, this is signal that called to
        the EngStretchBlt(HALFTONE) is failing for some reason


--*/

{
    CLIPOBJ *pcoNew;
    CLIPOBJ coSave;
    RECTL   rclMask;
    RECTL   rclBounds;
    DWORD   MaskRop3;
    UINT    Loop;
    BOOL    DoRotate;
    BOOL    Ok;



    if (!IS_RASTER(pPDev)) {

        PLOTDBG(DBG_BANDINGHTBLT, ("BandingHTBlt: Pen Plotter: IGNORE and return OK"));
        return(TRUE);
    }

    if (pPDev->pPlotGPC->ROPLevel < ROP_LEVEL_1) {

        PLOTDBG(DBG_BITBLT, ("BandingHTBlt: RopLevel < 1, Cannot Do it"));
        return(TRUE);
    }

    if (pPDev->Flags & PDEVF_IN_BANDHTBLT) {

        //
        // Something is wrong here
        //

        PLOTERR(("BandingHTBlt: Recursive is not allowed, FAILED"));
        return(FALSE);
    }

    //
    // Turn on the flag now
    //

    pPDev->Flags |= PDEVF_IN_BANDHTBLT;

    if ((!pca) || (pca->caFlags & ~(CA_NEGATIVE | CA_LOG_FILTER))) {

        //
        // If we have a NULL or invalid flag then use the default one
        //

        PLOTASSERT(0, "DrvStretchBlt: INVALID ColorAdjustment Flags=%04lx, USE DEFAULT",
                   ((pca) && (pca->caFlags & ~(CA_NEGATIVE | CA_LOG_FILTER))),
                   (pca) ? pca->caFlags : 0);

        pca = &(pPDev->PlotDM.ca);
    }

    if (!pptlBrushOrg) {

        pptlBrushOrg = (PPOINTL)&(ptlZeroOrigin);
    }

    if (pPDev->PlotDM.Flags & PDMF_PLOT_ON_THE_FLY) {

        if (psoMask) {

            PLOTWARN(("BandingHTBlt: PosterMode -> Ignored MASK"));
            psoMask = NULL;
        }
    }

    if (psoMask) {

        //
        // If we have a source mask then we will first do (S|D)=0xEE or
        // (~S|D)=0xBB to white out the mask area then use (S&D)=0x88 to AND
        // in the halftoned bitmap. This is done to simulate the desired ROP
        // since the target device can't handle this on its own.
        //

        rclMask.left   = pptlMask->x;
        rclMask.top    = pptlMask->y;
        rclMask.right  = rclMask.left + (prclSrc->right - prclSrc->left);
        rclMask.bottom = rclMask.top +  (prclSrc->bottom - prclSrc->top);
        HTRop3         = (WORD)HIBYTE(HTRop3);
        MaskRop3       = (DWORD)((InvertMask) ? 0xBB : 0xEE);
        Loop           = 2;

        //
        // We must call this function to set up the xlate table correctly
        //

        IsHTCompatibleSurfObj(pPDev,
                              psoMask,
                              NULL,
                              ISHTF_ALTFMT | ISHTF_HTXB | ISHTF_DSTPRIM_OK);

    } else {

        HTRop3   = (WORD)LOBYTE(HTRop3);
        Loop     = 1;
    }

    if (!HTRop3) {

        HTRop3 = 0xCC;
    }

    //
    // Now look at how we can modify the clipping rect, in case we need
    // to band.
    //

    if (pco) {

        //
        // Save the original clipping object so we can restore it before exiting
        //

        pcoNew = NULL;
        coSave = *pco;

    } else {

        PLOTDBG(DBG_BANDINGHTBLT, ("BandingHTBlt: Create NEW EMPTY pco"));

        if (!(pcoNew = pco = EngCreateClip())) {

            PLOTERR(("BandingHTBlt: EngCreateClip() FAILED, got NO CLIP"));

            pPDev->Flags &= ~PDEVF_IN_BANDHTBLT;
            return(FALSE);
        }

        pco->iDComplexity = DC_TRIVIAL;
    }

    PLOTDBG(DBG_BANDINGHTBLT, ("BandingHTBlt: The pco->iDComplexity=%ld",
                                pco->iDComplexity));

    if (pco->iDComplexity == DC_TRIVIAL) {

        //
        // Since it is trivial, we just draw the whole destination
        //

        pco->iDComplexity = DC_RECT;
        pco->rclBounds    = *prclDst;
    }

    //
    // Now make sure our bounds will not go outside of the surface
    //

    rclBounds.left    =
    rclBounds.top     = 0;
    rclBounds.right   = psoDst->sizlBitmap.cx;
    rclBounds.bottom  = psoDst->sizlBitmap.cy;

    if (IntersectRECTL(&rclBounds, &(pco->rclBounds))) {

        PLOTDBG(DBG_BANDINGHTBLT,
                ("BandingHTBlt: rclBounds=(%ld, %ld)-(%ld, %ld), %ld x %ld, ROP=%02lx",
                    rclBounds.left, rclBounds.top,
                    rclBounds.right, rclBounds.bottom,
                    rclBounds.right - rclBounds.left,
                    rclBounds.bottom - rclBounds.top, (DWORD)HTRop3));

    } else {

        PLOTDBG(DBG_BANDINGHTBLT, ("BandingHTBlt: rclBounds=NULL, NOTHING TO DO"));
        Loop = 0;
    }

    //
    // Now let's band it through
    //

    DoRotate = (BOOL)(pPDev->PlotForm.BmpRotMode == BMP_ROT_RIGHT_90);
    Ok       = TRUE;

    while ((Ok) && (Loop--)) {

        RECTL   rclDst;
        SIZEL   szlDst;
        LONG    cScan;
        DWORD   BmpFormat;
        DWORD   OHTFlags;

        //
        // When Loop = 1 then we are doing the MASK
        // When Loop = 0 then we are doing the SOURCE
        //
        // We will band only MAX_STRETCH_BLT_SIZE at once
        //

        rclDst    = *prclDst;
        szlDst.cx = rclDst.right - rclDst.left;
        szlDst.cy = rclDst.bottom - rclDst.top;
        BmpFormat = (DWORD)((Loop) ? BMF_1BPP : HTBMPFORMAT(pPDev));

        cScan = (LONG)(MAX_STRETCH_BLT_SIZE /
                           GetBmpDelta(BmpFormat, (DoRotate) ? szlDst.cy :
                                                               szlDst.cx));


        //
        // We always want at least 8 scan lines and also a multiple of 8.
        //


        if (!cScan) {

            cScan = 8;

        } else if (cScan & 0x07) {

            cScan = (LONG)((cScan + 7) & ~(DWORD)0x07);
        }


        PLOTDBG(DBG_BANDINGHTBLT, ("BandingHTBlt: cScan=%ld, Total=%ld",
                                cScan, (DoRotate) ? szlDst.cx : szlDst.cy));

        OHTFlags = 0;

        while ((Ok)                             &&
               (rclDst.top < prclDst->bottom)   &&
               (rclDst.right > prclDst->left)) {

            if (DoRotate) {

                if ((rclDst.left = rclDst.right - cScan) < prclDst->left) {

                    rclDst.left = prclDst->left;
                }

            } else {

                if ((rclDst.bottom = rclDst.top + cScan) > prclDst->bottom) {

                    rclDst.bottom = prclDst->bottom;
                }
            }

            pco->rclBounds = rclBounds;

            if (IntersectRECTL(&(pco->rclBounds), &rclDst)) {

                PLOTDBG(DBG_BANDINGHTBLT,
                        ("BandingHTBlt: Banding RECTL=(%ld, %ld)-(%ld, %ld), %ld x %ld",
                            pco->rclBounds.left, pco->rclBounds.top,
                            pco->rclBounds.right, pco->rclBounds.bottom,
                            pco->rclBounds.right - pco->rclBounds.left,
                            pco->rclBounds.bottom - pco->rclBounds.top));

                if (Loop) {

                    SURFOBJ *psoNew;
                    HBITMAP hNewBmp;
                    RECTL   rclNew;

                    //
                    // We have a mask, so create a 1BPP bitmap, and stretch it
                    // to the new destination size, then output it using
                    // MaskRop3
                    //

                    Ok = FALSE;

                    PLOTDBG(DBG_CSI, ("BandingHTBlt: CreateBitmapSURFOBJ(MASK)"));

                    if (psoNew = CreateBitmapSURFOBJ(pPDev,
                                                     &hNewBmp,
                                                     pco->rclBounds.right -
                                                        pco->rclBounds.left,
                                                     pco->rclBounds.bottom -
                                                        pco->rclBounds.top,
                                                     BMF_1BPP,
                                                     NULL)) {

                        rclNew.left   = prclDst->left - pco->rclBounds.left;
                        rclNew.top    = prclDst->top - pco->rclBounds.top;
                        rclNew.right  = rclNew.left + szlDst.cx;
                        rclNew.bottom = rclNew.top + szlDst.cy;

                        PLOTDBG(DBG_BANDINGHTBLT,
                                ("BandingHTBlt: Banding MASK RECTL=(%ld, %ld)-(%ld, %ld), %ld x %ld",
                                rclNew.left, rclNew.top,
                                rclNew.right, rclNew.bottom,
                                psoNew->sizlBitmap.cx,
                                psoNew->sizlBitmap.cy));

                        if (EngStretchBlt(psoNew,           // psoDst
                                          psoMask,          // psoSrc
                                          NULL,             // psoMask,
                                          NULL,             // pco
                                          NULL,             // pxlo
                                          NULL,             // pca
                                          pptlBrushOrg,     // pptlHTOrg
                                          &rclNew,          // prclDst
                                          &rclMask,         // prclSrc
                                          NULL,             // pptlMask
                                          BLACKONWHITE)) {


                            if (!(Ok = OutputHTBitmap(pPDev,
                                                      psoNew,
                                                      NULL,
                                                      (PPOINTL)&rclDst,
                                                      NULL,
                                                      MaskRop3,
                                                      &OHTFlags))) {

                                PLOTERR(("BandingHTBlt: OutputHTBitmap(M|D) FAILED"));
                            }

                        } else {

                            PLOTERR(("BandingHTBlt: EngStretchBlt(MASK B/W) FAILED"));
                        }

                        //
                        // Delete this band of the mask bitmap
                        //

                        EngUnlockSurface(psoNew);

                        PLOTDBG(DBG_CSI, ("BandingHTBlt: EngDeleteSuface(MASK)"));

                        if (!EngDeleteSurface((HSURF)hNewBmp)) {

                            PLOTERR(("PLOTTER: BandingHTBlt, EngDeleteSurface(%p) FAILED",
                                        (DWORD_PTR)hNewBmp));
                        }

                    } else {

                        PLOTERR(("BandingHTBlt: Create MASK SURFOBJ (%ld x %ld) failed",
                                    pco->rclBounds.right - pco->rclBounds.left,
                                    pco->rclBounds.bottom - pco->rclBounds.top));
                    }

                } else {


                    //
                    // We must pass the psoMask/pptlMask so the haltone
                    // operations will not overwrite the non masked
                    // area (erasing it).
                    //

                    pPDev->Rop3CopyBits = HTRop3;

                    if (!(Ok = EngStretchBlt(psoDst,        // psoDst
                                             psoSrc,        // psoSrc
                                             psoMask,       // psoMask,
                                             pco,           // pco
                                             pxlo,          // pxlo
                                             pca,           // pca
                                             pptlBrushOrg,  // pptlHTOrg
                                             prclDst,       // prclDst
                                             prclSrc,       // prclSrc
                                             pptlMask,      // pptlMask
                                             HALFTONE))) {

                        PLOTERR(("BandingHTBlt: EngStretchBlt(Halftone:S&D) FAILED"));
                    }
                }
            }

            if (DoRotate) {

                rclDst.right = rclDst.left;

            } else {

                rclDst.top = rclDst.bottom;
            }
        }


        //
        // We must do this in order to exit HPGL/2 mode. This is because the
        // next call for the source will go through EngStrecthBlt(HALFTONE)
        // which will re-enter RTL mode again.
        //

        if (OHTFlags & OHTF_MASK) {

            OHTFlags |= OHTF_EXIT_TO_HPGL2;

            OutputHTBitmap(pPDev, NULL, NULL, NULL, NULL, 0xAA, &OHTFlags);
        }
    }

    if (pcoNew) {

        EngDeleteClip(pcoNew);

    } else {

        *pco = coSave;
    }

    pPDev->Flags &= ~PDEVF_IN_BANDHTBLT;

    return(Ok);
}




BOOL
DoFill(
    SURFOBJ     *psoDst,
    SURFOBJ     *psoSrc,
    CLIPOBJ     *pco,
    XLATEOBJ    *pxlo,
    PRECTL      prclDst,
    PPOINTL     pptlSrc,
    BRUSHOBJ    *pbo,
    PPOINTL     pptlBrush,
    ROP4        Rop4
    )

/*++

Routine Description:

    This function fills a RECT area with a brush and takes clipping into
    consideration

Arguments:

    psoDst      - Destination surface obj

    psoSrc      - source surface obj

    pco         - Clip obj

    pxlo        - translate obj

    prclDst     - destination rect area

    pptlSrc     - point where source starts

    pbo         - Brush obj to fill with

    pptlBrush   - Brush alignment origin

    Rop4        - ROP4 to use

Return Value:

    TRUE if ok, FALSE if failed


Author:

    Created - v-jimbr

    18-Dec-1993 Sat 09:34:06 created  -by-  DC
        Clean up formal argumeneted, commented

    15-Jan-1994 Sat 01:41:48 updated  -by-  DC
        added rclDst to DoFill() in case pco is NULL

    10-Mar-1994 Thu 00:35:06 updated  -by-  DC
        Fixed so when we call DoPolygon it will take prclDst (if not NULL)
        into account by intersect it with the rclBounds in the pco first

    25-Mar-1994 update -by- JB
        Modified function to enumerate clipping region if destination
        rectangle exists.

Revision History:


--*/

{
    PPDEV   pPDev;
    RECTL   rclDst;


    if (!(pPDev = SURFOBJ_GETPDEV(psoDst))) {

        PLOTERR(("DoFill: Invalid pPDev in psoDst"));
        return(FALSE);
    }

    //
    // Here we have to see if the clip obj is trivial or non existant, in which
    // case we pass this directly to fill rect.
    //

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

        if ((pco) && (pco->iDComplexity == DC_RECT)) {

            PLOTDBG(DBG_DOFILL,
              ("DoFill: pco = RECT %s", (prclDst) ? ", WITH dest rect" : "" ));

            //
            // First grab the destination as the bounding rect since,
            // we have a RECT clipping region
            //

            rclDst = pco->rclBounds;

            //
            // Now if we also had a destination rect passed in as well,
            // intersect down to the final rect
            //

            if (prclDst) {

                if ( !IntersectRECTL(&rclDst, prclDst)) {

                   return( TRUE );

                }
            }

            //
            // And finally point to the new rect for the fill
            //

            prclDst = &rclDst;

        } else if (!prclDst) {


            PLOTWARN(
              ("DoFill: No destination rectange and NULL or TRIVIAL pco!"));

            //
            // We don't have any clipping so fill the target rect
            //

            rclDst.left   =
            rclDst.top    = 0;
            rclDst.right  = psoDst->sizlBitmap.cx;
            rclDst.bottom = psoDst->sizlBitmap.cy;
            prclDst       = &rclDst;
        }

        return(DoRect(pPDev,
                      prclDst,
                      pbo,
                      NULL,
                      pptlBrush,
                      Rop4,
                      NULL,
                      FPOLY_FILL));

    } else {

        BOOL        Ok = TRUE;
        BOOL        bMore;
        HTENUMRCL   EnumRects;
        PRECTL      pCurClipRect;

        //
        // We have complex clipping but we also have a destination rect to
        // fill, this means we have to enum the clipping region as rects
        // so we can intersect each one with the target rect..
        //

        PLOTDBG(DBG_DOFILL,
                 ("DoFill: pco = COMPLEX %s", (prclDst) ? ", WITH dest rect" : "" ));

        if (prclDst) {


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

            do {

                //
                // See if the job has been aborted
                //

                if (PLOT_CANCEL_JOB(pPDev)) {

                    break;
                }

                //
                // Grab the next batch of rectangles
                //

                if (bMore) {

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


                //
                /// Set up for enuming the clip rectangles
                //

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


                while ((Ok) && (EnumRects.c--)) {

                    rclDst = *pCurClipRect;

                    //
                    // Make sure we have something left to fill after the
                    // intersect
                    //

                    if( IntersectRECTL(&rclDst, prclDst) ) {

                        Ok = DoRect( pPDev,
                                     &rclDst,
                                     pbo,
                                     NULL,
                                     pptlBrush,
                                     Rop4,
                                     NULL,
                                     FPOLY_FILL );

                    }
                    pCurClipRect++;
                }


            } while ( bMore );


        } else {


            Ok = DoPolygon(pPDev,
                           NULL,
                           pco,
                           NULL,
                           pptlBrush,
                           pbo,
                           NULL,
                           Rop4,
                           NULL,
                           FPOLY_FILL);


       }

       return(Ok);
    }
}




BOOL
DrvPaint(
    SURFOBJ     *psoDst,
    CLIPOBJ     *pco,
    BRUSHOBJ    *pbo,
    PPOINTL     pptlBrushOrg,
    MIX         Mix
    )

/*++

Routine Description:

    This function is the most basic drawing function in the driver. As graphic
    calls get failed, the NT graphics engine will reduce those other calls
    (if we fail them) down to DrvPaint. We cannot fail DrvPaint as the engine
    has nowhere else to go.

Arguments:

    Per DDI Spec.


Return Value:

    TRUE of OK, FALSE if falied

Author:

    Created by v-jimbr

    18-Dec-1993 Sat 09:27:29 updated  -by-  DC
        Updated, commented, change to correct formal header

    15-Jan-1994 Sat 00:38:41 updated  -by-  DC
        Re-arranged and call DrvBitBlt() if can do a damm thing.


Revision History:


--*/

{
    PPDEV       pPDev;
    RECTL       rclDst;
    DWORD       Rop4;

    //
    // get our PDEV from the SURFOBJ
    //

    if (!(pPDev = SURFOBJ_GETPDEV(psoDst))) {

        PLOTERR(("DrvPaint: Invalid pPDev in pso"));
        return(FALSE);
    }

    PLOTASSERT(0, "DrvPaint: WARNING: pco [%08lx] is NULL or DC_TRIVIAL???",
                (pco) && (pco->iDComplexity != DC_TRIVIAL), pco);

    if ((pco)                               &&
        (pco->iDComplexity == DC_TRIVIAL)   &&
        (pco->iFComplexity == FC_RECT)) {

        PLOTWARN(("DrvPaint: <pco> DC_TRIVIAL but NOT FC_RECT, make DC_RECT ??? (%ld,%ld)-(%ld,%ld)",
                    pco->rclBounds.left,
                    pco->rclBounds.top,
                    pco->rclBounds.right,
                    pco->rclBounds.bottom));

        pco->iDComplexity = DC_RECT;
    }

    //
    // Make sure we don't pass a NULL rect.
    //

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

        rclDst = pco->rclBounds;

    } else {

        rclDst.left   =
        rclDst.top    = 0;
        rclDst.right  = psoDst->sizlBitmap.cx;
        rclDst.bottom = psoDst->sizlBitmap.cy;
    }

    Rop4 = MixToRop4(Mix);

    //
    // If we can actually draw the passed object with device brushes (etc)
    // then do it now. Otherwise, we will have to simulate it via DrvBitBlt
    //

    if (GetColor(pPDev, pbo, NULL, NULL, Rop4) > 0) {

        PLOTDBG(DBG_DRVPAINT, ("DrvPAINT: Calling DoFill()"));

        return(DoFill(psoDst,               // psoDst
                      NULL,                 // psoSrc
                      pco,                  // pco
                      NULL,                 // pxlo
                      NULL,                 // prclDest only fill based on pco
                      NULL,                 // prclSrc
                      pbo,                  // pbo
                      pptlBrushOrg,         // pptlBrushOrg
                      Rop4));               // Rop4

    } else {

        PLOTDBG(DBG_DRVPAINT, ("DrvPAINT: Can't do it Calling DrvBitBlt()"));

        return(DrvBitBlt(psoDst,            // psoDst
                         NULL,              // psoSrc
                         NULL,              // psoMask
                         pco,               // pco
                         NULL,              // pxlo
                         &rclDst,           // prclDst
                         (PPOINTL)&rclDst,  // pptlSrc
                         NULL,              // pptlMask
                         pbo,               // pbo,
                         pptlBrushOrg,      // pptlBrushOrg,
                         Rop4));            // Rop4
    }
}





BOOL
DrvFillPath(
    SURFOBJ     *pso,
    PATHOBJ     *ppo,
    CLIPOBJ     *pco,
    BRUSHOBJ    *pbo,
    POINTL      *pptlBrushOrg,
    MIX         Mix,
    FLONG       flOptions
    )

/*++

Routine Description:

    This function will take a PATHOBJ as a parameter and fill in
    the closed region with the specified brush.

Arguments:

    Per DDI spec.


Return Value:

    TRUE if ok, FALSE if error

Author:

    18-Dec-1993 Sat 09:27:29 created  -by-  DC
        Updated, commented


    Created by v-jimbr

Revision History:


--*/

{
    PPDEV    pPDev;
    ULONG    ulOptions;
    ROP4     rop4;
    BOOL     bRetVal;



    //
    // Convert the mix to a rop since we  use it more than once
    //

    rop4 = MixToRop4(Mix);

    PLOTDBG(DBG_DRVFILLPATH, ("DrvFillPath: Mix = %x, Rop4 = %x", Mix, rop4));

    if (!(pPDev = SURFOBJ_GETPDEV(pso))) {

        PLOTERR(("DrvFillPath: Invalid pPDev in pso"));
        return(FALSE);
    }


    //
    // Get color will tell us if the requested op can be done in HPGL2 mode
    // if it cant, we have to simulate via DrvBitBlt
    //

    if (GetColor(pPDev, pbo, NULL, NULL, rop4) > 0 ) {

       ulOptions = FPOLY_FILL;

       if (flOptions & FP_WINDINGMODE) {

          //
          // Set the flag to notify the generic path code about the fill type
          //

          ulOptions |= FPOLY_WINDING;
       }

       bRetVal = DoPolygon(pPDev,
                           NULL,
                           pco,
                           ppo,
                           pptlBrushOrg,
                           pbo,
                           NULL,
                           rop4,
                           NULL,
                           ulOptions);
    } else {

       bRetVal = FALSE;

       PLOTDBG(DBG_DRVFILLPATH, ("DrvFillPath: Failing because GetColor <= 0 "));

    }

    return( bRetVal );
}





BOOL
DrvStrokeAndFillPath(
    SURFOBJ     *pso,
    PATHOBJ     *ppo,
    CLIPOBJ     *pco,
    XFORMOBJ    *pxo,
    BRUSHOBJ    *pboStroke,
    LINEATTRS   *plineattrs,
    BRUSHOBJ    *pboFill,
    POINTL      *pptlBrushOrg,
    MIX         MixFill,
    FLONG       flOptions
    )

/*++

Routine Description:

    This function will take a PATHOBJ as a parameter, fill in
    the closed region with the FILL brush, and stroke the path with
    the STROKE brush.


Arguments:

    Per DDI

Return Value:

    TRUE if ok, FALSE if error


Author:

    18-Dec-1993 Sat 09:27:29 created  -by-  DC
        Updated, commented


    Created by v-jimbr


Revision History:


--*/

{
    PPDEV       pPDev;
    ULONG       ulOptions;
    BOOL        bRetVal;
    ROP4        rop4;


    //
    // Convert the mix to a rop since we  use it more than once
    //

    rop4 = MixToRop4(MixFill);


    PLOTDBG(DBG_DRVSTROKEANDFILL, ("DrvStrokeAndFillPath: Mix = %x, Rop4 = %x", MixFill, rop4));

    if (!(pPDev = SURFOBJ_GETPDEV(pso))) {

        PLOTERR(("DrvStrokeAndFillPath: Invalid pPDev in pso"));
        return(FALSE);
    }




    if (GetColor(pPDev, pboFill, NULL, NULL, rop4) > 0 ) {


       ulOptions = FPOLY_STROKE | FPOLY_FILL;

       if (flOptions & FP_WINDINGMODE) {

          ulOptions |= FPOLY_WINDING;
       }

       bRetVal = DoPolygon(pPDev,
                           NULL,
                           pco,
                           ppo,
                           pptlBrushOrg,
                           pboFill,
                           pboStroke,
                           rop4,
                           plineattrs,
                           ulOptions);
    } else {


       bRetVal = FALSE;

       PLOTDBG(DBG_DRVSTROKEANDFILL,
                ("DrvStrokeAndFillPath: Failing because GetColor is <= 0",
                MixFill, rop4));

    }

    return(bRetVal);
}




BOOL
DrvCopyBits(
   SURFOBJ  *psoDst,
   SURFOBJ  *psoSrc,
   CLIPOBJ  *pco,
   XLATEOBJ *pxlo,
   RECTL    *prclDst,
   POINTL   *pptlSrc
   )

/*++

Routine Description:

    Convert between two bitmap formats

Arguments:

    Per Engine spec.

Return Value:

    BOOLEAN


Author:

    11-Feb-1993 Thu 21:00:43 created  -by-  DC

    09-Feb-1994 Wed 16:49:17 updated  -by-  DC
        Adding rclHTBlt to have psoHTBlt correctly tiled, also check if the
        pco is passed.

    19-Jan-1994 Wed 14:28:45 updated  -by-  DC
        Adding hack to handle EngStretchBlt() to our own temp surfobj

    06-Jan-1994 Thu 04:34:37 updated  -by-  DC
        Make sure we do not do this for pen plotter

    01-Mar-1994 Tue 10:51:58 updated  -by-  DC
        Make the call to BandingHTBlt() rather to EngStretchBlt()


Revision History:


--*/

{
    SURFOBJ *psoHTBlt;
    PPDEV   pPDev;
    RECTL   rclDst;


    //
    // Copy down the destination rectangle
    //

    rclDst = *prclDst;

    PLOTDBG(DBG_COPYBITS, ("DrvCopyBits: Dst=(%ld, %ld)-(%ld-%ld) [%ld x %ld]",
                rclDst.left, rclDst.top, rclDst.right, rclDst.bottom,
                rclDst.right - rclDst.left,
                rclDst.bottom - rclDst.top));

    //
    // The DrvCopyBits() function lets applicatiosn convert between bitmap and
    // device formats.
    //
    // BUT... for our plotter device we cannot read the printer surface
    //        bitmap back, so tell the caller that we cannot do it if they
    //        really called us with that sort of request.
    //

    if (psoSrc->iType != STYPE_BITMAP) {

        DWORD   Color = 0xFFFFFF;

        PLOTASSERT(1, "DrvCopyBits: psoSrc->iType not STYPE_DEVICE",
                    psoSrc->iType == STYPE_DEVICE, psoSrc->iType);

        //
        // Someone tried to copy from a non-bitmap surface, ie STYPE_DEVICE
        //

        if (pxlo) {

            Color = XLATEOBJ_iXlate(pxlo, Color);
        }

        //
        // If we doing XOR then we want to have all area = 0 first
        //

        if (!(pPDev = SURFOBJ_GETPDEV(psoSrc))) {

            PLOTERR(("DrvCopyBits: invalid pPDev"));
            return(FALSE);
        }

        if (pPDev->Rop3CopyBits == 0x66) {

            PLOTWARN(("DrvCopyBits: Rop3CopyBits = 0x66, Color = 0x0"));
            Color = 0;
        }

        PLOTWARN(("DrvCopyBits: Cannot copy from DEVICE, Do EngErase=(%ld,%ld)-(%ld, %ld), COLOR=%08lx)",
                rclDst.left, rclDst.top, rclDst.right, rclDst.bottom, Color));

        return(EngEraseSurface(psoDst, prclDst, Color));
    }

    if (psoDst->iType != STYPE_DEVICE) {

        //
        // Someone tried to copy to bitmap surface, ie STYPE_BITMAP
        //

        PLOTWARN(("DrvCopyBits: Cannot copy to NON-DEVICE destination"));

        SetLastError(ERROR_INVALID_PARAMETER);
        return(FALSE);
    }

    if (!(pPDev = SURFOBJ_GETPDEV(psoDst))) {

        PLOTERR(("DrvCopyBits: invalid pPDev"));
        return(FALSE);
    }

    //
    // If this is us calling ourselves during bitmap handling do it now.
    //

    if (psoHTBlt = pPDev->psoHTBlt) {

        PLOTDBG(DBG_TEMPSRC, ("DrvCopyBits: psoHTBlt=%ld x %ld, psoSrc=%ld x %ld, pptlSrc=(%ld, %ld)",
                    psoHTBlt->sizlBitmap.cx, psoHTBlt->sizlBitmap.cy,
                    psoSrc->sizlBitmap.cx, psoSrc->sizlBitmap.cy,
                    pptlSrc->x, pptlSrc->y));

        PLOTDBG(DBG_TEMPSRC, ("DrvCopyBits: szlHTBlt=(%ld, %ld)-(%ld, %ld) = %ld x %ld",
                        pPDev->rclHTBlt.left,  pPDev->rclHTBlt.top,
                        pPDev->rclHTBlt.right, pPDev->rclHTBlt.bottom,
                        pPDev->rclHTBlt.right - pPDev->rclHTBlt.left,
                        pPDev->rclHTBlt.bottom - pPDev->rclHTBlt.top));

        PLOTASSERT(1, "DrvCopyBits: psoHTBlt Type != psoSrc Type",
                    psoHTBlt->iType == psoSrc->iType, 0);

        PLOTASSERT(0, "DrvCopyBits: ??? pptlSrc [%08lx] != (0, 0)",
                    (pptlSrc->x == 0) && (pptlSrc->y == 0), pptlSrc);

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

            PLOTASSERT(1, "DrvCopyBits: psoHTBlt Size < psoSrc Size",
                    (psoHTBlt->sizlBitmap.cx >= psoSrc->sizlBitmap.cx) &&
                    (psoHTBlt->sizlBitmap.cy >= psoSrc->sizlBitmap.cy), 0);

            PLOTASSERT(1, "DrvCopyBits: rclHTBlt > psoHTBlt size",
                    (pPDev->rclHTBlt.left   <= psoHTBlt->sizlBitmap.cx) &&
                    (pPDev->rclHTBlt.right  <= psoHTBlt->sizlBitmap.cx) &&
                    (pPDev->rclHTBlt.top    <= psoHTBlt->sizlBitmap.cy) &&
                    (pPDev->rclHTBlt.bottom <= psoHTBlt->sizlBitmap.cy),
                    0);

            PLOTASSERT(1, "DrvCopyBits: pPDev->rclHTBlt Size != psoSrc Size",
                ((pPDev->rclHTBlt.right - pPDev->rclHTBlt.left) ==
                                                    psoSrc->sizlBitmap.cx) &&
                ((pPDev->rclHTBlt.bottom - pPDev->rclHTBlt.top) ==
                                                    psoSrc->sizlBitmap.cy),
                0);

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

            PLOTWARN(("DrvCopyBits: **** MAY BE EngStretchBlt(HALFTONE) FAILED but we got EngStretchBlt(COLORONCOLOR) instead"));

            PLOTASSERT(1, "DrvCopyBits: rclHTBlt != pco->rclBounds, pco=%08lx",
                ((pPDev->rclHTBlt.right - pPDev->rclHTBlt.left) ==
                 (pco->rclBounds.right - pco->rclBounds.left)) &&
                ((pPDev->rclHTBlt.bottom - pPDev->rclHTBlt.top) ==
                 (pco->rclBounds.bottom - pco->rclBounds.top)), pco);

        } else {

            PLOTASSERT(1, "DrvCopyBits: <psoHTBlt>, pco [%08lx] is Complex.",
                       pco->iDComplexity != DC_COMPLEX, pco);
        }

        if (!EngCopyBits(psoHTBlt,              // psoDst
                         psoSrc,                // psoSrc
                         pco,                   // pco
                         NULL,                  // pxlo
                         &(pPDev->rclHTBlt),    // prclDst
                         pptlSrc)) {            // pptlSrc

            PLOTERR(("DrvCopyBits: EngCopyBits(psoHTBlt, psoSrc) Failed"));
        }

        return(TRUE);
    }

    if (!IS_RASTER(pPDev)) {

        PLOTDBG(DBG_COPYBITS, ("DrvCopyBits: Pen Plotter: IGNORE and return OK"));
        return(TRUE);
    }

    //
    // First validate everything to see if this one is the halftoned result
    // or is compatible with halftoned result, otherwise we will call
    // EngStretchBlt(HALFTONE) halftone the sources then it will eventually
    // come back to this function to output the halftoned result.
    //

    if (IsHTCompatibleSurfObj(pPDev,
                              psoSrc,
                              pxlo,
                              ISHTF_ALTFMT | ISHTF_HTXB | ISHTF_DSTPRIM_OK)) {

        DWORD   Rop;

        if (!(Rop = (DWORD)(pPDev->Rop3CopyBits & 0xFF))) {

            Rop = 0xCC;
        }

        PLOTDBG(DBG_COPYBITS, ("DrvCopyBits: HTCompatible: Rop=%08lx", Rop));

        pPDev->Rop3CopyBits = 0xCC;     // RESET!!!

        return(OutputHTBitmap(pPDev,
                              psoSrc,
                              pco,
                              (PPOINTL)&rclDst,
                              NULL,
                              Rop,
                              NULL));

    } else {

        RECTL   rclSrc;


        rclSrc.left   = pptlSrc->x;
        rclSrc.top    = pptlSrc->y;
        rclSrc.right  = rclSrc.left + (rclDst.right - rclDst.left);
        rclSrc.bottom = rclSrc.top  + (rclDst.bottom - rclDst.top);

        //
        // Validate that we only BLT the available source size
        //

        if ((rclSrc.right > psoSrc->sizlBitmap.cx) ||
            (rclSrc.bottom > psoSrc->sizlBitmap.cy)) {

            PLOTWARN(("DrvCopyBits: Engine passed SOURCE != DEST size, CLIP IT"));

            rclSrc.right  = psoSrc->sizlBitmap.cx;
            rclSrc.bottom = psoSrc->sizlBitmap.cy;

            rclDst.right  = (LONG)(rclSrc.right - rclSrc.left + rclDst.left);
            rclDst.bottom = (LONG)(rclSrc.bottom - rclSrc.top + rclDst.top);
        }

        PLOTDBG(DBG_COPYBITS, ("DrvCopyBits CALLING BandingHTBlt()"));

        return(BandingHTBlt(pPDev,          // pPDev
                            psoDst,         // psoDst
                            psoSrc,         // psoSrc
                            NULL,           // psoMask,
                            pco,            // pco
                            pxlo,           // pxlo
                            NULL,           // pca
                            NULL,           // pptlHTOrg
                            &rclDst,        // prclDst
                            &rclSrc,        // prclSrc
                            NULL,           // pptlMask
                            0xCCCC,         // HTRop3
                            FALSE));        // InvertMask
    }

}




BOOL
DrvStretchBlt(
    SURFOBJ         *psoDst,
    SURFOBJ         *psoSrc,
    SURFOBJ         *psoMask,
    CLIPOBJ         *pco,
    XLATEOBJ        *pxlo,
    COLORADJUSTMENT *pca,
    POINTL          *pptlBrushOrg,
    PRECTL          prclDst,
    PRECTL          prclSrc,
    PPOINTL         pptlMask,
    ULONG           iMode
    )

/*++

Routine Description:

    This function halftones a source rectangle and optionally can invert
    the source and handle a mask.

    It also provides, StretchBlt capabilities between Device managed and
    GDI managed surfaces. We want the driver to be able to write on GDI
    managed bitmaps, especially when doing halftoning. This allows the same
    algorithm to be used for both GDI and device surfaces.

    This function is optional in drivers, it can return FALSE if it does
    not know how to handle the work.

Arguments:

    psoDst      - This is a pointer to a SURFOBJ.    It identifies the surface
                  on which to draw.

    psoSrc      - This SURFOBJ defines the source for the Blt operation.  The
                  driver must call GDI Services to find out if this is a device
                  managed  surface or a bitmap managed by GDI.

    psoMask     - This optional surface provides a mask for the source.  It is
                  defined by a logic map, i.e. a bitmap with one bit per pel.

                  The mask is used to limit the area of the source that is
                  copied.  When a mask is provided there is an implicit rop4 of
                  0xCCAA, which means that the source should be copied wherever
                  the mask is 1, but the destination should be left alone
                  wherever the mask is 0.

                  When this argument is NULL there is an implicit rop4 of
                  0xCCCC, which means that the source should be copied
                  everywhere in the source rectangle.

                  The mask will always be large enough to contain the source
                  rectangle, tiling does not need to be done.

    pco         - This is a pointer to a CLIPOBJ.    GDI Services are provided
                  to enumerate the clipping region as a set of rectangles or
                  trapezoids. This limits the area of the destination that will
                  be modified.

                  Whenever possible, GDI will simplify the clipping involved.
                  However, unlike DrvBitBlt, DrvStretchBlt may be called with a
                  single clipping rectangle.  This is necessary to prevent
                  roundoff errors in clipping the output.

    pxlo        - This is a pointer to an XLATEOBJ.  It tells how color indices
                  should be translated between the source and target surfaces.

                  The XLATEOBJ can also be queried to find the RGB color for
                  any source index.  A high quality stretching Blt will need
                  to interpolate colors in some cases.

    pca         - This is a pointer to COLORADJUSTMENT structure, if NULL it
                  specified that appiclation did not set any color adjustment
                  for this DC, and is up to the driver to provide default
                  adjustment

    pptlBrushOrg- Pointer to the POINT structure to specified the location
                  where halftone brush should alignment to, if this pointer is
                  NULL then it assume that (0, 0) as origin of the brush

    prclDst     - This RECTL defines the area in the coordinate system of the
                  destination surface that can be modified.

                  The rectangle is defined by two points.    These points are
                  not well ordered, i.e. the coordinates of the second point
                  are not necessarily larger than those of the first point.
                  The rectangle they describe does not include the lower and
                  right edges.  DrvStretchBlt will never be called with an
                  empty destination rectangle.

                  DrvStretchBlt can do inversions in both x and y, this happens
                  when the destination rectangle is not well ordered.

    prclSrc     - This RECTL defines the area in the coordinate system of the
                  source surface that will be copied.  The rectangle is defined
                  by two points, and will map onto the rectangle defined by
                  prclDst.  The points of the source rectangle are well
                  ordered.  DrvStretch will never be given an empty source
                  rectangle.

                  Note that the mapping to be done is defined by prclSrc and
                  prclDsst. To be precise, the given points in prclDst and
                  prclSrc lie on integer coordinates, which we consider to
                  correspond to pel centers.  A rectangle defined by two such
                  points should be considered a geometric rectangle with two
                  vertices whose coordinates are the given points, but with 0.5
                  subtracted from each coordinate.  (The POINTLs should just be
                  considered a shorthand notation for specifying these
                  fractional coordinate vertices.)  Note thate the edges of any
                  such rectangle never intersect a pel, but go around a set of
                  pels.  Note also that the pels that are inside the rectangle
                  are just what you would expect for a "bottom-right exclusive"
                  rectangle.  The mapping to be done by DrvStretchBlt will map
                  the geometric source rectangle exactly onto the geometric
                  destination rectangle.

    pptlMask    - This POINTL specifies which pel in the given mask corresponds
                  to the upper left pel in the source rectangle.  Ignore this
                  argument if there is no given mask.


    iMode       - This defines how source pels should be combined to get output
                  pels. The methods SB_OR, SB_AND, and SB_IGNORE are all simple
                  and fast.  They provide compatibility for old applications,
                  but don't produce the best looking results for color surfaces.


                  SB_OR         On a shrinking Blt the pels should be combined
                                with an OR operation.  On a stretching Blt pels
                                should be replicated.

                  SB_AND        On a shrinking Blt the pels should be combined
                                with an AND operation.  On a stretching Blt
                                pels should be replicated.

                  SB_IGNORE     On a shrinking Blt enough pels should be
                                ignored so that pels don't need to be combined.
                                On a stretching Blt pels should be replicated.

                  SB_BLEND      RGB colors of output pels should be a linear
                                blending of the RGB colors of the pels that get
                                mapped onto them.

                  SB_HALFTONE   The driver may use groups of pels in the output
                                surface to best approximate the color or gray
                                level of the input.

                  For this function we will ignored this parameter and always
                  output the SB_HALFTONE result

Return Value:


    TRUE if sucessful FALSE if failed

Author:

    11-Feb-1993 Thu 19:52:29 created  -by-  DC

    06-Jan-1994 Thu 04:34:37 updated  -by-  DC
        Make sure we do not do this for pen plotter

    23-Feb-1994 Wed 11:02:45 updated  -by-  DC
        Re-write and take banding the bitmap into account

    01-Mar-1994 Tue 10:55:03 updated  -by-  DC
        spawan out to a separate function and Make call to BandingHTBlt()

Revision History:


--*/

{
    PPDEV   pPDev;

    UNREFERENCED_PARAMETER(iMode);          // we always do HALFTONE

    //
    // get the pointer to our DEVDATA structure and make sure it is ours.
    //

    if (!(pPDev = SURFOBJ_GETPDEV(psoDst))) {

        PLOTERR(("DrvStretchBlt: invalid pPDev"));
        return(FALSE);
    }

    return(BandingHTBlt(pPDev,          // pPDev
                        psoDst,         // psoDst
                        psoSrc,         // psoSrc
                        psoMask,        // psoMask,
                        pco,            // pco
                        pxlo,           // pxlo
                        pca,            // pca
                        pptlBrushOrg,   // pptlHTOrg
                        prclDst,        // prclDst
                        prclSrc,        // prclSrc
                        pptlMask,       // pptlMask
                        0x88CC,         // HTRo3
                        FALSE));        // InvertMask
}





BOOL
DrvBitBlt(
    SURFOBJ     *psoDst,
    SURFOBJ     *psoSrc,
    SURFOBJ     *psoMask,
    CLIPOBJ     *pco,
    XLATEOBJ    *pxlo,
    PRECTL      prclDst,
    PPOINTL     pptlSrc,
    PPOINTL     pptlMask,
    BRUSHOBJ    *pbo,
    PPOINTL     pptlBrushOrg,
    ROP4        Rop4
    )

/*++

Routine Description:

    Provides general Blt capabilities to device managed surfaces.  The Blt
    might be from an Engine managed bitmap.  In that case, the bitmap is
    one of the standard format bitmaps.    The driver will never be asked
    to Blt to an Engine managed surface.

    This function is required if any drawing is done to device managed
    surfaces.  The basic functionality required is:

      1    Blt from any standard format bitmap or device surface to a device
           surface,

      2    with any ROP,

      3    optionally masked,

      4    with color index translation,

      5    with arbitrary clipping.

    Engine services allow the clipping to be reduced to a series of clip
    rectangles.    A translation vector is provided to assist in color index
    translation for palettes.

    This is a large and complex function.  It represents most of the work
    in writing a driver for a raster display device that does not have
    a standard format frame buffer.  The Microsoft VGA driver provides
    example code that supports the basic function completely for a planar
    device.

    NOTE: Plotters do not support copying from device bitmaps. Nor can they
          perform raster operations on bitmaps.  Therefore, it is not possible
          to support ROPs which interact with the destination (ie inverting
          the destination).  The driver will do its best to map these ROPs
          into ROPs utilizing functions on the Source or Pattern.

          This driver supports the bitblt cases indicated below:

          Device -> Memory  No
          Device -> Device  No
          Memory -> Memory  No
          Memory -> Device  Yes
          Brush  -> Memory  No
          Brush  -> Device  Yes


Arguments:


    psoDest         - This is a pointer to a device managed SURFOBJ.  It
                      identifies the surface on which to draw.

    psoSrc          - If the rop requires it, this SURFOBJ defines the source
                      for the Blt operation.  The driver must call the Engine
                      Services to find out if this is a device managed surface
                      or a bitmap managed by the Engine.

    psoMask         - This optional surface provides another input for the
                      Rop4.  It is defined by a logic map, i.e. a bitmap with
                      one bit per pel.  The mask is typically used to limit the
                      area of the destination that should be modified.  This
                      masking is accomplished by a Rop4 whose lower byte is AA,
                      leaving the destination unaffected when the mask is 0.

                      This mask, like a brush, may be of any size and is
                      assumed to tile to cover the destination of the Blt.  If
                      this argument is NULL and a mask is required by the Rop4,
                      the implicit mask in the brush will be used.

    pco             - This is a pointer to a CLIPOBJ.    Engine Services are
                      provided to enumerate the clipping region as a set of
                      rectangles or trapezoids.  This limits the area of the
                      destination that will be modified.  Whenever possible,
                      the Graphics Engine will simplify the clipping involved.
                      For example, BitBlt will never be called with exactly one
                      clipping rectangle.    The Engine will have clipped the
                      destination rectangle before calling, so that no clipping
                      needs to be considered.

    pxlo            - This is a pointer to an XLATEOBJ.  It tells how color
                      indices should be translated between the source and
                      target surfaces.

                      If the source surface is palette managed, then its colors
                      are represented by indices into a list of RGB colors.
                      In this case, the XLATEOBJ can be queried to get a
                      translate vector that will allow the device driver to
                      quickly translate any source index into a color index for
                      the destination.

                      The situation is more complicated when the source is, for
                      example, RGB but the destination is palette managed.  In
                      this case a closest match to each source RGB must be
                      found in the destination palette.  The XLATEOBJ provides
                      a service routine to do this matching.  (The device
                      driver is allowed to do the matching itself when the
                      target palette is the default device palette.)

    prclDst         - This RECTL defines the area in the coordinate system of
                      the destination surface that will be modified.  The
                      rectangle is defined as two points, upper left and lower
                      right.  The lower and right edges of this rectangle are
                      not part of the Blt, i.e. the rectangle is lower right
                      exclusive.  vBitBlt will never be called with an empty
                      destination rectangle, and the two points of the
                      rectangle will always be well ordered.

    pptlSrc         - This POINTL defines the upper left corner of the source
                      rectangle, if there is a source.  Ignore this argument
                      if there is no source.

    pptlMask        - This POINTL defines which pel in the mask corresponds to
                      the upper left corner of the destination rectangle.
                      Ignore this argument if no mask is provided with psoMask.

    pdbrush         - This is a pointer to the device's realization of the
                      brush to be used in the Blt.  The pattern for the Blt is
                      defined by this brush.  Ignore this argument if the Rop4
                      does not require a pattern.

    pptlBrushOrg    - This is a pointer to a POINTL which defines the origin of
                      the brush.  The upper left pel of the brush is aligned
                      here and the brush repeats according to its dimensions.
                      Ignore this argument if the Rop4 does not require a
                      pattern.

    Rop4            - This raster operation defines how the mask, pattern,
                      source, and destination pels should be combined to
                      determine an output pel to be written on the destination
                      surface.

                      This is a quaternary raster operation, which is a natural
                      extension of the usual ternary rop3.  There are 16
                      relevant bits in the Rop4,  these are like the 8 defining
                      bits of a rop3.  (We ignore the other bits of the rop3,
                      which are redundant.)    The simplest way to implement a
                      Rop4 is to consider its two bytes separately.  The lower
                      byte specifies a rop3 that should be computed wherever
                      the mask is 0.  The high byte specifies a rop3 that
                      should then be computed and applied wherever the mask
                      is 1.


Return Value:

    TRUE if sucessfule FALSE otherwise


Author:

    04-Dec-1990     -by-     KS
        Wrote it.

    27-Mar-1992 Fri 00:08:43 updated  -by-  DC
        1) Remove 'pco' parameter and replaced it with prclClipBound parameter,
           since pco is never referenced, prclClipBound is used for the
           halftone.
        2) Add another parameter to do NOTSRCCOPY

    11-Feb-1993 Thu 21:29:15 updated  -by-  DC
        Modified so that it call DrvStretchBlt(HALFTONE) when it can.

    18-Dec-1993 Sat 09:08:16 updated  -by-  DC
        Clean up for plotter driver

    06-Jan-1994 Thu 04:34:37 updated  -by-  DC
        Make sure we do not do this for pen plotter

    15-Jan-1994 Sat 04:02:22 updated  -by-  DC
        Re-write

    17-Mar-1994 Thu 22:36:42 updated  -by-  DC
        Changed it so we only use PATTERN=psoMask if the ROP4 do not required
        PATTERNs and a MASK is required


Revision History:


--*/

{
    PPDEV       pPDev;
    DWORD       Rop3FG;
    DWORD       Rop3BG;
    RECTL       rclSrc;
    RECTL       rclPat;
    UINT        i;
    BOOL        Ok = TRUE;


    //
    // if the source is NULL it must be a fill, so call the fill code,
    //

    PLOTDBG(DBG_BITBLT, ("DrvBitBlt: ROP4  = %08lx", Rop4));

    PLOTASSERT(1, "DrvBitBlt: Invalid ROP code = %08lx",
                                            (Rop4 & 0xffff0000) == 0, Rop4);

    //
    // get the pointer to our DEVDATA structure and make sure it is ours.
    //

    if (!(pPDev = SURFOBJ_GETPDEV(psoDst))) {

        PLOTERR(("DrvBithBlt: invalid pPDev"));
        return(FALSE);
    }

    if (IS_RASTER(pPDev)) {

        i = (UINT)pPDev->pPlotGPC->ROPLevel;

    } else {

        PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Pen Plotter: TRY ROP_LEVEL_0"));

        i = ROP_LEVEL_0;
    }

    Rop3BG = (DWORD)ROP4_BG_ROP(Rop4);
    Rop3FG = (DWORD)ROP4_FG_ROP(Rop4);

    switch (i) {

    case ROP_LEVEL_0:

        //
        // For RopLevel 0, or Pen Plotter we will only process the pattern
        // which is compatible with our device
        //

        if (ROP3_NEED_PAT(Rop3FG)) {

            PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Device ROP_LEVEL_0, NEED PAT"));

            if (GetColor(pPDev, pbo, NULL, NULL, Rop3FG) <= 0) {

                PLOTWARN(("DrvBitBlt: NOT Device Comptible PAT"));
                return(TRUE);
            }

            PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Device ROP_LEVEL_0, TRY COMPATIBLE PAT"));

        } else {

            PLOTWARN(("DrvBitBlt: Device ROP_LEVEL_0, CANNOT Do RASTER BLT"));
            return(TRUE);
        }

        //
        // Make it PAT Copy
        //

        Rop4   = 0xF0F0;
        Rop3BG =
        Rop3FG = 0xF0;

        break;

    case ROP_LEVEL_1:

        //
        // Can only do ROP1 SRC COPY/NOT SRCCOPY
        //

        PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Device ROP_LEVEL_1, Rop4=%08lx", Rop4));

        switch(Rop4 = ROP4_FG_ROP(Rop4)) {

        case 0xAA:
        case 0xCC:
        case 0x33:

            break;

        default:

            PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Make ROP4 = 0xCC"));

            Rop4 = 0xCC;
            break;
        }

        Rop4 |= (Rop4 << 8);
        break;


    case ROP_LEVEL_2:
    case ROP_LEVEL_3:

        break;

    default:

        PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Device RopLevel=%ld, do nothing",
                                (DWORD)pPDev->pPlotGPC->ROPLevel));
        return(TRUE);
    }

    //
    // Do DrvStrethcBlt (HALFTONE) first if we can. Since there is no way
    // for us to read back the device surface we can only try our best
    // to simulate the requested drawing operation.
    //

    if (pptlSrc) {

        rclSrc.left = pptlSrc->x;
        rclSrc.top  = pptlSrc->y;

    } else {

        rclSrc.left =
        rclSrc.top  = 0;
    }

    rclSrc.right  = rclSrc.left + (prclDst->right - prclDst->left);
    rclSrc.bottom = rclSrc.top  + (prclDst->bottom - prclDst->top);

    switch (Rop4) {

    case 0xAAAA:    //  D

        return(TRUE);

    case 0xAACC:
    case 0xCCAA:
    case 0xAA33:
    case 0x33AA:

        //
        // If we have ~S (NOT SOURCE) then we want to make the non-mask area
        // black , we do this using S^D (0x66).
        //

        if ((Rop4 == 0xAA33) || (Rop4 == 0x33AA)) {

            Rop4 = 0x6666;

        } else {

            Rop4 = 0x8888;
        }

        return(BandingHTBlt(pPDev,                      // pPDev
                            psoDst,                     // psoDst
                            psoSrc,                     // psoSrc
                            psoMask,                    // psoMask,
                            pco,                        // pco
                            pxlo,                       // pxlo
                            NULL,                       // pca
                            pptlBrushOrg,               // pptlHTOrg
                            prclDst,                    // prclDst
                            &rclSrc,                    // prclSrc
                            pptlMask,                   // pptlMask
                            (WORD)Rop4,                 // HTRo3
                            Rop3FG == 0xAA));          // InvertMask

    case 0x3333:    // ~S
    case 0xCCCC:    //  S

        //
        // We will output the bitmap directly to the surface if the following
        // conditions are all met
        //
        //  1. SRC = STYPE_BITMAP
        //  2. Format is compatible with HT
        //

        if ((psoSrc->iType == STYPE_BITMAP) &&
            (IsHTCompatibleSurfObj(pPDev,
                                   psoSrc,
                                   pxlo,
                                   ISHTF_ALTFMT     |
                                    ISHTF_HTXB      |
                                    ISHTF_DSTPRIM_OK))) {

            return(OutputHTBitmap(pPDev,
                                  psoSrc,
                                  pco,
                                  (PPOINTL)prclDst,
                                  &rclSrc,
                                  Rop4 & 0xFF,
                                  NULL));

        } else {

            //
            // Call BandingHTBlt(Rop4) to do the job
            //

            return(BandingHTBlt(pPDev,                  // pPDev
                                psoDst,                 // psoDst
                                psoSrc,                 // psoSrc
                                NULL,                   // psoMask,
                                pco,                    // pco
                                pxlo,                   // pxlo
                                NULL,                   // pca
                                pptlBrushOrg,           // pptlHTOrg
                                prclDst,                // prclDst
                                &rclSrc,                // prclSrc
                                NULL,                   // pptlMask
                                (WORD)Rop4,             // HTRo3
                                FALSE));                // InvertMask
        }

        break;

    default:

        if ((Rop3BG != Rop3FG)          &&          // NEED MASK?
            (!ROP3_NEED_DST(Rop3BG))    &&
            (!ROP3_NEED_DST(Rop3FG))) {

            PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Not required DEST, Calling  EngBitBlt()"));

            if (!(Ok = EngBitBlt(psoDst,            // psoDst
                                 psoSrc,            // psoSrc
                                 psoMask,           // psoMask
                                 pco,               // pco
                                 pxlo,              // pxlo
                                 prclDst,           // prclDst
                                 pptlSrc,           // pptlSrc
                                 pptlMask,          // pptlMask
                                 pbo,               // pbo
                                 pptlBrushOrg,      // pptlBrushOrg ZERO
                                 Rop4))) {

                PLOTERR(("DrvBitBlt: EngBitBlt(%04lx) FAILED", Rop4));
            }

        } else {

            CLONESO CloneSO[CSI_TOTAL];

            //
            // Clear all the clone surface memory
            //

            ZeroMemory(CloneSO, sizeof(CloneSO));

            //
            // We will using psoMask as Pattern ONLY IF
            //
            //  1. ROP4 required a MASK
            //  2. Forground NOT required a PATTERN
            //  3. Background NOT reauired a PATTERN
            //

            if ((Rop3BG != Rop3FG)          &&
                (!ROP3_NEED_PAT(Rop3BG))    &&
                (!ROP3_NEED_PAT(Rop3FG))) {

                //
                // We will condense the ROP4 to a ROP3 and use the psoMAsk
                // as the Pattern. We must make sure the pptlBrushOrg is NULL
                // so we DON'T align rclPat on the destination.
                //

                Rop3FG = (Rop3BG & 0xF0) | (Rop3FG & 0x0F);
                Rop3BG = 0xAA;

                PLOTDBG(DBG_BITBLT, ("DrvBitBlt: Rop4=%04lx, Pattern=psoMask=%08lx, Rop3=%02lx/%02lx",
                                                Rop4, psoMask, Rop3BG, Rop3FG));

                rclPat.left   = pptlMask->x;
                rclPat.top    = pptlMask->y;
                rclPat.right  = rclPat.left + (rclSrc.right - rclSrc.left);
                rclPat.bottom = rclPat.top + (rclSrc.bottom - rclSrc.top);
                pptlBrushOrg  = NULL;

            } else {

                //
                // We will NOT do the background operation for now
                //

                if (Rop3FG == 0xAA) {

                    Rop3FG = Rop3BG;

                } else {

                    Rop3BG = Rop3FG;
                }

                //
                // We have a real pattern so make sure we aligned rclPat on
                // the destination correctly by passing a valid pptlBrushOrg,
                // NOTE: The rclPat will be setup by CloneBitBltSURFOBJ()
                //

                psoMask = NULL;

                if (!pptlBrushOrg) {

                    pptlBrushOrg = (PPOINTL)&ptlZeroOrigin;
                }
            }

            if (!(Ok = CloneBitBltSURFOBJ(pPDev,
                                          psoDst,
                                          psoSrc,
                                          psoMask,
                                          pxlo,
                                          prclDst,
                                          &rclSrc,
                                          &rclPat,
                                          pbo,
                                          CloneSO,
                                          Rop3BG,
                                          Rop3FG))) {

                PLOTDBG(DBG_BITBLT, ("DrvBitBlt: CloneBitbltSURFOBJ: failed"));
            }

            if (CloneSO[CSI_SRC].pso) {

                psoSrc = CloneSO[CSI_SRC].pso;
                pxlo   = NULL;
            }

            //
            // Only do background if BG != FG, and BG != DEST
            //

            if ((Ok) && (Rop3BG != Rop3FG) && (Rop3BG != 0xAA)) {

                if (!(Ok = DoRop3(pPDev,
                                  psoDst,
                                  psoSrc,
                                  CloneSO[CSI_PAT].pso,
                                  CloneSO[CSI_TMP].pso,
                                  pco,
                                  pxlo,
                                  prclDst,
                                  &rclSrc,
                                  &rclPat,
                                  pptlBrushOrg,
                                  pbo,
                                  Rop3BG))) {

                    PLOTERR(("DrvBitBlt(Rop3BG=%02lx) FAILED", Rop3BG));
                }
            }

            if ((Ok) && (Rop3FG != 0xAA)) {

                if (!(Ok = DoRop3(pPDev,
                                  psoDst,
                                  psoSrc,
                                  CloneSO[CSI_PAT].pso,
                                  CloneSO[CSI_TMP].pso,
                                  pco,
                                  pxlo,
                                  prclDst,
                                  &rclSrc,
                                  &rclPat,
                                  pptlBrushOrg,
                                  pbo,
                                  Rop3FG))) {

                    PLOTERR(("DrvBitBlt(Rop3FG=%02lx) FAILED", Rop3FG));
                }
            }

            //
            // Release all cloned objects
            //

            for (i = 0; i < CSI_TOTAL; i++) {

                if (CloneSO[i].pso) {

                    PLOTDBG(DBG_CSI, ("DrvBitBlt: EngUnlockSuface(%hs)", pCSIName[i]));

                    EngUnlockSurface(CloneSO[i].pso);
                }

                if (CloneSO[i].hBmp) {

                    PLOTDBG(DBG_CSI, ("DrvBitBlt: EngDeleteSurface(%hs)", pCSIName[i]));

                    if (!EngDeleteSurface((HSURF)CloneSO[i].hBmp)) {

                        PLOTERR(("PLOTTER: DrvBitBlt, EngDeleteSurface(%ld:%p) FAILED",
                                            (DWORD)i, (DWORD_PTR)CloneSO[i].hBmp));
                    }
                }
            }
        }

        break;
    }

    return(Ok);
}



ULONG
DrvDitherColor(
    DHPDEV  dhpdev,
    ULONG   iMode,
    ULONG   rgbColor,
    ULONG  *pulDither
    )

/*++

Routine Description:

    This is the hooked brush creation function, it asks CreateHalftoneBrush()
    to do the actual work (By returning DCR_HALFTONE).


Arguments:

    dhpdev      - DHPDEV passed, it is our pDEV

    iMode       - Not used

    rgbColor    - Solid rgb color to be used

    pulDither   - buffer to put the halftone brush.

Return Value:

    BOOLEAN

Author:

    02-May-1995 Tue 10:34:10 created  -by-  DC


Revision History:



--*/

{
    UNREFERENCED_PARAMETER(dhpdev);
    UNREFERENCED_PARAMETER(iMode);
    UNREFERENCED_PARAMETER(rgbColor);
    UNREFERENCED_PARAMETER(pulDither);

    return(DCR_HALFTONE);
}

