You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
491 lines
17 KiB
491 lines
17 KiB
/******************************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()
|
|
|