]> Pileus Git - ~andy/gtk/blob - gdk/gdkregion-generic.c
Cleanups
[~andy/gtk] / gdk / gdkregion-generic.c
1 /* $TOG: Region.c /main/31 1998/02/06 17:50:22 kaleb $ */
2 /************************************************************************
3
4 Copyright 1987, 1988, 1998  The Open Group
5
6 All Rights Reserved.
7
8 The above copyright notice and this permission notice shall be included in
9 all copies or substantial portions of the Software.
10
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.
17
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.
21
22
23 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24
25                         All Rights Reserved
26
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.  
34
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
41 SOFTWARE.
42
43 ************************************************************************/
44 /* $XFree86: xc/lib/X11/Region.c,v 1.5 1999/05/09 10:50:01 dawes Exp $ */
45 /*
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.
51  *
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".
59  *
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
62  * to touch.
63  *
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...
69  */
70
71 #include <config.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <gdkregion.h>
75 #include "gdkregion-generic.h"
76 #include "gdkalias.h"
77
78 typedef void (*overlapFunc) (GdkRegion    *pReg,
79                              GdkRegionBox *r1,
80                              GdkRegionBox *r1End,
81                              GdkRegionBox *r2,
82                              GdkRegionBox *r2End,
83                              gint          y1,
84                              gint          y2);
85 typedef void (*nonOverlapFunc) (GdkRegion    *pReg,
86                                 GdkRegionBox *r,
87                                 GdkRegionBox *rEnd,
88                                 gint          y1,
89                                 gint          y2);
90
91 static void miRegionCopy (GdkRegion      *dstrgn,
92                           GdkRegion      *rgn);
93 static void miRegionOp   (GdkRegion      *newReg,
94                           GdkRegion      *reg1,
95                           GdkRegion      *reg2,
96                           overlapFunc     overlapFn,
97                           nonOverlapFunc  nonOverlap1Fn,
98                           nonOverlapFunc  nonOverlap2Fn);
99
100 /**
101  * gdk_region_new:
102  *
103  * Creates a new empty #GdkRegion.
104  *
105  * Returns: a new empty #GdkRegion
106  */
107 GdkRegion *
108 gdk_region_new (void)
109 {
110   GdkRegion *temp;
111
112   temp = g_slice_new (GdkRegion);
113
114   temp->numRects = 0;
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;
120   temp->size = 1;
121   
122   return temp;
123 }
124
125 /**
126  * gdk_region_rectangle:
127  * @rectangle: a #GdkRectangle
128  * 
129  * Creates a new region containing the area @rectangle.
130  * 
131  * Return value: a new region
132  **/
133 GdkRegion *
134 gdk_region_rectangle (GdkRectangle *rectangle)
135 {
136   GdkRegion *temp;
137
138   g_return_val_if_fail (rectangle != NULL, NULL);
139
140   if (rectangle->width <= 0 || rectangle->height <= 0)
141     return gdk_region_new();
142
143   temp = g_slice_new (GdkRegion);
144
145   temp->numRects = 1;
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;
151   temp->size = 1;
152   
153   return temp;
154 }
155
156 /**
157  * gdk_region_copy:
158  * @region: a #GdkRegion
159  * 
160  * Copies @region, creating an identical new region.
161  * 
162  * Return value: a new region identical to @region
163  **/
164 GdkRegion *
165 gdk_region_copy (GdkRegion *region)
166 {
167   GdkRegion *temp;
168
169   g_return_val_if_fail (region != NULL, NULL);
170
171   temp = gdk_region_new ();
172
173   miRegionCopy (temp, region);
174
175   return temp;
176 }
177
178 /**
179  * gdk_region_get_clipbox:
180  * @region: a #GdkRegion
181  * @rectangle: return location for the clipbox
182  *
183  * Obtains the smallest rectangle which includes the entire #GdkRegion.
184  *
185  */
186 void
187 gdk_region_get_clipbox (GdkRegion    *region, 
188                         GdkRectangle *rectangle)
189 {
190   g_return_if_fail (region != NULL);
191   g_return_if_fail (rectangle != NULL);
192   
193   rectangle->x = region->extents.x1;
194   rectangle->y = region->extents.y1;
195   rectangle->width = region->extents.x2 - region->extents.x1;
196   rectangle->height = region->extents.y2 - region->extents.y1;
197 }
198
199
200 /**
201  * gdk_region_get_rectangles:
202  * @region: a #GdkRegion
203  * @rectangles: return location for an array of rectangles
204  * @n_rectangles: length of returned array
205  *
206  * Obtains the area covered by the region as a list of rectangles.
207  * The array returned in @rectangles must be freed with g_free().
208  **/
209 void
210 gdk_region_get_rectangles (GdkRegion     *region,
211                            GdkRectangle **rectangles,
212                            gint          *n_rectangles)
213 {
214   gint i;
215   
216   g_return_if_fail (region != NULL);
217   g_return_if_fail (rectangles != NULL);
218   g_return_if_fail (n_rectangles != NULL);
219   
220   *n_rectangles = region->numRects;
221   *rectangles = g_new (GdkRectangle, region->numRects);
222
223   for (i = 0; i < region->numRects; i++)
224     {
225       GdkRegionBox rect;
226       rect = region->rects[i];
227       (*rectangles)[i].x = rect.x1;
228       (*rectangles)[i].y = rect.y1;
229       (*rectangles)[i].width = rect.x2 - rect.x1;
230       (*rectangles)[i].height = rect.y2 - rect.y1;
231     }
232 }
233
234 /**
235  * gdk_region_union_with_rect:
236  * @region: a #GdkRegion.
237  * @rect: a #GdkRectangle.
238  * 
239  * Sets the area of @region to the union of the areas of @region and
240  * @rect. The resulting area is the set of pixels contained in
241  * either @region or @rect.
242  **/
243 void
244 gdk_region_union_with_rect (GdkRegion    *region,
245                             GdkRectangle *rect)
246 {
247   GdkRegion tmp_region;
248
249   g_return_if_fail (region != NULL);
250   g_return_if_fail (rect != NULL);
251
252   if (rect->width <= 0 || rect->height <= 0)
253     return;
254     
255   tmp_region.rects = &tmp_region.extents;
256   tmp_region.numRects = 1;
257   tmp_region.extents.x1 = rect->x;
258   tmp_region.extents.y1 = rect->y;
259   tmp_region.extents.x2 = rect->x + rect->width;
260   tmp_region.extents.y2 = rect->y + rect->height;
261   tmp_region.size = 1;
262
263   gdk_region_union (region, &tmp_region);
264 }
265
266 /*-
267  *-----------------------------------------------------------------------
268  * miSetExtents --
269  *      Reset the extents of a region to what they should be. Called by
270  *      miSubtract and miIntersect b/c they can't figure it out along the
271  *      way or do so easily, as miUnion can.
272  *
273  * Results:
274  *      None.
275  *
276  * Side Effects:
277  *      The region's 'extents' structure is overwritten.
278  *
279  *-----------------------------------------------------------------------
280  */
281 static void
282 miSetExtents (GdkRegion *pReg)
283 {
284   GdkRegionBox *pBox, *pBoxEnd, *pExtents;
285
286   if (pReg->numRects == 0)
287     {
288       pReg->extents.x1 = 0;
289       pReg->extents.y1 = 0;
290       pReg->extents.x2 = 0;
291       pReg->extents.y2 = 0;
292       return;
293     }
294   
295   pExtents = &pReg->extents;
296   pBox = pReg->rects;
297   pBoxEnd = &pBox[pReg->numRects - 1];
298
299     /*
300      * Since pBox is the first rectangle in the region, it must have the
301      * smallest y1 and since pBoxEnd is the last rectangle in the region,
302      * it must have the largest y2, because of banding. Initialize x1 and
303      * x2 from  pBox and pBoxEnd, resp., as good things to initialize them
304      * to...
305      */
306   pExtents->x1 = pBox->x1;
307   pExtents->y1 = pBox->y1;
308   pExtents->x2 = pBoxEnd->x2;
309   pExtents->y2 = pBoxEnd->y2;
310
311   g_assert(pExtents->y1 < pExtents->y2);
312   while (pBox <= pBoxEnd)
313     {
314       if (pBox->x1 < pExtents->x1)
315         {
316           pExtents->x1 = pBox->x1;
317         }
318       if (pBox->x2 > pExtents->x2)
319         {
320           pExtents->x2 = pBox->x2;
321         }
322       pBox++;
323     }
324   g_assert(pExtents->x1 < pExtents->x2);
325 }
326
327 /**
328  * gdk_region_destroy:
329  * @region: a #GdkRegion
330  *
331  * Destroys a #GdkRegion.
332  */
333 void
334 gdk_region_destroy (GdkRegion *region)
335 {
336   g_return_if_fail (region != NULL);
337
338   if (region->rects != &region->extents)
339     g_free (region->rects);
340   g_slice_free (GdkRegion, region);
341 }
342
343
344 /**
345  * gdk_region_offset:
346  * @region: a #GdkRegion
347  * @dx: the distance to move the region horizontally
348  * @dy: the distance to move the region vertically
349  *
350  * Moves a region the specified distance.
351  */
352 void
353 gdk_region_offset (GdkRegion *region,
354                    gint       x,
355                    gint       y)
356 {
357   int nbox;
358   GdkRegionBox *pbox;
359
360   g_return_if_fail (region != NULL);
361
362   pbox = region->rects;
363   nbox = region->numRects;
364
365   while(nbox--)
366     {
367       pbox->x1 += x;
368       pbox->x2 += x;
369       pbox->y1 += y;
370       pbox->y2 += y;
371       pbox++;
372     }
373   if (region->rects != &region->extents)
374     {
375       region->extents.x1 += x;
376       region->extents.x2 += x;
377       region->extents.y1 += y;
378       region->extents.y2 += y;
379     }
380 }
381
382 /* 
383    Utility procedure Compress:
384    Replace r by the region r', where 
385      p in r' iff (Quantifer m <= dx) (p + m in r), and
386      Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
387      (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
388
389    Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
390    of all points p such that p and the next dx points on the same
391    horizontal scan line are all in r.  We do this using by noting
392    that p is the head of a run of length 2^i + k iff p is the head
393    of a run of length 2^i and p+2^i is the head of a run of length
394    k. Thus, the loop invariant: s contains the region corresponding
395    to the runs of length shift.  r contains the region corresponding
396    to the runs of length 1 + dxo & (shift-1), where dxo is the original
397    value of dx.  dx = dxo & ~(shift-1).  As parameters, s and t are
398    scratch regions, so that we don't have to allocate them on every
399    call.
400 */
401
402 #define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \
403                          else gdk_region_intersect (a,b)
404 #define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \
405                           else gdk_region_offset (a,0,b)
406
407 static void
408 Compress(GdkRegion *r,
409          GdkRegion *s,
410          GdkRegion *t,
411          guint      dx,
412          int        xdir,
413          int        grow)
414 {
415   guint shift = 1;
416
417   miRegionCopy (s, r);
418   while (dx)
419     {
420       if (dx & shift)
421         {
422           ZShiftRegion(r, -(int)shift);
423           ZOpRegion(r, s);
424           dx -= shift;
425           if (!dx) break;
426         }
427       miRegionCopy (t, s);
428       ZShiftRegion(s, -(int)shift);
429       ZOpRegion(s, t);
430       shift <<= 1;
431     }
432 }
433
434 #undef ZOpRegion
435 #undef ZShiftRegion
436 #undef ZCopyRegion
437
438 /**
439  * gdk_region_shrink:
440  * @region: a #GdkRegion
441  * @dx: the number of pixels to shrink the region horizontally
442  * @dy: the number of pixels to shrink the region vertically
443  *
444  * Resizes a region by the specified amount.
445  * Positive values shrink the region. Negative values expand it.
446  */
447 void
448 gdk_region_shrink (GdkRegion *region,
449                    int        dx,
450                    int        dy)
451 {
452   GdkRegion *s, *t;
453   int grow;
454
455   g_return_if_fail (region != NULL);
456
457   if (!dx && !dy)
458     return;
459
460   s = gdk_region_new ();
461   t = gdk_region_new ();
462
463   grow = (dx < 0);
464   if (grow)
465     dx = -dx;
466   if (dx)
467      Compress(region, s, t, (unsigned) 2*dx, TRUE, grow);
468      
469   grow = (dy < 0);
470   if (grow)
471     dy = -dy;
472   if (dy)
473      Compress(region, s, t, (unsigned) 2*dy, FALSE, grow);
474   
475   gdk_region_offset (region, dx, dy);
476   gdk_region_destroy (s);
477   gdk_region_destroy (t);
478 }
479
480 \f
481 /*======================================================================
482  *          Region Intersection
483  *====================================================================*/
484 /*-
485  *-----------------------------------------------------------------------
486  * miIntersectO --
487  *      Handle an overlapping band for miIntersect.
488  *
489  * Results:
490  *      None.
491  *
492  * Side Effects:
493  *      Rectangles may be added to the region.
494  *
495  *-----------------------------------------------------------------------
496  */
497 /* static void*/
498 static void
499 miIntersectO (GdkRegion    *pReg,
500               GdkRegionBox *r1,
501               GdkRegionBox *r1End,
502               GdkRegionBox *r2,
503               GdkRegionBox *r2End,
504               gint          y1,
505               gint          y2)
506 {
507   int   x1;
508   int   x2;
509   GdkRegionBox *pNextRect;
510
511   pNextRect = &pReg->rects[pReg->numRects];
512
513   while ((r1 != r1End) && (r2 != r2End))
514     {
515       x1 = MAX (r1->x1,r2->x1);
516       x2 = MIN (r1->x2,r2->x2);
517
518       /*
519        * If there's any overlap between the two rectangles, add that
520        * overlap to the new region.
521        * There's no need to check for subsumption because the only way
522        * such a need could arise is if some region has two rectangles
523        * right next to each other. Since that should never happen...
524        */
525       if (x1 < x2)
526         {
527           g_assert (y1<y2);
528
529           MEMCHECK (pReg, pNextRect, pReg->rects);
530           pNextRect->x1 = x1;
531           pNextRect->y1 = y1;
532           pNextRect->x2 = x2;
533           pNextRect->y2 = y2;
534           pReg->numRects += 1;
535           pNextRect++;
536           g_assert (pReg->numRects <= pReg->size);
537         }
538
539       /*
540        * Need to advance the pointers. Shift the one that extends
541        * to the right the least, since the other still has a chance to
542        * overlap with that region's next rectangle, if you see what I mean.
543        */
544       if (r1->x2 < r2->x2)
545         {
546           r1++;
547         }
548       else if (r2->x2 < r1->x2)
549         {
550           r2++;
551         }
552       else
553         {
554           r1++;
555           r2++;
556         }
557     }
558 }
559
560 /**
561  * gdk_region_intersect:
562  * @source1: a #GdkRegion
563  * @source2: another #GdkRegion
564  *
565  * Sets the area of @source1 to the intersection of the areas of @source1
566  * and @source2. The resulting area is the set of pixels contained in
567  * both @source1 and @source2.
568  **/
569 void
570 gdk_region_intersect (GdkRegion *source1,
571                       GdkRegion *source2)
572 {
573   g_return_if_fail (source1 != NULL);
574   g_return_if_fail (source2 != NULL);
575   
576   /* check for trivial reject */
577   if ((!(source1->numRects)) || (!(source2->numRects))  ||
578       (!EXTENTCHECK(&source1->extents, &source2->extents)))
579     source1->numRects = 0;
580   else
581     miRegionOp (source1, source1, source2, 
582                 miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL);
583     
584   /*
585    * Can't alter source1's extents before miRegionOp depends on the
586    * extents of the regions being unchanged. Besides, this way there's
587    * no checking against rectangles that will be nuked due to
588    * coalescing, so we have to examine fewer rectangles.
589    */
590   miSetExtents(source1);
591 }
592
593 static void
594 miRegionCopy (GdkRegion *dstrgn, 
595               GdkRegion *rgn)
596 {
597   if (dstrgn != rgn) /*  don't want to copy to itself */
598     {  
599       if (dstrgn->size < rgn->numRects)
600         {
601           if (dstrgn->rects != &dstrgn->extents)
602             g_free (dstrgn->rects);
603
604           dstrgn->rects = g_new (GdkRegionBox, rgn->numRects);
605           dstrgn->size = rgn->numRects;
606         }
607
608       dstrgn->numRects = rgn->numRects;
609       dstrgn->extents = rgn->extents;
610
611       memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox));
612     }
613 }
614
615
616 /*======================================================================
617  *          Generic Region Operator
618  *====================================================================*/
619
620 /*-
621  *-----------------------------------------------------------------------
622  * miCoalesce --
623  *      Attempt to merge the boxes in the current band with those in the
624  *      previous one. Used only by miRegionOp.
625  *
626  * Results:
627  *      The new index for the previous band.
628  *
629  * Side Effects:
630  *      If coalescing takes place:
631  *          - rectangles in the previous band will have their y2 fields
632  *            altered.
633  *          - pReg->numRects will be decreased.
634  *
635  *-----------------------------------------------------------------------
636  */
637 /* static int*/
638 static int
639 miCoalesce (GdkRegion *pReg,         /* Region to coalesce */
640             gint       prevStart,    /* Index of start of previous band */
641             gint       curStart)     /* Index of start of current band */
642 {
643   GdkRegionBox *pPrevBox;       /* Current box in previous band */
644   GdkRegionBox *pCurBox;        /* Current box in current band */
645   GdkRegionBox *pRegEnd;        /* End of region */
646   int           curNumRects;    /* Number of rectangles in current
647                                  * band */
648   int           prevNumRects;   /* Number of rectangles in previous
649                                  * band */
650   int           bandY1;         /* Y1 coordinate for current band */
651
652   pRegEnd = &pReg->rects[pReg->numRects];
653
654   pPrevBox = &pReg->rects[prevStart];
655   prevNumRects = curStart - prevStart;
656
657     /*
658      * Figure out how many rectangles are in the current band. Have to do
659      * this because multiple bands could have been added in miRegionOp
660      * at the end when one region has been exhausted.
661      */
662   pCurBox = &pReg->rects[curStart];
663   bandY1 = pCurBox->y1;
664   for (curNumRects = 0;
665        (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
666        curNumRects++)
667     {
668       pCurBox++;
669     }
670     
671   if (pCurBox != pRegEnd)
672     {
673       /*
674        * If more than one band was added, we have to find the start
675        * of the last band added so the next coalescing job can start
676        * at the right place... (given when multiple bands are added,
677        * this may be pointless -- see above).
678        */
679       pRegEnd--;
680       while (pRegEnd[-1].y1 == pRegEnd->y1)
681         {
682           pRegEnd--;
683         }
684       curStart = pRegEnd - pReg->rects;
685       pRegEnd = pReg->rects + pReg->numRects;
686     }
687         
688   if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
689     pCurBox -= curNumRects;
690     /*
691      * The bands may only be coalesced if the bottom of the previous
692      * matches the top scanline of the current.
693      */
694     if (pPrevBox->y2 == pCurBox->y1)
695       {
696         /*
697          * Make sure the bands have boxes in the same places. This
698          * assumes that boxes have been added in such a way that they
699          * cover the most area possible. I.e. two boxes in a band must
700          * have some horizontal space between them.
701          */
702         do
703           {
704             if ((pPrevBox->x1 != pCurBox->x1) ||
705                 (pPrevBox->x2 != pCurBox->x2))
706               {
707                 /*
708                  * The bands don't line up so they can't be coalesced.
709                  */
710                 return (curStart);
711               }
712             pPrevBox++;
713             pCurBox++;
714             prevNumRects -= 1;
715           } while (prevNumRects != 0);
716
717         pReg->numRects -= curNumRects;
718         pCurBox -= curNumRects;
719         pPrevBox -= curNumRects;
720
721         /*
722          * The bands may be merged, so set the bottom y of each box
723          * in the previous band to that of the corresponding box in
724          * the current band.
725          */
726         do
727           {
728             pPrevBox->y2 = pCurBox->y2;
729             pPrevBox++;
730             pCurBox++;
731             curNumRects -= 1;
732           }
733         while (curNumRects != 0);
734
735         /*
736          * If only one band was added to the region, we have to backup
737          * curStart to the start of the previous band.
738          *
739          * If more than one band was added to the region, copy the
740          * other bands down. The assumption here is that the other bands
741          * came from the same region as the current one and no further
742          * coalescing can be done on them since it's all been done
743          * already... curStart is already in the right place.
744          */
745         if (pCurBox == pRegEnd)
746           {
747             curStart = prevStart;
748           }
749         else
750           {
751             do
752               {
753                 *pPrevBox++ = *pCurBox++;
754               }
755             while (pCurBox != pRegEnd);
756           }
757             
758       }
759   }
760   return curStart;
761 }
762
763 /*-
764  *-----------------------------------------------------------------------
765  * miRegionOp --
766  *      Apply an operation to two regions. Called by miUnion, miInverse,
767  *      miSubtract, miIntersect...
768  *
769  * Results:
770  *      None.
771  *
772  * Side Effects:
773  *      The new region is overwritten.
774  *
775  * Notes:
776  *      The idea behind this function is to view the two regions as sets.
777  *      Together they cover a rectangle of area that this function divides
778  *      into horizontal bands where points are covered only by one region
779  *      or by both. For the first case, the nonOverlapFunc is called with
780  *      each the band and the band's upper and lower extents. For the
781  *      second, the overlapFunc is called to process the entire band. It
782  *      is responsible for clipping the rectangles in the band, though
783  *      this function provides the boundaries.
784  *      At the end of each band, the new region is coalesced, if possible,
785  *      to reduce the number of rectangles in the region.
786  *
787  *-----------------------------------------------------------------------
788  */
789 /* static void*/
790 static void
791 miRegionOp(GdkRegion *newReg,
792            GdkRegion *reg1,
793            GdkRegion *reg2,
794            overlapFunc    overlapFn,            /* Function to call for over-
795                                                  * lapping bands */
796            nonOverlapFunc nonOverlap1Fn,        /* Function to call for non-
797                                                  * overlapping bands in region
798                                                  * 1 */
799            nonOverlapFunc nonOverlap2Fn)        /* Function to call for non-
800                                                  * overlapping bands in region
801                                                  * 2 */
802 {
803     GdkRegionBox *r1;                   /* Pointer into first region */
804     GdkRegionBox *r2;                   /* Pointer into 2d region */
805     GdkRegionBox *r1End;                /* End of 1st region */
806     GdkRegionBox *r2End;                /* End of 2d region */
807     int           ybot;                 /* Bottom of intersection */
808     int           ytop;                 /* Top of intersection */
809     GdkRegionBox *oldRects;             /* Old rects for newReg */
810     int           prevBand;             /* Index of start of
811                                          * previous band in newReg */
812     int           curBand;              /* Index of start of current
813                                          * band in newReg */
814     GdkRegionBox *r1BandEnd;            /* End of current band in r1 */
815     GdkRegionBox *r2BandEnd;            /* End of current band in r2 */
816     int           top;                  /* Top of non-overlapping
817                                          * band */
818     int           bot;                  /* Bottom of non-overlapping
819                                          * band */
820     
821     /*
822      * Initialization:
823      *  set r1, r2, r1End and r2End appropriately, preserve the important
824      * parts of the destination region until the end in case it's one of
825      * the two source regions, then mark the "new" region empty, allocating
826      * another array of rectangles for it to use.
827      */
828     r1 = reg1->rects;
829     r2 = reg2->rects;
830     r1End = r1 + reg1->numRects;
831     r2End = r2 + reg2->numRects;
832     
833     oldRects = newReg->rects;
834     
835     EMPTY_REGION(newReg);
836
837     /*
838      * Allocate a reasonable number of rectangles for the new region. The idea
839      * is to allocate enough so the individual functions don't need to
840      * reallocate and copy the array, which is time consuming, yet we don't
841      * have to worry about using too much memory. I hope to be able to
842      * nuke the Xrealloc() at the end of this function eventually.
843      */
844     newReg->size = MAX (reg1->numRects, reg2->numRects) * 2;
845     newReg->rects = g_new (GdkRegionBox, newReg->size);
846     
847     /*
848      * Initialize ybot and ytop.
849      * In the upcoming loop, ybot and ytop serve different functions depending
850      * on whether the band being handled is an overlapping or non-overlapping
851      * band.
852      *  In the case of a non-overlapping band (only one of the regions
853      * has points in the band), ybot is the bottom of the most recent
854      * intersection and thus clips the top of the rectangles in that band.
855      * ytop is the top of the next intersection between the two regions and
856      * serves to clip the bottom of the rectangles in the current band.
857      *  For an overlapping band (where the two regions intersect), ytop clips
858      * the top of the rectangles of both regions and ybot clips the bottoms.
859      */
860     if (reg1->extents.y1 < reg2->extents.y1)
861       ybot = reg1->extents.y1;
862     else
863       ybot = reg2->extents.y1;
864     
865     /*
866      * prevBand serves to mark the start of the previous band so rectangles
867      * can be coalesced into larger rectangles. qv. miCoalesce, above.
868      * In the beginning, there is no previous band, so prevBand == curBand
869      * (curBand is set later on, of course, but the first band will always
870      * start at index 0). prevBand and curBand must be indices because of
871      * the possible expansion, and resultant moving, of the new region's
872      * array of rectangles.
873      */
874     prevBand = 0;
875     
876     do
877       {
878         curBand = newReg->numRects;
879
880         /*
881          * This algorithm proceeds one source-band (as opposed to a
882          * destination band, which is determined by where the two regions
883          * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
884          * rectangle after the last one in the current band for their
885          * respective regions.
886          */
887         r1BandEnd = r1;
888         while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
889           {
890             r1BandEnd++;
891           }
892         
893         r2BandEnd = r2;
894         while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
895           {
896             r2BandEnd++;
897           }
898         
899         /*
900          * First handle the band that doesn't intersect, if any.
901          *
902          * Note that attention is restricted to one band in the
903          * non-intersecting region at once, so if a region has n
904          * bands between the current position and the next place it overlaps
905          * the other, this entire loop will be passed through n times.
906          */
907         if (r1->y1 < r2->y1)
908           {
909             top = MAX (r1->y1,ybot);
910             bot = MIN (r1->y2,r2->y1);
911
912             if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL))
913               {
914                 (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot);
915               }
916
917             ytop = r2->y1;
918           }
919         else if (r2->y1 < r1->y1)
920           {
921             top = MAX (r2->y1,ybot);
922             bot = MIN (r2->y2,r1->y1);
923
924             if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL))
925               {
926                 (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot);
927               }
928
929             ytop = r1->y1;
930           }
931         else
932           {
933             ytop = r1->y1;
934           }
935
936         /*
937          * If any rectangles got added to the region, try and coalesce them
938          * with rectangles from the previous band. Note we could just do
939          * this test in miCoalesce, but some machines incur a not
940          * inconsiderable cost for function calls, so...
941          */
942         if (newReg->numRects != curBand)
943           {
944             prevBand = miCoalesce (newReg, prevBand, curBand);
945           }
946
947         /*
948          * Now see if we've hit an intersecting band. The two bands only
949          * intersect if ybot > ytop
950          */
951         ybot = MIN (r1->y2, r2->y2);
952         curBand = newReg->numRects;
953         if (ybot > ytop)
954           {
955             (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
956
957           }
958         
959         if (newReg->numRects != curBand)
960           {
961             prevBand = miCoalesce (newReg, prevBand, curBand);
962           }
963
964         /*
965          * If we've finished with a band (y2 == ybot) we skip forward
966          * in the region to the next band.
967          */
968         if (r1->y2 == ybot)
969           {
970             r1 = r1BandEnd;
971           }
972         if (r2->y2 == ybot)
973           {
974             r2 = r2BandEnd;
975           }
976       } while ((r1 != r1End) && (r2 != r2End));
977
978     /*
979      * Deal with whichever region still has rectangles left.
980      */
981     curBand = newReg->numRects;
982     if (r1 != r1End)
983       {
984         if (nonOverlap1Fn != (nonOverlapFunc )NULL)
985           {
986             do
987               {
988                 r1BandEnd = r1;
989                 while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
990                   {
991                     r1BandEnd++;
992                   }
993                 (* nonOverlap1Fn) (newReg, r1, r1BandEnd,
994                                      MAX (r1->y1,ybot), r1->y2);
995                 r1 = r1BandEnd;
996               } while (r1 != r1End);
997           }
998       }
999     else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL))
1000       {
1001         do
1002           {
1003             r2BandEnd = r2;
1004             while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
1005               {
1006                 r2BandEnd++;
1007               }
1008             (* nonOverlap2Fn) (newReg, r2, r2BandEnd,
1009                                MAX (r2->y1,ybot), r2->y2);
1010             r2 = r2BandEnd;
1011           } while (r2 != r2End);
1012       }
1013
1014     if (newReg->numRects != curBand)
1015     {
1016       (void) miCoalesce (newReg, prevBand, curBand);
1017     }
1018
1019     /*
1020      * A bit of cleanup. To keep regions from growing without bound,
1021      * we shrink the array of rectangles to match the new number of
1022      * rectangles in the region. This never goes to 0, however...
1023      *
1024      * Only do this stuff if the number of rectangles allocated is more than
1025      * twice the number of rectangles in the region (a simple optimization...).
1026      */
1027     if (newReg->numRects < (newReg->size >> 1))
1028       {
1029         if (REGION_NOT_EMPTY (newReg))
1030           {
1031             newReg->size = newReg->numRects;
1032             newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size);
1033           }
1034         else
1035           {
1036             /*
1037              * No point in doing the extra work involved in an Xrealloc if
1038              * the region is empty
1039              */
1040             newReg->size = 1;
1041             g_free (newReg->rects);
1042             newReg->rects = &newReg->extents;
1043           }
1044       }
1045
1046     if (oldRects != &newReg->extents)
1047       g_free (oldRects);
1048 }
1049
1050 \f
1051 /*======================================================================
1052  *          Region Union
1053  *====================================================================*/
1054
1055 /*-
1056  *-----------------------------------------------------------------------
1057  * miUnionNonO --
1058  *      Handle a non-overlapping band for the union operation. Just
1059  *      Adds the rectangles into the region. Doesn't have to check for
1060  *      subsumption or anything.
1061  *
1062  * Results:
1063  *      None.
1064  *
1065  * Side Effects:
1066  *      pReg->numRects is incremented and the final rectangles overwritten
1067  *      with the rectangles we're passed.
1068  *
1069  *-----------------------------------------------------------------------
1070  */
1071 static void
1072 miUnionNonO (GdkRegion    *pReg,
1073              GdkRegionBox *r,
1074              GdkRegionBox *rEnd,
1075              gint          y1,
1076              gint          y2)
1077 {
1078   GdkRegionBox *pNextRect;
1079
1080   pNextRect = &pReg->rects[pReg->numRects];
1081
1082   g_assert(y1 < y2);
1083
1084   while (r != rEnd)
1085     {
1086       g_assert(r->x1 < r->x2);
1087       MEMCHECK(pReg, pNextRect, pReg->rects);
1088       pNextRect->x1 = r->x1;
1089       pNextRect->y1 = y1;
1090       pNextRect->x2 = r->x2;
1091       pNextRect->y2 = y2;
1092       pReg->numRects += 1;
1093       pNextRect++;
1094
1095       g_assert(pReg->numRects<=pReg->size);
1096       r++;
1097     }
1098 }
1099
1100
1101 /*-
1102  *-----------------------------------------------------------------------
1103  * miUnionO --
1104  *      Handle an overlapping band for the union operation. Picks the
1105  *      left-most rectangle each time and merges it into the region.
1106  *
1107  * Results:
1108  *      None.
1109  *
1110  * Side Effects:
1111  *      Rectangles are overwritten in pReg->rects and pReg->numRects will
1112  *      be changed.
1113  *
1114  *-----------------------------------------------------------------------
1115  */
1116
1117 /* static void*/
1118 static void
1119 miUnionO (GdkRegion *pReg,
1120           GdkRegionBox *r1,
1121           GdkRegionBox *r1End,
1122           GdkRegionBox *r2,
1123           GdkRegionBox *r2End,
1124           gint          y1,
1125           gint          y2)
1126 {
1127   GdkRegionBox *        pNextRect;
1128     
1129   pNextRect = &pReg->rects[pReg->numRects];
1130
1131 #define MERGERECT(r)                                    \
1132     if ((pReg->numRects != 0) &&                        \
1133         (pNextRect[-1].y1 == y1) &&                     \
1134         (pNextRect[-1].y2 == y2) &&                     \
1135         (pNextRect[-1].x2 >= r->x1))                    \
1136       {                                                 \
1137         if (pNextRect[-1].x2 < r->x2)                   \
1138           {                                             \
1139             pNextRect[-1].x2 = r->x2;                   \
1140             g_assert(pNextRect[-1].x1<pNextRect[-1].x2);        \
1141           }                                             \
1142       }                                                 \
1143     else                                                \
1144       {                                                 \
1145         MEMCHECK(pReg, pNextRect, pReg->rects);         \
1146         pNextRect->y1 = y1;                             \
1147         pNextRect->y2 = y2;                             \
1148         pNextRect->x1 = r->x1;                          \
1149         pNextRect->x2 = r->x2;                          \
1150         pReg->numRects += 1;                            \
1151         pNextRect += 1;                                 \
1152       }                                                 \
1153     g_assert(pReg->numRects<=pReg->size);                       \
1154     r++;
1155     
1156     g_assert (y1<y2);
1157     while ((r1 != r1End) && (r2 != r2End))
1158     {
1159         if (r1->x1 < r2->x1)
1160         {
1161             MERGERECT(r1);
1162         }
1163         else
1164         {
1165             MERGERECT(r2);
1166         }
1167     }
1168     
1169     if (r1 != r1End)
1170     {
1171         do
1172         {
1173             MERGERECT(r1);
1174         } while (r1 != r1End);
1175     }
1176     else while (r2 != r2End)
1177     {
1178         MERGERECT(r2);
1179     }
1180 }
1181
1182 /**
1183  * gdk_region_union:
1184  * @source1:  a #GdkRegion
1185  * @source2: a #GdkRegion 
1186  * 
1187  * Sets the area of @source1 to the union of the areas of @source1 and
1188  * @source2. The resulting area is the set of pixels contained in
1189  * either @source1 or @source2.
1190  **/
1191 void
1192 gdk_region_union (GdkRegion *source1,
1193                   GdkRegion *source2)
1194 {
1195   g_return_if_fail (source1 != NULL);
1196   g_return_if_fail (source2 != NULL);
1197   
1198   /*  checks all the simple cases */
1199
1200   /*
1201    * source1 and source2 are the same or source2 is empty
1202    */
1203   if ((source1 == source2) || (!(source2->numRects)))
1204     return;
1205
1206   /* 
1207    * source1 is empty
1208    */
1209   if (!(source1->numRects))
1210     {
1211       miRegionCopy (source1, source2);
1212       return;
1213     }
1214   
1215   /*
1216    * source1 completely subsumes source2
1217    */
1218   if ((source1->numRects == 1) && 
1219       (source1->extents.x1 <= source2->extents.x1) &&
1220       (source1->extents.y1 <= source2->extents.y1) &&
1221       (source1->extents.x2 >= source2->extents.x2) &&
1222       (source1->extents.y2 >= source2->extents.y2))
1223     return;
1224
1225   /*
1226    * source2 completely subsumes source1
1227    */
1228   if ((source2->numRects == 1) && 
1229       (source2->extents.x1 <= source1->extents.x1) &&
1230       (source2->extents.y1 <= source1->extents.y1) &&
1231       (source2->extents.x2 >= source1->extents.x2) &&
1232       (source2->extents.y2 >= source1->extents.y2))
1233     {
1234       miRegionCopy(source1, source2);
1235       return;
1236     }
1237
1238   miRegionOp (source1, source1, source2, miUnionO, 
1239               miUnionNonO, miUnionNonO);
1240
1241   source1->extents.x1 = MIN (source1->extents.x1, source2->extents.x1);
1242   source1->extents.y1 = MIN (source1->extents.y1, source2->extents.y1);
1243   source1->extents.x2 = MAX (source1->extents.x2, source2->extents.x2);
1244   source1->extents.y2 = MAX (source1->extents.y2, source2->extents.y2);
1245 }
1246
1247 \f
1248 /*======================================================================
1249  *                Region Subtraction
1250  *====================================================================*/
1251
1252 /*-
1253  *-----------------------------------------------------------------------
1254  * miSubtractNonO --
1255  *      Deal with non-overlapping band for subtraction. Any parts from
1256  *      region 2 we discard. Anything from region 1 we add to the region.
1257  *
1258  * Results:
1259  *      None.
1260  *
1261  * Side Effects:
1262  *      pReg may be affected.
1263  *
1264  *-----------------------------------------------------------------------
1265  */
1266 /* static void*/
1267 static void
1268 miSubtractNonO1 (GdkRegion    *pReg,
1269                  GdkRegionBox *r,
1270                  GdkRegionBox *rEnd,
1271                  gint          y1,
1272                  gint          y2)
1273 {
1274   GdkRegionBox *        pNextRect;
1275         
1276   pNextRect = &pReg->rects[pReg->numRects];
1277         
1278   g_assert(y1<y2);
1279
1280   while (r != rEnd)
1281     {
1282       g_assert (r->x1<r->x2);
1283       MEMCHECK (pReg, pNextRect, pReg->rects);
1284       pNextRect->x1 = r->x1;
1285       pNextRect->y1 = y1;
1286       pNextRect->x2 = r->x2;
1287       pNextRect->y2 = y2;
1288       pReg->numRects += 1;
1289       pNextRect++;
1290
1291       g_assert (pReg->numRects <= pReg->size);
1292
1293       r++;
1294     }
1295 }
1296
1297 /*-
1298  *-----------------------------------------------------------------------
1299  * miSubtractO --
1300  *      Overlapping band subtraction. x1 is the left-most point not yet
1301  *      checked.
1302  *
1303  * Results:
1304  *      None.
1305  *
1306  * Side Effects:
1307  *      pReg may have rectangles added to it.
1308  *
1309  *-----------------------------------------------------------------------
1310  */
1311 /* static void*/
1312 static void
1313 miSubtractO (GdkRegion    *pReg,
1314              GdkRegionBox *r1,
1315              GdkRegionBox *r1End,
1316              GdkRegionBox *r2,
1317              GdkRegionBox *r2End,
1318              gint          y1,
1319              gint          y2)
1320 {
1321   GdkRegionBox *        pNextRect;
1322   int   x1;
1323     
1324   x1 = r1->x1;
1325     
1326   g_assert(y1<y2);
1327   pNextRect = &pReg->rects[pReg->numRects];
1328
1329   while ((r1 != r1End) && (r2 != r2End))
1330     {
1331       if (r2->x2 <= x1)
1332         {
1333           /*
1334            * Subtrahend missed the boat: go to next subtrahend.
1335            */
1336           r2++;
1337         }
1338       else if (r2->x1 <= x1)
1339         {
1340           /*
1341            * Subtrahend preceeds minuend: nuke left edge of minuend.
1342            */
1343           x1 = r2->x2;
1344           if (x1 >= r1->x2)
1345             {
1346               /*
1347                * Minuend completely covered: advance to next minuend and
1348                * reset left fence to edge of new minuend.
1349                */
1350               r1++;
1351               if (r1 != r1End)
1352                 x1 = r1->x1;
1353             }
1354           else
1355             {
1356               /*
1357                * Subtrahend now used up since it doesn't extend beyond
1358                * minuend
1359                */
1360               r2++;
1361             }
1362         }
1363       else if (r2->x1 < r1->x2)
1364         {
1365           /*
1366            * Left part of subtrahend covers part of minuend: add uncovered
1367            * part of minuend to region and skip to next subtrahend.
1368            */
1369           g_assert(x1<r2->x1);
1370           MEMCHECK(pReg, pNextRect, pReg->rects);
1371           pNextRect->x1 = x1;
1372           pNextRect->y1 = y1;
1373           pNextRect->x2 = r2->x1;
1374           pNextRect->y2 = y2;
1375           pReg->numRects += 1;
1376           pNextRect++;
1377
1378           g_assert(pReg->numRects<=pReg->size);
1379
1380           x1 = r2->x2;
1381           if (x1 >= r1->x2)
1382             {
1383               /*
1384                * Minuend used up: advance to new...
1385                */
1386               r1++;
1387               if (r1 != r1End)
1388                 x1 = r1->x1;
1389             }
1390           else
1391             {
1392               /*
1393                * Subtrahend used up
1394                */
1395               r2++;
1396             }
1397         }
1398       else
1399         {
1400           /*
1401            * Minuend used up: add any remaining piece before advancing.
1402            */
1403           if (r1->x2 > x1)
1404             {
1405               MEMCHECK(pReg, pNextRect, pReg->rects);
1406               pNextRect->x1 = x1;
1407               pNextRect->y1 = y1;
1408               pNextRect->x2 = r1->x2;
1409               pNextRect->y2 = y2;
1410               pReg->numRects += 1;
1411               pNextRect++;
1412               g_assert(pReg->numRects<=pReg->size);
1413             }
1414           r1++;
1415           if (r1 != r1End)
1416             x1 = r1->x1;
1417         }
1418     }
1419
1420   /*
1421      * Add remaining minuend rectangles to region.
1422      */
1423   while (r1 != r1End)
1424     {
1425       g_assert(x1<r1->x2);
1426       MEMCHECK(pReg, pNextRect, pReg->rects);
1427       pNextRect->x1 = x1;
1428       pNextRect->y1 = y1;
1429       pNextRect->x2 = r1->x2;
1430       pNextRect->y2 = y2;
1431       pReg->numRects += 1;
1432       pNextRect++;
1433
1434       g_assert(pReg->numRects<=pReg->size);
1435
1436       r1++;
1437       if (r1 != r1End)
1438         {
1439           x1 = r1->x1;
1440         }
1441     }
1442 }
1443
1444 /**
1445  * gdk_region_subtract:
1446  * @source1: a #GdkRegion
1447  * @source2: another #GdkRegion
1448  *
1449  * Subtracts the area of @source2 from the area @source1. The resulting
1450  * area is the set of pixels contained in @source1 but not in @source2.
1451  **/
1452 void
1453 gdk_region_subtract (GdkRegion *source1,
1454                      GdkRegion *source2)
1455 {
1456   g_return_if_fail (source1 != NULL);
1457   g_return_if_fail (source2 != NULL);
1458   
1459   /* check for trivial reject */
1460   if ((!(source1->numRects)) || (!(source2->numRects)) ||
1461       (!EXTENTCHECK(&source1->extents, &source2->extents)))
1462     return;
1463  
1464   miRegionOp (source1, source1, source2, miSubtractO,
1465               miSubtractNonO1, (nonOverlapFunc) NULL);
1466
1467   /*
1468    * Can't alter source1's extents before we call miRegionOp because miRegionOp
1469    * depends on the extents of those regions being the unaltered. Besides, this
1470    * way there's no checking against rectangles that will be nuked
1471    * due to coalescing, so we have to examine fewer rectangles.
1472    */
1473   miSetExtents (source1);
1474 }
1475
1476 /**
1477  * gdk_region_xor:
1478  * @source1: a #GdkRegion
1479  * @source2: another #GdkRegion
1480  *
1481  * Sets the area of @source1 to the exclusive-OR of the areas of @source1
1482  * and @source2. The resulting area is the set of pixels contained in one
1483  * or the other of the two sources but not in both.
1484  **/
1485 void
1486 gdk_region_xor (GdkRegion *source1,
1487                 GdkRegion *source2)
1488 {
1489   GdkRegion *trb;
1490
1491   g_return_if_fail (source1 != NULL);
1492   g_return_if_fail (source2 != NULL);
1493
1494   trb = gdk_region_copy (source2);
1495
1496   gdk_region_subtract (trb, source1);
1497   gdk_region_subtract (source1, source2);
1498
1499   gdk_region_union (source1, trb);
1500   
1501   gdk_region_destroy (trb);
1502 }
1503
1504 /**
1505  * gdk_region_empty: 
1506  * @region: a #GdkRegion
1507  *
1508  * Finds out if the #GdkRegion is empty.
1509  *
1510  * Returns: %TRUE if @region is empty.
1511  */
1512 gboolean
1513 gdk_region_empty (GdkRegion *region)
1514 {
1515   g_return_val_if_fail (region != NULL, FALSE);
1516   
1517   if (region->numRects == 0)
1518     return TRUE;
1519   else
1520     return FALSE;
1521 }
1522
1523 /**
1524  * gdk_region_equal:
1525  * @region1: a #GdkRegion
1526  * @region2: a #GdkRegion
1527  *
1528  * Finds out if the two regions are the same.
1529  *
1530  * Returns: %TRUE if @region1 and @region2 are equal.
1531  */
1532 gboolean
1533 gdk_region_equal (GdkRegion *region1,
1534                   GdkRegion *region2)
1535 {
1536   int i;
1537
1538   g_return_val_if_fail (region1 != NULL, FALSE);
1539   g_return_val_if_fail (region2 != NULL, FALSE);
1540
1541   if (region1->numRects != region2->numRects) return FALSE;
1542   else if (region1->numRects == 0) return TRUE;
1543   else if (region1->extents.x1 != region2->extents.x1) return FALSE;
1544   else if (region1->extents.x2 != region2->extents.x2) return FALSE;
1545   else if (region1->extents.y1 != region2->extents.y1) return FALSE;
1546   else if (region1->extents.y2 != region2->extents.y2) return FALSE;
1547   else
1548     for(i = 0; i < region1->numRects; i++ )
1549       {
1550         if (region1->rects[i].x1 != region2->rects[i].x1) return FALSE;
1551         else if (region1->rects[i].x2 != region2->rects[i].x2) return FALSE;
1552         else if (region1->rects[i].y1 != region2->rects[i].y1) return FALSE;
1553         else if (region1->rects[i].y2 != region2->rects[i].y2) return FALSE;
1554       }
1555   return TRUE;
1556 }
1557
1558 /**
1559  * gdk_region_point_in:
1560  * @region: a #GdkRegion
1561  * @x: the x coordinate of a point
1562  * @y: the y coordinate of a point
1563  *
1564  * Finds out if a point is in a region.
1565  *
1566  * Returns: %TRUE if the point is in @region.
1567  */
1568 gboolean
1569 gdk_region_point_in (GdkRegion *region,
1570                      int        x,
1571                      int        y)
1572 {
1573   int i;
1574
1575   g_return_val_if_fail (region != NULL, FALSE);
1576
1577   if (region->numRects == 0)
1578     return FALSE;
1579   if (!INBOX(region->extents, x, y))
1580     return FALSE;
1581   for (i = 0; i < region->numRects; i++)
1582     {
1583       if (INBOX (region->rects[i], x, y))
1584         return TRUE;
1585     }
1586   return FALSE;
1587 }
1588
1589 /**
1590  * gdk_region_rect_in: 
1591  * @region: a #GdkRegion.
1592  * @rectangle: a #GdkRectangle.
1593  *
1594  * Tests whether a rectangle is within a region.
1595  *
1596  * Returns: %GDK_OVERLAP_RECTANGLE_IN, %GDK_OVERLAP_RECTANGLE_OUT, or
1597  *   %GDK_OVERLAP_RECTANGLE_PART, depending on whether the rectangle is inside,
1598  *   outside, or partly inside the #GdkRegion, respectively.
1599  */
1600 GdkOverlapType
1601 gdk_region_rect_in (GdkRegion    *region,
1602                     GdkRectangle *rectangle)
1603 {
1604   GdkRegionBox *pbox;
1605   GdkRegionBox *pboxEnd;
1606   GdkRegionBox  rect;
1607   GdkRegionBox *prect = &rect;
1608   gboolean      partIn, partOut;
1609   gint rx, ry;
1610
1611   g_return_val_if_fail (region != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1612   g_return_val_if_fail (rectangle != NULL, GDK_OVERLAP_RECTANGLE_OUT);
1613
1614   rx = rectangle->x;
1615   ry = rectangle->y;
1616   
1617   prect->x1 = rx;
1618   prect->y1 = ry;
1619   prect->x2 = rx + rectangle->width;
1620   prect->y2 = ry + rectangle->height;
1621     
1622     /* this is (just) a useful optimization */
1623   if ((region->numRects == 0) || !EXTENTCHECK (&region->extents, prect))
1624     return GDK_OVERLAP_RECTANGLE_OUT;
1625
1626   partOut = FALSE;
1627   partIn = FALSE;
1628
1629     /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
1630   for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1631        pbox < pboxEnd;
1632        pbox++)
1633     {
1634
1635       if (pbox->y2 <= ry)
1636         continue;       /* getting up to speed or skipping remainder of band */
1637
1638       if (pbox->y1 > ry)
1639         {
1640           partOut = TRUE;       /* missed part of rectangle above */
1641           if (partIn || (pbox->y1 >= prect->y2))
1642             break;
1643           ry = pbox->y1;        /* x guaranteed to be == prect->x1 */
1644         }
1645
1646       if (pbox->x2 <= rx)
1647         continue;               /* not far enough over yet */
1648
1649       if (pbox->x1 > rx)
1650         {
1651           partOut = TRUE;       /* missed part of rectangle to left */
1652           if (partIn)
1653             break;
1654         }
1655
1656       if (pbox->x1 < prect->x2)
1657         {
1658           partIn = TRUE;        /* definitely overlap */
1659           if (partOut)
1660             break;
1661         }
1662
1663       if (pbox->x2 >= prect->x2)
1664         {
1665           ry = pbox->y2;        /* finished with this band */
1666           if (ry >= prect->y2)
1667             break;
1668           rx = prect->x1;       /* reset x out to left again */
1669         }
1670       else
1671         {
1672           /*
1673            * Because boxes in a band are maximal width, if the first box
1674            * to overlap the rectangle doesn't completely cover it in that
1675            * band, the rectangle must be partially out, since some of it
1676            * will be uncovered in that band. partIn will have been set true
1677            * by now...
1678            */
1679           break;
1680         }
1681
1682     }
1683
1684   return (partIn ?
1685              ((ry < prect->y2) ?
1686               GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) : 
1687           GDK_OVERLAP_RECTANGLE_OUT);
1688 }
1689
1690
1691 static void
1692 gdk_region_unsorted_spans_intersect_foreach (GdkRegion *region,
1693                                              GdkSpan   *spans,
1694                                              int        n_spans,
1695                                              GdkSpanFunc function,
1696                                              gpointer data)
1697 {
1698   gint i, left, right, y;
1699   gint clipped_left, clipped_right;
1700   GdkRegionBox *pbox;
1701   GdkRegionBox *pboxEnd;
1702   GdkSpan out_span;
1703
1704   if (!region->numRects)
1705     return;
1706
1707   for (i=0;i<n_spans;i++)
1708     {
1709       y = spans[i].y;
1710       left = spans[i].x;
1711       right = left + spans[i].width; /* right is not in the span! */
1712     
1713       if (! ((region->extents.y1 <= y) &&
1714              (region->extents.y2 > y) &&
1715              (region->extents.x1 < right) &&
1716              (region->extents.x2 > left)) ) 
1717         continue;
1718
1719       /* can stop when we passed y */
1720       for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1721            pbox < pboxEnd;
1722            pbox++)
1723         {
1724           if (pbox->y2 <= y)
1725             continue; /* Not quite there yet */
1726           
1727           if (pbox->y1 > y)
1728             break; /* passed the spanline */
1729           
1730           if ((right > pbox->x1) && (left < pbox->x2)) 
1731             {
1732               clipped_left = MAX (left, pbox->x1);
1733               clipped_right = MIN (right, pbox->x2);
1734               
1735               out_span.y = y;
1736               out_span.x = clipped_left;
1737               out_span.width = clipped_right - clipped_left;
1738               (*function) (&out_span, data);
1739             }
1740         }
1741     }
1742 }
1743
1744 /**
1745  * gdk_region_spans_intersect_foreach:
1746  * @region: a #GdkRegion
1747  * @spans: an array of #GdkSpans
1748  * @n_spans: the length of @spans
1749  * @sorted: %TRUE if @spans is sorted wrt. the y coordinate
1750  * @function: function to call on each span in the intersection
1751  * @data: data to pass to @function
1752  *
1753  * Calls a function on each span in the intersection of @region and @spans.
1754  */
1755 void
1756 gdk_region_spans_intersect_foreach (GdkRegion  *region,
1757                                     GdkSpan    *spans,
1758                                     int         n_spans,
1759                                     gboolean    sorted,
1760                                     GdkSpanFunc function,
1761                                     gpointer    data)
1762 {
1763   gint left, right, y;
1764   gint clipped_left, clipped_right;
1765   GdkRegionBox *pbox;
1766   GdkRegionBox *pboxEnd;
1767   GdkSpan *span, *tmpspan;
1768   GdkSpan *end_span;
1769   GdkSpan out_span;
1770
1771   g_return_if_fail (region != NULL);
1772   g_return_if_fail (spans != NULL);
1773
1774   if (!sorted)
1775     {
1776       gdk_region_unsorted_spans_intersect_foreach (region,
1777                                                    spans,
1778                                                    n_spans,
1779                                                    function,
1780                                                    data);
1781       return;
1782     }
1783   
1784   if ((!region->numRects) || (n_spans == 0))
1785     return;
1786
1787   /* The main method here is to step along the
1788    * sorted rectangles and spans in lock step, and
1789    * clipping the spans that are in the current
1790    * rectangle before going on to the next rectangle.
1791    */
1792
1793   span = spans;
1794   end_span = spans + n_spans;
1795   pbox = region->rects;
1796   pboxEnd = pbox + region->numRects;
1797   while (pbox < pboxEnd)
1798     {
1799       while ((pbox->y2 < span->y) || (span->y < pbox->y1))
1800         {
1801           /* Skip any rectangles that are above the current span */
1802           if (pbox->y2 < span->y)
1803             {
1804               pbox++;
1805               if (pbox == pboxEnd)
1806                 return;
1807             }
1808           /* Skip any spans that are above the current rectangle */
1809           if (span->y < pbox->y1)
1810             {
1811               span++;
1812               if (span == end_span)
1813                 return;
1814             }
1815         }
1816       
1817       /* Ok, we got at least one span that might intersect this rectangle. */
1818       tmpspan = span;
1819       while ((tmpspan < end_span) &&
1820              (tmpspan->y < pbox->y2))
1821         {
1822           y = tmpspan->y;
1823           left = tmpspan->x;
1824           right = left + tmpspan->width; /* right is not in the span! */
1825           
1826           if ((right > pbox->x1) && (left < pbox->x2))
1827             {
1828               clipped_left = MAX (left, pbox->x1);
1829               clipped_right = MIN (right, pbox->x2);
1830               
1831               out_span.y = y;
1832               out_span.x = clipped_left;
1833               out_span.width = clipped_right - clipped_left;
1834               (*function) (&out_span, data);
1835             }
1836           
1837           tmpspan++;
1838         }
1839
1840       /* Finished this rectangle.
1841        * The spans could still intersect the next one
1842        */
1843       pbox++;
1844     }
1845 }
1846
1847 #define __GDK_REGION_GENERIC_C__
1848 #include "gdkaliasdef.c"