Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

773 lines
22 KiB

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