|
|
/******************************Module*Header*******************************\
* Module Name: clipobj.cxx * * Clipping object non-inline methods * * Created: 15-Sep-1990 15:25:02 * Author: Donald Sidoroff [donalds] * * Copyright (c) 1990-1999 Microsoft Corporation \**************************************************************************/
#include "precomp.hxx"
/******************************Public*Routine******************************\
* XCLIPOBJ::vSetup * * Create an XCLIPOBJ. * * input: * * prgn_ - region to build clipobj from * erclExcl - Only intrested in the part of the region that intersects this * iForcedClip - CLIP_NOFORCE - will be trivial if erclExcl is fully bounded * CLIP_FORCE - will never be trivial * CLIP_NOFORCETRIV - will be trivial if single rectangle * * output/clipobj: * * iUniq - iUniq from region * * rclBounds - bounds of the clipping area * intersection of erclExcl and region bounds. It may further * be reduced to only bound rectangles that touch erclExcl. * * iDComplexity - complexity of part of region that intersects drawing * * TRIVIAL - all parts of erclExcl are visible. The object may * span multiple scans but is fully contained. * * RECT - The object (bounded by erclExcl) need only be clipped * against a single rectangle set in rclBounds. * * COMPLEX - enumeration is needed to get a list of rectangles to clip against * * iFComplexity - full region complexity, RECT, RECT4, COMPLEX * * iMode - TC_RECTANGLES - internal storage mode * * fjOptions - OC_RESERVED - this bit used to be called OC_BANK_CLIP and * is set by banking drivers. This bit has been obsoleted, * but because 4.0 drivers may still set the bit we can't * re-use it for something else. * * * hidden fields: * * cObj - number of rectangles that intersect erclExcl * * History: * 06-Oct-1993 -by- Eric Kutter [erick] * Update documentation, remove traps, add paths * * 24-Jul-1991 -by- Donald Sidoroff [donalds] * Updated to DDI spec. * * 15-Sep-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
#define CMAXOBJ 10
VOID XCLIPOBJ::vSetup(REGION *prgn_, ERECTL& erclExcl, int iForcedClip) { // Set up the internal fields
prgn = prgn_;
// Initialize as many of the CLIPOBJ fields as we can:
*((ULONG*)&iDComplexity) = 0; // iDComplexity = DC_TRIVIAL,
// iMode = TC_RECTANGLES,
// fjOptions = 0,
// iFComplexity = invalid
rclBounds.bottom = erclExcl.bottom; rclBounds.right = erclExcl.right; rclBounds.top = erclExcl.top; rclBounds.left = erclExcl.left; iUniq = prgn->iUnique;
if ((prgn->sizeRgn <= SINGLE_REGION_SIZE) && (rclBounds.left >= prgn->rcl.left) && (rclBounds.top >= prgn->rcl.top) && (rclBounds.right <= prgn->rcl.right) && (rclBounds.bottom <= prgn->rcl.bottom) && (iForcedClip != CLIP_FORCE)) { // Unfortunately, some callers may give us an empty or crossed
// 'erclExcl', and we have to watch for that case:
if ((rclBounds.top < rclBounds.bottom) && (rclBounds.left < rclBounds.right)) {
// This is the trivial acceptance case, which will be the most common
// case. The region is a single rectangle that bounds the drawing.
// We've already set up for trivial clipping.
return; } }
// Calculate the intersection of the drawing bounds with the region
// bounds:
rclBounds.left = max(rclBounds.left, prgn->rcl.left); rclBounds.top = max(rclBounds.top, prgn->rcl.top); rclBounds.right = min(rclBounds.right, prgn->rcl.right); rclBounds.bottom = min(rclBounds.bottom, prgn->rcl.bottom);
if ((rclBounds.left >= rclBounds.right) || (rclBounds.top >= rclBounds.bottom)) { // This takes care of the trivial rejection case. It is the caller's
// responsibility to again check for this case by calling 'bEmpty'
// on 'rclBounds' -- in which case, we have to collapse the rectangle:
rclBounds.left = rclBounds.right; return; }
if ((prgn->sizeRgn <= SINGLE_REGION_SIZE) && (iForcedClip != CLIP_FORCE)) { // If the region is a single rectangle, the region bound IS the
// region, so we're done. Note that the trivial rejection case
// may come through this case -- it's the caller's responsibility
// to check that the rectangle is not empty.
if (iForcedClip != CLIP_NOFORCETRIV) iDComplexity = DC_RECT;
return; }
// Darn, now we have to do some real work.
ERECTL *percl = (ERECTL *) &rclBounds;
cObjs = 0; // Initialize object count
if (prgn->sizeRgn > QUANTUM_REGION_SIZE) iFComplexity = FC_COMPLEX; else if (prgn->sizeRgn > SINGLE_REGION_SIZE) iFComplexity = FC_RECT4;
// Traverse the scans
ERECTL erclAcc(0, 0, 0, 0); // Accumulate segments here
PSCAN pscn = prgn->pscnHead(); COUNT cScan = prgn->cScans; COUNT iWall; BOOL bSimple = (iForcedClip != CLIP_FORCE); // Assume DC_TRIVIAL clipping unless forced
COUNT cHit = 0;
// find the first one inside
while (cScan && (percl->top >= pscn->yBottom)) { pscn = pscnGet(pscn); --cScan; }
while (cScan--) { if (pscn->yTop >= percl->bottom) // Have we passed the rectangle?
break;
BOOL bBounded = FALSE; // Assume rectangle is unbounded
for (iWall = 0; iWall != pscn->cWalls; iWall += 2) { // If the right edge of the segment is to the left of the rectangle
// advance to the next scan segment and test again.
if (pscn->ai_x[iWall + 1].x <= percl->left) continue;
// If the left edge of the segment is to the right of the rectangle
// goto the next scan.
if (pscn->ai_x[iWall].x >= percl->right) break;
// Increment count of objects
cObjs++;
// this is getting rediculous, we assume it is complex
if (cObjs >= CMAXOBJ) { iDComplexity = DC_COMPLEX; cObjs = (COUNT)-1; return; }
// OK, we now know that SOME part of the rectangle overlaps
// this scan segment. Find the overlap and accumulate it.
RECTL rclTmp;
rclTmp.left = pscn->ai_x[iWall].x; rclTmp.right = pscn->ai_x[iWall + 1].x; rclTmp.top = pscn->yTop; rclTmp.bottom = pscn->yBottom;
erclAcc += rclTmp; // Expand the accumlated rectangle
// Now see if this rectangle peeks out past the segment. If
// it doesn't, then we still might be simply clipped.
if ((percl->left >= pscn->ai_x[iWall].x) && (percl->right <= pscn->ai_x[iWall + 1].x)) { bBounded = TRUE; } }
// If the rectangle was not left/right bounded by some segment in
// the scan, then this can't be a simple clip case.
bSimple &= bBounded;
pscn = pscnGet(pscn); }
// Clip the exclusion rectangle against the accumulated rectangle
*percl *= erclAcc;
if (!bSimple) { iDComplexity = (BYTE)(cObjs == 1 ? DC_RECT : DC_COMPLEX); } else { if ((iForcedClip == CLIP_NOFORCE) && !percl->bEqual(erclExcl)) iDComplexity = DC_RECT; } }
/******************************Public*Routine******************************\
* ULONG XCLIPOBJ::cEnumStart(bAll, iType, iDir, cLimit) * * Set up the enumerator for the clipping object * * History: * 24-Jul-1991 -by- Donald Sidoroff [donalds] * Updated to DDI spec. * * 19-Sep-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/ extern "C" ULONG CLIPOBJ_cEnumStart( CLIPOBJ *pco, BOOL bAll, ULONG iType, ULONG iDir, ULONG cLimit) { return (*(XCLIPOBJ *)pco).cEnumStart(bAll, iType, iDir, cLimit); }
ULONG XCLIPOBJ::cEnumStart( BOOL bAll, ULONG iType, ULONG iDir, ULONG cLimit) { // We may need to merge trapezoid region with the bounding rect if the flag
// indicate so.
// Handle any old direction requests
if (iDir == CD_ANY) iDir = CD_RIGHTDOWN;
// Save the info in the enumerator
enmr.iType = iType; enmr.iDir = iDir; enmr.bAll = bAll;
// If we are enumerating the entire region, use the region bounding box
// otherwise use the exclusion rectangle
if (enmr.bAll) enmr.ercl = *((ERECTL *) &prgn->rcl); else { enmr.ercl = *((ERECTL *) &rclBounds); }
// Now we have to do some actual work
// Set up top to bottom info
enmr.cScans = prgn->cScans - 1; enmr.yCurr = 0; enmr.yFinal = 0;
// Start from the top or bottom? Also, find the scan before the first scan
// that intersects the rclBounds. By finding the previous scan, we can let
// the normal enumeration find the first wall.
PSCAN pscn1;
if (iDir < CD_RIGHTUP) { // top to bottom
enmr.pscn = prgn->pscnHead(); enmr.yDelta = 1;
if (!enmr.bAll) { pscn1 = pscnGet(enmr.pscn);
while (pscn1->yBottom <= enmr.ercl.top) { --enmr.cScans;
if (enmr.cScans == 0) return((ULONG)-1);
enmr.pscn = pscn1; pscn1 = pscnGet(pscn1); } } } else { // bottom to top
enmr.pscn = pscnGot(prgn->pscnTail); enmr.yDelta = -1;
if (!enmr.bAll) { pscn1 = pscnGot(enmr.pscn);
while (pscn1->yTop >= enmr.ercl.bottom) { --enmr.cScans;
if (enmr.cScans == 0) return((ULONG)-1);
enmr.pscn = pscn1; pscn1 = pscnGot(pscn1); } } }
// Left to right info
enmr.iWall = 0; enmr.iFinal = 0;
// Going left or right?
if (iDir & 1) enmr.iOff = (COUNT) -2; else enmr.iOff = 2;
if (enmr.bAll) return(cObjs <= cLimit ? cObjs : (ULONG) -1);
return((ULONG) -1); }
/******************************Public*Routine******************************\
* ULONG XCLIPOBJ::cEnum(cj, pv) * * Get the next batch of rectangles or trapezoids from the clipping object * * History: * 24-Jul-1991 -by- Donald Sidoroff [donalds] * Updated to DDI spec. * * 15-Sep-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/ extern "C" BOOL CLIPOBJ_bEnum(CLIPOBJ* pco, ULONG cj, ULONG *pv) { return (*(XCLIPOBJ *)pco).bEnum(cj, (VOID *)pv); }
BOOL XCLIPOBJ::bEnum(ULONG cj,PVOID pv,ULONG *pcjFilled) { // Good old rectangle to rectangle enumeration
ENUMRECTS *penrc = (ENUMRECTS *) pv; RECTL *prcl = penrc->arcl;
if (cj < sizeof(ENUMRECTS)) { if (pcjFilled) *pcjFilled = 0; return FALSE; }
cj -= offsetof(ENUMRECTS,arcl);
if (pcjFilled) *pcjFilled = offsetof(ENUMRECTS,arcl);
penrc->c = 0;
if (enmr.bAll) { ULONG const RightToLeft = enmr.iDir & 1; ULONG const TopToBottom = enmr.iDir < CD_RIGHTUP; ULONG RectsWanted = cj / sizeof(RECTL); PSCAN pCurrentScan = enmr.pscn; COUNT CurrentWall = enmr.iWall; COUNT FinalWall = enmr.iFinal; int long Offset = enmr.iOff;
while (enmr.cScans) { if (CurrentWall == FinalWall) { // go to the next scan
if (TopToBottom) pCurrentScan = pscnGet(pCurrentScan); else pCurrentScan = pscnGot(pCurrentScan);
enmr.cScans--;
if(!pCurrentScan->cWalls) { continue; }
if (RightToLeft) { CurrentWall = pCurrentScan->cWalls - 2; FinalWall = (COUNT) -2; } else { CurrentWall = 0; FinalWall = pCurrentScan->cWalls; } }
prcl->left = pCurrentScan->ai_x[CurrentWall].x; prcl->right = pCurrentScan->ai_x[CurrentWall + 1].x; prcl->top = pCurrentScan->yTop; prcl->bottom = pCurrentScan->yBottom;
CurrentWall += Offset;
prcl++; penrc->c++; --RectsWanted;
if (pcjFilled) *pcjFilled += sizeof(RECTL);
if (!RectsWanted) { // this is the only time we need to save state, no?
enmr.iWall = CurrentWall; enmr.iFinal = FinalWall; enmr.pscn = pCurrentScan; return(TRUE); } } } else // and the not all case
{
ERECTL erclSrc; erclSrc.top = enmr.pscn->yTop; // Reset source top
erclSrc.bottom = enmr.pscn->yBottom; // and bottom
while (enmr.cScans) { // do we need a new scan?
if (enmr.iWall == enmr.iFinal) { // go to the next scan and see if we are done
if (enmr.iDir < CD_RIGHTUP) { // top to bottom
if (enmr.pscn->yBottom >= enmr.ercl.bottom) { enmr.cScans = 0; break; } enmr.pscn = pscnGet(enmr.pscn); } else { // bottom to top
if (enmr.pscn->yTop <= enmr.ercl.top) { enmr.cScans = 0; break; } enmr.pscn = pscnGot(enmr.pscn); }
enmr.cScans--;
// setup the scan
erclSrc.top = enmr.pscn->yTop; // Reset source top
erclSrc.bottom = enmr.pscn->yBottom; // and bottom
// find the first rectangle within the scan
if (enmr.iDir & 1) { // right to left
enmr.iWall = enmr.pscn->cWalls - 2; enmr.iFinal = (COUNT) -2;
while ((enmr.iWall != -2) && (enmr.pscn->ai_x[enmr.iWall].x >= enmr.ercl.right)) { enmr.iWall -= 2; } } else { // left to right
enmr.iWall = 0; enmr.iFinal = enmr.pscn->cWalls;
while ((enmr.iWall != enmr.iFinal) && (enmr.pscn->ai_x[enmr.iWall+1].x <= enmr.ercl.left)) { enmr.iWall += 2; } }
continue; }
// get the left and right from the scan
erclSrc.left = enmr.pscn->ai_x[enmr.iWall].x; erclSrc.right = enmr.pscn->ai_x[enmr.iWall + 1].x;
// intersect the side walls. If the rectangles don't overlap, we
// are done with this scan.
prcl->left = max(enmr.ercl.left,erclSrc.left); prcl->right = min(enmr.ercl.right,erclSrc.right);
if (prcl->left >= prcl->right) { enmr.iWall = enmr.iFinal; continue; }
// compute the top and bottom - these should be moved out of inner loop(erick)
prcl->top = max(enmr.ercl.top,erclSrc.top); prcl->bottom = min(enmr.ercl.bottom,erclSrc.bottom);
ASSERTGDI(prcl->top < prcl->bottom,"CLIPOBJ_benum, top >= bottom\n");
// advance to the next rectangle
enmr.iWall += enmr.iOff;
prcl++; cj -= sizeof(RECTL);
if (pcjFilled) *pcjFilled += sizeof(RECTL);
penrc->c++;
if (cj < sizeof(RECTL)) return(TRUE); } } return(FALSE); }
/******************************Public*Routine******************************\
* PATHOBJ *XCLIPOBJ::ppoGetPath() * * Create PATHOBJ from the clipping region. * * NOTE: This does NOT take the multi-monitor offset into account, so this * may not be used by display drivers. * * History: * 09-Mar-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/ extern "C" PATHOBJ *CLIPOBJ_ppoGetPath(CLIPOBJ *pco) { return (*(XCLIPOBJ*)pco).ppoGetPath(); }
PATHOBJ *XCLIPOBJ::ppoGetPath() { // Allocate a PATHOBJ from the heap. We will free this memory only if
// this function fails. Otherwise, we will rely on the device driver to
// call EngDeletePath later on to free the memory.
PVOID pepo = PALLOCNOZ(sizeof(EPATHOBJ),'oppG'); if (pepo == (PVOID) NULL) { return((PATHOBJ *) NULL); }
// Sigh, we have to create a path for this region.
PATHMEMOBJ pmo; if (!pmo.bValid()) { VFREEMEM(pepo); return(NULL); }
EXFORMOBJ exo(IDENTITY); ASSERTGDI(exo.bValid(), "Invalid Identity transform");
{ // pmoRect will contain an intermediate rectangular path that will
// in turn be processed into a diagonalized path that will be
// contained in pmo.
RTP_PATHMEMOBJ pmoRect; if ( !pmoRect.bValid() || !bCreate(pmoRect, &exo) || !pmoRect.bDiagonalizePath(&pmo) ) { VFREEMEM(pepo); return(NULL); } // pmoRect passes out of scope. Since I have NOT called
// pmoRect.vKeepIt() the memory of the rectangular path will
// be freed, while the diagonalized path as embodied in pmo
// will live on.
}
// OK, nothing can fail now. We will keep the diagonalized path
// by marking it as permanent. We will allow the destructor to
// kill the rectangular path in pmo.
pmo.vKeepIt();
// Make sure we copy the accelerator fields to the path we'll be
// giving out:
*((EPATHOBJ*) pepo) = pmo;
// Now lock down the object and point our path to it:
((EPATHOBJ *) pepo)->vLock(pmo.hpath());
return((PATHOBJ *) pepo); }
/******************************Public*Routine******************************\
* VOID XCLIPOBJ::vFindScan(prcl, y) * * Search for the scan that contains the given point * * History: * 13-Aug-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID XCLIPOBJ::vFindScan( RECTL *prcl, LONG y) { if (y < enmr.pscn->yTop) { while (y < enmr.pscn->yTop) enmr.pscn = pscnGot(enmr.pscn);
prcl->top = max(enmr.pscn->yTop, rclBounds.top); prcl->bottom = min(enmr.pscn->yBottom, rclBounds.bottom); prcl->left = prcl->right;
if (prcl->top >= prcl->bottom) prcl->top = NEG_INFINITY;
if (prcl->top == NEG_INFINITY) prcl->bottom = NEG_INFINITY; } else if (y >= enmr.pscn->yBottom) { while (y >= enmr.pscn->yBottom) enmr.pscn = pscnGet(enmr.pscn);
prcl->top = max(enmr.pscn->yTop, rclBounds.top); prcl->bottom = min(enmr.pscn->yBottom, rclBounds.bottom); prcl->left = prcl->right;
if (prcl->top >= prcl->bottom) prcl->bottom = POS_INFINITY;
if (prcl->bottom == POS_INFINITY) prcl->top = POS_INFINITY; } }
/******************************Public*Routine******************************\
* VOID XCLIPOBJ::vFindSegment(prcl, x, y) * * Search for the segment in the current scan that contains the given point * * History: * 13-Aug-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID XCLIPOBJ::vFindSegment( RECTL *prcl, LONG x, LONG y) { DONTUSE(y);
COUNT iWall; LONG xLeft; LONG xRight;
for (iWall = 0; iWall != enmr.pscn->cWalls; iWall += 2) { if ((x >= enmr.pscn->ai_x[iWall].x) && (x < enmr.pscn->ai_x[iWall + 1].x)) { xLeft = max(enmr.pscn->ai_x[iWall].x, rclBounds.left); xRight = min(enmr.pscn->ai_x[iWall + 1].x, rclBounds.right);
if (xLeft < xRight) { prcl->left = xLeft; prcl->right = xRight; } return; } } }
|