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 if (rectangle->width <= 0 || rectangle->height <= 0)
140 return gdk_region_new();
142 temp = g_new (GdkRegion, 1);
143 temp->rects = g_new (GdkRegionBox, 1);
146 temp->extents.x1 = temp->rects[0].x1 = rectangle->x;
147 temp->extents.y1 = temp->rects[0].y1 = rectangle->y;
148 temp->extents.x2 = temp->rects[0].x2 = rectangle->x + rectangle->width;
149 temp->extents.y2 = temp->rects[0].y2 = rectangle->y + rectangle->height;
157 * @region: a #GdkRegion
159 * Copies @region, creating an identical new region.
161 * Return value: a new region identical to @region
164 gdk_region_copy (GdkRegion *region)
168 temp = g_new (GdkRegion, 1);
169 temp->rects = g_new (GdkRegionBox, region->numRects);
171 temp->numRects = region->numRects;
172 temp->extents = region->extents;
173 temp->size = region->numRects;
175 memcpy (temp->rects, region->rects, region->numRects * sizeof (GdkRegionBox));
181 gdk_region_get_clipbox (GdkRegion *r, GdkRectangle *rect)
183 rect->x = r->extents.x1;
184 rect->y = r->extents.y1;
185 rect->width = r->extents.x2 - r->extents.x1;
186 rect->height = r->extents.y2 - r->extents.y1;
191 * gdk_region_get_rectangles:
192 * @region: a #GdkRegion
193 * @rectangles: return location for an array of rectangles
194 * @n_rectangles: length of returned array
196 * Obtains the area covered by the region as a list of rectangles.
197 * The array returned in @rectangles must be freed with g_free().
201 gdk_region_get_rectangles (GdkRegion *region,
202 GdkRectangle **rectangles,
207 g_return_if_fail (region != NULL);
208 g_return_if_fail (rectangles != NULL);
209 g_return_if_fail (n_rectangles != NULL);
211 *n_rectangles = region->numRects;
212 *rectangles = g_new (GdkRectangle, region->numRects);
214 for (i = 0; i < region->numRects; i++)
217 rect = region->rects[i];
218 (*rectangles)[i].x = rect.x1;
219 (*rectangles)[i].y = rect.y1;
220 (*rectangles)[i].width = rect.x2 - rect.x1;
221 (*rectangles)[i].height = rect.y2 - rect.y1;
226 gdk_region_union_with_rect (GdkRegion *region,
229 GdkRegion tmp_region;
231 if (!rect->width || !rect->height)
234 tmp_region.rects = &tmp_region.extents;
235 tmp_region.numRects = 1;
236 tmp_region.extents.x1 = rect->x;
237 tmp_region.extents.y1 = rect->y;
238 tmp_region.extents.x2 = rect->x + rect->width;
239 tmp_region.extents.y2 = rect->y + rect->height;
242 gdk_region_union (region, &tmp_region);
246 *-----------------------------------------------------------------------
248 * Reset the extents of a region to what they should be. Called by
249 * miSubtract and miIntersect b/c they can't figure it out along the
250 * way or do so easily, as miUnion can.
256 * The region's 'extents' structure is overwritten.
258 *-----------------------------------------------------------------------
261 miSetExtents (GdkRegion *pReg)
263 GdkRegionBox *pBox, *pBoxEnd, *pExtents;
265 if (pReg->numRects == 0)
267 pReg->extents.x1 = 0;
268 pReg->extents.y1 = 0;
269 pReg->extents.x2 = 0;
270 pReg->extents.y2 = 0;
274 pExtents = &pReg->extents;
276 pBoxEnd = &pBox[pReg->numRects - 1];
279 * Since pBox is the first rectangle in the region, it must have the
280 * smallest y1 and since pBoxEnd is the last rectangle in the region,
281 * it must have the largest y2, because of banding. Initialize x1 and
282 * x2 from pBox and pBoxEnd, resp., as good things to initialize them
285 pExtents->x1 = pBox->x1;
286 pExtents->y1 = pBox->y1;
287 pExtents->x2 = pBoxEnd->x2;
288 pExtents->y2 = pBoxEnd->y2;
290 assert(pExtents->y1 < pExtents->y2);
291 while (pBox <= pBoxEnd)
293 if (pBox->x1 < pExtents->x1)
295 pExtents->x1 = pBox->x1;
297 if (pBox->x2 > pExtents->x2)
299 pExtents->x2 = pBox->x2;
303 assert(pExtents->x1 < pExtents->x2);
307 gdk_region_destroy (GdkRegion *r)
314 /* TranslateRegion(pRegion, x, y)
320 gdk_region_offset (GdkRegion *region,
327 pbox = region->rects;
328 nbox = region->numRects;
338 region->extents.x1 += x;
339 region->extents.x2 += x;
340 region->extents.y1 += y;
341 region->extents.y2 += y;
345 Utility procedure Compress:
346 Replace r by the region r', where
347 p in r' iff (Quantifer m <= dx) (p + m in r), and
348 Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
349 (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
351 Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
352 of all points p such that p and the next dx points on the same
353 horizontal scan line are all in r. We do this using by noting
354 that p is the head of a run of length 2^i + k iff p is the head
355 of a run of length 2^i and p+2^i is the head of a run of length
356 k. Thus, the loop invariant: s contains the region corresponding
357 to the runs of length shift. r contains the region corresponding
358 to the runs of length 1 + dxo & (shift-1), where dxo is the original
359 value of dx. dx = dxo & ~(shift-1). As parameters, s and t are
360 scratch regions, so that we don't have to allocate them on every
364 #define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \
365 else gdk_region_intersect (a,b)
366 #define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \
367 else gdk_region_offset (a,0,b)
370 Compress(GdkRegion *r,
384 ZShiftRegion(r, -(int)shift);
390 ZShiftRegion(s, -(int)shift);
401 gdk_region_shrink (GdkRegion *r,
411 s = gdk_region_new ();
412 t = gdk_region_new ();
418 Compress(r, s, t, (unsigned) 2*dx, TRUE, grow);
424 Compress(r, s, t, (unsigned) 2*dy, FALSE, grow);
426 gdk_region_offset (r, dx, dy);
427 gdk_region_destroy (s);
428 gdk_region_destroy (t);
432 /*======================================================================
433 * Region Intersection
434 *====================================================================*/
436 *-----------------------------------------------------------------------
438 * Handle an overlapping band for miIntersect.
444 * Rectangles may be added to the region.
446 *-----------------------------------------------------------------------
450 miIntersectO (GdkRegion *pReg,
460 GdkRegionBox *pNextRect;
462 pNextRect = &pReg->rects[pReg->numRects];
464 while ((r1 != r1End) && (r2 != r2End))
466 x1 = MAX (r1->x1,r2->x1);
467 x2 = MIN (r1->x2,r2->x2);
470 * If there's any overlap between the two rectangles, add that
471 * overlap to the new region.
472 * There's no need to check for subsumption because the only way
473 * such a need could arise is if some region has two rectangles
474 * right next to each other. Since that should never happen...
480 MEMCHECK (pReg, pNextRect, pReg->rects);
487 assert (pReg->numRects <= pReg->size);
491 * Need to advance the pointers. Shift the one that extends
492 * to the right the least, since the other still has a chance to
493 * overlap with that region's next rectangle, if you see what I mean.
499 else if (r2->x2 < r1->x2)
512 * gdk_region_intersect:
513 * @source1: a #GdkRegion
514 * @source2: another #GdkRegion
516 * Converts @source1 into the intersection between @source1 and @source2.
517 * That is, after calling this function @source2 will be unchanged and
518 * @source1 will be the areas the two regions have in common.
522 gdk_region_intersect (GdkRegion *region,
525 /* check for trivial reject */
526 if ((!(region->numRects)) || (!(other->numRects)) ||
527 (!EXTENTCHECK(®ion->extents, &other->extents)))
528 region->numRects = 0;
530 miRegionOp (region, region, other,
531 miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL);
534 * Can't alter region's extents before miRegionOp depends on the
535 * extents of the regions being unchanged. Besides, this way there's
536 * no checking against rectangles that will be nuked due to
537 * coalescing, so we have to examine fewer rectangles.
539 miSetExtents(region);
543 miRegionCopy(GdkRegion *dstrgn, GdkRegion *rgn)
545 if (dstrgn != rgn) /* don't want to copy to itself */
547 if (dstrgn->size < rgn->numRects)
549 dstrgn->rects = g_renew (GdkRegionBox, dstrgn->rects, rgn->numRects);
550 dstrgn->size = rgn->numRects;
552 dstrgn->numRects = rgn->numRects;
553 dstrgn->extents.x1 = rgn->extents.x1;
554 dstrgn->extents.y1 = rgn->extents.y1;
555 dstrgn->extents.x2 = rgn->extents.x2;
556 dstrgn->extents.y2 = rgn->extents.y2;
558 memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox));
563 /*======================================================================
564 * Generic Region Operator
565 *====================================================================*/
568 *-----------------------------------------------------------------------
570 * Attempt to merge the boxes in the current band with those in the
571 * previous one. Used only by miRegionOp.
574 * The new index for the previous band.
577 * If coalescing takes place:
578 * - rectangles in the previous band will have their y2 fields
580 * - pReg->numRects will be decreased.
582 *-----------------------------------------------------------------------
586 miCoalesce (GdkRegion *pReg, /* Region to coalesce */
587 gint prevStart, /* Index of start of previous band */
588 gint curStart) /* Index of start of current band */
590 GdkRegionBox *pPrevBox; /* Current box in previous band */
591 GdkRegionBox *pCurBox; /* Current box in current band */
592 GdkRegionBox *pRegEnd; /* End of region */
593 int curNumRects; /* Number of rectangles in current
595 int prevNumRects; /* Number of rectangles in previous
597 int bandY1; /* Y1 coordinate for current band */
599 pRegEnd = &pReg->rects[pReg->numRects];
601 pPrevBox = &pReg->rects[prevStart];
602 prevNumRects = curStart - prevStart;
605 * Figure out how many rectangles are in the current band. Have to do
606 * this because multiple bands could have been added in miRegionOp
607 * at the end when one region has been exhausted.
609 pCurBox = &pReg->rects[curStart];
610 bandY1 = pCurBox->y1;
611 for (curNumRects = 0;
612 (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
618 if (pCurBox != pRegEnd)
621 * If more than one band was added, we have to find the start
622 * of the last band added so the next coalescing job can start
623 * at the right place... (given when multiple bands are added,
624 * this may be pointless -- see above).
627 while (pRegEnd[-1].y1 == pRegEnd->y1)
631 curStart = pRegEnd - pReg->rects;
632 pRegEnd = pReg->rects + pReg->numRects;
635 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
636 pCurBox -= curNumRects;
638 * The bands may only be coalesced if the bottom of the previous
639 * matches the top scanline of the current.
641 if (pPrevBox->y2 == pCurBox->y1)
644 * Make sure the bands have boxes in the same places. This
645 * assumes that boxes have been added in such a way that they
646 * cover the most area possible. I.e. two boxes in a band must
647 * have some horizontal space between them.
651 if ((pPrevBox->x1 != pCurBox->x1) ||
652 (pPrevBox->x2 != pCurBox->x2))
655 * The bands don't line up so they can't be coalesced.
662 } while (prevNumRects != 0);
664 pReg->numRects -= curNumRects;
665 pCurBox -= curNumRects;
666 pPrevBox -= curNumRects;
669 * The bands may be merged, so set the bottom y of each box
670 * in the previous band to that of the corresponding box in
675 pPrevBox->y2 = pCurBox->y2;
680 while (curNumRects != 0);
683 * If only one band was added to the region, we have to backup
684 * curStart to the start of the previous band.
686 * If more than one band was added to the region, copy the
687 * other bands down. The assumption here is that the other bands
688 * came from the same region as the current one and no further
689 * coalescing can be done on them since it's all been done
690 * already... curStart is already in the right place.
692 if (pCurBox == pRegEnd)
694 curStart = prevStart;
700 *pPrevBox++ = *pCurBox++;
702 while (pCurBox != pRegEnd);
711 *-----------------------------------------------------------------------
713 * Apply an operation to two regions. Called by miUnion, miInverse,
714 * miSubtract, miIntersect...
720 * The new region is overwritten.
723 * The idea behind this function is to view the two regions as sets.
724 * Together they cover a rectangle of area that this function divides
725 * into horizontal bands where points are covered only by one region
726 * or by both. For the first case, the nonOverlapFunc is called with
727 * each the band and the band's upper and lower extents. For the
728 * second, the overlapFunc is called to process the entire band. It
729 * is responsible for clipping the rectangles in the band, though
730 * this function provides the boundaries.
731 * At the end of each band, the new region is coalesced, if possible,
732 * to reduce the number of rectangles in the region.
734 *-----------------------------------------------------------------------
738 miRegionOp(GdkRegion *newReg,
741 overlapFunc overlapFn, /* Function to call for over-
743 nonOverlapFunc nonOverlap1Fn, /* Function to call for non-
744 * overlapping bands in region
746 nonOverlapFunc nonOverlap2Fn) /* Function to call for non-
747 * overlapping bands in region
750 GdkRegionBox *r1; /* Pointer into first region */
751 GdkRegionBox *r2; /* Pointer into 2d region */
752 GdkRegionBox *r1End; /* End of 1st region */
753 GdkRegionBox *r2End; /* End of 2d region */
754 int ybot; /* Bottom of intersection */
755 int ytop; /* Top of intersection */
756 GdkRegionBox *oldRects; /* Old rects for newReg */
757 int prevBand; /* Index of start of
758 * previous band in newReg */
759 int curBand; /* Index of start of current
761 GdkRegionBox *r1BandEnd; /* End of current band in r1 */
762 GdkRegionBox *r2BandEnd; /* End of current band in r2 */
763 int top; /* Top of non-overlapping
765 int bot; /* Bottom of non-overlapping
770 * set r1, r2, r1End and r2End appropriately, preserve the important
771 * parts of the destination region until the end in case it's one of
772 * the two source regions, then mark the "new" region empty, allocating
773 * another array of rectangles for it to use.
777 r1End = r1 + reg1->numRects;
778 r2End = r2 + reg2->numRects;
780 oldRects = newReg->rects;
782 EMPTY_REGION(newReg);
785 * Allocate a reasonable number of rectangles for the new region. The idea
786 * is to allocate enough so the individual functions don't need to
787 * reallocate and copy the array, which is time consuming, yet we don't
788 * have to worry about using too much memory. I hope to be able to
789 * nuke the Xrealloc() at the end of this function eventually.
791 newReg->size = MAX (reg1->numRects, reg2->numRects) * 2;
792 newReg->rects = g_new (GdkRegionBox, newReg->size);
795 * Initialize ybot and ytop.
796 * In the upcoming loop, ybot and ytop serve different functions depending
797 * on whether the band being handled is an overlapping or non-overlapping
799 * In the case of a non-overlapping band (only one of the regions
800 * has points in the band), ybot is the bottom of the most recent
801 * intersection and thus clips the top of the rectangles in that band.
802 * ytop is the top of the next intersection between the two regions and
803 * serves to clip the bottom of the rectangles in the current band.
804 * For an overlapping band (where the two regions intersect), ytop clips
805 * the top of the rectangles of both regions and ybot clips the bottoms.
807 if (reg1->extents.y1 < reg2->extents.y1)
808 ybot = reg1->extents.y1;
810 ybot = reg2->extents.y1;
813 * prevBand serves to mark the start of the previous band so rectangles
814 * can be coalesced into larger rectangles. qv. miCoalesce, above.
815 * In the beginning, there is no previous band, so prevBand == curBand
816 * (curBand is set later on, of course, but the first band will always
817 * start at index 0). prevBand and curBand must be indices because of
818 * the possible expansion, and resultant moving, of the new region's
819 * array of rectangles.
825 curBand = newReg->numRects;
828 * This algorithm proceeds one source-band (as opposed to a
829 * destination band, which is determined by where the two regions
830 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
831 * rectangle after the last one in the current band for their
832 * respective regions.
835 while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
841 while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
847 * First handle the band that doesn't intersect, if any.
849 * Note that attention is restricted to one band in the
850 * non-intersecting region at once, so if a region has n
851 * bands between the current position and the next place it overlaps
852 * the other, this entire loop will be passed through n times.
856 top = MAX (r1->y1,ybot);
857 bot = MIN (r1->y2,r2->y1);
859 if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL))
861 (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot);
866 else if (r2->y1 < r1->y1)
868 top = MAX (r2->y1,ybot);
869 bot = MIN (r2->y2,r1->y1);
871 if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL))
873 (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot);
884 * If any rectangles got added to the region, try and coalesce them
885 * with rectangles from the previous band. Note we could just do
886 * this test in miCoalesce, but some machines incur a not
887 * inconsiderable cost for function calls, so...
889 if (newReg->numRects != curBand)
891 prevBand = miCoalesce (newReg, prevBand, curBand);
895 * Now see if we've hit an intersecting band. The two bands only
896 * intersect if ybot > ytop
898 ybot = MIN (r1->y2, r2->y2);
899 curBand = newReg->numRects;
902 (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
906 if (newReg->numRects != curBand)
908 prevBand = miCoalesce (newReg, prevBand, curBand);
912 * If we've finished with a band (y2 == ybot) we skip forward
913 * in the region to the next band.
923 } while ((r1 != r1End) && (r2 != r2End));
926 * Deal with whichever region still has rectangles left.
928 curBand = newReg->numRects;
931 if (nonOverlap1Fn != (nonOverlapFunc )NULL)
936 while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
940 (* nonOverlap1Fn) (newReg, r1, r1BandEnd,
941 MAX (r1->y1,ybot), r1->y2);
943 } while (r1 != r1End);
946 else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL))
951 while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
955 (* nonOverlap2Fn) (newReg, r2, r2BandEnd,
956 MAX (r2->y1,ybot), r2->y2);
958 } while (r2 != r2End);
961 if (newReg->numRects != curBand)
963 (void) miCoalesce (newReg, prevBand, curBand);
967 * A bit of cleanup. To keep regions from growing without bound,
968 * we shrink the array of rectangles to match the new number of
969 * rectangles in the region. This never goes to 0, however...
971 * Only do this stuff if the number of rectangles allocated is more than
972 * twice the number of rectangles in the region (a simple optimization...).
974 if (newReg->numRects < (newReg->size >> 1))
976 if (REGION_NOT_EMPTY (newReg))
978 newReg->size = newReg->numRects;
979 newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size);
984 * No point in doing the extra work involved in an Xrealloc if
985 * the region is empty
988 g_free (newReg->rects);
989 newReg->rects = g_new (GdkRegionBox, 1);
996 /*======================================================================
998 *====================================================================*/
1001 *-----------------------------------------------------------------------
1003 * Handle a non-overlapping band for the union operation. Just
1004 * Adds the rectangles into the region. Doesn't have to check for
1005 * subsumption or anything.
1011 * pReg->numRects is incremented and the final rectangles overwritten
1012 * with the rectangles we're passed.
1014 *-----------------------------------------------------------------------
1017 miUnionNonO (GdkRegion *pReg,
1023 GdkRegionBox *pNextRect;
1025 pNextRect = &pReg->rects[pReg->numRects];
1031 assert(r->x1 < r->x2);
1032 MEMCHECK(pReg, pNextRect, pReg->rects);
1033 pNextRect->x1 = r->x1;
1035 pNextRect->x2 = r->x2;
1037 pReg->numRects += 1;
1040 assert(pReg->numRects<=pReg->size);
1047 *-----------------------------------------------------------------------
1049 * Handle an overlapping band for the union operation. Picks the
1050 * left-most rectangle each time and merges it into the region.
1056 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1059 *-----------------------------------------------------------------------
1064 miUnionO (GdkRegion *pReg,
1066 GdkRegionBox *r1End,
1068 GdkRegionBox *r2End,
1072 GdkRegionBox * pNextRect;
1074 pNextRect = &pReg->rects[pReg->numRects];
1076 #define MERGERECT(r) \
1077 if ((pReg->numRects != 0) && \
1078 (pNextRect[-1].y1 == y1) && \
1079 (pNextRect[-1].y2 == y2) && \
1080 (pNextRect[-1].x2 >= r->x1)) \
1082 if (pNextRect[-1].x2 < r->x2) \
1084 pNextRect[-1].x2 = r->x2; \
1085 assert(pNextRect[-1].x1<pNextRect[-1].x2); \
1090 MEMCHECK(pReg, pNextRect, pReg->rects); \
1091 pNextRect->y1 = y1; \
1092 pNextRect->y2 = y2; \
1093 pNextRect->x1 = r->x1; \
1094 pNextRect->x2 = r->x2; \
1095 pReg->numRects += 1; \
1098 assert(pReg->numRects<=pReg->size); \
1102 while ((r1 != r1End) && (r2 != r2End))
1104 if (r1->x1 < r2->x1)
1119 } while (r1 != r1End);
1121 else while (r2 != r2End)
1128 gdk_region_union (GdkRegion *region,
1131 /* checks all the simple cases */
1134 * region and other are the same or other is empty
1136 if ((region == other) || (!(other->numRects)))
1142 if (!(region->numRects))
1144 miRegionCopy (region, other);
1149 * region completely subsumes otehr
1151 if ((region->numRects == 1) &&
1152 (region->extents.x1 <= other->extents.x1) &&
1153 (region->extents.y1 <= other->extents.y1) &&
1154 (region->extents.x2 >= other->extents.x2) &&
1155 (region->extents.y2 >= other->extents.y2))
1159 * other completely subsumes region
1161 if ((other->numRects == 1) &&
1162 (other->extents.x1 <= region->extents.x1) &&
1163 (other->extents.y1 <= region->extents.y1) &&
1164 (other->extents.x2 >= region->extents.x2) &&
1165 (other->extents.y2 >= region->extents.y2))
1167 miRegionCopy(region, other);
1171 miRegionOp (region, region, other, miUnionO,
1172 miUnionNonO, miUnionNonO);
1174 region->extents.x1 = MIN (region->extents.x1, other->extents.x1);
1175 region->extents.y1 = MIN (region->extents.y1, other->extents.y1);
1176 region->extents.x2 = MAX (region->extents.x2, other->extents.x2);
1177 region->extents.y2 = MAX (region->extents.y2, other->extents.y2);
1181 /*======================================================================
1182 * Region Subtraction
1183 *====================================================================*/
1186 *-----------------------------------------------------------------------
1188 * Deal with non-overlapping band for subtraction. Any parts from
1189 * region 2 we discard. Anything from region 1 we add to the region.
1195 * pReg may be affected.
1197 *-----------------------------------------------------------------------
1201 miSubtractNonO1 (GdkRegion *pReg,
1207 GdkRegionBox * pNextRect;
1209 pNextRect = &pReg->rects[pReg->numRects];
1215 assert (r->x1<r->x2);
1216 MEMCHECK (pReg, pNextRect, pReg->rects);
1217 pNextRect->x1 = r->x1;
1219 pNextRect->x2 = r->x2;
1221 pReg->numRects += 1;
1224 assert (pReg->numRects <= pReg->size);
1231 *-----------------------------------------------------------------------
1233 * Overlapping band subtraction. x1 is the left-most point not yet
1240 * pReg may have rectangles added to it.
1242 *-----------------------------------------------------------------------
1246 miSubtractO (GdkRegion *pReg,
1248 GdkRegionBox *r1End,
1250 GdkRegionBox *r2End,
1254 GdkRegionBox * pNextRect;
1260 pNextRect = &pReg->rects[pReg->numRects];
1262 while ((r1 != r1End) && (r2 != r2End))
1267 * Subtrahend missed the boat: go to next subtrahend.
1271 else if (r2->x1 <= x1)
1274 * Subtrahend preceeds minuend: nuke left edge of minuend.
1280 * Minuend completely covered: advance to next minuend and
1281 * reset left fence to edge of new minuend.
1290 * Subtrahend now used up since it doesn't extend beyond
1296 else if (r2->x1 < r1->x2)
1299 * Left part of subtrahend covers part of minuend: add uncovered
1300 * part of minuend to region and skip to next subtrahend.
1303 MEMCHECK(pReg, pNextRect, pReg->rects);
1306 pNextRect->x2 = r2->x1;
1308 pReg->numRects += 1;
1311 assert(pReg->numRects<=pReg->size);
1317 * Minuend used up: advance to new...
1326 * Subtrahend used up
1334 * Minuend used up: add any remaining piece before advancing.
1338 MEMCHECK(pReg, pNextRect, pReg->rects);
1341 pNextRect->x2 = r1->x2;
1343 pReg->numRects += 1;
1345 assert(pReg->numRects<=pReg->size);
1353 * Add remaining minuend rectangles to region.
1358 MEMCHECK(pReg, pNextRect, pReg->rects);
1361 pNextRect->x2 = r1->x2;
1363 pReg->numRects += 1;
1366 assert(pReg->numRects<=pReg->size);
1377 * gdk_region_subtract:
1378 * @source1: a #GdkRegion
1379 * @source2: another #GdkRegion
1381 * Subtracts any area in @source2 from the area in @source1.
1385 gdk_region_subtract (GdkRegion *region,
1388 /* check for trivial reject */
1389 if ((!(region->numRects)) || (!(other->numRects)) ||
1390 (!EXTENTCHECK(®ion->extents, &other->extents)))
1393 miRegionOp (region, region, other, miSubtractO,
1394 miSubtractNonO1, (nonOverlapFunc) NULL);
1397 * Can't alter region's extents before we call miRegionOp because miRegionOp
1398 * depends on the extents of those regions being the unaltered. Besides, this
1399 * way there's no checking against rectangles that will be nuked
1400 * due to coalescing, so we have to examine fewer rectangles.
1402 miSetExtents (region);
1407 * @source1: a #GdkRegion
1408 * @source2: another #GdkRegion
1410 * XORs the two regions, placing the result in @source1. The XOR of two
1411 * regions contains all areas which were not overlapping. That is,
1412 * it's the union of the regions minus the intersection of the
1417 gdk_region_xor (GdkRegion *sra,
1422 trb = gdk_region_copy (srb);
1424 gdk_region_subtract (trb, sra);
1425 gdk_region_subtract (sra, srb);
1427 gdk_region_union (sra,trb);
1429 gdk_region_destroy (trb);
1433 * Check to see if the region is empty. Assumes a region is passed
1437 gdk_region_empty (GdkRegion *r)
1439 if (r->numRects == 0)
1446 * Check to see if two regions are equal
1449 gdk_region_equal (GdkRegion *r1,
1454 if (r1->numRects != r2->numRects) return FALSE;
1455 else if (r1->numRects == 0) return TRUE;
1456 else if (r1->extents.x1 != r2->extents.x1) return FALSE;
1457 else if (r1->extents.x2 != r2->extents.x2) return FALSE;
1458 else if (r1->extents.y1 != r2->extents.y1) return FALSE;
1459 else if (r1->extents.y2 != r2->extents.y2) return FALSE;
1461 for(i=0; i < r1->numRects; i++ )
1463 if (r1->rects[i].x1 != r2->rects[i].x1) return FALSE;
1464 else if (r1->rects[i].x2 != r2->rects[i].x2) return FALSE;
1465 else if (r1->rects[i].y1 != r2->rects[i].y1) return FALSE;
1466 else if (r1->rects[i].y2 != r2->rects[i].y2) return FALSE;
1472 gdk_region_point_in (GdkRegion *region,
1478 if (region->numRects == 0)
1480 if (!INBOX(region->extents, x, y))
1482 for (i=0; i<region->numRects; i++)
1484 if (INBOX (region->rects[i], x, y))
1491 gdk_region_rect_in (GdkRegion *region,
1492 GdkRectangle *rectangle)
1495 GdkRegionBox *pboxEnd;
1497 GdkRegionBox *prect = ▭
1498 gboolean partIn, partOut;
1500 gint rx = rectangle->x;
1501 gint ry = rectangle->y;
1505 prect->x2 = rx + rectangle->width;
1506 prect->y2 = ry + rectangle->height;
1508 /* this is (just) a useful optimization */
1509 if ((region->numRects == 0) || !EXTENTCHECK (®ion->extents, prect))
1510 return GDK_OVERLAP_RECTANGLE_OUT;
1515 /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
1516 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1522 continue; /* getting up to speed or skipping remainder of band */
1526 partOut = TRUE; /* missed part of rectangle above */
1527 if (partIn || (pbox->y1 >= prect->y2))
1529 ry = pbox->y1; /* x guaranteed to be == prect->x1 */
1533 continue; /* not far enough over yet */
1537 partOut = TRUE; /* missed part of rectangle to left */
1542 if (pbox->x1 < prect->x2)
1544 partIn = TRUE; /* definitely overlap */
1549 if (pbox->x2 >= prect->x2)
1551 ry = pbox->y2; /* finished with this band */
1552 if (ry >= prect->y2)
1554 rx = prect->x1; /* reset x out to left again */
1559 * Because boxes in a band are maximal width, if the first box
1560 * to overlap the rectangle doesn't completely cover it in that
1561 * band, the rectangle must be partially out, since some of it
1562 * will be uncovered in that band. partIn will have been set true
1572 GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) :
1573 GDK_OVERLAP_RECTANGLE_OUT);
1578 gdk_region_unsorted_spans_intersect_foreach (GdkRegion *region,
1581 GdkSpanFunc function,
1584 gint i, left, right, y;
1585 gint clipped_left, clipped_right;
1587 GdkRegionBox *pboxEnd;
1590 if (!region->numRects)
1593 for (i=0;i<n_spans;i++)
1597 right = left + spans[i].width; /* right is not in the span! */
1599 if (! ((region->extents.y1 <= y) &&
1600 (region->extents.y2 > y) &&
1601 (region->extents.x1 < right) &&
1602 (region->extents.x2 > left)) )
1605 /* can stop when we passed y */
1606 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1611 continue; /* Not quite there yet */
1614 break; /* passed the spanline */
1616 if ((right > pbox->x1) && (left < pbox->x2))
1618 clipped_left = MAX (left, pbox->x1);
1619 clipped_right = MIN (right, pbox->x2);
1622 out_span.x = clipped_left;
1623 out_span.width = clipped_right - clipped_left;
1624 (*function) (&out_span, data);
1632 gdk_region_spans_intersect_foreach (GdkRegion *region,
1636 GdkSpanFunc function,
1639 gint left, right, y;
1640 gint clipped_left, clipped_right;
1642 GdkRegionBox *pboxEnd;
1643 GdkSpan *span, *tmpspan;
1649 gdk_region_unsorted_spans_intersect_foreach (region,
1657 if ((!region->numRects) || (n_spans == 0))
1662 right = span->x + span->width; /* right is not in the span! */
1664 /* The main method here is to step along the
1665 * sorted rectangles and spans in lock step, and
1666 * clipping the spans that are in the current
1667 * rectangle before going on to the next rectangle.
1671 end_span = spans + n_spans;
1672 pbox = region->rects;
1673 pboxEnd = pbox + region->numRects;
1674 while (pbox < pboxEnd)
1676 while ((pbox->y2 < span->y) || (span->y < pbox->y1))
1678 /* Skip any rectangles that are above the current span */
1679 if (pbox->y2 < span->y)
1682 if (pbox == pboxEnd)
1685 /* Skip any spans that are above the current rectangle */
1686 if (span->y < pbox->y1)
1689 if (span == end_span)
1694 /* Ok, we got at least one span that might intersect this rectangle. */
1696 while ((tmpspan < end_span) &&
1697 (tmpspan->y < pbox->y2))
1701 right = left + tmpspan->width; /* right is not in the span! */
1703 if ((right > pbox->x1) && (left < pbox->x2))
1705 clipped_left = MAX (left, pbox->x1);
1706 clipped_right = MIN (right, pbox->x2);
1709 out_span.x = clipped_left;
1710 out_span.width = clipped_right - clipped_left;
1711 (*function) (&out_span, data);
1717 /* Finished this rectangle.
1718 * The spans could still intersect the next one