/******************************Module*Header*******************************\
*
*                           *******************
*                           * GDI SAMPLE CODE *
*                           *******************
*
* Module Name: brush.c
*
* Content:   Handles all brush/pattern initialization and realization. 
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\**************************************************************************/
#include "precomp.h"
#include "heap.h"

//-----------------------------------------------------------------------------
//
// VOID vRealizeDitherPattern
//
// Generates an 8x8 dither pattern, in our internal realization format, for
// the colour ulRGBToDither.
//
//-----------------------------------------------------------------------------
VOID
vRealizeDitherPattern(HDEV      hdev,
                      RBrush*   prb,
                      ULONG     ulRGBToDither)
{
    //
    // Do the actual dithering
    // Note: This function is NT5 only. If you want to write a NT4 driver,
    // you have to implement dither function in the driver
    //
    EngDitherColor(hdev, DM_DEFAULT, ulRGBToDither, &prb->aulPattern[0]);

    //
    // Initialize the fields we need
    //
    prb->ptlBrushOrg.x = LONG_MIN;
    prb->fl            = 0;
    prb->pbe           = NULL;
}// vRealizeDitherPattern()

//---------------------------Public*Routine------------------------------------
//
// BOOL DrvRealizeBrush
//
// This function allows us to convert GDI brushes into an internal form
// we can use.  It is called by GDI when we've called BRUSHOBJ_pvGetRbrush
// in some other function like DrvBitBlt, and GDI doesn't happen have a cached
// realization lying around.
//
// Parameters:
// pbo----------Points to the BRUSHOBJ that is to be realized. All other
//              parameters, except for psoTarget, can be queried from this
//              object. Parameter specifications are provided as an
//              optimization. This parameter is best used only as a parameter
//              for BRUSHOBJ_pvAllocRBrush, which allocates the memory for the
//              realized brush. 
// psoTarget----Points to the surface for which the brush is to be realized.
//              This surface can be the physical surface for the device, a
//              device format bitmap, or a standard format bitmap. 
// psoPattern---Points to the surface that describes the pattern for the brush.
//              For a raster device, this is a bitmap. For a vector device,
//              this is one of the pattern surfaces provided by DrvEnablePDEV. 
// psoMask------Points to a transparency mask for the brush. This is a 1 bit
//              per pixel bitmap that has the same extent as the pattern. A
//              mask of zero means the pixel is considered a background pixel
//              for the brush. (In transparent background mode, the background
//              pixels are unaffected in a fill.) Plotters can ignore this
//              parameter because they never draw background information.
// pxlo---------Points to a XLATEOBJ that defines the interpretration of colors
//              in the pattern. A XLATEOBJXxx service routine can be called to
//              translate the colors to device color indices. Vector devices
//              should translate color zero through the XLATEOBJ to get the
//              foreground color for the brush. 
// ulHatch------Specifies whether psoPattern is one of the hatch brushes
//              returned by DrvEnablePDEV. This is true if the value of this
//              parameter is less than HS_API_MAX. 
//
// Return Value
//  The return value is TRUE if the brush was successfully realized. Otherwise,
//  it is FALSE, and an error code is logged.
//
// Comments
//  To realize a brush, the driver converts a GDI brush into a form that can be
//  used internally. A realized brush contains information and accelerators the
//  driver needs to fill an area with a pattern; information that is defined by
//  the driver and used only by the driver.
//
//  The driver's realization of a brush is written into the buffer allocated by
//  a call to BRUSHOBJ_pvAllocRbrush.
//
//  DrvRealizeBrush is required for a driver that does any drawing to any
//  surface.
//
//  ppdev->bRealizeTransparent -- Hint for whether or not the brush should be
//                              realized for transparency.  If this hint is
//                              wrong, there will be no error, but the brush
//                              will have to be unnecessarily re-realized.
//
// Note: You should always set 'ppdev->bRealizeTransparent' before calling
//       BRUSHOBJ_pvGetRbrush!
//
//-----------------------------------------------------------------------------
BOOL
DrvRealizeBrush(BRUSHOBJ*   pbo,
                SURFOBJ*    psoDst,
                SURFOBJ*    psoPattern,
                SURFOBJ*    psoMask,
                XLATEOBJ*   pxlo,
                ULONG       ulHatch)
{
    PDev*       ppdev = (PDev*)psoDst->dhpdev;
    
    BYTE*       pbDst;
    BYTE*       pbSrc;
    LONG        i;
    LONG        j;
    LONG        lNumPixelToBeCopied;
    LONG        lSrcDelta;
    RBrush*     prb;    
    ULONG*      pulXlate;
    ULONG       ulPatternFormat;

    PERMEDIA_DECL;

    DBG_GDI((6, "DrvRealizeBrush called for pbo 0x%x", pbo));

    //
    // We have a fast path for dithers when we set GCAPS_DITHERONREALIZE:
    //
    if ( ulHatch & RB_DITHERCOLOR )
    {
        //
        // Move this test in here since we always support monochrome brushes
        // as they live in the on-chip area stipple. These dithered brushes
        // will always be colored requiring available off-screen memory.
        //
        if ( !(ppdev->flStatus & STAT_BRUSH_CACHE) )
        {
            //
            // We only handle brushes if we have an off-screen brush cache
            // available.  If there isn't one, we can simply fail the
            // realization, and eventually GDI will do the drawing for us
            // (although a lot slower than we could have done it)
            //
            DBG_GDI((6, "brush cache not enabled"));
            goto ReturnFalse;
        }

        DBG_GDI((7, "DITHERONREALIZE"));

        //
        // We have a fast path for dithers when we set GCAPS_DITHERONREALIZE
        // First, we need to allocate memory for the realization of a brush
        // Note: actually we ask for allocation of a RBRUSH + the brush
        // stamp size
        //
        prb = (RBrush*)BRUSHOBJ_pvAllocRbrush(pbo,
                       sizeof(RBrush) + (TOTAL_BRUSH_SIZE << ppdev->cPelSize));
        if ( prb == NULL )
        {
            DBG_GDI((1, "BRUSHOBJ_pvAllocRbrush() in dither return NULL\n"));
            goto ReturnFalse;
        }

        //
        // Dither and realize the brsuh pattern
        //
        vRealizeDitherPattern(psoDst->hdev, prb, ulHatch);

        goto ReturnTrue;
    }// if ( ulHatch & RB_DITHERCOLOR )

    //
    // We only handle brushes if we have an off-screen brush cache available
    // If there isn't one, we can simply fail the realization, and eventually
    // GDI will do the drawing for us (although a lot slower than we could have
    // done it). We always succeed for 1bpp patterns since we use the area
    // stipple unit to do these rather than off-screen memory.
    //
    ulPatternFormat = psoPattern->iBitmapFormat;

    if ( !(ppdev->flStatus & STAT_BRUSH_CACHE)
        &&(ulPatternFormat != BMF_1BPP) )
    {
        DBG_GDI((1, "brush cache not enabled, or Bitmap is not 1 BPP"));
        goto ReturnFalse;
    }

    //
    // We only accelerate 8x8 patterns since most of the video card can only
    // accelerate 8x8 brush
    //
    if ( (psoPattern->sizlBitmap.cx != 8)
       ||(psoPattern->sizlBitmap.cy != 8) )
    {
        DBG_GDI((1, "Brush Bitmap size is not 8x8"));
        goto ReturnFalse;
    }

    //    
    // We need to allocate memory for the realization of a brush
    // Note: actually we ask for allocation of a RBRUSH + the brush stamp size
    //
    prb = (RBrush*)BRUSHOBJ_pvAllocRbrush(pbo,
                   sizeof(RBrush) + (TOTAL_BRUSH_SIZE << ppdev->cPelSize));
    if ( prb == NULL )
    {
        DBG_GDI((0, "BRUSHOBJ_pvAllocRbrush() failed"));
        goto ReturnFalse;
    }

    //
    // Initialize the fields we need
    //
    prb->ptlBrushOrg.x = LONG_MIN;
    prb->fl            = 0;

    prb->pbe = NULL;

    lSrcDelta = psoPattern->lDelta;
    pbSrc     = (BYTE*)psoPattern->pvScan0;
    pbDst     = (BYTE*)&prb->aulPattern[0];

    //
    // At 8bpp, we handle patterns at 1bpp, 4bpp and 8bpp with/without an xlate
    // At 16bpp, we handle patterns at 16 bpp without an xlate.
    // At 32bpp, we handle patterns at 32bpp without an xlate.
    // We handle all the patterns at 1 bpp with/without an Xlate
    //
    // Check if the brush pattern has the same color depth as our current
    // display color depth
    //
    if ( ulPatternFormat == BMF_1BPP )
    {
        DWORD   Data;

        DBG_GDI((7, "Realizing 1bpp brush"));

        //
        // We dword align the monochrome bitmap so that every row starts
        // on a new long (so that we can do long writes later to transfer
        // the bitmap to the area stipple unit).
        //
        for ( i = 8; i != 0; i-- )
        {
            //
            // Replicate the brush to 32 bits wide, as the TX cannot
            // span fill 8 bit wide brushes
            //
            Data = (*pbSrc) & 0xff;
            Data |= Data << 8;
            Data |= Data << 16;
            *(DWORD*)pbDst = Data;

            //
            // Area stipple is loaded with DWORDS
            //
            pbDst += sizeof(DWORD);
            pbSrc += lSrcDelta;
        }

        pulXlate         = pxlo->pulXlate;
        prb->fl         |= RBRUSH_2COLOR;
        prb->ulForeColor = pulXlate[1];
        prb->ulBackColor = pulXlate[0];
    }// Pattern at 1 BPP
    else if ( (ulPatternFormat == BMF_4BPP)&&(ppdev->iBitmapFormat == BMF_8BPP))
    {
        DBG_GDI((7, "Realizing 4bpp brush"));

        //
        // The screen is 8bpp and the pattern is 4bpp:
        //            
        pulXlate = pxlo->pulXlate;

        for ( i = 8; i != 0; i-- )
        {
            //
            // Inner loop is repeated only 4 times because each loop
            // handles 2 pixels:
            //
            for ( j = 4; j != 0; j-- )
            {
                *pbDst++ = (BYTE)pulXlate[*pbSrc >> 4];
                *pbDst++ = (BYTE)pulXlate[*pbSrc & 15];
                pbSrc++;
            }

            pbSrc += lSrcDelta - 4;
        }
    }// Pattern 4BPP and Screen 8 BPP
    else if ( ( ppdev->iBitmapFormat == ulPatternFormat )
            &&( (pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL) ) )
    {
        DBG_GDI((7, "Realizing un-translated brush"));

        //
        // The pattern is the same colour depth as the screen, and
        // there's no translation to be done.
        // Here we first need to calculate how many pixel need to be
        // copied
        //
        lNumPixelToBeCopied = (8 << ppdev->cPelSize);

        for ( i = 8; i != 0; i-- )
        {
            RtlCopyMemory(pbDst, pbSrc, lNumPixelToBeCopied);

            pbSrc += lSrcDelta;
            pbDst += lNumPixelToBeCopied;
        }
    }// Pattern and Screen has same color depth, No Xlate
    else if ( (ppdev->iBitmapFormat == BMF_8BPP)
            &&(ulPatternFormat == BMF_8BPP) )
    {
        DBG_GDI((7, "Realizing 8bpp translated brush"));

        //
        // The screen is 8bpp, and there's translation to be done
        // So we have to do copy + Xlate one by one
        //
        pulXlate = pxlo->pulXlate;

        for ( i = 8; i != 0; i-- )
        {
            for ( j = 8; j != 0; j-- )
            {
                *pbDst++ = (BYTE)pulXlate[*pbSrc++];
            }

            pbSrc += lSrcDelta - 8;
        }
    }// Screen mode and pattern mode all at 8 BPP
    else
    {
        //
        // We've got a brush whose format we haven't special cased.
        //
        goto ReturnFalse;
    }

ReturnTrue:
    DBG_GDI((6, "DrvRealizeBrush returning true"));
    
    return(TRUE);

ReturnFalse:

    if ( psoPattern != NULL )
    {
        DBG_GDI((1, "Failed realization -- Type: %li Format: %li cx: %li cy: %li",
                 psoPattern->iType, psoPattern->iBitmapFormat,
                 psoPattern->sizlBitmap.cx, psoPattern->sizlBitmap.cy));
    }
    DBG_GDI((6, "DrvRealizeBrush returning false"));

    return(FALSE);
}// DrvRealizeBrush()

//-----------------------------------------------------------------------------
//
// BOOL bEnableBrushCache
//
// Allocates off-screen memory for storing the brush cache.
//
//-----------------------------------------------------------------------------
BOOL
bEnableBrushCache(PDev* ppdev)
{
    BrushEntry* pbe;            // Pointer to the brush-cache entry
    LONG        i;
    LONG        lDelta;
    ULONG       ulPixOffset;

    DBG_GDI((6, "bEnableBrushCache"));

    ASSERTDD(!(ppdev->flStatus & STAT_BRUSH_CACHE),
                "bEnableBrushCache: unexpected already enabled brush cache");
    
    //
    // ENABLE_BRUSH_CACHE by default is on. It would be turned off in
    // bInitializeHw() if 3D buffers runs out of memory
    //
    if ( !(ppdev->flStatus & ENABLE_BRUSH_CACHE) )
    {
        DBG_GDI((1, "Brush cache not valid for creation"));
        goto ReturnTrue;
    }

    ppdev->ulBrushVidMem = ulVidMemAllocate(ppdev,
                                            CACHED_BRUSH_WIDTH,
                                            CACHED_BRUSH_HEIGHT
                                             *NUM_CACHED_BRUSHES,
                                             ppdev->cPelSize,
                                            &lDelta,
                                            &ppdev->pvmBrushHeap,
                                            &ppdev->ulBrushPackedPP,
                                            FALSE);

    if (ppdev->ulBrushVidMem == 0 )
    {
        DBG_GDI((0, "bEnableBrushCache: failed to allocate video memory"));
        goto ReturnTrue;    // See note about why we can return TRUE...
    }

    ASSERTDD(lDelta == (CACHED_BRUSH_WIDTH << ppdev->cPelSize),
             "bEnableBrushCache: unexpected stride does not match width");

    ppdev->cBrushCache = NUM_CACHED_BRUSHES;

    ulPixOffset = (ULONG) ppdev->ulBrushVidMem >> ppdev->cPelSize;
    pbe = &ppdev->abe[0];
    
    for (i = 0; i < NUM_CACHED_BRUSHES; i++, pbe++)
    {
        pbe->prbVerify = NULL;
        pbe->ulPixelOffset = ulPixOffset;
        ulPixOffset += CACHED_BRUSH_SIZE;

        memset((pbe->ulPixelOffset << ppdev->cPelSize) + ppdev->pjScreen, 
                    0x0, (CACHED_BRUSH_SIZE << ppdev->cPelSize)); 
    }
    
    //
    // We successfully allocated the brush cache, so let's turn
    // on the switch showing that we can use it:
    //
    DBG_GDI((6, "bEnableBrushCache: successfully allocated brush cache"));
    ppdev->flStatus |= STAT_BRUSH_CACHE;

ReturnTrue:
    //
    // If we couldn't allocate a brush cache, it's not a catastrophic
    // failure; patterns will still work, although they'll be a bit
    // slower since they'll go through GDI.  As a result we don't
    // actually have to fail this call:
    //
    DBG_GDI((6, "Passed bEnableBrushCache"));

    return(TRUE);
}// bEnableBrushCache()

//-----------------------------------------------------------------------------
//
// VOID vDisableBrushCache
//
// Cleans up anything done in bEnableBrushCache.
//
//-----------------------------------------------------------------------------
VOID
vDisableBrushCache(PDev* ppdev)
{
    DBG_GDI((6,"vDisableBrushCache"));
    if(ppdev->flStatus & STAT_BRUSH_CACHE)
    {
        DBG_GDI((6,"vDisableBrushCache: freeing brush cache"));
        VidMemFree(ppdev->pvmBrushHeap->lpHeap,
                   (FLATPTR)(ppdev->ulBrushVidMem));
        ppdev->cBrushCache = 0;

        ppdev->flStatus &= ~STAT_BRUSH_CACHE;
        DBG_GDI((6,"vDisableBrushCache: freeing brush cache done"));
    }

}// vDisableBrushCache()

//-----------------------------------------------------------------------------
//
// VOID vAssertModeBrushCache
//
// Resets the brush cache when we exit out of full-screen.
//
//-----------------------------------------------------------------------------
VOID
vAssertModeBrushCache(PDev*   ppdev,
                      BOOL    bEnable)
{
    if ( bEnable )
    {
        bEnableBrushCache(ppdev);
    }
    else
    {
        vDisableBrushCache(ppdev);
    }
}// vAssertModeBrushCache()


