584 lines
19 KiB
C
584 lines
19 KiB
C
/******************************Module*Header*******************************\
|
|
* Module Name: Brush.c
|
|
*
|
|
* Handles all brush/pattern initialization and realization.
|
|
*
|
|
* Copyright (c) 1992-1996 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vRealizeDitherPattern
|
|
*
|
|
* Generates an 8x8 dither pattern, in our internal realization format, for
|
|
* the color ulRGBToDither. Note that the high byte of ulRGBToDither does
|
|
* not need to be set to zero, because vComputeSubspaces ignores it.
|
|
\**************************************************************************/
|
|
|
|
VOID vRealizeDitherPattern(
|
|
RBRUSH* prb,
|
|
ULONG ulRGBToDither,
|
|
ULONG cTile)
|
|
{
|
|
ULONG ulNumVertices;
|
|
VERTEX_DATA vVertexData[4];
|
|
VERTEX_DATA* pvVertexData;
|
|
LONG i;
|
|
|
|
// Calculate what color subspaces are involved in the dither:
|
|
|
|
pvVertexData = vComputeSubspaces(ulRGBToDither, vVertexData);
|
|
|
|
// Now that we have found the bounding vertices and the number of
|
|
// pixels to dither for each vertex, we can create the dither pattern
|
|
|
|
ulNumVertices = (ULONG)(pvVertexData - vVertexData);
|
|
// # of vertices with more than zero pixels in the dither
|
|
|
|
// Do the actual dithering:
|
|
|
|
vDitherColor(&prb->aulPattern[0], vVertexData, pvVertexData, ulNumVertices);
|
|
|
|
if (cTile)
|
|
{
|
|
BYTE* pjDst = (BYTE*)&prb->aulPattern[0];
|
|
|
|
// Probably another version of vDitherColor is
|
|
// in order.
|
|
|
|
i = 56; // start with last row of 8x8
|
|
RtlCopyMemory(&pjDst[i<<1], &pjDst[i], 8);
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
i -= 8;
|
|
RtlCopyMemory(&pjDst[i<<1], &pjDst[i], 8);
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
i -= 8;
|
|
RtlCopyMemory(&pjDst[i<<1], &pjDst[i], 8);
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
i -= 8;
|
|
RtlCopyMemory(&pjDst[i<<1], &pjDst[i], 8);
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
i -= 8;
|
|
RtlCopyMemory(&pjDst[i<<1], &pjDst[i], 8);
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
i -= 8;
|
|
RtlCopyMemory(&pjDst[i<<1], &pjDst[i], 8);
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
i -= 8;
|
|
RtlCopyMemory(&pjDst[i<<1], &pjDst[i], 8);
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
i -= 8;
|
|
// bytes 0-7 are already in place
|
|
RtlCopyMemory(&pjDst[(i<<1)+8], &pjDst[i], 8);
|
|
|
|
RtlCopyMemory(&pjDst[128], pjDst, 128);
|
|
}
|
|
|
|
// Initialize the fields we need:
|
|
|
|
prb->ptlBrushOrg.x = LONG_MIN;
|
|
prb->fl = 0;
|
|
prb->pbe = NULL;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL DrvRealizeBrush
|
|
*
|
|
* This function allows us to convert GDI brushes into an internal form
|
|
* we can use. It may be called directly by GDI at SelectObject time, or
|
|
* it may be called by GDI as a result of us calling BRUSHOBJ_pvGetRbrush
|
|
* to create a realized brush in a function like DrvBitBlt.
|
|
*
|
|
* Note that we have no way of determining what the current Rop or brush
|
|
* alignment are at this point.
|
|
*
|
|
* Warning: psoPattern will be null if the RB_DITHERCOLOR flag is set in
|
|
* iHatch.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL DrvRealizeBrush(
|
|
BRUSHOBJ* pbo,
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoPattern,
|
|
SURFOBJ* psoMask,
|
|
XLATEOBJ* pxlo,
|
|
ULONG iHatch)
|
|
{
|
|
PDEV* ppdev;
|
|
ULONG iPatternFormat;
|
|
BYTE* pjSrc;
|
|
BYTE* pjDst;
|
|
LONG lSrcDelta;
|
|
LONG cj;
|
|
LONG dp;
|
|
LONG i;
|
|
LONG j;
|
|
RBRUSH* prb;
|
|
ULONG* pulXlate;
|
|
ULONG cTile = 0;
|
|
ULONG ulSize;
|
|
|
|
ppdev = (PDEV*) psoDst->dhpdev;
|
|
|
|
// 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):
|
|
|
|
if (!(ppdev->flStatus & STAT_BRUSH_CACHE))
|
|
{
|
|
DISPDBG((2,"There is no brush cache"));
|
|
goto ReturnFalse;
|
|
}
|
|
|
|
if ((ppdev->ulChipID != W32P) && (ppdev->ulChipID != ET6000))
|
|
{
|
|
// Patterns are duplicated horizontally and vertically (4 tiles)
|
|
|
|
cTile = 1;
|
|
}
|
|
|
|
// We have a fast path for dithers when we set GCAPS_DITHERONREALIZE:
|
|
|
|
ulSize = sizeof(RBRUSH) + (TOTAL_BRUSH_SIZE * ppdev->cBpp);
|
|
if (cTile)
|
|
{
|
|
ulSize *= 4;
|
|
}
|
|
|
|
if (iHatch & RB_DITHERCOLOR)
|
|
{
|
|
// Implementing DITHERONREALIZE increased our score on a certain
|
|
// unmentionable benchmark by 0.4 million 'megapixels'. Too bad
|
|
// this didn't work in the first version of NT.
|
|
|
|
prb = BRUSHOBJ_pvAllocRbrush(pbo, ulSize);
|
|
if (prb == NULL)
|
|
{
|
|
goto ReturnFalse;
|
|
}
|
|
|
|
vRealizeDitherPattern(prb, iHatch, cTile);
|
|
goto ReturnTrue;
|
|
}
|
|
|
|
// We only accelerate 8x8 patterns. Since Win3.1 and Chicago don't
|
|
// support patterns of any other size, it's a safe bet that 99.9%
|
|
// of the patterns we'll ever get will be 8x8:
|
|
|
|
if ((psoPattern->sizlBitmap.cx != 8) ||
|
|
(psoPattern->sizlBitmap.cy != 8))
|
|
{
|
|
goto ReturnFalse;
|
|
}
|
|
|
|
// At 8bpp, we handle patterns at 1bpp, 4bpp and 8bpp with/without an xlate.
|
|
// At 16bpp, we handle patterns at 1bpp and 16bpp without an xlate.
|
|
// At 32bpp, we handle patterns at 1bpp and 32bpp without an xlate.
|
|
|
|
iPatternFormat = psoPattern->iBitmapFormat;
|
|
|
|
if ((iPatternFormat == ppdev->iBitmapFormat) ||
|
|
(iPatternFormat == BMF_1BPP) ||
|
|
(iPatternFormat == BMF_4BPP) && (ppdev->iBitmapFormat == BMF_8BPP))
|
|
{
|
|
cj = (8 * ppdev->cBpp); // Every pattern is 8 pels wide
|
|
|
|
prb = BRUSHOBJ_pvAllocRbrush(pbo, ulSize);
|
|
if (prb == NULL)
|
|
{
|
|
goto ReturnFalse;
|
|
}
|
|
|
|
// Initialize the fields we need:
|
|
|
|
prb->ptlBrushOrg.x = LONG_MIN;
|
|
prb->fl = 0;
|
|
prb->pbe = NULL;
|
|
|
|
lSrcDelta = psoPattern->lDelta;
|
|
pjSrc = (BYTE*) psoPattern->pvScan0;
|
|
pjDst = (BYTE*) &prb->aulPattern[0];
|
|
|
|
if (ppdev->iBitmapFormat == iPatternFormat)
|
|
{
|
|
if ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL))
|
|
{
|
|
DISPDBG((2, "Realizing un-translated brush"));
|
|
|
|
// The pattern is the same color depth as the screen, and
|
|
// there's no translation to be done:
|
|
|
|
for (i = 8; i != 0; i--)
|
|
{
|
|
RtlCopyMemory(pjDst, pjSrc, cj);
|
|
if (cTile)
|
|
{
|
|
RtlCopyMemory(pjDst + cj, pjDst, cj);
|
|
pjDst += cj;
|
|
}
|
|
pjSrc += lSrcDelta;
|
|
pjDst += cj;
|
|
}
|
|
}
|
|
else if (ppdev->iBitmapFormat == BMF_8BPP)
|
|
{
|
|
DISPDBG((2, "Realizing 8bpp translated brush"));
|
|
|
|
// The screen is 8bpp, and there's translation to be done:
|
|
|
|
pulXlate = pxlo->pulXlate;
|
|
|
|
for (i = 8; i != 0; i--)
|
|
{
|
|
for (j = 8; j != 0; j--)
|
|
{
|
|
*pjDst++ = (BYTE) pulXlate[*pjSrc++];
|
|
}
|
|
if (cTile)
|
|
{
|
|
RtlCopyMemory(pjDst, pjDst - 8, 8);
|
|
pjDst += 8;
|
|
}
|
|
|
|
pjSrc += lSrcDelta - 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// I don't feel like writing code to handle translations
|
|
// when our screen is 16bpp or higher (although I probably
|
|
// should; we could allocate a temporary buffer and use
|
|
// GDI to convert, like is done in the VGA driver).
|
|
|
|
DISPDBG((2, "Not realizing translated brush for 16bpp or higher"));
|
|
goto ReturnFalse;
|
|
}
|
|
}
|
|
else if (iPatternFormat == BMF_1BPP)
|
|
{
|
|
DISPDBG((2, "Realizing 1bpp brush"));
|
|
|
|
pulXlate = pxlo->pulXlate;
|
|
|
|
if (ppdev->iBitmapFormat == BMF_8BPP)
|
|
{
|
|
for (i = 8; i != 0; i--)
|
|
{
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 7) & 1];
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 6) & 1];
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 5) & 1];
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 4) & 1];
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 3) & 1];
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 2) & 1];
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 1) & 1];
|
|
*pjDst++ = (BYTE) pulXlate[(*pjSrc >> 0) & 1];
|
|
|
|
if (cTile)
|
|
{
|
|
RtlCopyMemory(pjDst, pjDst - cj, cj);
|
|
pjDst += cj;
|
|
}
|
|
|
|
pjSrc += lSrcDelta;
|
|
}
|
|
}
|
|
else if (ppdev->iBitmapFormat == BMF_16BPP)
|
|
{
|
|
dp = ppdev->cBpp; // Every pattern is 8 pels wide
|
|
|
|
for (i = 8; i != 0; i--)
|
|
{
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 7) & 1]); pjDst += dp;
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 6) & 1]); pjDst += dp;
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 5) & 1]); pjDst += dp;
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 4) & 1]); pjDst += dp;
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 3) & 1]); pjDst += dp;
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 2) & 1]); pjDst += dp;
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 1) & 1]); pjDst += dp;
|
|
*((WORD *)pjDst) = (WORD) (pulXlate[(*pjSrc >> 0) & 1]); pjDst += dp;
|
|
|
|
if (cTile)
|
|
{
|
|
RtlCopyMemory(pjDst, pjDst - cj, cj);
|
|
pjDst += cj;
|
|
}
|
|
|
|
pjSrc += lSrcDelta;
|
|
}
|
|
}
|
|
else if (ppdev->iBitmapFormat == BMF_24BPP)
|
|
{
|
|
dp = ppdev->cBpp; // Every pattern is 8 pels wide
|
|
|
|
for (i = 8; i != 0; i--)
|
|
{
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 7) & 1]; pjDst += dp;
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 6) & 1]; pjDst += dp;
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 5) & 1]; pjDst += dp;
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 4) & 1]; pjDst += dp;
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 3) & 1]; pjDst += dp;
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 2) & 1]; pjDst += dp;
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 1) & 1]; pjDst += dp;
|
|
*((ULONG *)pjDst) = pulXlate[(*pjSrc >> 0) & 1]; pjDst += dp;
|
|
|
|
if (cTile)
|
|
{
|
|
RtlCopyMemory(pjDst, pjDst - cj, cj);
|
|
pjDst += cj;
|
|
}
|
|
|
|
pjSrc += lSrcDelta;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DISPDBG((2, "Realizing 4bpp brush"));
|
|
|
|
// The screen is 8bpp and the pattern is 4bpp:
|
|
|
|
ASSERTDD((ppdev->iBitmapFormat == BMF_8BPP) &&
|
|
(iPatternFormat == BMF_4BPP),
|
|
"Messed up brush logic");
|
|
|
|
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--)
|
|
{
|
|
*pjDst++ = (BYTE) pulXlate[*pjSrc >> 4];
|
|
*pjDst++ = (BYTE) pulXlate[*pjSrc & 15];
|
|
pjSrc++;
|
|
}
|
|
|
|
if (cTile)
|
|
{
|
|
RtlCopyMemory(pjDst, pjDst - 8, 8);
|
|
pjDst += 8;
|
|
}
|
|
|
|
pjSrc += lSrcDelta - 4;
|
|
}
|
|
}
|
|
|
|
if (cTile)
|
|
{
|
|
pjDst = (BYTE*) &prb->aulPattern[0];
|
|
RtlCopyMemory(pjDst + (cj*8*2), pjDst, cj*8*2);
|
|
}
|
|
|
|
ReturnTrue:
|
|
|
|
ppdev->pfnFastPatRealize(ppdev, prb, NULL, FALSE);
|
|
|
|
if (psoPattern != NULL)
|
|
{
|
|
DISPDBG((2, "Succeeded realization -- Type: %li Format: %li cx: %li cy: %li",
|
|
psoPattern->iType, psoPattern->iBitmapFormat,
|
|
psoPattern->sizlBitmap.cx, psoPattern->sizlBitmap.cy));
|
|
}
|
|
else
|
|
{
|
|
DISPDBG((2, "Succeeded realization -- it was a DITHER_ON_REALIZE"));
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
ReturnFalse:
|
|
|
|
if (psoPattern != NULL)
|
|
{
|
|
DISPDBG((2, "Failed realization -- Type: %li Format: %li cx: %li cy: %li",
|
|
psoPattern->iType, psoPattern->iBitmapFormat,
|
|
psoPattern->sizlBitmap.cx, psoPattern->sizlBitmap.cy));
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bEnableBrushCache
|
|
*
|
|
* Allocates off-screen memory for storing the brush cache.
|
|
\**************************************************************************/
|
|
|
|
BOOL bEnableBrushCache(
|
|
PDEV* ppdev)
|
|
{
|
|
OH* poh; // Points to off-screen chunk of memory
|
|
BRUSHENTRY* pbe; // Pointer to the brush-cache entry
|
|
LONG i;
|
|
|
|
pbe = &ppdev->abe[0]; // Points to where we'll put the first brush
|
|
// cache entry
|
|
{
|
|
LONG x;
|
|
LONG y;
|
|
ULONG cTileFactor = 1;
|
|
|
|
// Reserve the offscreen space that is required for the ACL to do
|
|
// solid fills. If this fails, our solid fill code will not work.
|
|
// We need two DWORD storage locations if we're going to do any
|
|
// monochrome expansion stuff (font painting...).
|
|
|
|
// Note: these must be dword aligned for the w32p
|
|
|
|
// Not that *I* ever made this mistake, but don't
|
|
// place any early outs (returns) before you allocate the solid
|
|
// color work area. Not having a solid color work area is a
|
|
// fatal error for this driver.
|
|
|
|
DISPDBG((2,"Allocating solid brush work area"));
|
|
poh = pohAllocate(ppdev, NULL, 8, 1, FLOH_MAKE_PERMANENT);
|
|
|
|
ASSERTDD((poh != NULL),
|
|
"We couldn't allocate offscreen space for the solid colors");
|
|
|
|
if (!poh)
|
|
return FALSE;
|
|
|
|
ppdev->ulSolidColorOffset = (poh->y * ppdev->lDelta) + ppdev->cBpp * poh->x;
|
|
DISPDBG((2,"Allocating brush cache"));
|
|
|
|
if ((ppdev->ulChipID != W32P) && (ppdev->ulChipID != ET6000))
|
|
{
|
|
cTileFactor = 4;
|
|
}
|
|
|
|
//
|
|
// Fix this mess up.
|
|
//
|
|
|
|
poh = pohAllocate(ppdev,
|
|
NULL,
|
|
cTileFactor * 2 * 64,
|
|
FAST_BRUSH_COUNT,
|
|
FLOH_MAKE_PERMANENT);
|
|
|
|
if (poh == NULL)
|
|
{
|
|
DISPDBG((1,"Failed to allocate brush cache"));
|
|
goto ReturnTrue; // See note about why we can return TRUE...
|
|
}
|
|
|
|
ppdev->cBrushCache = FAST_BRUSH_COUNT;
|
|
|
|
// Hardware brushes require that the bits start on a 64 (height*width)
|
|
// pixel boundary. The heap manager doesn't guarantee us any such
|
|
// alignment, so we allocate a bit of extra room so that we can
|
|
// do the alignment ourselves:
|
|
|
|
x = poh->x;
|
|
y = poh->y;
|
|
|
|
for (i = FAST_BRUSH_COUNT; i != 0; i--)
|
|
{
|
|
ULONG ulOffset;
|
|
ULONG ulCeil;
|
|
ULONG ulDiff;
|
|
|
|
// Note: I learned the HARD way that you can't just align x
|
|
// to your pattern size, because the lDelta of your screen
|
|
// is not guaranteed to be a multiple of your pattern size.
|
|
// Since y is changing in this loop, the recalc must
|
|
// be done inside this loop. I really need to set these
|
|
// up with a hardcoded linear buffer or else make the
|
|
// heap linear.
|
|
|
|
ulOffset = (y * ppdev->lDelta) + (x * ppdev->cBpp);
|
|
ulCeil = (ulOffset + ((ppdev->cBpp*64)-1)) & ~((ppdev->cBpp*64)-1);
|
|
ulDiff = (ulCeil - ulOffset)/ppdev->cBpp;
|
|
|
|
// If we hadn't allocated 'ppdev' with LMEM_ZEROINIT,
|
|
// we would have to initialize pbe->prbVerify too...
|
|
|
|
pbe->x = x + ulDiff;
|
|
pbe->y = y;
|
|
|
|
DISPDBG((2, "BrushCache[%d] pos(%d,%d) pbe(%d,%d) delta(%d) o(%d) c(%d) d(%d)",
|
|
i,
|
|
x,
|
|
y,
|
|
pbe->x,
|
|
pbe->y,
|
|
ppdev->lDelta,
|
|
ulOffset,
|
|
ulCeil,
|
|
ulDiff
|
|
));
|
|
|
|
//x += FAST_BRUSH_ALLOCATION * FAST_BRUSH_ALLOCATION; // size of a brush (x*y)
|
|
y++;
|
|
//
|
|
|
|
pbe++;
|
|
}
|
|
}
|
|
|
|
// Note that we don't have to remember 'poh' for when we have
|
|
// to disable brushes -- the off-screen heap frees any
|
|
// off-screen heap allocations automatically.
|
|
|
|
// We successfully allocated the brush cache, so let's turn
|
|
// on the switch showing that we can use it:
|
|
|
|
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:
|
|
|
|
DISPDBG((5, "Passed bEnableBrushCache"));
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vDisableBrushCache
|
|
*
|
|
* Cleans up anything done in bEnableBrushCache.
|
|
\**************************************************************************/
|
|
|
|
VOID vDisableBrushCache(PDEV* ppdev)
|
|
{
|
|
// We ain't gotta do nothin'
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vAssertModeBrushCache
|
|
*
|
|
* Resets the brush cache when we exit out of full-screen.
|
|
\**************************************************************************/
|
|
|
|
VOID vAssertModeBrushCache(
|
|
PDEV* ppdev,
|
|
BOOL bEnable)
|
|
{
|
|
BRUSHENTRY* pbe;
|
|
LONG i;
|
|
|
|
if (bEnable)
|
|
{
|
|
// Invalidate the brush cache:
|
|
|
|
pbe = &ppdev->abe[0];
|
|
|
|
for (i = ppdev->cBrushCache; i != 0; i--)
|
|
{
|
|
pbe->prbVerify = NULL;
|
|
pbe++;
|
|
}
|
|
}
|
|
}
|