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