|
|
/******************************Module*Header*******************************\
* Module Name: rgnobj.cxx * * Non inline RGNOBJ methods * * Created: 02-Jul-1990 12:36:30 * Author: Donald Sidoroff [donalds] * * Copyright (c) 1990-1999 Microsoft Corporation \**************************************************************************/
#include "precomp.hxx"
// Region is expanded by this size when the size is too small.
// Used by bAddScans().
#define FILL_REGION_SIZE 10 * QUANTUM_REGION_SIZE
extern RECTL rclEmpty;
#if DBG
RGNOBJ *gprgn = NULL; #define VALIDATE(ro) {gprgn = &ro; (ro).bValidateFramedRegion(); }
#else
#define VALIDATE(ro)
#endif
//
// The following declarations are required by the native c8 compiler.
//
ULONG REGION::ulUniqueREGION; HRGN hrgnDefault; REGION *prgnDefault;
/**************************************************************************\
* \**************************************************************************/
#if DBG
#define MAXRGNLOG 1000
extern "C" { int gMaxRgnLog = MAXRGNLOG; RGNLOGENTRY argnlog[MAXRGNLOG]; LONG iLog = 0; LONG iPass = 0; };
BOOL bDispRgn = 0; BOOL bLogRgn = 1;
VOID vPrintRgn(RGNLOGENTRY& rl) { DbgPrint("%p,%p,(%8lx),%8lx,%8lx,%4lx, %s, %p, %p\n", rl.hrgn,rl.prgn,rl.lRes,rl.lParm1,rl.lParm2,rl.lParm3, rl.pszOperation,rl.pvCaller,rl.pvCallersCaller); }
RGNLOG::RGNLOG(HRGN hrgn,PREGION prgn,PSZ psz,ULONG_PTR l1, ULONG_PTR l2, ULONG_PTR l3) { if (!bLogRgn) return;
plog = &argnlog[iLog++]; if (iLog >= MAXRGNLOG) { iLog = 0; ++iPass; }
if (plog >= argnlog+MAXRGNLOG) plog = &argnlog[0];
plog->hrgn = (HOBJ) hrgn; plog->prgn = prgn; plog->pszOperation = psz; plog->lRes = 0xff; plog->lParm1 = l1; plog->lParm2 = l2; plog->lParm3 = l3; plog->teb = (PVOID)W32GetCurrentThread();
RtlGetCallersAddress(&plog->pvCaller,&plog->pvCallersCaller);
if (bDispRgn) vPrintRgn(*plog); }
RGNLOG::RGNLOG(PREGION prgn,PSZ psz,ULONG_PTR l1, ULONG_PTR l2, ULONG_PTR l3) { if (!bLogRgn) return;
plog = &argnlog[iLog++]; if (iLog >= MAXRGNLOG) { iLog = 0; ++iPass; }
if (plog >= argnlog+MAXRGNLOG) plog = &argnlog[0];
plog->hrgn = (HOBJ) 1; plog->prgn = prgn; plog->pszOperation = psz; plog->lRes = 0xff; plog->lParm1 = l1; plog->lParm2 = l2; plog->lParm3 = l3; plog->teb = (PVOID)W32GetCurrentThread();
RtlGetCallersAddress(&plog->pvCaller,&plog->pvCallersCaller);
if (bDispRgn) vPrintRgn(*plog); } #endif
/******************************Public*Routine******************************\
* RGNOBJ::vGetSubRect * * Return largest rectange completely within the region. * * History: * 09-Sep-1992 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
VOID RGNOBJ::vGetSubRect(PRECTL prcl) { // We should try and do better here but this will solve 80% of our
// performance goal. We should try and return the biggest rectangle
// completely contained within the region that we can quickly compute.
if (prgn->sizeRgn <= SINGLE_REGION_SIZE) { // Bounding rect == rect region
*prcl = prgn->rcl; return; }
*prcl = rclEmpty; }
/******************************Public*Routine******************************\
* * * History: * 05-Jul-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
RGNOBJAPI::RGNOBJAPI(HRGN hrgn,BOOL bSelect) { prgn = (REGION *)HmgLock((HOBJ)hrgn, RGN_TYPE); RGNLOG rl(hrgn,prgn,"RGNOBJAPI::RGNOBJAPI"); hrgn_ = hrgn; bSelect_ = bSelect;
if (prgn != (PREGION)NULL) { BOOL bStatus = TRUE; //
// Does this region have valid user-mode data? If there is
// any problem with the user-mode state, then the contructor
// must unlock the region set prgn to NULL.
//
PRGNATTR prRegion = (PRGNATTR)(PENTRY_FROM_POBJ(prgn)->pUser);
if (prRegion != (PRGNATTR)NULL) { //
// update a valid rgn, we can get an invalid
// region here because gdi32!DeleteRegion must
// clear the VALID flag before calling the
// kernel.
//
if ( (prRegion->AttrFlags & ATTR_RGN_VALID) && !(prRegion->AttrFlags & ATTR_CACHED) ) { if (prRegion->AttrFlags & ATTR_RGN_DIRTY) { if (prRegion->Flags == NULLREGION) { vSet(); prRegion->AttrFlags &= ~ATTR_RGN_DIRTY; } else if (prRegion->Flags == SIMPLEREGION) { vSet(&prRegion->Rect); prRegion->AttrFlags &= ~ATTR_RGN_DIRTY; } } } else { bStatus = FALSE; } }
if (!bStatus) { DEC_EXCLUSIVE_REF_CNT(prgn); prgn = NULL; hrgn_ = NULL; } } }
/******************************Member*Function*****************************\
* * History: * 22-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL RGNOBJAPI::bDeleteHandle() { RGNLOG rl(hrgn_,prgn,"RGNOBJAPI::bDeleteHandle");
ASSERTGDI(hrgn_ != (HRGN) NULL, "Delete NULL\n");
if (hrgn_ == hrgnDefault) { rl.vRet(0); return(FALSE); }
PREGION prgn1 = (PREGION)HmgRemoveObject((HOBJ) hrgn_, 1, 0, FALSE, RGN_TYPE);
if (prgn1 != prgn) { rl.vRet(-1);
#if DBG
DbgPrint("couldn't delete api rgn - %p, prgn1 = %p\n",hrgn_,prgn1); DbgBreakPoint(); #endif
return(FALSE); } hrgn_ = NULL;
rl.vRet(1); return(TRUE); }
/******************************Member*Function*****************************\
* * History: * 22-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL RGNOBJAPI::bDeleteRGNOBJAPI() { RGNLOG rl(hrgn_,prgn,"RGNOBJAPI::bDelete"); BOOL bRes = FALSE; POBJECTATTR pRgnattr = NULL;
//
// if this region has user-mode memory, try to place in cache
//
if (prgn) { PENTRY pEntry = PENTRY_FROM_POBJ(prgn); pRgnattr = (POBJECTATTR)pEntry->pUser;
if (pRgnattr) { bRes = bPEBCacheHandle(prgn->hGet(),RegionHandle,pRgnattr,pEntry); } }
if (!bRes) { bRes = bDeleteHandle() && bDeleteRGNOBJ();
if (bRes && (pRgnattr != NULL)) { HmgFreeObjectAttr((POBJECTATTR)pRgnattr); } } rl.vRet(bRes); return(bRes); }
VOID RGNOBJ::vDeleteRGNOBJ() { //
// Deletes the region and sets it to NULL.
//
RGNLOG rl(prgn,"RGNOBJ::vDeleteRGNOBJ"); prgn->vDeleteREGION(); prgn = NULL; }
/******************************Member*Function*****************************\
* * History: * 22-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
//
// This struct and the following union can be thrown away
// when someone fixes the BASEOBJECT cExclusiveLock and BaseFlags sharing
// the same DWORD.
//
struct SplitLockAndFlags { USHORT c_cExclusiveLock; USHORT c_BaseFlags; };
union SplitOrCombinedLockAndFlags { SplitLockAndFlags S; ULONG W; };
BOOL RGNOBJ::bSwap(RGNOBJ *pro) { RGNLOG rl(prgn,"RGNOBJ::bSwap",(ULONG_PTR)pro,(ULONG_PTR)pro->prgn);
//
// Swap the BASEOBJECT info at the start of the region.
// Ensuring that when cExclusiveLock is swapped, the BaseFlags
// is not swapped with it.
// BaseFlags contains information about how the
// object was allocated (and therefore, how it must be deallocated). Thus,
// it represents state associated with the actual memory, not the object,
// and should not be swapped.
//
// See comments regarding BASEOBJECT in inc\hmgshare.h for more details.
// Also, there is swapping code in HmgSwapLockedHandleContents (hmgrapi.cxx).
//
BASEOBJECT *proB = (BASEOBJECT *)pro->prgnGet(); BASEOBJECT obj = *proB; SplitOrCombinedLockAndFlags lfTmp;
proB->hHmgr = prgn->hHmgr; lfTmp.S.c_cExclusiveLock = prgn->cExclusiveLock; lfTmp.S.c_BaseFlags = proB->BaseFlags; InterlockedExchange((LONG *) &(proB->cExclusiveLock), lfTmp.W); proB->Tid = prgn->Tid;
prgn->hHmgr = obj.hHmgr;
lfTmp.S.c_cExclusiveLock = obj.cExclusiveLock; lfTmp.S.c_BaseFlags = prgn->BaseFlags; InterlockedExchange((LONG *) &(prgn->cExclusiveLock), lfTmp.W); prgn->Tid = obj.Tid;
// Swap the selection data.
COUNT cRefsTemp = prgn->cRefs; prgn->cRefs = pro->prgn->cRefs; pro->prgn->cRefs = cRefsTemp;
// swap the pointers in the objects
PREGION prgnTmp = prgn; prgn = pro->prgnGet(); pro->vSetRgn(prgnTmp);
return(TRUE); }
/******************************Member*Function*****************************\
* * History: * 22-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL RGNOBJAPI::bSwap(RGNOBJ *pro) { RGNLOG rl(hrgn_,prgn,"RGNOBJAPI::bSwap",(ULONG_PTR)pro,(ULONG_PTR)pro->prgn);
ASSERTGDI(hrgn_ != NULL,"RGNOBJAPI::bSwap - hrgn is null\n");
//
// dunno if this is safe without grabbing the handle lock first
//
//
// This increments the lock count so that between the HmgReplace and
// the bSwap we're guaranteed a cExclusiveLock > 0 for both objects.
//
INC_EXCLUSIVE_REF_CNT(pro->prgnGet()); INC_EXCLUSIVE_REF_CNT(prgn);
// swap the pointer in the handle
PREGION prgnRet = (PREGION)HmgReplace((HOBJ) hrgn_,(POBJ) pro->prgnGet(),0,1,RGN_TYPE);
if (prgnRet != prgn) { rl.vRet((ULONG_PTR)prgnRet); RIP("RGNOBJ::bSwap - swapping invalid rgn\n");
return(FALSE); }
// swap the objects
rl.vRet(1);
BOOL retVal = RGNOBJ::bSwap(pro);
//
// Decrementing the cExclusiveLock for both objects in this way ensures
// that after the swap the Lock status is restored for _both_ objects.
//
DEC_EXCLUSIVE_REF_CNT(pro->prgnGet()); DEC_EXCLUSIVE_REF_CNT(prgn);
return retVal; }
/******************************Public*Routine******************************\
* VOID RGNOBJ::vCopy() * * Copy region. There are some fields like the object size that don't need * copying. This deals with them appropriately * * History: * 22-Jul-1993 -by- Eric Kutter [erick] * reorged region structure so don't have to deal with individual * fields that may not need copying. * * 04-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID RGNOBJ::vCopy(RGNOBJ& roSrc) { RGNLOG rl(prgn,"RGNOBJ::vCopy",(ULONG_PTR)roSrc.prgn);
ASSERTGDI(prgn->sizeObj >= roSrc.prgn->sizeRgn, "sizeObj Src > sizeRgn Trg\n");
RtlCopyMemory ((PBYTE) prgn + RGN_COPYOFFSET, (PBYTE) roSrc.prgn + RGN_COPYOFFSET, roSrc.prgn->sizeRgn - RGN_COPYOFFSET);
// Get the difference and add it to the pscnTail pointer. This is faster
// than running through the list to find it.
prgn->pscnTail = (SCAN *) ((BYTE *) prgn->pscnHead() + (LONG) ((BYTE *) roSrc.prgn->pscnTail - (BYTE *) roSrc.prgn->pscnHead())); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bCopy(roSrc) * * Copy region. * * NOTE: This is significantly different than vCopy. This routine will * create a new region if the source and target are of different * complexities. * * WARNING: the prgn may change. If this rgn is associated with a handle, * the handle's version will not have changed. * * History: * 04-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bCopy(RGNOBJ& roSrc) { RGNLOG rl(prgn,"RGNOBJ::bCopy",(ULONG_PTR)roSrc.prgn);
if (prgn->sizeObj <= QUANTUM_REGION_SIZE) { if (roSrc.prgn->sizeObj <= QUANTUM_REGION_SIZE) { rl.vRet(1); vCopy(roSrc); return(TRUE); } else { rl.vRet(2); RGNMEMOBJTMP rmo(roSrc.prgn->sizeRgn);
if (!rmo.bValid()) return(FALSE);
rmo.vCopy(roSrc); return(bSwap(&rmo)); } }
if (roSrc.prgn->sizeObj <= QUANTUM_REGION_SIZE) { rl.vRet(3); RGNMEMOBJTMP rmo2;
if (!rmo2.bValid()) return(FALSE);
rmo2.vCopy(roSrc); return(bSwap(&rmo2)); }
if (prgn->sizeObj >= roSrc.prgn->sizeRgn) { rl.vRet(4); vCopy(roSrc); return(TRUE); }
RGNMEMOBJTMP rmo3(roSrc.prgn->sizeRgn);
rl.vRet(5);
if (!rmo3.bValid()) return(FALSE);
rmo3.vCopy(roSrc); return(bSwap(&rmo3)); }
/******************************Member*Function*****************************\
* bCopy * * This should be used if you want any prgn change reflected in the handle. * * History: * 22-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL RGNOBJAPI::bCopy(RGNOBJ& roSrc) { RGNLOG rl(hrgn_,prgn,"RGNOBJAPI::bCopy",(ULONG_PTR)roSrc.prgn);
ASSERTGDI(hrgn_ != NULL,"RGNOBJAPI::bCopy\n");
PREGION prgnOrg = prgn;
// lock the handle so no one can reference it while the pobj may be invalid
OBJLOCK ol((HOBJ) hrgn_);
BOOL bRes = RGNOBJ::bCopy(roSrc);
rl.vRet(0);
if (bRes) { rl.vRet(1); if (prgn != prgnOrg) { rl.vRet((ULONG_PTR)prgn);
PVOID pv = HmgReplace((HOBJ) hrgn_,(POBJ) prgn,0,1,OBJLOCK_TYPE); ASSERTGDI(pv != NULL,"RGNOBJAPI::bCopy - HmgReplace failed\n"); } }
return(bRes); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bExpand(size) * * Expand the object to the given size * * History: * 10-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bExpand(ULONGSIZE_T size) { RGNLOG rl(prgn,"RGNOBJ::bExpand",size);
ASSERTGDI(size > prgn->sizeObj, "Expanded size <= original size\n");
RGNMEMOBJTMP rmo(size);
rl.vRet((ULONG_PTR)rmo.prgnGet());
if (!rmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); }
rmo.vCopy(*this);
return(bSwap(&rmo)); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bInside(pptl) * * Is the point inside the region? * * Returns: * ERROR 0L * REGION_POINT_OUTSIDE 1L * REGION_POINT_INSIDE 2L * * History: * 02-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bInside(PPOINTL pptl) { // First check if the point is in the bounding box
if (!bBounded(pptl)) return(REGION_POINT_OUTSIDE);
BOOL b = REGION_POINT_OUTSIDE; // Assume point is outside
PSCAN pscn; COUNT cScan; COUNT cWall; COUNT iWall;
pscn = prgn->pscnHead(); // Get first scan
cScan = prgn->cScans; // Get scan count
while (cScan--) { if (pscn->yTop > pptl->y) // Have we passed the point?
return(b); // Yes, exit.
if (pscn->yBottom > pptl->y) // Does this scan overlap the point?
{ // Yes, test walls/lines.
ASSERTGDI(!(pscn->cWalls & 1), "Invalid cWalls\n");
iWall = 0; cWall = pscn->cWalls;
while (iWall != cWall) if (xGet(pscn, (PTRDIFF)iWall++) > pptl->x) return(b); else b ^= (REGION_POINT_INSIDE | REGION_POINT_OUTSIDE); }
pscn = pscnGet(pscn);
ASSERTGDI( pscn <= prgn->pscnTail, "bInside:Went past end of region\n"); }
WARNING("********** RGNOBJ::bInside *****************\n");
return(b); }
/******************************Public*Routine******************************\
* RGNOBJ::bInside (prcl) * * * * Does the rectangle intersect the region? * * * * Returns: * * ERROR 0L * * REGION_RECT_OUTSIDE 1L * * REGION_RECT_INTERSECT 2L * * * * History: * * Tue 12-May-1992 22:23:10 -by- Charles Whitmer [chuckwh] * * Rewrote the scan search. I want this zippy fast since I'm going to use * * it for pointer exclusion. * * * * 11-May-1991 -by- Kent Diamond [kentd] * * Rewrote. No more support for partial intersection. * * * * 02-Jul-1990 -by- Donald Sidoroff [donalds] * * Wrote it. * \**************************************************************************/
BOOL RGNOBJ::bInside(PRECTL prcl) { // First check if the rectangle is outside the bounding box
if ((prcl->left >= prgn->rcl.right) || (prcl->right <= prgn->rcl.left) || (prcl->top >= prgn->rcl.bottom) || (prcl->bottom <= prgn->rcl.top)) return(REGION_RECT_OUTSIDE);
// Skip scans above the rectangle.
PSCAN pscn = prgn->pscnHead(); COUNT cScan = prgn->cScans;
while (cScan && (prcl->top >= pscn->yBottom)) { pscn = pscnGet(pscn); cScan--; }
// Examine all interesting scans.
INDEX_LONG *pix,*pixEnd;
while (cScan && (prcl->bottom > pscn->yTop)) { pix = pscn->ai_x; pixEnd = pix + 2 * pscn->cWalls;
// Skip segments to the left.
while ((pix < pixEnd) && (prcl->left >= pix[1].x)) pix += 2;
// It's this segment or nothing!
if ((pix < pixEnd) && (prcl->right > pix[0].x)) return(REGION_RECT_INTERSECT);
// Move to the next scan.
pscn = pscnGet(pscn); cScan--; } return(REGION_RECT_OUTSIDE); // Did not find an intersection
}
/******************************Public*Routine******************************\
* RGNOBJ::bEqual(roSrc) * * Are the two regions equal? * * NOTE: The handle and sizeObj maybe different even though the regions * are the same, so these are skipped. * * Returns: * TRUE if they are equal. * FALSE if they are not. * * History: * 13-May-1991 -by- Donald Sidoroff [donalds] * Rewrote it. * 02-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bEqual(RGNOBJ& roSrc) { // In the region header, only cScans need to be similar for
// a region to equal another region
// Now run thru the scans and determine if they are the same
ASSERT4GB((LONGLONG)((BYTE *)prgn->pscnTail - (BYTE *)prgn->pscnHead()));
return( (prgn->cScans == roSrc.prgn->cScans) && (!memcmp(prgn->pscnHead(), roSrc.prgn->pscnHead(), (ULONG)((BYTE *)prgn->pscnTail - (BYTE *)prgn->pscnHead())))); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bOffset(pptl) * * Offset the region by the point. * * Note: * Since the point is in DEVICE coords it will have to be converted to * FIX notation before it can be added to endpoints of trapezoid lines. * * History: * 02-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bOffset(PPOINTL pptl) { COUNT cscn; COUNT cwll; PSCAN pscn; int x = (int)pptl->x; int y = (int)pptl->y;
// Can't fail to offset a NULL region
if (prgn->cScans == 1) return(TRUE);
// First try to update the bounding box. If we are successful here then
// all the other offsets will be successful. This eliminates the need
// for checking for overflow/underflow on every offset operation.
ERECTL ercl(prgn->rcl); // Get the current bounding box
if (ercl.bWrapped()) return(TRUE);
ercl.left += x; ercl.bottom += y; ercl.right += x; ercl.top += y;
if (!VALID_SCRRC(ercl)) { // Foo, we over/under flowed.
SAVE_ERROR_CODE(ERROR_ARITHMETIC_OVERFLOW); return(FALSE); } prgn->rcl = *((RECTL *)&ercl); // Set the current bounding box
cscn = prgn->cScans; // Number of scans;
pscn = prgn->pscnHead(); // First scan
while (cscn--) { pscn->yTop += y; pscn->yBottom += y;
cwll = pscn->cWalls; while (cwll) pscn->ai_x[--cwll].x += x;
pscn = pscnGet(pscn); // Get next scan
ASSERTGDI(pscn <= prgn->pscnTail, "bOffset:Went past end of region\n"); }
pscn = pscnGot(pscn); // Fix top ...
pscn->yBottom = POS_INFINITY; pscn = prgn->pscnHead(); // ... and bottom.
pscn->yTop = NEG_INFINITY;
return(TRUE); }
/******************************Public*Routine******************************\
* VOID RGNOBJ::vSet() * * Set region to null region * * History: * 05-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID RGNOBJ::vSet() { RGNLOG rl(prgn,"RGNOBJ::vSet");
PREGION prgn1 = prgn;
prgn1->sizeRgn = NULL_REGION_SIZE; prgn1->cScans = 1; prgn1->rcl.left = 0; prgn1->rcl.top = 0; prgn1->rcl.right = 0; prgn1->rcl.bottom = 0;
PSCAN pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); }
/******************************Public*Routine******************************\
* VOID RGNOBJ::vSet() * * Set region from list of rectangles * * History: * 7-18-2000 bhouse Wrote it * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bSet(ULONG cRect, RECTL * prcl) { RGNMEMOBJTMP rmoTmp1, rmoTmp2;
if (!rmoTmp1.bValid() || !rmoTmp2.bValid()) { return(FALSE); }
// NOTE: the iCombine call below is O(n*n) so
// we want to only call it with small n. We use
// merge sort which is O(n*log n) to handle larger n.
//
// The limit of 20 was picked as a reasonable number which
// balances the cost of the RGNMEMOBJ creation
// with the cost of the n squared algorithm.
//
if(cRect < 20) { BOOL bDoneFirst=FALSE; for (ULONG i=0; i < cRect; i++, prcl++) { // ignore bad rectangles
if (!((prcl->left>=prcl->right) || (prcl->top>=prcl->bottom) || (prcl->left<MIN_REGION_COORD) || (prcl->right>MAX_REGION_COORD)|| (prcl->top<MIN_REGION_COORD) || (prcl->bottom>MAX_REGION_COORD))) { if(!bDoneFirst) { // Do the first rectangle
vSet(prcl); bDoneFirst=TRUE; } else { // Now get the rest of the rectangles, one by one
rmoTmp1.vSet(prcl); rmoTmp2.iCombine(*this, rmoTmp1, RGN_OR); // put result of merge into rmoTmp2
bSwap(&rmoTmp2); // now move into rmo
} } } } else { RGNMEMOBJTMP rmoTmp3; ULONG cTmp1 = cRect >> 1; ULONG cTmp2 = cRect - cTmp1;
if(!rmoTmp1.bSet(cTmp1, prcl) || !rmoTmp2.bSet(cTmp2, prcl+cTmp1)) { return FALSE; }
rmoTmp3.iCombine(rmoTmp2, rmoTmp1, RGN_OR); bSwap(&rmoTmp3);
} return TRUE; }
/******************************Public*Routine******************************\
* VOID RGNOBJ::vSet(prcl) * * Set region to single rect * * History: * 09-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID RGNOBJ::vSet(PRECTL prcl) { RGNLOG rl(prgn,"RGNOBJ::vSet prcl");
PSCAN pscn;
if ((prcl->left == prcl->right) || (prcl->top == prcl->bottom)) { vSet(); return; }
// If the region is already a RECT region, this can be much faster
PREGION prgn1 = prgn;
prgn1->rcl = *prcl;
if (prgn1->sizeRgn == SINGLE_REGION_SIZE) { rl.vRet(0);
ASSERTGDI(prgn1->cScans == 3,"RGNOBJ::vSet - cScans != 3\n");
// scan 0
pscn = prgn1->pscnHead(); ASSERTGDI(pscn->yTop == NEG_INFINITY,"RGNOBJ::vSet - yTop0\n");
pscn->yBottom = prcl->top;
// scan 1
pscn = pscnGet(pscn); ASSERTGDI(pscn->cWalls == 2,"RGNOBJ::vSet - cWalls1 != 2\n");
pscn->yTop = prcl->top; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right;
// scan 2
pscn = pscnGet(pscn); ASSERTGDI(pscn->cWalls == 0,"RGNOBJ::vSet - cWalls2 != 0\n"); ASSERTGDI(pscn->yBottom == POS_INFINITY,"RGNOBJ::vSet - yBottom2\n");
pscn->yTop = prcl->bottom;
// tail
prgn1->pscnTail = pscnGet(pscn); } else { rl.vRet(0);
prgn1->sizeRgn = SINGLE_REGION_SIZE; prgn1->cScans = 3;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); } VALIDATE(*(RGNOBJ *)this); }
/******************************Public*Routine******************************\
* VOID SCAN::vMerge(pscnSrcA, pscnSrcB, fj) * * Merge the scans. * * The algorithm takes advantage of the pre-sorted nature of the source * scans. We define the 'events' to be the merged sorted list of X values * from both A and B. The 'op' is referenced to determine if the event * is worth recording in the destination. * * There are a few tricks used in this routine. The first is the way that * the state is recorded. There are four states possible, since * we can either be IN or OUT of A or B. These states are: * * 0001b OUT A, OUT B * 0010b OUT A, IN B * 0100b IN A, OUT B * 1000b IN A, IN B * * From this we can see A is equal to 1100 (IN_A_OUT_B | IN_A_IN_B) and * that B is equal to 1010 (OUT_A_IN_B | IN_A_IN_B). * * We use a state table to find the next state. The index into the table * is the current state and the value we get out the new state. * * We always begin with an initial state OUT A, OUT B. We then take the * lowest X value from either A or B. We then map through the table of * which ever scan we pulled an X value from and so the state always * indicates whether we are in (entering) or out (leaving) of either scan. * * The second trick is in the similar way that the logical operation * to be used for the merge is encoded in the 'op'. This is done as a * simple logical truth table that can be tested against the state to * see if we are IN or OUT of the result. The frequently used ops are: * * 1110 OR * 1000 AND * 0110 XOR * 0100 DIFF * * These ops are seen to be the OR of the appropriate state definitions * needed for the boolean operation. For example the op for 'A OR B' is * derived from (1100 | 1010). * * In fact, any op whose lower bit is 0 is allowed. This gives eight * possible operations. * * The final trick we use to determine when an event worthy of recording * has occured. When we are not in a destination rectangle, we just * test the new state against the op to see if we've just changed to IN. * If so, we invert the op. Now we only need to test the state against * this inverted op to determine when we pop OUT again! So whenever we * record a point we invert the op. * * History: * 11-Nov-1992 -by- Donald Sidoroff [donalds] * Merged into RGNOBJ::bMerge for speed. * * 09-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
#define MERGE_OUT_OUT 0x01
#define MERGE_OUT_IN 0x02
#define MERGE_IN_OUT 0x04
#define MERGE_IN_IN 0x08
#define MERGE_INITIAL_STATE MERGE_OUT_OUT
static FCHAR afjA[16] = { 0x00, //
MERGE_IN_OUT, // OUT OUT -> IN OUT
MERGE_IN_IN, // OUT IN -> IN IN
0x00, //
MERGE_OUT_OUT, // IN OUT -> OUT OUT
0x00, //
0x00, //
0x00, //
MERGE_OUT_IN // IN IN -> OUT IN
};
static FCHAR afjB[16] = { 0x00, //
MERGE_OUT_IN, // OUT OUT -> OUT IN
MERGE_OUT_OUT, // OUT IN -> OUT OUT
0x00, //
MERGE_IN_IN, // IN OUT -> IN IN
0x00, //
0x00, //
0x00, //
MERGE_IN_OUT // IN IN -> IN OUT
};
static FCHAR afjAB[16] = { 0x00, //
MERGE_IN_IN, // OUT OUT -> IN IN
MERGE_IN_OUT, // OUT IN -> IN OUT
0x00, //
MERGE_OUT_IN, // IN OUT -> OUT IN
0x00, //
0x00, //
0x00, //
MERGE_OUT_OUT // IN IN -> OUT OUT
};
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bMerge(proSrc1, proSrc2, fjOp) * * Merge two regions together. * * WARNING: If this function returns FALSE, the region may be inconsistent. * The caller must discard or reset the region. (See bug #343770) * * History: * 11-Nov-1992 -by- Donald Sidoroff [donalds] * Merged SCAN::vMerge into code for speed up. More aggressive memory * usage scheme and other optimizations. * * 09-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bMerge(RGNOBJ& roSrc1, RGNOBJ& roSrc2, FCHAR fjOp) { RGNLOG rl(prgn,"RGNOBJ::bMerge",(ULONG_PTR)roSrc1.prgn,(ULONG_PTR)roSrc2.prgn,(ULONG_PTR)fjOp);
SCAN *pscnSrc1 = roSrc1.prgn->pscnHead(); SCAN *pscnSrc2 = roSrc2.prgn->pscnHead(); SCAN *pscnOld = (SCAN *) NULL; SCAN *pscnTrg; LONG yTop; LONG yBottom; ULONG size;
// Set target region to be TOTALLY empty, yet valid.
prgn->pscnTail = prgn->pscnHead(); prgn->sizeRgn = NULL_REGION_SIZE - NULL_SCAN_SIZE; prgn->cScans = 0;
// Ensure the bounding box gets updated later on.
prgn->rcl.left = POS_INFINITY; prgn->rcl.top = POS_INFINITY; prgn->rcl.right = NEG_INFINITY; prgn->rcl.bottom = NEG_INFINITY;
// Merge the source scans into the target
for(;;) { pscnTrg = prgn->pscnTail;
// Check for nearly full region
size = (pscnSrc1->cWalls + pscnSrc2->cWalls) * sizeof(INDEX_LONG) + NULL_SCAN_SIZE;
if (size > prgn->sizeObj - prgn->sizeRgn) { // OK, we need to realloc this region. Lets be fairly aggressive
// on the allocate to cut down on realloc's
if (!bExpand(prgn->sizeRgn * 2 + (ULONGSIZE_T)size)) return(FALSE);
pscnTrg = prgn->pscnTail; // Get the updated object's tail.
if (pscnOld) // If we had an old scan
pscnOld = pscnGot(pscnTrg); // Get the updated old scan.
}
yTop = MAX(pscnSrc1->yTop, pscnSrc2->yTop); yBottom = MIN(pscnSrc1->yBottom, pscnSrc2->yBottom);
ASSERTGDI(yBottom > yTop, "Bottom <= Top\n");
// Merge the current scans
pscnTrg->yBottom = yBottom; pscnTrg->yTop = yTop;
{ register INDEX_LONG *plTrg = &pscnTrg->ai_x[0]; register INDEX_LONG *plSrc1 = &pscnSrc1->ai_x[0]; register INDEX_LONG *plSrc2 = &pscnSrc2->ai_x[0]; COUNT cSrc1 = pscnSrc1->cWalls; COUNT cSrc2 = pscnSrc2->cWalls; LONG xEvent; FCHAR fjState = MERGE_INITIAL_STATE; FCHAR fjOpTmp = fjOp;
pscnTrg->cWalls = 0; // Init the wall count to zero
// Continue to loop as long as either cSrc1 or cSrc2 (or both) are non-zero.
// We terminate the loop via a break statement because this causes the compiler
// to generate more efficient code (fewer branches). I believe this is a key loop
// that is worth optimizing at a minor cost to readability.
while (1) { if (cSrc1) { if (cSrc2) { // Both cSrc1 and cSrc2 are non-zero, so the next xEvent
// will come from whichever is smaller: *plSrc1 or *plSrc2
if (plSrc1->x < plSrc2->x) { xEvent = plSrc1->x, plSrc1++, cSrc1--, fjState = afjA[fjState]; } else if (plSrc1->x > plSrc2->x) { xEvent = plSrc2->x, plSrc2++, cSrc2--, fjState = afjB[fjState]; } else { // *plSrc1 and *plSrc2 are equal so advance both pointers
xEvent = plSrc1->x, plSrc1++, cSrc1--, plSrc2++, cSrc2--, fjState = afjAB[fjState]; } } else { // cSrc1 is non-zero, but cSrc2 is zero: the next xEvent is
// at *plSrc1
xEvent=plSrc1->x, plSrc1++, cSrc1--, fjState = afjA[fjState]; } } else { if (cSrc2) {
// cSrc1 is zero and cSrc2 is non-zero: the next xEvent is
// at *plSrc2
xEvent = plSrc2->x, plSrc2++, cSrc2--, fjState = afjB[fjState];
} else { // both cSrc1 and cSrc2 are zero. We are done with the loop.
break; }
} // We now have the next event and a new state
if (fjOpTmp & fjState) pscnTrg->cWalls++, plTrg->x = xEvent, plTrg++, fjOpTmp ^= 0x0f; }
pscnTrg->ai_x[pscnTrg->cWalls].x = pscnTrg->cWalls; }
ASSERTGDI(!(pscnTrg->cWalls & 1), "Odd cWalls\n");
// Try to coalesce the current scan with the previous scan
if (pscnOld != (SCAN *) NULL) { // If the wall counts are the same, compare the walls
if (pscnOld->cWalls == pscnTrg->cWalls) if (!memcmp(&pscnOld->ai_x[0], &pscnTrg->ai_x[0], (UINT)pscnOld->cWalls * sizeof(INDEX_LONG))) { pscnOld->yBottom = pscnTrg->yBottom; pscnTrg = pscnOld; } }
// If the scans didn't coalesce, update size and count information
if (pscnOld != pscnTrg) { prgn->pscnTail = pscnGet(pscnTrg); prgn->sizeRgn += pscnTrg->sizeGet(); prgn->cScans++; }
// We might be done
if (pscnTrg->yBottom == POS_INFINITY) { ASSERTGDI((prgn->sizeRgn <= prgn->sizeObj),"bMerge: sizeRgn > sizeObj\n"); ASSERTGDI(prgn->sizeRgn == (SIZE_T)((BYTE *)prgn->pscnTail - (BYTE *)prgn), "bMerge:sizeRgn != size of region\n");
return(TRUE); }
// Maybe update the bounding rectangle
if (pscnTrg->cWalls) { if (pscnTrg->ai_x[0].x < prgn->rcl.left) prgn->rcl.left = pscnTrg->ai_x[0].x;
if (pscnTrg->yTop < prgn->rcl.top) prgn->rcl.top = pscnTrg->yTop;
if (pscnTrg->ai_x[pscnTrg->cWalls - 1].x > prgn->rcl.right) prgn->rcl.right = pscnTrg->ai_x[pscnTrg->cWalls - 1].x;
if (pscnTrg->yBottom > prgn->rcl.bottom) prgn->rcl.bottom = pscnTrg->yBottom; }
// Decide which source pointers need to be advanced
if (yBottom == pscnSrc1->yBottom) pscnSrc1 = pscnGet(pscnSrc1);
if (yBottom == pscnSrc2->yBottom) pscnSrc2 = pscnGet(pscnSrc2);
// Set the pscnOld to the current scan
pscnOld = pscnTrg; } }
/******************************Public*Routine******************************\
* LONG RGNOBJ::iCombine(proSrc1, proSrc2, iMode) * * Combine the two regions by the mode and update the objects region. * Return the complexity of the resulting region. * * History: * 09-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
FCHAR gafjRgnOp[] = { 0x00, //
0x08, // RGN_AND
0x0e, // RGN_OR
0x06, // RGN_XOR
0x04, // RGN_DIFF
};
LONG RGNOBJ::iCombine(RGNOBJ& roSrc1, RGNOBJ& roSrc2, LONG iMode) { RGNLOG rl(prgn,"RGNOBJ::iCombine",(ULONG_PTR)roSrc1.prgn,(ULONG_PTR)roSrc2.prgn,iMode);
// The target region MUST NOT be either of the source regions
ASSERTGDI(prgn != roSrc1.prgn, "Trg == Src1\n"); ASSERTGDI(prgn != roSrc2.prgn, "Trg == Src2\n");
// if this is the global empty one, we don't want to mess with it.
if (prgn == prgnDefault) return(iComplexity());
// Check for the special case of merging one big single region with others.
if ((iMode == RGN_AND)||(iMode == RGN_OR)) {
if (roSrc1.bRectl()) { // if roSrc2.prgn's bounding box is smaller or equal
if (roSrc1.bContain(roSrc2)) { // The result will be identical to one region
if (!bCopy((iMode == RGN_AND) ? roSrc2 : roSrc1)) { WARNING("Unable to copy region!!"); vSet(); return(ERROR); } rl.vRet((ULONG_PTR)prgn); return(iComplexity()); } }
if (roSrc2.bRectl()) {
// if roSrc1.prgn's bounding box is smaller or equal
if (roSrc2.bContain(roSrc1)) { // The result will be identical to one region
if (!bCopy((iMode == RGN_AND) ? roSrc1 : roSrc2)) { WARNING("Unable to copy region!!"); vSet(); rl.vRet((ULONG_PTR)prgn); return(ERROR); } rl.vRet((ULONG_PTR)prgn); return(iComplexity()); } } }
// Check for the special case of ANDing two single regions together.
if ((iMode == RGN_AND) && (roSrc1.prgn->sizeRgn == SINGLE_REGION_SIZE) && (roSrc2.prgn->sizeRgn == SINGLE_REGION_SIZE)) { // Cool, all we have to do is AND the bounding boxes and we're done.
RECTL rclSrc1; RECTL rclSrc2; RECTL rclTrg;
rclSrc1 = roSrc1.prgn->rcl; rclSrc2 = roSrc2.prgn->rcl;
rclTrg.left = MAX(rclSrc1.left, rclSrc2.left); rclTrg.right = MIN(rclSrc1.right, rclSrc2.right); rclTrg.top = MAX(rclSrc1.top, rclSrc2.top); rclTrg.bottom = MIN(rclSrc1.bottom, rclSrc2.bottom);
// Was the resulting region NULL?
if ((rclTrg.left >= rclTrg.right) || (rclTrg.top >= rclTrg.bottom)) vSet(); // Make target NULL;
else vSet(&rclTrg); // Make target a rect
rl.vRet((ULONG_PTR)prgn); return(SIMPLEREGION); // Since we know what we get
}
// Do the general cases.
if (!bMerge(roSrc1, roSrc2, gafjRgnOp[iMode])) { vSet(); rl.vRet((ULONG_PTR)prgn); return(ERROR); } rl.vRet((ULONG_PTR)prgn); return(iComplexity()); }
/******************************Member*Function*****************************\
* * History: * 22-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
LONG RGNOBJAPI::iCombine( RGNOBJ& roSrc1, RGNOBJ& roSrc2, LONG iMode) { RGNLOG rl(hrgn_,prgn,"RGNOBJAPI::iCombine",(ULONG_PTR)roSrc1.prgn,(ULONG_PTR)roSrc2.prgn,iMode);
PREGION prgnOrg = prgn;
// lock the handle so no one can reference it while the pobj may be invalid
OBJLOCK ol((HOBJ) hrgn_);
LONG iRet = RGNOBJ::iCombine(roSrc1,roSrc2,iMode);
if (prgn != prgnOrg) { rl.vRet((ULONG_PTR)prgn);
PVOID pv = HmgReplace((HOBJ) hrgn_,(POBJ) prgn,0,1,OBJLOCK_TYPE); ASSERTGDI(pv != NULL,"RGNOBJAPI::iCombine - HmgReplace failed\n"); } return(iRet); }
/******************************Member*Function*****************************\
* RGNOBJ::iReduce() * * copy the roSrc into this reducing the size of the region if possible. * * History: * 13-Aug-1992 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
LONG RGNMEMOBJ::iReduce(RGNOBJ& roSrc) { RGNLOG rl(prgn,"RGNMEMOBJ::iReduce",(ULONG_PTR)roSrc.prgn);
RGNMEMOBJTMP rmoBigRect; RECTL rcl;
rcl.left = MIN_REGION_COORD; rcl.right = MAX_REGION_COORD; rcl.top = MIN_REGION_COORD; rcl.bottom = MAX_REGION_COORD;
rmoBigRect.vSet(&rcl);
// Set the bounding box to be maximally crossed (left > right, top > bottom)
prgn->rcl.left = POS_INFINITY; prgn->rcl.top = POS_INFINITY; prgn->rcl.right = NEG_INFINITY; prgn->rcl.bottom = NEG_INFINITY;
if (!bMerge(rmoBigRect, roSrc, gafjRgnOp[RGN_AND])) { vSet(); rl.vRet((ULONG_PTR)prgn); return(ERROR); }
rl.vRet((ULONG_PTR)prgn); return(iComplexity()); }
/******************************Public*Routine******************************\
* SIZE_T RGNOBJ::sizeSave() * * Compute the size of the save data for this region * * History: * 26-Oct-1991 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
ULONGSIZE_T RGNOBJ::sizeSave() { COUNT cscn = prgn->cScans; PSCAN pscn = prgn->pscnHead(); COUNT crcl = 0;
// The number of rectangles per scan is:
//
// For RECT regions -- (# of walls / 2)
// For TRAP regions -- (# of walls / 2) * (height of scan)
while (cscn--) { crcl += (pscn->cWalls / 2); pscn = pscnGet(pscn);
ASSERTGDI(pscn <= prgn->pscnTail, "sizeSave:Went past end of region\n"); }
return(crcl * sizeof(RECTL)); }
/******************************Public*Routine******************************\
* VOID RGNOBJ::vDownload(pv) * * Download the region to the buffer * * History: * 26-Oct-1991 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID RGNOBJ::vDownload(PVOID pv) { PRECTL prcl = (PRECTL) pv; PSCAN pscn = prgn->pscnHead(); COUNT cscn = prgn->cScans; RECTL rclTmp; LONG lPrevBottom = NEG_INFINITY; LONG lPrevRight;
while (cscn--) { #if DBG
if (pscn->yTop < lPrevBottom) DbgPrint("top < prev bottom, scan %ld, pscn @ 0x%lx\n", prgn->cScans - cscn, (BYTE *)pscn - (BYTE *)prgn->pscnHead());
if (pscn->yTop > pscn->yBottom) DbgPrint("top > bottom, scan %ld, pscn @ 0x%lx\n", prgn->cScans - cscn, (BYTE *)pscn - (BYTE *)prgn->pscnHead()); #endif
lPrevBottom = pscn->yBottom;
rclTmp.top = pscn->yTop; rclTmp.bottom = pscn->yBottom;
COUNT iWall = 0;
lPrevRight = NEG_INFINITY;
while (iWall < pscn->cWalls) { rclTmp.left = xGet(pscn, (PTRDIFF) iWall); rclTmp.right = xGet(pscn, (PTRDIFF) iWall + 1);
#if DBG
if ((rclTmp.left <= lPrevRight) || (rclTmp.right <= rclTmp.left)) DbgPrint("left[i] < left[i+1], pscn @ 0x%lx, iWall = 0x%lx\n", (BYTE *)pscn - (BYTE *)prgn->pscnHead(),iWall); #endif
lPrevRight = rclTmp.right;
*prcl++ = rclTmp; iWall += 2; }
#if DBG
if (pscn->cWalls != (COUNT)xGet(pscn,(PTRDIFF)iWall)) DbgPrint("cWalls != cWalls2 @ 0x%lx\n", (BYTE *)pscn - (BYTE *)prgn->pscnHead()); #endif
pscn = pscnGet(pscn);
#if DBG
if (pscn > prgn->pscnTail) { DbgPrint("vDownload1:Went past end of region\n"); return; } #endif
}
return; }
/******************************Public*Routine******************************\
* VOID RGNOBJ::vComputeUncoveredSpriteRegion(po) * * Upload the region describing the parts of the screen not covered by * sprites. Derived from 'bupload'. * * History: * 28-Nov-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID RGNOBJ::vComputeUncoveredSpriteRegion(PDEVOBJ& po) { SPRITESTATE* pState = po.pSpriteState(); PSCAN pscn = prgn->pscnHead(); LONG yTop; LONG yBottom; ULONG cScans; ULONG cWalls; RECTL rcl;
// First, reset to an empty region:
vSet();
// Find the first non-covered range:
ENUMUNCOVERED Enum(pState); if (!Enum.bEnum(&rcl)) return; // Empty region
cScans = prgn->cScans;
// Setup variables for first scan:
yTop = NEG_INFINITY; yBottom = rcl.top; cWalls = 0;
do { if (rcl.top != yTop) { cScans++;
// Close off current scan:
pscn->yTop = yTop; pscn->yBottom = yBottom; pscn->cWalls = cWalls; pscn->ai_x[cWalls].x = cWalls;
// Add a null scan if the top of the new rectangle and
// the bottom of the old don't overlap:
if (rcl.top != yBottom) { cScans++; pscn = pscnGet(pscn); pscn->yTop = yBottom; pscn->yBottom = rcl.top; pscn->cWalls = 0; pscn->ai_x[0].x = 0; }
// Advance to the next scan:
pscn = pscnGet(pscn);
// Open up current scan:
yTop = rcl.top; yBottom = rcl.bottom; cWalls = 0; }
pscn->ai_x[cWalls].x = rcl.left; pscn->ai_x[cWalls + 1].x = rcl.right; cWalls += 2;
} while (Enum.bEnum(&rcl));
// Close off current scan:
pscn->yTop = yTop; pscn->yBottom = yBottom; pscn->cWalls = cWalls; pscn->ai_x[cWalls].x = cWalls;
// Build final scan:
cScans++; pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = yBottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0;
prgn->pscnTail = pscnGet(pscn); prgn->cScans = cScans; prgn->sizeRgn = NULL_REGION_SIZE - NULL_SCAN_SIZE; prgn->sizeRgn += (ULONGSIZE_T) (((BYTE *) prgn->pscnTail - (BYTE *) prgn->pscnHead())); }
/******************************Public*Routine******************************\
* * History: * 24-Sep-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
RGNMEMOBJ::RGNMEMOBJ() { vInitialize(QUANTUM_REGION_SIZE); }
/******************************Public*Routine******************************\
* RGNMEMOBJ::RGNMEMOBJ(size) * * Create a new region object * * History: * 02-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
RGNMEMOBJ::RGNMEMOBJ(ULONGSIZE_T size) { vInitialize(size); }
/******************************Public*Routine******************************\
* RGNMEMOBJ::vInitialize(size) * * Create a new region object * * History: * 02-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID RGNMEMOBJ::vInitialize(ULONGSIZE_T size) { RGNLOG rl((PREGION)0,"RGNMEMOBJ::vInitialize(sz)",(ULONG_PTR)size);
// don't bother with anything smaller than a QUANTUM_REGION_SIZE
if (size < QUANTUM_REGION_SIZE) size = QUANTUM_REGION_SIZE;
// Got to allocate a new one.
prgn = (PREGION)ALLOCOBJ(size,RGN_TYPE,FALSE);
rl.vRet((ULONG_PTR)prgn);
if (prgn) { vInit(size); } }
/******************************Public*Routine******************************\
* RGNMEMOBJ::RGNMEMOBJ(bInit) * * Create a new region object * * History: * 02-Jul-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
RGNMEMOBJ::RGNMEMOBJ(BOOL bInit) { RGNLOG rl((PREGION)0,"RGNMEMOBJ::RGNMEMOBJ(b)",(ULONG_PTR)bInit);
ASSERTGDI(bInit == FALSE,"RGNMEMOBJ::RGNMEMOBJ - bInit == TRUE\n");
prgn = (PREGION)ALLOCOBJ(QUANTUM_REGION_SIZE,RGN_TYPE,FALSE);
rl.vRet((ULONG_PTR)prgn);
if (prgn) { prgn->sizeObj = QUANTUM_REGION_SIZE; prgn->sizeRgn = 0; prgn->cRefs = 0; prgn->iUnique = 0; } }
/******************************Public*Function*****************************\
* AddEdgeToGET * * Adds the edge described by the two passed-in points to the Global Edge * Table, if the edge spans at least one pixel vertically. * * History: * 09-Sep-1993 -by- Wendy Wu [wendywu] * Stolen from DrvFillPath. \**************************************************************************/
PEDGE AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge, POINTFIX *ppfxEdgeStart, POINTFIX *ppfxEdgeEnd, RECTL *pBound) { LONG lyStart, lyEnd, lxStart, lxEnd, lyHeight, lxWidth,lyStartSave; BOOL bTopClip;
// Set the winding-rule direction of the edge, and put the endpoints in
// top-to-bottom order
lyHeight = ppfxEdgeEnd->y - ppfxEdgeStart->y; if (lyHeight >= 0) { lxStart = ppfxEdgeStart->x; lyStart = ppfxEdgeStart->y; lxEnd = ppfxEdgeEnd->x; lyEnd = ppfxEdgeEnd->y; pFreeEdge->lWindingDirection = 1; } else { lyHeight = -lyHeight; lxEnd = ppfxEdgeStart->x; lyEnd = ppfxEdgeStart->y; lxStart = ppfxEdgeEnd->x; lyStart = ppfxEdgeEnd->y; pFreeEdge->lWindingDirection = -1; }
bTopClip = FALSE;
if( pBound != NULL ) { if( ( lyEnd < pBound->top ) || ( lyStart > pBound->bottom ) ) { // completely above or below the bound rectangle so skip this segment
return(pFreeEdge); }
if( lyStart < pBound->top ) { // starts above the rect so clip to the top of the rect
bTopClip = TRUE; lyStartSave = lyStart; lyStart = pBound->top; }
if( lyEnd > pBound->bottom ) { // ends below the rect so clip to the bottom of the rect
lyEnd = pBound->bottom; }
}
// First pixel scan line (non-fractional GIQ Y coordinate) edge intersects.
// Dividing by 16 with a shift is okay because Y is always positive
pFreeEdge->Y = (lyStart + 15) >> 4;
// Calculate the number of pixels spanned by this edge
pFreeEdge->lScansLeft = ((lyEnd + 15) >> 4) - pFreeEdge->Y;
if (pFreeEdge->lScansLeft <= 0) return(pFreeEdge); // no pixels at all are spanned, so we can ignore
// this edge
// Set the error term and adjustment factors, all in GIQ coordinates for new.
lxWidth = lxEnd - lxStart; if (lxWidth >= 0) { // Left to right, so we change X as soon as we move at all.
pFreeEdge->lXDirection = 1; pFreeEdge->lErrorTerm = -1; } else { // Right to left, so we don't change X until we've moved a full GIQ
// coordinate.
lxWidth = -lxWidth; pFreeEdge->lXDirection = -1; pFreeEdge->lErrorTerm = -lyHeight; }
if (lxWidth >= lyHeight) { // Calculate base run length (minimum distance advanced in X for a 1-
// scan advance in Y)
pFreeEdge->lXWhole = lxWidth / lyHeight;
// Add sign back into base run length if going right to left
if (pFreeEdge->lXDirection == -1) pFreeEdge->lXWhole = -pFreeEdge->lXWhole;
pFreeEdge->lErrorAdjustUp = lxWidth % lyHeight; } else { // Base run length is 0, because line is closer to vertical than
// horizontal.
pFreeEdge->lXWhole = 0; pFreeEdge->lErrorAdjustUp = lxWidth; } pFreeEdge->lErrorAdjustDown = lyHeight;
// If the edge doesn't start on a pixel scan (that is, it starts at a
// fractional GIQ coordinate), advance it to the first pixel scan it
// intersects or to the top of the clip rectangle if we are top clipped
LONG lyAdjust;
if( bTopClip ) { lyAdjust = pBound->top; lyStart = lyStartSave; } else { lyAdjust = ( lyStart + 15 ) & ~15; }
while( lyStart != lyAdjust ) { // Starts at a fractional GIQ coordinate, not exactly on a pixel scan
// Advance the edge's GIQ X coordinate for a 1-GIQ-pixel Y advance
// Advance by the minimum amount
lxStart += pFreeEdge->lXWhole;
// Advance the error term and see if we got one extra pixel this time.
pFreeEdge->lErrorTerm += pFreeEdge->lErrorAdjustUp; if (pFreeEdge->lErrorTerm >= 0) { // The error term turned over, so adjust the error term and
// advance the extra pixel.
pFreeEdge->lErrorTerm -= pFreeEdge->lErrorAdjustDown; lxStart += pFreeEdge->lXDirection; } lyStart++; // advance to the next GIQ Y coordinate
}
// Turn the calculations into pixel rather than GIQ calculations
// Move the X coordinate to the nearest pixel, and adjust the error term
// accordingly
// Dividing by 16 with a shift is okay because X is always positive
pFreeEdge->X = (lxStart + 15) >> 4; // convert from GIQ to pixel coordinates
if (pFreeEdge->lXDirection == 1) { // Left to right
pFreeEdge->lErrorTerm -= pFreeEdge->lErrorAdjustDown * (((lxStart + 15) & ~0x0F) - lxStart); } else { // Right to left
pFreeEdge->lErrorTerm -= pFreeEdge->lErrorAdjustDown * ((lxStart - 1) & 0x0F); }
// Scale the error adjusts up by 16 times, to move 16 GIQ pixels at a time.
// Shifts work to do the multiplying because these values are always
// non-negative
pFreeEdge->lErrorAdjustUp <<= 4; pFreeEdge->lErrorAdjustDown <<= 4;
// Insert the edge into the GET in YX-sorted order. The search always ends
// because the GET has a sentinel with a greater-than-possible Y value
while ((pFreeEdge->Y > ((EDGE *)pGETHead->pNext)->Y) || ((pFreeEdge->Y == ((EDGE *)pGETHead->pNext)->Y) && (pFreeEdge->X > ((EDGE *)pGETHead->pNext)->X))) { pGETHead = pGETHead->pNext; }
pFreeEdge->pNext = pGETHead->pNext; // link the edge into the GET
pGETHead->pNext = pFreeEdge;
return(++pFreeEdge); // point to the next edge storage location for next
// time
}
/******************************Public*Function*****************************\
* vConstructGET * * Build the Global Edge Table from the path. The GET is constructed in * Y-X order, and has a head/tail/sentinel node at pGETHead. * * History: * 09-Sep-1993 -by- Wendy Wu [wendywu] * Stolen from DrvFillPath. \**************************************************************************/
VOID vConstructGET(EPATHOBJ& po, EDGE *pGETHead, EDGE *pFreeEdges,RECTL *pBound) { // Create an empty GET with the head node also a tail sentinel
pGETHead->pNext = pGETHead; // mark that the GET is empty
pGETHead->Y = 0x7FFFFFFF; // this is greater than any valid Y value, so
// searches will always terminate
PPATH ppath = po.ppath;
PPOINTFIX pptfxStart, pptfxEnd, pptfx; PPOINTFIX pptfxPrev = NULL;
for (PATHRECORD *ppr = ppath->pprfirst; ppr != (PPATHREC) NULL; ppr = ppr->pprnext) { // If first point starts a subpath, remember it as such
// and go on to the next point, so we can get an edge.
pptfx = ppr->aptfx;
if (ppr->flags & PD_BEGINSUBPATH) { pptfxStart = ppr->aptfx; // the subpath starts here
pptfxPrev = ppr->aptfx; // this points starts next edge
pptfx++; // advance to the next point
}
// Add edges in PATH to GET, in Y-X sorted order.
pptfxEnd = ppr->aptfx + ppr->count;
while (pptfx < pptfxEnd) { ASSERTGDI(pptfxPrev != NULL, "No path record with PD_BEGINSUBPATH");
pFreeEdges = AddEdgeToGET(pGETHead, pFreeEdges,pptfxPrev,pptfx,pBound); pptfxPrev = pptfx; pptfx++; // advance to the next point
}
// If last point ends the subpath, insert the edge that
// connects to first point.
if (ppr->flags & PD_ENDSUBPATH) { pFreeEdges = AddEdgeToGET(pGETHead, pFreeEdges,pptfxPrev, pptfxStart,pBound);
pptfxPrev = NULL; } } }
/******************************Public*Function*****************************\
* vAdvanceAETEdges * * Advance the edges in the AET to the next scan, dropping any for which we've * done all scans. Assumes there is at least one edge in the AET. * * History: * 09-Sep-1993 -by- Wendy Wu [wendywu] * Stolen from DrvFillPath. \**************************************************************************/
VOID vAdvanceAETEdges(EDGE *pAETHead) { EDGE *pLastEdge, *pCurrentEdge; COUNT c = pAETHead->Y; // Y is used as edge count in AET
pLastEdge = pAETHead; pCurrentEdge = pLastEdge->pNext; do { // Count down this edge's remaining scans
if (--pCurrentEdge->lScansLeft == 0) { // We've done all scans for this edge; drop this edge from the AET
pLastEdge->pNext = pCurrentEdge->pNext; c--; } else { // Advance the edge's X coordinate for a 1-scan Y advance
// Advance by the minimum amount
pCurrentEdge->X += pCurrentEdge->lXWhole;
// Advance the error term and see if we got one extra pixel this time.
pCurrentEdge->lErrorTerm += pCurrentEdge->lErrorAdjustUp; if (pCurrentEdge->lErrorTerm >= 0) { // The error term turned over, so adjust the error term and
// advance the extra pixel.
pCurrentEdge->lErrorTerm -= pCurrentEdge->lErrorAdjustDown; pCurrentEdge->X += pCurrentEdge->lXDirection; }
pLastEdge = pCurrentEdge; } } while ((pCurrentEdge = pLastEdge->pNext) != pAETHead); pAETHead->Y = c; // Y is used as edge count in AET
}
/******************************Public*Function*****************************\
* vXSortAETEdges * * X-sort the AET, because the edges may have moved around relative to * one another when we advanced them. We'll use a multipass bubble * sort, which is actually okay for this application because edges * rarely move relative to one another, so we usually do just one pass. * Also, this makes it easy to keep just a singly-linked list. Assumes there * are at least two edges in the AET. * * History: * 09-Sep-1993 -by- Wendy Wu [wendywu] * Stolen from DrvFillPath. \**************************************************************************/
VOID vXSortAETEdges(EDGE *pAETHead) { BOOL bEdgesSwapped; EDGE *pLastEdge, *pCurrentEdge, *pNextEdge;
do { bEdgesSwapped = FALSE; pLastEdge = pAETHead; pCurrentEdge = pLastEdge->pNext; pNextEdge = pCurrentEdge->pNext;
do { if (pNextEdge->X < pCurrentEdge->X) { // Next edge is to the left of the current edge; swap them.
pLastEdge->pNext = pNextEdge; pCurrentEdge->pNext = pNextEdge->pNext; pNextEdge->pNext = pCurrentEdge; bEdgesSwapped = TRUE; pCurrentEdge = pNextEdge; // continue sorting before the edge
// we just swapped; it might move
// farther yet
} pLastEdge = pCurrentEdge; pCurrentEdge = pLastEdge->pNext; } while ((pNextEdge = pCurrentEdge->pNext) != pAETHead); } while (bEdgesSwapped); }
/******************************Public*Function*****************************\
* vMoveNewEdges * * Moves all edges that start on the current scan from the GET to the AET in * X-sorted order. Parameters are pointer to head of GET and pointer to dummy * edge at head of AET, plus current scan line. Assumes there's at least one * edge to be moved. * * History: * 09-Sep-1993 -by- Wendy Wu [wendywu] * Stolen from DrvFillPath. \**************************************************************************/
VOID vMoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, LONG lyCurrent) { EDGE *pCurrentEdge = pAETHead; EDGE *pGETNext = pGETHead->pNext; COUNT c = pAETHead->Y; // Y is used as edge count in AET
do { // Scan through the AET until the X-sorted insertion point for this
// edge is found. We can continue from where the last search left
// off because the edges in the GET are in X sorted order, as is
// the AET. The search always terminates because the AET sentinel
// is greater than any valid X
while (pGETNext->X > ((EDGE *)pCurrentEdge->pNext)->X) pCurrentEdge = pCurrentEdge->pNext;
// We've found the insertion point; add the GET edge to the AET, and
// remove it from the GET.
pGETHead->pNext = pGETNext->pNext; pGETNext->pNext = pCurrentEdge->pNext; pCurrentEdge->pNext = pGETNext; pCurrentEdge = pGETNext; // continue insertion search for the next
// GET edge after the edge we just added
pGETNext = pGETHead->pNext; c++;
} while (pGETNext->Y == lyCurrent); pAETHead->Y = c; // Y is used as edge count in AET
}
/******************************Member*Function*****************************\
* BOOL RGNOBJ::bAddScans * * Add a new scan into the region. * * History: * 16-Sep-1993 -by- Wendy Wu [wendywu] * Wrote it. \**************************************************************************/
BOOL RGNMEMOBJ::bAddScans(LONG yTop, PEDGE pAETHead, FLONG flOptions) { ULONGSIZE_T cWalls = (ULONGSIZE_T)pAETHead->Y; // Y is used as edge count in AET
ULONGSIZE_T size = cWalls * sizeof(INDEX_LONG) + NULL_SCAN_SIZE;
// Check for nearly full region
if (size > prgn->sizeObj - prgn->sizeRgn) { // There isn't enough space for this scan, lets try to realloc this region.
// We want to expand to a big enough size to avoid reallocation in the near
// future.
if (!bExpand(prgn->sizeObj + size + FILL_REGION_SIZE)) return(FALSE); }
PSCAN pscn = (SCAN *) prgn->pscnTail; // points pass last scan
ASSERTGDI(yTop == pscnGot(pscn)->yBottom, "bAddNullScan: bad yTop\n");
PEDGE pCurrentEdge = pAETHead->pNext; // point to the first edge
// yBottom is the dword before ai_x[0] in the SCAN structure.
// Stuff NEG_INFINITY there so the first wall is bigger than
// the "previous" wall. yBottom will be initialized later on.
PLONG pWallStart = (LONG *)&pscn->yBottom; PLONG pWall = pWallStart; *pWall = NEG_INFINITY;
if (flOptions & WINDING) { // Do winding fill; scan across until we've found equal numbers
// of up and down edges.
while (pCurrentEdge != pAETHead) { if (*pWall < pCurrentEdge->X) { pWall++; *pWall = pCurrentEdge->X; } else pWall--;
LONG lWindingCount = pCurrentEdge->lWindingDirection; do { pCurrentEdge = pCurrentEdge->pNext; lWindingCount += pCurrentEdge->lWindingDirection; } while (lWindingCount != 0);
if (*pWall < pCurrentEdge->X) { pWall++; *pWall = pCurrentEdge->X; } else pWall--;
pCurrentEdge = pCurrentEdge->pNext; } } else { while (pCurrentEdge != pAETHead) { if (*pWall < pCurrentEdge->X) { pWall++; *pWall = pCurrentEdge->X; } else pWall--;
pCurrentEdge = pCurrentEdge->pNext; } }
ASSERT4GB ((LONGLONG)(((BYTE *)pWall - (BYTE *)pWallStart) / sizeof(LONG))); cWalls = (ULONGSIZE_T)(((BYTE *)pWall - (BYTE *)pWallStart) / sizeof(LONG));
PSCAN pscnPrev = pscnGot(pscn);
if ((pscnPrev->cWalls == cWalls) && !memcmp(pscnPrev->ai_x, pscn->ai_x,cWalls * sizeof(LONG))) { pscnPrev->yBottom = yTop+1; } else { prgn->cScans += 1; prgn->sizeRgn += (ULONGSIZE_T)(NULL_SCAN_SIZE + sizeof(INDEX_LONG) * cWalls); ASSERTGDI(prgn->sizeRgn <= prgn->sizeObj, "bAddScans: sizeRgn > sizeObj\n"); ASSERTGDI((cWalls & 1) == 0,"bAddScan error: cWalls odd number\n");
pscn->yTop = yTop; pscn->yBottom = yTop+1;
pscn->cWalls = cWalls; pscn->ai_x[cWalls].x = cWalls; // This sets cWalls2
prgn->pscnTail = pscnGet(pscn); }
return(TRUE); }
/******************************Member*Function*****************************\
* BOOL RGNOBJ::bAddNullScan * * Add a null scan into the region. * * History: * 16-Sep-1993 -by- Wendy Wu [wendywu] * Wrote it. \**************************************************************************/
BOOL RGNMEMOBJ::bAddNullScan(LONG yTop, LONG yBottom) { // Check for nearly full region
if (NULL_SCAN_SIZE > prgn->sizeObj - prgn->sizeRgn) { // There isn't enough space for this scan, lets try to realloc this region.
// We want to expand to a big enough size to avoid reallocation in the near
// future.
if (!bExpand(prgn->sizeObj + NULL_SCAN_SIZE + FILL_REGION_SIZE)) return(FALSE); }
PSCAN pscn = (SCAN *) prgn->pscnTail; // points pass last scan
ASSERTGDI(prgn->cScans == 0 || yTop == pscnGot(pscn)->yBottom, "bAddNullScan: bad yTop\n");
prgn->cScans += 1;
pscn->yTop = yTop; pscn->yBottom = yBottom;
prgn->sizeRgn += (ULONGSIZE_T)NULL_SCAN_SIZE; ASSERTGDI(prgn->sizeRgn <= prgn->sizeObj, "bAddNullScan: sizeRgn > sizeObj\n");
pscn->cWalls = pscn->ai_x[0].x = 0; prgn->pscnTail = pscnGet(pscn);
return(TRUE); }
/******************************Member*Function*****************************\
* VOID RGNMEMOBJ::vCreate(po, fl) * * Routine for constructing a region from a path. * * History: * 16-Sep-1993 -by- Wendy Wu [wendywu] * Removed trapazoidal regions. * 04-Apr-1991 -by- Wendy Wu [wendywu] * Wrote it. \**************************************************************************/
VOID RGNMEMOBJ::vCreate( EPATHOBJ& po, FLONG flOptions, // ALTERNATE or WINDING
RECTL *pBound ) { GDIFunctionID(RGNMEMOBJ::vCreate);
RGNLOG rl((PREGION)NULL,"RGNMEMOBJ::vCreate"); rl.vRet(0);
if (!po.bValid()) { RIP("Invalid EPATHOBJ"); } else {
ULONG count; 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];
prgn = (REGION *) NULL; // Assume failure.
if (po.bBeziers() && !po.bFlatten()) return;
// The given path should be closed when create region from path
// then make sure it here.
po.vCloseAllFigures();
if ((count = po.cCurves) < 2) return;
// Is path contained in a bounding rectangle?
//
// If there is no bound (pBound = NULL) or
// the bound won't shrink the current BoundBox
// then try bFastFillWrapper.
//
// Many calling sites only initialize the top and bottom of the
// pBound rectange because the underlying clipping code only
// uses the top and bottom.
// Also, they always initialize them in FIX coordinate.
if ( (pBound==NULL) || ( ( pBound->top < po.rcfxBoundBox().yTop ) && ( pBound->bottom > po.rcfxBoundBox().yBottom) ) ) { if (bFastFillWrapper(po)) { vTighten(); rl.vRet((ULONG_PTR)prgn); return; } }
// Allocate memory for edge storage.
BOOL bAlloc; EDGE *pFreeEdges; // pointer to memory free for use to store edges
if (count < MAX_POINTS) { pFreeEdges = &aEdge[0]; bAlloc = FALSE; } else { pFreeEdges = (PEDGE)PALLOCNOZ(sizeof(EDGE) * (count + 1), 'ngrG'); if (pFreeEdges == (PEDGE)NULL) return; bAlloc = TRUE; }
// This is a size of random guess...
// Given a path of n points, assume there are n scans with 4 walls on each
// scan. The region will be expanded later if this size is too small.
ULONG size; LONGLONG llSize; FIX top = po.rcfxBoundBox().yTop; FIX bottom = po.rcfxBoundBox().yBottom;
if (bottom < top) { // WINBUG #178736 claudebe 9-7-2000 we should check for math overflow where the path is created/offset/scaled see stress #178262
// when this is done, we can chage this test back into an assert
WARNING("PATHOBJ BoundBox is invalid.\n");
if (bAlloc) VFREEMEM(pFreeEdges);
return; }
// Make sure we account for the clipping in our estimate.
// Without this code, we could overestimate by many orders
// of magnitude.
if (pBound) { top = MAX(top, pBound->top); bottom = MIN(bottom, pBound->bottom); }
// The smallest region we should have from bounding is zero.
// Note this also takes care of the conversion issue of using
// FXTOL and assigning the result to an unsigned long.
// using LONGLONG because bottom-top may overflow a ULONG
llSize = MAX((LONGLONG)bottom-(LONGLONG)top, 0);
// we know that after >> 4, the result will fit in a ULONG
size = (ULONG)(FXTOL(llSize)) + 10;
if (size < 0x00ffffff) { size = QUANTUM_REGION_SIZE + (sizeof(INDEX_LONG) * 4 + NULL_SCAN_SIZE) * size;
prgn = (PREGION)ALLOCOBJ(size,RGN_TYPE,FALSE); }
if (!prgn) { if (bAlloc) VFREEMEM(pFreeEdges);
return; }
prgn->sizeObj = size; prgn->sizeRgn = offsetof(REGION,scan); prgn->cRefs = 0; prgn->iUnique = 0; prgn->cScans = 0; // start from scratch, assume no scans
prgn->pscnTail = prgn->pscnHead();
// Construct the global edge list.
pGETHead = &GETHead; vConstructGET(po, pGETHead, pFreeEdges,pBound); // bad line coordinates or
BOOL bSucceed = TRUE; 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 (bSucceed) { // 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.
LONG yTopOld = yTop; yTop = ((EDGE *)GETHead.pNext)->Y; if (yTop != yTopOld) { // Fill in NULL scan between rectangles.
if (!(bSucceed = bAddNullScan(yTopOld, yTop))) break; } } 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)
bSucceed = bAddScans(yTop, pAETHead, flOptions); yTop++; // next scan
}
if (!bSucceed || !bAddNullScan(yTop, POS_INFINITY)) { bDeleteRGNOBJ(); bSucceed = FALSE; } else { vTighten(); }
if (bAlloc) VFREEMEM(pFreeEdges);
rl.vRet((ULONG_PTR)prgn);
} }
/******************************Member*Function*****************************\
* RGNMEMOBJ::bFastFillWrapper * * create a rgn from a convex polygon. * * History: * 27-Sep-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
#define QUICKPOINTS 40
BOOL RGNMEMOBJ::bFastFillWrapper( EPATHOBJ& epo) { PATHDATA pd; BOOL bRes = FALSE;
ASSERTGDI(!(epo.fl & PO_BEZIERS),"RGNMEMOBJ::bFastFill - bez\n"); ASSERTGDI(epo.cCurves == epo.cTotalPts(),"RGNMEMOBJ::cCurves != cTotalPts\n");
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) goto DoStart;
RtlCopyMemory(aptfx+cPoints,pd.pptfx,(SIZE_T)pd.count*sizeof(POINTFIX)); cPoints += pd.count;
} while(bMore);
// Should never hit this assert. if hit, the stack is already broken...
ASSERTGDI(cPoints <= QUICKPOINTS,"RGNMEMOBJ::bFastFillWrapper - too many points\n");
bRes = bFastFill(epo,cPoints,aptfx); } } else if (pd.count > 1) { bRes = bFastFill(epo,pd.count,pd.pptfx); } else { bRes = TRUE; }
DoStart: epo.vEnumStart();
return(bRes); }
/******************************Member*Function*****************************\
* RGNMEMOBJ::bFastFill * * create a rgn from a convex polygon. * this routine is very similar to bFastFill in fastfill.cxx * * History: * 14-Oct-1993 -by- Eric Kutter [erick] * initial code stolen from s3 driver. \**************************************************************************/
BOOL RGNMEMOBJ::bFastFill( EPATHOBJ& po, LONG cEdges, // Includes close figure edge
POINTFIX* pptfxFirst) { 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 iEdge; 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:
yCurrent = (pptfxTop->y + 15) >> 4;
// 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 region
ULONGSIZE_T size = (ULONGSIZE_T)(NULL_REGION_SIZE + NULL_SCAN_SIZE + (sizeof(INDEX_LONG) * 2 + NULL_SCAN_SIZE) * FXTOL(po.rcfxBoundBox().yBottom - po.rcfxBoundBox().yTop + 15));
prgn = (PREGION)ALLOCOBJ(size,RGN_TYPE,FALSE);
if (!prgn) return(FALSE);
prgn->sizeObj = size; prgn->sizeRgn = offsetof(REGION,scan); prgn->cRefs = 0; prgn->cScans = 0; // start from scratch, assume no scans
prgn->pscnTail = (PSCAN)((PBYTE)prgn + size);
// setup the scans
PSCAN pscnPrev= prgn->pscnHead(); ULONG cScans = 1;
pscnPrev->yTop = NEG_INFINITY; pscnPrev->yBottom = yCurrent; pscnPrev->cWalls = 0; pscnPrev->ai_x[0].x = 0;
PSCAN pscn = pscnGet(pscnPrev);
NextEdge:
ASSERTGDI(pscn < prgn->pscnTail,"bFastFill - pscn past end\n");
// We loop through this routine on a per-trapezoid basis.
for (iEdge = 1; iEdge >= 0; iEdge--) { ped = &aed[iEdge]; if (ped->cy == 0) { // Need a new DDA:
do { cEdges--; if (cEdges < 0) { // the only way out
if (pscnPrev->cWalls == 0) { pscnPrev->yBottom = POS_INFINITY; } else { pscn->cWalls = 0; pscn->ai_x[0].x = 0; pscn->yTop = yCurrent; pscn->yBottom = POS_INFINITY; ++cScans; pscn = pscnGet(pscn); }
ASSERTGDI(pscn <= prgn->pscnTail,"bFastFill - pscn past end2\n");
prgn->cScans = cScans; prgn->pscnTail = pscn;
ASSERT4GB((ULONGLONG)((PBYTE)pscn - (PBYTE)prgn)); prgn->sizeRgn = (ULONG)((PBYTE)pscn - (PBYTE)prgn);
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; } }
cyTrapezoid = min(aed[LEFT].cy, aed[RIGHT].cy); // # of scans in this trap
aed[LEFT].cy -= cyTrapezoid; aed[RIGHT].cy -= cyTrapezoid;
// 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)) { LONG xL = aed[LEFT].x; LONG xR = aed[RIGHT].x;
if (xL != xR) { if (xL > xR) { LONG l = xL; xL = xR; xR = l; }
// non NULL case
if ((pscnPrev->cWalls == 2) && (pscnPrev->ai_x[0].x == xL) && (pscnPrev->ai_x[1].x == xR)) { pscnPrev->yBottom = yCurrent+cyTrapezoid; } else { pscn->cWalls = 2; pscn->ai_x[0].x = xL; pscn->ai_x[1].x = xR; pscn->ai_x[2].x = 2; pscn->yTop = yCurrent; pscn->yBottom = yCurrent+cyTrapezoid; pscnPrev = pscn; pscn = pscnGet(pscn); ++cScans; } } else { // NULL scan case
if (pscnPrev->cWalls == 0) { pscnPrev->yBottom = yCurrent+cyTrapezoid; } else { pscn->cWalls = 0; pscn->ai_x[0].x = 0; pscn->yTop = yCurrent; pscn->yBottom = yCurrent+cyTrapezoid; pscnPrev = pscn; pscn = pscnGet(pscn); ++cScans; } }
yCurrent += cyTrapezoid;
goto NextEdge; }
while (TRUE) { LONG lWidth = aed[RIGHT].x - aed[LEFT].x;
if (lWidth > 0) { if ((pscnPrev->cWalls == 2) && (pscnPrev->ai_x[0].x == aed[LEFT].x) && (pscnPrev->ai_x[1].x == aed[RIGHT].x)) { // the scans coalesce, just need to change the bottom
ContinueAfterZeroDup:
pscnPrev->yBottom = ++yCurrent; } else { // need to setup the entire scan
pscn->cWalls = 2; pscn->ai_x[0].x = aed[LEFT].x; pscn->ai_x[1].x = aed[RIGHT].x; pscn->ai_x[2].x = 2;
ContinueAfterZero:
pscn->yTop = yCurrent; pscn->yBottom = ++yCurrent; pscnPrev = pscn; pscn = pscnGet(pscn); // (PBYTE)pscn + NULL_SCAN_SIZE + 2 * sizeof(LONG);
++cScans; }
// 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--;
if (cyTrapezoid == 0) goto NextEdge; } else if (lWidth == 0) { // NULL scan, do null scan specific stuff
if (pscnPrev->cWalls == 0) { goto ContinueAfterZeroDup; } else { pscn->cWalls = 0; pscn->ai_x[0].x = 0;
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; } } }
#if DBG
/******************************Member*Function*****************************\
* VOID RGNOBJ::vPrintScans() * * DbgPrint the walls of every scan of the given region. This is * for debugging only. * * History: * 08-Mar-1991 -by- Wendy Wu [wendywu] * Wrote it. \**************************************************************************/
VOID RGNOBJ::vPrintScans() { COUNT cScans = prgn->cScans; PSCAN pscn = prgn->pscnHead();
for (COUNT i = 0; i < cScans; i++) { COUNT cWalls = pscn->cWalls; DbgPrint("Scan %ld: yTop = %ld, yBottom = %ld, cWalls = %ld\n", i, pscn->yTop, pscn->yBottom, cWalls);
for (COUNT j = 0; j < cWalls; j+=2) { DbgPrint(" Left edge: index = %ld\n", pscn->ai_x[j].x); DbgPrint(" Right edge: index = %ld\n", pscn->ai_x[j+1].x); } pscn = pscnGet(pscn); } } #endif
/******************************Public*Routine******************************\
* VOID vTighten() * * Tighten the bounding rectangle * * History: * 21-May-1991 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID RGNOBJ::vTighten() { // If this is a NULL region, zero the bounding box and get out of here.
if (prgn->cScans == 1) { prgn->rcl.left = 0; prgn->rcl.bottom = 0; prgn->rcl.right = 0; prgn->rcl.top = 0; return; }
// Start with a maximally crossed rectangle
ERECTL ercl(POS_INFINITY, POS_INFINITY, NEG_INFINITY, NEG_INFINITY); COUNT cScans = prgn->cScans; PSCAN pscn = pscnGot(prgn->pscnTail);
ercl.bottom = pscn->yTop; // The top of the empty bottom scan
pscn = prgn->pscnHead(); ercl.top = pscn->yBottom; // The bottom of the empty top scan
COUNT cWall;
while (cScans--) { // Are there any walls?
if ((cWall = pscn->cWalls) != 0) { if (ercl.left > pscn->ai_x[0].x) ercl.left = pscn->ai_x[0].x; if (ercl.right < pscn->ai_x[cWall - 1].x) ercl.right = pscn->ai_x[cWall - 1].x; }
pscn = pscnGet(pscn);
ASSERTGDI(pscn <= prgn->pscnTail, "vTighten2:Went past end of region\n"); }
if (ercl.left >= ercl.right) { ercl.left = 0; ercl.right = 0; }
prgn->rcl = *((RECTL *) &ercl); }
/******************************Public*Routine******************************\
* BOOL vFramed(pscn, iWall) * * Marks the given wall as having been added to the frame path. * * History: * 30-Apr-1992 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
#define REGION_FRAMED_OFFSET 0x10000000L
#define DIR_UP 0x00000001L
#define DIR_DOWN 0x00000000L
inline VOID vFramed(SCAN* pscn, LONG iWall, RGNOBJ* pro) { #if DBG
if (!VALID_SCR(pscn->ai_x[iWall].x)) { pro->bValidateFramedRegion(); RIP("Wall revisited"); } #endif
DONTUSE(pro); pscn->ai_x[iWall].x += REGION_FRAMED_OFFSET; }
/******************************Public*Routine******************************\
* BOOL bFramed(pscn, iWall) * * Returns TRUE if the wall has already been added to the frame's path. * * History: * 30-Apr-1992 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
inline BOOL bFramed(SCAN* pscn, LONG iWall) { return(pscn->ai_x[iWall].x > MAX_REGION_COORD); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::xMyGet(pscn, iWall, iDir) * * Retrieves the x-value of the wall of the given scan, iDir indicating * if it should be the top or bottom. * * History: * 31-Apr-1992 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
inline LONG RGNOBJ::xMyGet(SCAN* pscn, LONG iWall, LONG iDir) { DONTUSE(iDir);
INDEX_LONG il = pscn->ai_x[iWall];
if (il.x > MAX_REGION_COORD) il.x -= REGION_FRAMED_OFFSET;
#if DBG
if (!VALID_SCR(il.x)) { bValidateFramedRegion(); RIP("Wall Revisited"); } #endif
return(il.x); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bOutline(epo, pexo) * * Create a path from the outline of a region. * * WARNING: This call destroys the data in the region! * * Note that this code makes the implicit assumption that the x-value of * adjacent walls is not the same (i.e., no empty rectangles). * * History: * 30-Apr-1992 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bOutline( EPATHOBJ& epo, EXFORMOBJ *pexo) { RGNLOG rl(prgn,"RGNOBJ::bOutline");
POINTL aptl[2]; COUNT cScans; SCAN* pscnCurrent; COUNT iWallCurrent; COUNT cWallsCurrent;
// Now compute the outline:
pscnCurrent = prgn->pscnHead(); cScans = prgn->cScans;
while (cScans--) { cWallsCurrent = pscnCurrent->cWalls; for (iWallCurrent = 0; iWallCurrent != cWallsCurrent; iWallCurrent++) { // Only start at unvisited walls:
if (!bFramed(pscnCurrent, iWallCurrent)) { SCAN* pscn = pscnCurrent; COUNT iWall = iWallCurrent; LONG iTurn;
aptl[0].x = xMyGet(pscn, iWallCurrent, DIR_UP); aptl[0].y = pscn->yTop;
if (!epo.bMoveTo(pexo, aptl)) return(FALSE);
SCAN* pscnSearch = pscnGet(pscn); BOOL bInside = (BOOL) (iWallCurrent & 1);
// Mark that we've visited this wall:
vFramed(pscn, iWall, this);
// Loop until the path closes on itself:
GoDown: iTurn = +1; while (pscnSearch->cWalls != 0) { LONG xWall = xMyGet(pscn, iWall, DIR_DOWN); LONG iNewWall; LONG xNewWall;
COUNT iLeft = bInside; COUNT iRight = pscnSearch->cWalls - 1 - bInside;
// It would be nice if the first wall in the region structure
// was minus infinity, but it isn't, so we do this check:
if (xMyGet(pscnSearch, iLeft, DIR_UP) > xWall) iNewWall = iLeft; else { // Check if it's possible to find a wall with the
// minimum x-value > xWall:
if (xMyGet(pscnSearch, iRight, DIR_UP) <= xWall) break; // =====>
// Do a binary search to find it:
while (TRUE) { COUNT iSearch = (iLeft + iRight) >> 1; if (iSearch == iLeft) break; // =====>
LONG xSearch = xMyGet(pscnSearch, iSearch, DIR_UP);
if (xSearch > xWall) iRight = iSearch; else iLeft = iSearch; } iNewWall = iRight; }
if ((iNewWall & 1) != bInside) { // There is a region directly below xWall. We can't
// move down if its left side is < the left
// side of our space:
if (iWall > 0 && xMyGet(pscnSearch, iNewWall - 1, DIR_UP) < xMyGet(pscn, iWall - 1, DIR_DOWN)) { iTurn = -1; break; // =====>
}
iNewWall--; } else { // There is a space directly below xWall. We can't
// move down if its right side is more than the
// right side of our region:
if (xMyGet(pscnSearch, iNewWall, DIR_UP) >= xMyGet(pscn, iWall + 1, DIR_DOWN)) break; // =====>
}
xNewWall = xMyGet(pscnSearch, iNewWall, DIR_UP);
// Don't bother outputing multiple in-line straight lines:
if (xWall != xNewWall || xMyGet(pscn, iWall, DIR_UP) != xNewWall || xMyGet(pscnSearch, iNewWall, DIR_DOWN) != xNewWall) { aptl[0].x = xWall; aptl[0].y = pscn->yBottom; aptl[1].y = pscn->yBottom; aptl[1].x = xNewWall;
if (!epo.bPolyLineTo(pexo, aptl, 2)) return(FALSE); }
pscn = pscnSearch; iWall = iNewWall; pscnSearch = pscnGet(pscn);
vFramed(pscn, iWall, this); }
// Setup to go up other side:
aptl[0].x = xMyGet(pscn, iWall, DIR_DOWN); aptl[0].y = pscn->yBottom; aptl[1].y = pscn->yBottom; aptl[1].x = xMyGet(pscn, iWall + iTurn, DIR_DOWN);
if (!epo.bPolyLineTo(pexo, aptl, 2)) return(FALSE);
pscnSearch = pscnGot(pscn); iWall += iTurn; vFramed(pscn, iWall, this);
// Go up:
iTurn = -1; while (pscnSearch->cWalls != 0) { LONG xWall = xMyGet(pscn, iWall, DIR_UP); LONG iNewWall; LONG xNewWall;
COUNT iLeft = bInside; COUNT iRight = pscnSearch->cWalls - 1 - bInside;
// It would be nice if the last wall in the region structure
// was plus infinity, but it isn't, so we do this check:
if (xMyGet(pscnSearch, iRight, DIR_DOWN) < xWall) iNewWall = iRight; else { // Check if it's possible to find a wall with the
// maximum x-value < xWall:
if (xMyGet(pscnSearch, iLeft, DIR_DOWN) >= xWall) break; // =====>
// Binary search to find it:
while (TRUE) { COUNT iSearch = (iLeft + iRight) >> 1; if (iSearch == iLeft) break; // =====>
LONG xSearch = xMyGet(pscnSearch, iSearch, DIR_DOWN);
if (xSearch >= xWall) iRight = iSearch; else iLeft = iSearch; } iNewWall = iLeft; }
if ((iNewWall & 1) == bInside) { // There is a region directly above xWall. We can't
// move up if its right side is more than the right
// side of our space:
if (iWall < pscn->cWalls - 1 && xMyGet(pscnSearch, iNewWall + 1, DIR_DOWN) > xMyGet(pscn, iWall + 1, DIR_UP)) { iTurn = +1; break; // =====>
}
iNewWall++; } else { // There is a space directly above xWall. We can't
// move up if its left side is <= the left side
// of our region:
if (xMyGet(pscnSearch, iNewWall, DIR_DOWN) <= xMyGet(pscn, iWall - 1, DIR_UP)) break; // =====>
}
xNewWall = xMyGet(pscnSearch, iNewWall, DIR_DOWN);
// Don't bother outputing multiple in-line straight lines:
if (xWall != xNewWall || xMyGet(pscn, iWall, DIR_DOWN) != xNewWall || xMyGet(pscnSearch, iNewWall, DIR_UP) != xNewWall) { aptl[0].x = xWall; aptl[0].y = pscn->yTop; aptl[1].y = pscn->yTop; aptl[1].x = xNewWall;
if (!epo.bPolyLineTo(pexo, aptl, 2)) return(FALSE); }
pscn = pscnSearch; iWall = iNewWall; pscnSearch = pscnGot(pscn);
vFramed(pscn, iWall, this); }
// Check if we've returned to where we started from:
if (pscnCurrent != pscn || iWallCurrent != iWall - 1) { // Setup to go down other side:
aptl[0].x = xMyGet(pscn, iWall, DIR_UP); aptl[0].y = pscn->yTop; aptl[1].y = pscn->yTop; aptl[1].x = xMyGet(pscn, iWall + iTurn, DIR_UP);
if (!epo.bPolyLineTo(pexo, aptl, 2)) return(FALSE);
pscnSearch = pscnGet(pscn); iWall += iTurn; vFramed(pscn, iWall, this);
goto GoDown; // =====>
}
// We're all done with this outline!
aptl[0].x = xMyGet(pscn, iWall, DIR_UP); aptl[0].y = pscn->yTop;
if (!epo.bPolyLineTo(pexo, aptl, 1) || !epo.bCloseFigure()) return(FALSE); } } pscnCurrent = pscnGet(pscnCurrent); }
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bCreate(epo, pexo) * * Create a path from the frame of a region without destroying the region. * * History: * 30-Apr-1992 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL RGNOBJ::bCreate( EPATHOBJ& epo, EXFORMOBJ *pexo) {
// When converting the region to a path, bCreate() destroys the region's
// data, which would be sort of rude, so make a copy of the region first:
BOOL bRes; RGNMEMOBJTMP rmoCopy(sizeRgn()); if (!rmoCopy.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); bRes = FALSE; } else { rmoCopy.vCopy(*this);
VALIDATE(rmoCopy);
bRes = rmoCopy.bOutline(epo, pexo);
VALIDATE(*(RGNOBJ *)this); }
return(bRes); }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bSubtract(prcl, arcl, crcl) * * Subtract a list of rectangles from a rectangle to produce a region. * This is just a special case of bMerge and is written to speed up * USER's computation of VisRgns for obscured windows. * * WARNING: If this function returns FALSE, the region may be inconsistent. * The caller must discard or reset the region. (See bug #343770) * * History: * 10-Aug-1993 -by- Eric Kutter [erick] * rewrote the complex case. (accounts for 50% of operations) * * 18-Nov-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL RGNOBJAPI::bSubtract(RECTL *prcl, RECTL *arcl, int crcl) { RGNLOG rl(hrgn_,prgn,"RGNOBJAPI::bSubtract"); rl.vRet(0);
PREGION prgn1 = prgn;
ASSERTGDI(crcl > 0, "Zero rectangles in RGNOBJ::bSubtract\n");
// Since we are really pressed for speed, we special case ONE rectangle being
// subtracted. There are only a few cases to deal with and the result always
// fits into a quantum region.
#if DBG
for (int i = 0; i < crcl; ++i) { if ((arcl[i].left >= arcl[i].right) || (arcl[i].top >= arcl[i].bottom)) { RIP("RGNOBJAPI::bSubtract - invalid rectangle from USER\n"); arcl[i].left = -10001; arcl[i].right = -10000; arcl[i].top = -10001; arcl[i].bottom = -10000; } } #endif
// Handle empty rectangles here. Otherwise if we pass them to
// bSubtractComplex they can cause an AV with the sentinel case
// (see bug 299398 for details).
if (((ERECTL *) prcl)->bEmpty()) { vSet(); return TRUE; } if (crcl == 1) { // First discard total misses
if ((arcl[0].top >= prcl->bottom) || (arcl[0].left >= prcl->right) || (arcl[0].bottom <= prcl->top) || (arcl[0].right <= prcl->left)) { vSet(prcl); return(TRUE); }
/*
OK, this gets a little tricky. There are 16 distinct ways that two rectangles can overlap. In the diagram below, 1 and 2 are rectangle boundaries and asterisks represent areas of overlap.
22 22222 2 22 2*11 2***2 1*1 11*2 1 1 1 1 1 1 1 1 TOP_NOTCH 111 111 111 111
22 22222 2 22 2*11 2***2 1*1 11*2 2* 1 2***2 1*1 1 *2 VERT_CLIP 2*11 2***2 1*1 11*2 22 22222 2 22
111 111 111 111 2* 1 2***2 1*1 1 *2 VERT_NOTCH 111 111 111 111
111 111 111 111 1 1 1 1 1 1 1 1 BOTTOM_NOTCH 2*11 2***2 1*1 11*2
LEFT_NOTCH HORIZ_CLIP HORIZ_NOTCH RIGHT_NOTCH
I have given each of the rows and columns names. By simply finding out which state I'm in, I can produce the correct output region. */
RECTL rcl; SCAN *pscn; int iState;
if (arcl[0].left <= prcl->left) iState = arcl[0].right < prcl->right ? SR_LEFT_NOTCH : SR_HORIZ_CLIP ; else iState = arcl[0].right < prcl->right ? SR_HORIZ_NOTCH : SR_RIGHT_NOTCH;
if (arcl[0].top <= prcl->top) iState += arcl[0].bottom < prcl->bottom ? SR_TOP_NOTCH : SR_VERT_CLIP ; else iState += arcl[0].bottom < prcl->bottom ? SR_VERT_NOTCH : SR_BOTTOM_NOTCH;
switch(iState) { // NULL case
case SR_VERT_CLIP | SR_HORIZ_CLIP: vSet(); break;
// SINGLE cases
case SR_TOP_NOTCH | SR_HORIZ_CLIP: rcl = *prcl; rcl.top = arcl[0].bottom; vSet(&rcl); break;
case SR_VERT_CLIP | SR_LEFT_NOTCH: rcl = *prcl; rcl.left = arcl[0].right; vSet(&rcl); break;
case SR_VERT_CLIP | SR_RIGHT_NOTCH: rcl = *prcl; rcl.right = arcl[0].left; vSet(&rcl); break;
case SR_BOTTOM_NOTCH | SR_HORIZ_CLIP: rcl = *prcl; rcl.bottom = arcl[0].top; vSet(&rcl); break;
// 2 scans (Corner notch)
case SR_TOP_NOTCH | SR_LEFT_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 3 * NULL_SCAN_SIZE + 4 * sizeof(INDEX_LONG); prgn1->cScans = 4; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].bottom; pscn->ai_x[0].x = arcl[0].right; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].bottom; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
case SR_TOP_NOTCH | SR_RIGHT_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 3 * NULL_SCAN_SIZE + 4 * sizeof(INDEX_LONG); prgn1->cScans = 4; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = arcl[0].left; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].bottom; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
case SR_BOTTOM_NOTCH | SR_LEFT_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 3 * NULL_SCAN_SIZE + 4 * sizeof(INDEX_LONG); prgn1->cScans = 4; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].top; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].top; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = arcl[0].right; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
case SR_BOTTOM_NOTCH | SR_RIGHT_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 3 * NULL_SCAN_SIZE + 4 * sizeof(INDEX_LONG); prgn1->cScans = 4; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].top; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].top; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = arcl[0].left; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
// 3 scans
case SR_VERT_CLIP | SR_HORIZ_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 2 * NULL_SCAN_SIZE + 4 * sizeof(INDEX_LONG); prgn1->cScans = 3; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 4; pscn->yTop = prcl->top; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = arcl[0].left; pscn->ai_x[2].x = arcl[0].right; pscn->ai_x[3].x = prcl->right; pscn->ai_x[4].x = 4; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
// 4 scans (Edge notch)
case SR_TOP_NOTCH | SR_HORIZ_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 3 * NULL_SCAN_SIZE + 6 * sizeof(INDEX_LONG); prgn1->cScans = 4; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 4; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = arcl[0].left; pscn->ai_x[2].x = arcl[0].right; pscn->ai_x[3].x = prcl->right; pscn->ai_x[4].x = 4; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].bottom; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
case SR_BOTTOM_NOTCH | SR_HORIZ_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 3 * NULL_SCAN_SIZE + 6 * sizeof(INDEX_LONG); prgn1->cScans = 4; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].top; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 4; pscn->yTop = arcl[0].top; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = arcl[0].left; pscn->ai_x[2].x = arcl[0].right; pscn->ai_x[3].x = prcl->right; pscn->ai_x[4].x = 4; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
// 5 scans
case SR_VERT_NOTCH | SR_HORIZ_CLIP: prgn1->sizeRgn = NULL_REGION_SIZE + 4 * NULL_SCAN_SIZE + 4 * sizeof(INDEX_LONG); prgn1->cScans = 5; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].top; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = arcl[0].top; pscn->yBottom = arcl[0].bottom; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].bottom; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
case SR_VERT_NOTCH | SR_LEFT_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 4 * NULL_SCAN_SIZE + 6 * sizeof(INDEX_LONG); prgn1->cScans = 5; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].top; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].top; pscn->yBottom = arcl[0].bottom; pscn->ai_x[0].x = arcl[0].right; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].bottom; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
case SR_VERT_NOTCH | SR_RIGHT_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 4 * NULL_SCAN_SIZE + 6 * sizeof(INDEX_LONG); prgn1->cScans = 5; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].top; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].top; pscn->yBottom = arcl[0].bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = arcl[0].left; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].bottom; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break;
// The classic toroidal region
case SR_VERT_NOTCH | SR_HORIZ_NOTCH: prgn1->sizeRgn = NULL_REGION_SIZE + 4 * NULL_SCAN_SIZE + 8 * sizeof(INDEX_LONG); prgn1->cScans = 5; prgn1->rcl = *prcl;
pscn = prgn1->pscnHead(); pscn->cWalls = 0; pscn->yTop = NEG_INFINITY; pscn->yBottom = prcl->top; pscn->ai_x[0].x = 0; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = prcl->top; pscn->yBottom = arcl[0].top; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 4; pscn->yTop = arcl[0].top; pscn->yBottom = arcl[0].bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = arcl[0].left; pscn->ai_x[2].x = arcl[0].right; pscn->ai_x[3].x = prcl->right; pscn->ai_x[4].x = 4; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 2; pscn->yTop = arcl[0].bottom; pscn->yBottom = prcl->bottom; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right; pscn->ai_x[2].x = 2; // This sets cWalls2
pscn = pscnGet(pscn); pscn->cWalls = 0; pscn->yTop = prcl->bottom; pscn->yBottom = POS_INFINITY; pscn->ai_x[0].x = 0; // This sets cWalls2
prgn1->pscnTail = pscnGet(pscn); break; }
return(TRUE); }
// its complex, do it the hard way
PREGION prgnOrg = prgn;
// lock the handle so no one can reference it while the pobj may be invalid
OBJLOCK ol((HOBJ) hrgn_);
BOOL b = bSubtractComplex(prcl,arcl,crcl);
if (prgn != prgnOrg) { PVOID pv = HmgReplace((HOBJ) hrgn_,(POBJ) prgn,0,1,OBJLOCK_TYPE); ASSERTGDI(pv != NULL,"RGNOBJAPI::bSubtract - HmgReplace failed\n"); }
rl.vRet((ULONG_PTR)prgn);
return(b); }
/******************************Member*Function*****************************\
* RGNOBJ::bSubtractComplex() * * Handle the complex cases of subtracting a list of rectangles from another * rectangle to generate a region. * * The alogrithm used here requires that the list of rectangles is always * sorted by both ytop and ybottom within the range of rectangles overlaping * the current scan. To have both of these true, ytop refers to the max of * ytop and the top of the current scan. There is no ordering in the * horizontal direction. Rectangles that fall below the current scan are still * sorted by top but not by bottom. While computing a new scan, all rectangles * that are just being added are inserted to keep the bottoms sorted. * * .................. * scan 1 top -->.+-----+ +-----+. * .| | | |. * .| | | |. * .| | | |. +-----+ * ................. | | * | | | | | | * | | | | | | * | | | | | | * | | | | | | * +-----+ | | | | * | | | | * | | +-----+ * | | * +-----+ * * * * +-----+ +-----+ * | | | |. * ........................... * scan 2 top --> .| | +-----+ | |. * .| | | | | |. * .| | | | | |. * .| | | | | |. * .| | | | | |. * .| | | | | |. * .+-----+ | | | |. * ........................... * +-----+ | | * | | * +-----+ * * * To keep this ordering, a list of pointers to rectangles is used. This reduces * the overhead of reordering the rectangles for each scan and reduces the amount * of temporary memory required. For each scan, iFirst and iLast bracket the * set of rectangles intersecting the current scan. * * To calculate the walls of a scan, we start assuming the entire with of the * dst rectangle and subtract from there. The left to right of each rectangle * from iFirst to iLast is subtracted from the scan. * * iFirst is update to remove any rectangles that are finished after the current * scan. * * iLast is updated to include any new rectangles that lie in the new scan. * * WARNING: If this function returns FALSE, the region may be inconsistent. * The caller must discard or reset the region. (See bug #343770) * * History: * 26-Jul-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL RGNOBJAPI::bSubtractComplex( RECTL *prcl, RECTL *arclRemove, int crcl) { ASSERTGDI(crcl > 0, "Zero rectangles in RGNOBJ::bSubtract\n"); ASSERTGDI(prcl->bottom >= prcl->top," RGNOBJ::bSubtractComplex - bottom > top\n");
// allocate array for temporary storage
PRECTL aprclStack[100]; PRECTL *aprcl;
if (crcl < 100) { aprcl = aprclStack; } else { aprcl = (PRECTL *)PALLOCNOZ((sizeof(PRECTL) + 1) * crcl, 'ngrG');
if (aprcl == NULL) return(FALSE); }
// sort the array of subtraction rectangles by yTop
int iInsert; int i;
for (i = 0; i < crcl; ++i) { // insert rect[i], search from the end so we don't do much in the
// case of a pre-sorted list and we do the pointer copies while we
// search backwards if it isn't sorted.
iInsert = i;
while (iInsert && (arclRemove[i].top < aprcl[iInsert-1]->top)) { aprcl[iInsert] = aprcl[iInsert-1]; --iInsert; }
aprcl[iInsert] = &arclRemove[i]; }
// add an extra rectangle at the end to make the end case simple
RECTL rclLast; rclLast.left = 0; rclLast.right = 0; rclLast.top = prcl->bottom; rclLast.bottom = POS_INFINITY;
aprcl[crcl] = &rclLast;
// partialy setup the first scan, allowing for coalescing of inital empty scans
PSCAN pscnOld = prgn->pscnHead();
pscnOld->cWalls = 0; pscnOld->yTop = NEG_INFINITY; pscnOld->yBottom = POS_INFINITY; pscnOld->ai_x[0].x = 0;
PSCAN pscn = pscnGet(pscnOld);
prgn->sizeRgn = NULL_REGION_SIZE; prgn->cScans = 1; prgn->rcl.left = POS_INFINITY; prgn->rcl.right = NEG_INFINITY;
// now do the real work, all rectangles in the range of iFirst to iLast
// are sorted by bottom. To get in this range, the rectangle must intersect
// yTop.
//
// yTop - top y value for current scan
// yBottom - bottom y value for current scan
// iFirst - index first rectangle in current scan
// iLast - one past index of last rectangle in current scan
LONG yTop = prcl->top; LONG yBottom = prcl->bottom; int cWalls = 0;
// throw out any easy ones
int iFirst = 0;
while (aprcl[iFirst]->bottom <= yTop) ++iFirst;
int iLast = iFirst;
// while we still have scans
do { // make sure this scan is going to have enough room, also insure enough space
// for last scan. The scan will never have more walls than 2 * (num rectangles + 1)
LONG size = prgn->sizeRgn + 2 * NULL_SCAN_SIZE + 2 * sizeof(INDEX_LONG) * (crcl - iFirst + 1);
if (size > (LONG)prgn->sizeObj) { // not big enough, grow it and be aggresive with size because growing is
// very expensive. Also set any fields need to grow and reset the scans
// afterwards.
prgn->pscnTail = pscn;
if (!bExpand((UINT)(size + (crcl - iFirst) * (NULL_SCAN_SIZE + (crcl - iFirst) * sizeof(INDEX_LONG))))) { if (aprcl != aprclStack) { VFREEMEM(aprcl); } return(FALSE); }
pscn = prgn->pscnTail; pscnOld = pscnGot(pscn); }
// setup the new scan, assume the entire width of prcl
cWalls = 2; pscn->ai_x[0].x = prcl->left; pscn->ai_x[1].x = prcl->right;
// check if we need to reduce the scan, do we have any overlapping rects?
if (aprcl[iFirst]->top > yTop) { // the bottom of this scan is the top of the next rectangle
yBottom = aprcl[iFirst]->top; } else { // assume the bottom is the bottom of the first rectangle
yBottom = aprcl[iFirst]->bottom;
// first find any new rectangles that fit in the new scan, and reduce
// ybottom appropriately
while (TRUE) { // does the next rectangle start below the current top
if (aprcl[iLast]->top > yTop) { // stop this scan where the next rectangle starts
if (aprcl[iLast]->top < yBottom) yBottom = aprcl[iLast]->top;
break; }
if (aprcl[iLast]->bottom < yBottom) yBottom = aprcl[iLast]->bottom;
// perculate it backwards to keep the current rectangles sorted by yBottom
iInsert = iLast; PRECTL prclTmp = aprcl[iLast];
while ((iInsert > iFirst) && (prclTmp->bottom < aprcl[iInsert-1]->bottom)) { aprcl[iInsert] = aprcl[iInsert-1]; --iInsert; }
// if we did some shuffling, put the rectangle in the right place.
// It is possible to have a rectangle that completely lies above
// the top of this scan if we are on the first scan and the top of
// this rectangle is greater than one before it but the bottom is
// less than the top of prcl.
if (aprcl[iInsert]->bottom <= yTop) { // the rectangle is completely clipped away
ASSERTGDI(iInsert == iFirst,"bSubtractComplex - iInsert != iFirst\n"); ++iFirst; } else { aprcl[iInsert] = prclTmp; }
++iLast; }
// build up the scan, for each new rectangle...
for (int irc = iFirst; irc < iLast; ++irc) { LONG xLeft = aprcl[irc]->left; LONG xRight = aprcl[irc]->right;
// merge it into the walls
for (int iWall = 0; iWall < cWalls; iWall += 2) { // the walls are before the rectangle, nothing to do yet
if (xLeft >= pscn->ai_x[iWall+1].x) continue;
// the walls are passed the rectangle, done with this rectangle
if (xRight <= pscn->ai_x[iWall].x) break;
// compute the overlap, update the walls
int iHit = 0;
if (xLeft <= pscn->ai_x[iWall].x) iHit = 1;
if (xRight >= pscn->ai_x[iWall+1].x) iHit += 2;
switch (iHit) { case 0: // completely inside the walls, insert new rectangle
RtlMoveMemory(&pscn->ai_x[iWall+3],&pscn->ai_x[iWall+1], (cWalls - iWall - 1) * sizeof(INDEX_LONG)); pscn->ai_x[iWall+1].x = xLeft; pscn->ai_x[iWall+2].x = xRight; cWalls += 2; break;
case 1: // overlapped the left wall, just update the left edge
pscn->ai_x[iWall].x = xRight; break;
case 2: // overlapped the right wall, just update the right edge
pscn->ai_x[iWall+1].x = xLeft; break;
case 3: // completely bounds the walls, remove rectangle
RtlMoveMemory(&pscn->ai_x[iWall],&pscn->ai_x[iWall+2], (cWalls - iWall - 2) * sizeof(INDEX_LONG)); cWalls -= 2; iWall -= 2; break; } } } }
// make sure yBottom isn't below the original rectangle
if (yBottom > prcl->bottom) yBottom = prcl->bottom;
// Try to coalesce the current scan with the previous scan
if (((int)pscnOld->cWalls == cWalls) && !memcmp(pscnOld->ai_x, pscn->ai_x,(UINT)cWalls * sizeof(INDEX_LONG))) { // the walls are identical to the previous scan, merge them
pscnOld->yBottom = yBottom; } else { // update the x bounds
if (cWalls) { if (pscn->ai_x[0].x < prgn->rcl.left) prgn->rcl.left = pscn->ai_x[0].x;
if (pscn->ai_x[cWalls-1].x > prgn->rcl.right) prgn->rcl.right = pscn->ai_x[cWalls-1].x; }
// update the rest of the fields
prgn->cScans++; pscn->cWalls = cWalls; prgn->sizeRgn += pscn->sizeGet();
pscn->yTop = yTop; pscn->yBottom = yBottom; pscn->ai_x[cWalls].x = cWalls;
pscnOld = pscn; pscn = pscnGet(pscn); }
// trim off the finished rectangles
yTop = yBottom;
while ((iFirst < iLast) && (aprcl[iFirst]->bottom <= yTop)) ++iFirst;
} while (yBottom < prcl->bottom);
// set the top and bottom bounds and the last scan
if (prgn->cScans == 1) { prgn->rcl.top = 0; prgn->rcl.bottom = 0; prgn->rcl.left = 0; prgn->rcl.right = 0; pscnOld->yBottom = POS_INFINITY; prgn->pscnTail = pscn; } else { // was the last scan empty?
if (pscnOld->cWalls == 0) { // combine the final scan with the last computed scan
pscn = pscnOld; } else { // set up any fields that wouldn't already be set by an empty scan
pscn->yTop = pscnOld->yBottom; prgn->cScans++; pscn->cWalls = 0; pscn->ai_x[0].x = 0; prgn->sizeRgn += pscn->sizeGet(); }
// set vertical bounds
prgn->pscnHead()->yBottom = pscnGet(prgn->pscnHead())->yTop; prgn->rcl.top = prgn->pscnHead()->yBottom; prgn->rcl.bottom = pscn->yTop;
// setup the other region fields
pscn->yBottom = POS_INFINITY; prgn->pscnTail = pscnGet(pscn); }
// if we allocated a buffer, free it
if (aprcl != aprclStack) VFREEMEM(aprcl);
return(TRUE); }
/******************************Public*Routine******************************\
* * SyncRgn converts a client NULL or SIMPLE rectangle into a normal region * for kernel operations * * Arguments: * * none * * Return Value: * * BOOL * * History: * * 22-Jun-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
BOOL RGNOBJ::SyncUserRgn() { BOOL bRet = FALSE;
RGNLOG rl(prgn,"RGNOBJ::SyncUserRgn");
if (prgn != (PREGION)NULL) { //
// does this fine region have valid user-mode data?
//
PRGNATTR prRegion = (PRGNATTR)(PENTRY_FROM_POBJ(prgn)->pUser);
if (prRegion != (PRGNATTR)NULL) { __try {
if (prRegion->AttrFlags & ATTR_RGN_VALID) {
if (prRegion->AttrFlags & ATTR_RGN_DIRTY) { if (prRegion->Flags == NULLREGION) { vSet(); prRegion->AttrFlags &= ~ATTR_RGN_DIRTY; } else if (prRegion->Flags == SIMPLEREGION) { vSet(&prRegion->Rect); prRegion->AttrFlags &= ~ATTR_RGN_DIRTY; } } } else { WARNING("RGNOBJ::SyncUserRgn invalid rectregion\n"); }
bRet = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("except in RGNOBJ::SyncRgn\n"); } } } return(bRet); }
VOID RGNOBJ::UpdateUserRgn() { RGNLOG rl(prgn,"RGNOBJ::UpdateUserRgn"); if (prgn != (PREGION)NULL) { //
// does this fine region have valid user-mode data?
//
PRGNATTR prRegion = (PRGNATTR)(PENTRY_FROM_POBJ(prgn)->pUser);
if (prRegion != (PRGNATTR)NULL) { //
// check for DCATTR
//
__try { //
// set user region complexity and bounding box
//
if (prRegion->AttrFlags & ATTR_RGN_VALID) { prRegion->Flags = iComplexity(); prRegion->Rect = prgn->rcl; } else { WARNING("UpdateUserRgn: Invalid region"); } } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("except in RGNOBJ::UpdateUserRgn\n"); } } } }
/******************************Public*Routine******************************\
* BOOL RGNOBJ::bValidateFramedRegion() * * Verify the region's integrity. For debugging purposes only. * * History: * 30-Apr-1992 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
#if DBG
#define REASONABLE 0x10000L
#define FX_REASONABLE (0x10000L << 4)
BOOL RGNOBJ::bValidateFramedRegion() { #ifdef DEBUGREGIONS
if (prgn == NULL) return(TRUE); // ???
// ASSERTGDI(prgn->rcl.left <= prgn->rcl.right &&
// prgn->rcl.top <= prgn->rcl.bottom, "Funky rcl");
ASSERTGDI(prgn->pscnHead() <= prgn->pscnTail, "Funky head/tail"); ASSERTGDI(prgn->sizeRgn <= prgn->sizeObj, "sizeRgn > sizeObj"); ASSERTGDI((BYTE*) prgn->pscnTail <= (BYTE*) prgn + prgn->sizeObj, "Tail > prgn + sizeObj"); ASSERTGDI((BYTE*) prgn->pscnTail <= (BYTE*) prgn + prgn->sizeRgn, "Tail > prgn + sizeRgn"); // ASSERTGDI(prgn->cScans < REASONABLE, "cScans not reasonable");
ASSERTGDI(prgn->cScans >= 1, "cScans < 1");
if (prgn->cScans < 2) return(TRUE);
COUNT cScans = prgn->cScans; SCAN *pscn = prgn->pscnHead(); LONG yOldBottom = pscn->yBottom;
ASSERTGDI(pscn->yTop == NEG_INFINITY, "Very yTop not NEG_INFINITY"); ASSERTGDI(pscn->cWalls == 0, "Very top cWalls not 0"); ASSERTGDI(pscn->ai_x[0].x == 0, "Very top cWalls2 not 0"); // ASSERTGDI(pscn->yBottom == prgn->rcl.top, "Very top yBottom not rcl.top");
pscn = pscnGet(pscn); cScans -= 2;
while (cScans--) { LONG iWall; LONG cWalls = pscn->cWalls; LONG yTop = pscn->yTop; LONG yBottom = pscn->yBottom; LONG xOld = NEG_INFINITY;
ASSERTGDI(pscn->ai_x[cWalls].x == cWalls, "cWalls1 != cWalls2"); ASSERTGDI(yTop < yBottom, "Region corrupted: yTop >= yBottom"); ASSERTGDI(yOldBottom == yTop, "Region corrupted: yOldBottom != yTop"); // ASSERTGDI(cWalls < REASONABLE, "cWalls not reasonable");
ASSERTGDI((cWalls & 1) == 0, "cWalls not even"); // ASSERTGDI(yTop > -REASONABLE && yTop < REASONABLE,
// "yTop not reasonable");
// ASSERTGDI(yBottom > -REASONABLE && yBottom < REASONABLE,
// "yBottom not reasonable");
for (iWall = 0; iWall != cWalls; iWall++) { LONG x = xGet(pscn, (PTRDIFF) iWall);
// Framed regions make things look weird:
if (x > MAX_REGION_COORD) x -= REGION_FRAMED_OFFSET;
ASSERTGDI(VALID_SCR(x), "Region corrupted: Invalid x");
if (x <= xOld) DbgPrint("x <= xOld - pscn = %p, iWall = %lx\n",pscn,iWall);
// ASSERTGDI(x > xOld, "Region corrupted: x <= xOld");
// ASSERTGDI(x > -REASONABLE && x < REASONABLE, "x not reasonable");
xOld = x; }
// if (cWalls > 0)
// {
// ASSERTGDI(xGet(pscn, (PTRDIFF) 0) >= (prgn->rcl.left - 1),
// "x < rcl.left");
// ASSERTGDI(xGet(pscn, (PTRDIFF) (cWalls - 1)) <= (prgn->rcl.right + 1),
// "x > rcl.right");
// }
yOldBottom = yBottom; pscn = pscnGet(pscn);
ASSERTGDI(pscn > prgn->pscnHead() && pscn <= prgn->pscnTail, "pscn out of bounds"); // ASSERTGDI(prgn->rcl.top <= (yBottom + 1) && prgn->rcl.bottom >= (yBottom - 1),
// "yBottom not in rcl");
}
ASSERTGDI(pscn->yBottom == POS_INFINITY, "Very yBottom not POS_INFINITY"); ASSERTGDI(pscn->cWalls == 0, "Very bottom cWalls not 0"); ASSERTGDI(pscn->ai_x[0].x == 0, "Very bottom cWalls2 not 0"); //ASSERTGDI(pscn->yTop == prgn->rcl.bottom, "Very bottom yTop not rcl.bottom");
#endif
return(TRUE); }
#endif
|