/******************************Module*Header*******************************\ * Module Name: fastfill.c * * Draws fast unclipped, non-complex rectangles. * * Copyright (c) 1993-1995 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******************************\ * BOOL bFastFill * * Draws a non-complex, unclipped polygon. 'Non-complex' is defined as * having only two edges that are monotonic increasing in 'y'. That is, * the polygon cannot have more than one disconnected segment on any given * scan. Note that the edges of the polygon can self-intersect, so hourglass * shapes are permissible. This restriction permits this routine to run two * simultaneous DDAs, and no sorting of the edges is required. * * Note that NT's fill convention is different from that of Win 3.1 or 4.0. * With the additional complication of fractional end-points, our convention * is the same as in 'X-Windows'. But a DDA is a DDA is a DDA, so once you * figure out how we compute the DDA terms for NT, you're golden. * * Returns TRUE if the polygon was drawn; FALSE if the polygon was complex. * \**************************************************************************/ BOOL bFastFill( PDEV* ppdev, LONG cEdges, // Includes close figure edge POINTFIX* pptfxFirst, ULONG ulHwMix, ULONG iSolidColor, RBRUSH* prb, POINTL* pptlBrush) { BYTE* pjBase; ULONG ulStat; 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) ULONG* pulPattern; ULONG ulPattern; LONG iEdge; LONG lQuotient; LONG lRemainder; EDGEDATA aed[2]; // DDA terms and stuff EDGEDATA* ped; // Most polygons will be convex, and so pjBase = ppdev->pjBase; if (iSolidColor == -1) { ///////////////////////////////////////////////////////////////// // Setup for patterns // Make sure accelerator is not buy for all types. // CP_WAIT(ppdev, pjBase); if (P9000(ppdev)) { CP_PATTERN_ORGX(ppdev, pjBase, ppdev->xOffset + pptlBrush->x); CP_PATTERN_ORGY(ppdev, pjBase, ppdev->yOffset + pptlBrush->y); CP_BACKGROUND(ppdev, pjBase, prb->ulColor[0]); CP_FOREGROUND(ppdev, pjBase, prb->ulColor[1]); pulPattern = &prb->aulPattern[0]; for (i = 0; i < 4; i++) { ulPattern = *pulPattern++; CP_PATTERN(ppdev, pjBase, i, ulPattern); CP_PATTERN(ppdev, pjBase, i + 4, ulPattern); } if (((ulHwMix >> 8) & 0xff) == (ulHwMix & 0xff)) { ulHwMix = gaulP9000OpaqueFromRop2[(ulHwMix & 0x3C) >> 2]; CP_RASTER(ppdev, pjBase, ulHwMix | P9000_ENABLE_PATTERN); } else { ulHwMix = gaulP9000TransparentFromRop2[(ulHwMix & 0x3C) >> 2]; CP_RASTER(ppdev, pjBase, ulHwMix | P9000_ENABLE_PATTERN); } } else { CP_PATTERN_ORGX(ppdev, pjBase, -(ppdev->xOffset + pptlBrush->x)); CP_PATTERN_ORGY(ppdev, pjBase, -(ppdev->yOffset + pptlBrush->y)); CP_COLOR0_FAST(ppdev, pjBase, prb->ulColor[0]); CP_COLOR1_FAST(ppdev, pjBase, prb->ulColor[1]); CP_PATTERN(ppdev, pjBase, 0, prb->aulPattern[0]); CP_PATTERN(ppdev, pjBase, 1, prb->aulPattern[1]); CP_PATTERN(ppdev, pjBase, 2, prb->aulPattern[2]); CP_PATTERN(ppdev, pjBase, 3, prb->aulPattern[3]); if (prb->fl & RBRUSH_2COLOR) { if (((ulHwMix >> 8) & 0xff) == (ulHwMix & 0xff)) { CP_RASTER(ppdev, pjBase, (ulHwMix & 0xff) | P9100_ENABLE_PATTERN); } else { CP_RASTER(ppdev, pjBase, (ulHwMix & 0xff) | P9100_ENABLE_PATTERN | P9100_TRANSPARENT_PATTERN); } } else { CP_COLOR2_FAST(ppdev, pjBase, prb->ulColor[2]); CP_COLOR3_FAST(ppdev, pjBase, prb->ulColor[3]); CP_RASTER(ppdev, pjBase, (ulHwMix & 0xff) | P9100_ENABLE_PATTERN | P9100_FOUR_COLOR_PATTERN); } } } else { ///////////////////////////////////////////////////////////////// // Setup the hardware for solid colours CP_WAIT(ppdev, pjBase); if (P9000(ppdev)) { CP_BACKGROUND(ppdev, pjBase, iSolidColor); CP_RASTER(ppdev, pjBase, ulHwMix); } else { CP_COLOR0(ppdev, pjBase, iSolidColor); CP_RASTER(ppdev, pjBase, ulHwMix & 0xff); } } // We can do all integer triangles and convex quadrilaterals directly // with the hardware: if (cEdges <= 4) { ASSERTDD(cEdges >= 3, "What's with the degenerate polygon?"); if ((((pptfxFirst)->x | (pptfxFirst)->y | (pptfxFirst+1)->x | (pptfxFirst+1)->y | (pptfxFirst+2)->x | (pptfxFirst+2)->y) & 0xF) == 0) { if (cEdges == 3) { CP_METATRI(ppdev, pjBase, (pptfxFirst)->x >> 4, (pptfxFirst)->y >> 4); CP_METATRI(ppdev, pjBase, (pptfxFirst+1)->x >> 4, (pptfxFirst+1)->y >> 4); CP_METATRI(ppdev, pjBase, (pptfxFirst+2)->x >> 4, (pptfxFirst+2)->y >> 4); CP_START_QUAD(ppdev, pjBase); return(TRUE); } else { if ((((pptfxFirst+3)->x | (pptfxFirst+3)->y) & 0xF) == 0) { CP_METAQUAD(ppdev, pjBase, (pptfxFirst)->x >> 4, (pptfxFirst)->y >> 4); CP_METAQUAD(ppdev, pjBase, (pptfxFirst+1)->x >> 4, (pptfxFirst+1)->y >> 4); CP_METAQUAD(ppdev, pjBase, (pptfxFirst+2)->x >> 4, (pptfxFirst+2)->y >> 4); CP_METAQUAD(ppdev, pjBase, (pptfxFirst+3)->x >> 4, (pptfxFirst+3)->y >> 4); CP_START_QUAD_STAT(ppdev, pjBase, ulStat); return(!(ulStat & QUADFAIL)); } } } } ///////////////////////////////////////////////////////////////// // See if the polygon is 'non-complex' 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: ///////////////////////////////////////////////////////////////// // Some Initialization yTrapezoid = (pptfxTop->y + 15) >> 4; // 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; NewTrapezoid: ///////////////////////////////////////////////////////////////// // DDA initialization 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; // 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)) { ///////////////////////////////////////////////////////////////// // Vertical-edge special case ContinueVertical: if (aed[LEFT].x < aed[RIGHT].x) { CP_METARECT(ppdev, pjBase, aed[LEFT].x, yTrapezoid); yTrapezoid += cyTrapezoid; CP_METARECT(ppdev, pjBase, aed[RIGHT].x, yTrapezoid); CP_START_QUAD_WAIT(ppdev, pjBase); } else if (aed[LEFT].x == aed[RIGHT].x) { // If the rectangle was too thin to light any pels, we still // have to advance the y current position: yTrapezoid += cyTrapezoid; } 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 NewTrapezoid; } while (TRUE) { ///////////////////////////////////////////////////////////////// // Run the DDAs if (aed[LEFT].x < aed[RIGHT].x) { CP_METARECT(ppdev, pjBase, aed[LEFT].x, yTrapezoid); yTrapezoid++; CP_METARECT(ppdev, pjBase, aed[RIGHT].x, yTrapezoid); CP_START_QUAD_WAIT(ppdev, pjBase); 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 NewTrapezoid; } else if (aed[LEFT].x == aed[RIGHT].x) { yTrapezoid++; 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; } } }