/******************************Module*Header**********************************\
*
*                           *******************
*                           * GDI SAMPLE CODE *
*                           *******************
*
* Module Name: patnfill.c
*
* Contains all the pattern fill routines
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\*****************************************************************************/
#include "precomp.h"
#include "gdi.h"
#include "directx.h"

//-----------------------------Note--------------------------------------------
//
// A Note on brushes
//
// Out cached brushes are 64x64.  Here is the reason.  The minimum brush
// size that we can use as a pattern is 32.
//
// Now, we need to be able to offset the pattern when rendering in x and y
// by as much as 7 pixels in either direction.  The P2
// hardware does not have a simple x/Y pattern offset mechanism.  Instead
// we are forced to offset the origin by offsetting the base address of the
// pattern.  This requires that we store in memory a pattern that is 
// 39 pixels wide.  However, the stride still needs to be acceptable to the
// texture address generation hardware.  The next valid stride is 64.
//
// That's why we have 64x64 pattern brushes in our cache.
//
// Note also that we over do it when caching duplicating the brush to fill
// up the entire 64x64 even though we only use 39x39.  We might change
// this in the near future.
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//
// VOID vMonoOffset(GFNPB* ppb)
//
// Update the offset to be used in the area stipple unit. We do this for a
// mono brush which is realized in the hardware but whose alignment has simply
// changed. This avoids a full scale realization.
//
// Argumentes needed from function block (GFNPB)
//
//  ppdev-------PPDev
//  prbrush-----Pointer to the RBrush structure
//  pptlBrush---Pointer to pointer brush structure
//
//-----------------------------------------------------------------------------
VOID
vMonoOffset(GFNPB* ppb)
{
    PPDev   ppdev = ppb->ppdev;
    
    DWORD   dwMode;
    POINTL* pptlBrush = ppb->pptlBrush;
    RBrush* prb = ppb->prbrush;

    PERMEDIA_DECL;

    DBG_GDI((6, "vMonoOffset started"));

    //
    // Construct the AreaStippleMode value. It contains the pattern size,
    // the offset for the brush origin and the enable bit. Remember the
    // offset so we can later check if it changes and update the hardware.
    // Remember the mode so we can do a mirrored stipple easily.
    //
    prb->ptlBrushOrg.x = pptlBrush->x;
    prb->ptlBrushOrg.y = pptlBrush->y;
    
    dwMode = __PERMEDIA_ENABLE
           | AREA_STIPPLE_XSEL(__PERMEDIA_AREA_STIPPLE_32_PIXEL_PATTERN)
           | AREA_STIPPLE_YSEL(__PERMEDIA_AREA_STIPPLE_8_PIXEL_PATTERN)
           | AREA_STIPPLE_MIRROR_X
           | AREA_STIPPLE_XOFF(8 - (prb->ptlBrushOrg.x & 7))
           | AREA_STIPPLE_YOFF(8 - (prb->ptlBrushOrg.y & 7));

    prb->areaStippleMode = dwMode;

    DBG_GDI((7, "setting new area stipple offset to %d, %d",
             8 - (prb->ptlBrushOrg.x & 7), 8 - (prb->ptlBrushOrg.y & 7)));

    ULONG* pBuffer;

    InputBufferReserve(ppdev, 2, &pBuffer);

    pBuffer[0] = __Permedia2TagAreaStippleMode;
    pBuffer[1] = dwMode;

    pBuffer += 2;

    InputBufferCommit(ppdev, pBuffer);

}// vMonoOffset()

//-----------------------------------------------------------------------------
//
// VOID vPatRealize(GFNPB* ppb)
//
// This routine transfers an 8x8 pattern to off-screen display memory, and
// duplicates it to make a 32x32 cached realization which is then used by
// vPatFill.
//
// Argumentes needed from function block (GFNPB)
//
//  ppdev-------PPDev
//  prbrush-----Pointer to the RBrush structure
//
//-----------------------------------------------------------------------------
VOID
vPatRealize(GFNPB* ppb)
{
    PDev*       ppdev = ppb->ppdev;
    RBrush*     prb = ppb->prbrush; // Points to brush realization structure
    BrushEntry* pbe = prb->pbe;
    
    BYTE*       pcSrc;
    LONG        lNextCachedBrush;
    LONG        lTemp;
    LONG        lPelSize;
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;

//    VALIDATE_GDI_CONTEXT;

    DBG_GDI((6, "vPatRealize started"));

    if ( (pbe == NULL) || (pbe->prbVerify != prb) )
    {
        //
        // Mono brushes are realized into the area stipple unit. For this we
        // have a set of special BRUSHENTRYs, one for each board.
        //
        if ( prb->fl & RBRUSH_2COLOR )
        {
            //
            // 1 BPP patten
            //
            DBG_GDI((7, "loading mono brush into cache"));
            pbe = &ppdev->abeMono;
            pbe->prbVerify = prb;
            prb->pbe = pbe;
        }
        else
        {
            //
            // We have to allocate a new off-screen cache brush entry for
            // the brush
            //
            lNextCachedBrush = ppdev->lNextCachedBrush; // Get the next index
            pbe = &ppdev->abe[lNextCachedBrush];        // Get the brush entry

            //
            // Check if this index is out of the total brush stamps cached
            // If yes, rotate to the 1st one
            //
            lNextCachedBrush++;
            if ( lNextCachedBrush >= ppdev->cBrushCache )
            {
                lNextCachedBrush = 0;
            }

            //
            // Reset the next brush to be allocated
            //
            ppdev->lNextCachedBrush = lNextCachedBrush;

            //
            // Update our links:
            //
            pbe->prbVerify = prb;
            prb->pbe = pbe;
            DBG_GDI((7, "new cache entry allocated for color brush"));
        }// Get cached brush entry depends on its color depth
    }// If the brush is not cached

    //
    // We're going to load mono patterns into the area stipple and set the
    // start offset to the brush origin. WARNING: we assume that we are
    // running little endian. I believe this is always true for NT.
    //
    if ( prb->fl & RBRUSH_2COLOR )
    {
        //
        // 1 BPP patten
        //
        DWORD*  pdwSrc = &prb->aulPattern[0];

        //
        // This function loads the stipple offset into the hardware. We also
        // call this function on its own if the brush is realized but its
        // offset changes. In that case we don't have to go through a complete
        // realize again.
        //
        ppb->prbrush = prb;

        (*ppdev->pgfnMonoOffset)(ppb);
        
        DBG_GDI((7, "area stipple pattern:"));
        
        InputBufferReserve(ppdev, 16, &pBuffer);

        for ( lTemp = 0; lTemp < 8; ++lTemp, ++pdwSrc )
        {
            pBuffer[0] = __Permedia2TagAreaStipplePattern0 + lTemp;
            pBuffer[1] = *pdwSrc;
            pBuffer += 2;
        }

        InputBufferCommit(ppdev, pBuffer);
        
        DBG_GDI((7, "area stipple downloaded. vPatRealize done"));

        return;
    }// 1 BPP case

    lPelSize = ppdev->cPelSize;
    pcSrc = (BYTE*)&prb->aulPattern[0];        // Copy from brush buffer


    InputBufferReserve(ppdev, 12 + 65, &pBuffer);

    pBuffer[0] = __Permedia2TagFBWindowBase;
    pBuffer[1] =  pbe->ulPixelOffset;
    pBuffer[2] = __Permedia2TagFBReadMode;
    pBuffer[3] =  PM_FBREADMODE_PARTIAL(ppdev->ulBrushPackedPP);
    pBuffer[4] = __Permedia2TagLogicalOpMode;
    pBuffer[5] =   __PERMEDIA_DISABLE;

    pBuffer[6] = __Permedia2TagRectangleOrigin;
    pBuffer[7] = 0;
    pBuffer[8] = __Permedia2TagRectangleSize;
    pBuffer[9] = (8 << 16) | 8;
	pBuffer[10] = __Permedia2TagRender;
    pBuffer[11] = __RENDER_RECTANGLE_PRIMITIVE
                | __RENDER_SYNC_ON_HOST_DATA
                | __RENDER_INCREASE_Y
                | __RENDER_INCREASE_X;

	pBuffer += 12;

    *pBuffer++ = (63 << 16) | __Permedia2TagColor;
    
    switch( lPelSize )
    {
        case 0:
            for ( lTemp = 0; lTemp < 64; ++lTemp )
            {
                *pBuffer++ =  pcSrc[lTemp];
            }
            break;

        case 1:    
            for ( lTemp = 0; lTemp < 64; ++lTemp )
            {
                *pBuffer++ =  ((USHORT *) pcSrc)[lTemp];
            }
            break;

        case 2:
            for ( lTemp = 0; lTemp < 64; ++lTemp )
            {
                *pBuffer++ =  ((ULONG *) pcSrc)[lTemp];
            }
            break;
    }

    InputBufferCommit(ppdev, pBuffer);


    // 
    // 012   We now have an 8x8 colour-expanded copy of
    //  the pattern sitting in off-screen memory,
    // 5       represented here by square '0'.
    //        
    //         We're now going to expand the pattern to
    //         64x64 by repeatedly copying larger rectangles
    //         in the indicated order, and doing a 'rolling'
    //         blt to copy vertically.
    //        
    // 

    InputBufferReserve(ppdev, 36, &pBuffer);

    pBuffer[0] = __Permedia2TagFBReadMode;
    pBuffer[1] = PM_FBREADMODE_PARTIAL(ppdev->ulBrushPackedPP)
               | __FB_READ_SOURCE;
    pBuffer[2] = __Permedia2TagStartXDom;
    pBuffer[3] =  INTtoFIXED(8);
    pBuffer[4] = __Permedia2TagStartXSub;
    pBuffer[5] =  INTtoFIXED(16);
    pBuffer[6] = __Permedia2TagFBSourceOffset;
    pBuffer[7] =  -8;
    pBuffer[8] = __Permedia2TagRender;
    pBuffer[9] =  __RENDER_TRAPEZOID_PRIMITIVE;

    pBuffer[10] = __Permedia2TagStartXDom;
    pBuffer[11] =  INTtoFIXED(16);
    pBuffer[12] = __Permedia2TagStartXSub;
    pBuffer[13] =  INTtoFIXED(32);
    pBuffer[14] = __Permedia2TagFBSourceOffset;
    pBuffer[15] =  -16;
    pBuffer[16] = __Permedia2TagRender;
    pBuffer[17] =  __RENDER_TRAPEZOID_PRIMITIVE;
    
    pBuffer[18] = __Permedia2TagStartXDom;
    pBuffer[19] =  INTtoFIXED(32);
    pBuffer[20] = __Permedia2TagStartXSub;
    pBuffer[21] =  INTtoFIXED(64);
    pBuffer[22] = __Permedia2TagFBSourceOffset;
    pBuffer[23] =  -32;
    pBuffer[24] = __Permedia2TagRender;
    pBuffer[25] =  __RENDER_TRAPEZOID_PRIMITIVE;
    
    //
    // Now rolling copy downward.
    //
    pBuffer[26] = __Permedia2TagStartXDom;
    pBuffer[27] =  INTtoFIXED(0);
    pBuffer[28] = __Permedia2TagStartY;
    pBuffer[29] =  INTtoFIXED(8);
    pBuffer[30] = __Permedia2TagFBSourceOffset;
    pBuffer[31] =  -(CACHED_BRUSH_WIDTH << 3);
    pBuffer[32] = __Permedia2TagCount;
    pBuffer[33] =  CACHED_BRUSH_HEIGHT - 8;
    pBuffer[34] = __Permedia2TagRender;
    pBuffer[35] =  __RENDER_TRAPEZOID_PRIMITIVE;

	pBuffer += 36;

	InputBufferCommit(ppdev, pBuffer);

}// vPatRealize()

//-----------------------------------------------------------------------------
//
// VOID vMonoPatFill(GFNPB* ppb)
//
// Fill a series of rectangles with a monochrome pattern previously loaded
// into the area stipple unit. If bTransparent is false we must do each
// rectangle twice, inverting the stipple pattern in the second go.
//
// Argumentes needed from function block (GFNPB)
//
//  ppdev-------PPDev
//  psurfDst----Destination surface
//  lNumRects---Number of rectangles to fill
//  pRects------Pointer to a list of rectangles information which needed to be
//              filled
//  ucFgRop3----Foreground Logic OP for the fill
//  ucBgRop3----Background Logic OP for the fill
//  prbrush-----Pointer to the RBrush structure
//  pptlBrush---Structure for brush origin
//  
//-----------------------------------------------------------------------------
VOID
vMonoPatFill(GFNPB* ppb)
{
    PPDev           ppdev = ppb->ppdev;
    Surf*           psurf = ppb->psurfDst;

    RBrush*         prb = ppb->prbrush;
    POINTL*         pptlBrush = ppb->pptlBrush;
    BrushEntry*     pbe = prb->pbe;             // Brush entry
    RECTL*          pRect = ppb->pRects;        // List of rectangles to be
                                                // filled in relative
                                                // coordinates
    ULONG*      pBuffer;
    
    DWORD           dwColorMode;
    DWORD           dwColorReg;
    DWORD           dwLogicMode;
    DWORD           dwReadMode;
    LONG            lNumPass;
    LONG            lNumRects;                  // Can't be zero
//    ULONG           ulBgLogicOp = ulRop3ToLogicop(ppb->ucBgRop3);
    ULONG           ulBgLogicOp = ulRop3ToLogicop(ppb->ulRop4 >> 8);
                                                // Not used (unless the brush
                                                // has a mask, in which case it
                                                // is the background mix mode)
//    ULONG           ulFgLogicOp = ulRop3ToLogicop(ppb->ucFgRop3);
    ULONG           ulFgLogicOp = ulRop3ToLogicop(ppb->ulRop4 & 0xFF);
                                                // Hardware mix mode
                                                // (foreground mix mode if
                                                // the brush has a mask)
    ULONG           ulBgColor = prb->ulBackColor;
    ULONG           ulFgColor = prb->ulForeColor;
    ULONG           ulCurrentFillColor;
    ULONG           ulCurrentLogicOp;

    PERMEDIA_DECL;
    
    DBG_GDI((6, "vMonoPatFill called: %d rects. ulRop4 = %x",
             ppb->lNumRects, ppb->ulRop4));
//    DBG_GDI((6, "ulFgLogicOp = 0x%x, ulBgLogicOp = 0x%x",
//             ulFgLogicOp, ulBgLogicOp));

    DBG_GDI((6, "ulFgColor 0x%x, ulBgColor 0x%x", ulFgColor, ulBgColor));

    //
    // If anything has changed with the brush we must re-realize it. If the
    // brush has been kicked out of the area stipple unit we must fully realize
    // it. If only the alignment has changed we can simply update the alignment
    // for the stipple.
    //
    if ( (pbe == NULL) || (pbe->prbVerify != prb) )
    {
        DBG_GDI((7, "full brush realize"));
        (*ppdev->pgfnPatRealize)(ppb);
    }
    else if ( (prb->ptlBrushOrg.x != pptlBrush->x)
            ||(prb->ptlBrushOrg.y != pptlBrush->y) )
    {
        DBG_GDI((7, "changing brush offset"));
        (*ppdev->pgfnMonoOffset)(ppb);
    }

    //
    // We get some common operations which are really noops. we can save
    // lots of time by cutting these out. As this happens a lot for masking
    // operations it's worth doing.
    //
    if ( ((ulFgLogicOp == K_LOGICOP_AND) && (ulFgColor == ppdev->ulWhite))
       ||((ulFgLogicOp == K_LOGICOP_XOR) && (ulFgColor == 0)) )
    {
        DBG_GDI((7, "Set FgLogicOp to NOOP"));
        ulFgLogicOp = K_LOGICOP_NOOP;        
    }

    //
    // Same for background
    //
    if ( ((ulBgLogicOp == K_LOGICOP_AND) && (ulBgColor == ppdev->ulWhite))
       ||((ulBgLogicOp == K_LOGICOP_XOR) && (ulBgColor == 0)) )
    {
        DBG_GDI((7, "Set BgLogicOp to NOOP"));
        ulBgLogicOp = K_LOGICOP_NOOP;
    }

    //
    // Try to do the background as a solid fill. lNumPass starts at 1 rather
    // than 2 because we want to do all comparisons with zero. This is faster.
    // We also do a trick with its value to avoid an extra WAIT_FIFO on the
    // first pass.
    //
    if ( (ulBgLogicOp == K_LOGICOP_COPY)
       &&(ulFgLogicOp == K_LOGICOP_COPY) )
    {
        DBG_GDI((7, "FgLogicOp and BgLogicOp are COPY"));

        //
        // For PatCopy case, we can use solid fill to fill the background first
        // Note: we do not need to set FBWindowBase, it will be set by
        // the solid fill
        //
        ppb->solidColor = ulBgColor;
        (*ppdev->pgfnSolidFill)(ppb);

        //
        // We've done the background so we only want to go round the stipple
        // loop once. So set the lNumPass counter up for only one loop and set
        // the ulCurrentLogicOp and color to the foreground values.
        //
        lNumPass           = 0;
        ulCurrentFillColor = ulFgColor;
        ulCurrentLogicOp   = ulFgLogicOp;

        //
        // Do this here in case the solid fill changed the packing.
        //

// brh not needed
//        P2_DEFAULT_FB_DEPTH;
    }
    else
    {
        //
        // For non-PATCOPY cases, we have to do 2 passes. Fill the background
        // first and then fill the foreground
        //
        lNumPass           = 1;
        ulCurrentFillColor = ulBgColor;
        ulCurrentLogicOp   = ulBgLogicOp;

        //
        // Note: In this case, dxDom, dXSub and dY are initialised to 0, 0,
        // and 1, so we don't need to re-load them here. But we need to set
        // WindowBase here
        //

        InputBufferReserve(ppdev, 2, &pBuffer);

        pBuffer[0] = __Permedia2TagFBWindowBase;
        pBuffer[1] =   psurf->ulPixOffset;

        pBuffer += 2;

        InputBufferCommit(ppdev, pBuffer);

    }// if-else for LOGICOP_COPY case

    //
    // Do 2 passes loop or single loop depends on "lNumPass"
    //
    while ( TRUE )
    {
        if ( ulCurrentLogicOp != K_LOGICOP_NOOP )
        {
            dwReadMode  = psurf->ulPackedPP;

            if ( ulCurrentLogicOp == K_LOGICOP_COPY )
            {
                DBG_GDI((7, "Current logicOP is COPY"));
                dwColorReg  = __Permedia2TagFBWriteData;
                dwColorMode = __PERMEDIA_DISABLE;
                dwLogicMode = __PERMEDIA_CONSTANT_FB_WRITE;
            }
            else
            {
                DBG_GDI((7, "Current logicOP is NOT-COPY"));
                dwColorReg  = __Permedia2TagConstantColor;
                dwColorMode = __COLOR_DDA_FLAT_SHADE;
                dwLogicMode = P2_ENABLED_LOGICALOP(ulCurrentLogicOp);
                dwReadMode |= LogicopReadDest[ulCurrentLogicOp];
            }

            //
            // On the bg fill pass, we have to invert the sense of the
            // download bits. On the first pass, lNumPass == 1; on the second
            // pass, lNumPass == 0, so we get our WAIT_FIFO sums correct!!
            //
            InputBufferReserve(ppdev, 10, &pBuffer);

            if ( lNumPass > 0 )
            {
                pBuffer[0] = __Permedia2TagAreaStippleMode;
                pBuffer[1] = (prb->areaStippleMode
                           | AREA_STIPPLE_INVERT_PAT);
                pBuffer += 2;

            }

            pBuffer[0] = __Permedia2TagColorDDAMode;
            pBuffer[1] =   dwColorMode;
            pBuffer[2] = __Permedia2TagFBReadMode;
            pBuffer[3] =     dwReadMode;
            pBuffer[4] = __Permedia2TagLogicalOpMode;
            pBuffer[5] =  dwLogicMode;

            pBuffer[6] = dwColorReg,
            pBuffer[7] = ulCurrentFillColor;

            pBuffer += 8;

            InputBufferCommit(ppdev, pBuffer);

            //
            // Fill rects one by one
            //
            lNumRects = ppb->lNumRects;

            while ( TRUE )
            {
                DBG_GDI((7, "mono pattern fill to rect (%d,%d) to (%d,%d)",
                        pRect->left,
                        pRect->top,
                        pRect->right,
                        pRect->bottom));

                InputBufferReserve(ppdev, 12, &pBuffer);

                //
                // Render the rectangle
                //
                pBuffer[0] = __Permedia2TagStartXDom;
                pBuffer[1] =  pRect->left << 16;
                pBuffer[2] = __Permedia2TagStartXSub;
                pBuffer[3] =  pRect->right << 16;
                pBuffer[4] = __Permedia2TagStartY;
                pBuffer[5] =  pRect->top << 16;
                pBuffer[6] = __Permedia2TagdY;
                pBuffer[7] =  1 << 16;
                pBuffer[8] = __Permedia2TagCount;
                pBuffer[9] =  pRect->bottom - pRect->top;

                pBuffer[10] = __Permedia2TagRender;
                pBuffer[11] = __RENDER_TRAPEZOID_PRIMITIVE
                           |__RENDER_AREA_STIPPLE_ENABLE;

                pBuffer += 12;

                InputBufferCommit(ppdev, pBuffer);

                if ( --lNumRects == 0 )
                {
                    break;
                }

                pRect++;
            }// loop through all the rectangles

            //
            // Reset our pixel values.
            //
            InputBufferReserve(ppdev, 2, &pBuffer);

            pBuffer[0] = __Permedia2TagLogicalOpMode;
            pBuffer[1] =  __PERMEDIA_DISABLE;

            pBuffer += 2;

            InputBufferCommit(ppdev, pBuffer);

            //
            // We must reset the area stipple mode for the foreground pass. if
            // there's no foreground pass we must reset it anyway.
            //
            if ( lNumPass > 0 )
            {
                InputBufferReserve(ppdev, 2, &pBuffer);

                pBuffer[0] = __Permedia2TagAreaStippleMode;
                pBuffer[1] =  prb->areaStippleMode;

                pBuffer += 2;

                InputBufferCommit(ppdev, pBuffer);
            }
        }// if ( ulCurrentLogicOp != K_LOGICOP_NOOP )

        if ( --lNumPass < 0 )
        {
            break;
        }

        //
        // We need to the 2nd pass. So reset the rectangle info, color mode
        // and logicop status
        //
        pRect              = ppb->pRects;
        ulCurrentFillColor = ulFgColor;
        ulCurrentLogicOp   = ulFgLogicOp;
    }// Loop through all the passes

    if ( dwColorMode != __PERMEDIA_DISABLE )
    {
        InputBufferReserve(ppdev, 2, &pBuffer);

        //
        // Restore ColorDDAMode
        //
        pBuffer[0] = __Permedia2TagColorDDAMode;
        pBuffer[1] =  __PERMEDIA_DISABLE;

        pBuffer += 2;

        InputBufferCommit(ppdev, pBuffer);
    }

    DBG_GDI((6, "vMonoPatFill returning"));

}// vMonoPatFill()

//-----------------------------------------------------------------------------
//
// VOID vPatFill(GFNPB* ppb)
//
// Function to fill a set of rectangles with a given pattern. Colored patterns
// only. Monochrome patterns are handled in a different routine. This routine
// only handles patterns which were not rotated in memory and which have been
// replicated in X to cope with different alignments.
//
// Parameter block arguments
//
//  ppdev-------Valid
//  lNumRects---Number of rects pointed to by pRects
//  pRects------Of destination rectangles to be filled
//  ucFgRop3----Valid Pattern fill rop3 code (source invariant)
//  pptlBrush---Origin of brush
//  pdsurfDst---Destination surface
//  prbrush-----ponter to RBRUSH
//
//-----------------------------------------------------------------------------
VOID
vPatFill(GFNPB* ppb)
{
    PPDev       ppdev = ppb->ppdev;
    LONG        lNumRects = ppb->lNumRects;
    RECTL*      prcl = ppb->pRects;
    POINTL*     pptlBrush = ppb->pptlBrush;
    Surf*       psurf = ppb->psurfDst;
    RBrush*     prbrush = ppb->prbrush;
    
    BrushEntry* pbe = prbrush->pbe;
    DWORD       dwRenderBits;    
    ULONG       ulBrushX;
    ULONG       ulBrushY;
    ULONG       ulBrushOffset;
//    ULONG       ulLogicOP = ulRop3ToLogicop(ppb->ucFgRop3);
    ULONG       ulLogicOP = ulRop3ToLogicop(ppb->ulRop4 & 0xFF);
    ULONG*      pBuffer;
    
    PERMEDIA_DECL;

    ASSERTDD(lNumRects > 0, "vPatFill: unexpected rectangle lNumRects <= 0");

    if ( (pbe == NULL) || (pbe->prbVerify != ppb->prbrush) )
    {
        vPatRealize(ppb);
        
        pbe = prbrush->pbe;
        ASSERTDD(pbe != NULL, "vPatFill: unexpected null pattern brush entry");
    }

    dwRenderBits = __RENDER_TRAPEZOID_PRIMITIVE
                 | __RENDER_TEXTURED_PRIMITIVE;
    
    InputBufferReserve(ppdev, 34, &pBuffer);

    pBuffer[0] = __Permedia2TagFBWindowBase;
    pBuffer[1] =   psurf->ulPixOffset;
    pBuffer[2] = __Permedia2TagLogicalOpMode;
    pBuffer[3] =  P2_ENABLED_LOGICALOP(ulLogicOP);
    pBuffer[4] = __Permedia2TagFBReadMode;
    pBuffer[5] = PM_FBREADMODE_PARTIAL(psurf->ulPackedPP)
               | LogicopReadDest[ulLogicOP];
    pBuffer[6] = __Permedia2TagFBWriteConfig; 
    pBuffer[7] = PM_FBREADMODE_PARTIAL(psurf->ulPackedPP)
               | LogicopReadDest[ulLogicOP];
    pBuffer[8] = __Permedia2TagFBSourceOffset;
    pBuffer[9] =  0;

    //
    // Setup the texture unit with the pattern
    //    
    pBuffer[10] = __Permedia2TagDitherMode;
    pBuffer[11] = (COLOR_MODE << PM_DITHERMODE_COLORORDER)
               | (ppdev->ulPermFormat << PM_DITHERMODE_COLORFORMAT)
               | (ppdev->ulPermFormatEx << PM_DITHERMODE_COLORFORMATEXTENSION)
               | (1 << PM_DITHERMODE_ENABLE);
    
    pBuffer[12] = __Permedia2TagTextureAddressMode;
    pBuffer[13] = (1 << PM_TEXADDRESSMODE_ENABLE);
    pBuffer[14] = __Permedia2TagTextureColorMode;
    pBuffer[15] = (1 << PM_TEXCOLORMODE_ENABLE)
               | (0 << 4)       // RGB
               | (3 << 1);     // Copy

    
    pBuffer[16] = __Permedia2TagTextureReadMode;
    pBuffer[17] = PM_TEXREADMODE_ENABLE(__PERMEDIA_ENABLE)
               | PM_TEXREADMODE_WIDTH(CACHED_BRUSH_WIDTH_LOG2 - 1)
               | PM_TEXREADMODE_HEIGHT(CACHED_BRUSH_HEIGHT_LOG2 - 1)
               | 1 << 1       // repeat S 
               | 1 << 3;     // repeat T
    
    pBuffer[18] = __Permedia2TagTextureDataFormat;
    pBuffer[19] = (ppdev->ulPermFormat << PM_TEXDATAFORMAT_FORMAT)
               | (ppdev->ulPermFormatEx << PM_TEXDATAFORMAT_FORMATEXTENSION)
               | (COLOR_MODE << PM_TEXDATAFORMAT_COLORORDER);
    
    pBuffer[20] = __Permedia2TagTextureMapFormat;
    pBuffer[21] = (ppdev->ulBrushPackedPP)
               | (ppdev->cPelSize << PM_TEXMAPFORMAT_TEXELSIZE);

    pBuffer[22] = __Permedia2TagSStart;
    pBuffer[23] =  0;
    pBuffer[24] = __Permedia2TagTStart;
    pBuffer[25] =  0;
    pBuffer[26] = __Permedia2TagdSdx;
    pBuffer[27] =       1 << 20;
    pBuffer[28] = __Permedia2TagdSdyDom;
    pBuffer[29] =    0;
    pBuffer[30] = __Permedia2TagdTdx;
    pBuffer[31] =       0;
    pBuffer[32] = __Permedia2TagdTdyDom;
    pBuffer[33] =    1 << 20;

    pBuffer += 34;

    InputBufferCommit(ppdev, pBuffer);

    //
    // Render rectangles
    //
    do
    {
        //
        // Caclulate brush offset taking into account the brush origin
        // NOTE: that the texture unit places the origin of the texture
        //       at the upper left of the destination rectangle
        //
        ulBrushX = (prcl->left - ppb->pptlBrush->x) & 7;
        ulBrushY = (prcl->top - ppb->pptlBrush->y) & 7;
        ulBrushOffset = pbe->ulPixelOffset 
                      + ulBrushX
                      + (ulBrushY * CACHED_BRUSH_WIDTH);

        InputBufferReserve(ppdev, 12, &pBuffer);

        pBuffer[0] = __Permedia2TagTextureBaseAddress;
        pBuffer[1] =  ulBrushOffset;
        pBuffer[2] = __Permedia2TagStartXDom;
        pBuffer[3] =  INTtoFIXED(prcl->left);
        pBuffer[4] = __Permedia2TagStartXSub;
        pBuffer[5] =  INTtoFIXED(prcl->right);
        pBuffer[6] = __Permedia2TagStartY;
        pBuffer[7] =  INTtoFIXED(prcl->top);
        pBuffer[8] = __Permedia2TagCount;
        pBuffer[9] =  (prcl->bottom - prcl->top);
        pBuffer[10] = __Permedia2TagRender;
        pBuffer[11] =  dwRenderBits;

        pBuffer += 12;

        InputBufferCommit(ppdev, pBuffer);

        prcl++;    

    } while (--lNumRects != 0);

    //
    // Restore defaults
    //
    InputBufferReserve(ppdev, 8, &pBuffer);

    pBuffer[0] = __Permedia2TagTextureAddressMode;
    pBuffer[1] = (0 << PM_TEXADDRESSMODE_ENABLE);
    pBuffer[2] = __Permedia2TagTextureColorMode;
    pBuffer[3] =  (0 << PM_TEXCOLORMODE_ENABLE);
    pBuffer[4] = __Permedia2TagDitherMode;
    pBuffer[5] =  (0 << PM_DITHERMODE_ENABLE);
    pBuffer[6] = __Permedia2TagTextureReadMode;
    pBuffer[7] =  __PERMEDIA_DISABLE;

    pBuffer += 8;

    InputBufferCommit(ppdev, pBuffer);

    DBG_GDI((6, "vPatternFillRects done"));
}// vPatFill


