556 lines
18 KiB
C
556 lines
18 KiB
C
/******************************Module*Header*******************************\
|
|
* Module Name: brush.c
|
|
*
|
|
* Contains the brush realization and dithering code.
|
|
*
|
|
* Copyright (c) 1992-1995 Microsoft Corporation
|
|
\**************************************************************************/
|
|
#include "driver.h"
|
|
|
|
#define OBR_REALIZED 1
|
|
#define OBR_4BPP 2
|
|
|
|
// aulDefBitMapping is used to translate packed pel into Planar
|
|
|
|
extern ULONG aulDefBitMapping[8];
|
|
|
|
typedef VOID (*PFNV)();
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DrvRealizeBrush
|
|
*
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL DrvRealizeBrush(
|
|
BRUSHOBJ *pbo,
|
|
SURFOBJ *psoTarget,
|
|
SURFOBJ *psoPattern,
|
|
SURFOBJ *psoMask,
|
|
XLATEOBJ *pxlo,
|
|
ULONG iHatch)
|
|
{
|
|
ULONG cx; // Height of pattern surface
|
|
ULONG cy; // Width of pattern surface
|
|
LONG cbScan; // Width in bytes of one scan
|
|
PVOID pvBits; // Source bits
|
|
ULONG *pulXlate; // Color translation
|
|
HBITMAP hbmTmp; // Temp bmp handle for brush conversion
|
|
BRUSHINST *pbri; // pointer to where realization goes
|
|
BYTE jBkColor, jFgColor; // local copies of mono attributes
|
|
PFNV pfnConvert; // function pointer to mono conversion
|
|
BYTE jColors[2]; // place holder for special color->mono
|
|
// conversion.
|
|
BYTE jTemp;
|
|
BOOL bConversion = FALSE; // True if we converted to 4bpp
|
|
|
|
//
|
|
// Allocate space for the realization.
|
|
//
|
|
if ((pbri = BRUSHOBJ_pvAllocRbrush(pbo,sizeof(BRUSHINST))) ==
|
|
(BRUSHINST *)NULL) {
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// If the dither-and-realize flag is set, the brush hasn't been dithered
|
|
// yet and there is no pattern; we get to do it all here at once, with
|
|
// the lower 24 bits being the RGB to dither. We can generate the dither
|
|
// directly into our internal format, and we can know without counting how
|
|
// many colors there are in the dither.
|
|
//
|
|
if (iHatch & RB_DITHERCOLOR) {
|
|
|
|
vRealizeDitherPattern(pbri, iHatch);
|
|
return(TRUE);
|
|
}
|
|
|
|
cx = psoPattern->sizlBitmap.cx;
|
|
cy = psoPattern->sizlBitmap.cy;
|
|
|
|
if ((cy != 8) || (cx > 16))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
pbri->RealWidth = (BYTE)cx;
|
|
|
|
switch (psoPattern->iBitmapFormat)
|
|
{
|
|
case BMF_1BPP:
|
|
case BMF_4BPP:
|
|
case BMF_8BPP:
|
|
break;
|
|
|
|
default:
|
|
if ((cx != 8) && (cx != 16))
|
|
return(FALSE);
|
|
|
|
// Convert to 4bpp
|
|
|
|
psoPattern = DrvConvertBrush(psoPattern, &hbmTmp, pxlo, cx, cy);
|
|
if (psoPattern == (SURFOBJ *)NULL)
|
|
return(FALSE);
|
|
|
|
bConversion = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Setup the pointer to the bits, and the scan-to-scan advance direction.
|
|
//
|
|
cbScan = psoPattern->lDelta;
|
|
pvBits = psoPattern->pvScan0;
|
|
|
|
if (psoPattern->iBitmapFormat == BMF_1BPP)
|
|
{
|
|
switch (cx) {
|
|
case 16:
|
|
if ((bShrinkPattern)((BYTE *)pvBits, cbScan))
|
|
{
|
|
pbri->RealWidth = (BYTE)cx = 8;
|
|
} else {
|
|
pfnConvert = vMono16Wide;
|
|
break;
|
|
}
|
|
case 8:
|
|
pfnConvert = vMono8Wide;
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
pbri->usStyle = BRI_MONO_PATTERN;
|
|
pbri->fjAccel = 0;
|
|
pbri->jBkColor = (BYTE)pxlo->pulXlate[0];
|
|
pbri->jFgColor = (BYTE)pxlo->pulXlate[1];
|
|
pbri->Width = 16;
|
|
pbri->pPattern = (BYTE *)&(pbri->ajPattern[0]);
|
|
pbri->jOldBrushRealized = 0;
|
|
|
|
(*pfnConvert)(pbri->pPattern, pvBits, cbScan);
|
|
|
|
if (bQuickPattern(pbri->pPattern, 8))
|
|
{
|
|
pbri->Height = 2;
|
|
pbri->YShiftValue = 1;
|
|
}
|
|
else
|
|
{
|
|
pbri->Height = 8;
|
|
pbri->YShiftValue = 3;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
if ((cx != 8) && (cx != 16))
|
|
return(FALSE);
|
|
|
|
// We always have a table, because the input is 4 or 8-bpp; see if we even
|
|
// need to pay attention to it.
|
|
pulXlate = NULL;
|
|
if (!(pxlo->flXlate & XO_TRIVIAL)) {
|
|
pulXlate = pxlo->pulXlate;
|
|
}
|
|
|
|
if ((psoPattern->iBitmapFormat == BMF_4BPP) &&
|
|
(psoTarget->iType != STYPE_DEVBITMAP) &&
|
|
(CountColors(pvBits, cx, (WORD *)&jColors, cbScan) == 2)) {
|
|
|
|
if ((cx == 16) && (bShrinkPattern)((BYTE *)pvBits, cbScan)) {
|
|
cx = 8;
|
|
pbri->RealWidth = (BYTE)cx;
|
|
}
|
|
|
|
pbri->usStyle = BRI_MONO_PATTERN;
|
|
pbri->Height = 8;
|
|
pbri->YShiftValue = 3;
|
|
pbri->Width = 16;
|
|
pbri->fjAccel = 0;
|
|
pbri->pPattern = (BYTE *)&(pbri->ajPattern[0]);
|
|
pbri->jOldBrushRealized = OBR_4BPP;
|
|
|
|
if (pulXlate != (PULONG)NULL) {
|
|
|
|
jBkColor = (BYTE)pulXlate[jColors[0]];
|
|
jFgColor = (BYTE)pulXlate[jColors[1]];
|
|
} else {
|
|
|
|
jBkColor = jColors[0];
|
|
jFgColor = jColors[1];
|
|
}
|
|
|
|
if (jBkColor > jFgColor) {
|
|
pbri->jBkColor = jBkColor;
|
|
pbri->jFgColor = jFgColor;
|
|
jTemp = jColors[0];
|
|
} else {
|
|
pbri->jBkColor = jFgColor;
|
|
pbri->jFgColor = jBkColor;
|
|
jTemp = jColors[1];
|
|
}
|
|
|
|
|
|
vBrush2ColorToMono(pbri->pPattern, (BYTE *)pvBits, cbScan,
|
|
cx, jTemp);
|
|
|
|
if (bQuickPattern(pbri->pPattern, 8))
|
|
{
|
|
pbri->Height = 2;
|
|
pbri->YShiftValue = 1;
|
|
}
|
|
|
|
vCopyOrgBrush(&(pbri->ajC0[0]),pvBits, cbScan, pxlo);
|
|
|
|
if (bConversion) {
|
|
EngUnlockSurface(psoPattern);
|
|
EngDeleteSurface((HSURF)hbmTmp);
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} else if (cx != 8) {
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
pbri->pPattern = (BYTE *)&(pbri->ajC0[0]);
|
|
|
|
// At this point we know we have an 8x8 color pattern in either a
|
|
// 4bpp or 8bpp format.
|
|
|
|
if (psoPattern->iBitmapFormat == BMF_4BPP){
|
|
|
|
vConvert4BppToPlanar(pbri->pPattern, (BYTE *)pvBits, cbScan, pulXlate);
|
|
|
|
} else { // 8bpp
|
|
|
|
vConvert8BppToPlanar(pbri->pPattern, (BYTE *)pvBits, cbScan, pulXlate);
|
|
}
|
|
|
|
// Set proper accelerators in the brush for the output code.
|
|
|
|
pbri->usStyle = BRI_COLOR_PATTERN; // Brush style is arbitrary pattern
|
|
pbri->Height = 8;
|
|
pbri->YShiftValue = 3;
|
|
pbri->Width = 8;
|
|
pbri->fjAccel = 0;
|
|
pbri->jOldBrushRealized = OBR_REALIZED|OBR_4BPP;
|
|
|
|
if (bConversion) {
|
|
EngUnlockSurface(psoPattern);
|
|
EngDeleteSurface((HSURF)hbmTmp);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* DrvConvertBrush()
|
|
*
|
|
* Converts a brush to a 4bpp bmp
|
|
*
|
|
\****************************************************************************/
|
|
|
|
SURFOBJ *DrvConvertBrush(
|
|
SURFOBJ *psoPattern,
|
|
HBITMAP *phbmTmp,
|
|
XLATEOBJ *pxlo,
|
|
ULONG cx,
|
|
ULONG cy)
|
|
{
|
|
SURFOBJ *psoTmp;
|
|
RECTL rclTmp;
|
|
SIZEL sizlTmp;
|
|
POINTL ptl;
|
|
|
|
ptl.x = 0;
|
|
ptl.y = 0;
|
|
rclTmp.top = 0;
|
|
rclTmp.left = 0;
|
|
rclTmp.right = cx;
|
|
sizlTmp.cx = cx;
|
|
rclTmp.bottom = cy;
|
|
sizlTmp.cy = cy;
|
|
|
|
// Create bitmap in our compatible format.
|
|
|
|
*phbmTmp = EngCreateBitmap(sizlTmp, (LONG) cx / 2, BMF_4BPP, 0, NULL);
|
|
|
|
if ((*phbmTmp) && ((psoTmp = EngLockSurface((HSURF)*phbmTmp)) != NULL))
|
|
{
|
|
if (EngCopyBits(psoTmp, psoPattern, NULL, pxlo, &rclTmp, &ptl))
|
|
return(psoTmp);
|
|
|
|
EngUnlockSurface(psoTmp);
|
|
EngDeleteSurface((HSURF)*phbmTmp);
|
|
}
|
|
|
|
return((SURFOBJ *)NULL);
|
|
}
|
|
|
|
|
|
/****************************************************************************\
|
|
* vCopyOrgBrush
|
|
*
|
|
* When we realize a mono or 2 color brush, we copy the original 4bpp brush
|
|
* to the ajC0 area of the realized brush. If we are called to do a
|
|
* rop that we don't directly support, vConvertBrush will be called to
|
|
* convert the orginal 4bpp brush to a planar brush that the blt compiler can
|
|
* use. Since this is a rare event, we do this on request instead of at
|
|
* realization time.
|
|
*
|
|
\****************************************************************************/
|
|
|
|
VOID vCopyOrgBrush(
|
|
BYTE *pDest,
|
|
BYTE *pSrc,
|
|
LONG lScan,
|
|
XLATEOBJ *pxlo)
|
|
{
|
|
ULONG *pulXlate, *pulDest;
|
|
BYTE jByte, jColor;
|
|
INT i;
|
|
|
|
if ((pxlo != NULL) && (pxlo->flXlate & XO_TABLE)) {
|
|
pulXlate = pxlo->pulXlate;
|
|
|
|
for (i=0; i<8; i++) {
|
|
INT j;
|
|
for (j=0; j<4; j++) {
|
|
jColor = *pSrc++; // Get Next byte
|
|
jByte = jColor;
|
|
|
|
jByte = (BYTE) pulXlate[jByte & 0xf];
|
|
jByte |= (BYTE) pulXlate[(jColor >> 4) & 0xf] << 4;
|
|
*pDest++ = jByte;
|
|
}
|
|
pSrc += lScan - 4;
|
|
}
|
|
} else {
|
|
pulDest = (ULONG *)pDest;
|
|
|
|
for (i=0;i<8;i++) {
|
|
*pulDest = *(ULONG *)pSrc;
|
|
|
|
pSrc += lScan;
|
|
pulDest++;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************\
|
|
* vConvertBrush()
|
|
*
|
|
* This called when we are going to do a rop3 with a non-solid brush. We have
|
|
* to convert our brush back to the old blt compiler format in order for this
|
|
* blt to work properly.
|
|
*
|
|
\****************************************************************************/
|
|
|
|
BOOL bConvertBrush(
|
|
BRUSHINST *pbri)
|
|
{
|
|
BYTE jPattern[32];
|
|
if (pbri->jOldBrushRealized & OBR_REALIZED)
|
|
return(TRUE);
|
|
|
|
//
|
|
// The blt compiler only handles 8x8
|
|
//
|
|
if (pbri->RealWidth != 8)
|
|
return(FALSE);
|
|
|
|
if (pbri->jOldBrushRealized & OBR_4BPP) {
|
|
// It's stored as a DIB, so convert the DIB to planar form
|
|
memcpy(&(jPattern[0]), &(pbri->ajC0[0]), 32);
|
|
vConvert4BppToPlanar(&(pbri->ajC0[0]), &(jPattern[0]), 4, NULL);
|
|
pbri->jOldBrushRealized |= OBR_REALIZED;
|
|
|
|
return(TRUE);
|
|
} else {
|
|
// It started life as a monochrome bitmap, so we have nothing else with
|
|
// which to work. Convert the monochrome to planar, based on the colors
|
|
// with which the brush was realized.
|
|
// Only 8-wide can be handled by the blt compiler
|
|
{
|
|
PBYTE pMono = pbri->pPattern;
|
|
PBYTE pMonoTemp;
|
|
PBYTE pColor = (BYTE *)&(pbri->ajC0[0]);
|
|
ULONG fgColor = pbri->jFgColor;
|
|
ULONG bkColor = pbri->jBkColor;
|
|
ULONG PlaneMask = 1;
|
|
INT i, j;
|
|
|
|
ASSERT((pbri->Width == 16), "mono brush width != 16");
|
|
|
|
// Expand the pattern for each plane, according to the fg & bg
|
|
for (i=0; i<4; i++) {
|
|
if (fgColor & PlaneMask) {
|
|
if (bkColor & PlaneMask) {
|
|
// fg = bg = 1 for this plane
|
|
*(LONG *)pColor = -1;
|
|
*(((LONG *)pColor) + 1) = -1;
|
|
pColor += 8; // point to next plane's storage loc
|
|
} else {
|
|
// fg = 1, bg = 0 for this plane
|
|
pMonoTemp = pMono;
|
|
for (j = 0; j < 8; j++) {
|
|
*pColor++ = *pMonoTemp;
|
|
pMonoTemp += 2;
|
|
}
|
|
}
|
|
} else {
|
|
if (bkColor & PlaneMask) {
|
|
// fg = 0, bg = 1 for this plane
|
|
pMonoTemp = pMono;
|
|
for (j = 0; j < 8; j++) {
|
|
*pColor++ = ~*pMonoTemp;
|
|
pMonoTemp += 2;
|
|
}
|
|
} else {
|
|
// fg = bg = 0 for this plane
|
|
*(ULONG *)pColor = 0;
|
|
*(((ULONG *)pColor) + 1) = 0;
|
|
pColor += 8; // point to next plane's storage loc
|
|
}
|
|
}
|
|
PlaneMask <<= 1; // select next plane
|
|
}
|
|
|
|
pbri->usStyle = BRI_COLOR_PATTERN;
|
|
pbri->Height = 8;
|
|
pbri->YShiftValue = 3;
|
|
pbri->Width = 8;
|
|
pbri->fjAccel = 0;
|
|
pbri->pPattern = (BYTE *)&(pbri->ajC0[0]);
|
|
|
|
// Mark that this brush is realized and is a full-color brush from
|
|
// now on (ideally, we'd keep both the mono and color versions
|
|
// around, but this is not a major performance issue and rarely
|
|
// happens)
|
|
pbri->jOldBrushRealized |= OBR_REALIZED | OBR_4BPP;
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* vRealizeDitherPattern()
|
|
*
|
|
* Generates an 8x8 dither pattern, in our internal realization format, for
|
|
* the color RGBToDither. Note that the high byte of RGBToDither does not
|
|
* need to be set to zero, because ComputeSubspaces ignores it.
|
|
*
|
|
* Note: this routine can be made a great deal faster for 2-color patterns by
|
|
* simply prestoring all 64 possible monochrome patterns, for a cost of 512
|
|
* bytes, then looking up the appropriate pattern based on the # of foreground
|
|
* pixels in the dither. This would save all the time required to reverse-
|
|
* engineer the monochrome pattern out of the 4-bpp DIB, as well as almost all
|
|
* the time required to generate the dithered DIB. Color patterns could be
|
|
* made faster by not having vDitherColor pack the DIB into 4-bpp form; this
|
|
* would save the DIB packing time, but then we'd need new DIB->planar
|
|
* routines. This is not as big a win as the monochrome case. So far, I
|
|
* haven't figured out a way to dither directly into planar format more
|
|
* efficiently than dithering to the DIB and then converting to planar form.
|
|
*
|
|
* It would also be faster to generate the DIB back from the monochrome
|
|
* pattern in the monochrome case, so we don't have to copy the DIB for
|
|
* later translation (this would be required if we dithered two-color
|
|
* patterns directly into monochrome bitmaps, to support the blt compiler).
|
|
*
|
|
\****************************************************************************/
|
|
VOID vRealizeDitherPattern(BRUSHINST *pbri, ULONG RGBToDither)
|
|
{
|
|
BYTE jDitherBuffer[32];
|
|
ULONG ulNumVertices;
|
|
VERTEX_DATA vVertexData[4];
|
|
VERTEX_DATA *pvVertexData;
|
|
|
|
// BYTE jBkColor, jFgColor; // local copies of mono attributes
|
|
// BYTE jColors[2]; // place holder for special color->mono conversion
|
|
|
|
|
|
// Generate the dither into a stack buffer
|
|
|
|
// Calculate what color subspaces are involved in the dither
|
|
pvVertexData = ComputeSubspaces(RGBToDither, 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 = pvVertexData - vVertexData;
|
|
// # of vertices with more than zero pixels in the dither
|
|
|
|
// Do the actual dithering
|
|
vDitherColor((ULONG *)jDitherBuffer, vVertexData, pvVertexData,
|
|
ulNumVertices);
|
|
|
|
pbri->RealWidth = 8;
|
|
pbri->Height = 8;
|
|
pbri->YShiftValue = 3;
|
|
pbri->fjAccel = 0;
|
|
|
|
// Special-case monochrome by storing monochrome masks for acceleration
|
|
|
|
if (ulNumVertices == 2) {
|
|
|
|
pbri->usStyle = BRI_MONO_PATTERN;
|
|
pbri->Width = 16;
|
|
pbri->pPattern = (BYTE *)&(pbri->ajPattern[0]);
|
|
pbri->jOldBrushRealized = OBR_4BPP; // 4-plane form not yet realized
|
|
|
|
// vBrush2ColorToMono requires that the smaller color map to the
|
|
// 1-bits, so switch the foreground and background if necessary
|
|
// to make this the case
|
|
pbri->jBkColor = (BYTE) vVertexData[0].ulVertex;
|
|
pbri->jFgColor = (BYTE) vVertexData[1].ulVertex;
|
|
|
|
if (vVertexData[0].ulVertex < vVertexData[1].ulVertex) {
|
|
|
|
pbri->jBkColor = (BYTE) vVertexData[1].ulVertex;
|
|
pbri->jFgColor = (BYTE) vVertexData[0].ulVertex;
|
|
}
|
|
|
|
|
|
// Convert the brush to a monochrome bitmap
|
|
vBrush2ColorToMono(pbri->pPattern, jDitherBuffer, 4,
|
|
8, pbri->jBkColor);
|
|
|
|
// Shrink the brush to 2 scans high if possible
|
|
if (bQuickPattern(pbri->pPattern, 8))
|
|
{
|
|
pbri->Height = 2;
|
|
pbri->YShiftValue = 1;
|
|
}
|
|
|
|
// Remember the original DIB in case we need the 4-plane form later
|
|
vCopyOrgBrush(&(pbri->ajC0[0]), jDitherBuffer, 4, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
// The realized (planar) pattern goes in ajC0
|
|
pbri->pPattern = (BYTE *)&(pbri->ajC0[0]);
|
|
|
|
// Because we're dithering, we always have an 8x8 color pattern in a 4bpp
|
|
// format, with no translation
|
|
vConvert4BppToPlanar(pbri->pPattern, jDitherBuffer, 4, NULL);
|
|
|
|
// Set proper accelerators in the brush for the output code.
|
|
pbri->usStyle = BRI_COLOR_PATTERN; // brush style is 4-bpp planar
|
|
pbri->Width = 8;
|
|
pbri->jOldBrushRealized = OBR_REALIZED | OBR_4BPP;
|
|
// the 4-plane realization that we use
|
|
// is the same format the blt compiler
|
|
// wants
|
|
}
|