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