/******************************Module*Header*******************************\ * Module Name: ffillddi.cxx * * routines for filling a polygon without building a region. * * Created: 12-Oct-1993 09:46:44 * Author: Eric Kutter [erick] * * Copyright (c) 1993-1999 Microsoft Corporation * \**************************************************************************/ #include "precomp.hxx" extern ULONG aulShiftFormat[]; extern ULONG aulMulFormat[]; typedef VOID (*PFN_FF)(PRECTL,ULONG,PVOID); typedef VOID (*PFN_FFROW)(LONG,PROW,ULONG,PVOID); PFN_PATBLT apfnPatRect[][3] = { { NULL, NULL, NULL }, { NULL, NULL, NULL }, { NULL, NULL, NULL }, { vPatCpyRect8, vPatNotRect8, vPatXorRect8 }, { vPatCpyRect8, vPatNotRect8, vPatXorRect8 }, { vPatCpyRect8, vPatNotRect8, vPatXorRect8 }, { vPatCpyRect8, vPatNotRect8, vPatXorRect8 } }; PFN_PATBLTROW apfnPatRow[][3] = { { NULL, NULL, NULL }, { NULL, NULL, NULL }, { NULL, NULL, NULL }, { vPatCpyRow8, vPatNotRow8, vPatXorRow8 }, { vPatCpyRow8, vPatNotRow8, vPatXorRow8 }, { vPatCpyRow8, vPatNotRow8, vPatXorRow8 }, { vPatCpyRow8, vPatNotRow8, vPatXorRow8 } }; BOOL bEngFastFillEnum( EPATHOBJ &epo, PRECTL prclClip, FLONG flOptions, PFN_FF pfn, PFN_FFROW pfnRow, PVOID pv); /******************************Public*Routine******************************\ * vPaintPath * * fill a path - This dispatches through bEngFastFillEnum which calls * back to either vPaintPathEnum or vPaintPathEnumRow. vPaintPathEnum * takes a list of rectangles, vPaintPathEnumRow takes a list of rows. * * History: * 12-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ typedef struct _PPAINT_PATH { PFN_SOLIDBLT pfn; PFN_SOLIDBLTROW pfnRow; LONG lDelta; ULONG cShift; ULONG iColor; PBYTE pjBits; } PAINT_PATH, *PPAINT_PATH; VOID vPaintPathEnum( PRECTL prcl, ULONG crcl, PVOID pv) { PPAINT_PATH ppp = (PPAINT_PATH)pv; (*ppp->pfn)( prcl, crcl, ppp->pjBits, ppp->lDelta, ppp->iColor, ppp->cShift); } VOID vPaintPathEnumRow( LONG yTop, PROW prow, ULONG cptl, PVOID pv) { PPAINT_PATH ppp = (PPAINT_PATH)pv; (*ppp->pfnRow)(prow,cptl,yTop,ppp->pjBits,ppp->iColor,ppp->lDelta,ppp->cShift); } BOOL bPaintPath( SURFACE *pSurf, PATHOBJ *ppo, PRECTL prclClip, ULONG iColor, BOOL bXor, FLONG flOptions) { PAINT_PATH pp; // Get the shift for the format pp.cShift = aulShiftFormat[pSurf->iFormat()]; // Promote the color to 32 bits switch(pSurf->iFormat()) { case BMF_1BPP: if (iColor) iColor = 0xFFFFFFFF; break; case BMF_4BPP: iColor = iColor | (iColor << 4); case BMF_8BPP: iColor = iColor | (iColor << 8); case BMF_16BPP: iColor = iColor | (iColor << 16); } if (bXor) if (pSurf->iFormat() == BMF_24BPP) { pp.pfn = vSolidXorRect24; pp.pfnRow = vSolidXorRow24; } else { pp.pfn = vSolidXorRect1; pp.pfnRow = vSolidXorRow1; } else if (pSurf->iFormat() == BMF_24BPP) { pp.pfn = vSolidFillRect24; pp.pfnRow = vSolidFillRow24; } else { pp.pfn = vSolidFillRect1; pp.pfnRow = vSolidFillRow1; } pp.pjBits = (PBYTE) pSurf->pvScan0(); pp.iColor = iColor; pp.lDelta = pSurf->lDelta(); return(bEngFastFillEnum( *(EPATHOBJ *)ppo, prclClip, flOptions, vPaintPathEnum, vPaintPathEnumRow, (PVOID)&pp)); } /******************************Public*Routine******************************\ * vBrushPath * * fill a path with a brush - This dispatches through bEngFastFillEnum which * calls back to either vBrushPathEnum or vBrushPathEnumRow. vBrushPathEnum * takes a list of rectangles, vBrushPathEnumRow takes a list of rows. * * History: * 12-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ typedef struct _PBRUSH_PATH { PFN_PATBLT pfn; PFN_PATBLTROW pfnRow; PATBLTFRAME pbf; } BRUSH_PATH, *PBRUSH_PATH; VOID vBrushPathEnum( PRECTL prcl, ULONG crcl, PVOID pv) { PBRUSH_PATH pbp = (PBRUSH_PATH)pv; for (UINT i = 0; i < crcl; i++) { pbp->pbf.pvObj = (PVOID) prcl++; (*pbp->pfn)(&pbp->pbf); } } VOID vBrushPathEnumRow( LONG yTop, PROW prow, ULONG cptl, PVOID pv) { PBRUSH_PATH pbp = (PBRUSH_PATH)pv; pbp->pbf.pvObj = (PVOID) prow; (*pbp->pfnRow)(&pbp->pbf,yTop,(INT)cptl); } BOOL bBrushPath( SURFACE *pSurf, PATHOBJ *ppo, PRECTL prclClip, BRUSHOBJ *pbo, POINTL *pptl, ULONG iMode, FLONG flOptions) { BRUSH_PATH bp; // Get the multiplier for the format bp.pbf.cMul = aulMulFormat[pSurf->iFormat()]; bp.pbf.pvTrg = pSurf->pvScan0(); bp.pbf.lDeltaTrg = pSurf->lDelta(); bp.pbf.pvPat = (PVOID) ((EBRUSHOBJ *) pbo)->pengbrush()->pjPat; bp.pbf.lDeltaPat = ((EBRUSHOBJ *) pbo)->pengbrush()->lDeltaPat; bp.pbf.cxPat = ((EBRUSHOBJ *) pbo)->pengbrush()->cxPat * bp.pbf.cMul; bp.pbf.cyPat = ((EBRUSHOBJ *) pbo)->pengbrush()->cyPat; bp.pbf.xPat = pptl->x * bp.pbf.cMul; bp.pbf.yPat = pptl->y; // Weird: The following code checks for xPat<0 and yPat<0, but it // doesn't check for xPat>=cxPat or yPat>=cyPat. Both cases are probably // handled lower down the call chain, but I can't be sure. So I've left the // code here (after correcting it). if (bp.pbf.xPat < 0) bp.pbf.xPat = bp.pbf.cxPat-1 - ((-bp.pbf.xPat - 1) % bp.pbf.cxPat); if (bp.pbf.yPat < 0) bp.pbf.yPat = bp.pbf.cyPat-1 - ((-bp.pbf.yPat - 1) % bp.pbf.cyPat); bp.pfn = apfnPatRect[pSurf->iFormat()][iMode]; bp.pfnRow = apfnPatRow[pSurf->iFormat()][iMode]; return(bEngFastFillEnum( *(EPATHOBJ *)ppo, prclClip, flOptions, vBrushPathEnum, vBrushPathEnumRow, (PVOID)&bp)); } /******************************Public*Routine******************************\ * vBrushPathN_8x8 * * fill a path with a brush - This dispatches through bEngFastFillEnum which * calls back to either vBrushPathEnum or vBrushPathEnumRow. vBrushPathEnum * takes a list of rectangles, vBrushPathEnumRow takes a list of rows. * * History: * 12-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ typedef struct _BRUSH_PATH_8x8 { PFN_PATBLT2 pfn; PATBLTFRAME pbf; } BRUSH_PATH_8x8, *PBRUSH_PATH_8x8; VOID vBrushPath4_8x8Enum( PRECTL prcl, ULONG crcl, PVOID pv) { PBRUSH_PATH_8x8 pb8 = (PBRUSH_PATH_8x8)pv; pb8->pbf.pvObj = (PVOID) prcl; vPatCpyRect4_8x8(&pb8->pbf, (INT) crcl); } VOID vBrushPath8_8x8Enum( PRECTL prcl, ULONG crcl, PVOID pv) { PBRUSH_PATH_8x8 pb8 = (PBRUSH_PATH_8x8)pv; pb8->pbf.pvObj = (PVOID) prcl; vPatCpyRect8_8x8(&pb8->pbf, (INT) crcl); } VOID vBrushPath4_8x8EnumRow( LONG yTop, PROW prow, ULONG cptl, PVOID pv) { PBRUSH_PATH_8x8 pb8 = (PBRUSH_PATH_8x8)pv; pb8->pbf.pvObj = (PVOID) prow; vPatCpyRow4_8x8(&pb8->pbf, yTop,(INT) cptl); } VOID vBrushPath8_8x8EnumRow( LONG yTop, PROW prow, ULONG cptl, PVOID pv) { PBRUSH_PATH_8x8 pb8 = (PBRUSH_PATH_8x8)pv; pb8->pbf.pvObj = (PVOID) prow; vPatCpyRow8_8x8(&pb8->pbf, yTop,(INT) cptl); } BOOL bBrushPathN_8x8( SURFACE *pSurf, PATHOBJ *ppo, PRECTL prclClip, BRUSHOBJ *pbo, POINTL *pptlBrush, ULONG iFormat, FLONG flOptions) { BRUSH_PATH_8x8 b8; PFN_FF pfnFF = vBrushPath4_8x8Enum; PFN_FFROW pfnFFRow = vBrushPath4_8x8EnumRow; b8.pbf.pvTrg = pSurf->pvScan0(); b8.pbf.lDeltaTrg = pSurf->lDelta(); b8.pbf.pvPat = (PVOID) ((EBRUSHOBJ *) pbo)->pengbrush()->pjPat; // Force the X and Y pattern origin coordinates into the ranges 0-7 and 0-7, // so we don't have to do modulo arithmetic all over again at a lower level b8.pbf.xPat = pptlBrush->x & 0x07; b8.pbf.yPat = pptlBrush->y & 0x07; // if format ius 8BPP then use 8Bpp fill routines if (iFormat == BMF_8BPP) { pfnFF = vBrushPath8_8x8Enum; pfnFFRow = vBrushPath8_8x8EnumRow; } return(bEngFastFillEnum( *(EPATHOBJ *)ppo, prclClip, flOptions, pfnFF, pfnFFRow, (PVOID)&b8)); } /******************************Public*Routine******************************\ * EngFastFill() * * Fill a path clipped to at most one rectangle. If the fill is a mode * that this routine supports, the vBrushPath, bPaintPath, or vBrushPath_8x8 * will be called. This routines setup a filling structure and call * the path enumeration code. The path enumeration code in turn, calls back * to a specific filling routine which fills either a list of rectangles * or a list of rows (left,right pairs). If it is a convex polygon with less * than 40 points, bFastFill is used, otherwise the slower bFill is used. * * returns: * -1 if unsupported mode for fastfill, should breakup into a region * true if the fill has been performed. * false an error occured * * History: * 12-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ LONG EngFastFill( SURFOBJ *pso, PATHOBJ *ppo, PRECTL prcl, BRUSHOBJ *pdbrush, POINTL *pptlBrush, MIX mix, FLONG flOptions) { PSURFACE pSurf = SURFOBJ_TO_SURFACE(pso); LONG lRes = -1; ROP4 rop4 = gaMix[(mix >> 8) & 0x0F]; rop4 = rop4 << 8; rop4 = rop4 | ((ULONG) gaMix[mix & 0x0F]); if (pso->iType == STYPE_BITMAP) { switch (rop4) { case 0x0000: // Black lRes = (LONG)bPaintPath(pSurf, ppo,prcl, 0, FALSE,flOptions); break; case 0x0F0F: // Pn if (pdbrush->iSolidColor != 0xFFFFFFFF) { lRes = (LONG)bPaintPath(pSurf,ppo, prcl, ~pdbrush->iSolidColor, FALSE,flOptions); } else if (pSurf->iFormat() >= BMF_8BPP) { if (pvGetEngRbrush(pdbrush)) // Can we use this brush? { if (((EBRUSHOBJ *) pdbrush)->pengbrush()->cxPat >= 4) { lRes = (LONG)bBrushPath(pSurf, ppo,prcl, pdbrush, pptlBrush, DPA_PATNOT,flOptions); } } } break; case 0x5555: // Dn lRes = (LONG)bPaintPath(pSurf, ppo,prcl, (ULONG)~0, TRUE,flOptions); break; case 0x5A5A: // DPx if (pdbrush->iSolidColor != 0xFFFFFFFF) { lRes = (LONG)bPaintPath(pSurf, ppo,prcl, pdbrush->iSolidColor, TRUE,flOptions); } else if (pSurf->iFormat() >= BMF_8BPP) { if (pvGetEngRbrush(pdbrush)) // Can we use this brush? { if (((EBRUSHOBJ *) pdbrush)->pengbrush()->cxPat >= 4) { lRes = (LONG)bBrushPath(pSurf, ppo, prcl, pdbrush, pptlBrush, DPA_PATXOR,flOptions); } } } break; case 0xAAAA: // D lRes = TRUE; break; case 0xF0F0: // P if (pdbrush->iSolidColor != 0xFFFFFFFF) { lRes = (LONG)bPaintPath(pSurf, ppo,prcl, pdbrush->iSolidColor, FALSE,flOptions); } else if ( (pSurf->iFormat() == BMF_4BPP) || (pSurf->iFormat() == BMF_8BPP) ) { // We only support 8x8 DIB4 patterns with SRCCOPY right now if (pvGetEngRbrush(pdbrush) != NULL) { if ((((EBRUSHOBJ *) pdbrush)->pengbrush()->cxPat == 8) && (((EBRUSHOBJ *) pdbrush)->pengbrush()->cyPat == 8)) { lRes = (LONG)bBrushPathN_8x8( pSurf, ppo, prcl, pdbrush, pptlBrush, pSurf->iFormat(), flOptions ); } } } else if (pSurf->iFormat() >= BMF_8BPP) { if (pvGetEngRbrush(pdbrush)) // Can we use this brush? { if (((EBRUSHOBJ *) pdbrush)->pengbrush()->cxPat >= 4) { lRes = (LONG)bBrushPath(pSurf, ppo,prcl, pdbrush, pptlBrush, DPA_PATCOPY, flOptions); } } } break; case 0xFFFF: // White lRes = (LONG)bPaintPath(pSurf, ppo,prcl, (ULONG)~0, FALSE,flOptions); break; } } return(lRes); } /******************************Public*Routine******************************\ * bFastFill() * * Fills a convex polygon quickly. Calls pfnRow with lists of adjacent * rows, pfn if a verticle rectangle is found. * * returns: * true if it is a simple polygon and has been drawn * false if it is too complex. * * History: * 12-Oct-1993 -by- Eric Kutter [erick] * initial code stolen from s3 driver. \**************************************************************************/ BOOL bMsg = FALSE; BOOL bFastFill( LONG cEdges, // Includes close figure edge POINTFIX* pptfxFirst, PRECTL prclClip, PFN_FF pfn, PFN_FFROW pfnRow, PVOID pv) { 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 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; LONG yCurrent; // '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: // Make sure we initialize the DDAs appropriately: #define RIGHT 0 #define LEFT 1 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; // setup the rectangles for enumeration #define MAXROW 40 RECTL rclClip; ROW arow[MAXROW]; PROW prow = arow; ULONG crow = 0; LONG yTop = 0; yCurrent = (pptfxTop->y + 15) >> 4; if (prclClip) { rclClip = *prclClip; if (rclClip.top > yCurrent) yCurrent = rclClip.top; if (yCurrent >= rclClip.bottom) return(TRUE); } else { rclClip.top = NEG_INFINITY; rclClip.bottom = POS_INFINITY; } // if there is clipping, remove all edges above rectangle if (prclClip) { for (LONG iEdge = 1; iEdge >= 0; iEdge--) { ped = &aed[iEdge]; for (;;) { if (cEdges == 0) return(TRUE); // find the next edge POINTFIX *pptfxNew = (POINTFIX*) ((BYTE*) ped->pptfx + ped->dptfx); if (pptfxNew < pptfxFirst) pptfxNew = pptfxLast; else if (pptfxNew > pptfxLast) pptfxNew = pptfxFirst; // we have found one that intersects the rect if ((pptfxNew->y >> 4) >= rclClip.top) break; // the bottom is outside the cliprect, throw it away and get the next cEdges--; ped->pptfx = pptfxNew; }; } } // now do the real work. We must loop through all edges. NextEdge: // We loop through this routine on a per-trapezoid basis. for (LONG iEdge = 1; iEdge >= 0; iEdge--) { ped = &aed[iEdge]; if (ped->cy == 0) { // Need a new DDA: do { cEdges--; if ((cEdges < 0) || (yCurrent >= rclClip.bottom)) { // flush the batch if (crow > 0) (*pfnRow)(yTop,arow,crow,pv); 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 yCurrent: ped->cy = ((ped->pptfx->y + 15) >> 4) - yCurrent; // 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 'yCurrent'. dN = ped->pptfx->y - pptfxOld->y; dM = ped->pptfx->x - pptfxOld->x; ASSERTGDI(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; // advance to the top yStart = (yStart + 15) >> 4; if (yStart < rclClip.top) { LONG yDelta = rclClip.top - yStart; // if x must change, advance to the x by the height of the trap if (((ped->pptfx->y >> 4) >= rclClip.top) || ped->dx || ped->lErrorUp) { ped->x += ped->dx * yDelta; LONGLONG eqerr = Int32x32To64(ped->lErrorUp,yDelta); eqerr += (LONGLONG) ped->lError; if (eqerr >= 0) { // warning. This divide is extremely expensive // NTFIXED 269540 02-02-2000 pravins GDI-some long // wide geometric lines dont show up in dibsections // We now shift eqerr by 31 bits to the right to see if // the it cannot be just cast as a LONG. if (eqerr >> 31) { // Cannot cast eqerr as a LONG ULONG ulRemainder; eqerr = DIVREM(eqerr,ped->lErrorDown,&ulRemainder); ped->lError = ulRemainder - ped->lErrorDown; ped->x += (LONG)eqerr + 1; } else { // Can cast eqerr as a LONG. ped->x += (LONG) eqerr / ped->lErrorDown + 1; ped->lError = (LONG) eqerr % ped->lErrorDown - ped->lErrorDown; } } else ped->lError = (LONG) eqerr; } #if DBG if (bMsg) { DbgPrint("x = %ld, e = %ld, eU = %ld, eD = %ld, cy = %ld, yD = %ld\n", ped->x,ped->lError,ped->lErrorUp, ped->lErrorDown,ped->cy,yDelta); DbgPrint("ptfxold.y = 0x%lx, ptfx.y = 0x%lx, yStart = %ld, yCurrent = %ld\n", pptfxOld->y,ped->pptfx->y,yStart,yCurrent); } #endif } } } cyTrapezoid = min(aed[LEFT].cy, aed[RIGHT].cy); // # of scans in this trap aed[LEFT].cy -= cyTrapezoid; aed[RIGHT].cy -= cyTrapezoid; // make sure we never go off the bottom if ((yCurrent + cyTrapezoid) > rclClip.bottom) cyTrapezoid = rclClip.bottom - yCurrent; // 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 > 2)) { // must flush any existing rows since rows must be contiguous if (crow) { (*pfnRow)(yTop,arow,crow,pv); prow = arow; crow = 0; } LONG xL = aed[LEFT].x; LONG xR = aed[RIGHT].x; if (xL != xR) { if (xL > xR) { LONG l = xL; xL = xR; xR = l; } // check if we are clipped RECTL rcl; rcl.top = yCurrent; rcl.bottom = yCurrent+cyTrapezoid; if (prclClip) { rcl.left = (xL >= rclClip.left) ? xL : rclClip.left; rcl.right = (xR <= rclClip.right) ? xR : rclClip.right; if (rcl.left < rcl.right) (*pfn)(&rcl,1,pv); } else { rcl.left = xL; rcl.right = xR; (*pfn)(&rcl,1,pv); } } yCurrent += cyTrapezoid; // done with the current trapezoid goto NextEdge; } // make sure we reset yTop when necessary if (crow == 0) yTop = yCurrent; // now run the dda, anytime a row is empty, we need to flush the batch while (TRUE) { LONG lWidth = aed[RIGHT].x - aed[LEFT].x; if (lWidth > 0) { // handle the unclipped case quickly if (!prclClip) { prow->left = aed[LEFT].x; prow->right = aed[RIGHT].x; ++crow; ++prow; CheckForFlush: if (crow == MAXROW) { // flush the batch (*pfnRow)(yTop,arow,crow,pv); prow = arow; crow = 0; yTop = yCurrent + 1; } 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--; ++yCurrent; if (cyTrapezoid == 0) goto NextEdge; continue; } else { // we are clipped. Need to do some real work prow->left = (aed[LEFT].x >= rclClip.left) ? aed[LEFT].x : rclClip.left; prow->right = (aed[RIGHT].x <= rclClip.right) ? aed[RIGHT].x : rclClip.right; if (prow->left < prow->right) { ++crow; ++prow; goto CheckForFlush; } else { // NULL scan - we must flush the batch if (crow) { (*pfnRow)(yTop,arow,crow,pv); prow = arow; crow = 0; } yTop = yCurrent+1; // check if we are donewith this trapezoid, // if the trap is fully left or fully right if (((aed[LEFT].x < rclClip.left) && ((aed[LEFT].pptfx->x >> 4) < rclClip.left) && ((aed[RIGHT].pptfx->x >> 4) < rclClip.left)) || ((aed[LEFT].x >= rclClip.right) && ((aed[LEFT].pptfx->x >> 4) >= rclClip.right) && ((aed[RIGHT].pptfx->x >> 4) >= rclClip.right))) { yCurrent += cyTrapezoid; goto NextEdge; } goto ContinueAfterZero; } } } else if (lWidth == 0) { // NULL scan - we must flush the batch if (crow) { (*pfnRow)(yTop,arow,crow,pv); prow = arow; crow = 0; } yTop = yCurrent + 1; goto ContinueAfterZero; } else { #define SWAP(a, b, tmp) { tmp = a; a = b; b = tmp; } // 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): EDGEDATA edTmp; SWAP(aed[LEFT],aed[RIGHT],edTmp); continue; } } } /******************************Public*Routine******************************\ * bFill() * * Fill a path the slow way. This handles arbitrary paths, builds up a list * of rectangles, and calls pfn. * * This code is very similar to RGNMEMOBJ::vCreate. * * History: * 07-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ BOOL bFill( EPATHOBJ& po, PRECTL prclClip, FLONG flOptions, // ALTERNATE or WINDING PFN_FF pfn, PVOID pv) { EDGE AETHead; // dummy head/tail node & sentinel for Active Edge Table EDGE *pAETHead; // pointer to AETHead EDGE GETHead; // dummy head/tail node & sentinel for Global Edge Table EDGE *pGETHead; // pointer to GETHead EDGE aEdge[MAX_POINTS]; // Allocate memory for edge storage. BOOL bAlloc; EDGE *pFreeEdges; // pointer to memory free for use to store edges if (po.cCurves <= MAX_POINTS) { pFreeEdges = &aEdge[0]; bAlloc = FALSE; } else { pFreeEdges = (PEDGE)PALLOCNOZ(sizeof(EDGE) * po.cCurves,'gdeG'); if (pFreeEdges == (PEDGE)NULL) return(FALSE); bAlloc = TRUE; } // setup the rectangles for enumeration #define MAXRECT 20 RECTL arcl[MAXRECT]; PRECTL prcl = arcl; ULONG crcl = 0; RECTL rclClip; RECTL rclBounds,*prclBounds; if (prclClip) { rclClip = *prclClip; // we'll pass this to vConstructGET which will clip edges to the top and // bottom of the clip rect rclBounds.top = prclClip->top << 4; //we need GIQ coordinats rclBounds.bottom = prclClip->bottom << 4; prclBounds = &rclBounds; } else { rclClip.top = NEG_INFINITY; rclClip.bottom = POS_INFINITY; prclBounds = NULL; } // Construct the global edge list. pGETHead = &GETHead; vConstructGET(po, pGETHead, pFreeEdges,prclBounds); // bad line coordinates or LONG yTop = NEG_INFINITY; // scan line for which we're currently scanning // Create an empty AET with the head node also a tail sentinel pAETHead = &AETHead; AETHead.pNext = pAETHead; // mark that the AET is empty AETHead.Y = 0; // used as a count for number of edges in AET AETHead.X = 0x7FFFFFFF; // this is greater than any valid X value, so // searches will always terminate // Loop through all the scans in the polygon, adding edges from the GET to // the Active Edge Table (AET) as we come to their starts, and scanning out // the AET at each scan into a rectangle list. Each time it fills up, the // rectangle list is passed to the filling routine, and then once again at // the end if any rectangles remain undrawn. We continue so long as there // are edges to be scanned out. while ( 1 ) { // Advance the edges in the AET one scan, discarding any that have // reached the end (if there are any edges in the AET) if (AETHead.pNext != pAETHead) vAdvanceAETEdges(pAETHead); // If the AET is empty, done if the GET is empty, else jump ahead to // the next edge in the GET; if the AET isn't empty, re-sort the AET if (AETHead.pNext == pAETHead) { // Done if there are no edges in either the AET or the GET if (GETHead.pNext == pGETHead) break; // There are no edges in the AET, so jump ahead to the next edge in // the GET. yTop = ((EDGE *)GETHead.pNext)->Y; } else { // Re-sort the edges in the AET by X coordinate, if there are at // least two edges in the AET (there could be one edge if the // balancing edge hasn't yet been added from the GET) if (((EDGE *)AETHead.pNext)->pNext != pAETHead) vXSortAETEdges(pAETHead); } // Move any new edges that start on this scan from the GET to the AET; // bother calling only if there's at least one edge to add if (((EDGE *)GETHead.pNext)->Y == yTop) vMoveNewEdges(pGETHead, pAETHead, yTop); // Scan the AET into region scans (there's always at least one // edge pair in the AET) EDGE *pCurrentEdge = AETHead.pNext; // point to the first edge do { // The left edge of any given edge pair is easy to find; it's just // wherever we happen to be currently LONG iLeftEdge = (int)pCurrentEdge->X; // Find the matching right edge according to the current fill rule if ((flOptions & FP_WINDINGMODE) != 0) { LONG lWindingCount; // Do winding fill; scan across until we've found equal numbers // of up and down edges lWindingCount = pCurrentEdge->lWindingDirection; do { pCurrentEdge = pCurrentEdge->pNext; lWindingCount += pCurrentEdge->lWindingDirection; } while (lWindingCount != 0); } else { // Odd-even fill; the next edge is the matching right edge pCurrentEdge = pCurrentEdge->pNext; } // See if the resulting span encompasses at least one pixel, and // add it to the list of rectangles to draw if so if (iLeftEdge < pCurrentEdge->X) { // Add the rectangle representing the current edge pair if (prclClip) { prcl->left = (iLeftEdge >= rclClip.left) ? iLeftEdge : rclClip.left; prcl->right = (pCurrentEdge->X <= rclClip.right) ? pCurrentEdge->X : rclClip.right;; prcl->top = yTop; prcl->bottom = yTop+1; if (prcl->left < prcl->right) { ++crcl; ++prcl; } } else { prcl->left = iLeftEdge; prcl->right = pCurrentEdge->X; prcl->top = yTop; prcl->bottom = yTop+1; ++crcl; ++prcl; } if (crcl == MAXRECT) { // flush the batch (*pfn)(arcl,crcl,pv); prcl = arcl; crcl = 0; } } } while ((pCurrentEdge = pCurrentEdge->pNext) != pAETHead); yTop++; // next scan } // flush the final batch if (crcl > 0) (*pfn)(arcl,crcl,pv); if (bAlloc) VFREEMEM(pFreeEdges); return(TRUE); } /******************************Member*Function*****************************\ * bEngFastFillEnum() * * fill in the path. If the path only has one sub path and fewer than 40 * points, try bFastFill. If we can't use bFastFill, do it the slow way * through bFill(). * * History: * 27-Sep-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ #define QUICKPOINTS 40 BOOL bEngFastFillEnum( EPATHOBJ &epo, PRECTL prclClip, FLONG flOptions, PFN_FF pfn, PFN_FFROW pfnRow, PVOID pv) { PATHDATA pd; BOOL bRes = FALSE; // check if there is anything to do if (epo.cCurves < 2) return(TRUE); // see if we can do it through fastfill epo.vEnumStart(); if (epo.bEnum(&pd)) { // if this ends the sub path, that means there is more than one sub path. // also don't handle if we can't copy points onto stack if (!(pd.flags & PD_ENDSUBPATH) && (epo.cCurves <= QUICKPOINTS)) { POINTFIX aptfx[QUICKPOINTS]; LONG cPoints; BOOL bMore; RtlCopyMemory(aptfx,pd.pptfx,(SIZE_T)pd.count*sizeof(POINTFIX)); cPoints = pd.count; do { bMore = epo.bEnum(&pd); if (pd.flags & PD_BEGINSUBPATH) { cPoints = 0; break; } RtlCopyMemory(aptfx+cPoints,pd.pptfx,(SIZE_T)pd.count*sizeof(POINTFIX)); cPoints += pd.count; } while(bMore); ASSERTGDI(cPoints <= QUICKPOINTS,"bFastFillWrapper - too many points\n"); if (cPoints) bRes = bFastFill(cPoints,aptfx,prclClip,pfn,pfnRow,pv); } } else if (pd.count > 1) { bRes = bFastFill(pd.count,pd.pptfx,prclClip,pfn,pfnRow,pv); } else { bRes = TRUE; } // did we succeed with fast fill? if (bRes == FALSE) { bRes = bFill(epo,prclClip,flOptions,pfn,pv); } return(bRes); }