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...
74 #include <gdkregion.h>
75 #include "gdkregion-generic.h"
79 #define assert(expr) {if (!(expr)) fprintf(stderr,\
80 "Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__); }
85 typedef void (*overlapFunc) (GdkRegion *pReg,
92 typedef void (*nonOverlapFunc) (GdkRegion *pReg,
98 static void miRegionCopy (GdkRegion *dstrgn,
100 static void miRegionOp (GdkRegion *newReg,
103 overlapFunc overlapFn,
104 nonOverlapFunc nonOverlap1Fn,
105 nonOverlapFunc nonOverlap2Fn);
107 /* Create a new empty region */
114 temp = g_new (GdkRegion, 1);
115 temp->rects = g_new (GdkRegionBox, 1);
118 temp->extents.x1 = 0;
119 temp->extents.y1 = 0;
120 temp->extents.x2 = 0;
121 temp->extents.y2 = 0;
128 * gdk_region_rectangle:
129 * @rectangle: a #GdkRectangle
131 * Creates a new region containing the area @rectangle.
133 * Return value: a new region
136 gdk_region_rectangle (GdkRectangle *rectangle)
140 g_return_val_if_fail (rectangle != NULL, NULL);
142 if (rectangle->width <= 0 || rectangle->height <= 0)
143 return gdk_region_new();
145 temp = g_new (GdkRegion, 1);
146 temp->rects = g_new (GdkRegionBox, 1);
149 temp->extents.x1 = temp->rects[0].x1 = rectangle->x;
150 temp->extents.y1 = temp->rects[0].y1 = rectangle->y;
151 temp->extents.x2 = temp->rects[0].x2 = rectangle->x + rectangle->width;
152 temp->extents.y2 = temp->rects[0].y2 = rectangle->y + rectangle->height;
160 * @region: a #GdkRegion
162 * Copies @region, creating an identical new region.
164 * Return value: a new region identical to @region
167 gdk_region_copy (GdkRegion *region)
171 g_return_val_if_fail (region != NULL, NULL);
173 temp = g_new (GdkRegion, 1);
174 temp->rects = g_new (GdkRegionBox, region->numRects);
176 temp->numRects = region->numRects;
177 temp->extents = region->extents;
178 temp->size = region->numRects;
180 memcpy (temp->rects, region->rects, region->numRects * sizeof (GdkRegionBox));
186 gdk_region_get_clipbox (GdkRegion *r, GdkRectangle *rect)
188 g_return_if_fail (r != NULL);
189 g_return_if_fail (rect != NULL);
191 rect->x = r->extents.x1;
192 rect->y = r->extents.y1;
193 rect->width = r->extents.x2 - r->extents.x1;
194 rect->height = r->extents.y2 - r->extents.y1;
199 * gdk_region_get_rectangles:
200 * @region: a #GdkRegion
201 * @rectangles: return location for an array of rectangles
202 * @n_rectangles: length of returned array
204 * Obtains the area covered by the region as a list of rectangles.
205 * The array returned in @rectangles must be freed with g_free().
209 gdk_region_get_rectangles (GdkRegion *region,
210 GdkRectangle **rectangles,
215 g_return_if_fail (region != NULL);
216 g_return_if_fail (rectangles != NULL);
217 g_return_if_fail (n_rectangles != NULL);
219 *n_rectangles = region->numRects;
220 *rectangles = g_new (GdkRectangle, region->numRects);
222 for (i = 0; i < region->numRects; i++)
225 rect = region->rects[i];
226 (*rectangles)[i].x = rect.x1;
227 (*rectangles)[i].y = rect.y1;
228 (*rectangles)[i].width = rect.x2 - rect.x1;
229 (*rectangles)[i].height = rect.y2 - rect.y1;
234 * gdk_region_union_with_rect:
235 * @region: a #GdkRegion.
236 * @rect: a #GdkRectangle.
238 * Sets the area of @region to the union of the areas of @region and
239 * @rect. The resulting area is the set of pixels contained in
240 * either @region or @rect.
243 gdk_region_union_with_rect (GdkRegion *region,
246 GdkRegion tmp_region;
248 g_return_if_fail (region != NULL);
249 g_return_if_fail (rect != NULL);
251 if (!rect->width || !rect->height)
254 tmp_region.rects = &tmp_region.extents;
255 tmp_region.numRects = 1;
256 tmp_region.extents.x1 = rect->x;
257 tmp_region.extents.y1 = rect->y;
258 tmp_region.extents.x2 = rect->x + rect->width;
259 tmp_region.extents.y2 = rect->y + rect->height;
262 gdk_region_union (region, &tmp_region);
266 *-----------------------------------------------------------------------
268 * Reset the extents of a region to what they should be. Called by
269 * miSubtract and miIntersect b/c they can't figure it out along the
270 * way or do so easily, as miUnion can.
276 * The region's 'extents' structure is overwritten.
278 *-----------------------------------------------------------------------
281 miSetExtents (GdkRegion *pReg)
283 GdkRegionBox *pBox, *pBoxEnd, *pExtents;
285 if (pReg->numRects == 0)
287 pReg->extents.x1 = 0;
288 pReg->extents.y1 = 0;
289 pReg->extents.x2 = 0;
290 pReg->extents.y2 = 0;
294 pExtents = &pReg->extents;
296 pBoxEnd = &pBox[pReg->numRects - 1];
299 * Since pBox is the first rectangle in the region, it must have the
300 * smallest y1 and since pBoxEnd is the last rectangle in the region,
301 * it must have the largest y2, because of banding. Initialize x1 and
302 * x2 from pBox and pBoxEnd, resp., as good things to initialize them
305 pExtents->x1 = pBox->x1;
306 pExtents->y1 = pBox->y1;
307 pExtents->x2 = pBoxEnd->x2;
308 pExtents->y2 = pBoxEnd->y2;
310 assert(pExtents->y1 < pExtents->y2);
311 while (pBox <= pBoxEnd)
313 if (pBox->x1 < pExtents->x1)
315 pExtents->x1 = pBox->x1;
317 if (pBox->x2 > pExtents->x2)
319 pExtents->x2 = pBox->x2;
323 assert(pExtents->x1 < pExtents->x2);
327 gdk_region_destroy (GdkRegion *r)
329 g_return_if_fail (r != NULL);
336 /* TranslateRegion(pRegion, x, y)
342 gdk_region_offset (GdkRegion *region,
349 g_return_if_fail (region != NULL);
351 pbox = region->rects;
352 nbox = region->numRects;
362 region->extents.x1 += x;
363 region->extents.x2 += x;
364 region->extents.y1 += y;
365 region->extents.y2 += y;
369 Utility procedure Compress:
370 Replace r by the region r', where
371 p in r' iff (Quantifer m <= dx) (p + m in r), and
372 Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
373 (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
375 Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
376 of all points p such that p and the next dx points on the same
377 horizontal scan line are all in r. We do this using by noting
378 that p is the head of a run of length 2^i + k iff p is the head
379 of a run of length 2^i and p+2^i is the head of a run of length
380 k. Thus, the loop invariant: s contains the region corresponding
381 to the runs of length shift. r contains the region corresponding
382 to the runs of length 1 + dxo & (shift-1), where dxo is the original
383 value of dx. dx = dxo & ~(shift-1). As parameters, s and t are
384 scratch regions, so that we don't have to allocate them on every
388 #define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \
389 else gdk_region_intersect (a,b)
390 #define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \
391 else gdk_region_offset (a,0,b)
394 Compress(GdkRegion *r,
408 ZShiftRegion(r, -(int)shift);
414 ZShiftRegion(s, -(int)shift);
425 gdk_region_shrink (GdkRegion *r,
432 g_return_if_fail (r != NULL);
437 s = gdk_region_new ();
438 t = gdk_region_new ();
444 Compress(r, s, t, (unsigned) 2*dx, TRUE, grow);
450 Compress(r, s, t, (unsigned) 2*dy, FALSE, grow);
452 gdk_region_offset (r, dx, dy);
453 gdk_region_destroy (s);
454 gdk_region_destroy (t);
458 /*======================================================================
459 * Region Intersection
460 *====================================================================*/
462 *-----------------------------------------------------------------------
464 * Handle an overlapping band for miIntersect.
470 * Rectangles may be added to the region.
472 *-----------------------------------------------------------------------
476 miIntersectO (GdkRegion *pReg,
486 GdkRegionBox *pNextRect;
488 pNextRect = &pReg->rects[pReg->numRects];
490 while ((r1 != r1End) && (r2 != r2End))
492 x1 = MAX (r1->x1,r2->x1);
493 x2 = MIN (r1->x2,r2->x2);
496 * If there's any overlap between the two rectangles, add that
497 * overlap to the new region.
498 * There's no need to check for subsumption because the only way
499 * such a need could arise is if some region has two rectangles
500 * right next to each other. Since that should never happen...
506 MEMCHECK (pReg, pNextRect, pReg->rects);
513 assert (pReg->numRects <= pReg->size);
517 * Need to advance the pointers. Shift the one that extends
518 * to the right the least, since the other still has a chance to
519 * overlap with that region's next rectangle, if you see what I mean.
525 else if (r2->x2 < r1->x2)
538 * gdk_region_intersect:
539 * @source1: a #GdkRegion
540 * @source2: another #GdkRegion
542 * Sets the area of @source1 to the intersection of the areas of @source1
543 * and @source2. The resulting area is the set of pixels contained in
544 * both @source1 and @source2.
547 gdk_region_intersect (GdkRegion *region,
550 g_return_if_fail (region != NULL);
551 g_return_if_fail (other != NULL);
553 /* check for trivial reject */
554 if ((!(region->numRects)) || (!(other->numRects)) ||
555 (!EXTENTCHECK(®ion->extents, &other->extents)))
556 region->numRects = 0;
558 miRegionOp (region, region, other,
559 miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL);
562 * Can't alter region's extents before miRegionOp depends on the
563 * extents of the regions being unchanged. Besides, this way there's
564 * no checking against rectangles that will be nuked due to
565 * coalescing, so we have to examine fewer rectangles.
567 miSetExtents(region);
571 miRegionCopy(GdkRegion *dstrgn, GdkRegion *rgn)
573 if (dstrgn != rgn) /* don't want to copy to itself */
575 if (dstrgn->size < rgn->numRects)
577 dstrgn->rects = g_renew (GdkRegionBox, dstrgn->rects, rgn->numRects);
578 dstrgn->size = rgn->numRects;
580 dstrgn->numRects = rgn->numRects;
581 dstrgn->extents.x1 = rgn->extents.x1;
582 dstrgn->extents.y1 = rgn->extents.y1;
583 dstrgn->extents.x2 = rgn->extents.x2;
584 dstrgn->extents.y2 = rgn->extents.y2;
586 memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox));
591 /*======================================================================
592 * Generic Region Operator
593 *====================================================================*/
596 *-----------------------------------------------------------------------
598 * Attempt to merge the boxes in the current band with those in the
599 * previous one. Used only by miRegionOp.
602 * The new index for the previous band.
605 * If coalescing takes place:
606 * - rectangles in the previous band will have their y2 fields
608 * - pReg->numRects will be decreased.
610 *-----------------------------------------------------------------------
614 miCoalesce (GdkRegion *pReg, /* Region to coalesce */
615 gint prevStart, /* Index of start of previous band */
616 gint curStart) /* Index of start of current band */
618 GdkRegionBox *pPrevBox; /* Current box in previous band */
619 GdkRegionBox *pCurBox; /* Current box in current band */
620 GdkRegionBox *pRegEnd; /* End of region */
621 int curNumRects; /* Number of rectangles in current
623 int prevNumRects; /* Number of rectangles in previous
625 int bandY1; /* Y1 coordinate for current band */
627 pRegEnd = &pReg->rects[pReg->numRects];
629 pPrevBox = &pReg->rects[prevStart];
630 prevNumRects = curStart - prevStart;
633 * Figure out how many rectangles are in the current band. Have to do
634 * this because multiple bands could have been added in miRegionOp
635 * at the end when one region has been exhausted.
637 pCurBox = &pReg->rects[curStart];
638 bandY1 = pCurBox->y1;
639 for (curNumRects = 0;
640 (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
646 if (pCurBox != pRegEnd)
649 * If more than one band was added, we have to find the start
650 * of the last band added so the next coalescing job can start
651 * at the right place... (given when multiple bands are added,
652 * this may be pointless -- see above).
655 while (pRegEnd[-1].y1 == pRegEnd->y1)
659 curStart = pRegEnd - pReg->rects;
660 pRegEnd = pReg->rects + pReg->numRects;
663 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
664 pCurBox -= curNumRects;
666 * The bands may only be coalesced if the bottom of the previous
667 * matches the top scanline of the current.
669 if (pPrevBox->y2 == pCurBox->y1)
672 * Make sure the bands have boxes in the same places. This
673 * assumes that boxes have been added in such a way that they
674 * cover the most area possible. I.e. two boxes in a band must
675 * have some horizontal space between them.
679 if ((pPrevBox->x1 != pCurBox->x1) ||
680 (pPrevBox->x2 != pCurBox->x2))
683 * The bands don't line up so they can't be coalesced.
690 } while (prevNumRects != 0);
692 pReg->numRects -= curNumRects;
693 pCurBox -= curNumRects;
694 pPrevBox -= curNumRects;
697 * The bands may be merged, so set the bottom y of each box
698 * in the previous band to that of the corresponding box in
703 pPrevBox->y2 = pCurBox->y2;
708 while (curNumRects != 0);
711 * If only one band was added to the region, we have to backup
712 * curStart to the start of the previous band.
714 * If more than one band was added to the region, copy the
715 * other bands down. The assumption here is that the other bands
716 * came from the same region as the current one and no further
717 * coalescing can be done on them since it's all been done
718 * already... curStart is already in the right place.
720 if (pCurBox == pRegEnd)
722 curStart = prevStart;
728 *pPrevBox++ = *pCurBox++;
730 while (pCurBox != pRegEnd);
739 *-----------------------------------------------------------------------
741 * Apply an operation to two regions. Called by miUnion, miInverse,
742 * miSubtract, miIntersect...
748 * The new region is overwritten.
751 * The idea behind this function is to view the two regions as sets.
752 * Together they cover a rectangle of area that this function divides
753 * into horizontal bands where points are covered only by one region
754 * or by both. For the first case, the nonOverlapFunc is called with
755 * each the band and the band's upper and lower extents. For the
756 * second, the overlapFunc is called to process the entire band. It
757 * is responsible for clipping the rectangles in the band, though
758 * this function provides the boundaries.
759 * At the end of each band, the new region is coalesced, if possible,
760 * to reduce the number of rectangles in the region.
762 *-----------------------------------------------------------------------
766 miRegionOp(GdkRegion *newReg,
769 overlapFunc overlapFn, /* Function to call for over-
771 nonOverlapFunc nonOverlap1Fn, /* Function to call for non-
772 * overlapping bands in region
774 nonOverlapFunc nonOverlap2Fn) /* Function to call for non-
775 * overlapping bands in region
778 GdkRegionBox *r1; /* Pointer into first region */
779 GdkRegionBox *r2; /* Pointer into 2d region */
780 GdkRegionBox *r1End; /* End of 1st region */
781 GdkRegionBox *r2End; /* End of 2d region */
782 int ybot; /* Bottom of intersection */
783 int ytop; /* Top of intersection */
784 GdkRegionBox *oldRects; /* Old rects for newReg */
785 int prevBand; /* Index of start of
786 * previous band in newReg */
787 int curBand; /* Index of start of current
789 GdkRegionBox *r1BandEnd; /* End of current band in r1 */
790 GdkRegionBox *r2BandEnd; /* End of current band in r2 */
791 int top; /* Top of non-overlapping
793 int bot; /* Bottom of non-overlapping
798 * set r1, r2, r1End and r2End appropriately, preserve the important
799 * parts of the destination region until the end in case it's one of
800 * the two source regions, then mark the "new" region empty, allocating
801 * another array of rectangles for it to use.
805 r1End = r1 + reg1->numRects;
806 r2End = r2 + reg2->numRects;
808 oldRects = newReg->rects;
810 EMPTY_REGION(newReg);
813 * Allocate a reasonable number of rectangles for the new region. The idea
814 * is to allocate enough so the individual functions don't need to
815 * reallocate and copy the array, which is time consuming, yet we don't
816 * have to worry about using too much memory. I hope to be able to
817 * nuke the Xrealloc() at the end of this function eventually.
819 newReg->size = MAX (reg1->numRects, reg2->numRects) * 2;
820 newReg->rects = g_new (GdkRegionBox, newReg->size);
823 * Initialize ybot and ytop.
824 * In the upcoming loop, ybot and ytop serve different functions depending
825 * on whether the band being handled is an overlapping or non-overlapping
827 * In the case of a non-overlapping band (only one of the regions
828 * has points in the band), ybot is the bottom of the most recent
829 * intersection and thus clips the top of the rectangles in that band.
830 * ytop is the top of the next intersection between the two regions and
831 * serves to clip the bottom of the rectangles in the current band.
832 * For an overlapping band (where the two regions intersect), ytop clips
833 * the top of the rectangles of both regions and ybot clips the bottoms.
835 if (reg1->extents.y1 < reg2->extents.y1)
836 ybot = reg1->extents.y1;
838 ybot = reg2->extents.y1;
841 * prevBand serves to mark the start of the previous band so rectangles
842 * can be coalesced into larger rectangles. qv. miCoalesce, above.
843 * In the beginning, there is no previous band, so prevBand == curBand
844 * (curBand is set later on, of course, but the first band will always
845 * start at index 0). prevBand and curBand must be indices because of
846 * the possible expansion, and resultant moving, of the new region's
847 * array of rectangles.
853 curBand = newReg->numRects;
856 * This algorithm proceeds one source-band (as opposed to a
857 * destination band, which is determined by where the two regions
858 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
859 * rectangle after the last one in the current band for their
860 * respective regions.
863 while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
869 while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
875 * First handle the band that doesn't intersect, if any.
877 * Note that attention is restricted to one band in the
878 * non-intersecting region at once, so if a region has n
879 * bands between the current position and the next place it overlaps
880 * the other, this entire loop will be passed through n times.
884 top = MAX (r1->y1,ybot);
885 bot = MIN (r1->y2,r2->y1);
887 if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL))
889 (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot);
894 else if (r2->y1 < r1->y1)
896 top = MAX (r2->y1,ybot);
897 bot = MIN (r2->y2,r1->y1);
899 if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL))
901 (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot);
912 * If any rectangles got added to the region, try and coalesce them
913 * with rectangles from the previous band. Note we could just do
914 * this test in miCoalesce, but some machines incur a not
915 * inconsiderable cost for function calls, so...
917 if (newReg->numRects != curBand)
919 prevBand = miCoalesce (newReg, prevBand, curBand);
923 * Now see if we've hit an intersecting band. The two bands only
924 * intersect if ybot > ytop
926 ybot = MIN (r1->y2, r2->y2);
927 curBand = newReg->numRects;
930 (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
934 if (newReg->numRects != curBand)
936 prevBand = miCoalesce (newReg, prevBand, curBand);
940 * If we've finished with a band (y2 == ybot) we skip forward
941 * in the region to the next band.
951 } while ((r1 != r1End) && (r2 != r2End));
954 * Deal with whichever region still has rectangles left.
956 curBand = newReg->numRects;
959 if (nonOverlap1Fn != (nonOverlapFunc )NULL)
964 while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
968 (* nonOverlap1Fn) (newReg, r1, r1BandEnd,
969 MAX (r1->y1,ybot), r1->y2);
971 } while (r1 != r1End);
974 else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL))
979 while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
983 (* nonOverlap2Fn) (newReg, r2, r2BandEnd,
984 MAX (r2->y1,ybot), r2->y2);
986 } while (r2 != r2End);
989 if (newReg->numRects != curBand)
991 (void) miCoalesce (newReg, prevBand, curBand);
995 * A bit of cleanup. To keep regions from growing without bound,
996 * we shrink the array of rectangles to match the new number of
997 * rectangles in the region. This never goes to 0, however...
999 * Only do this stuff if the number of rectangles allocated is more than
1000 * twice the number of rectangles in the region (a simple optimization...).
1002 if (newReg->numRects < (newReg->size >> 1))
1004 if (REGION_NOT_EMPTY (newReg))
1006 newReg->size = newReg->numRects;
1007 newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size);
1012 * No point in doing the extra work involved in an Xrealloc if
1013 * the region is empty
1016 g_free (newReg->rects);
1017 newReg->rects = g_new (GdkRegionBox, 1);
1024 /*======================================================================
1026 *====================================================================*/
1029 *-----------------------------------------------------------------------
1031 * Handle a non-overlapping band for the union operation. Just
1032 * Adds the rectangles into the region. Doesn't have to check for
1033 * subsumption or anything.
1039 * pReg->numRects is incremented and the final rectangles overwritten
1040 * with the rectangles we're passed.
1042 *-----------------------------------------------------------------------
1045 miUnionNonO (GdkRegion *pReg,
1051 GdkRegionBox *pNextRect;
1053 pNextRect = &pReg->rects[pReg->numRects];
1059 assert(r->x1 < r->x2);
1060 MEMCHECK(pReg, pNextRect, pReg->rects);
1061 pNextRect->x1 = r->x1;
1063 pNextRect->x2 = r->x2;
1065 pReg->numRects += 1;
1068 assert(pReg->numRects<=pReg->size);
1075 *-----------------------------------------------------------------------
1077 * Handle an overlapping band for the union operation. Picks the
1078 * left-most rectangle each time and merges it into the region.
1084 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1087 *-----------------------------------------------------------------------
1092 miUnionO (GdkRegion *pReg,
1094 GdkRegionBox *r1End,
1096 GdkRegionBox *r2End,
1100 GdkRegionBox * pNextRect;
1102 pNextRect = &pReg->rects[pReg->numRects];
1104 #define MERGERECT(r) \
1105 if ((pReg->numRects != 0) && \
1106 (pNextRect[-1].y1 == y1) && \
1107 (pNextRect[-1].y2 == y2) && \
1108 (pNextRect[-1].x2 >= r->x1)) \
1110 if (pNextRect[-1].x2 < r->x2) \
1112 pNextRect[-1].x2 = r->x2; \
1113 assert(pNextRect[-1].x1<pNextRect[-1].x2); \
1118 MEMCHECK(pReg, pNextRect, pReg->rects); \
1119 pNextRect->y1 = y1; \
1120 pNextRect->y2 = y2; \
1121 pNextRect->x1 = r->x1; \
1122 pNextRect->x2 = r->x2; \
1123 pReg->numRects += 1; \
1126 assert(pReg->numRects<=pReg->size); \
1130 while ((r1 != r1End) && (r2 != r2End))
1132 if (r1->x1 < r2->x1)
1147 } while (r1 != r1End);
1149 else while (r2 != r2End)
1157 * @source1: a #GdkRegion
1158 * @source2: a #GdkRegion
1160 * Sets the area of @source1 to the union of the areas of @source1 and
1161 * @source2. The resulting area is the set of pixels contained in
1162 * either @source1 or @source2.
1165 gdk_region_union (GdkRegion *region,
1168 g_return_if_fail (region != NULL);
1169 g_return_if_fail (other != NULL);
1171 /* checks all the simple cases */
1174 * region and other are the same or other is empty
1176 if ((region == other) || (!(other->numRects)))
1182 if (!(region->numRects))
1184 miRegionCopy (region, other);
1189 * region completely subsumes otehr
1191 if ((region->numRects == 1) &&
1192 (region->extents.x1 <= other->extents.x1) &&
1193 (region->extents.y1 <= other->extents.y1) &&
1194 (region->extents.x2 >= other->extents.x2) &&
1195 (region->extents.y2 >= other->extents.y2))
1199 * other completely subsumes region
1201 if ((other->numRects == 1) &&
1202 (other->extents.x1 <= region->extents.x1) &&
1203 (other->extents.y1 <= region->extents.y1) &&
1204 (other->extents.x2 >= region->extents.x2) &&
1205 (other->extents.y2 >= region->extents.y2))
1207 miRegionCopy(region, other);
1211 miRegionOp (region, region, other, miUnionO,
1212 miUnionNonO, miUnionNonO);
1214 region->extents.x1 = MIN (region->extents.x1, other->extents.x1);
1215 region->extents.y1 = MIN (region->extents.y1, other->extents.y1);
1216 region->extents.x2 = MAX (region->extents.x2, other->extents.x2);
1217 region->extents.y2 = MAX (region->extents.y2, other->extents.y2);
1221 /*======================================================================
1222 * Region Subtraction
1223 *====================================================================*/
1226 *-----------------------------------------------------------------------
1228 * Deal with non-overlapping band for subtraction. Any parts from
1229 * region 2 we discard. Anything from region 1 we add to the region.
1235 * pReg may be affected.
1237 *-----------------------------------------------------------------------
1241 miSubtractNonO1 (GdkRegion *pReg,
1247 GdkRegionBox * pNextRect;
1249 pNextRect = &pReg->rects[pReg->numRects];
1255 assert (r->x1<r->x2);
1256 MEMCHECK (pReg, pNextRect, pReg->rects);
1257 pNextRect->x1 = r->x1;
1259 pNextRect->x2 = r->x2;
1261 pReg->numRects += 1;
1264 assert (pReg->numRects <= pReg->size);
1271 *-----------------------------------------------------------------------
1273 * Overlapping band subtraction. x1 is the left-most point not yet
1280 * pReg may have rectangles added to it.
1282 *-----------------------------------------------------------------------
1286 miSubtractO (GdkRegion *pReg,
1288 GdkRegionBox *r1End,
1290 GdkRegionBox *r2End,
1294 GdkRegionBox * pNextRect;
1300 pNextRect = &pReg->rects[pReg->numRects];
1302 while ((r1 != r1End) && (r2 != r2End))
1307 * Subtrahend missed the boat: go to next subtrahend.
1311 else if (r2->x1 <= x1)
1314 * Subtrahend preceeds minuend: nuke left edge of minuend.
1320 * Minuend completely covered: advance to next minuend and
1321 * reset left fence to edge of new minuend.
1330 * Subtrahend now used up since it doesn't extend beyond
1336 else if (r2->x1 < r1->x2)
1339 * Left part of subtrahend covers part of minuend: add uncovered
1340 * part of minuend to region and skip to next subtrahend.
1343 MEMCHECK(pReg, pNextRect, pReg->rects);
1346 pNextRect->x2 = r2->x1;
1348 pReg->numRects += 1;
1351 assert(pReg->numRects<=pReg->size);
1357 * Minuend used up: advance to new...
1366 * Subtrahend used up
1374 * Minuend used up: add any remaining piece before advancing.
1378 MEMCHECK(pReg, pNextRect, pReg->rects);
1381 pNextRect->x2 = r1->x2;
1383 pReg->numRects += 1;
1385 assert(pReg->numRects<=pReg->size);
1394 * Add remaining minuend rectangles to region.
1399 MEMCHECK(pReg, pNextRect, pReg->rects);
1402 pNextRect->x2 = r1->x2;
1404 pReg->numRects += 1;
1407 assert(pReg->numRects<=pReg->size);
1418 * gdk_region_subtract:
1419 * @source1: a #GdkRegion
1420 * @source2: another #GdkRegion
1422 * Subtracts the area of @source2 from the area @source1. The resulting
1423 * area is the set of pixels contained in @source1 but not in @source2.
1426 gdk_region_subtract (GdkRegion *region,
1429 g_return_if_fail (region != NULL);
1430 g_return_if_fail (other != NULL);
1432 /* check for trivial reject */
1433 if ((!(region->numRects)) || (!(other->numRects)) ||
1434 (!EXTENTCHECK(®ion->extents, &other->extents)))
1437 miRegionOp (region, region, other, miSubtractO,
1438 miSubtractNonO1, (nonOverlapFunc) NULL);
1441 * Can't alter region's extents before we call miRegionOp because miRegionOp
1442 * depends on the extents of those regions being the unaltered. Besides, this
1443 * way there's no checking against rectangles that will be nuked
1444 * due to coalescing, so we have to examine fewer rectangles.
1446 miSetExtents (region);
1451 * @source1: a #GdkRegion
1452 * @source2: another #GdkRegion
1454 * Sets the area of @source1 to the exclusive-OR of the areas of @source1
1455 * and @source2. The resulting area is the set of pixels contained in one
1456 * or the other of the two sources but not in both.
1459 gdk_region_xor (GdkRegion *sra,
1464 g_return_if_fail (sra != NULL);
1465 g_return_if_fail (srb != NULL);
1467 trb = gdk_region_copy (srb);
1469 gdk_region_subtract (trb, sra);
1470 gdk_region_subtract (sra, srb);
1472 gdk_region_union (sra,trb);
1474 gdk_region_destroy (trb);
1478 * Check to see if the region is empty. Assumes a region is passed
1482 gdk_region_empty (GdkRegion *r)
1484 g_return_val_if_fail (r != NULL, FALSE);
1486 if (r->numRects == 0)
1493 * Check to see if two regions are equal
1496 gdk_region_equal (GdkRegion *r1,
1501 g_return_val_if_fail (r1 != NULL, FALSE);
1502 g_return_val_if_fail (r2 != NULL, FALSE);
1504 if (r1->numRects != r2->numRects) return FALSE;
1505 else if (r1->numRects == 0) return TRUE;
1506 else if (r1->extents.x1 != r2->extents.x1) return FALSE;
1507 else if (r1->extents.x2 != r2->extents.x2) return FALSE;
1508 else if (r1->extents.y1 != r2->extents.y1) return FALSE;
1509 else if (r1->extents.y2 != r2->extents.y2) return FALSE;
1511 for(i=0; i < r1->numRects; i++ )
1513 if (r1->rects[i].x1 != r2->rects[i].x1) return FALSE;
1514 else if (r1->rects[i].x2 != r2->rects[i].x2) return FALSE;
1515 else if (r1->rects[i].y1 != r2->rects[i].y1) return FALSE;
1516 else if (r1->rects[i].y2 != r2->rects[i].y2) return FALSE;
1522 gdk_region_point_in (GdkRegion *region,
1528 g_return_val_if_fail (region != NULL, FALSE);
1530 if (region->numRects == 0)
1532 if (!INBOX(region->extents, x, y))
1534 for (i=0; i<region->numRects; i++)
1536 if (INBOX (region->rects[i], x, y))
1543 gdk_region_rect_in (GdkRegion *region,
1544 GdkRectangle *rectangle)
1547 GdkRegionBox *pboxEnd;
1549 GdkRegionBox *prect = ▭
1550 gboolean partIn, partOut;
1553 g_return_val_if_fail (region != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1554 g_return_val_if_fail (rectangle != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1561 prect->x2 = rx + rectangle->width;
1562 prect->y2 = ry + rectangle->height;
1564 /* this is (just) a useful optimization */
1565 if ((region->numRects == 0) || !EXTENTCHECK (®ion->extents, prect))
1566 return GDK_OVERLAP_RECTANGLE_OUT;
1571 /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
1572 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1578 continue; /* getting up to speed or skipping remainder of band */
1582 partOut = TRUE; /* missed part of rectangle above */
1583 if (partIn || (pbox->y1 >= prect->y2))
1585 ry = pbox->y1; /* x guaranteed to be == prect->x1 */
1589 continue; /* not far enough over yet */
1593 partOut = TRUE; /* missed part of rectangle to left */
1598 if (pbox->x1 < prect->x2)
1600 partIn = TRUE; /* definitely overlap */
1605 if (pbox->x2 >= prect->x2)
1607 ry = pbox->y2; /* finished with this band */
1608 if (ry >= prect->y2)
1610 rx = prect->x1; /* reset x out to left again */
1615 * Because boxes in a band are maximal width, if the first box
1616 * to overlap the rectangle doesn't completely cover it in that
1617 * band, the rectangle must be partially out, since some of it
1618 * will be uncovered in that band. partIn will have been set true
1628 GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) :
1629 GDK_OVERLAP_RECTANGLE_OUT);
1634 gdk_region_unsorted_spans_intersect_foreach (GdkRegion *region,
1637 GdkSpanFunc function,
1640 gint i, left, right, y;
1641 gint clipped_left, clipped_right;
1643 GdkRegionBox *pboxEnd;
1646 if (!region->numRects)
1649 for (i=0;i<n_spans;i++)
1653 right = left + spans[i].width; /* right is not in the span! */
1655 if (! ((region->extents.y1 <= y) &&
1656 (region->extents.y2 > y) &&
1657 (region->extents.x1 < right) &&
1658 (region->extents.x2 > left)) )
1661 /* can stop when we passed y */
1662 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1667 continue; /* Not quite there yet */
1670 break; /* passed the spanline */
1672 if ((right > pbox->x1) && (left < pbox->x2))
1674 clipped_left = MAX (left, pbox->x1);
1675 clipped_right = MIN (right, pbox->x2);
1678 out_span.x = clipped_left;
1679 out_span.width = clipped_right - clipped_left;
1680 (*function) (&out_span, data);
1688 gdk_region_spans_intersect_foreach (GdkRegion *region,
1692 GdkSpanFunc function,
1695 gint left, right, y;
1696 gint clipped_left, clipped_right;
1698 GdkRegionBox *pboxEnd;
1699 GdkSpan *span, *tmpspan;
1703 g_return_if_fail (region != NULL);
1704 g_return_if_fail (spans != NULL);
1708 gdk_region_unsorted_spans_intersect_foreach (region,
1716 if ((!region->numRects) || (n_spans == 0))
1719 /* The main method here is to step along the
1720 * sorted rectangles and spans in lock step, and
1721 * clipping the spans that are in the current
1722 * rectangle before going on to the next rectangle.
1726 end_span = spans + n_spans;
1727 pbox = region->rects;
1728 pboxEnd = pbox + region->numRects;
1729 while (pbox < pboxEnd)
1731 while ((pbox->y2 < span->y) || (span->y < pbox->y1))
1733 /* Skip any rectangles that are above the current span */
1734 if (pbox->y2 < span->y)
1737 if (pbox == pboxEnd)
1740 /* Skip any spans that are above the current rectangle */
1741 if (span->y < pbox->y1)
1744 if (span == end_span)
1749 /* Ok, we got at least one span that might intersect this rectangle. */
1751 while ((tmpspan < end_span) &&
1752 (tmpspan->y < pbox->y2))
1756 right = left + tmpspan->width; /* right is not in the span! */
1758 if ((right > pbox->x1) && (left < pbox->x2))
1760 clipped_left = MAX (left, pbox->x1);
1761 clipped_right = MIN (right, pbox->x2);
1764 out_span.x = clipped_left;
1765 out_span.width = clipped_right - clipped_left;
1766 (*function) (&out_span, data);
1772 /* Finished this rectangle.
1773 * The spans could still intersect the next one