|
|
/******************************Module*Header*******************************\
* Module Name: fastfill.c * * Fills solid-coloured, unclipped, non-complex rectangles. * * Copyright (c) 1993-1994 Microsoft Corporation \**************************************************************************/
#include "precomp.h"
#define RIGHT 0
#define LEFT 1
#define SWAP(a, b, tmp) { tmp = a; a = b; b = tmp; }
typedef struct _EDGEDATA { LONG x; // Current x position
LONG dx; // # pixels to advance x on each scan
LONG lError; // Current DDA error
LONG lErrorUp; // DDA error increment on each scan
LONG lErrorDown; // DDA error adjustment
POINTFIX* pptfx; // Points to start of current edge
LONG dptfx; // Delta (in bytes) from pptfx to next point
LONG cy; // Number of scans to go for this edge
} EDGEDATA; /* ed, ped */
/******************************Public*Routine******************************\
* bFastFill * * Draws a non-complex, unclipped polygon. * * Returns TRUE if the polygon was drawn; FALSE if the polygon was complex. * \**************************************************************************/
BOOL bFastFill( PPDEV ppdev, LONG cEdges, // Includes close figure edge
POINTFIX* pptfxFirst, ULONG ulHwMix, ULONG iSolidColor) { LONG yTrapezoid; // Top scan for next trapezoid
LONG cyTrapezoid; // Number of scans in current trapezoid
LONG yStart; // y-position of start point in current edge
LONG dM; // Edge delta in FIX units in x direction
LONG dN; // Edge delta in FIX units in y direction
LONG i; POINTFIX* pptfxLast; // Points to the last point in the polygon array
POINTFIX* pptfxTop; // Points to the top-most point in the polygon
POINTFIX* pptfxOld; // Start point in current edge
POINTFIX* pptfxScan; // Current edge pointer for finding pptfxTop
LONG cScanEdges; // Number of edges scanned to find pptfxTop
// (doesn't include the closefigure edge)
LONG iEdge; LONG lQuotient; LONG lRemainder;
EDGEDATA aed[2]; // DDA terms and stuff
EDGEDATA* ped;
pptfxScan = pptfxFirst; pptfxTop = pptfxFirst; // Assume for now that the first
// point in path is the topmost
pptfxLast = pptfxFirst + cEdges - 1;
// 'pptfxScan' will always point to the first point in the current
// edge, and 'cScanEdges' will the number of edges remaining, including
// the current one:
cScanEdges = cEdges - 1; // The number of edges, not counting close figure
if ((pptfxScan + 1)->y > pptfxScan->y) { // Collect all downs:
do { if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y >= pptfxScan->y);
// Collect all ups:
do { if (--cScanEdges == 0) goto SetUpForFillingCheck; pptfxScan++; } while ((pptfxScan + 1)->y <= pptfxScan->y);
// Collect all downs:
pptfxTop = pptfxScan;
do { if ((pptfxScan + 1)->y > pptfxFirst->y) break;
if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y >= pptfxScan->y);
return(FALSE); } else { // Collect all ups:
do { pptfxTop++; // We increment this now because we
// want it to point to the very last
// point if we early out in the next
// statement...
if (--cScanEdges == 0) goto SetUpForFilling; } while ((pptfxTop + 1)->y <= pptfxTop->y);
// Collect all downs:
pptfxScan = pptfxTop; do { if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y >= pptfxScan->y);
// Collect all ups:
do { if ((pptfxScan + 1)->y < pptfxFirst->y) break;
if (--cScanEdges == 0) goto SetUpForFilling; pptfxScan++; } while ((pptfxScan + 1)->y <= pptfxScan->y);
return(FALSE); }
SetUpForFillingCheck:
// We check to see if the end of the current edge is higher
// than the top edge we've found so far:
if ((pptfxScan + 1)->y < pptfxTop->y) pptfxTop = pptfxScan + 1;
SetUpForFilling:
yTrapezoid = (pptfxTop->y + 15) >> 4;
// We initialize the hardware for the colour, mix, pixel operation,
// rectangle height of one, and the y position for the first scan:
IO_FIFO_WAIT(ppdev, 5); IO_CUR_Y(ppdev, yTrapezoid); IO_FRGD_COLOR(ppdev, (INT) iSolidColor); IO_FRGD_MIX(ppdev, FOREGROUND_COLOR | (WORD) ulHwMix); IO_PIX_CNTL(ppdev, ALL_ONES); IO_MIN_AXIS_PCNT(ppdev, 0);
// Make sure we initialize the DDAs appropriately:
aed[LEFT].cy = 0; aed[RIGHT].cy = 0;
// For now, guess as to which is the left and which is the right edge:
aed[LEFT].dptfx = -(LONG) sizeof(POINTFIX); aed[RIGHT].dptfx = sizeof(POINTFIX); aed[LEFT].pptfx = pptfxTop; aed[RIGHT].pptfx = pptfxTop;
NextEdge:
// We loop through this routine on a per-trapezoid basis.
for (iEdge = 1; iEdge >= 0; iEdge--) { ped = &aed[iEdge]; if (ped->cy == 0) { // Need a new DDA:
do { cEdges--; if (cEdges < 0) return(TRUE);
// Find the next left edge, accounting for wrapping:
pptfxOld = ped->pptfx; ped->pptfx = (POINTFIX*) ((BYTE*) ped->pptfx + ped->dptfx);
if (ped->pptfx < pptfxFirst) ped->pptfx = pptfxLast; else if (ped->pptfx > pptfxLast) ped->pptfx = pptfxFirst;
// Have to find the edge that spans yTrapezoid:
ped->cy = ((ped->pptfx->y + 15) >> 4) - yTrapezoid;
// With fractional coordinate end points, we may get edges
// that don't cross any scans, in which case we try the
// next one:
} while (ped->cy <= 0);
// 'pptfx' now points to the end point of the edge spanning
// the scan 'yTrapezoid'.
dN = ped->pptfx->y - pptfxOld->y; dM = ped->pptfx->x - pptfxOld->x;
ASSERTDD(dN > 0, "Should be going down only");
// Compute the DDA increment terms:
if (dM < 0) { dM = -dM; if (dM < dN) // Can't be '<='
{ ped->dx = -1; ped->lErrorUp = dN - dM; } else { QUOTIENT_REMAINDER(dM, dN, lQuotient, lRemainder);
ped->dx = -lQuotient; // - dM / dN
ped->lErrorUp = lRemainder; // dM % dN
if (ped->lErrorUp > 0) { ped->dx--; ped->lErrorUp = dN - ped->lErrorUp; } } } else { if (dM < dN) // Can't be '<='
{ ped->dx = 0; ped->lErrorUp = dM; } else { QUOTIENT_REMAINDER(dM, dN, lQuotient, lRemainder);
ped->dx = lQuotient; // dM / dN
ped->lErrorUp = lRemainder; // dM % dN
} }
ped->lErrorDown = dN; // DDA limit
ped->lError = -1; // Error is initially zero (add dN - 1 for
// the ceiling, but subtract off dN so that
// we can check the sign instead of comparing
// to dN)
ped->x = pptfxOld->x; yStart = pptfxOld->y;
if ((yStart & 15) != 0) { // Advance to the next integer y coordinate
for (i = 16 - (yStart & 15); i != 0; i--) { ped->x += ped->dx; ped->lError += ped->lErrorUp; if (ped->lError >= 0) { ped->lError -= ped->lErrorDown; ped->x++; } } }
if ((ped->x & 15) != 0) { ped->lError -= ped->lErrorDown * (16 - (ped->x & 15)); ped->x += 15; // We'll want the ceiling in just a bit...
}
// Chop off those fractional bits:
ped->x >>= 4; ped->lError >>= 4; } }
cyTrapezoid = min(aed[LEFT].cy, aed[RIGHT].cy); // # of scans in this trap
aed[LEFT].cy -= cyTrapezoid; aed[RIGHT].cy -= cyTrapezoid; yTrapezoid += cyTrapezoid; // Top scan in next trap
// If the left and right edges are vertical, simply output as
// a rectangle:
if (((aed[LEFT].lErrorUp | aed[RIGHT].lErrorUp) == 0) && ((aed[LEFT].dx | aed[RIGHT].dx) == 0) && (cyTrapezoid > 1)) { LONG lWidth;
ContinueVertical:
lWidth = aed[RIGHT].x - aed[LEFT].x - 1; if (lWidth >= 0) { IO_FIFO_WAIT(ppdev, 5);
IO_MAJ_AXIS_PCNT(ppdev, lWidth); IO_MIN_AXIS_PCNT(ppdev, cyTrapezoid - 1); IO_CUR_X(ppdev, aed[LEFT].x); IO_CMD(ppdev, RECTANGLE_FILL | DRAWING_DIR_TBLRXM | DRAW | DIR_TYPE_XY | LAST_PIXEL_ON | MULTIPLE_PIXELS | WRITE); IO_MIN_AXIS_PCNT(ppdev, 0); } else if (lWidth == -1) { // If the rectangle was too thin to light any pels, we still
// have to advance the y current position:
IO_FIFO_WAIT(ppdev, 1); IO_CUR_Y(ppdev, yTrapezoid - cyTrapezoid + 1); } else { LONG lTmp; POINTFIX* pptfxTmp;
SWAP(aed[LEFT].x, aed[RIGHT].x, lTmp); SWAP(aed[LEFT].cy, aed[RIGHT].cy, lTmp); SWAP(aed[LEFT].dptfx, aed[RIGHT].dptfx, lTmp); SWAP(aed[LEFT].pptfx, aed[RIGHT].pptfx, pptfxTmp); goto ContinueVertical; }
goto NextEdge; }
while (TRUE) { LONG lWidth;
// The very first time through, make sure we set x:
lWidth = aed[RIGHT].x - aed[LEFT].x - 1; if (lWidth >= 0) { IO_FIFO_WAIT(ppdev, 3); IO_MAJ_AXIS_PCNT(ppdev, lWidth); IO_CUR_X(ppdev, aed[LEFT].x); IO_CMD(ppdev, RECTANGLE_FILL | DRAWING_DIR_TBLRXM | DRAW | DIR_TYPE_XY | LAST_PIXEL_ON | MULTIPLE_PIXELS | WRITE);
ContinueAfterZero:
// Advance the right wall:
aed[RIGHT].x += aed[RIGHT].dx; aed[RIGHT].lError += aed[RIGHT].lErrorUp;
if (aed[RIGHT].lError >= 0) { aed[RIGHT].lError -= aed[RIGHT].lErrorDown; aed[RIGHT].x++; }
// Advance the left wall:
aed[LEFT].x += aed[LEFT].dx; aed[LEFT].lError += aed[LEFT].lErrorUp;
if (aed[LEFT].lError >= 0) { aed[LEFT].lError -= aed[LEFT].lErrorDown; aed[LEFT].x++; }
cyTrapezoid--; if (cyTrapezoid == 0) goto NextEdge; } else if (lWidth == -1) { IO_FIFO_WAIT(ppdev, 1); IO_CUR_Y(ppdev, yTrapezoid - cyTrapezoid + 1); goto ContinueAfterZero; } else { // We certainly don't want to optimize for this case because we
// should rarely get self-intersecting polygons (if we're slow,
// the app gets what it deserves):
LONG lTmp; POINTFIX* pptfxTmp;
SWAP(aed[LEFT].x, aed[RIGHT].x, lTmp); SWAP(aed[LEFT].dx, aed[RIGHT].dx, lTmp); SWAP(aed[LEFT].lError, aed[RIGHT].lError, lTmp); SWAP(aed[LEFT].lErrorUp, aed[RIGHT].lErrorUp, lTmp); SWAP(aed[LEFT].lErrorDown, aed[RIGHT].lErrorDown, lTmp); SWAP(aed[LEFT].cy, aed[RIGHT].cy, lTmp); SWAP(aed[LEFT].dptfx, aed[RIGHT].dptfx, lTmp); SWAP(aed[LEFT].pptfx, aed[RIGHT].pptfx, pptfxTmp);
continue; } } }
|