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"
78 typedef void (*overlapFunc) (GdkRegion *pReg,
85 typedef void (*nonOverlapFunc) (GdkRegion *pReg,
91 static void miRegionCopy (GdkRegion *dstrgn,
93 static void miRegionOp (GdkRegion *newReg,
96 overlapFunc overlapFn,
97 nonOverlapFunc nonOverlap1Fn,
98 nonOverlapFunc nonOverlap2Fn);
103 * Creates a new empty #GdkRegion.
105 * Returns: a new empty #GdkRegion
112 temp = g_slice_new (GdkRegion);
115 temp->rects = &temp->extents;
116 temp->extents.x1 = 0;
117 temp->extents.y1 = 0;
118 temp->extents.x2 = 0;
119 temp->extents.y2 = 0;
126 * gdk_region_rectangle:
127 * @rectangle: a #GdkRectangle
129 * Creates a new region containing the area @rectangle.
131 * Return value: a new region
134 gdk_region_rectangle (GdkRectangle *rectangle)
138 g_return_val_if_fail (rectangle != NULL, NULL);
140 if (rectangle->width <= 0 || rectangle->height <= 0)
141 return gdk_region_new();
143 temp = g_slice_new (GdkRegion);
146 temp->rects = &temp->extents;
147 temp->extents.x1 = rectangle->x;
148 temp->extents.y1 = rectangle->y;
149 temp->extents.x2 = rectangle->x + rectangle->width;
150 temp->extents.y2 = rectangle->y + rectangle->height;
158 * @region: a #GdkRegion
160 * Copies @region, creating an identical new region.
162 * Return value: a new region identical to @region
165 gdk_region_copy (GdkRegion *region)
169 g_return_val_if_fail (region != NULL, NULL);
171 temp = gdk_region_new ();
173 miRegionCopy (temp, region);
179 * gdk_region_get_clipbox:
180 * @region: a #GdkRegion
181 * @rectangle: return location for the clipbox
183 * Returns the smallest rectangle which includes the entire #GdkRegion.
186 gdk_region_get_clipbox (GdkRegion *region,
187 GdkRectangle *rectangle)
189 g_return_if_fail (region != NULL);
190 g_return_if_fail (rectangle != NULL);
192 rectangle->x = region->extents.x1;
193 rectangle->y = region->extents.y1;
194 rectangle->width = region->extents.x2 - region->extents.x1;
195 rectangle->height = region->extents.y2 - region->extents.y1;
200 * gdk_region_get_rectangles:
201 * @region: a #GdkRegion
202 * @rectangles: return location for an array of rectangles
203 * @n_rectangles: length of returned array
205 * Obtains the area covered by the region as a list of rectangles.
206 * 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 g_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 g_assert(pExtents->x1 < pExtents->x2);
327 * gdk_region_destroy:
328 * @region: a #GdkRegion
330 * Destroys a #GdkRegion.
333 gdk_region_destroy (GdkRegion *region)
335 g_return_if_fail (region != NULL);
337 if (region->rects != ®ion->extents)
338 g_free (region->rects);
339 g_slice_free (GdkRegion, region);
345 * @region: a #GdkRegion
346 * @dx: the distance to move the region horizontally
347 * @dy: the distance to move the region vertically
349 * Moves a region the specified distance.
352 gdk_region_offset (GdkRegion *region,
359 g_return_if_fail (region != NULL);
361 pbox = region->rects;
362 nbox = region->numRects;
372 if (region->rects != ®ion->extents)
374 region->extents.x1 += x;
375 region->extents.x2 += x;
376 region->extents.y1 += y;
377 region->extents.y2 += y;
382 Utility procedure Compress:
383 Replace r by the region r', where
384 p in r' iff (Quantifer m <= dx) (p + m in r), and
385 Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
386 (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
388 Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
389 of all points p such that p and the next dx points on the same
390 horizontal scan line are all in r. We do this using by noting
391 that p is the head of a run of length 2^i + k iff p is the head
392 of a run of length 2^i and p+2^i is the head of a run of length
393 k. Thus, the loop invariant: s contains the region corresponding
394 to the runs of length shift. r contains the region corresponding
395 to the runs of length 1 + dxo & (shift-1), where dxo is the original
396 value of dx. dx = dxo & ~(shift-1). As parameters, s and t are
397 scratch regions, so that we don't have to allocate them on every
401 #define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \
402 else gdk_region_intersect (a,b)
403 #define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \
404 else gdk_region_offset (a,0,b)
407 Compress(GdkRegion *r,
421 ZShiftRegion(r, -(int)shift);
427 ZShiftRegion(s, -(int)shift);
439 * @region: a #GdkRegion
440 * @dx: the number of pixels to shrink the region horizontally
441 * @dy: the number of pixels to shrink the region vertically
443 * Resizes a region by the specified amount.
444 * Positive values shrink the region. Negative values expand it.
447 gdk_region_shrink (GdkRegion *region,
454 g_return_if_fail (region != NULL);
459 s = gdk_region_new ();
460 t = gdk_region_new ();
466 Compress(region, s, t, (unsigned) 2*dx, TRUE, grow);
472 Compress(region, s, t, (unsigned) 2*dy, FALSE, grow);
474 gdk_region_offset (region, dx, dy);
475 gdk_region_destroy (s);
476 gdk_region_destroy (t);
480 /*======================================================================
481 * Region Intersection
482 *====================================================================*/
484 *-----------------------------------------------------------------------
486 * Handle an overlapping band for miIntersect.
492 * Rectangles may be added to the region.
494 *-----------------------------------------------------------------------
498 miIntersectO (GdkRegion *pReg,
508 GdkRegionBox *pNextRect;
510 pNextRect = &pReg->rects[pReg->numRects];
512 while ((r1 != r1End) && (r2 != r2End))
514 x1 = MAX (r1->x1,r2->x1);
515 x2 = MIN (r1->x2,r2->x2);
518 * If there's any overlap between the two rectangles, add that
519 * overlap to the new region.
520 * There's no need to check for subsumption because the only way
521 * such a need could arise is if some region has two rectangles
522 * right next to each other. Since that should never happen...
528 MEMCHECK (pReg, pNextRect, pReg->rects);
535 g_assert (pReg->numRects <= pReg->size);
539 * Need to advance the pointers. Shift the one that extends
540 * to the right the least, since the other still has a chance to
541 * overlap with that region's next rectangle, if you see what I mean.
547 else if (r2->x2 < r1->x2)
560 * gdk_region_intersect:
561 * @source1: a #GdkRegion
562 * @source2: another #GdkRegion
564 * Sets the area of @source1 to the intersection of the areas of @source1
565 * and @source2. The resulting area is the set of pixels contained in
566 * both @source1 and @source2.
569 gdk_region_intersect (GdkRegion *source1,
572 g_return_if_fail (source1 != NULL);
573 g_return_if_fail (source2 != NULL);
575 /* check for trivial reject */
576 if ((!(source1->numRects)) || (!(source2->numRects)) ||
577 (!EXTENTCHECK(&source1->extents, &source2->extents)))
578 source1->numRects = 0;
580 miRegionOp (source1, source1, source2,
581 miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL);
584 * Can't alter source1's extents before miRegionOp depends on the
585 * extents of the regions being unchanged. Besides, this way there's
586 * no checking against rectangles that will be nuked due to
587 * coalescing, so we have to examine fewer rectangles.
589 miSetExtents(source1);
593 miRegionCopy (GdkRegion *dstrgn,
596 if (dstrgn != rgn) /* don't want to copy to itself */
598 if (dstrgn->size < rgn->numRects)
600 if (dstrgn->rects != &dstrgn->extents)
601 g_free (dstrgn->rects);
603 dstrgn->rects = g_new (GdkRegionBox, rgn->numRects);
604 dstrgn->size = rgn->numRects;
607 dstrgn->numRects = rgn->numRects;
608 dstrgn->extents = rgn->extents;
610 memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox));
615 /*======================================================================
616 * Generic Region Operator
617 *====================================================================*/
620 *-----------------------------------------------------------------------
622 * Attempt to merge the boxes in the current band with those in the
623 * previous one. Used only by miRegionOp.
626 * The new index for the previous band.
629 * If coalescing takes place:
630 * - rectangles in the previous band will have their y2 fields
632 * - pReg->numRects will be decreased.
634 *-----------------------------------------------------------------------
638 miCoalesce (GdkRegion *pReg, /* Region to coalesce */
639 gint prevStart, /* Index of start of previous band */
640 gint curStart) /* Index of start of current band */
642 GdkRegionBox *pPrevBox; /* Current box in previous band */
643 GdkRegionBox *pCurBox; /* Current box in current band */
644 GdkRegionBox *pRegEnd; /* End of region */
645 int curNumRects; /* Number of rectangles in current
647 int prevNumRects; /* Number of rectangles in previous
649 int bandY1; /* Y1 coordinate for current band */
651 pRegEnd = &pReg->rects[pReg->numRects];
653 pPrevBox = &pReg->rects[prevStart];
654 prevNumRects = curStart - prevStart;
657 * Figure out how many rectangles are in the current band. Have to do
658 * this because multiple bands could have been added in miRegionOp
659 * at the end when one region has been exhausted.
661 pCurBox = &pReg->rects[curStart];
662 bandY1 = pCurBox->y1;
663 for (curNumRects = 0;
664 (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
670 if (pCurBox != pRegEnd)
673 * If more than one band was added, we have to find the start
674 * of the last band added so the next coalescing job can start
675 * at the right place... (given when multiple bands are added,
676 * this may be pointless -- see above).
679 while (pRegEnd[-1].y1 == pRegEnd->y1)
683 curStart = pRegEnd - pReg->rects;
684 pRegEnd = pReg->rects + pReg->numRects;
687 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
688 pCurBox -= curNumRects;
690 * The bands may only be coalesced if the bottom of the previous
691 * matches the top scanline of the current.
693 if (pPrevBox->y2 == pCurBox->y1)
696 * Make sure the bands have boxes in the same places. This
697 * assumes that boxes have been added in such a way that they
698 * cover the most area possible. I.e. two boxes in a band must
699 * have some horizontal space between them.
703 if ((pPrevBox->x1 != pCurBox->x1) ||
704 (pPrevBox->x2 != pCurBox->x2))
707 * The bands don't line up so they can't be coalesced.
714 } while (prevNumRects != 0);
716 pReg->numRects -= curNumRects;
717 pCurBox -= curNumRects;
718 pPrevBox -= curNumRects;
721 * The bands may be merged, so set the bottom y of each box
722 * in the previous band to that of the corresponding box in
727 pPrevBox->y2 = pCurBox->y2;
732 while (curNumRects != 0);
735 * If only one band was added to the region, we have to backup
736 * curStart to the start of the previous band.
738 * If more than one band was added to the region, copy the
739 * other bands down. The assumption here is that the other bands
740 * came from the same region as the current one and no further
741 * coalescing can be done on them since it's all been done
742 * already... curStart is already in the right place.
744 if (pCurBox == pRegEnd)
746 curStart = prevStart;
752 *pPrevBox++ = *pCurBox++;
754 while (pCurBox != pRegEnd);
763 *-----------------------------------------------------------------------
765 * Apply an operation to two regions. Called by miUnion, miInverse,
766 * miSubtract, miIntersect...
772 * The new region is overwritten.
775 * The idea behind this function is to view the two regions as sets.
776 * Together they cover a rectangle of area that this function divides
777 * into horizontal bands where points are covered only by one region
778 * or by both. For the first case, the nonOverlapFunc is called with
779 * each the band and the band's upper and lower extents. For the
780 * second, the overlapFunc is called to process the entire band. It
781 * is responsible for clipping the rectangles in the band, though
782 * this function provides the boundaries.
783 * At the end of each band, the new region is coalesced, if possible,
784 * to reduce the number of rectangles in the region.
786 *-----------------------------------------------------------------------
790 miRegionOp(GdkRegion *newReg,
793 overlapFunc overlapFn, /* Function to call for over-
795 nonOverlapFunc nonOverlap1Fn, /* Function to call for non-
796 * overlapping bands in region
798 nonOverlapFunc nonOverlap2Fn) /* Function to call for non-
799 * overlapping bands in region
802 GdkRegionBox *r1; /* Pointer into first region */
803 GdkRegionBox *r2; /* Pointer into 2d region */
804 GdkRegionBox *r1End; /* End of 1st region */
805 GdkRegionBox *r2End; /* End of 2d region */
806 int ybot; /* Bottom of intersection */
807 int ytop; /* Top of intersection */
808 GdkRegionBox *oldRects; /* Old rects for newReg */
809 int prevBand; /* Index of start of
810 * previous band in newReg */
811 int curBand; /* Index of start of current
813 GdkRegionBox *r1BandEnd; /* End of current band in r1 */
814 GdkRegionBox *r2BandEnd; /* End of current band in r2 */
815 int top; /* Top of non-overlapping
817 int bot; /* Bottom of non-overlapping
822 * set r1, r2, r1End and r2End appropriately, preserve the important
823 * parts of the destination region until the end in case it's one of
824 * the two source regions, then mark the "new" region empty, allocating
825 * another array of rectangles for it to use.
829 r1End = r1 + reg1->numRects;
830 r2End = r2 + reg2->numRects;
832 oldRects = newReg->rects;
834 EMPTY_REGION(newReg);
837 * Allocate a reasonable number of rectangles for the new region. The idea
838 * is to allocate enough so the individual functions don't need to
839 * reallocate and copy the array, which is time consuming, yet we don't
840 * have to worry about using too much memory. I hope to be able to
841 * nuke the Xrealloc() at the end of this function eventually.
843 newReg->size = MAX (reg1->numRects, reg2->numRects) * 2;
844 newReg->rects = g_new (GdkRegionBox, newReg->size);
847 * Initialize ybot and ytop.
848 * In the upcoming loop, ybot and ytop serve different functions depending
849 * on whether the band being handled is an overlapping or non-overlapping
851 * In the case of a non-overlapping band (only one of the regions
852 * has points in the band), ybot is the bottom of the most recent
853 * intersection and thus clips the top of the rectangles in that band.
854 * ytop is the top of the next intersection between the two regions and
855 * serves to clip the bottom of the rectangles in the current band.
856 * For an overlapping band (where the two regions intersect), ytop clips
857 * the top of the rectangles of both regions and ybot clips the bottoms.
859 if (reg1->extents.y1 < reg2->extents.y1)
860 ybot = reg1->extents.y1;
862 ybot = reg2->extents.y1;
865 * prevBand serves to mark the start of the previous band so rectangles
866 * can be coalesced into larger rectangles. qv. miCoalesce, above.
867 * In the beginning, there is no previous band, so prevBand == curBand
868 * (curBand is set later on, of course, but the first band will always
869 * start at index 0). prevBand and curBand must be indices because of
870 * the possible expansion, and resultant moving, of the new region's
871 * array of rectangles.
877 curBand = newReg->numRects;
880 * This algorithm proceeds one source-band (as opposed to a
881 * destination band, which is determined by where the two regions
882 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
883 * rectangle after the last one in the current band for their
884 * respective regions.
887 while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
893 while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
899 * First handle the band that doesn't intersect, if any.
901 * Note that attention is restricted to one band in the
902 * non-intersecting region at once, so if a region has n
903 * bands between the current position and the next place it overlaps
904 * the other, this entire loop will be passed through n times.
908 top = MAX (r1->y1,ybot);
909 bot = MIN (r1->y2,r2->y1);
911 if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL))
913 (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot);
918 else if (r2->y1 < r1->y1)
920 top = MAX (r2->y1,ybot);
921 bot = MIN (r2->y2,r1->y1);
923 if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL))
925 (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot);
936 * If any rectangles got added to the region, try and coalesce them
937 * with rectangles from the previous band. Note we could just do
938 * this test in miCoalesce, but some machines incur a not
939 * inconsiderable cost for function calls, so...
941 if (newReg->numRects != curBand)
943 prevBand = miCoalesce (newReg, prevBand, curBand);
947 * Now see if we've hit an intersecting band. The two bands only
948 * intersect if ybot > ytop
950 ybot = MIN (r1->y2, r2->y2);
951 curBand = newReg->numRects;
954 (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
958 if (newReg->numRects != curBand)
960 prevBand = miCoalesce (newReg, prevBand, curBand);
964 * If we've finished with a band (y2 == ybot) we skip forward
965 * in the region to the next band.
975 } while ((r1 != r1End) && (r2 != r2End));
978 * Deal with whichever region still has rectangles left.
980 curBand = newReg->numRects;
983 if (nonOverlap1Fn != (nonOverlapFunc )NULL)
988 while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
992 (* nonOverlap1Fn) (newReg, r1, r1BandEnd,
993 MAX (r1->y1,ybot), r1->y2);
995 } while (r1 != r1End);
998 else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL))
1003 while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
1007 (* nonOverlap2Fn) (newReg, r2, r2BandEnd,
1008 MAX (r2->y1,ybot), r2->y2);
1010 } while (r2 != r2End);
1013 if (newReg->numRects != curBand)
1015 (void) miCoalesce (newReg, prevBand, curBand);
1019 * A bit of cleanup. To keep regions from growing without bound,
1020 * we shrink the array of rectangles to match the new number of
1021 * rectangles in the region. This never goes to 0, however...
1023 * Only do this stuff if the number of rectangles allocated is more than
1024 * twice the number of rectangles in the region (a simple optimization...).
1026 if (newReg->numRects < (newReg->size >> 1))
1028 if (REGION_NOT_EMPTY (newReg))
1030 newReg->size = newReg->numRects;
1031 newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size);
1036 * No point in doing the extra work involved in an Xrealloc if
1037 * the region is empty
1040 g_free (newReg->rects);
1041 newReg->rects = &newReg->extents;
1045 if (oldRects != &newReg->extents)
1050 /*======================================================================
1052 *====================================================================*/
1055 *-----------------------------------------------------------------------
1057 * Handle a non-overlapping band for the union operation. Just
1058 * Adds the rectangles into the region. Doesn't have to check for
1059 * subsumption or anything.
1065 * pReg->numRects is incremented and the final rectangles overwritten
1066 * with the rectangles we're passed.
1068 *-----------------------------------------------------------------------
1071 miUnionNonO (GdkRegion *pReg,
1077 GdkRegionBox *pNextRect;
1079 pNextRect = &pReg->rects[pReg->numRects];
1085 g_assert(r->x1 < r->x2);
1086 MEMCHECK(pReg, pNextRect, pReg->rects);
1087 pNextRect->x1 = r->x1;
1089 pNextRect->x2 = r->x2;
1091 pReg->numRects += 1;
1094 g_assert(pReg->numRects<=pReg->size);
1101 *-----------------------------------------------------------------------
1103 * Handle an overlapping band for the union operation. Picks the
1104 * left-most rectangle each time and merges it into the region.
1110 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1113 *-----------------------------------------------------------------------
1118 miUnionO (GdkRegion *pReg,
1120 GdkRegionBox *r1End,
1122 GdkRegionBox *r2End,
1126 GdkRegionBox * pNextRect;
1128 pNextRect = &pReg->rects[pReg->numRects];
1130 #define MERGERECT(r) \
1131 if ((pReg->numRects != 0) && \
1132 (pNextRect[-1].y1 == y1) && \
1133 (pNextRect[-1].y2 == y2) && \
1134 (pNextRect[-1].x2 >= r->x1)) \
1136 if (pNextRect[-1].x2 < r->x2) \
1138 pNextRect[-1].x2 = r->x2; \
1139 g_assert(pNextRect[-1].x1<pNextRect[-1].x2); \
1144 MEMCHECK(pReg, pNextRect, pReg->rects); \
1145 pNextRect->y1 = y1; \
1146 pNextRect->y2 = y2; \
1147 pNextRect->x1 = r->x1; \
1148 pNextRect->x2 = r->x2; \
1149 pReg->numRects += 1; \
1152 g_assert(pReg->numRects<=pReg->size); \
1156 while ((r1 != r1End) && (r2 != r2End))
1158 if (r1->x1 < r2->x1)
1173 } while (r1 != r1End);
1175 else while (r2 != r2End)
1183 * @source1: a #GdkRegion
1184 * @source2: a #GdkRegion
1186 * Sets the area of @source1 to the union of the areas of @source1 and
1187 * @source2. The resulting area is the set of pixels contained in
1188 * either @source1 or @source2.
1191 gdk_region_union (GdkRegion *source1,
1194 g_return_if_fail (source1 != NULL);
1195 g_return_if_fail (source2 != NULL);
1197 /* checks all the simple cases */
1200 * source1 and source2 are the same or source2 is empty
1202 if ((source1 == source2) || (!(source2->numRects)))
1208 if (!(source1->numRects))
1210 miRegionCopy (source1, source2);
1215 * source1 completely subsumes source2
1217 if ((source1->numRects == 1) &&
1218 (source1->extents.x1 <= source2->extents.x1) &&
1219 (source1->extents.y1 <= source2->extents.y1) &&
1220 (source1->extents.x2 >= source2->extents.x2) &&
1221 (source1->extents.y2 >= source2->extents.y2))
1225 * source2 completely subsumes source1
1227 if ((source2->numRects == 1) &&
1228 (source2->extents.x1 <= source1->extents.x1) &&
1229 (source2->extents.y1 <= source1->extents.y1) &&
1230 (source2->extents.x2 >= source1->extents.x2) &&
1231 (source2->extents.y2 >= source1->extents.y2))
1233 miRegionCopy(source1, source2);
1237 miRegionOp (source1, source1, source2, miUnionO,
1238 miUnionNonO, miUnionNonO);
1240 source1->extents.x1 = MIN (source1->extents.x1, source2->extents.x1);
1241 source1->extents.y1 = MIN (source1->extents.y1, source2->extents.y1);
1242 source1->extents.x2 = MAX (source1->extents.x2, source2->extents.x2);
1243 source1->extents.y2 = MAX (source1->extents.y2, source2->extents.y2);
1247 /*======================================================================
1248 * Region Subtraction
1249 *====================================================================*/
1252 *-----------------------------------------------------------------------
1254 * Deal with non-overlapping band for subtraction. Any parts from
1255 * region 2 we discard. Anything from region 1 we add to the region.
1261 * pReg may be affected.
1263 *-----------------------------------------------------------------------
1267 miSubtractNonO1 (GdkRegion *pReg,
1273 GdkRegionBox * pNextRect;
1275 pNextRect = &pReg->rects[pReg->numRects];
1281 g_assert (r->x1<r->x2);
1282 MEMCHECK (pReg, pNextRect, pReg->rects);
1283 pNextRect->x1 = r->x1;
1285 pNextRect->x2 = r->x2;
1287 pReg->numRects += 1;
1290 g_assert (pReg->numRects <= pReg->size);
1297 *-----------------------------------------------------------------------
1299 * Overlapping band subtraction. x1 is the left-most point not yet
1306 * pReg may have rectangles added to it.
1308 *-----------------------------------------------------------------------
1312 miSubtractO (GdkRegion *pReg,
1314 GdkRegionBox *r1End,
1316 GdkRegionBox *r2End,
1320 GdkRegionBox * pNextRect;
1326 pNextRect = &pReg->rects[pReg->numRects];
1328 while ((r1 != r1End) && (r2 != r2End))
1333 * Subtrahend missed the boat: go to next subtrahend.
1337 else if (r2->x1 <= x1)
1340 * Subtrahend preceeds minuend: nuke left edge of minuend.
1346 * Minuend completely covered: advance to next minuend and
1347 * reset left fence to edge of new minuend.
1356 * Subtrahend now used up since it doesn't extend beyond
1362 else if (r2->x1 < r1->x2)
1365 * Left part of subtrahend covers part of minuend: add uncovered
1366 * part of minuend to region and skip to next subtrahend.
1368 g_assert(x1<r2->x1);
1369 MEMCHECK(pReg, pNextRect, pReg->rects);
1372 pNextRect->x2 = r2->x1;
1374 pReg->numRects += 1;
1377 g_assert(pReg->numRects<=pReg->size);
1383 * Minuend used up: advance to new...
1392 * Subtrahend used up
1400 * Minuend used up: add any remaining piece before advancing.
1404 MEMCHECK(pReg, pNextRect, pReg->rects);
1407 pNextRect->x2 = r1->x2;
1409 pReg->numRects += 1;
1411 g_assert(pReg->numRects<=pReg->size);
1420 * Add remaining minuend rectangles to region.
1424 g_assert(x1<r1->x2);
1425 MEMCHECK(pReg, pNextRect, pReg->rects);
1428 pNextRect->x2 = r1->x2;
1430 pReg->numRects += 1;
1433 g_assert(pReg->numRects<=pReg->size);
1444 * gdk_region_subtract:
1445 * @source1: a #GdkRegion
1446 * @source2: another #GdkRegion
1448 * Subtracts the area of @source2 from the area @source1. The resulting
1449 * area is the set of pixels contained in @source1 but not in @source2.
1452 gdk_region_subtract (GdkRegion *source1,
1455 g_return_if_fail (source1 != NULL);
1456 g_return_if_fail (source2 != NULL);
1458 /* check for trivial reject */
1459 if ((!(source1->numRects)) || (!(source2->numRects)) ||
1460 (!EXTENTCHECK(&source1->extents, &source2->extents)))
1463 miRegionOp (source1, source1, source2, miSubtractO,
1464 miSubtractNonO1, (nonOverlapFunc) NULL);
1467 * Can't alter source1's extents before we call miRegionOp because miRegionOp
1468 * depends on the extents of those regions being the unaltered. Besides, this
1469 * way there's no checking against rectangles that will be nuked
1470 * due to coalescing, so we have to examine fewer rectangles.
1472 miSetExtents (source1);
1477 * @source1: a #GdkRegion
1478 * @source2: another #GdkRegion
1480 * Sets the area of @source1 to the exclusive-OR of the areas of @source1
1481 * and @source2. The resulting area is the set of pixels contained in one
1482 * or the other of the two sources but not in both.
1485 gdk_region_xor (GdkRegion *source1,
1490 g_return_if_fail (source1 != NULL);
1491 g_return_if_fail (source2 != NULL);
1493 trb = gdk_region_copy (source2);
1495 gdk_region_subtract (trb, source1);
1496 gdk_region_subtract (source1, source2);
1498 gdk_region_union (source1, trb);
1500 gdk_region_destroy (trb);
1505 * @region: a #GdkRegion
1507 * Returns %TRUE if the #GdkRegion is empty.
1509 * Returns: %TRUE if @region is empty.
1512 gdk_region_empty (GdkRegion *region)
1514 g_return_val_if_fail (region != NULL, FALSE);
1516 if (region->numRects == 0)
1524 * @region1: a #GdkRegion
1525 * @region2: a #GdkRegion
1527 * Returns %TRUE if the two regions are the same.
1529 * Returns: %TRUE if @region1 and @region2 are equal.
1532 gdk_region_equal (GdkRegion *region1,
1537 g_return_val_if_fail (region1 != NULL, FALSE);
1538 g_return_val_if_fail (region2 != NULL, FALSE);
1540 if (region1->numRects != region2->numRects) return FALSE;
1541 else if (region1->numRects == 0) return TRUE;
1542 else if (region1->extents.x1 != region2->extents.x1) return FALSE;
1543 else if (region1->extents.x2 != region2->extents.x2) return FALSE;
1544 else if (region1->extents.y1 != region2->extents.y1) return FALSE;
1545 else if (region1->extents.y2 != region2->extents.y2) return FALSE;
1547 for(i = 0; i < region1->numRects; i++ )
1549 if (region1->rects[i].x1 != region2->rects[i].x1) return FALSE;
1550 else if (region1->rects[i].x2 != region2->rects[i].x2) return FALSE;
1551 else if (region1->rects[i].y1 != region2->rects[i].y1) return FALSE;
1552 else if (region1->rects[i].y2 != region2->rects[i].y2) return FALSE;
1558 * gdk_region_point_in:
1559 * @region: a #GdkRegion
1560 * @x: the x coordinate of a point
1561 * @y: the y coordinate of a point
1563 * Returns %TRUE if a point is in a region.
1565 * Returns: %TRUE if the point is in @region.
1568 gdk_region_point_in (GdkRegion *region,
1574 g_return_val_if_fail (region != NULL, FALSE);
1576 if (region->numRects == 0)
1578 if (!INBOX(region->extents, x, y))
1580 for (i = 0; i < region->numRects; i++)
1582 if (INBOX (region->rects[i], x, y))
1589 * gdk_region_rect_in:
1590 * @region: a #GdkRegion.
1591 * @rectangle: a #GdkRectangle.
1593 * Tests whether a rectangle is within a region.
1595 * Returns: %GDK_OVERLAP_RECTANGLE_IN, %GDK_OVERLAP_RECTANGLE_OUT, or
1596 * %GDK_OVERLAP_RECTANGLE_PART, depending on whether the rectangle is inside,
1597 * outside, or partly inside the #GdkRegion, respectively.
1600 gdk_region_rect_in (GdkRegion *region,
1601 GdkRectangle *rectangle)
1604 GdkRegionBox *pboxEnd;
1606 GdkRegionBox *prect = ▭
1607 gboolean partIn, partOut;
1610 g_return_val_if_fail (region != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1611 g_return_val_if_fail (rectangle != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1618 prect->x2 = rx + rectangle->width;
1619 prect->y2 = ry + rectangle->height;
1621 /* this is (just) a useful optimization */
1622 if ((region->numRects == 0) || !EXTENTCHECK (®ion->extents, prect))
1623 return GDK_OVERLAP_RECTANGLE_OUT;
1628 /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
1629 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1635 continue; /* getting up to speed or skipping remainder of band */
1639 partOut = TRUE; /* missed part of rectangle above */
1640 if (partIn || (pbox->y1 >= prect->y2))
1642 ry = pbox->y1; /* x guaranteed to be == prect->x1 */
1646 continue; /* not far enough over yet */
1650 partOut = TRUE; /* missed part of rectangle to left */
1655 if (pbox->x1 < prect->x2)
1657 partIn = TRUE; /* definitely overlap */
1662 if (pbox->x2 >= prect->x2)
1664 ry = pbox->y2; /* finished with this band */
1665 if (ry >= prect->y2)
1667 rx = prect->x1; /* reset x out to left again */
1672 * Because boxes in a band are maximal width, if the first box
1673 * to overlap the rectangle doesn't completely cover it in that
1674 * band, the rectangle must be partially out, since some of it
1675 * will be uncovered in that band. partIn will have been set true
1685 GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) :
1686 GDK_OVERLAP_RECTANGLE_OUT);
1691 gdk_region_unsorted_spans_intersect_foreach (GdkRegion *region,
1694 GdkSpanFunc function,
1697 gint i, left, right, y;
1698 gint clipped_left, clipped_right;
1700 GdkRegionBox *pboxEnd;
1703 if (!region->numRects)
1706 for (i=0;i<n_spans;i++)
1710 right = left + spans[i].width; /* right is not in the span! */
1712 if (! ((region->extents.y1 <= y) &&
1713 (region->extents.y2 > y) &&
1714 (region->extents.x1 < right) &&
1715 (region->extents.x2 > left)) )
1718 /* can stop when we passed y */
1719 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1724 continue; /* Not quite there yet */
1727 break; /* passed the spanline */
1729 if ((right > pbox->x1) && (left < pbox->x2))
1731 clipped_left = MAX (left, pbox->x1);
1732 clipped_right = MIN (right, pbox->x2);
1735 out_span.x = clipped_left;
1736 out_span.width = clipped_right - clipped_left;
1737 (*function) (&out_span, data);
1744 * gdk_region_spans_intersect_foreach:
1745 * @region: a #GdkRegion
1746 * @spans: an array of #GdkSpans
1747 * @n_spans: the length of @spans
1748 * @sorted: %TRUE if @spans is sorted wrt. the y coordinate
1749 * @function: function to call on each span in the intersection
1750 * @data: data to pass to @function
1752 * Calls a function on each span in the intersection of @region and @spans.
1755 gdk_region_spans_intersect_foreach (GdkRegion *region,
1759 GdkSpanFunc function,
1762 gint left, right, y;
1763 gint clipped_left, clipped_right;
1765 GdkRegionBox *pboxEnd;
1766 GdkSpan *span, *tmpspan;
1770 g_return_if_fail (region != NULL);
1771 g_return_if_fail (spans != NULL);
1775 gdk_region_unsorted_spans_intersect_foreach (region,
1783 if ((!region->numRects) || (n_spans == 0))
1786 /* The main method here is to step along the
1787 * sorted rectangles and spans in lock step, and
1788 * clipping the spans that are in the current
1789 * rectangle before going on to the next rectangle.
1793 end_span = spans + n_spans;
1794 pbox = region->rects;
1795 pboxEnd = pbox + region->numRects;
1796 while (pbox < pboxEnd)
1798 while ((pbox->y2 < span->y) || (span->y < pbox->y1))
1800 /* Skip any rectangles that are above the current span */
1801 if (pbox->y2 < span->y)
1804 if (pbox == pboxEnd)
1807 /* Skip any spans that are above the current rectangle */
1808 if (span->y < pbox->y1)
1811 if (span == end_span)
1816 /* Ok, we got at least one span that might intersect this rectangle. */
1818 while ((tmpspan < end_span) &&
1819 (tmpspan->y < pbox->y2))
1823 right = left + tmpspan->width; /* right is not in the span! */
1825 if ((right > pbox->x1) && (left < pbox->x2))
1827 clipped_left = MAX (left, pbox->x1);
1828 clipped_right = MIN (right, pbox->x2);
1831 out_span.x = clipped_left;
1832 out_span.width = clipped_right - clipped_left;
1833 (*function) (&out_span, data);
1839 /* Finished this rectangle.
1840 * The spans could still intersect the next one
1846 #define __GDK_REGION_GENERIC_C__
1847 #include "gdkaliasdef.c"