|
|
/**************************************************************************\
* * Copyright (c) 1998 Microsoft Corporation * * Module Name: * * DpRegion.hpp * * Abstract: * * DpRegion class operates on scan-converted Y spans of rects * * Created: * * 12/16/1998 DCurtis * \**************************************************************************/
#ifndef _DPREGION_HPP
#define _DPREGION_HPP
// Each Y span consists of 4 values
#define YSPAN_SIZE 4
#define YSPAN_YMIN 0 // minimum Y value (inclusive)
#define YSPAN_YMAX 1 // maximum Y value (exclusive)
#define YSPAN_XOFFSET 2 // offset into XCoords for this span
#define YSPAN_XCOUNT 3 // num XCoords for this span (multiple of 2)
#if 0
// The infinite max and min values are set up to be the greatest
// values that will interop with GDI HRGNs on WinNT successfully (divided by 2).
// We divide those values by 2 so that there is a little room to offset
// the GDI region and still have it work correctly.
// Of course, this won't work on Win9x -- we have to handle Win9x as
// a special case in the GetHRGN code.
#define INFINITE_MAX 0x03FFFFC7 // (0x07ffff8f / 2)
#define INFINITE_MIN 0xFC000038 // (0xF8000070 / 2)
#define INFINITE_SIZE (INFINITE_MAX - INFINITE_MIN)
#else
// Instead of the above, let's use the largest possible integer value
// that is guaranteed to round trip correctly between float and int,
// but as above, let's leave a little head room so we can offset the region.
#include "float.h"
#define INFINITE_MAX (1 << (FLT_MANT_DIG - 2))
#define INFINITE_MIN (-INFINITE_MAX)
#define INFINITE_SIZE (INFINITE_MAX - INFINITE_MIN)
#endif
class DpComplexRegion { public: INT XCoordsCapacity; // num XCoords (INTs) allocated
INT XCoordsCount; // num XCoords (INTs) used
INT YSpansCapacity; // num YSpans allocated
INT NumYSpans; // NumYSpans used
INT YSearchIndex; // search start index in YSpans
INT * XCoords; // pointer to XCoords
INT * YSpans; // pointer to YSpans
// (XCoords + XCoordsCapacity)
public: INT * GetYSpan (INT spanIndex) { return YSpans + (spanIndex * YSPAN_SIZE); }
VOID ResetSearchIndex() { YSearchIndex = NumYSpans >> 1; }
BOOL YSpanSearch(INT y, INT ** ySpan, INT * spanIndex); };
class DpRegionBuilder : public GpOutputYSpan { private: // We now use an ObjectTag to determine if the object is valid
// instead of using a BOOL. This is much more robust and helps
// with debugging. It also enables us to version our objects
// more easily with a version number in the ObjectTag.
ObjectTag Tag; // Keep this as the 1st value in the object!
protected: VOID SetValid(BOOL valid) { Tag = valid ? ObjectTagDpRegionBuilder : ObjectTagInvalid; }
public: INT XMin; INT YMin; INT XMax; INT YMax; DpComplexRegion * ComplexData;
public: DpRegionBuilder(INT ySpans) { SetValid(InitComplexData(ySpans) == Ok); }
~DpRegionBuilder() { GpFree(ComplexData);
SetValid(FALSE); // so we don't use a deleted object
}
BOOL IsValid() const { ASSERT((Tag == ObjectTagDpRegionBuilder) || (Tag == ObjectTagInvalid)); #if DBG
if (Tag == ObjectTagInvalid) { WARNING1("Invalid DpRegionBuilder"); } #endif
return (Tag == ObjectTagDpRegionBuilder); }
virtual GpStatus OutputYSpan( INT yMin, INT yMax, INT * xCoords, // even number of X coordinates
INT numXCoords // must be a multiple of 2
);
GpStatus InitComplexData(INT ySpans); };
// Forward Declaration for friendliness
class DpClipRegion;
// This class was constructed to optimize for space and speed for the common
// case of the region consisting of a single rectangle. For that case of a
// simple region, we want the region to be as small and as fast as possible so
// that it is not expensive to have a region associated with every window.
class DpRegion { private: friend DpClipRegion; // We now use an ObjectTag to determine if the object is valid
// instead of using a BOOL. This is much more robust and helps
// with debugging. It also enables us to version our objects
// more easily with a version number in the ObjectTag.
ObjectTag Tag; // Keep this as the 1st value in the object!
protected: VOID SetValid(BOOL valid) { Tag = valid ? ObjectTagDpRegion : ObjectTagInvalid; }
protected: UINT32 Infinite : 1; UINT32 Empty : 1; UINT32 Lazy : 1; UINT32 Pad : 29;
INT XMin; INT YMin; INT XMax; // exclusive
INT YMax; // exclusive
DpComplexRegion * ComplexData; mutable INT Uniqueness;
public: enum Visibility { Invisible = 0, PartiallyVisible = 1, ClippedVisible = 2, TotallyVisible = 3, };
public: DpRegion(BOOL empty = FALSE); // default is infinite region
DpRegion(const GpRect * rect); DpRegion(INT x, INT y, INT width, INT height); DpRegion(const DpPath * path, const GpMatrix * matrix); DpRegion(const DpRegion * region, BOOL lazy = FALSE); DpRegion(DpRegion & region); // copy constructor
DpRegion(const RECT * rects, INT count); ~DpRegion() { FreeData();
SetValid(FALSE); // so we don't use a deleted object
}
DpRegion &operator=( DpRegion & region );
BOOL IsValid() const { ASSERT((Tag == ObjectTagDpRegion) || (Tag == ObjectTagInvalid)); #if DBG
if (Tag == ObjectTagInvalid) { WARNING1("Invalid DpRegion"); } #endif
return (Tag == ObjectTagDpRegion); }
VOID Set(INT x, INT y, INT width, INT height); VOID Set(GpRect * rect) { ASSERT (rect != NULL); Set(rect->X, rect->Y, rect->Width, rect->Height); } GpStatus Set(const DpPath * path, const GpMatrix * matrix); // path is in world units
GpStatus Set(const DpRegion * region, BOOL lazy = FALSE); GpStatus Set(const RECT * rects, INT count); VOID SetInfinite(); VOID SetEmpty();
GpStatus Offset(INT xOffset, INT yOffset);
GpStatus And (const DpRegion * region); GpStatus Or (const DpRegion * region); GpStatus Xor (const DpRegion * region); GpStatus Exclude (const DpRegion * region); GpStatus Complement(const DpRegion * region);
INT GetXMin() const { ASSERT(IsValid()); return XMin; } INT GetYMin() const { ASSERT(IsValid()); return YMin; } INT GetXMax() const { ASSERT(IsValid()); return XMax; } INT GetYMax() const { ASSERT(IsValid()); return YMax; }
INT GetUniqueness() const { ASSERT(IsValid()); if (Uniqueness == 0) { Uniqueness = GenerateUniqueness(); } return Uniqueness; }
VOID UpdateUID() { ASSERT(IsValid()); Uniqueness = 0; }
VOID GetBounds(GpRect * bounds) const { ASSERT(IsValid()); ASSERT(bounds != NULL);
bounds->X = XMin; bounds->Y = YMin; bounds->Width = XMax - XMin; bounds->Height = YMax - YMin; }
VOID GetBounds(GpPointF * topLeft, GpPointF * bottomRight) const { ASSERT(IsValid()); ASSERT((topLeft != NULL) && (bottomRight != NULL));
topLeft->X = (REAL)XMin; topLeft->Y = (REAL)YMin; bottomRight->X = (REAL)XMax; bottomRight->Y = (REAL)YMax; }
BOOL IsEqual(DpRegion * region);
BOOL IsEmpty() const { ASSERT(IsValid()); return Empty; }
BOOL IsInfinite() const { ASSERT(IsValid()); return Infinite; }
// Empty and Infinite regions are always simple as well.
BOOL IsSimple() const { ASSERT(IsValid()); return (ComplexData == NULL); }
BOOL IsComplex() const { return !IsSimple(); }
Visibility GetRectVisibility( INT xMin, INT yMin, INT xMax, INT yMax, GpRect * rectClipped = NULL );
BOOL RegionVisible (DpRegion * region); BOOL RectVisible(INT xMin, INT yMin, INT xMax, INT yMax); BOOL RectVisible(GpRect * rect) { return RectVisible(rect->X, rect->Y, rect->X + rect->Width, rect->Y + rect->Height); } BOOL RectInside (INT xMin, INT yMin, INT xMax, INT yMax); BOOL PointInside(INT x, INT y);
GpStatus Fill(DpOutputSpan * output, GpRect * clipBounds) const;
HRGN GetHRgn() const;
BOOL GetOutlinePoints(DynPointArray& points, DynByteArray& types) const;
// If rects is NULL, return the number of rects in the region.
// Otherwise, assume rects is big enough to hold all the region rects
// and fill them in and return the number of rects filled in.
INT GetRects(GpRect * rects) const; INT GetRects(GpRectF * rects) const;
protected: VOID FreeData() { if (!Lazy) { GpFree(ComplexData); } ComplexData = NULL; Lazy = FALSE; }
GpStatus Set(DpRegionBuilder & regionBuilder);
// If rects is NULL, return the number of rects in the region.
// Otherwise, assume rects is big enough to hold all the region rects
// and fill them in and return the number of rects filled in.
INT GetRects(RECT * rects, BOOL clampToWin9xSize = FALSE) const;
GpStatus CompactAndOutput( INT yMin, INT yMax, INT * xCoords, INT numXCoords, DpRegionBuilder * regionBuilder, DynIntArray * combineCoords );
GpStatus Diff( DpRegion * region1, DpRegion * region2, BOOL set1 );
GpStatus XSpansAND ( DynIntArray * combineCoords, INT * xSpan1, INT numXCoords1, INT * xSpan2, INT numXCoords2 );
GpStatus XSpansOR ( DynIntArray * combineCoords, INT * xSpan1, INT numXCoords1, INT * xSpan2, INT numXCoords2 );
GpStatus XSpansXOR ( DynIntArray * combineCoords, INT * xSpan1, INT numXCoords1, INT * xSpan2, INT numXCoords2 );
GpStatus XSpansDIFF( DynIntArray * combineCoords, INT * xSpan1, INT numXCoords1, INT * xSpan2, INT numXCoords2 );
private: static INT GenerateUniqueness( ) { LONG_PTR Uid; static LONG_PTR gUniqueness = 0 ;
// Use InterlockedCompareExchangeFunction instead of
// InterlockedIncrement, because InterlockedIncrement doesn't work
// the way we need it to on Win9x.
do { Uid = gUniqueness; } while (CompareExchangeLong_Ptr(&gUniqueness, (Uid + 1), Uid) != Uid);
return (INT) (Uid + 1); }
};
class DpClipRegion : public DpRegion, public DpOutputSpan { public: enum Direction { NotEnumerating, TopLeftToBottomRight, TopRightToBottomLeft, BottomLeftToTopRight, BottomRightToTopLeft };
protected: DpOutputSpan * OutputClippedSpan; DpRegion OriginalRegion; // The old Region
BOOL ComplexClipDisabled; // Did we disable the complexClip
// enumeration stuff
Direction EnumDirection; INT EnumSpan;
private: friend class DriverPrint;
// This is a special method intended for use only by DriverPrint.
VOID StartBanding() { ASSERT(IsValid()); ASSERT(!Empty); // if clipping is empty, we shouldn't be banding
ASSERT(ComplexClipDisabled == FALSE); // Unless we are banding we can't
// have ComplexClip disabled
ASSERT(OriginalRegion.IsEmpty());
// Save the current information of the ClipRegion into OriginalRegion
// this is done by setting the complexData to our current complexData
ASSERT(OriginalRegion.ComplexData == NULL); OriginalRegion.ComplexData = ComplexData; OriginalRegion.Lazy = Lazy; OriginalRegion.Uniqueness = Uniqueness; OriginalRegion.Infinite = Infinite; OriginalRegion.Empty = Empty; OriginalRegion.XMin = XMin; OriginalRegion.YMin = YMin; OriginalRegion.XMax = XMax; OriginalRegion.YMax = YMax;
// We don't want to both regions to point to the same data
ComplexData = NULL; Lazy = FALSE;
// Create a lazy region so that we don't copy unless needed
Set(&OriginalRegion, TRUE); }
// This is a special method intended for use only by DriverPrint.
VOID EndBanding() { ASSERT(IsValid()); ASSERT(!OriginalRegion.IsEmpty()); ASSERT(!ComplexClipDisabled); // Make sure that we don't leave an opened
// DisableComplexClip call
// Free our Data, we can't be Lazy
FreeData(); ASSERT(ComplexData == NULL); ComplexData = OriginalRegion.ComplexData; Lazy = OriginalRegion.Lazy; Uniqueness = OriginalRegion.Uniqueness; Infinite = OriginalRegion.Infinite; Empty = OriginalRegion.Empty; XMin = OriginalRegion.XMin; YMin = OriginalRegion.YMin; XMax = OriginalRegion.XMax; YMax = OriginalRegion.YMax;
// We don't want to both regions to point to the same data
OriginalRegion.ComplexData = NULL; OriginalRegion.Lazy = FALSE; OriginalRegion.SetEmpty(); }
// This is a special method intended for use only by DriverPrint.
// We're relying on DriverPrint to restore the bounds back correctly
// after it's done with banding (by bracketing the banding with
// StartBanding and EndBanding calls).
// This works, even when there is complex clipping, because OutputSpan
// clips against the bounds before clipping against the complex region.
VOID SetBandBounds(GpRect & bandBounds, BOOL doIntersect = TRUE) { ASSERT(IsValid()); ASSERT(!OriginalRegion.IsEmpty()); ASSERT((bandBounds.Width > 0) && (bandBounds.Height > 0));
GpRect intersectedBounds;
if (!doIntersect) { // We are coming from DisableComplexClipping. We should not have
// any ComplexData.
ASSERT(!Lazy); ASSERT(ComplexData == NULL);
// The reason for not doing the intersection is that sometimes
// printing using a capped DPI which means that the bandBounds is
// using a different coordinate system than the original region.
intersectedBounds = bandBounds; } else { GpRect boundsOriginal(OriginalRegion.XMin, OriginalRegion.YMin, OriginalRegion.XMax - OriginalRegion.XMin, OriginalRegion.YMax - OriginalRegion.YMin); if (!GpRect::IntersectRect(intersectedBounds, bandBounds, boundsOriginal)) { // intersection is empty
SetEmpty(); return; } }
// Create a region for our band
DpRegion newRegion(&intersectedBounds);
// If we haven't disabled the complex clipping, then restore the original
// complexRegion and intersect with our current region
if (!ComplexClipDisabled) { // Get the original Clipping Region
Set(&OriginalRegion, TRUE);
// Intersect with our current band
And(&newRegion); } else { // We've disabled the complexClipping, Set the clipping to our band
Set(&newRegion); ASSERT(ComplexData == NULL); } }
// This is a special method intended for use only by DriverPrint.
// We're relying on DriverPrint to re-enable the complex clipping
// and to restore the bounds back correctly after it's done with banding
// (by bracketing the banding with StartBanding and EndBanding calls).
// Typically, when we disable complex clipping, it's because we are
// using a capped DPI, which means that the bandBounds is in a different
// resolution than the original clipping region.
VOID DisableComplexClipping(GpRect & bandBounds, BOOL doIntersect = FALSE) { ASSERT(IsValid()); ASSERT(!OriginalRegion.IsEmpty());
// Make sure that we call ourselves twice in a row
ASSERT(ComplexClipDisabled == FALSE);
ComplexClipDisabled = TRUE;
Set(&bandBounds); ASSERT(ComplexData == NULL); SetBandBounds(bandBounds, doIntersect); }
// This is a special method intended for use only by DriverPrint.
VOID ReEnableComplexClipping() { ASSERT(IsValid()); ASSERT(!OriginalRegion.IsEmpty()); ASSERT(ComplexClipDisabled);
ComplexClipDisabled = FALSE;
// Set the clipping back to the original state, make in Lazy
Set(&OriginalRegion, TRUE); }
public: DpClipRegion(BOOL empty = FALSE) : DpRegion(empty) { OutputClippedSpan = NULL; ComplexClipDisabled = FALSE; #if DBG
OriginalRegion.SetEmpty(); #endif
} DpClipRegion(const GpRect * rect) : DpRegion(rect) { OutputClippedSpan = NULL; ComplexClipDisabled = FALSE; #if DBG
OriginalRegion.SetEmpty(); #endif
} DpClipRegion(INT x, INT y, INT width, INT height) : DpRegion(x, y, width, height) { OutputClippedSpan = NULL; ComplexClipDisabled = FALSE; #if DBG
OriginalRegion.SetEmpty(); #endif
} DpClipRegion(const DpPath * path, const GpMatrix * matrix) : DpRegion (path, matrix) { OutputClippedSpan = NULL; ComplexClipDisabled = FALSE; #if DBG
OriginalRegion.SetEmpty(); #endif
} DpClipRegion(const DpRegion * region) : DpRegion(region) { OutputClippedSpan = NULL; ComplexClipDisabled = FALSE; #if DBG
OriginalRegion.SetEmpty(); #endif
} DpClipRegion(DpClipRegion & region) : DpRegion(region) { OutputClippedSpan = NULL; ComplexClipDisabled = FALSE; #if DBG
OriginalRegion.SetEmpty(); #endif
}
VOID StartEnumeration ( INT yMin, Direction direction = TopLeftToBottomRight );
// returns FALSE when done enumerating
// numRects going in is the number of rects in the buffer and going out
// is the number of rects that we filled.
BOOL Enumerate ( GpRect * rects, INT & numRects );
virtual GpStatus OutputSpan( INT y, INT xMin, INT xMax );
VOID InitClipping(DpOutputSpan * outputClippedSpan, INT yMin);
// EndClipping is here only for debugging (and the Rasterizer doesn't
// call it):
VOID EndClipping() {}
virtual BOOL IsValid() const { return TRUE; } };
#endif // _DPREGION_HPP
|