1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/regiong.cpp
3 // Purpose: generic wxRegion class
4 // Author: David Elliott
7 // Copyright: (c) 2004 David Elliott
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
19 #include "wx/region.h"
25 // ========================================================================
26 // Classes to interface with X.org code
27 // ========================================================================
31 wxCoord x1
, x2
, y1
, y2
;
32 } Box
, BOX
, BoxRec
, *BoxPtr
;
34 typedef struct REGION
*Region
;
39 // Default constructor initializes nothing
42 REGION(const wxRect
& rect
)
48 extents
.x2
= rect
.x
+ rect
.width
;
49 extents
.y2
= rect
.y
+ rect
.height
;
55 return i
< numRects
? rects
+ i
: NULL
;
62 static bool XOffsetRegion(
63 register Region pRegion
,
66 static bool XIntersectRegion(
68 Region reg2
, /* source regions */
69 register Region newReg
); /* destination Region */
70 static bool XUnionRegion(
72 Region reg2
, /* source regions */
73 Region newReg
); /* destination Region */
74 static bool XSubtractRegion(
77 register Region regD
);
78 static bool XXorRegion(Region sra
, Region srb
, Region dr
);
79 static bool XEmptyRegion(
81 static bool XEqualRegion(Region r1
, Region r2
);
82 static bool XPointInRegion(
85 static wxRegionContain
XRectInRegion(
86 register Region region
,
88 unsigned int rwidth
, unsigned int rheight
);
91 static Region
XCreateRegion(void);
92 static void miSetExtents (
94 static bool XDestroyRegion(Region r
);
95 static int miIntersectO (
103 static void miRegionCopy(
104 register Region dstrgn
,
105 register Region rgn
);
106 static int miCoalesce(
107 register Region pReg
, /* Region to coalesce */
108 int prevStart
, /* Index of start of previous band */
109 int curStart
); /* Index of start of current band */
110 static void miRegionOp(
111 register Region newReg
, /* Place to store result */
112 Region reg1
, /* First region in operation */
113 Region reg2
, /* 2d region in operation */
115 register Region pReg
,
121 wxCoord y2
), /* Function to call for over-
123 int (*nonOverlap1Func
)(
124 register Region pReg
,
128 register wxCoord y2
), /* Function to call for non-
129 * overlapping bands in region
131 int (*nonOverlap2Func
)(
132 register Region pReg
,
136 register wxCoord y2
)); /* Function to call for non-
137 * overlapping bands in region
139 static int miUnionNonO (
140 register Region pReg
,
144 register wxCoord y2
);
145 static int miUnionO (
146 register Region pReg
,
152 register wxCoord y2
);
153 static int miSubtractNonO1 (
154 register Region pReg
,
158 register wxCoord y2
);
159 static int miSubtractO (
160 register Region pReg
,
166 register wxCoord y2
);
174 // ========================================================================
176 // ========================================================================
178 class wxRegionRefData
: public wxGDIRefData
,
188 rects
= ( BOX
* )malloc( (unsigned) sizeof( BOX
));
195 wxRegionRefData(const wxPoint
& topLeft
, const wxPoint
& bottomRight
)
199 rects
= (BOX
*)malloc(sizeof(BOX
));
202 extents
.x1
= topLeft
.x
;
203 extents
.y1
= topLeft
.y
;
204 extents
.x2
= bottomRight
.x
;
205 extents
.y2
= bottomRight
.y
;
209 wxRegionRefData(const wxRect
& rect
)
213 rects
= (BOX
*)malloc(sizeof(BOX
));
217 wxRegionRefData(const wxRegionRefData
& refData
)
222 numRects
= refData
.numRects
;
223 rects
= (Box
*)malloc(numRects
*sizeof(Box
));
224 memcpy(rects
, refData
.rects
, numRects
*sizeof(Box
));
225 extents
= refData
.extents
;
228 virtual ~wxRegionRefData()
235 wxRegionRefData(const REGION
&);
238 // ========================================================================
240 // ========================================================================
241 //IMPLEMENT_DYNAMIC_CLASS(wxRegionGeneric, wxGDIObject)
243 #define M_REGIONDATA ((wxRegionRefData *)m_refData)
244 #define M_REGIONDATA_OF(rgn) ((wxRegionRefData *)(rgn.m_refData))
246 // ----------------------------------------------------------------------------
247 // wxRegionGeneric construction
248 // ----------------------------------------------------------------------------
250 wxRegionGeneric::wxRegionGeneric()
254 wxRegionGeneric::~wxRegionGeneric()
258 wxRegionGeneric::wxRegionGeneric(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
260 m_refData
= new wxRegionRefData(wxRect(x
,y
,w
,h
));
263 wxRegionGeneric::wxRegionGeneric(const wxRect
& rect
)
265 m_refData
= new wxRegionRefData(rect
);
268 wxRegionGeneric::wxRegionGeneric(const wxPoint
& topLeft
, const wxPoint
& bottomRight
)
270 m_refData
= new wxRegionRefData(topLeft
, bottomRight
);
273 wxRegionGeneric::wxRegionGeneric(const wxBitmap
& bmp
)
275 wxFAIL_MSG("NOT IMPLEMENTED: wxRegionGeneric::wxRegionGeneric(const wxBitmap& bmp)");
278 wxRegionGeneric::wxRegionGeneric(size_t n
, const wxPoint
*points
, wxPolygonFillMode fillStyle
)
280 wxFAIL_MSG("NOT IMPLEMENTED: wxRegionGeneric::wxRegionGeneric(size_t n, const wxPoint *points, wxPolygonFillMode fillStyle)");
283 wxRegionGeneric::wxRegionGeneric(const wxBitmap
& bmp
, const wxColour
& transp
, int tolerance
)
285 wxFAIL_MSG("NOT IMPLEMENTED: wxRegionGeneric::wxRegionGeneric(const wxBitmap& bmp, const wxColour& transp, int tolerance)");
288 void wxRegionGeneric::Clear()
292 m_refData
= new wxRegionRefData(wxRect(0,0,0,0));
295 wxGDIRefData
*wxRegionGeneric::CreateGDIRefData() const
297 return new wxRegionRefData
;
300 wxGDIRefData
*wxRegionGeneric::CloneGDIRefData(const wxGDIRefData
*data
) const
302 return new wxRegionRefData(*(wxRegionRefData
*)data
);
305 bool wxRegionGeneric::DoIsEqual(const wxRegion
& region
) const
307 return REGION::XEqualRegion(M_REGIONDATA
,M_REGIONDATA_OF(region
));
310 bool wxRegionGeneric::DoGetBox(wxCoord
& x
, wxCoord
& y
, wxCoord
&w
, wxCoord
&h
) const
316 REGION::XClipBox(M_REGIONDATA
,&rect
);
324 // ----------------------------------------------------------------------------
325 // wxRegionGeneric operations
326 // ----------------------------------------------------------------------------
328 bool wxRegionGeneric::DoUnionWithRect(const wxRect
& rect
)
330 if ( rect
.IsEmpty() )
338 return REGION::XUnionRegion(®ion
,M_REGIONDATA
,M_REGIONDATA
);
341 bool wxRegionGeneric::DoUnionWithRegion(const wxRegion
& region
)
344 return REGION::XUnionRegion(M_REGIONDATA_OF(region
),M_REGIONDATA
,M_REGIONDATA
);
347 bool wxRegionGeneric::DoIntersect(const wxRegion
& region
)
350 return REGION::XIntersectRegion(M_REGIONDATA_OF(region
),M_REGIONDATA
,M_REGIONDATA
);
353 bool wxRegionGeneric::DoSubtract(const wxRegion
& region
)
355 if ( region
.IsEmpty() )
361 return REGION::XSubtractRegion(M_REGIONDATA_OF(region
),M_REGIONDATA
,M_REGIONDATA
);
364 bool wxRegionGeneric::DoXor(const wxRegion
& region
)
367 return REGION::XXorRegion(M_REGIONDATA_OF(region
),M_REGIONDATA
,M_REGIONDATA
);
370 bool wxRegionGeneric::DoOffset(wxCoord x
, wxCoord y
)
373 return REGION::XOffsetRegion(M_REGIONDATA
, x
, y
);
376 // ----------------------------------------------------------------------------
377 // wxRegionGeneric comparison
378 // ----------------------------------------------------------------------------
380 bool wxRegionGeneric::IsEmpty() const
383 return REGION::XEmptyRegion(M_REGIONDATA
);
386 // Does the region contain the point (x,y)?
387 wxRegionContain
wxRegionGeneric::DoContainsPoint(wxCoord x
, wxCoord y
) const
390 return REGION::XPointInRegion(M_REGIONDATA
,x
,y
) ? wxInRegion
: wxOutRegion
;
393 // Does the region contain the rectangle rect?
394 wxRegionContain
wxRegionGeneric::DoContainsRect(const wxRect
& rect
) const
397 return REGION::XRectInRegion(M_REGIONDATA
,rect
.x
,rect
.y
,rect
.width
,rect
.height
);
400 // ========================================================================
401 // wxRegionIteratorGeneric
402 // ========================================================================
403 //IMPLEMENT_DYNAMIC_CLASS(wxRegionIteratorGeneric,wxObject)
405 wxRegionIteratorGeneric::wxRegionIteratorGeneric()
410 wxRegionIteratorGeneric::wxRegionIteratorGeneric(const wxRegionGeneric
& region
)
416 wxRegionIteratorGeneric::wxRegionIteratorGeneric(const wxRegionIteratorGeneric
& iterator
)
417 : m_region(iterator
.m_region
)
419 m_current
= iterator
.m_current
;
422 void wxRegionIteratorGeneric::Reset(const wxRegionGeneric
& region
)
428 bool wxRegionIteratorGeneric::HaveRects() const
430 return M_REGIONDATA_OF(m_region
)->GetBox(m_current
);
433 wxRegionIteratorGeneric
& wxRegionIteratorGeneric::operator++()
439 wxRegionIteratorGeneric
wxRegionIteratorGeneric::operator++(int)
441 wxRegionIteratorGeneric
copy(*this);
446 wxRect
wxRegionIteratorGeneric::GetRect() const
448 wxASSERT(m_region
.m_refData
);
449 const Box
*box
= M_REGIONDATA_OF(m_region
)->GetBox(m_current
);
459 long wxRegionIteratorGeneric::GetX() const
461 wxASSERT(m_region
.m_refData
);
462 const Box
*box
= M_REGIONDATA_OF(m_region
)->GetBox(m_current
);
467 long wxRegionIteratorGeneric::GetY() const
469 wxASSERT(m_region
.m_refData
);
470 const Box
*box
= M_REGIONDATA_OF(m_region
)->GetBox(m_current
);
475 long wxRegionIteratorGeneric::GetW() const
477 wxASSERT(m_region
.m_refData
);
478 const Box
*box
= M_REGIONDATA_OF(m_region
)->GetBox(m_current
);
480 return box
->x2
- box
->x1
;
483 long wxRegionIteratorGeneric::GetH() const
485 wxASSERT(m_region
.m_refData
);
486 const Box
*box
= M_REGIONDATA_OF(m_region
)->GetBox(m_current
);
488 return box
->y2
- box
->y1
;
491 wxRegionIteratorGeneric::~wxRegionIteratorGeneric()
496 // ========================================================================
497 // The guts (from X.org)
498 // ========================================================================
500 /************************************************************************
502 Copyright 1987, 1988, 1998 The Open Group
504 Permission to use, copy, modify, distribute, and sell this software and its
505 documentation for any purpose is hereby granted without fee, provided that
506 the above copyright notice appear in all copies and that both that
507 copyright notice and this permission notice appear in supporting
510 The above copyright notice and this permission notice shall be included in
511 all copies or substantial portions of the Software.
513 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
514 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
515 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
516 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
517 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
518 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
520 Except as contained in this notice, the name of The Open Group shall not be
521 used in advertising or otherwise to promote the sale, use or other dealings
522 in this Software without prior written authorization from The Open Group.
525 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
529 Permission to use, copy, modify, and distribute this software and its
530 documentation for any purpose and without fee is hereby granted,
531 provided that the above copyright notice appear in all copies and that
532 both that copyright notice and this permission notice appear in
533 supporting documentation, and that the name of Digital not be
534 used in advertising or publicity pertaining to distribution of the
535 software without specific, written prior permission.
537 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
538 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
539 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
540 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
541 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
542 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
545 ************************************************************************/
547 /* 1 if two BOXs overlap.
548 * 0 if two BOXs do not overlap.
549 * Remember, x2 and y2 are not in the region
551 #define EXTENTCHECK(r1, r2) \
552 ((r1)->x2 > (r2)->x1 && \
553 (r1)->x1 < (r2)->x2 && \
554 (r1)->y2 > (r2)->y1 && \
558 * Check to see if there is enough memory in the present region.
560 #define MEMCHECK(reg, rect, firstrect){\
561 if ((reg)->numRects >= ((reg)->size - 1)){\
562 (firstrect) = (BOX *) realloc \
563 ((char *)(firstrect), (unsigned) (2 * (sizeof(BOX)) * ((reg)->size)));\
564 if ((firstrect) == 0)\
567 (rect) = &(firstrect)[(reg)->numRects];\
571 #define EMPTY_REGION(pReg) pReg->numRects = 0
573 #define REGION_NOT_EMPTY(pReg) pReg->numRects
575 #define INBOX(r, x, y) \
576 ( ( ((r).x2 > x)) && \
577 ( ((r).x1 <= x)) && \
582 * The functions in this file implement the Region abstraction, similar to one
583 * used in the X11 sample server. A Region is simply an area, as the name
584 * implies, and is implemented as a "y-x-banded" array of rectangles. To
585 * explain: Each Region is made up of a certain number of rectangles sorted
586 * by y coordinate first, and then by x coordinate.
588 * Furthermore, the rectangles are banded such that every rectangle with a
589 * given upper-left y coordinate (y1) will have the same lower-right y
590 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
591 * will span the entire vertical distance of the band. This means that some
592 * areas that could be merged into a taller rectangle will be represented as
593 * several shorter rectangles to account for shorter rectangles to its left
594 * or right but within its "vertical scope".
596 * An added constraint on the rectangles is that they must cover as much
597 * horizontal area as possible. E.g. no two rectangles in a band are allowed
600 * Whenever possible, bands will be merged together to cover a greater vertical
601 * distance (and thus reduce the number of rectangles). Two bands can be merged
602 * only if the bottom of one touches the top of the other and they have
603 * rectangles in the same places (of the same width, of course). This maintains
604 * the y-x-banding that's so nice to have...
607 /* Create a new empty region */
608 Region
REGION::XCreateRegion(void)
610 Region temp
= new REGION
;
613 return (Region
) NULL
;
615 temp
->rects
= ( BOX
* )malloc( (unsigned) sizeof( BOX
));
620 return (Region
) NULL
;
623 temp
->extents
.x1
= 0;
624 temp
->extents
.y1
= 0;
625 temp
->extents
.x2
= 0;
626 temp
->extents
.y2
= 0;
631 bool REGION::XClipBox(Region r
, wxRect
*rect
)
633 rect
->x
= r
->extents
.x1
;
634 rect
->y
= r
->extents
.y1
;
635 rect
->width
= r
->extents
.x2
- r
->extents
.x1
;
636 rect
->height
= r
->extents
.y2
- r
->extents
.y1
;
641 *-----------------------------------------------------------------------
643 * Reset the extents of a region to what they should be. Called by
644 * miSubtract and miIntersect b/c they can't figure it out along the
645 * way or do so easily, as miUnion can.
651 * The region's 'extents' structure is overwritten.
653 *-----------------------------------------------------------------------
656 miSetExtents (Region pReg
)
658 register BoxPtr pBox
,
662 if (pReg
->numRects
== 0)
664 pReg
->extents
.x1
= 0;
665 pReg
->extents
.y1
= 0;
666 pReg
->extents
.x2
= 0;
667 pReg
->extents
.y2
= 0;
671 pExtents
= &pReg
->extents
;
673 pBoxEnd
= &pBox
[pReg
->numRects
- 1];
676 * Since pBox is the first rectangle in the region, it must have the
677 * smallest y1 and since pBoxEnd is the last rectangle in the region,
678 * it must have the largest y2, because of banding. Initialize x1 and
679 * x2 from pBox and pBoxEnd, resp., as good things to initialize them
682 pExtents
->x1
= pBox
->x1
;
683 pExtents
->y1
= pBox
->y1
;
684 pExtents
->x2
= pBoxEnd
->x2
;
685 pExtents
->y2
= pBoxEnd
->y2
;
687 wxASSERT_LEVEL_2(pExtents
->y1
< pExtents
->y2
);
688 while (pBox
<= pBoxEnd
)
690 if (pBox
->x1
< pExtents
->x1
)
692 pExtents
->x1
= pBox
->x1
;
694 if (pBox
->x2
> pExtents
->x2
)
696 pExtents
->x2
= pBox
->x2
;
700 wxASSERT_LEVEL_2(pExtents
->x1
< pExtents
->x2
);
707 free( (char *) r
->rects
);
712 /* TranslateRegion(pRegion, x, y)
719 register Region pRegion
,
726 pbox
= pRegion
->rects
;
727 nbox
= pRegion
->numRects
;
737 pRegion
->extents
.x1
+= x
;
738 pRegion
->extents
.x2
+= x
;
739 pRegion
->extents
.y1
+= y
;
740 pRegion
->extents
.y2
+= y
;
744 /*======================================================================
745 * Region Intersection
746 *====================================================================*/
748 *-----------------------------------------------------------------------
750 * Handle an overlapping band for miIntersect.
756 * Rectangles may be added to the region.
758 *-----------------------------------------------------------------------
763 register Region pReg
,
773 register BoxPtr pNextRect
;
775 pNextRect
= &pReg
->rects
[pReg
->numRects
];
777 while ((r1
!= r1End
) && (r2
!= r2End
))
779 x1
= wxMax(r1
->x1
,r2
->x1
);
780 x2
= wxMin(r1
->x2
,r2
->x2
);
783 * If there's any overlap between the two rectangles, add that
784 * overlap to the new region.
785 * There's no need to check for subsumption because the only way
786 * such a need could arise is if some region has two rectangles
787 * right next to each other. Since that should never happen...
791 wxASSERT_LEVEL_2(y1
<y2
);
793 MEMCHECK(pReg
, pNextRect
, pReg
->rects
);
800 wxASSERT_LEVEL_2(pReg
->numRects
<= pReg
->size
);
804 * Need to advance the pointers. Shift the one that extends
805 * to the right the least, since the other still has a chance to
806 * overlap with that region's next rectangle, if you see what I mean.
812 else if (r2
->x2
< r1
->x2
)
828 Region reg2
, /* source regions */
829 register Region newReg
) /* destination Region */
831 /* check for trivial reject */
832 if ( (!(reg1
->numRects
)) || (!(reg2
->numRects
)) ||
833 (!EXTENTCHECK(®1
->extents
, ®2
->extents
)))
834 newReg
->numRects
= 0;
836 miRegionOp (newReg
, reg1
, reg2
,
837 miIntersectO
, NULL
, NULL
);
840 * Can't alter newReg's extents before we call miRegionOp because
841 * it might be one of the source regions and miRegionOp depends
842 * on the extents of those regions being the same. Besides, this
843 * way there's no checking against rectangles that will be nuked
844 * due to coalescing, so we have to examine fewer rectangles.
846 miSetExtents(newReg
);
852 register Region dstrgn
,
856 if (dstrgn
!= rgn
) /* don't want to copy to itself */
858 if (dstrgn
->size
< rgn
->numRects
)
862 BOX
*prevRects
= dstrgn
->rects
;
864 dstrgn
->rects
= (BOX
*)
865 realloc((char *) dstrgn
->rects
,
866 (unsigned) rgn
->numRects
* (sizeof(BOX
)));
873 dstrgn
->size
= rgn
->numRects
;
875 dstrgn
->numRects
= rgn
->numRects
;
876 dstrgn
->extents
.x1
= rgn
->extents
.x1
;
877 dstrgn
->extents
.y1
= rgn
->extents
.y1
;
878 dstrgn
->extents
.x2
= rgn
->extents
.x2
;
879 dstrgn
->extents
.y2
= rgn
->extents
.y2
;
881 memcpy((char *) dstrgn
->rects
, (char *) rgn
->rects
,
882 (int) (rgn
->numRects
* sizeof(BOX
)));
886 /*======================================================================
887 * Generic Region Operator
888 *====================================================================*/
891 *-----------------------------------------------------------------------
893 * Attempt to merge the boxes in the current band with those in the
894 * previous one. Used only by miRegionOp.
897 * The new index for the previous band.
900 * If coalescing takes place:
901 * - rectangles in the previous band will have their y2 fields
903 * - pReg->numRects will be decreased.
905 *-----------------------------------------------------------------------
910 register Region pReg
, /* Region to coalesce */
911 int prevStart
, /* Index of start of previous band */
912 int curStart
) /* Index of start of current band */
914 register BoxPtr pPrevBox
; /* Current box in previous band */
915 register BoxPtr pCurBox
; /* Current box in current band */
916 register BoxPtr pRegEnd
; /* End of region */
917 int curNumRects
; /* Number of rectangles in current
919 int prevNumRects
; /* Number of rectangles in previous
921 int bandY1
; /* Y1 coordinate for current band */
923 pRegEnd
= &pReg
->rects
[pReg
->numRects
];
925 pPrevBox
= &pReg
->rects
[prevStart
];
926 prevNumRects
= curStart
- prevStart
;
929 * Figure out how many rectangles are in the current band. Have to do
930 * this because multiple bands could have been added in miRegionOp
931 * at the end when one region has been exhausted.
933 pCurBox
= &pReg
->rects
[curStart
];
934 bandY1
= pCurBox
->y1
;
935 for (curNumRects
= 0;
936 (pCurBox
!= pRegEnd
) && (pCurBox
->y1
== bandY1
);
942 if (pCurBox
!= pRegEnd
)
945 * If more than one band was added, we have to find the start
946 * of the last band added so the next coalescing job can start
947 * at the right place... (given when multiple bands are added,
948 * this may be pointless -- see above).
951 while (pRegEnd
[-1].y1
== pRegEnd
->y1
)
955 curStart
= pRegEnd
- pReg
->rects
;
956 pRegEnd
= pReg
->rects
+ pReg
->numRects
;
959 if ((curNumRects
== prevNumRects
) && (curNumRects
!= 0))
961 pCurBox
-= curNumRects
;
963 * The bands may only be coalesced if the bottom of the previous
964 * matches the top scanline of the current.
966 if (pPrevBox
->y2
== pCurBox
->y1
)
969 * Make sure the bands have boxes in the same places. This
970 * assumes that boxes have been added in such a way that they
971 * cover the most area possible. I.e. two boxes in a band must
972 * have some horizontal space between them.
976 if ((pPrevBox
->x1
!= pCurBox
->x1
) ||
977 (pPrevBox
->x2
!= pCurBox
->x2
))
980 * The bands don't line up so they can't be coalesced.
987 } while (prevNumRects
!= 0);
989 pReg
->numRects
-= curNumRects
;
990 pCurBox
-= curNumRects
;
991 pPrevBox
-= curNumRects
;
994 * The bands may be merged, so set the bottom y of each box
995 * in the previous band to that of the corresponding box in
1000 pPrevBox
->y2
= pCurBox
->y2
;
1004 } while (curNumRects
!= 0);
1007 * If only one band was added to the region, we have to backup
1008 * curStart to the start of the previous band.
1010 * If more than one band was added to the region, copy the
1011 * other bands down. The assumption here is that the other bands
1012 * came from the same region as the current one and no further
1013 * coalescing can be done on them since it's all been done
1014 * already... curStart is already in the right place.
1016 if (pCurBox
== pRegEnd
)
1018 curStart
= prevStart
;
1024 *pPrevBox
++ = *pCurBox
++;
1025 } while (pCurBox
!= pRegEnd
);
1034 *-----------------------------------------------------------------------
1036 * Apply an operation to two regions. Called by miUnion, miInverse,
1037 * miSubtract, miIntersect...
1043 * The new region is overwritten.
1046 * The idea behind this function is to view the two regions as sets.
1047 * Together they cover a rectangle of area that this function divides
1048 * into horizontal bands where points are covered only by one region
1049 * or by both. For the first case, the nonOverlapFunc is called with
1050 * each the band and the band's upper and lower extents. For the
1051 * second, the overlapFunc is called to process the entire band. It
1052 * is responsible for clipping the rectangles in the band, though
1053 * this function provides the boundaries.
1054 * At the end of each band, the new region is coalesced, if possible,
1055 * to reduce the number of rectangles in the region.
1057 *-----------------------------------------------------------------------
1062 register Region newReg
, /* Place to store result */
1063 Region reg1
, /* First region in operation */
1064 Region reg2
, /* 2d region in operation */
1066 register Region pReg
,
1072 wxCoord y2
), /* Function to call for over-
1074 int (*nonOverlap1Func
)(
1075 register Region pReg
,
1078 register wxCoord y1
,
1079 register wxCoord y2
), /* Function to call for non-
1080 * overlapping bands in region
1082 int (*nonOverlap2Func
)(
1083 register Region pReg
,
1086 register wxCoord y1
,
1087 register wxCoord y2
)) /* Function to call for non-
1088 * overlapping bands in region
1091 register BoxPtr r1
; /* Pointer into first region */
1092 register BoxPtr r2
; /* Pointer into 2d region */
1093 BoxPtr r1End
; /* End of 1st region */
1094 BoxPtr r2End
; /* End of 2d region */
1095 register wxCoord ybot
; /* Bottom of intersection */
1096 register wxCoord ytop
; /* Top of intersection */
1097 BoxPtr oldRects
; /* Old rects for newReg */
1098 int prevBand
; /* Index of start of
1099 * previous band in newReg */
1100 int curBand
; /* Index of start of current
1102 register BoxPtr r1BandEnd
; /* End of current band in r1 */
1103 register BoxPtr r2BandEnd
; /* End of current band in r2 */
1104 wxCoord top
; /* Top of non-overlapping
1106 wxCoord bot
; /* Bottom of non-overlapping
1111 * set r1, r2, r1End and r2End appropriately, preserve the important
1112 * parts of the destination region until the end in case it's one of
1113 * the two source regions, then mark the "new" region empty, allocating
1114 * another array of rectangles for it to use.
1118 r1End
= r1
+ reg1
->numRects
;
1119 r2End
= r2
+ reg2
->numRects
;
1121 oldRects
= newReg
->rects
;
1123 EMPTY_REGION(newReg
);
1126 * Allocate a reasonable number of rectangles for the new region. The idea
1127 * is to allocate enough so the individual functions don't need to
1128 * reallocate and copy the array, which is time consuming, yet we don't
1129 * have to worry about using too much memory. I hope to be able to
1130 * nuke the realloc() at the end of this function eventually.
1132 newReg
->size
= wxMax(reg1
->numRects
,reg2
->numRects
) * 2;
1134 newReg
->rects
= (BoxPtr
)malloc((unsigned) (sizeof(BoxRec
) * newReg
->size
));
1143 * Initialize ybot and ytop.
1144 * In the upcoming loop, ybot and ytop serve different functions depending
1145 * on whether the band being handled is an overlapping or non-overlapping
1147 * In the case of a non-overlapping band (only one of the regions
1148 * has points in the band), ybot is the bottom of the most recent
1149 * intersection and thus clips the top of the rectangles in that band.
1150 * ytop is the top of the next intersection between the two regions and
1151 * serves to clip the bottom of the rectangles in the current band.
1152 * For an overlapping band (where the two regions intersect), ytop clips
1153 * the top of the rectangles of both regions and ybot clips the bottoms.
1155 if (reg1
->extents
.y1
< reg2
->extents
.y1
)
1156 ybot
= reg1
->extents
.y1
;
1158 ybot
= reg2
->extents
.y1
;
1161 * prevBand serves to mark the start of the previous band so rectangles
1162 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1163 * In the beginning, there is no previous band, so prevBand == curBand
1164 * (curBand is set later on, of course, but the first band will always
1165 * start at index 0). prevBand and curBand must be indices because of
1166 * the possible expansion, and resultant moving, of the new region's
1167 * array of rectangles.
1173 curBand
= newReg
->numRects
;
1176 * This algorithm proceeds one source-band (as opposed to a
1177 * destination band, which is determined by where the two regions
1178 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1179 * rectangle after the last one in the current band for their
1180 * respective regions.
1183 while ((r1BandEnd
!= r1End
) && (r1BandEnd
->y1
== r1
->y1
))
1189 while ((r2BandEnd
!= r2End
) && (r2BandEnd
->y1
== r2
->y1
))
1195 * First handle the band that doesn't intersect, if any.
1197 * Note that attention is restricted to one band in the
1198 * non-intersecting region at once, so if a region has n
1199 * bands between the current position and the next place it overlaps
1200 * the other, this entire loop will be passed through n times.
1202 if (r1
->y1
< r2
->y1
)
1204 top
= wxMax(r1
->y1
,ybot
);
1205 bot
= wxMin(r1
->y2
,r2
->y1
);
1207 if ((top
!= bot
) && (nonOverlap1Func
!= NULL
))
1209 (* nonOverlap1Func
) (newReg
, r1
, r1BandEnd
, top
, bot
);
1214 else if (r2
->y1
< r1
->y1
)
1216 top
= wxMax(r2
->y1
,ybot
);
1217 bot
= wxMin(r2
->y2
,r1
->y1
);
1219 if ((top
!= bot
) && (nonOverlap2Func
!= NULL
))
1221 (* nonOverlap2Func
) (newReg
, r2
, r2BandEnd
, top
, bot
);
1232 * If any rectangles got added to the region, try and coalesce them
1233 * with rectangles from the previous band. Note we could just do
1234 * this test in miCoalesce, but some machines incur a not
1235 * inconsiderable cost for function calls, so...
1237 if (newReg
->numRects
!= curBand
)
1239 prevBand
= miCoalesce (newReg
, prevBand
, curBand
);
1243 * Now see if we've hit an intersecting band. The two bands only
1244 * intersect if ybot > ytop
1246 ybot
= wxMin(r1
->y2
, r2
->y2
);
1247 curBand
= newReg
->numRects
;
1250 (* overlapFunc
) (newReg
, r1
, r1BandEnd
, r2
, r2BandEnd
, ytop
, ybot
);
1254 if (newReg
->numRects
!= curBand
)
1256 prevBand
= miCoalesce (newReg
, prevBand
, curBand
);
1260 * If we've finished with a band (y2 == ybot) we skip forward
1261 * in the region to the next band.
1271 } while ((r1
!= r1End
) && (r2
!= r2End
));
1274 * Deal with whichever region still has rectangles left.
1276 curBand
= newReg
->numRects
;
1279 if (nonOverlap1Func
!= NULL
)
1284 while ((r1BandEnd
< r1End
) && (r1BandEnd
->y1
== r1
->y1
))
1288 (* nonOverlap1Func
) (newReg
, r1
, r1BandEnd
,
1289 wxMax(r1
->y1
,ybot
), r1
->y2
);
1291 } while (r1
!= r1End
);
1294 else if ((r2
!= r2End
) && (nonOverlap2Func
!= NULL
))
1299 while ((r2BandEnd
< r2End
) && (r2BandEnd
->y1
== r2
->y1
))
1303 (* nonOverlap2Func
) (newReg
, r2
, r2BandEnd
,
1304 wxMax(r2
->y1
,ybot
), r2
->y2
);
1306 } while (r2
!= r2End
);
1309 if (newReg
->numRects
!= curBand
)
1311 (void) miCoalesce (newReg
, prevBand
, curBand
);
1315 * A bit of cleanup. To keep regions from growing without bound,
1316 * we shrink the array of rectangles to match the new number of
1317 * rectangles in the region. This never goes to 0, however...
1319 * Only do this stuff if the number of rectangles allocated is more than
1320 * twice the number of rectangles in the region (a simple optimization...).
1322 if (newReg
->numRects
< (newReg
->size
>> 1))
1324 if (REGION_NOT_EMPTY(newReg
))
1326 BoxPtr prev_rects
= newReg
->rects
;
1327 newReg
->size
= newReg
->numRects
;
1328 newReg
->rects
= (BoxPtr
) realloc ((char *) newReg
->rects
,
1329 (unsigned) (sizeof(BoxRec
) * newReg
->size
));
1330 if (! newReg
->rects
)
1331 newReg
->rects
= prev_rects
;
1336 * No point in doing the extra work involved in an realloc if
1337 * the region is empty
1340 free((char *) newReg
->rects
);
1341 newReg
->rects
= (BoxPtr
) malloc(sizeof(BoxRec
));
1344 free ((char *) oldRects
);
1348 /*======================================================================
1350 *====================================================================*/
1353 *-----------------------------------------------------------------------
1355 * Handle a non-overlapping band for the union operation. Just
1356 * Adds the rectangles into the region. Doesn't have to check for
1357 * subsumption or anything.
1363 * pReg->numRects is incremented and the final rectangles overwritten
1364 * with the rectangles we're passed.
1366 *-----------------------------------------------------------------------
1371 register Region pReg
,
1374 register wxCoord y1
,
1375 register wxCoord y2
)
1377 register BoxPtr pNextRect
;
1379 pNextRect
= &pReg
->rects
[pReg
->numRects
];
1381 wxASSERT_LEVEL_2(y1
< y2
);
1385 wxASSERT_LEVEL_2(r
->x1
< r
->x2
);
1386 MEMCHECK(pReg
, pNextRect
, pReg
->rects
);
1387 pNextRect
->x1
= r
->x1
;
1389 pNextRect
->x2
= r
->x2
;
1391 pReg
->numRects
+= 1;
1394 wxASSERT_LEVEL_2(pReg
->numRects
<=pReg
->size
);
1397 return 0; /* lint */
1402 *-----------------------------------------------------------------------
1404 * Handle an overlapping band for the union operation. Picks the
1405 * left-most rectangle each time and merges it into the region.
1411 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1414 *-----------------------------------------------------------------------
1420 register Region pReg
,
1425 register wxCoord y1
,
1426 register wxCoord y2
)
1428 register BoxPtr pNextRect
;
1430 pNextRect
= &pReg
->rects
[pReg
->numRects
];
1432 #define MERGERECT(r) \
1433 if ((pReg->numRects != 0) && \
1434 (pNextRect[-1].y1 == y1) && \
1435 (pNextRect[-1].y2 == y2) && \
1436 (pNextRect[-1].x2 >= r->x1)) \
1438 if (pNextRect[-1].x2 < r->x2) \
1440 pNextRect[-1].x2 = r->x2; \
1441 wxASSERT_LEVEL_2(pNextRect[-1].x1<pNextRect[-1].x2); \
1446 MEMCHECK(pReg, pNextRect, pReg->rects); \
1447 pNextRect->y1 = y1; \
1448 pNextRect->y2 = y2; \
1449 pNextRect->x1 = r->x1; \
1450 pNextRect->x2 = r->x2; \
1451 pReg->numRects += 1; \
1454 wxASSERT_LEVEL_2(pReg->numRects<=pReg->size);\
1457 wxASSERT_LEVEL_2 (y1
<y2
);
1458 while ((r1
!= r1End
) && (r2
!= r2End
))
1460 if (r1
->x1
< r2
->x1
)
1475 } while (r1
!= r1End
);
1477 else while (r2
!= r2End
)
1481 return 0; /* lint */
1487 Region reg2
, /* source regions */
1488 Region newReg
) /* destination Region */
1490 /* checks all the simple cases */
1493 * Region 1 and 2 are the same or region 1 is empty
1495 if ( (reg1
== reg2
) || (!(reg1
->numRects
)) )
1498 miRegionCopy(newReg
, reg2
);
1503 * if nothing to union (region 2 empty)
1505 if (!(reg2
->numRects
))
1508 miRegionCopy(newReg
, reg1
);
1513 * Region 1 completely subsumes region 2
1515 if ((reg1
->numRects
== 1) &&
1516 (reg1
->extents
.x1
<= reg2
->extents
.x1
) &&
1517 (reg1
->extents
.y1
<= reg2
->extents
.y1
) &&
1518 (reg1
->extents
.x2
>= reg2
->extents
.x2
) &&
1519 (reg1
->extents
.y2
>= reg2
->extents
.y2
))
1522 miRegionCopy(newReg
, reg1
);
1527 * Region 2 completely subsumes region 1
1529 if ((reg2
->numRects
== 1) &&
1530 (reg2
->extents
.x1
<= reg1
->extents
.x1
) &&
1531 (reg2
->extents
.y1
<= reg1
->extents
.y1
) &&
1532 (reg2
->extents
.x2
>= reg1
->extents
.x2
) &&
1533 (reg2
->extents
.y2
>= reg1
->extents
.y2
))
1536 miRegionCopy(newReg
, reg2
);
1540 miRegionOp (newReg
, reg1
, reg2
, miUnionO
,
1541 miUnionNonO
, miUnionNonO
);
1543 newReg
->extents
.x1
= wxMin(reg1
->extents
.x1
, reg2
->extents
.x1
);
1544 newReg
->extents
.y1
= wxMin(reg1
->extents
.y1
, reg2
->extents
.y1
);
1545 newReg
->extents
.x2
= wxMax(reg1
->extents
.x2
, reg2
->extents
.x2
);
1546 newReg
->extents
.y2
= wxMax(reg1
->extents
.y2
, reg2
->extents
.y2
);
1551 /*======================================================================
1552 * Region Subtraction
1553 *====================================================================*/
1556 *-----------------------------------------------------------------------
1558 * Deal with non-overlapping band for subtraction. Any parts from
1559 * region 2 we discard. Anything from region 1 we add to the region.
1565 * pReg may be affected.
1567 *-----------------------------------------------------------------------
1572 register Region pReg
,
1575 register wxCoord y1
,
1576 register wxCoord y2
)
1578 register BoxPtr pNextRect
;
1580 pNextRect
= &pReg
->rects
[pReg
->numRects
];
1582 wxASSERT_LEVEL_2(y1
<y2
);
1586 wxASSERT_LEVEL_2(r
->x1
<r
->x2
);
1587 MEMCHECK(pReg
, pNextRect
, pReg
->rects
);
1588 pNextRect
->x1
= r
->x1
;
1590 pNextRect
->x2
= r
->x2
;
1592 pReg
->numRects
+= 1;
1595 wxASSERT_LEVEL_2(pReg
->numRects
<= pReg
->size
);
1599 return 0; /* lint */
1603 *-----------------------------------------------------------------------
1605 * Overlapping band subtraction. x1 is the left-most point not yet
1612 * pReg may have rectangles added to it.
1614 *-----------------------------------------------------------------------
1619 register Region pReg
,
1624 register wxCoord y1
,
1625 register wxCoord y2
)
1627 register BoxPtr pNextRect
;
1632 wxASSERT_LEVEL_2(y1
<y2
);
1633 pNextRect
= &pReg
->rects
[pReg
->numRects
];
1635 while ((r1
!= r1End
) && (r2
!= r2End
))
1640 * Subtrahend missed the boat: go to next subtrahend.
1644 else if (r2
->x1
<= x1
)
1647 * Subtrahend precedes minuend: nuke left edge of minuend.
1653 * Minuend completely covered: advance to next minuend and
1654 * reset left fence to edge of new minuend.
1663 * Subtrahend now used up since it doesn't extend beyond
1669 else if (r2
->x1
< r1
->x2
)
1672 * Left part of subtrahend covers part of minuend: add uncovered
1673 * part of minuend to region and skip to next subtrahend.
1675 wxASSERT_LEVEL_2(x1
<r2
->x1
);
1676 MEMCHECK(pReg
, pNextRect
, pReg
->rects
);
1679 pNextRect
->x2
= r2
->x1
;
1681 pReg
->numRects
+= 1;
1684 wxASSERT_LEVEL_2(pReg
->numRects
<=pReg
->size
);
1690 * Minuend used up: advance to new...
1699 * Subtrahend used up
1707 * Minuend used up: add any remaining piece before advancing.
1711 MEMCHECK(pReg
, pNextRect
, pReg
->rects
);
1714 pNextRect
->x2
= r1
->x2
;
1716 pReg
->numRects
+= 1;
1718 wxASSERT_LEVEL_2(pReg
->numRects
<=pReg
->size
);
1727 * Add remaining minuend rectangles to region.
1731 wxASSERT_LEVEL_2(x1
<r1
->x2
);
1732 MEMCHECK(pReg
, pNextRect
, pReg
->rects
);
1735 pNextRect
->x2
= r1
->x2
;
1737 pReg
->numRects
+= 1;
1740 wxASSERT_LEVEL_2(pReg
->numRects
<=pReg
->size
);
1748 return 0; /* lint */
1752 *-----------------------------------------------------------------------
1754 * Subtract regS from regM and leave the result in regD.
1755 * S stands for subtrahend, M for minuend and D for difference.
1761 * regD is overwritten.
1763 *-----------------------------------------------------------------------
1766 bool REGION::XSubtractRegion(Region regM
, Region regS
, register Region regD
)
1768 /* check for trivial reject */
1769 if ( (!(regM
->numRects
)) || (!(regS
->numRects
)) ||
1770 (!EXTENTCHECK(®M
->extents
, ®S
->extents
)) )
1772 miRegionCopy(regD
, regM
);
1776 miRegionOp (regD
, regM
, regS
, miSubtractO
,
1777 miSubtractNonO1
, NULL
);
1780 * Can't alter newReg's extents before we call miRegionOp because
1781 * it might be one of the source regions and miRegionOp depends
1782 * on the extents of those regions being the unaltered. Besides, this
1783 * way there's no checking against rectangles that will be nuked
1784 * due to coalescing, so we have to examine fewer rectangles.
1786 miSetExtents (regD
);
1790 bool REGION::XXorRegion(Region sra
, Region srb
, Region dr
)
1792 Region tra
= XCreateRegion();
1794 wxCHECK_MSG( tra
, false, wxT("region not created") );
1796 Region trb
= XCreateRegion();
1798 wxCHECK_MSG( trb
, false, wxT("region not created") );
1800 (void) XSubtractRegion(sra
,srb
,tra
);
1801 (void) XSubtractRegion(srb
,sra
,trb
);
1802 (void) XUnionRegion(tra
,trb
,dr
);
1803 XDestroyRegion(tra
);
1804 XDestroyRegion(trb
);
1809 * Check to see if the region is empty. Assumes a region is passed
1812 bool REGION::XEmptyRegion(Region r
)
1814 if( r
->numRects
== 0 ) return true;
1819 * Check to see if two regions are equal
1821 bool REGION::XEqualRegion(Region r1
, Region r2
)
1825 if( r1
->numRects
!= r2
->numRects
) return false;
1826 else if( r1
->numRects
== 0 ) return true;
1827 else if ( r1
->extents
.x1
!= r2
->extents
.x1
) return false;
1828 else if ( r1
->extents
.x2
!= r2
->extents
.x2
) return false;
1829 else if ( r1
->extents
.y1
!= r2
->extents
.y1
) return false;
1830 else if ( r1
->extents
.y2
!= r2
->extents
.y2
) return false;
1831 else for( i
=0; i
< r1
->numRects
; i
++ ) {
1832 if ( r1
->rects
[i
].x1
!= r2
->rects
[i
].x1
) return false;
1833 else if ( r1
->rects
[i
].x2
!= r2
->rects
[i
].x2
) return false;
1834 else if ( r1
->rects
[i
].y1
!= r2
->rects
[i
].y1
) return false;
1835 else if ( r1
->rects
[i
].y2
!= r2
->rects
[i
].y2
) return false;
1840 bool REGION::XPointInRegion(Region pRegion
, int x
, int y
)
1844 if (pRegion
->numRects
== 0)
1846 if (!INBOX(pRegion
->extents
, x
, y
))
1848 for (i
=0; i
<pRegion
->numRects
; i
++)
1850 if (INBOX (pRegion
->rects
[i
], x
, y
))
1856 wxRegionContain
REGION::XRectInRegion(register Region region
,
1858 unsigned int rwidth
,
1859 unsigned int rheight
)
1861 register BoxPtr pbox
;
1862 register BoxPtr pboxEnd
;
1864 register BoxPtr prect
= &rect
;
1865 int partIn
, partOut
;
1869 prect
->x2
= rwidth
+ rx
;
1870 prect
->y2
= rheight
+ ry
;
1872 /* this is (just) a useful optimization */
1873 if ((region
->numRects
== 0) || !EXTENTCHECK(®ion
->extents
, prect
))
1874 return(wxOutRegion
);
1879 /* can stop when both partOut and partIn are true, or we reach prect->y2 */
1880 for (pbox
= region
->rects
, pboxEnd
= pbox
+ region
->numRects
;
1886 continue; /* getting up to speed or skipping remainder of band */
1890 partOut
= true; /* missed part of rectangle above */
1891 if (partIn
|| (pbox
->y1
>= prect
->y2
))
1893 ry
= pbox
->y1
; /* x guaranteed to be == prect->x1 */
1897 continue; /* not far enough over yet */
1901 partOut
= true; /* missed part of rectangle to left */
1906 if (pbox
->x1
< prect
->x2
)
1908 partIn
= true; /* definitely overlap */
1913 if (pbox
->x2
>= prect
->x2
)
1915 ry
= pbox
->y2
; /* finished with this band */
1916 if (ry
>= prect
->y2
)
1918 rx
= prect
->x1
; /* reset x out to left again */
1922 * Because boxes in a band are maximal width, if the first box
1923 * to overlap the rectangle doesn't completely cover it in that
1924 * band, the rectangle must be partially out, since some of it
1925 * will be uncovered in that band. partIn will have been set true
1933 return(partIn
? ((ry
< prect
->y2
) ? wxPartRegion
: wxInRegion
) :