2025-04-27 07:49:33 -04:00

704 lines
22 KiB
C

/******************************Module*Header*******************************\
* Module Name: Stroke.c
*
* DrvStrokePath for VGA driver
*
* Copyright (c) 1992-1995 Microsoft Corporation
\**************************************************************************/
#include "driver.h"
#include "lines.h"
// External calls
VOID vSetStrips(ULONG, ULONG);
VOID vClearStrips(ULONG);
#define MIN(A,B) ((A) < (B) ? (A) : (B))
// Prototypes to go to the screen:
VOID vStripSolidHorizontal(STRIP*, LINESTATE*, LONG*);
VOID vStripSolidHorizontalSet(STRIP*, LINESTATE*, LONG*);
VOID vStripSolidVertical(STRIP*, LINESTATE*, LONG*);
VOID vStripSolidDiagonalHorizontal(STRIP*, LINESTATE*, LONG*);
VOID vStripSolidDiagonalVertical(STRIP*, LINESTATE*, LONG*);
VOID vStripStyledHorizontal(STRIP*, LINESTATE*, LONG*);
VOID vStripStyledVertical(STRIP*, LINESTATE*, LONG*);
VOID vStripMaskedHorizontal(STRIP*, LINESTATE*, LONG*);
VOID vStripMaskedVertical(STRIP*, LINESTATE*, LONG*);
PFNSTRIP gapfnStripSolidSet[] = {
// Special strip drawers for solid lines with SET style ROPs:
vStripSolidHorizontalSet,
vStripSolidVertical,
vStripSolidDiagonalHorizontal,
vStripSolidDiagonalVertical
};
PFNSTRIP gapfnStrip[] = {
// The order of these first 3 sets of strip drawers is determined by
// the FL_STYLE_MASK bits of the line flags:
vStripSolidHorizontal,
vStripSolidVertical,
vStripSolidDiagonalHorizontal,
vStripSolidDiagonalVertical,
vStripStyledHorizontal,
vStripStyledVertical,
NULL, // Diagonal goes here
NULL, // Diagonal goes here
vStripMaskedHorizontal,
vStripMaskedVertical,
NULL, // Diagonal goes here
NULL, // Diagonal goes here
};
STYLEPOS gaspAlternateStyle[] = { 1 };
// Prototypes to go to a device-format bitmap:
VOID vBitmapSolidHorizontal(STRIP*, LINESTATE*, LONG*);
VOID vBitmapSolidVertical(STRIP*, LINESTATE*, LONG*);
VOID vBitmapSolidDiagonal(STRIP*, LINESTATE*, LONG*);
VOID vBitmapStyledHorizontal(STRIP*, LINESTATE*, LONG*);
VOID vBitmapStyledVertical(STRIP*, LINESTATE*, LONG*);
VOID vCatchDFB(STRIP*, LINESTATE*, LONG*);
PFNSTRIP gapfnCatchDFB[] = {
vCatchDFB,
vCatchDFB,
vCatchDFB,
vCatchDFB
};
PFNSTRIP gapfnDFB[] = {
vBitmapSolidHorizontal,
vBitmapSolidVertical,
vBitmapSolidDiagonal,
vBitmapSolidDiagonal,
vBitmapStyledHorizontal,
vBitmapStyledVertical,
NULL, // Diagonal goes here
NULL // Diagonal goes here
};
// For two-pass ROPs:
VOID vCatchTwoPass(STRIP*, LINESTATE*, LONG*);
PFNSTRIP gapfnCatchTwoPass[] = {
vCatchTwoPass,
vCatchTwoPass,
vCatchTwoPass,
vCatchTwoPass
};
// VGA ulVgaMode constants:
#define DR_SET 0x00
#define DR_AND 0x08
#define DR_OR 0x10
#define DR_XOR 0x18
// Bit flag set if two passes needed:
#define DR_2PASS 0x80
// Table to convert ROP to usable information:
static struct {
ULONG ulColorAnd;
ULONG ulColorXor;
ULONG ulVgaMode;
} arop[] = {
{0x00, 0xff, DR_SET}, // 1 R2_WHITE
{0x00, 0x00, DR_SET}, // 0 R2_BLACK
{0xff, 0xff, DR_AND|DR_2PASS}, // DPon R2_NOTMERGEPEN Dest invert + DPna
{0xff, 0xff, DR_AND}, // DPna R2_MASKNOTPEN
{0xff, 0xff, DR_SET}, // PN R2_NOTCOPYPEN
{0xff, 0x00, DR_AND|DR_2PASS}, // PDna R2_MASKPENNOT Dest invert + DPa
{0x00, 0xff, DR_XOR}, // Dn R2_NOT Invert dest without pen
{0xff, 0x00, DR_XOR}, // DPx R2_XORPEN
{0xff, 0xff, DR_OR|DR_2PASS}, // DPan R2_NOTMASKPEN Dest invert + DPno
{0xff, 0x00, DR_AND}, // DPa R2_MASKPEN
{0xff, 0xff, DR_XOR}, // DPxn R2_NOTXORPEN DPxn == DPnx
{0x00, 0x00, DR_OR}, // D R2_NOP Silliness!
{0xff, 0xff, DR_OR}, // DPno R2_MERGENOTPEN
{0xff, 0x00, DR_SET}, // P R2_COPYPEN
{0xff, 0x00, DR_OR|DR_2PASS}, // PDno R2_MERGEPENNOT Dest invert + DPo
{0xff, 0x00, DR_OR}, // DPo R2_MERGEPEN
};
// The gaulAndXorTable contains and-masks and xor-masks for setting
// pels to one of four possible values. The and-masks have been
// inverted, and are stored in the low byte of each word. The
// xor-masks are stored in the high bytes.
//
// ROP XOR ~AND
// -------------------
// DDx 00 FF Set to zero
// Dn FF 00 Not destination
// D 00 00 Leave alone
// DDxn FF FF Set to one
ULONG gaulAndXorTable[] = {
0x00ff, // Set to zero
0xff00, // Not destination
0x0000, // Leave alone
0xffff // Set to one
};
ULONG gaulInitMasksLtoR[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f };
ULONG gaulInitMasksRtoL[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
/******************************Public*Routine******************************\
* BOOL DrvStrokePath(pso, ppo, pco, pxo, pbo, pptlBrushOrg, pla, mix)
*
* Strokes the path.
*
\**************************************************************************/
BOOL DrvStrokePath(
SURFOBJ* pso,
PATHOBJ* ppo,
CLIPOBJ* pco,
XFORMOBJ* pxo,
BRUSHOBJ* pbo,
POINTL* pptlBrushOrg,
LINEATTRS* pla,
MIX mix)
{
STYLEPOS aspLtoR[STYLE_MAX_COUNT];
STYLEPOS aspRtoL[STYLE_MAX_COUNT];
LINESTATE ls;
PFNSTRIP* apfn;
FLONG fl;
PDEVSURF pdsurf;
ULONG ulVgaMode;
UNREFERENCED_PARAMETER(pxo);
UNREFERENCED_PARAMETER(pptlBrushOrg);
// Get the device ready:
pdsurf = (PDEVSURF) pso->dhsurf;
fl = 0;
// Look after styling initialization:
if (pla->fl & LA_ALTERNATE)
{
ASSERT(pla->pstyle == (FLOAT_LONG*) NULL && pla->cstyle == 0,
"DrvStrokePath: Non-empty style array for PS_ALTERNATE");
ls.spNext = HIWORD(pla->elStyleState.l) & 1;
// Light first pixel if
// a multiple of 2
ls.spTotal = 1; // Sum of style array
ls.spTotal2 = 2; // Twice the sum
ls.xyDensity = 1; // Each 'dot' is one
// pixel long
if (pdsurf->iFormat == BMF_DFB)
{
fl |= (FL_ARBITRARYSTYLED);
ls.cStyle = 1; // Size of style array
ls.aspRtoL = &gaspAlternateStyle[0]; // Right-to-left array
ls.aspLtoR = &gaspAlternateStyle[0]; // Left-to-right array
ls.ulStartMask = 0; // First pel is a dash
}
else
{
fl |= (FL_ALTERNATESTYLED | FL_MASKSTYLED);
}
}
else if (pla->pstyle != (FLOAT_LONG*) NULL)
{
PFLOAT_LONG pstyle;
pstyle = &pla->pstyle[pla->cstyle];
ls.xyDensity = STYLE_DENSITY;
ls.spTotal = 0;
while (pstyle-- > pla->pstyle)
{
ls.spTotal += pstyle->l;
}
ls.spTotal *= STYLE_DENSITY;
ls.spTotal2 = 2 * ls.spTotal;
// Compute starting style position (this is guaranteed not to overflow):
ls.spNext = HIWORD(pla->elStyleState.l) * STYLE_DENSITY +
LOWORD(pla->elStyleState.l);
// We optimize the style arrays that are even length and sum to 8. We
// also don't have any masked strip drawers for DFBs:
if (ls.spTotal == (8 * STYLE_DENSITY) &&
(pla->cstyle & 1) == 0 &&
pdsurf->iFormat != BMF_DFB)
{
// Do "masked" styles.
// -------------------
//
// This case is merely an optimization; you can remove it and
// all style lines will still work. It just so happens that the
// default styles (such as PS_DOT, PS_DASHDOT, etc.) will all
// sum to 8, and there are some optimizations we can do on the
// VGA for that case.
//
// We make an 8 bit mask that represents each style unit in the
// style array, and we pass this to the "Masked" strip drawers
// (instead of calling the "Styled" strip drawers which still
// handle the arbitrary styles).
LONG ii;
ls.ulStyleMaskLtoR = 0;
ls.ulStyleMaskRtoL = 0;
for (pstyle = pla->pstyle, ii = 8; ii > 0;)
{
LONG l1 = (pstyle++)->l;
LONG l2 = (pstyle++)->l;
ls.ulStyleMaskRtoL >>= l1 + l2;
ls.ulStyleMaskRtoL |= gaulInitMasksRtoL[l2];
ls.ulStyleMaskLtoR <<= l1 + l2;
ls.ulStyleMaskLtoR |= gaulInitMasksLtoR[l2];
ii -= (l1 + l2);
}
// Replicate byte and initialize to style position zero:
ls.ulStyleMaskLtoR |= (ls.ulStyleMaskLtoR << 8);
ls.ulStyleMaskLtoR |= (ls.ulStyleMaskLtoR << 16);
ls.ulStyleMaskRtoL |= (ls.ulStyleMaskRtoL << 8);
ls.ulStyleMaskRtoL |= (ls.ulStyleMaskRtoL << 16);
// Check if we should start with a gap or start with a dash:
if (pla->fl & LA_STARTGAP)
{
ls.ulStyleMaskLtoR = ~ls.ulStyleMaskLtoR;
ls.ulStyleMaskRtoL = ~ls.ulStyleMaskRtoL;
}
// Initialize some other state:
fl |= FL_MASKSTYLED;
}
else
// Okay, we've got to do it the slow way:
{
// Handle Arbitrary Styles
// -----------------------
//
// Because arbitrary styles are new to Win32, many apps won't
// know about them and will use the default styles (which will
// be handled by the "Masked" optimization case above), and so
// this code path won't get exercised too often. (See GDI's
// ExtCreatePen API.)
//
// But you still have to handle them, and do them right!
FLOAT_LONG* pstyle;
STYLEPOS* pspDown;
STYLEPOS* pspUp;
fl |= FL_ARBITRARYSTYLED;
ls.cStyle = pla->cstyle;
ls.aspRtoL = aspRtoL;
ls.aspLtoR = aspLtoR;
if (pla->fl & LA_STARTGAP)
ls.ulStartMask = 0xffffffffL;
else
ls.ulStartMask = 0L;
pstyle = pla->pstyle;
pspDown = &ls.aspRtoL[ls.cStyle - 1];
pspUp = &ls.aspLtoR[0];
// We always draw strips left-to-right, but styles have to be laid
// down in the direction of the original line. This means that in
// the strip code we have to traverse the style array in the
// opposite direction;
while (pspDown >= &ls.aspRtoL[0])
{
*pspDown = pstyle->l * STYLE_DENSITY;
*pspUp = *pspDown;
pspUp++;
pspDown--;
pstyle++;
}
}
}
if (pdsurf->iFormat == BMF_DFB)
{
ULONG iStripIndex;
// Handle device-format bitmaps.
// The table we give to the line routine will have all entries pointing
// to the vCatchDFB function; it will intercept the strip draw call and
// in turn break it into 4 calls to the appropriate DFB strip drawing
// routines, once for each plane.
apfn = gapfnCatchDFB;
ls.ulDrawModeIndex = mix - 1;
ls.lNextPlane = pdsurf->lNextPlane;
ls.iColor = pbo->iSolidColor;
iStripIndex = 4 * ((fl & FL_ARBITRARYSTYLED) >> FL_STYLE_SHIFT);
ls.apfnStrip = &gapfnDFB[iStripIndex];
}
else
{
ULONG iColor;
ULONG iStripIndex;
fl |= FL_PHYSICAL_DEVICE;
// Compute the pointer to the correct strip drawing table. We
// have a special table for solid lines done with VGA mode == SET:
iStripIndex = 4 * ((fl & FL_STYLE_MASK) >> FL_STYLE_SHIFT);
apfn = &gapfnStrip[iStripIndex];
mix &= 0xf;
ulVgaMode = arop[mix].ulVgaMode;
if (ulVgaMode == DR_SET && iStripIndex == 0)
apfn = &gapfnStripSolidSet[0];
// Compute the correct color, based on the ROP we're doing:
iColor = (pbo->iSolidColor & arop[mix].ulColorAnd)
^ (arop[mix].ulColorXor);
if (!(ulVgaMode & DR_2PASS))
vSetStrips(iColor, ulVgaMode);
else
{
// If the ROP requires 2 passes, we sneakily change our strip
// table pointer to point to only our own routine, and it
// handles calling the appropriate strip routines twice:
ls.ulVgaMode = ulVgaMode ^ DR_2PASS;
ls.iColor = iColor;
ls.apfnStrip = apfn;
apfn = gapfnCatchTwoPass;
}
}
// Set up to enumerate the path:
if (pco->iDComplexity != DC_COMPLEX)
{
RECTL arclClip[4]; // For rectangular clipping
PATHDATA pd;
RECTL* prclClip = (RECTL*) NULL;
BOOL bMore;
ULONG cptfx;
POINTFIX ptfxStartFigure;
POINTFIX ptfxLast;
POINTFIX* pptfxFirst;
POINTFIX* pptfxBuf;
if (pco->iDComplexity == DC_RECT)
{
fl |= FL_SIMPLE_CLIP;
arclClip[0] = pco->rclBounds;
// FL_FLIP_D:
arclClip[1].top = pco->rclBounds.left;
arclClip[1].left = pco->rclBounds.top;
arclClip[1].bottom = pco->rclBounds.right;
arclClip[1].right = pco->rclBounds.bottom;
// FL_FLIP_V:
arclClip[2].top = -pco->rclBounds.bottom + 1;
arclClip[2].left = pco->rclBounds.left;
arclClip[2].bottom = -pco->rclBounds.top + 1;
arclClip[2].right = pco->rclBounds.right;
// FL_FLIP_V | FL_FLIP_D:
arclClip[3].top = pco->rclBounds.left;
arclClip[3].left = -pco->rclBounds.bottom + 1;
arclClip[3].bottom = pco->rclBounds.right;
arclClip[3].right = -pco->rclBounds.top + 1;
prclClip = arclClip;
}
PATHOBJ_vEnumStart(ppo);
do {
bMore = PATHOBJ_bEnum(ppo, &pd);
cptfx = pd.count;
if (cptfx == 0)
{
break;
}
if (pd.flags & PD_BEGINSUBPATH)
{
ptfxStartFigure = *pd.pptfx;
pptfxFirst = pd.pptfx;
pptfxBuf = pd.pptfx + 1;
cptfx--;
}
else
{
pptfxFirst = &ptfxLast;
pptfxBuf = pd.pptfx;
}
if (pd.flags & PD_RESETSTYLE)
ls.spNext = 0;
// We have to check for cptfx == 0 because the only point in the
// subpath may have been the StartFigure point:
if (cptfx > 0)
{
if (!bLines(pdsurf,
pptfxFirst,
pptfxBuf,
(RUN*) NULL,
cptfx,
&ls,
prclClip,
apfn,
fl))
return(FALSE);
}
ptfxLast = pd.pptfx[pd.count - 1];
if (pd.flags & PD_CLOSEFIGURE)
{
if (!bLines(pdsurf,
&ptfxLast,
&ptfxStartFigure,
(RUN*) NULL,
1,
&ls,
prclClip,
apfn,
fl))
return(FALSE);
}
} while (bMore);
if (fl & FL_STYLED)
{
// Save the style state:
ULONG ulHigh;
ULONG ulLow;
// !!! The engine handles unnormalized style states. This can
// !!! be removed. Might have to remove some asserts in the
// !!! engine.
// Masked styles don't normalize the style state. It's a good
// thing to do, so let's do it now:
if ((ULONG) ls.spNext >= (ULONG) ls.spTotal2)
ls.spNext = (ULONG) ls.spNext % (ULONG) ls.spTotal2;
ulHigh = ls.spNext / ls.xyDensity;
ulLow = ls.spNext % ls.xyDensity;
pla->elStyleState.l = MAKELONG(ulLow, ulHigh);
}
}
else
{
// Local state for path enumeration:
BOOL bMore;
union {
BYTE aj[offsetof(CLIPLINE, arun) + RUN_MAX * sizeof(RUN)];
CLIPLINE cl;
} cl;
fl |= FL_COMPLEX_CLIP;
// We use the clip object when non-simple clipping is involved:
PATHOBJ_vEnumStartClipLines(ppo, pco, pso, pla);
do {
bMore = PATHOBJ_bEnumClipLines(ppo, sizeof(cl), &cl.cl);
if (cl.cl.c != 0)
{
if (fl & FL_STYLED)
{
ls.spComplex = HIWORD(cl.cl.lStyleState) * ls.xyDensity
+ LOWORD(cl.cl.lStyleState);
}
if (!bLines(pdsurf,
&cl.cl.ptfxA,
&cl.cl.ptfxB,
&cl.cl.arun[0],
cl.cl.c,
&ls,
(RECTL*) NULL,
apfn,
fl))
return(FALSE);
}
} while (bMore);
}
if (fl & FL_PHYSICAL_DEVICE)
vClearStrips(ulVgaMode);
return(TRUE);
}
/******************************Public*Routine******************************\
* VOID vCatchTwoPass(pstrip, pls, plStripEnd)
*
* Handles ROPs that cannot be done in a single pass using the VGA
* hardware. In order not to have a check in our main drawing loop for
* two-pass ROPs, we change the strip function table so that this function
* intercepts the call to draw the strips.
*
* This routine then figures out the appropriate actual strip drawer, and
* makes two calls to it: first to invert the destination, then to do the
* rest of the ROP.
*
\**************************************************************************/
VOID vCatchTwoPass(STRIP* pstrip, LINESTATE* pls, LONG* plStripEnd)
{
BYTE* pjScreen = pstrip->pjScreen;
BYTE jBitMask = pstrip->jBitMask;
BYTE jStyleMask = pstrip->jStyleMask;
STYLEPOS* psp = pstrip->psp;
STYLEPOS spRemaining = pstrip->spRemaining;
// Figure out the actual strip routine we're supposed to call:
PFNSTRIP pfn = pls->apfnStrip[(pstrip->flFlips & FL_STRIP_MASK) >>
FL_STRIP_SHIFT];
// On the first pass, we invert the destination:
vSetStrips(0xff, DR_XOR);
(*pfn)(pstrip, pls, plStripEnd);
// We reset our strip variables for the second pass and handle the rest
// of the ROP:
pstrip->pjScreen = pjScreen;
pstrip->jBitMask = jBitMask;
pstrip->jStyleMask = jStyleMask;
pstrip->psp = psp;
pstrip->spRemaining = spRemaining;
vSetStrips(pls->iColor, pls->ulVgaMode);
(*pfn)(pstrip, pls, plStripEnd);
}
/******************************Public*Routine******************************\
* VOID vCatchDFB(pstrip, pls, plStripEnd)
*
* Intercepts the strip draw call and in turn breaks it into 4 calls
* to the appropriate DFB strip drawing routines, once for each plane.
*
\**************************************************************************/
VOID vCatchDFB(STRIP* pstrip, LINESTATE* pls, LONG* plStripEnd)
{
BYTE* pjScreen = pstrip->pjScreen;
BYTE jBitMask = pstrip->jBitMask;
BYTE jStyleMask = pstrip->jStyleMask;
STYLEPOS* psp = pstrip->psp;
STYLEPOS spRemaining = pstrip->spRemaining;
BYTE* pjScreenNextPass;
LONG lNextPlane = pls->lNextPlane;
ULONG ulPen0 = gaulAndXorTable[
(pls->ulDrawModeIndex) & 3];
ULONG ulPen1 = gaulAndXorTable[
(pls->ulDrawModeIndex >> 2) & 3];
// Figure out the actual strip routine we're supposed to call:
PFNSTRIP pfn = pls->apfnStrip[(pstrip->flFlips & FL_STRIP_MASK) >>
FL_STRIP_SHIFT];
pstrip->ulBitmapROP = (pls->iColor & 1) ? ulPen1 : ulPen0;
(*pfn)(pstrip, pls, plStripEnd); // Plane 0
pjScreenNextPass = pstrip->pjScreen;
pstrip->ulBitmapROP = (pls->iColor & 2) ? ulPen1 : ulPen0;
pjScreen += lNextPlane;
pstrip->pjScreen = pjScreen;
pstrip->jBitMask = jBitMask;
pstrip->jStyleMask = jStyleMask;
pstrip->psp = psp;
pstrip->spRemaining = spRemaining;
(*pfn)(pstrip, pls, plStripEnd); // Plane 1
pstrip->ulBitmapROP = (pls->iColor & 4) ? ulPen1 : ulPen0;
pjScreen += lNextPlane;
pstrip->pjScreen = pjScreen;
pstrip->jBitMask = jBitMask;
pstrip->jStyleMask = jStyleMask;
pstrip->psp = psp;
pstrip->spRemaining = spRemaining;
(*pfn)(pstrip, pls, plStripEnd); // Plane 2
pstrip->ulBitmapROP = (pls->iColor & 8) ? ulPen1 : ulPen0;
pjScreen += lNextPlane;
pstrip->pjScreen = pjScreen;
pstrip->jBitMask = jBitMask;
pstrip->jStyleMask = jStyleMask;
pstrip->psp = psp;
pstrip->spRemaining = spRemaining;
(*pfn)(pstrip, pls, plStripEnd); // Plane 3
pstrip->pjScreen = pjScreenNextPass;
}