Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1426 lines
41 KiB

/******************************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);
}