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.
1292 lines
35 KiB
1292 lines
35 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: clipline.cxx
|
|
*
|
|
* This module handles the clipping of lines to a rectangular region.
|
|
*
|
|
* Created: 07-Mar-1991 13:31:00
|
|
* Author: Eric Kutter [erick]
|
|
*
|
|
* Copyright (c) 1991-1999 Microsoft Corporation
|
|
*
|
|
* (General description of its use)
|
|
* Given a dda and region.
|
|
*
|
|
* 0. setup dda
|
|
* 1. find first scan
|
|
* if (!scan) exit
|
|
* find first segment
|
|
*enter
|
|
* if (done) exit
|
|
* 2. do
|
|
* {
|
|
* 2.A. do
|
|
* {
|
|
* 2.A.1. find end of segment
|
|
* 2.A.2. record segment (check for connection)
|
|
* 2.A.3. advance to next segment
|
|
* if (segment && (out of room))
|
|
* return(TRUE);
|
|
* } while (segment)
|
|
*
|
|
* 2.C advance to next scan
|
|
* find first segment
|
|
* } while (scan)
|
|
*
|
|
* set done
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
//#define CLIPDEBUG
|
|
|
|
#ifdef CLIPDEBUG
|
|
FLONG MSGLEVEL = 5;
|
|
#endif
|
|
|
|
/**************************************************************************\
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#ifdef CLIPDEBUG
|
|
VOID XCLIPOBJ::DBGDISPLAYSTATE(PSZ psz)
|
|
{
|
|
DbgPrint("\t%s\n",psz);
|
|
DbgPrint("\t\tpcle->ptB = (%ld,%ld), pcle->ptC = (%ld,%ld), pcle->ptF = (%ld,%ld)\n",
|
|
pcle->ptB.x,pcle->ptB.y,pcle->ptC.x,pcle->ptC.y,pcle->ptF.x,pcle->ptF.y);
|
|
|
|
DbgPrint("\t\tiWall = %ld, cScans = %ld\n",enmr.iWall, enmr.cScans);
|
|
}
|
|
|
|
VOID XCLIPOBJ::DBGDISPLAYDDA()
|
|
{
|
|
DbgPrint("\tDDA: ");
|
|
DbgPrint("\t\tpt0 = (%ld,%ld), pt1 = (%ld,%ld)\n",
|
|
pcle->dda.lX0,pcle->dda.lY0,pcle->dda.lX1,pcle->dda.lY1);
|
|
}
|
|
#endif
|
|
|
|
/******************************Member*Function*****************************\
|
|
* EPATHOBJ::vUpdateCosmeticStyleState(pso, pla)
|
|
*
|
|
* Updates the style state when a path is completely clipped away, for
|
|
* cosmetic lines.
|
|
*
|
|
* History:
|
|
* 3-Nov-1992 -by- J. Andrew Goossen [andrewgo]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID EPATHOBJ::vUpdateCosmeticStyleState(SURFACE* pSurf, LINEATTRS* pla)
|
|
{
|
|
DDA_CLIPLINE dda; // For calculating line length in pixels
|
|
ULONG xStep; // Advance xStep/xyDensity style units for each
|
|
// pel if line is x-styled
|
|
ULONG yStep; // Advance yStep/xyDensity units
|
|
ULONG xyDensity;
|
|
STYLEPOS spTotal2; // Twice the sum of the style array
|
|
|
|
if (pla->fl & LA_ALTERNATE)
|
|
{
|
|
// Style information for alternate lines is special:
|
|
|
|
xStep = 1;
|
|
yStep = 1;
|
|
xyDensity = 1;
|
|
spTotal2 = 2;
|
|
}
|
|
else
|
|
{
|
|
// Get styling information about device:
|
|
|
|
PDEVOBJ po(pSurf->hdev());
|
|
|
|
xStep = po.xStyleStep();
|
|
yStep = po.yStyleStep();
|
|
xyDensity = po.denStyleStep();
|
|
|
|
PFLOAT_LONG pstyle = pla->pstyle + pla->cstyle;
|
|
spTotal2 = 0;
|
|
|
|
while (pstyle > pla->pstyle)
|
|
{
|
|
pstyle--;
|
|
spTotal2 += pstyle->l;
|
|
}
|
|
|
|
ASSERTGDI((spTotal2 & ~0x7fffL) == 0, "Style array too long");
|
|
ASSERTGDI(spTotal2 != 0, "Zero style array?");
|
|
ASSERTGDI(xyDensity > 0, "Zero xyDensity?");
|
|
spTotal2 = 2 * spTotal2 * xyDensity;
|
|
}
|
|
|
|
// Find the beginning of the last subpath in the path:
|
|
|
|
PATHRECORD* ppr = ppath->pprlast;
|
|
while (!(ppr->flags & PD_BEGINSUBPATH))
|
|
ppr = ppr->pprprev;
|
|
|
|
// Initialize the style state appropriately:
|
|
|
|
ASSERTGDI((ppr->flags & PD_RESETSTYLE) || (ppr == ppath->pprfirst),
|
|
"Expected PD_RESETSTYLE on subpaths after first");
|
|
|
|
STYLEPOS sp = 0;
|
|
if (!(ppr->flags & PD_RESETSTYLE))
|
|
{
|
|
sp = HIWORD(pla->elStyleState.l) * xyDensity
|
|
+ LOWORD(pla->elStyleState.l);
|
|
}
|
|
|
|
POINTFIX* pptfx0 = &ppr->aptfx[0];
|
|
POINTFIX* pptfx1 = &ppr->aptfx[1];
|
|
POINTFIX* pptfxEnd = &ppr->aptfx[ppr->count];
|
|
|
|
// Loop through all PATHRECORDs in path:
|
|
|
|
while (TRUE)
|
|
{
|
|
// Loop through all points in PATHRECORD:
|
|
|
|
while (pptfx1 < pptfxEnd)
|
|
{
|
|
if (dda.bInit(pptfx0, pptfx1))
|
|
{
|
|
STYLEPOS spStyleStep;
|
|
BOOL bXStyled;
|
|
LONG lDelta;
|
|
|
|
// Determine if x-styled or y-styled:
|
|
|
|
FIX dx = ABS(pptfx1->x - pptfx0->x);
|
|
FIX dy = ABS(pptfx1->y - pptfx0->y);
|
|
|
|
if (xStep == yStep)
|
|
bXStyled = (dx >= dy);
|
|
else
|
|
{
|
|
bXStyled = UInt32x32To64(xStep,dx) >= UInt32x32To64(yStep,dy);
|
|
}
|
|
|
|
// Calculate new style state at the end of this line:
|
|
|
|
if ((bXStyled && dda.bXMajor()) ||
|
|
(!bXStyled && !dda.bXMajor()))
|
|
{
|
|
spStyleStep = xStep;
|
|
lDelta = dda.lX1 - dda.lX0 + 1;
|
|
}
|
|
else
|
|
{
|
|
spStyleStep = yStep;
|
|
lDelta = dda.lY1 - dda.lY0 + 1;
|
|
}
|
|
|
|
ASSERTGDI(lDelta > 0, "Expected positive lDelta");
|
|
|
|
if ((lDelta & ~0xffffL) == 0)
|
|
{
|
|
sp += lDelta * spStyleStep;
|
|
if (sp >= spTotal2)
|
|
sp %= spTotal2;
|
|
}
|
|
else
|
|
{
|
|
ULONGLONG euq = UInt32x32To64((ULONG) lDelta, (ULONG) spStyleStep)
|
|
+ (ULONGLONG) sp;
|
|
DIVREM(euq,(ULONG) spTotal2, (ULONG*) &sp);
|
|
}
|
|
}
|
|
|
|
pptfx0 = pptfx1;
|
|
pptfx1++;
|
|
}
|
|
|
|
ppr = ppr->pprnext;
|
|
if (ppr == (PATHRECORD*) NULL)
|
|
break;
|
|
|
|
pptfx1 = &ppr->aptfx[0];
|
|
pptfxEnd = &ppr->aptfx[ppr->count];
|
|
}
|
|
|
|
pla->elStyleState.l = MAKELONG(sp % xyDensity, sp / xyDensity);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* VOID XCLIPOBJ::vUpdateStyleState()
|
|
*
|
|
* Updates the style state (spStyleEnd) for the end of the current line.
|
|
*
|
|
* History:
|
|
* 20-Feb-1992 -by- J. Andrew Goossen [andrewgo]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID XCLIPOBJ::vUpdateStyleState()
|
|
{
|
|
BOOL bXStyled;
|
|
LONG lDelta; // Length of line in style-major direction
|
|
STYLEPOS spStyleStep; // Step size for style-major direction of line
|
|
|
|
{
|
|
// Determine if x-styled or y-styled
|
|
|
|
FIX dx = ABS(pcle->ptfx0.x - pcle->pptfx1->x);
|
|
FIX dy = ABS(pcle->ptfx0.y - pcle->pptfx1->y);
|
|
|
|
if (pcle->xStep == pcle->yStep)
|
|
bXStyled = (dx >= dy);
|
|
else
|
|
{
|
|
bXStyled = UInt32x32To64(pcle->xStep, dx) >= UInt32x32To64(pcle->yStep, dy);
|
|
}
|
|
}
|
|
|
|
// Now calculate new style state at the end of this line:
|
|
|
|
pcle->spStyleEnd = pcle->spStyleStart;
|
|
ASSERTGDI(pcle->spStyleEnd >= 0, "Negative style state");
|
|
|
|
if (bXStyled)
|
|
{
|
|
spStyleStep = pcle->xStep;
|
|
lDelta = ABS(pcle->lX1 - pcle->lX0) + 1;
|
|
}
|
|
else
|
|
{
|
|
spStyleStep = pcle->yStep;
|
|
lDelta = ABS(pcle->lY1 - pcle->lY0) + 1;
|
|
}
|
|
|
|
if ((lDelta & ~0xffffL) == 0)
|
|
{
|
|
pcle->spStyleEnd += lDelta * spStyleStep;
|
|
if (pcle->spStyleEnd >= pcle->spTotal2)
|
|
pcle->spStyleEnd %= pcle->spTotal2;
|
|
}
|
|
else
|
|
{
|
|
WARNING("Long style state comp");
|
|
|
|
ULONGLONG euq = UInt32x32To64((ULONG) lDelta, (ULONG) spStyleStep)
|
|
+ (ULONGLONG) pcle->spStyleEnd;
|
|
DIVREM(euq, pcle->spTotal2, (PULONG) &pcle->spStyleEnd);
|
|
}
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bEnumStartLine
|
|
*
|
|
* History:
|
|
* 20-Feb-1992 -by- J. Andrew Goossen [andrewgo]
|
|
* Adding styling support.
|
|
*
|
|
* 22-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bEnumStartLine(
|
|
FLONG flPath)
|
|
{
|
|
ASSERTGDI((PD_ALL & CLO_ALL) == 0,"PD_ALL and CLIPOBJ_ALL != 0\n");
|
|
|
|
// reset all flags except CLO_PATHDONE.
|
|
|
|
pcle->fl = (pcle->fl & CLO_PATHDONE) | flPath;
|
|
|
|
// initialize the dda
|
|
|
|
if (!pcle->dda.bInit(&pcle->ptfx0,pcle->pptfx1))
|
|
{
|
|
vSetLineDone();
|
|
return(FALSE);
|
|
}
|
|
|
|
pcle->lX0 = pcle->dda.lX0;
|
|
pcle->lY0 = pcle->dda.lY0;
|
|
pcle->lX1 = pcle->dda.lX1;
|
|
pcle->lY1 = pcle->dda.lY1;
|
|
|
|
pcle->dda.vUnflip(&pcle->lX0, &pcle->lY0);
|
|
pcle->dda.vUnflip(&pcle->lX1, &pcle->lY1);
|
|
|
|
// if this is the first line of the sub path
|
|
|
|
if (pcle->fl & PD_BEGINSUBPATH)
|
|
{
|
|
pcle->ptfxStartSub = pcle->ptfx0;
|
|
}
|
|
|
|
if (bStyling())
|
|
{
|
|
// the style state at the start of this line is the same as the style
|
|
// state for the end of the previous line, unless told differently:
|
|
|
|
pcle->spStyleStart = pcle->spStyleEnd;
|
|
if (pcle->fl & PD_RESETSTYLE)
|
|
{
|
|
pcle->spStyleStart = 0;
|
|
}
|
|
|
|
// calculate style state at end of this line:
|
|
|
|
vUpdateStyleState();
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bRecordSegment()
|
|
*
|
|
* Find the exit point of the current segment and record the run. If there
|
|
* is not enough room to record the run, return false.
|
|
*
|
|
* On Entry:
|
|
*
|
|
* guaranteed intersection with segment
|
|
*
|
|
* enmr.pscn - current scan
|
|
* ptB - entry point into segment
|
|
* ptE,ptF - exit intersection from scan
|
|
*
|
|
* On Exit:
|
|
*
|
|
* if room to record the run, return TRUE otherwise return false.
|
|
*
|
|
* iC - exiting index from segment
|
|
*
|
|
* History:
|
|
* 11-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
// This is inline since it gets called from only one place:
|
|
|
|
inline BOOL XCLIPOBJ::bRecordSegment()
|
|
{
|
|
enmr.iWall += enmr.iOff;
|
|
|
|
LONG x = xWall(0);
|
|
|
|
// if line intersects wall before exits scan...
|
|
|
|
if (bLeftToRight() == (x > pcle->ptE.x))
|
|
{
|
|
// line exits scan before intersecting next wall...
|
|
|
|
return(bRecordRun(pcle->iE));
|
|
}
|
|
else
|
|
{
|
|
bIntersectWall(x, &pcle->ptC, NULL, &pcle->iC);
|
|
|
|
return(bRecordRun(pcle->iC));
|
|
}
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bSetup()
|
|
*
|
|
* If this is the first call for a line, setup the enumeration data structure.
|
|
* The first point may need adjusting if the previous line of the sub path
|
|
* ended on the same point this line starts.
|
|
*
|
|
* Otherwise, just the temporary data structure needs to be initialized as
|
|
* well as recording the last run that wouldn't fit in the previous call.
|
|
*
|
|
* If this is the closing line of a sub path, the last point might need to
|
|
* be adjusted if it lies on the first point of the first line of the
|
|
* sub-path.
|
|
*
|
|
* On Exit:
|
|
*
|
|
* bRecordSegment() needs to be called to record the current segment.
|
|
*
|
|
* true - if segment found
|
|
*
|
|
* ptB,iStart - begining of current segment
|
|
* ptE,ptF,iE - intersection leaving previous scan or first point.
|
|
* pscn - first scan to be processed.
|
|
*
|
|
* History:
|
|
* 07-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bSetup()
|
|
{
|
|
// setup CLIPOBJ fields
|
|
// Is this the first call for this line
|
|
|
|
if (!bRecordPending()) // First call
|
|
{
|
|
// init pclt variables
|
|
|
|
// setup CLIPOBJ fields
|
|
|
|
pcle->iE = -1; // last index of previous run
|
|
|
|
vSetLeftToRight(pcle->lX0 <= pcle->lX1);
|
|
|
|
if (pcle->lY0 <= pcle->lY1)
|
|
pcle->fl |= CLO_TOPTOBOTTOM;
|
|
|
|
// Check if we have to further clip to the rclBounds (it would be
|
|
// nice to have a flag in the CLIPOBJ telling us that we also have
|
|
// to clip the rclBounds...)
|
|
|
|
if ((pcle->lY0 < rclBounds.top && pcle->lY1 < rclBounds.top) ||
|
|
(pcle->lY0 >= rclBounds.bottom && pcle->lY1 >= rclBounds.bottom))
|
|
return(FALSE);
|
|
|
|
// Calculate the intersections of the line with rclBounds:
|
|
|
|
if (bTopToBottom())
|
|
{
|
|
if (pcle->lY0 < rclBounds.top)
|
|
{
|
|
POINTL ptlTop;
|
|
vIntersectScan(rclBounds.top, NULL, &ptlTop, &pcle->iE);
|
|
pcle->lX0 = ptlTop.x;
|
|
pcle->lY0 = ptlTop.y;
|
|
ASSERTGDI(pcle->lY0 == rclBounds.top, "TtoB top wrong");
|
|
}
|
|
|
|
if (pcle->lY1 >= rclBounds.bottom)
|
|
{
|
|
POINTL ptlBottom;
|
|
LONG iEnd;
|
|
vIntersectScan(rclBounds.bottom, &ptlBottom, NULL, &iEnd);
|
|
pcle->lX1 = ptlBottom.x;
|
|
pcle->lY1 = ptlBottom.y;
|
|
ASSERTGDI(pcle->lY1 == rclBounds.bottom - 1, "TtoB bottom wrong");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pcle->lY1 < rclBounds.top)
|
|
{
|
|
POINTL ptlTop;
|
|
LONG iEnd;
|
|
vIntersectScan(rclBounds.top, &ptlTop, NULL, &iEnd);
|
|
pcle->lX1 = ptlTop.x;
|
|
pcle->lY1 = ptlTop.y;
|
|
ASSERTGDI(pcle->lY1 == rclBounds.top, "BtoT top wrong");
|
|
}
|
|
|
|
if (pcle->lY0 >= rclBounds.bottom)
|
|
{
|
|
POINTL ptlBottom;
|
|
vIntersectScan(rclBounds.bottom, NULL, &ptlBottom, &pcle->iE);
|
|
pcle->lX0 = ptlBottom.x;
|
|
pcle->lY0 = ptlBottom.y;
|
|
ASSERTGDI(pcle->lY0 == rclBounds.bottom - 1, "BtoT bottom wrong");
|
|
}
|
|
}
|
|
|
|
// Check if we have to further clip to the rclBounds:
|
|
|
|
if ((pcle->lX0 < rclBounds.left && pcle->lX1 < rclBounds.left) ||
|
|
(pcle->lX0 >= rclBounds.right && pcle->lX1 >= rclBounds.right))
|
|
return(FALSE);
|
|
|
|
if (bLeftToRight())
|
|
{
|
|
if (pcle->lX0 < rclBounds.left)
|
|
{
|
|
POINTL ptlLeft;
|
|
bIntersectWall(rclBounds.left, NULL, &ptlLeft, &pcle->iE);
|
|
pcle->lX0 = ptlLeft.x;
|
|
pcle->lY0 = ptlLeft.y;
|
|
ASSERTGDI(pcle->lX0 == rclBounds.left, "LtoR left wrong");
|
|
}
|
|
|
|
if (pcle->lX1 >= rclBounds.right)
|
|
{
|
|
POINTL ptlRight;
|
|
LONG iEnd;
|
|
bIntersectWall(rclBounds.right, &ptlRight, NULL, &iEnd);
|
|
pcle->lX1 = ptlRight.x;
|
|
pcle->lY1 = ptlRight.y;
|
|
ASSERTGDI(pcle->lX1 == rclBounds.right - 1, "LtoR right wrong");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pcle->lX1 < rclBounds.left)
|
|
{
|
|
POINTL ptlLeft;
|
|
LONG iEnd;
|
|
bIntersectWall(rclBounds.left, &ptlLeft, NULL, &iEnd);
|
|
pcle->lX1 = ptlLeft.x;
|
|
pcle->lY1 = ptlLeft.y;
|
|
ASSERTGDI(pcle->lX1 == rclBounds.left, "RtoL left wrong");
|
|
}
|
|
|
|
if (pcle->lX0 >= rclBounds.right)
|
|
{
|
|
POINTL ptlRight;
|
|
bIntersectWall(rclBounds.right, NULL, &ptlRight, &pcle->iE);
|
|
pcle->lX0 = ptlRight.x;
|
|
pcle->lY0 = ptlRight.y;
|
|
ASSERTGDI(pcle->lX0 == rclBounds.right - 1, "RtoL right wrong");
|
|
}
|
|
}
|
|
|
|
ASSERTGDI(pcle->lY0 >= rclBounds.top && pcle->lY0 < rclBounds.bottom &&
|
|
pcle->lY1 >= rclBounds.top && pcle->lY1 < rclBounds.bottom &&
|
|
pcle->lX0 >= rclBounds.left && pcle->lX0 < rclBounds.right &&
|
|
pcle->lX1 >= rclBounds.left && pcle->lX1 < rclBounds.right,
|
|
"Line out of rclBounds");
|
|
|
|
pcle->ptF.x = pcle->lX0;
|
|
pcle->ptF.y = pcle->lY0;
|
|
|
|
// setup enmr fields
|
|
|
|
vSetRecordPending();
|
|
|
|
// find first segment of first scan containing runs
|
|
|
|
if (!bFindFirstScan())
|
|
return(FALSE);
|
|
}
|
|
else // following calls for this segment
|
|
{
|
|
// record the segment that wouldn't fit last time.
|
|
// guaranteed success!
|
|
|
|
bRecordRun(pcle->iC);
|
|
|
|
// find the next segment to get back in sync with the enumeration.
|
|
// if there is no next segment in this scan, find the next scan.
|
|
// if there is no next scan containing any segments, return false.
|
|
|
|
if (bFindNextSegment())
|
|
return(TRUE);
|
|
|
|
// Are there more scans?
|
|
|
|
if (!bFindNextScan())
|
|
return(FALSE);
|
|
}
|
|
|
|
// need to find a segment. If no segment found in scan, goto the next scan.
|
|
|
|
do
|
|
{
|
|
if (bFindFirstSegment())
|
|
return(TRUE);
|
|
|
|
} while (bFindNextScan());
|
|
|
|
// didn't find any. Done with this line.
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bEnumLine()
|
|
*
|
|
* Fill the CLIPLINE data structure with line segments for the current line.
|
|
* If there are too many segments to fit in the structure, TRUE is returned
|
|
* and this routine must be called again with the same line. Each new line
|
|
* must first be initialized by calling vEnumStartLine.
|
|
*
|
|
* {
|
|
* 1. if (done) exit
|
|
*
|
|
* 2. do
|
|
* {
|
|
* 2.A. do
|
|
* {
|
|
* 2.A.1. find end of segment
|
|
* 2.A.2. record segment
|
|
* 2.A.3. advance to next segment
|
|
* if (segment && (out of room))
|
|
* return(TRUE);
|
|
* 2.A } while (segments)
|
|
*
|
|
* 2.C advance to next scan
|
|
* 2.D find first segment
|
|
* 2. } while (scan)
|
|
* 3. return(FALSE)
|
|
* }
|
|
*
|
|
* RETURN:
|
|
* TRUE - there are more segments and this routine should be called again
|
|
* FALSE - no more segments. Use vEnumStartLine to prepare next line.
|
|
*
|
|
* History:
|
|
* 07-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bEnumLine(
|
|
ULONG cj,
|
|
PCLIPLINE pcl)
|
|
{
|
|
pcl->ptfxA = pcle->ptfx0;
|
|
pcl->ptfxB = *pcle->pptfx1;
|
|
pcl->c = 0;
|
|
|
|
if (bStyling())
|
|
pcl->lStyleState = lGetStyleState(pcle->spStyleStart);
|
|
|
|
// if we have already completed the enumeration
|
|
|
|
if (bLineDone())
|
|
return(FALSE);
|
|
|
|
// setup the run structure
|
|
|
|
pcle->cMaxRuns = (cj - offsetof(CLIPLINE,arun)) / sizeof(RUN);
|
|
pcle->prun = pcl->arun;
|
|
pcle->pcRuns = &pcl->c;
|
|
pcle->iPrevStop = LONG_MAX; // If this is a valid stop, there is no way there
|
|
// could be anothr run after it anyways.
|
|
|
|
// there must be room for at least 1 segment, otherwise tell them we are done.
|
|
|
|
if (pcle->cMaxRuns == 0)
|
|
return(FALSE);
|
|
|
|
// setup intial state.
|
|
|
|
if (!bSetup())
|
|
{
|
|
vSetLineDone();
|
|
return(FALSE);
|
|
}
|
|
|
|
// enumerate through the scans and segments
|
|
|
|
for (;;)
|
|
{
|
|
// enumerate through the segments
|
|
|
|
do
|
|
{
|
|
if (!bRecordSegment())
|
|
return(TRUE);
|
|
|
|
} while (bFindNextSegment());
|
|
|
|
// find the next scan with intersecting segments
|
|
|
|
do
|
|
{
|
|
if (!bFindNextScan())
|
|
{
|
|
vSetLineDone();
|
|
return(FALSE);
|
|
}
|
|
} while (!bFindFirstSegment());
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL XCLIPOBJ::bFindFirstScan()
|
|
*
|
|
* if (topdown)
|
|
* {
|
|
* find first scan ending below first point
|
|
* return(does the line intersect the scan)
|
|
* }
|
|
* else (bottom up
|
|
* {
|
|
* find first scan ending above point
|
|
* return(does the line intersect the scan)
|
|
* }
|
|
*
|
|
* On Entry:
|
|
* pcle->ptF.y - first Y coordinate of line
|
|
* pcle->lY1 - last Y coordinate of line
|
|
*
|
|
* On Exit:
|
|
* enmr.pscn - current scan
|
|
* enmr.cScans - number of scans still to traverse
|
|
*
|
|
* History:
|
|
* 07-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bFindFirstScan()
|
|
{
|
|
if (prgn->cScans <= 2) // empty region
|
|
return(FALSE);
|
|
|
|
enmr.cScans = prgn->cScans - 2; // ignore first and last (no walls)
|
|
|
|
// Start from the top or bottom?
|
|
|
|
if (bTopToBottom())
|
|
{
|
|
enmr.pscn = pscnGet(prgn->pscnHead()); // ignore first scan (empty)
|
|
|
|
// while the scans are above the start of the line
|
|
|
|
while (bEmptyScan() || (enmr.pscn->yBottom <= pcle->ptF.y))
|
|
{
|
|
if (--enmr.cScans == 0)
|
|
return(FALSE);
|
|
|
|
enmr.pscn = pscnGet(enmr.pscn);
|
|
}
|
|
|
|
return(enmr.pscn->yTop <= pcle->lY1); // is the line between scans
|
|
}
|
|
else // bottom up
|
|
{
|
|
enmr.pscn = pscnGot(pscnGot(prgn->pscnTail)); // ignore last scan
|
|
|
|
// while the scans are below the start of the line
|
|
|
|
while (bEmptyScan() || (enmr.pscn->yTop > pcle->ptF.y))
|
|
{
|
|
if (--enmr.cScans == 0)
|
|
return(FALSE);
|
|
|
|
enmr.pscn = pscnGot(enmr.pscn);
|
|
}
|
|
|
|
return(enmr.pscn->yBottom > pcle->lY1);
|
|
}
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bFindNextScan()
|
|
*
|
|
* On Entry:
|
|
*
|
|
* enmr.pscn - last scan searched
|
|
* enmr.cScans - total remaining scans (some may be empty)
|
|
*
|
|
* On Exit:
|
|
*
|
|
* if there are more scans to search, return true with:
|
|
* enmr.pscn - next scan to search
|
|
*
|
|
* History:
|
|
* 09-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bFindNextScan()
|
|
{
|
|
// Start from the top or bottom?
|
|
|
|
if (bTopToBottom())
|
|
{
|
|
do {
|
|
if (enmr.cScans == 1) // already on last scan
|
|
return(FALSE);
|
|
|
|
--enmr.cScans;
|
|
|
|
enmr.pscn = pscnGet(enmr.pscn);
|
|
|
|
if (enmr.pscn->yTop > pcle->lY1) // does line end before scan?
|
|
return(FALSE);
|
|
|
|
} while (bEmptyScan());
|
|
}
|
|
else // bottom to top
|
|
{
|
|
do {
|
|
if (enmr.cScans == 1) // already on last scan
|
|
return(FALSE);
|
|
|
|
--enmr.cScans;
|
|
|
|
enmr.pscn = pscnGot(enmr.pscn);
|
|
|
|
if (enmr.pscn->yBottom <= pcle->lY1)// does line end before scan?
|
|
return(FALSE);
|
|
|
|
} while (bEmptyScan());
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bFindFirstSegment()
|
|
*
|
|
* On Entry:
|
|
*
|
|
* enmr.pscn - current scan
|
|
* ptF - exit point from previous scan containing segments. For
|
|
* the first scan, this is lX0,lY0.
|
|
* On Exit:
|
|
*
|
|
* if no intersections, return FALSE, otherwise
|
|
*
|
|
* ptB,iStart - intersection entering first segment
|
|
* ptE,ptF,iE - intersection exiting scan
|
|
* xWall(0) - last wall intersected
|
|
*
|
|
* History:
|
|
* 08-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bFindFirstSegment()
|
|
{
|
|
// setup the intersections entering and leaving the current scan.
|
|
|
|
pcle->ptB = pcle->ptF;
|
|
|
|
if (bTopToBottom())
|
|
{
|
|
pcle->lYEnter = enmr.pscn->yTop;
|
|
pcle->lYLeave = enmr.pscn->yBottom;
|
|
}
|
|
else
|
|
{
|
|
pcle->lYEnter = enmr.pscn->yBottom;
|
|
pcle->lYLeave = enmr.pscn->yTop;
|
|
}
|
|
|
|
if (bTopToBottom() == (pcle->ptB.y < pcle->lYEnter))
|
|
{
|
|
vIntersectScan(pcle->lYEnter,NULL,&pcle->ptB,&pcle->iStart);
|
|
}
|
|
else
|
|
{
|
|
pcle->iStart = pcle->iE;
|
|
pcle->lYEnter = pcle->ptB.y;
|
|
}
|
|
|
|
if (bTopToBottom() == (pcle->lY1 >= pcle->lYLeave))
|
|
{
|
|
vIntersectScan(pcle->lYLeave,&pcle->ptE,&pcle->ptF,&pcle->iE);
|
|
}
|
|
else
|
|
{
|
|
// The line ends in this scan:
|
|
|
|
pcle->ptE.y = pcle->lY1;
|
|
pcle->ptE.x = pcle->lX1;
|
|
pcle->lYLeave = pcle->lY1 + 1;
|
|
|
|
// Calculate the iPosition of the line's last pixel in the scan.
|
|
// If we didn't have to take into account rclBounds, this would
|
|
// simply be pcle->iE = pcle->dda.lX1 - pcle->dda.lX0:
|
|
|
|
// Unnormalize the (unclipped) start of the line:
|
|
|
|
EPOINTL eptlStart(pcle->dda.lX0, pcle->dda.lY0);
|
|
|
|
pcle->dda.vUnflip(&eptlStart.x, &eptlStart.y);
|
|
|
|
// Compute the length in the line's major direction:
|
|
|
|
if (pcle->dda.bDFlip())
|
|
pcle->iE = ABS(pcle->lY1 - eptlStart.y);
|
|
else
|
|
pcle->iE = ABS(pcle->lX1 - eptlStart.x);
|
|
}
|
|
|
|
// find first wall intersecting line. Do this by means of a binary search
|
|
|
|
enmr.iFinal = enmr.pscn->cWalls - 1;
|
|
enmr.iWall = 0; // must be 0 for special cases
|
|
|
|
// Special case if the line enters the scan before the first or after the
|
|
// last segment. This makes the general case much simpler.
|
|
|
|
if (pcle->ptB.x >= xWall(enmr.iFinal))
|
|
{
|
|
enmr.iWall = enmr.iFinal;
|
|
|
|
if (bLeftToRight())
|
|
return(FALSE);
|
|
|
|
enmr.iWall++;
|
|
}
|
|
else if (pcle->ptB.x < xWall(0))
|
|
{
|
|
if (!bLeftToRight())
|
|
return(FALSE);
|
|
|
|
enmr.iWall--;
|
|
}
|
|
else
|
|
{
|
|
// if the line starts somewhere in the middle of the scan find the first
|
|
// wall to the right of the entrance via a binary search.
|
|
|
|
LONG iLow = 0;
|
|
LONG iHigh = enmr.iFinal;
|
|
|
|
for (;;)
|
|
{
|
|
enmr.iWall = (iLow + iHigh) / 2;
|
|
|
|
if (pcle->ptB.x < xWall(0)) // to the left
|
|
{
|
|
// enters after previous wall?
|
|
|
|
if (pcle->ptB.x >= xWall(-1))
|
|
break;
|
|
|
|
// keep searching
|
|
|
|
iHigh = enmr.iWall - 1;
|
|
}
|
|
else
|
|
{
|
|
// enters before next wall?
|
|
|
|
if (pcle->ptB.x < xWall(1))
|
|
{
|
|
++enmr.iWall;
|
|
break;
|
|
}
|
|
|
|
// keep searching
|
|
|
|
iLow = enmr.iWall + 1;
|
|
}
|
|
}
|
|
|
|
// WARNING! SLEEZY! subtract one if left to right.
|
|
// sets iWall to wall before entering scan.
|
|
|
|
enmr.iWall -= bLeftToRight();
|
|
}
|
|
|
|
// set pcle->ptB to first intersection. Already set if starting inside segment.
|
|
// If we are starting inside a segment, iWall will be odd if we are moving
|
|
// right and even if we are moving left. If we are outside of a segment when
|
|
// the scan is entered, compute the intersection with the first wall.
|
|
|
|
if (bLeftToRight() == (BOOL)(enmr.iWall & 1))
|
|
{
|
|
enmr.iWall += enmr.iOff;
|
|
|
|
LONG x = xWall(0);
|
|
|
|
if (bLeftToRight() == (x > pcle->ptE.x))
|
|
{
|
|
// line exits scan before intersecting next wall...
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
bIntersectWall(x,NULL,&pcle->ptB,&pcle->iStart);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bFindNextSegment()
|
|
*
|
|
* On Entry:
|
|
*
|
|
* ptE,ptF - intersection exiting scan
|
|
* xWall(0) - exiting wall of previous segment
|
|
*
|
|
* On Exit:
|
|
*
|
|
* if no intersections, return FALSE, otherwise
|
|
*
|
|
* ptB - intersection entering next segment
|
|
* ptE,ptF - intersection exiting scan
|
|
* xWall(0) - exiting wall of new segment
|
|
*
|
|
* History:
|
|
* 12-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bFindNextSegment()
|
|
{
|
|
// if we are past the last segment...
|
|
|
|
if (bLeftToRight())
|
|
{
|
|
if (enmr.iWall >= enmr.iFinal)
|
|
return(FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (enmr.iWall <= 0)
|
|
return(FALSE);
|
|
}
|
|
|
|
// find the intersection
|
|
|
|
enmr.iWall += enmr.iOff;
|
|
|
|
LONG x = xWall(0);
|
|
|
|
if (bLeftToRight() == (x > pcle->ptE.x))
|
|
{
|
|
// line exits scan before intersecting next wall...
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
bIntersectWall(x,NULL,&pcle->ptB,&pcle->iStart);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bRecordRun(LONG& iStop)
|
|
*
|
|
* On Entry:
|
|
*
|
|
* pcle->iStart - index of start - 1
|
|
* iStop - index of end of run
|
|
*
|
|
* On Exit:
|
|
*
|
|
* iC - same as iStop if can't record
|
|
*
|
|
* History:
|
|
* 12-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bRecordRun(LONG& iStop)
|
|
{
|
|
// don't record if going backwards
|
|
|
|
if (iStop <= pcle->iStart)
|
|
return(TRUE);
|
|
|
|
// do we append the run?
|
|
|
|
if (pcle->iStart != pcle->iPrevStop)
|
|
{
|
|
// Are we out of room?
|
|
|
|
if (*pcle->pcRuns == pcle->cMaxRuns)
|
|
{
|
|
pcle->iC = iStop;
|
|
return(FALSE);
|
|
}
|
|
|
|
// set the run
|
|
|
|
pcle->prun->iStop = iStop;
|
|
pcle->prun->iStart = pcle->iStart + 1;
|
|
|
|
// increment the count
|
|
|
|
(*pcle->pcRuns)++;
|
|
pcle->prun++;
|
|
}
|
|
else // append the run
|
|
{
|
|
ASSERTGDI(*pcle->pcRuns > 0,"CLIPOBJ::bRecordRun,cruns == 0\n");
|
|
pcle->prun[-1].iStop = iStop;
|
|
}
|
|
|
|
pcle->iPrevStop = iStop;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* VOID XCLIPOBJ::vIntersectScan
|
|
*
|
|
* On Entry:
|
|
*
|
|
* y - y coordinate of scan to intersect
|
|
*
|
|
* On Exit:
|
|
*
|
|
* ppt0 - last point before intersection (may be NULL)
|
|
* ppt1 - first point after intersection
|
|
* pi0 - run position for ppt0.
|
|
* run position for ppt1 = pi0 + 1;
|
|
*
|
|
* History:
|
|
* 27-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID XCLIPOBJ::vIntersectScan(LONG y, PPOINTL ppt0, PPOINTL ppt1, PLONG pi0)
|
|
{
|
|
if (pcle->dda.bVFlip())
|
|
y = -y + 1;
|
|
|
|
if (!pcle->dda.bDFlip())
|
|
vIntersectHorizontal(&pcle->dda,y,ppt0,ppt1,pi0);
|
|
else
|
|
vIntersectVertical(&pcle->dda,y,ppt0,ppt1,pi0);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL XCLIPOBJ::bIntersectWall
|
|
*
|
|
* Compute the intersection of the line with the current wall + i.
|
|
* The important results are the last pel before the intersection and
|
|
* the first pel after the intersection as well as the run index of the
|
|
* last pel before the intersection.
|
|
*
|
|
* On Entry:
|
|
*
|
|
* x - wall column
|
|
*
|
|
* On Exit:
|
|
*
|
|
* always assumes there will be an intersection with the given wall, so
|
|
* always returns TRUE
|
|
*
|
|
* ppt0 - last point before intersection (may be NULL)
|
|
* ppt1 - first point after intersection (may be NULL)
|
|
* pi0 - run position for ppt0.
|
|
* run position for ppt1 = pi0 + 1;
|
|
*
|
|
* History:
|
|
* 27-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL XCLIPOBJ::bIntersectWall(LONG x, PPOINTL ppt0, PPOINTL ppt1, PLONG pi0)
|
|
{
|
|
if (pcle->dda.bHFlip())
|
|
x = -x + 1;
|
|
|
|
if (!pcle->dda.bDFlip())
|
|
vIntersectVertical(&pcle->dda,x,ppt0,ppt1,pi0);
|
|
else
|
|
vIntersectHorizontal(&pcle->dda,x,ppt0,ppt1,pi0);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* LONG yCompute(x)
|
|
*
|
|
* For the given column x, calculate the y-coordinate of the pixel
|
|
* on the line that will be lit:
|
|
|
|
* History:
|
|
* 27-Aug-1992 -by- J. Andrew Goossen [andrewgo]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
inline LONG DDA_CLIPLINE::yCompute(LONG x)
|
|
{
|
|
LONGLONG eq = Int32x32To64((LONG) dN, x - ptlOrg.x) + eqGamma;
|
|
ASSERTGDI(eq >= 0, "Negative x not expected");
|
|
return((LONG) DIV(eq,dM) + ptlOrg.y);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* LONG xCompute(y)
|
|
*
|
|
* For the given row y, calculates the x-coordinate of the rightmost
|
|
* pixel that will be lit on the line:
|
|
*
|
|
* History:
|
|
* 27-Aug-1992 -by- J. Andrew Goossen [andrewgo]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
inline LONG DDA_CLIPLINE::xCompute(LONG y)
|
|
{
|
|
LONGLONG eq = Int32x32To64((LONG) dM, y - ptlOrg.y + 1) - eqGamma - 1;
|
|
ASSERTGDI(eq >= 0, "Negative y not expected");
|
|
return((LONG) DIV(eq,dN) + ptlOrg.x);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vIntersectHorizontal(pdda, yBorder, ppt0, ppt1, pi0)
|
|
*
|
|
* Calculates ppt0, the last point on the line given by pdda before a
|
|
* horizontal bound at yBorder. Also calculates ppt1, the next point on
|
|
* the line that would be lit.
|
|
*
|
|
* History:
|
|
* 27-Aug-1992 -by- J. Andrew Goossen [andrewgo]
|
|
* Rewrote it.
|
|
*
|
|
* 12-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vIntersectHorizontal(
|
|
DDA_CLIPLINE* pdda,
|
|
LONG yBorder,
|
|
POINTL* ppt0, // may be NULL
|
|
POINTL* ppt1, // may be NULL
|
|
LONG* pi0)
|
|
{
|
|
LONG xLast = pdda->xCompute(yBorder - 1);
|
|
|
|
// calculation
|
|
|
|
if (ppt0 != (POINTL*) NULL)
|
|
{
|
|
ppt0->x = xLast;
|
|
ppt0->y = yBorder - 1;
|
|
pdda->vUnflip(&ppt0->x, &ppt0->y);
|
|
}
|
|
|
|
if (ppt1 != (POINTL*) NULL)
|
|
{
|
|
ppt1->x = xLast + 1;
|
|
ppt1->y = yBorder;
|
|
pdda->vUnflip(&ppt1->x, &ppt1->y);
|
|
}
|
|
|
|
ASSERTGDI(xLast <= pdda->lX1, "xLast out of bounds");
|
|
|
|
*pi0 = xLast - pdda->lX0;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vIntersectVertical(pdda, xBorder, ppt0, ppt1, pi0)
|
|
*
|
|
* Calculates ppt0, the last point on the line given by pdda before a
|
|
* vertical bound at xBorder. Also calculates ppt1, the next point on
|
|
* the line that would be lit.
|
|
*
|
|
* History:
|
|
* 27-Aug-1992 -by- J. Andrew Goossen [andrewgo]
|
|
* Rewrote it.
|
|
*
|
|
* 12-Mar-1991 -by- Eric Kutter [erick]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vIntersectVertical(
|
|
DDA_CLIPLINE* pdda,
|
|
LONG xBorder,
|
|
POINTL* ppt0, // may be NULL
|
|
POINTL* ppt1, // may be NULL
|
|
LONG* pi0)
|
|
{
|
|
// compute the coordinates and run index
|
|
|
|
LONG xLast = xBorder - 1;
|
|
|
|
if (ppt0 != (POINTL*) NULL)
|
|
{
|
|
ppt0->x = xBorder - 1;
|
|
ppt0->y = pdda->yCompute(xBorder - 1);
|
|
pdda->vUnflip(&ppt0->x, &ppt0->y);
|
|
}
|
|
|
|
if (ppt1 != (POINTL*) NULL)
|
|
{
|
|
ppt1->x = xBorder;
|
|
ppt1->y = pdda->yCompute(xBorder);
|
|
pdda->vUnflip(&ppt1->x, &ppt1->y);
|
|
}
|
|
|
|
ASSERTGDI(xLast <= pdda->lX1, "xLast out of bounds");
|
|
|
|
*pi0 = xLast - pdda->lX0;
|
|
}
|