1 /* $TOG: Region.c /main/31 1998/02/06 17:50:22 kaleb $ */
2 /************************************************************************
4 Copyright 1987, 1988, 1998 The Open Group
8 The above copyright notice and this permission notice shall be included in
9 all copies or substantial portions of the Software.
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
15 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 Except as contained in this notice, the name of The Open Group shall not be
19 used in advertising or otherwise to promote the sale, use or other dealings
20 in this Software without prior written authorization from The Open Group.
23 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
27 Permission to use, copy, modify, and distribute this software and its
28 documentation for any purpose and without fee is hereby granted,
29 provided that the above copyright notice appear in all copies and that
30 both that copyright notice and this permission notice appear in
31 supporting documentation, and that the name of Digital not be
32 used in advertising or publicity pertaining to distribution of the
33 software without specific, written prior permission.
35 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43 ************************************************************************/
44 /* $XFree86: xc/lib/X11/Region.c,v 1.5 1999/05/09 10:50:01 dawes Exp $ */
46 * The functions in this file implement the Region abstraction, similar to one
47 * used in the X11 sample server. A Region is simply an area, as the name
48 * implies, and is implemented as a "y-x-banded" array of rectangles. To
49 * explain: Each Region is made up of a certain number of rectangles sorted
50 * by y coordinate first, and then by x coordinate.
52 * Furthermore, the rectangles are banded such that every rectangle with a
53 * given upper-left y coordinate (y1) will have the same lower-right y
54 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
55 * will span the entire vertical distance of the band. This means that some
56 * areas that could be merged into a taller rectangle will be represented as
57 * several shorter rectangles to account for shorter rectangles to its left
58 * or right but within its "vertical scope".
60 * An added constraint on the rectangles is that they must cover as much
61 * horizontal area as possible. E.g. no two rectangles in a band are allowed
64 * Whenever possible, bands will be merged together to cover a greater vertical
65 * distance (and thus reduce the number of rectangles). Two bands can be merged
66 * only if the bottom of one touches the top of the other and they have
67 * rectangles in the same places (of the same width, of course). This maintains
68 * the y-x-banding that's so nice to have...
73 #include <gdkregion.h>
74 #include "gdkregion-generic.h"
78 #define assert(expr) {if (!(expr)) fprintf(stderr,\
79 "Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__); }
84 typedef void (*overlapFunc) (GdkRegion *pReg,
91 typedef void (*nonOverlapFunc) (GdkRegion *pReg,
97 static void miRegionCopy (GdkRegion *dstrgn,
99 static void miRegionOp (GdkRegion *newReg,
102 overlapFunc overlapFn,
103 nonOverlapFunc nonOverlap1Fn,
104 nonOverlapFunc nonOverlap2Fn);
106 /* Create a new empty region */
113 temp = g_new (GdkRegion, 1);
114 temp->rects = g_new (GdkRegionBox, 1);
117 temp->extents.x1 = 0;
118 temp->extents.y1 = 0;
119 temp->extents.x2 = 0;
120 temp->extents.y2 = 0;
127 * gdk_region_rectangle:
128 * @rectangle: a #GdkRectangle
130 * Creates a new region containing the area @rectangle.
132 * Return value: a new region
135 gdk_region_rectangle (GdkRectangle *rectangle)
139 g_return_val_if_fail (rectangle != NULL, NULL);
141 if (rectangle->width <= 0 || rectangle->height <= 0)
142 return gdk_region_new();
144 temp = g_new (GdkRegion, 1);
145 temp->rects = g_new (GdkRegionBox, 1);
148 temp->extents.x1 = temp->rects[0].x1 = rectangle->x;
149 temp->extents.y1 = temp->rects[0].y1 = rectangle->y;
150 temp->extents.x2 = temp->rects[0].x2 = rectangle->x + rectangle->width;
151 temp->extents.y2 = temp->rects[0].y2 = rectangle->y + rectangle->height;
159 * @region: a #GdkRegion
161 * Copies @region, creating an identical new region.
163 * Return value: a new region identical to @region
166 gdk_region_copy (GdkRegion *region)
170 g_return_val_if_fail (region != NULL, NULL);
172 temp = g_new (GdkRegion, 1);
173 temp->rects = g_new (GdkRegionBox, region->numRects);
175 temp->numRects = region->numRects;
176 temp->extents = region->extents;
177 temp->size = region->numRects;
179 memcpy (temp->rects, region->rects, region->numRects * sizeof (GdkRegionBox));
185 gdk_region_get_clipbox (GdkRegion *r, GdkRectangle *rect)
187 g_return_if_fail (r != NULL);
188 g_return_if_fail (rect != NULL);
190 rect->x = r->extents.x1;
191 rect->y = r->extents.y1;
192 rect->width = r->extents.x2 - r->extents.x1;
193 rect->height = r->extents.y2 - r->extents.y1;
198 * gdk_region_get_rectangles:
199 * @region: a #GdkRegion
200 * @rectangles: return location for an array of rectangles
201 * @n_rectangles: length of returned array
203 * Obtains the area covered by the region as a list of rectangles.
204 * The array returned in @rectangles must be freed with g_free().
208 gdk_region_get_rectangles (GdkRegion *region,
209 GdkRectangle **rectangles,
214 g_return_if_fail (region != NULL);
215 g_return_if_fail (rectangles != NULL);
216 g_return_if_fail (n_rectangles != NULL);
218 *n_rectangles = region->numRects;
219 *rectangles = g_new (GdkRectangle, region->numRects);
221 for (i = 0; i < region->numRects; i++)
224 rect = region->rects[i];
225 (*rectangles)[i].x = rect.x1;
226 (*rectangles)[i].y = rect.y1;
227 (*rectangles)[i].width = rect.x2 - rect.x1;
228 (*rectangles)[i].height = rect.y2 - rect.y1;
233 * gdk_region_union_with_rect:
234 * @region: a #GdkRegion.
235 * @rect: a #GdkRectangle.
237 * Sets the area of @region to the union of the areas of @region and
238 * @rect. The resulting area is the set of pixels contained in
239 * either @region or @rect.
242 gdk_region_union_with_rect (GdkRegion *region,
245 GdkRegion tmp_region;
247 g_return_if_fail (region != NULL);
248 g_return_if_fail (rect != NULL);
250 if (!rect->width || !rect->height)
253 tmp_region.rects = &tmp_region.extents;
254 tmp_region.numRects = 1;
255 tmp_region.extents.x1 = rect->x;
256 tmp_region.extents.y1 = rect->y;
257 tmp_region.extents.x2 = rect->x + rect->width;
258 tmp_region.extents.y2 = rect->y + rect->height;
261 gdk_region_union (region, &tmp_region);
265 *-----------------------------------------------------------------------
267 * Reset the extents of a region to what they should be. Called by
268 * miSubtract and miIntersect b/c they can't figure it out along the
269 * way or do so easily, as miUnion can.
275 * The region's 'extents' structure is overwritten.
277 *-----------------------------------------------------------------------
280 miSetExtents (GdkRegion *pReg)
282 GdkRegionBox *pBox, *pBoxEnd, *pExtents;
284 if (pReg->numRects == 0)
286 pReg->extents.x1 = 0;
287 pReg->extents.y1 = 0;
288 pReg->extents.x2 = 0;
289 pReg->extents.y2 = 0;
293 pExtents = &pReg->extents;
295 pBoxEnd = &pBox[pReg->numRects - 1];
298 * Since pBox is the first rectangle in the region, it must have the
299 * smallest y1 and since pBoxEnd is the last rectangle in the region,
300 * it must have the largest y2, because of banding. Initialize x1 and
301 * x2 from pBox and pBoxEnd, resp., as good things to initialize them
304 pExtents->x1 = pBox->x1;
305 pExtents->y1 = pBox->y1;
306 pExtents->x2 = pBoxEnd->x2;
307 pExtents->y2 = pBoxEnd->y2;
309 assert(pExtents->y1 < pExtents->y2);
310 while (pBox <= pBoxEnd)
312 if (pBox->x1 < pExtents->x1)
314 pExtents->x1 = pBox->x1;
316 if (pBox->x2 > pExtents->x2)
318 pExtents->x2 = pBox->x2;
322 assert(pExtents->x1 < pExtents->x2);
326 gdk_region_destroy (GdkRegion *r)
328 g_return_if_fail (r != NULL);
335 /* TranslateRegion(pRegion, x, y)
341 gdk_region_offset (GdkRegion *region,
348 g_return_if_fail (region != NULL);
350 pbox = region->rects;
351 nbox = region->numRects;
361 region->extents.x1 += x;
362 region->extents.x2 += x;
363 region->extents.y1 += y;
364 region->extents.y2 += y;
368 Utility procedure Compress:
369 Replace r by the region r', where
370 p in r' iff (Quantifer m <= dx) (p + m in r), and
371 Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
372 (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
374 Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
375 of all points p such that p and the next dx points on the same
376 horizontal scan line are all in r. We do this using by noting
377 that p is the head of a run of length 2^i + k iff p is the head
378 of a run of length 2^i and p+2^i is the head of a run of length
379 k. Thus, the loop invariant: s contains the region corresponding
380 to the runs of length shift. r contains the region corresponding
381 to the runs of length 1 + dxo & (shift-1), where dxo is the original
382 value of dx. dx = dxo & ~(shift-1). As parameters, s and t are
383 scratch regions, so that we don't have to allocate them on every
387 #define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \
388 else gdk_region_intersect (a,b)
389 #define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \
390 else gdk_region_offset (a,0,b)
393 Compress(GdkRegion *r,
407 ZShiftRegion(r, -(int)shift);
413 ZShiftRegion(s, -(int)shift);
424 gdk_region_shrink (GdkRegion *r,
431 g_return_if_fail (r != NULL);
436 s = gdk_region_new ();
437 t = gdk_region_new ();
443 Compress(r, s, t, (unsigned) 2*dx, TRUE, grow);
449 Compress(r, s, t, (unsigned) 2*dy, FALSE, grow);
451 gdk_region_offset (r, dx, dy);
452 gdk_region_destroy (s);
453 gdk_region_destroy (t);
457 /*======================================================================
458 * Region Intersection
459 *====================================================================*/
461 *-----------------------------------------------------------------------
463 * Handle an overlapping band for miIntersect.
469 * Rectangles may be added to the region.
471 *-----------------------------------------------------------------------
475 miIntersectO (GdkRegion *pReg,
485 GdkRegionBox *pNextRect;
487 pNextRect = &pReg->rects[pReg->numRects];
489 while ((r1 != r1End) && (r2 != r2End))
491 x1 = MAX (r1->x1,r2->x1);
492 x2 = MIN (r1->x2,r2->x2);
495 * If there's any overlap between the two rectangles, add that
496 * overlap to the new region.
497 * There's no need to check for subsumption because the only way
498 * such a need could arise is if some region has two rectangles
499 * right next to each other. Since that should never happen...
505 MEMCHECK (pReg, pNextRect, pReg->rects);
512 assert (pReg->numRects <= pReg->size);
516 * Need to advance the pointers. Shift the one that extends
517 * to the right the least, since the other still has a chance to
518 * overlap with that region's next rectangle, if you see what I mean.
524 else if (r2->x2 < r1->x2)
537 * gdk_region_intersect:
538 * @source1: a #GdkRegion
539 * @source2: another #GdkRegion
541 * Sets the area of @source1 to the intersection of the areas of @source1
542 * and @source2. The resulting area is the set of pixels contained in
543 * both @source1 and @source2.
546 gdk_region_intersect (GdkRegion *region,
549 g_return_if_fail (region != NULL);
550 g_return_if_fail (other != NULL);
552 /* check for trivial reject */
553 if ((!(region->numRects)) || (!(other->numRects)) ||
554 (!EXTENTCHECK(®ion->extents, &other->extents)))
555 region->numRects = 0;
557 miRegionOp (region, region, other,
558 miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL);
561 * Can't alter region's extents before miRegionOp depends on the
562 * extents of the regions being unchanged. Besides, this way there's
563 * no checking against rectangles that will be nuked due to
564 * coalescing, so we have to examine fewer rectangles.
566 miSetExtents(region);
570 miRegionCopy(GdkRegion *dstrgn, GdkRegion *rgn)
572 if (dstrgn != rgn) /* don't want to copy to itself */
574 if (dstrgn->size < rgn->numRects)
576 dstrgn->rects = g_renew (GdkRegionBox, dstrgn->rects, rgn->numRects);
577 dstrgn->size = rgn->numRects;
579 dstrgn->numRects = rgn->numRects;
580 dstrgn->extents.x1 = rgn->extents.x1;
581 dstrgn->extents.y1 = rgn->extents.y1;
582 dstrgn->extents.x2 = rgn->extents.x2;
583 dstrgn->extents.y2 = rgn->extents.y2;
585 memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox));
590 /*======================================================================
591 * Generic Region Operator
592 *====================================================================*/
595 *-----------------------------------------------------------------------
597 * Attempt to merge the boxes in the current band with those in the
598 * previous one. Used only by miRegionOp.
601 * The new index for the previous band.
604 * If coalescing takes place:
605 * - rectangles in the previous band will have their y2 fields
607 * - pReg->numRects will be decreased.
609 *-----------------------------------------------------------------------
613 miCoalesce (GdkRegion *pReg, /* Region to coalesce */
614 gint prevStart, /* Index of start of previous band */
615 gint curStart) /* Index of start of current band */
617 GdkRegionBox *pPrevBox; /* Current box in previous band */
618 GdkRegionBox *pCurBox; /* Current box in current band */
619 GdkRegionBox *pRegEnd; /* End of region */
620 int curNumRects; /* Number of rectangles in current
622 int prevNumRects; /* Number of rectangles in previous
624 int bandY1; /* Y1 coordinate for current band */
626 pRegEnd = &pReg->rects[pReg->numRects];
628 pPrevBox = &pReg->rects[prevStart];
629 prevNumRects = curStart - prevStart;
632 * Figure out how many rectangles are in the current band. Have to do
633 * this because multiple bands could have been added in miRegionOp
634 * at the end when one region has been exhausted.
636 pCurBox = &pReg->rects[curStart];
637 bandY1 = pCurBox->y1;
638 for (curNumRects = 0;
639 (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
645 if (pCurBox != pRegEnd)
648 * If more than one band was added, we have to find the start
649 * of the last band added so the next coalescing job can start
650 * at the right place... (given when multiple bands are added,
651 * this may be pointless -- see above).
654 while (pRegEnd[-1].y1 == pRegEnd->y1)
658 curStart = pRegEnd - pReg->rects;
659 pRegEnd = pReg->rects + pReg->numRects;
662 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
663 pCurBox -= curNumRects;
665 * The bands may only be coalesced if the bottom of the previous
666 * matches the top scanline of the current.
668 if (pPrevBox->y2 == pCurBox->y1)
671 * Make sure the bands have boxes in the same places. This
672 * assumes that boxes have been added in such a way that they
673 * cover the most area possible. I.e. two boxes in a band must
674 * have some horizontal space between them.
678 if ((pPrevBox->x1 != pCurBox->x1) ||
679 (pPrevBox->x2 != pCurBox->x2))
682 * The bands don't line up so they can't be coalesced.
689 } while (prevNumRects != 0);
691 pReg->numRects -= curNumRects;
692 pCurBox -= curNumRects;
693 pPrevBox -= curNumRects;
696 * The bands may be merged, so set the bottom y of each box
697 * in the previous band to that of the corresponding box in
702 pPrevBox->y2 = pCurBox->y2;
707 while (curNumRects != 0);
710 * If only one band was added to the region, we have to backup
711 * curStart to the start of the previous band.
713 * If more than one band was added to the region, copy the
714 * other bands down. The assumption here is that the other bands
715 * came from the same region as the current one and no further
716 * coalescing can be done on them since it's all been done
717 * already... curStart is already in the right place.
719 if (pCurBox == pRegEnd)
721 curStart = prevStart;
727 *pPrevBox++ = *pCurBox++;
729 while (pCurBox != pRegEnd);
738 *-----------------------------------------------------------------------
740 * Apply an operation to two regions. Called by miUnion, miInverse,
741 * miSubtract, miIntersect...
747 * The new region is overwritten.
750 * The idea behind this function is to view the two regions as sets.
751 * Together they cover a rectangle of area that this function divides
752 * into horizontal bands where points are covered only by one region
753 * or by both. For the first case, the nonOverlapFunc is called with
754 * each the band and the band's upper and lower extents. For the
755 * second, the overlapFunc is called to process the entire band. It
756 * is responsible for clipping the rectangles in the band, though
757 * this function provides the boundaries.
758 * At the end of each band, the new region is coalesced, if possible,
759 * to reduce the number of rectangles in the region.
761 *-----------------------------------------------------------------------
765 miRegionOp(GdkRegion *newReg,
768 overlapFunc overlapFn, /* Function to call for over-
770 nonOverlapFunc nonOverlap1Fn, /* Function to call for non-
771 * overlapping bands in region
773 nonOverlapFunc nonOverlap2Fn) /* Function to call for non-
774 * overlapping bands in region
777 GdkRegionBox *r1; /* Pointer into first region */
778 GdkRegionBox *r2; /* Pointer into 2d region */
779 GdkRegionBox *r1End; /* End of 1st region */
780 GdkRegionBox *r2End; /* End of 2d region */
781 int ybot; /* Bottom of intersection */
782 int ytop; /* Top of intersection */
783 GdkRegionBox *oldRects; /* Old rects for newReg */
784 int prevBand; /* Index of start of
785 * previous band in newReg */
786 int curBand; /* Index of start of current
788 GdkRegionBox *r1BandEnd; /* End of current band in r1 */
789 GdkRegionBox *r2BandEnd; /* End of current band in r2 */
790 int top; /* Top of non-overlapping
792 int bot; /* Bottom of non-overlapping
797 * set r1, r2, r1End and r2End appropriately, preserve the important
798 * parts of the destination region until the end in case it's one of
799 * the two source regions, then mark the "new" region empty, allocating
800 * another array of rectangles for it to use.
804 r1End = r1 + reg1->numRects;
805 r2End = r2 + reg2->numRects;
807 oldRects = newReg->rects;
809 EMPTY_REGION(newReg);
812 * Allocate a reasonable number of rectangles for the new region. The idea
813 * is to allocate enough so the individual functions don't need to
814 * reallocate and copy the array, which is time consuming, yet we don't
815 * have to worry about using too much memory. I hope to be able to
816 * nuke the Xrealloc() at the end of this function eventually.
818 newReg->size = MAX (reg1->numRects, reg2->numRects) * 2;
819 newReg->rects = g_new (GdkRegionBox, newReg->size);
822 * Initialize ybot and ytop.
823 * In the upcoming loop, ybot and ytop serve different functions depending
824 * on whether the band being handled is an overlapping or non-overlapping
826 * In the case of a non-overlapping band (only one of the regions
827 * has points in the band), ybot is the bottom of the most recent
828 * intersection and thus clips the top of the rectangles in that band.
829 * ytop is the top of the next intersection between the two regions and
830 * serves to clip the bottom of the rectangles in the current band.
831 * For an overlapping band (where the two regions intersect), ytop clips
832 * the top of the rectangles of both regions and ybot clips the bottoms.
834 if (reg1->extents.y1 < reg2->extents.y1)
835 ybot = reg1->extents.y1;
837 ybot = reg2->extents.y1;
840 * prevBand serves to mark the start of the previous band so rectangles
841 * can be coalesced into larger rectangles. qv. miCoalesce, above.
842 * In the beginning, there is no previous band, so prevBand == curBand
843 * (curBand is set later on, of course, but the first band will always
844 * start at index 0). prevBand and curBand must be indices because of
845 * the possible expansion, and resultant moving, of the new region's
846 * array of rectangles.
852 curBand = newReg->numRects;
855 * This algorithm proceeds one source-band (as opposed to a
856 * destination band, which is determined by where the two regions
857 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
858 * rectangle after the last one in the current band for their
859 * respective regions.
862 while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
868 while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
874 * First handle the band that doesn't intersect, if any.
876 * Note that attention is restricted to one band in the
877 * non-intersecting region at once, so if a region has n
878 * bands between the current position and the next place it overlaps
879 * the other, this entire loop will be passed through n times.
883 top = MAX (r1->y1,ybot);
884 bot = MIN (r1->y2,r2->y1);
886 if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL))
888 (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot);
893 else if (r2->y1 < r1->y1)
895 top = MAX (r2->y1,ybot);
896 bot = MIN (r2->y2,r1->y1);
898 if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL))
900 (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot);
911 * If any rectangles got added to the region, try and coalesce them
912 * with rectangles from the previous band. Note we could just do
913 * this test in miCoalesce, but some machines incur a not
914 * inconsiderable cost for function calls, so...
916 if (newReg->numRects != curBand)
918 prevBand = miCoalesce (newReg, prevBand, curBand);
922 * Now see if we've hit an intersecting band. The two bands only
923 * intersect if ybot > ytop
925 ybot = MIN (r1->y2, r2->y2);
926 curBand = newReg->numRects;
929 (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
933 if (newReg->numRects != curBand)
935 prevBand = miCoalesce (newReg, prevBand, curBand);
939 * If we've finished with a band (y2 == ybot) we skip forward
940 * in the region to the next band.
950 } while ((r1 != r1End) && (r2 != r2End));
953 * Deal with whichever region still has rectangles left.
955 curBand = newReg->numRects;
958 if (nonOverlap1Fn != (nonOverlapFunc )NULL)
963 while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
967 (* nonOverlap1Fn) (newReg, r1, r1BandEnd,
968 MAX (r1->y1,ybot), r1->y2);
970 } while (r1 != r1End);
973 else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL))
978 while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
982 (* nonOverlap2Fn) (newReg, r2, r2BandEnd,
983 MAX (r2->y1,ybot), r2->y2);
985 } while (r2 != r2End);
988 if (newReg->numRects != curBand)
990 (void) miCoalesce (newReg, prevBand, curBand);
994 * A bit of cleanup. To keep regions from growing without bound,
995 * we shrink the array of rectangles to match the new number of
996 * rectangles in the region. This never goes to 0, however...
998 * Only do this stuff if the number of rectangles allocated is more than
999 * twice the number of rectangles in the region (a simple optimization...).
1001 if (newReg->numRects < (newReg->size >> 1))
1003 if (REGION_NOT_EMPTY (newReg))
1005 newReg->size = newReg->numRects;
1006 newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size);
1011 * No point in doing the extra work involved in an Xrealloc if
1012 * the region is empty
1015 g_free (newReg->rects);
1016 newReg->rects = g_new (GdkRegionBox, 1);
1023 /*======================================================================
1025 *====================================================================*/
1028 *-----------------------------------------------------------------------
1030 * Handle a non-overlapping band for the union operation. Just
1031 * Adds the rectangles into the region. Doesn't have to check for
1032 * subsumption or anything.
1038 * pReg->numRects is incremented and the final rectangles overwritten
1039 * with the rectangles we're passed.
1041 *-----------------------------------------------------------------------
1044 miUnionNonO (GdkRegion *pReg,
1050 GdkRegionBox *pNextRect;
1052 pNextRect = &pReg->rects[pReg->numRects];
1058 assert(r->x1 < r->x2);
1059 MEMCHECK(pReg, pNextRect, pReg->rects);
1060 pNextRect->x1 = r->x1;
1062 pNextRect->x2 = r->x2;
1064 pReg->numRects += 1;
1067 assert(pReg->numRects<=pReg->size);
1074 *-----------------------------------------------------------------------
1076 * Handle an overlapping band for the union operation. Picks the
1077 * left-most rectangle each time and merges it into the region.
1083 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1086 *-----------------------------------------------------------------------
1091 miUnionO (GdkRegion *pReg,
1093 GdkRegionBox *r1End,
1095 GdkRegionBox *r2End,
1099 GdkRegionBox * pNextRect;
1101 pNextRect = &pReg->rects[pReg->numRects];
1103 #define MERGERECT(r) \
1104 if ((pReg->numRects != 0) && \
1105 (pNextRect[-1].y1 == y1) && \
1106 (pNextRect[-1].y2 == y2) && \
1107 (pNextRect[-1].x2 >= r->x1)) \
1109 if (pNextRect[-1].x2 < r->x2) \
1111 pNextRect[-1].x2 = r->x2; \
1112 assert(pNextRect[-1].x1<pNextRect[-1].x2); \
1117 MEMCHECK(pReg, pNextRect, pReg->rects); \
1118 pNextRect->y1 = y1; \
1119 pNextRect->y2 = y2; \
1120 pNextRect->x1 = r->x1; \
1121 pNextRect->x2 = r->x2; \
1122 pReg->numRects += 1; \
1125 assert(pReg->numRects<=pReg->size); \
1129 while ((r1 != r1End) && (r2 != r2End))
1131 if (r1->x1 < r2->x1)
1146 } while (r1 != r1End);
1148 else while (r2 != r2End)
1156 * @source1: a #GdkRegion
1157 * @source2: a #GdkRegion
1159 * Sets the area of @source1 to the union of the areas of @source1 and
1160 * @source2. The resulting area is the set of pixels contained in
1161 * either @source1 or @source2.
1164 gdk_region_union (GdkRegion *region,
1167 g_return_if_fail (region != NULL);
1168 g_return_if_fail (other != NULL);
1170 /* checks all the simple cases */
1173 * region and other are the same or other is empty
1175 if ((region == other) || (!(other->numRects)))
1181 if (!(region->numRects))
1183 miRegionCopy (region, other);
1188 * region completely subsumes otehr
1190 if ((region->numRects == 1) &&
1191 (region->extents.x1 <= other->extents.x1) &&
1192 (region->extents.y1 <= other->extents.y1) &&
1193 (region->extents.x2 >= other->extents.x2) &&
1194 (region->extents.y2 >= other->extents.y2))
1198 * other completely subsumes region
1200 if ((other->numRects == 1) &&
1201 (other->extents.x1 <= region->extents.x1) &&
1202 (other->extents.y1 <= region->extents.y1) &&
1203 (other->extents.x2 >= region->extents.x2) &&
1204 (other->extents.y2 >= region->extents.y2))
1206 miRegionCopy(region, other);
1210 miRegionOp (region, region, other, miUnionO,
1211 miUnionNonO, miUnionNonO);
1213 region->extents.x1 = MIN (region->extents.x1, other->extents.x1);
1214 region->extents.y1 = MIN (region->extents.y1, other->extents.y1);
1215 region->extents.x2 = MAX (region->extents.x2, other->extents.x2);
1216 region->extents.y2 = MAX (region->extents.y2, other->extents.y2);
1220 /*======================================================================
1221 * Region Subtraction
1222 *====================================================================*/
1225 *-----------------------------------------------------------------------
1227 * Deal with non-overlapping band for subtraction. Any parts from
1228 * region 2 we discard. Anything from region 1 we add to the region.
1234 * pReg may be affected.
1236 *-----------------------------------------------------------------------
1240 miSubtractNonO1 (GdkRegion *pReg,
1246 GdkRegionBox * pNextRect;
1248 pNextRect = &pReg->rects[pReg->numRects];
1254 assert (r->x1<r->x2);
1255 MEMCHECK (pReg, pNextRect, pReg->rects);
1256 pNextRect->x1 = r->x1;
1258 pNextRect->x2 = r->x2;
1260 pReg->numRects += 1;
1263 assert (pReg->numRects <= pReg->size);
1270 *-----------------------------------------------------------------------
1272 * Overlapping band subtraction. x1 is the left-most point not yet
1279 * pReg may have rectangles added to it.
1281 *-----------------------------------------------------------------------
1285 miSubtractO (GdkRegion *pReg,
1287 GdkRegionBox *r1End,
1289 GdkRegionBox *r2End,
1293 GdkRegionBox * pNextRect;
1299 pNextRect = &pReg->rects[pReg->numRects];
1301 while ((r1 != r1End) && (r2 != r2End))
1306 * Subtrahend missed the boat: go to next subtrahend.
1310 else if (r2->x1 <= x1)
1313 * Subtrahend preceeds minuend: nuke left edge of minuend.
1319 * Minuend completely covered: advance to next minuend and
1320 * reset left fence to edge of new minuend.
1329 * Subtrahend now used up since it doesn't extend beyond
1335 else if (r2->x1 < r1->x2)
1338 * Left part of subtrahend covers part of minuend: add uncovered
1339 * part of minuend to region and skip to next subtrahend.
1342 MEMCHECK(pReg, pNextRect, pReg->rects);
1345 pNextRect->x2 = r2->x1;
1347 pReg->numRects += 1;
1350 assert(pReg->numRects<=pReg->size);
1356 * Minuend used up: advance to new...
1365 * Subtrahend used up
1373 * Minuend used up: add any remaining piece before advancing.
1377 MEMCHECK(pReg, pNextRect, pReg->rects);
1380 pNextRect->x2 = r1->x2;
1382 pReg->numRects += 1;
1384 assert(pReg->numRects<=pReg->size);
1393 * Add remaining minuend rectangles to region.
1398 MEMCHECK(pReg, pNextRect, pReg->rects);
1401 pNextRect->x2 = r1->x2;
1403 pReg->numRects += 1;
1406 assert(pReg->numRects<=pReg->size);
1417 * gdk_region_subtract:
1418 * @source1: a #GdkRegion
1419 * @source2: another #GdkRegion
1421 * Subtracts the area of @source2 from the area @source1. The resulting
1422 * area is the set of pixels contained in @source1 but not in @source2.
1425 gdk_region_subtract (GdkRegion *region,
1428 g_return_if_fail (region != NULL);
1429 g_return_if_fail (other != NULL);
1431 /* check for trivial reject */
1432 if ((!(region->numRects)) || (!(other->numRects)) ||
1433 (!EXTENTCHECK(®ion->extents, &other->extents)))
1436 miRegionOp (region, region, other, miSubtractO,
1437 miSubtractNonO1, (nonOverlapFunc) NULL);
1440 * Can't alter region's extents before we call miRegionOp because miRegionOp
1441 * depends on the extents of those regions being the unaltered. Besides, this
1442 * way there's no checking against rectangles that will be nuked
1443 * due to coalescing, so we have to examine fewer rectangles.
1445 miSetExtents (region);
1450 * @source1: a #GdkRegion
1451 * @source2: another #GdkRegion
1453 * Sets the area of @source1 to the exclusive-OR of the areas of @source1
1454 * and @source2. The resulting area is the set of pixels contained in one
1455 * or the other of the two sources but not in both.
1458 gdk_region_xor (GdkRegion *sra,
1463 g_return_if_fail (sra != NULL);
1464 g_return_if_fail (srb != NULL);
1466 trb = gdk_region_copy (srb);
1468 gdk_region_subtract (trb, sra);
1469 gdk_region_subtract (sra, srb);
1471 gdk_region_union (sra,trb);
1473 gdk_region_destroy (trb);
1477 * Check to see if the region is empty. Assumes a region is passed
1481 gdk_region_empty (GdkRegion *r)
1483 g_return_val_if_fail (r != NULL, FALSE);
1485 if (r->numRects == 0)
1492 * Check to see if two regions are equal
1495 gdk_region_equal (GdkRegion *r1,
1500 g_return_val_if_fail (r1 != NULL, FALSE);
1501 g_return_val_if_fail (r2 != NULL, FALSE);
1503 if (r1->numRects != r2->numRects) return FALSE;
1504 else if (r1->numRects == 0) return TRUE;
1505 else if (r1->extents.x1 != r2->extents.x1) return FALSE;
1506 else if (r1->extents.x2 != r2->extents.x2) return FALSE;
1507 else if (r1->extents.y1 != r2->extents.y1) return FALSE;
1508 else if (r1->extents.y2 != r2->extents.y2) return FALSE;
1510 for(i=0; i < r1->numRects; i++ )
1512 if (r1->rects[i].x1 != r2->rects[i].x1) return FALSE;
1513 else if (r1->rects[i].x2 != r2->rects[i].x2) return FALSE;
1514 else if (r1->rects[i].y1 != r2->rects[i].y1) return FALSE;
1515 else if (r1->rects[i].y2 != r2->rects[i].y2) return FALSE;
1521 gdk_region_point_in (GdkRegion *region,
1527 g_return_val_if_fail (region != NULL, FALSE);
1529 if (region->numRects == 0)
1531 if (!INBOX(region->extents, x, y))
1533 for (i=0; i<region->numRects; i++)
1535 if (INBOX (region->rects[i], x, y))
1542 gdk_region_rect_in (GdkRegion *region,
1543 GdkRectangle *rectangle)
1546 GdkRegionBox *pboxEnd;
1548 GdkRegionBox *prect = ▭
1549 gboolean partIn, partOut;
1552 g_return_val_if_fail (region != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1553 g_return_val_if_fail (rectangle != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1560 prect->x2 = rx + rectangle->width;
1561 prect->y2 = ry + rectangle->height;
1563 /* this is (just) a useful optimization */
1564 if ((region->numRects == 0) || !EXTENTCHECK (®ion->extents, prect))
1565 return GDK_OVERLAP_RECTANGLE_OUT;
1570 /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
1571 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1577 continue; /* getting up to speed or skipping remainder of band */
1581 partOut = TRUE; /* missed part of rectangle above */
1582 if (partIn || (pbox->y1 >= prect->y2))
1584 ry = pbox->y1; /* x guaranteed to be == prect->x1 */
1588 continue; /* not far enough over yet */
1592 partOut = TRUE; /* missed part of rectangle to left */
1597 if (pbox->x1 < prect->x2)
1599 partIn = TRUE; /* definitely overlap */
1604 if (pbox->x2 >= prect->x2)
1606 ry = pbox->y2; /* finished with this band */
1607 if (ry >= prect->y2)
1609 rx = prect->x1; /* reset x out to left again */
1614 * Because boxes in a band are maximal width, if the first box
1615 * to overlap the rectangle doesn't completely cover it in that
1616 * band, the rectangle must be partially out, since some of it
1617 * will be uncovered in that band. partIn will have been set true
1627 GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) :
1628 GDK_OVERLAP_RECTANGLE_OUT);
1633 gdk_region_unsorted_spans_intersect_foreach (GdkRegion *region,
1636 GdkSpanFunc function,
1639 gint i, left, right, y;
1640 gint clipped_left, clipped_right;
1642 GdkRegionBox *pboxEnd;
1645 if (!region->numRects)
1648 for (i=0;i<n_spans;i++)
1652 right = left + spans[i].width; /* right is not in the span! */
1654 if (! ((region->extents.y1 <= y) &&
1655 (region->extents.y2 > y) &&
1656 (region->extents.x1 < right) &&
1657 (region->extents.x2 > left)) )
1660 /* can stop when we passed y */
1661 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1666 continue; /* Not quite there yet */
1669 break; /* passed the spanline */
1671 if ((right > pbox->x1) && (left < pbox->x2))
1673 clipped_left = MAX (left, pbox->x1);
1674 clipped_right = MIN (right, pbox->x2);
1677 out_span.x = clipped_left;
1678 out_span.width = clipped_right - clipped_left;
1679 (*function) (&out_span, data);
1687 gdk_region_spans_intersect_foreach (GdkRegion *region,
1691 GdkSpanFunc function,
1694 gint left, right, y;
1695 gint clipped_left, clipped_right;
1697 GdkRegionBox *pboxEnd;
1698 GdkSpan *span, *tmpspan;
1702 g_return_if_fail (region != NULL);
1703 g_return_if_fail (spans != NULL);
1707 gdk_region_unsorted_spans_intersect_foreach (region,
1715 if ((!region->numRects) || (n_spans == 0))
1720 right = span->x + span->width; /* right is not in the span! */
1722 /* The main method here is to step along the
1723 * sorted rectangles and spans in lock step, and
1724 * clipping the spans that are in the current
1725 * rectangle before going on to the next rectangle.
1729 end_span = spans + n_spans;
1730 pbox = region->rects;
1731 pboxEnd = pbox + region->numRects;
1732 while (pbox < pboxEnd)
1734 while ((pbox->y2 < span->y) || (span->y < pbox->y1))
1736 /* Skip any rectangles that are above the current span */
1737 if (pbox->y2 < span->y)
1740 if (pbox == pboxEnd)
1743 /* Skip any spans that are above the current rectangle */
1744 if (span->y < pbox->y1)
1747 if (span == end_span)
1752 /* Ok, we got at least one span that might intersect this rectangle. */
1754 while ((tmpspan < end_span) &&
1755 (tmpspan->y < pbox->y2))
1759 right = left + tmpspan->width; /* right is not in the span! */
1761 if ((right > pbox->x1) && (left < pbox->x2))
1763 clipped_left = MAX (left, pbox->x1);
1764 clipped_right = MIN (right, pbox->x2);
1767 out_span.x = clipped_left;
1768 out_span.width = clipped_right - clipped_left;
1769 (*function) (&out_span, data);
1775 /* Finished this rectangle.
1776 * The spans could still intersect the next one