512 lines
16 KiB
C
512 lines
16 KiB
C
/******************************Module*Header*******************************\
|
|
* Module Name: Brush.c
|
|
*
|
|
* Handles all brush/pattern initialization and realization.
|
|
*
|
|
* Copyright (c) 1992-1995 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vRealizeDitherPattern
|
|
*
|
|
* Generates an 8x8 dither pattern, in our internal realization format, for
|
|
* the colour 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 ulNumVertices;
|
|
VERTEX_DATA vVertexData[4];
|
|
VERTEX_DATA* pvVertexData;
|
|
LONG i;
|
|
|
|
// Calculate what colour 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);
|
|
|
|
// Initialize the fields we need:
|
|
|
|
prb->fl = 0;
|
|
|
|
for (i = 0; i < MAX_BOARDS; i++)
|
|
{
|
|
prb->apbe[i] = 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.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL DrvRealizeBrush(
|
|
BRUSHOBJ* pbo,
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoPattern,
|
|
SURFOBJ* psoMask,
|
|
XLATEOBJ* pxlo,
|
|
ULONG iHatch)
|
|
{
|
|
PDEV* ppdev;
|
|
ULONG iPatternFormat;
|
|
BYTE* pjSrc;
|
|
BYTE* pjDst;
|
|
BYTE jSrc;
|
|
LONG lSrcDelta;
|
|
LONG cj;
|
|
LONG i;
|
|
LONG j;
|
|
RBRUSH* prb;
|
|
ULONG* pulXlate;
|
|
ULONG ulColor;
|
|
|
|
ppdev = (PDEV*) psoDst->dhpdev;
|
|
|
|
// We don't do brushes in high-colour modes on the P9000:
|
|
|
|
if (ppdev->flStat & STAT_UNACCELERATED)
|
|
goto ReturnFalse;
|
|
|
|
// We have a fast path for dithers when we set GCAPS_DITHERONREALIZE:
|
|
|
|
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,
|
|
sizeof(RBRUSH) + (TOTAL_BRUSH_SIZE * ppdev->cjPel));
|
|
if (prb == NULL)
|
|
goto ReturnFalse;
|
|
|
|
if (!P9000(ppdev))
|
|
{
|
|
ASSERTDD(ppdev->iBitmapFormat == BMF_8BPP,
|
|
"GCAPS_COLOR_DITHER shouldn't be set at higher than 8bpp");
|
|
|
|
// Oh goody, we get to use the P9100's 4-colour pattern
|
|
// support:
|
|
|
|
vRealize4ColorDither(prb, iHatch);
|
|
goto ReturnTrue;
|
|
}
|
|
else
|
|
{
|
|
// We do coloured patterns on the P9000 only at 8bpp, and only
|
|
// if we've successfully managed to allocate an off-screen
|
|
// brush cache:
|
|
|
|
if (!(ppdev->flStat & STAT_BRUSH_CACHE))
|
|
goto ReturnFalse;
|
|
|
|
vRealizeDitherPattern(prb, iHatch);
|
|
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 on the P9100.
|
|
// At 32bpp, we handle patterns at 1bpp on the P9100.
|
|
|
|
iPatternFormat = psoPattern->iBitmapFormat;
|
|
|
|
// We only handle arbitrary color brushes if we have an off-screen
|
|
// brush cache available.
|
|
|
|
if ((iPatternFormat != BMF_1BPP) && !(ppdev->flStat & STAT_BRUSH_CACHE))
|
|
goto ReturnFalse;
|
|
|
|
if ((iPatternFormat == BMF_1BPP) ||
|
|
(iPatternFormat == ppdev->iBitmapFormat) ||
|
|
(iPatternFormat == BMF_4BPP) && (ppdev->iBitmapFormat == BMF_8BPP))
|
|
{
|
|
prb = BRUSHOBJ_pvAllocRbrush(pbo,
|
|
sizeof(RBRUSH) + (TOTAL_BRUSH_SIZE * ppdev->cjPel));
|
|
if (prb == NULL)
|
|
goto ReturnFalse;
|
|
|
|
// Initialize the fields we need:
|
|
|
|
prb->fl = 0;
|
|
|
|
for (i = 0; i < MAX_BOARDS; i++)
|
|
{
|
|
prb->apbe[i] = 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((1, "Realizing un-translated brush"));
|
|
|
|
// The pattern is the same colour depth as the screen, and
|
|
// there's no translation to be done:
|
|
|
|
cj = (8 * ppdev->cjPel); // Every pattern is 8 pels wide
|
|
|
|
for (i = 8; i != 0; i--)
|
|
{
|
|
RtlCopyMemory(pjDst, pjSrc, cj);
|
|
|
|
pjSrc += lSrcDelta;
|
|
pjDst += cj;
|
|
}
|
|
}
|
|
else if (ppdev->iBitmapFormat == BMF_8BPP)
|
|
{
|
|
DISPDBG((1, "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++];
|
|
}
|
|
|
|
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).
|
|
|
|
goto ReturnFalse;
|
|
}
|
|
}
|
|
else if (iPatternFormat == BMF_1BPP)
|
|
{
|
|
DISPDBG((1, "Realizing 1bpp brush"));
|
|
|
|
// We word align the monochrome bitmap so that every row starts
|
|
// on a new word (so that we can do word writes later to transfer
|
|
// the bitmap):
|
|
|
|
for (i = 4; i != 0; i--)
|
|
{
|
|
// The P9000 uses a monochrome 16x16 pattern, but we're
|
|
// given an 8x8 source pattern. So copy each source row
|
|
// horizontally.
|
|
//
|
|
// This works for the P9100 too, because although it supports
|
|
// only an 8x8 monochrome pattern, it ignores the high byte
|
|
// in every word.
|
|
|
|
jSrc = *pjSrc;
|
|
pjSrc += lSrcDelta;
|
|
|
|
// The pattern register we use has little-endian byte ordering:
|
|
|
|
*(pjDst ) = jSrc;
|
|
*(pjDst + 1) = jSrc;
|
|
|
|
jSrc = *pjSrc;
|
|
pjSrc += lSrcDelta;
|
|
|
|
*(pjDst + 2) = jSrc;
|
|
*(pjDst + 3) = jSrc;
|
|
|
|
pjDst += 4;
|
|
}
|
|
|
|
pulXlate = pxlo->pulXlate;
|
|
prb->fl = RBRUSH_2COLOR;
|
|
|
|
// The P9100 require that colours be 'packed' into a dword.
|
|
// We do it here rather than when we go to draw because
|
|
// we may draw using the same brush multiple times...
|
|
|
|
PACK_COLOR(ppdev, pulXlate[0], ulColor);
|
|
prb->ulColor[0] = ulColor;
|
|
|
|
PACK_COLOR(ppdev, pulXlate[1], ulColor);
|
|
prb->ulColor[1] = ulColor;
|
|
}
|
|
else
|
|
{
|
|
DISPDBG((1, "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++;
|
|
}
|
|
|
|
pjSrc += lSrcDelta - 4;
|
|
}
|
|
}
|
|
|
|
ReturnTrue:
|
|
|
|
// The last time I checked, GDI took some 500 odd instructions to
|
|
// get from here back to whereever we called 'BRUSHOBJ_pvGetRbrush'.
|
|
// We can at least use this time to get some overlap between the
|
|
// CPU and the display hardware: we'll initialize the 72x72 off-
|
|
// screen cache entry now, which will keep the accelerator busy for
|
|
// a while.
|
|
|
|
if (!prb->fl & (RBRUSH_2COLOR | RBRUSH_4COLOR))
|
|
{
|
|
ASSERTDD(ppdev->bEnabled, "Realizing brush when in full-screen?");
|
|
|
|
vSlowPatRealize(ppdev, prb);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
ReturnFalse:
|
|
|
|
if (psoPattern != NULL)
|
|
{
|
|
DISPDBG((1, "Failed realization -- Type: %li Format: %li cx: %li cy: %li",
|
|
psoPattern->iType, psoPattern->iBitmapFormat,
|
|
psoPattern->sizlBitmap.cx, psoPattern->sizlBitmap.cy));
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vAssertModeBrushCache
|
|
*
|
|
* Resets the brush cache when we exit out of full-screen.
|
|
\**************************************************************************/
|
|
|
|
VOID vAssertModeBrushCache(
|
|
PDEV* ppdev,
|
|
BOOL bEnable)
|
|
{
|
|
BRUSHENTRY* pbe;
|
|
CIRCLEENTRY* pce;
|
|
LONG i;
|
|
BYTE* pjBase;
|
|
|
|
if (bEnable)
|
|
{
|
|
// Invalidate the brush cache:
|
|
|
|
pbe = &ppdev->abe[0];
|
|
|
|
for (i = ppdev->cBrushCache; i != 0; i--)
|
|
{
|
|
pbe->prbVerify = NULL;
|
|
pbe++;
|
|
}
|
|
|
|
// Invalidate the circle cache:
|
|
|
|
pce = &ppdev->ace[0];
|
|
|
|
for (i = TOTAL_CIRCLE_COUNT; i != 0; i--)
|
|
{
|
|
pce->rcfxCircle.xLeft = 0;
|
|
pce->rcfxCircle.xRight = 0;
|
|
pce++;
|
|
}
|
|
|
|
// Download our favourite pattern for doing solid fills when
|
|
// running 16bpp on the P9000:
|
|
|
|
if ((ppdev->flStat & STAT_UNACCELERATED) &&
|
|
(ppdev->iBitmapFormat == BMF_16BPP))
|
|
{
|
|
pjBase = ppdev->pjBase;
|
|
|
|
CP_WAIT(ppdev, pjBase);
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
CP_PATTERN(ppdev, pjBase, i, 0xAAAAAAAA);
|
|
}
|
|
|
|
// Anchor the pattern origin, too:
|
|
|
|
CP_PATTERN_ORGX(ppdev, pjBase, 0);
|
|
CP_PATTERN_ORGY(ppdev, pjBase, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************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;
|
|
LONG j;
|
|
CIRCLEENTRY* pce;
|
|
|
|
// On the P9000, we draw coloured patterns using screen-to-screen
|
|
// copies. When a coloured pattern is used, we first expand the
|
|
// 8 x 8 pattern to a 64 x 64 pattern in off-screen memory; we
|
|
// then use this as the basis for our screen-to-screen blts to the
|
|
// target rectangle. The off-screen 64 x 64 pattern is cached for
|
|
// future use.
|
|
//
|
|
// Coloured patterns are used primarily at 8bpp, for dithers. The
|
|
// P9100 has direct support for 4-coloured patterns at 8bpp, which
|
|
// allows it to to draw any dithered colours using the hardware
|
|
// (our dithers are always a maximum of 4 colours). Consequently,
|
|
// we only use the off-screen brush cache on the P9000, and only
|
|
// at 8bpp.
|
|
|
|
if (P9000(ppdev) && (ppdev->flStat & STAT_8BPP))
|
|
{
|
|
// Typically, we'll be running at 1024x768x256 on a 1meg board,
|
|
// giving us off-screen memory of the dimension 1024x253 (accounting
|
|
// for the space taken by the hardware pointer). If we allocate
|
|
// the brush cache as one long one-high row of brushes, the heap
|
|
// manager would shave that amount off the largest chunk of memory
|
|
// we could allocate (meaning the largest bitmap potentially stored
|
|
// in off-screen memory couldn't be larger than 253 - 64 = 189 pels
|
|
// high, but it could be 1024 wide).
|
|
//
|
|
// To make this more square, I want to shave off a left-side chunk
|
|
// for the brush cache, and I want at least 8 brushes cached.
|
|
// Since floor(253/64) = 3, we'll allocate a 3 x 3 cache:
|
|
|
|
poh = pohAllocatePermanent(ppdev,
|
|
SLOW_BRUSH_CACHE_DIM * SLOW_BRUSH_ALLOCATION,
|
|
SLOW_BRUSH_CACHE_DIM * SLOW_BRUSH_ALLOCATION);
|
|
|
|
if (poh == NULL)
|
|
goto ReturnTrue; // See note about why we can return TRUE...
|
|
|
|
ppdev->cBrushCache = SLOW_BRUSH_COUNT;
|
|
|
|
pbe = &ppdev->abe[0]; // Points to where we'll put the first brush
|
|
// cache entry
|
|
for (i = 0; i < SLOW_BRUSH_CACHE_DIM; i++)
|
|
{
|
|
for (j = 0; j < SLOW_BRUSH_CACHE_DIM; j++)
|
|
{
|
|
pbe->x = poh->x + (i * SLOW_BRUSH_ALLOCATION);
|
|
pbe->y = poh->y + (j * SLOW_BRUSH_ALLOCATION);
|
|
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->flStat |= STAT_BRUSH_CACHE;
|
|
}
|
|
|
|
// Now allocate our circle cache.
|
|
//
|
|
// Note that we don't have to initially mark the entries as invalid,
|
|
// as the ppdev was zero-filled, and so we are assured that every
|
|
// 'rcfxBound' will be {0, 0, 0, 0}, which will never match any
|
|
// circle when looking for a matching entry.
|
|
|
|
poh = pohAllocatePermanent(ppdev, CIRCLE_ALLOCATION_CX * TOTAL_CIRCLE_COUNT,
|
|
CIRCLE_ALLOCATION_CY);
|
|
if (poh == NULL)
|
|
goto ReturnTrue;
|
|
|
|
pce = &ppdev->ace[0]; // Points to where we'll put the first circle
|
|
// cache entry
|
|
for (i = 0; i < TOTAL_CIRCLE_COUNT; i++)
|
|
{
|
|
pce->x = poh->x + (i * CIRCLE_ALLOCATION_CX);
|
|
pce->y = poh->y;
|
|
pce++;
|
|
}
|
|
|
|
ppdev->flStat |= STAT_CIRCLE_CACHE;
|
|
|
|
ReturnTrue:
|
|
|
|
// Invalidate our caches and initialize our high-colour pattern:
|
|
|
|
vAssertModeBrushCache(ppdev, TRUE);
|
|
|
|
// 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'
|
|
}
|