]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkregion-generic.c
1140ce04216d9596c6ef43b92d6bc59a961a50b0
[~andy/gtk] / gdk / x11 / 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 <gdkregion.h>
72 #include "gdkregion-generic.h"
73
74 #ifdef DEBUG
75 #include <stdio.h>
76 #define assert(expr) {if (!(expr)) fprintf(stderr,\
77 "Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__); }
78 #else
79 #define assert(expr)
80 #endif
81
82 typedef void (*overlapFunc) (GdkRegion    *pReg,
83                              GdkRegionBox *r1,
84                              GdkRegionBox *r1End,
85                              GdkRegionBox *r2,
86                              GdkRegionBox *r2End,
87                              gint          y1,
88                              gint          y2);
89 typedef void (*nonOverlapFunc) (GdkRegion    *pReg,
90                                 GdkRegionBox *r,
91                                 GdkRegionBox *rEnd,
92                                 gint          y1,
93                                 gint          y2);
94
95 static void miRegionCopy (GdkRegion      *dstrgn,
96                           GdkRegion      *rgn);
97 static void miRegionOp   (GdkRegion      *newReg,
98                           GdkRegion      *reg1,
99                           GdkRegion      *reg2,
100                           overlapFunc     overlapFn,
101                           nonOverlapFunc  nonOverlap1Fn,
102                           nonOverlapFunc  nonOverlap2Fn);
103
104 /*      Create a new empty region       */
105
106 GdkRegion *
107 gdk_region_new ()
108 {
109   GdkRegion *temp;
110
111   temp = g_new (GdkRegion, 1);
112   temp->rects = g_new (GdkRegionBox, 1);
113
114   temp->numRects = 0;
115   temp->extents.x1 = 0;
116   temp->extents.y1 = 0;
117   temp->extents.x2 = 0;
118   temp->extents.y2 = 0;
119   temp->size = 1;
120   
121   return temp;
122 }
123
124 GdkRegion *
125 gdk_region_rectangle (GdkRectangle *rectangle)
126 {
127   GdkRegion *temp;
128
129   if (rectangle->width <= 0 || rectangle->height <= 0)
130     return gdk_region_new();
131
132   temp = g_new (GdkRegion, 1);
133   temp->rects = g_new (GdkRegionBox, 1);
134
135   temp->numRects = 1;
136   temp->extents.x1 = temp->rects[0].x1 = rectangle->x;
137   temp->extents.y1 = temp->rects[0].y1 = rectangle->y;
138   temp->extents.x2 = temp->rects[0].x2 = rectangle->x + rectangle->width;
139   temp->extents.y2 = temp->rects[0].y2 = rectangle->y + rectangle->height;
140   temp->size = 1;
141   
142   return temp;
143 }
144
145 GdkRegion *
146 gdk_region_copy (GdkRegion *region)
147 {
148   GdkRegion *temp;
149
150   temp = g_new (GdkRegion, 1);
151   temp->rects = g_new (GdkRegionBox, region->numRects);
152
153   temp->numRects = region->numRects;
154   temp->extents = region->extents;
155   temp->size = region->numRects;
156   
157   memcpy (temp->rects, region->rects, region->numRects * sizeof (GdkRegionBox));
158
159   return temp;
160 }
161
162 void
163 gdk_region_get_clipbox (GdkRegion *r, GdkRectangle *rect)
164 {
165   rect->x = r->extents.x1;
166   rect->y = r->extents.y1;
167   rect->width = r->extents.x2 - r->extents.x1;
168   rect->height = r->extents.y2 - r->extents.y1;
169 }
170
171 void
172 gdk_region_union_with_rect (GdkRegion    *region,
173                             GdkRectangle *rect)
174 {
175   GdkRegion tmp_region;
176
177   if (!rect->width || !rect->height)
178     return;
179     
180   tmp_region.rects = &tmp_region.extents;
181   tmp_region.numRects = 1;
182   tmp_region.extents.x1 = rect->x;
183   tmp_region.extents.y1 = rect->y;
184   tmp_region.extents.x2 = rect->x + rect->width;
185   tmp_region.extents.y2 = rect->y + rect->height;
186   tmp_region.size = 1;
187
188   gdk_region_union (region, &tmp_region);
189 }
190
191 /*-
192  *-----------------------------------------------------------------------
193  * miSetExtents --
194  *      Reset the extents of a region to what they should be. Called by
195  *      miSubtract and miIntersect b/c they can't figure it out along the
196  *      way or do so easily, as miUnion can.
197  *
198  * Results:
199  *      None.
200  *
201  * Side Effects:
202  *      The region's 'extents' structure is overwritten.
203  *
204  *-----------------------------------------------------------------------
205  */
206 static void
207 miSetExtents (GdkRegion *pReg)
208 {
209   GdkRegionBox *pBox, *pBoxEnd, *pExtents;
210
211   if (pReg->numRects == 0)
212     {
213       pReg->extents.x1 = 0;
214       pReg->extents.y1 = 0;
215       pReg->extents.x2 = 0;
216       pReg->extents.y2 = 0;
217       return;
218     }
219
220   pExtents = &pReg->extents;
221   pBox = pReg->rects;
222   pBoxEnd = &pBox[pReg->numRects - 1];
223
224     /*
225      * Since pBox is the first rectangle in the region, it must have the
226      * smallest y1 and since pBoxEnd is the last rectangle in the region,
227      * it must have the largest y2, because of banding. Initialize x1 and
228      * x2 from  pBox and pBoxEnd, resp., as good things to initialize them
229      * to...
230      */
231   pExtents->x1 = pBox->x1;
232   pExtents->y1 = pBox->y1;
233   pExtents->x2 = pBoxEnd->x2;
234   pExtents->y2 = pBoxEnd->y2;
235
236   assert(pExtents->y1 < pExtents->y2);
237   while (pBox <= pBoxEnd)
238     {
239       if (pBox->x1 < pExtents->x1)
240         {
241           pExtents->x1 = pBox->x1;
242         }
243       if (pBox->x2 > pExtents->x2)
244         {
245           pExtents->x2 = pBox->x2;
246         }
247       pBox++;
248     }
249   assert(pExtents->x1 < pExtents->x2);
250 }
251
252 void
253 gdk_region_destroy (GdkRegion *r)
254 {
255     g_free (r->rects);
256     g_free (r);
257 }
258
259
260 /* TranslateRegion(pRegion, x, y)
261    translates in place
262    added by raymond
263 */
264
265 void
266 gdk_region_offset (GdkRegion *region,
267                    gint       x,
268                    gint       y)
269 {
270   int nbox;
271   GdkRegionBox *pbox;
272
273   pbox = region->rects;
274   nbox = region->numRects;
275
276   while(nbox--)
277     {
278       pbox->x1 += x;
279       pbox->x2 += x;
280       pbox->y1 += y;
281       pbox->y2 += y;
282       pbox++;
283     }
284   region->extents.x1 += x;
285   region->extents.x2 += x;
286   region->extents.y1 += y;
287   region->extents.y2 += y;
288 }
289
290 /* 
291    Utility procedure Compress:
292    Replace r by the region r', where 
293      p in r' iff (Quantifer m <= dx) (p + m in r), and
294      Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
295      (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
296
297    Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
298    of all points p such that p and the next dx points on the same
299    horizontal scan line are all in r.  We do this using by noting
300    that p is the head of a run of length 2^i + k iff p is the head
301    of a run of length 2^i and p+2^i is the head of a run of length
302    k. Thus, the loop invariant: s contains the region corresponding
303    to the runs of length shift.  r contains the region corresponding
304    to the runs of length 1 + dxo & (shift-1), where dxo is the original
305    value of dx.  dx = dxo & ~(shift-1).  As parameters, s and t are
306    scratch regions, so that we don't have to allocate them on every
307    call.
308 */
309
310 #define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \
311                          else gdk_region_intersect (a,b)
312 #define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \
313                           else gdk_region_offset (a,0,b)
314
315 static void
316 Compress(GdkRegion *r,
317          GdkRegion *s,
318          GdkRegion *t,
319          guint      dx,
320          int        xdir,
321          int        grow)
322 {
323   guint shift = 1;
324
325   miRegionCopy (s, r);
326   while (dx)
327     {
328       if (dx & shift)
329         {
330           ZShiftRegion(r, -(int)shift);
331           ZOpRegion(r, s);
332           dx -= shift;
333           if (!dx) break;
334         }
335       miRegionCopy (t, s);
336       ZShiftRegion(s, -(int)shift);
337       ZOpRegion(s, t);
338       shift <<= 1;
339     }
340 }
341
342 #undef ZOpRegion
343 #undef ZShiftRegion
344 #undef ZCopyRegion
345
346 void
347 gdk_region_shrink (GdkRegion *r,
348                    int        dx,
349                    int        dy)
350 {
351   GdkRegion *s, *t;
352   int grow;
353
354   if (!dx && !dy)
355     return;
356
357   s = gdk_region_new ();
358   t = gdk_region_new ();
359
360   grow = (dx < 0);
361   if (grow)
362     dx = -dx;
363   if (dx)
364      Compress(r, s, t, (unsigned) 2*dx, TRUE, grow);
365      
366   grow = (dy < 0);
367   if (grow)
368     dy = -dy;
369   if (dy)
370      Compress(r, s, t, (unsigned) 2*dy, FALSE, grow);
371   
372   gdk_region_offset (r, dx, dy);
373   gdk_region_destroy (s);
374   gdk_region_destroy (t);
375 }
376
377 \f
378 /*======================================================================
379  *          Region Intersection
380  *====================================================================*/
381 /*-
382  *-----------------------------------------------------------------------
383  * miIntersectO --
384  *      Handle an overlapping band for miIntersect.
385  *
386  * Results:
387  *      None.
388  *
389  * Side Effects:
390  *      Rectangles may be added to the region.
391  *
392  *-----------------------------------------------------------------------
393  */
394 /* static void*/
395 static void
396 miIntersectO (GdkRegion    *pReg,
397               GdkRegionBox *r1,
398               GdkRegionBox *r1End,
399               GdkRegionBox *r2,
400               GdkRegionBox *r2End,
401               gint          y1,
402               gint          y2)
403 {
404   int   x1;
405   int   x2;
406   GdkRegionBox *pNextRect;
407
408   pNextRect = &pReg->rects[pReg->numRects];
409
410   while ((r1 != r1End) && (r2 != r2End))
411     {
412       x1 = MAX (r1->x1,r2->x1);
413       x2 = MIN (r1->x2,r2->x2);
414
415       /*
416        * If there's any overlap between the two rectangles, add that
417        * overlap to the new region.
418        * There's no need to check for subsumption because the only way
419        * such a need could arise is if some region has two rectangles
420        * right next to each other. Since that should never happen...
421        */
422       if (x1 < x2)
423         {
424           assert (y1<y2);
425
426           MEMCHECK (pReg, pNextRect, pReg->rects);
427           pNextRect->x1 = x1;
428           pNextRect->y1 = y1;
429           pNextRect->x2 = x2;
430           pNextRect->y2 = y2;
431           pReg->numRects += 1;
432           pNextRect++;
433           assert (pReg->numRects <= pReg->size);
434         }
435
436       /*
437        * Need to advance the pointers. Shift the one that extends
438        * to the right the least, since the other still has a chance to
439        * overlap with that region's next rectangle, if you see what I mean.
440        */
441       if (r1->x2 < r2->x2)
442         {
443           r1++;
444         }
445       else if (r2->x2 < r1->x2)
446         {
447           r2++;
448         }
449       else
450         {
451           r1++;
452           r2++;
453         }
454     }
455 }
456
457 void
458 gdk_region_intersect (GdkRegion *region,
459                       GdkRegion *other)
460 {
461   /* check for trivial reject */
462   if ((!(region->numRects)) || (!(other->numRects))  ||
463       (!EXTENTCHECK(&region->extents, &other->extents)))
464     region->numRects = 0;
465   else
466     miRegionOp (region, region, other, 
467                 miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL);
468     
469   /*
470    * Can't alter region's extents before miRegionOp depends on the
471    * extents of the regions being unchanged. Besides, this way there's
472    * no checking against rectangles that will be nuked due to
473    * coalescing, so we have to examine fewer rectangles.
474    */
475   miSetExtents(region);
476 }
477
478 static void
479 miRegionCopy(GdkRegion *dstrgn, GdkRegion *rgn)
480 {
481   if (dstrgn != rgn) /*  don't want to copy to itself */
482     {  
483       if (dstrgn->size < rgn->numRects)
484         {
485           dstrgn->rects = g_renew (GdkRegionBox, dstrgn->rects, rgn->numRects);
486           dstrgn->size = rgn->numRects;
487         }
488       dstrgn->numRects = rgn->numRects;
489       dstrgn->extents.x1 = rgn->extents.x1;
490       dstrgn->extents.y1 = rgn->extents.y1;
491       dstrgn->extents.x2 = rgn->extents.x2;
492       dstrgn->extents.y2 = rgn->extents.y2;
493
494       memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox));
495     }
496 }
497
498
499 /*======================================================================
500  *          Generic Region Operator
501  *====================================================================*/
502
503 /*-
504  *-----------------------------------------------------------------------
505  * miCoalesce --
506  *      Attempt to merge the boxes in the current band with those in the
507  *      previous one. Used only by miRegionOp.
508  *
509  * Results:
510  *      The new index for the previous band.
511  *
512  * Side Effects:
513  *      If coalescing takes place:
514  *          - rectangles in the previous band will have their y2 fields
515  *            altered.
516  *          - pReg->numRects will be decreased.
517  *
518  *-----------------------------------------------------------------------
519  */
520 /* static int*/
521 static int
522 miCoalesce (GdkRegion *pReg,         /* Region to coalesce */
523             gint       prevStart,    /* Index of start of previous band */
524             gint       curStart)     /* Index of start of current band */
525 {
526   GdkRegionBox *pPrevBox;       /* Current box in previous band */
527   GdkRegionBox *pCurBox;        /* Current box in current band */
528   GdkRegionBox *pRegEnd;        /* End of region */
529   int           curNumRects;    /* Number of rectangles in current
530                                  * band */
531   int           prevNumRects;   /* Number of rectangles in previous
532                                  * band */
533   int           bandY1;         /* Y1 coordinate for current band */
534
535   pRegEnd = &pReg->rects[pReg->numRects];
536
537   pPrevBox = &pReg->rects[prevStart];
538   prevNumRects = curStart - prevStart;
539
540     /*
541      * Figure out how many rectangles are in the current band. Have to do
542      * this because multiple bands could have been added in miRegionOp
543      * at the end when one region has been exhausted.
544      */
545   pCurBox = &pReg->rects[curStart];
546   bandY1 = pCurBox->y1;
547   for (curNumRects = 0;
548        (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
549        curNumRects++)
550     {
551       pCurBox++;
552     }
553     
554   if (pCurBox != pRegEnd)
555     {
556       /*
557        * If more than one band was added, we have to find the start
558        * of the last band added so the next coalescing job can start
559        * at the right place... (given when multiple bands are added,
560        * this may be pointless -- see above).
561        */
562       pRegEnd--;
563       while (pRegEnd[-1].y1 == pRegEnd->y1)
564         {
565           pRegEnd--;
566         }
567       curStart = pRegEnd - pReg->rects;
568       pRegEnd = pReg->rects + pReg->numRects;
569     }
570         
571   if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
572     pCurBox -= curNumRects;
573     /*
574      * The bands may only be coalesced if the bottom of the previous
575      * matches the top scanline of the current.
576      */
577     if (pPrevBox->y2 == pCurBox->y1)
578       {
579         /*
580          * Make sure the bands have boxes in the same places. This
581          * assumes that boxes have been added in such a way that they
582          * cover the most area possible. I.e. two boxes in a band must
583          * have some horizontal space between them.
584          */
585         do
586           {
587             if ((pPrevBox->x1 != pCurBox->x1) ||
588                 (pPrevBox->x2 != pCurBox->x2))
589               {
590                 /*
591                  * The bands don't line up so they can't be coalesced.
592                  */
593                 return (curStart);
594               }
595             pPrevBox++;
596             pCurBox++;
597             prevNumRects -= 1;
598           } while (prevNumRects != 0);
599
600         pReg->numRects -= curNumRects;
601         pCurBox -= curNumRects;
602         pPrevBox -= curNumRects;
603
604         /*
605          * The bands may be merged, so set the bottom y of each box
606          * in the previous band to that of the corresponding box in
607          * the current band.
608          */
609         do
610           {
611             pPrevBox->y2 = pCurBox->y2;
612             pPrevBox++;
613             pCurBox++;
614             curNumRects -= 1;
615           }
616         while (curNumRects != 0);
617
618         /*
619          * If only one band was added to the region, we have to backup
620          * curStart to the start of the previous band.
621          *
622          * If more than one band was added to the region, copy the
623          * other bands down. The assumption here is that the other bands
624          * came from the same region as the current one and no further
625          * coalescing can be done on them since it's all been done
626          * already... curStart is already in the right place.
627          */
628         if (pCurBox == pRegEnd)
629           {
630             curStart = prevStart;
631           }
632         else
633           {
634             do
635               {
636                 *pPrevBox++ = *pCurBox++;
637               }
638             while (pCurBox != pRegEnd);
639           }
640             
641       }
642   }
643   return curStart;
644 }
645
646 /*-
647  *-----------------------------------------------------------------------
648  * miRegionOp --
649  *      Apply an operation to two regions. Called by miUnion, miInverse,
650  *      miSubtract, miIntersect...
651  *
652  * Results:
653  *      None.
654  *
655  * Side Effects:
656  *      The new region is overwritten.
657  *
658  * Notes:
659  *      The idea behind this function is to view the two regions as sets.
660  *      Together they cover a rectangle of area that this function divides
661  *      into horizontal bands where points are covered only by one region
662  *      or by both. For the first case, the nonOverlapFunc is called with
663  *      each the band and the band's upper and lower extents. For the
664  *      second, the overlapFunc is called to process the entire band. It
665  *      is responsible for clipping the rectangles in the band, though
666  *      this function provides the boundaries.
667  *      At the end of each band, the new region is coalesced, if possible,
668  *      to reduce the number of rectangles in the region.
669  *
670  *-----------------------------------------------------------------------
671  */
672 /* static void*/
673 static void
674 miRegionOp(GdkRegion *newReg,
675            GdkRegion *reg1,
676            GdkRegion *reg2,
677            overlapFunc    overlapFn,            /* Function to call for over-
678                                                  * lapping bands */
679            nonOverlapFunc nonOverlap1Fn,        /* Function to call for non-
680                                                  * overlapping bands in region
681                                                  * 1 */
682            nonOverlapFunc nonOverlap2Fn)        /* Function to call for non-
683                                                  * overlapping bands in region
684                                                  * 2 */
685 {
686     GdkRegionBox *r1;                   /* Pointer into first region */
687     GdkRegionBox *r2;                   /* Pointer into 2d region */
688     GdkRegionBox *r1End;                /* End of 1st region */
689     GdkRegionBox *r2End;                /* End of 2d region */
690     int           ybot;                 /* Bottom of intersection */
691     int           ytop;                 /* Top of intersection */
692     GdkRegionBox *oldRects;             /* Old rects for newReg */
693     int           prevBand;             /* Index of start of
694                                          * previous band in newReg */
695     int           curBand;              /* Index of start of current
696                                          * band in newReg */
697     GdkRegionBox *r1BandEnd;            /* End of current band in r1 */
698     GdkRegionBox *r2BandEnd;            /* End of current band in r2 */
699     int           top;                  /* Top of non-overlapping
700                                          * band */
701     int           bot;                  /* Bottom of non-overlapping
702                                          * band */
703     
704     /*
705      * Initialization:
706      *  set r1, r2, r1End and r2End appropriately, preserve the important
707      * parts of the destination region until the end in case it's one of
708      * the two source regions, then mark the "new" region empty, allocating
709      * another array of rectangles for it to use.
710      */
711     r1 = reg1->rects;
712     r2 = reg2->rects;
713     r1End = r1 + reg1->numRects;
714     r2End = r2 + reg2->numRects;
715     
716     oldRects = newReg->rects;
717     
718     EMPTY_REGION(newReg);
719
720     /*
721      * Allocate a reasonable number of rectangles for the new region. The idea
722      * is to allocate enough so the individual functions don't need to
723      * reallocate and copy the array, which is time consuming, yet we don't
724      * have to worry about using too much memory. I hope to be able to
725      * nuke the Xrealloc() at the end of this function eventually.
726      */
727     newReg->size = MAX (reg1->numRects, reg2->numRects) * 2;
728     newReg->rects = g_new (GdkRegionBox, newReg->size);
729     
730     /*
731      * Initialize ybot and ytop.
732      * In the upcoming loop, ybot and ytop serve different functions depending
733      * on whether the band being handled is an overlapping or non-overlapping
734      * band.
735      *  In the case of a non-overlapping band (only one of the regions
736      * has points in the band), ybot is the bottom of the most recent
737      * intersection and thus clips the top of the rectangles in that band.
738      * ytop is the top of the next intersection between the two regions and
739      * serves to clip the bottom of the rectangles in the current band.
740      *  For an overlapping band (where the two regions intersect), ytop clips
741      * the top of the rectangles of both regions and ybot clips the bottoms.
742      */
743     if (reg1->extents.y1 < reg2->extents.y1)
744       ybot = reg1->extents.y1;
745     else
746       ybot = reg2->extents.y1;
747     
748     /*
749      * prevBand serves to mark the start of the previous band so rectangles
750      * can be coalesced into larger rectangles. qv. miCoalesce, above.
751      * In the beginning, there is no previous band, so prevBand == curBand
752      * (curBand is set later on, of course, but the first band will always
753      * start at index 0). prevBand and curBand must be indices because of
754      * the possible expansion, and resultant moving, of the new region's
755      * array of rectangles.
756      */
757     prevBand = 0;
758     
759     do
760       {
761         curBand = newReg->numRects;
762
763         /*
764          * This algorithm proceeds one source-band (as opposed to a
765          * destination band, which is determined by where the two regions
766          * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
767          * rectangle after the last one in the current band for their
768          * respective regions.
769          */
770         r1BandEnd = r1;
771         while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
772           {
773             r1BandEnd++;
774           }
775         
776         r2BandEnd = r2;
777         while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
778           {
779             r2BandEnd++;
780           }
781         
782         /*
783          * First handle the band that doesn't intersect, if any.
784          *
785          * Note that attention is restricted to one band in the
786          * non-intersecting region at once, so if a region has n
787          * bands between the current position and the next place it overlaps
788          * the other, this entire loop will be passed through n times.
789          */
790         if (r1->y1 < r2->y1)
791           {
792             top = MAX (r1->y1,ybot);
793             bot = MIN (r1->y2,r2->y1);
794
795             if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL))
796               {
797                 (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot);
798               }
799
800             ytop = r2->y1;
801           }
802         else if (r2->y1 < r1->y1)
803           {
804             top = MAX (r2->y1,ybot);
805             bot = MIN (r2->y2,r1->y1);
806
807             if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL))
808               {
809                 (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot);
810               }
811
812             ytop = r1->y1;
813           }
814         else
815           {
816             ytop = r1->y1;
817           }
818
819         /*
820          * If any rectangles got added to the region, try and coalesce them
821          * with rectangles from the previous band. Note we could just do
822          * this test in miCoalesce, but some machines incur a not
823          * inconsiderable cost for function calls, so...
824          */
825         if (newReg->numRects != curBand)
826           {
827             prevBand = miCoalesce (newReg, prevBand, curBand);
828           }
829
830         /*
831          * Now see if we've hit an intersecting band. The two bands only
832          * intersect if ybot > ytop
833          */
834         ybot = MIN (r1->y2, r2->y2);
835         curBand = newReg->numRects;
836         if (ybot > ytop)
837           {
838             (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
839
840           }
841         
842         if (newReg->numRects != curBand)
843           {
844             prevBand = miCoalesce (newReg, prevBand, curBand);
845           }
846
847         /*
848          * If we've finished with a band (y2 == ybot) we skip forward
849          * in the region to the next band.
850          */
851         if (r1->y2 == ybot)
852           {
853             r1 = r1BandEnd;
854           }
855         if (r2->y2 == ybot)
856           {
857             r2 = r2BandEnd;
858           }
859       } while ((r1 != r1End) && (r2 != r2End));
860
861     /*
862      * Deal with whichever region still has rectangles left.
863      */
864     curBand = newReg->numRects;
865     if (r1 != r1End)
866       {
867         if (nonOverlap1Fn != (nonOverlapFunc )NULL)
868           {
869             do
870               {
871                 r1BandEnd = r1;
872                 while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
873                   {
874                     r1BandEnd++;
875                   }
876                 (* nonOverlap1Fn) (newReg, r1, r1BandEnd,
877                                      MAX (r1->y1,ybot), r1->y2);
878                 r1 = r1BandEnd;
879               } while (r1 != r1End);
880           }
881       }
882     else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL))
883       {
884         do
885           {
886             r2BandEnd = r2;
887             while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
888               {
889                 r2BandEnd++;
890               }
891             (* nonOverlap2Fn) (newReg, r2, r2BandEnd,
892                                MAX (r2->y1,ybot), r2->y2);
893             r2 = r2BandEnd;
894           } while (r2 != r2End);
895       }
896
897     if (newReg->numRects != curBand)
898     {
899       (void) miCoalesce (newReg, prevBand, curBand);
900     }
901
902     /*
903      * A bit of cleanup. To keep regions from growing without bound,
904      * we shrink the array of rectangles to match the new number of
905      * rectangles in the region. This never goes to 0, however...
906      *
907      * Only do this stuff if the number of rectangles allocated is more than
908      * twice the number of rectangles in the region (a simple optimization...).
909      */
910     if (newReg->numRects < (newReg->size >> 1))
911       {
912         if (REGION_NOT_EMPTY (newReg))
913           {
914             newReg->size = newReg->numRects;
915             newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size);
916           }
917         else
918           {
919             /*
920              * No point in doing the extra work involved in an Xrealloc if
921              * the region is empty
922              */
923             newReg->size = 1;
924             g_free (newReg->rects);
925             newReg->rects = g_new (GdkRegionBox, 1);
926           }
927       }
928     g_free (oldRects);
929 }
930
931 \f
932 /*======================================================================
933  *          Region Union
934  *====================================================================*/
935
936 /*-
937  *-----------------------------------------------------------------------
938  * miUnionNonO --
939  *      Handle a non-overlapping band for the union operation. Just
940  *      Adds the rectangles into the region. Doesn't have to check for
941  *      subsumption or anything.
942  *
943  * Results:
944  *      None.
945  *
946  * Side Effects:
947  *      pReg->numRects is incremented and the final rectangles overwritten
948  *      with the rectangles we're passed.
949  *
950  *-----------------------------------------------------------------------
951  */
952 static void
953 miUnionNonO (GdkRegion    *pReg,
954              GdkRegionBox *r,
955              GdkRegionBox *rEnd,
956              gint          y1,
957              gint          y2)
958 {
959   GdkRegionBox *pNextRect;
960
961   pNextRect = &pReg->rects[pReg->numRects];
962
963   assert(y1 < y2);
964
965   while (r != rEnd)
966     {
967       assert(r->x1 < r->x2);
968       MEMCHECK(pReg, pNextRect, pReg->rects);
969       pNextRect->x1 = r->x1;
970       pNextRect->y1 = y1;
971       pNextRect->x2 = r->x2;
972       pNextRect->y2 = y2;
973       pReg->numRects += 1;
974       pNextRect++;
975
976       assert(pReg->numRects<=pReg->size);
977       r++;
978     }
979 }
980
981
982 /*-
983  *-----------------------------------------------------------------------
984  * miUnionO --
985  *      Handle an overlapping band for the union operation. Picks the
986  *      left-most rectangle each time and merges it into the region.
987  *
988  * Results:
989  *      None.
990  *
991  * Side Effects:
992  *      Rectangles are overwritten in pReg->rects and pReg->numRects will
993  *      be changed.
994  *
995  *-----------------------------------------------------------------------
996  */
997
998 /* static void*/
999 static void
1000 miUnionO (GdkRegion *pReg,
1001           GdkRegionBox *r1,
1002           GdkRegionBox *r1End,
1003           GdkRegionBox *r2,
1004           GdkRegionBox *r2End,
1005           gint          y1,
1006           gint          y2)
1007 {
1008   GdkRegionBox *        pNextRect;
1009     
1010   pNextRect = &pReg->rects[pReg->numRects];
1011
1012 #define MERGERECT(r)                                    \
1013     if ((pReg->numRects != 0) &&                        \
1014         (pNextRect[-1].y1 == y1) &&                     \
1015         (pNextRect[-1].y2 == y2) &&                     \
1016         (pNextRect[-1].x2 >= r->x1))                    \
1017       {                                                 \
1018         if (pNextRect[-1].x2 < r->x2)                   \
1019           {                                             \
1020             pNextRect[-1].x2 = r->x2;                   \
1021             assert(pNextRect[-1].x1<pNextRect[-1].x2);  \
1022           }                                             \
1023       }                                                 \
1024     else                                                \
1025       {                                                 \
1026         MEMCHECK(pReg, pNextRect, pReg->rects);         \
1027         pNextRect->y1 = y1;                             \
1028         pNextRect->y2 = y2;                             \
1029         pNextRect->x1 = r->x1;                          \
1030         pNextRect->x2 = r->x2;                          \
1031         pReg->numRects += 1;                            \
1032         pNextRect += 1;                                 \
1033       }                                                 \
1034     assert(pReg->numRects<=pReg->size);                 \
1035     r++;
1036     
1037     assert (y1<y2);
1038     while ((r1 != r1End) && (r2 != r2End))
1039     {
1040         if (r1->x1 < r2->x1)
1041         {
1042             MERGERECT(r1);
1043         }
1044         else
1045         {
1046             MERGERECT(r2);
1047         }
1048     }
1049     
1050     if (r1 != r1End)
1051     {
1052         do
1053         {
1054             MERGERECT(r1);
1055         } while (r1 != r1End);
1056     }
1057     else while (r2 != r2End)
1058     {
1059         MERGERECT(r2);
1060     }
1061 }
1062
1063 void
1064 gdk_region_union (GdkRegion *region,
1065                   GdkRegion *other)
1066 {
1067   /*  checks all the simple cases */
1068
1069     /*
1070      * region and other are the same or other is empty
1071      */
1072   if ((region == other) || (!(other->numRects)))
1073     return;
1074
1075     /* 
1076      * region is empty
1077      */
1078   if (!(region->numRects))
1079     {
1080       miRegionCopy (region, other);
1081       return;
1082     }
1083
1084   /*
1085      * region completely subsumes otehr
1086      */
1087   if ((region->numRects == 1) && 
1088       (region->extents.x1 <= other->extents.x1) &&
1089       (region->extents.y1 <= other->extents.y1) &&
1090       (region->extents.x2 >= other->extents.x2) &&
1091       (region->extents.y2 >= other->extents.y2))
1092     return;
1093
1094   /*
1095      * other completely subsumes region
1096      */
1097   if ((other->numRects == 1) && 
1098       (other->extents.x1 <= region->extents.x1) &&
1099       (other->extents.y1 <= region->extents.y1) &&
1100       (other->extents.x2 >= region->extents.x2) &&
1101       (other->extents.y2 >= region->extents.y2))
1102     {
1103       miRegionCopy(region, other);
1104       return;
1105     }
1106
1107   miRegionOp (region, region, other, miUnionO, 
1108               miUnionNonO, miUnionNonO);
1109
1110   region->extents.x1 = MIN (region->extents.x1, other->extents.x1);
1111   region->extents.y1 = MIN (region->extents.y1, other->extents.y1);
1112   region->extents.x2 = MAX (region->extents.x2, other->extents.x2);
1113   region->extents.y2 = MAX (region->extents.y2, other->extents.y2);
1114 }
1115
1116 \f
1117 /*======================================================================
1118  *                Region Subtraction
1119  *====================================================================*/
1120
1121 /*-
1122  *-----------------------------------------------------------------------
1123  * miSubtractNonO --
1124  *      Deal with non-overlapping band for subtraction. Any parts from
1125  *      region 2 we discard. Anything from region 1 we add to the region.
1126  *
1127  * Results:
1128  *      None.
1129  *
1130  * Side Effects:
1131  *      pReg may be affected.
1132  *
1133  *-----------------------------------------------------------------------
1134  */
1135 /* static void*/
1136 static void
1137 miSubtractNonO1 (GdkRegion    *pReg,
1138                  GdkRegionBox *r,
1139                  GdkRegionBox *rEnd,
1140                  gint          y1,
1141                  gint          y2)
1142 {
1143   GdkRegionBox *        pNextRect;
1144         
1145   pNextRect = &pReg->rects[pReg->numRects];
1146         
1147   assert(y1<y2);
1148
1149   while (r != rEnd)
1150     {
1151       assert (r->x1<r->x2);
1152       MEMCHECK (pReg, pNextRect, pReg->rects);
1153       pNextRect->x1 = r->x1;
1154       pNextRect->y1 = y1;
1155       pNextRect->x2 = r->x2;
1156       pNextRect->y2 = y2;
1157       pReg->numRects += 1;
1158       pNextRect++;
1159
1160       assert (pReg->numRects <= pReg->size);
1161
1162       r++;
1163     }
1164 }
1165
1166 /*-
1167  *-----------------------------------------------------------------------
1168  * miSubtractO --
1169  *      Overlapping band subtraction. x1 is the left-most point not yet
1170  *      checked.
1171  *
1172  * Results:
1173  *      None.
1174  *
1175  * Side Effects:
1176  *      pReg may have rectangles added to it.
1177  *
1178  *-----------------------------------------------------------------------
1179  */
1180 /* static void*/
1181 static void
1182 miSubtractO (GdkRegion    *pReg,
1183              GdkRegionBox *r1,
1184              GdkRegionBox *r1End,
1185              GdkRegionBox *r2,
1186              GdkRegionBox *r2End,
1187              gint          y1,
1188              gint          y2)
1189 {
1190   GdkRegionBox *        pNextRect;
1191   int   x1;
1192     
1193   x1 = r1->x1;
1194     
1195   assert(y1<y2);
1196   pNextRect = &pReg->rects[pReg->numRects];
1197
1198   while ((r1 != r1End) && (r2 != r2End))
1199     {
1200       if (r2->x2 <= x1)
1201         {
1202           /*
1203            * Subtrahend missed the boat: go to next subtrahend.
1204            */
1205           r2++;
1206         }
1207       else if (r2->x1 <= x1)
1208         {
1209           /*
1210            * Subtrahend preceeds minuend: nuke left edge of minuend.
1211            */
1212           x1 = r2->x2;
1213           if (x1 >= r1->x2)
1214             {
1215               /*
1216                * Minuend completely covered: advance to next minuend and
1217                * reset left fence to edge of new minuend.
1218                */
1219               r1++;
1220               if (r1 != r1End)
1221                 x1 = r1->x1;
1222             }
1223           else
1224             {
1225               /*
1226                * Subtrahend now used up since it doesn't extend beyond
1227                * minuend
1228                */
1229               r2++;
1230             }
1231         }
1232       else if (r2->x1 < r1->x2)
1233         {
1234           /*
1235            * Left part of subtrahend covers part of minuend: add uncovered
1236            * part of minuend to region and skip to next subtrahend.
1237            */
1238           assert(x1<r2->x1);
1239           MEMCHECK(pReg, pNextRect, pReg->rects);
1240           pNextRect->x1 = x1;
1241           pNextRect->y1 = y1;
1242           pNextRect->x2 = r2->x1;
1243           pNextRect->y2 = y2;
1244           pReg->numRects += 1;
1245           pNextRect++;
1246
1247           assert(pReg->numRects<=pReg->size);
1248
1249           x1 = r2->x2;
1250           if (x1 >= r1->x2)
1251             {
1252               /*
1253                * Minuend used up: advance to new...
1254                */
1255               r1++;
1256               if (r1 != r1End)
1257                 x1 = r1->x1;
1258             }
1259           else
1260             {
1261               /*
1262                * Subtrahend used up
1263                */
1264               r2++;
1265             }
1266         }
1267       else
1268         {
1269           /*
1270            * Minuend used up: add any remaining piece before advancing.
1271            */
1272           if (r1->x2 > x1)
1273             {
1274               MEMCHECK(pReg, pNextRect, pReg->rects);
1275               pNextRect->x1 = x1;
1276               pNextRect->y1 = y1;
1277               pNextRect->x2 = r1->x2;
1278               pNextRect->y2 = y2;
1279               pReg->numRects += 1;
1280               pNextRect++;
1281               assert(pReg->numRects<=pReg->size);
1282             }
1283           r1++;
1284           x1 = r1->x1;
1285         }
1286     }
1287
1288   /*
1289      * Add remaining minuend rectangles to region.
1290      */
1291   while (r1 != r1End)
1292     {
1293       assert(x1<r1->x2);
1294       MEMCHECK(pReg, pNextRect, pReg->rects);
1295       pNextRect->x1 = x1;
1296       pNextRect->y1 = y1;
1297       pNextRect->x2 = r1->x2;
1298       pNextRect->y2 = y2;
1299       pReg->numRects += 1;
1300       pNextRect++;
1301
1302       assert(pReg->numRects<=pReg->size);
1303
1304       r1++;
1305       if (r1 != r1End)
1306         {
1307           x1 = r1->x1;
1308         }
1309     }
1310 }
1311         
1312 /*-
1313  *-----------------------------------------------------------------------
1314  * gdk_region_subtract --
1315  *      Subtract other from region and leave the result in region.
1316  *
1317  * Results:
1318  *      TRUE.
1319  *
1320  * Side Effects:
1321  *      region is overwritten.
1322  *
1323  *-----------------------------------------------------------------------
1324  */
1325
1326 void
1327 gdk_region_subtract (GdkRegion *region,
1328                      GdkRegion *other)
1329 {
1330   /* check for trivial reject */
1331   if ((!(region->numRects)) || (!(other->numRects)) ||
1332       (!EXTENTCHECK(&region->extents, &other->extents)))
1333     return;
1334  
1335   miRegionOp (region, region, other, miSubtractO,
1336               miSubtractNonO1, (nonOverlapFunc) NULL);
1337
1338   /*
1339    * Can't alter region's extents before we call miRegionOp because miRegionOp
1340    * depends on the extents of those regions being the unaltered. Besides, this
1341    * way there's no checking against rectangles that will be nuked
1342    * due to coalescing, so we have to examine fewer rectangles.
1343    */
1344   miSetExtents (region);
1345 }
1346
1347 void
1348 gdk_region_xor (GdkRegion *sra,
1349                 GdkRegion *srb)
1350 {
1351   GdkRegion *trb;
1352
1353   trb = gdk_region_copy (srb);
1354
1355   gdk_region_subtract (trb, sra);
1356   gdk_region_subtract (sra, srb);
1357
1358   gdk_region_union (sra,trb);
1359   
1360   gdk_region_destroy (trb);
1361 }
1362
1363 /*
1364  * Check to see if the region is empty.  Assumes a region is passed 
1365  * as a parameter
1366  */
1367 gboolean
1368 gdk_region_empty (GdkRegion *r)
1369 {
1370   if (r->numRects == 0)
1371     return TRUE;
1372   else
1373     return FALSE;
1374 }
1375
1376 /*
1377  *      Check to see if two regions are equal   
1378  */
1379 gboolean
1380 gdk_region_equal (GdkRegion *r1,
1381                   GdkRegion *r2)
1382 {
1383   int i;
1384
1385   if (r1->numRects != r2->numRects) return FALSE;
1386   else if (r1->numRects == 0) return TRUE;
1387   else if (r1->extents.x1 != r2->extents.x1) return FALSE;
1388   else if (r1->extents.x2 != r2->extents.x2) return FALSE;
1389   else if (r1->extents.y1 != r2->extents.y1) return FALSE;
1390   else if (r1->extents.y2 != r2->extents.y2) return FALSE;
1391   else
1392     for(i=0; i < r1->numRects; i++ )
1393       {
1394         if (r1->rects[i].x1 != r2->rects[i].x1) return FALSE;
1395         else if (r1->rects[i].x2 != r2->rects[i].x2) return FALSE;
1396         else if (r1->rects[i].y1 != r2->rects[i].y1) return FALSE;
1397         else if (r1->rects[i].y2 != r2->rects[i].y2) return FALSE;
1398       }
1399   return TRUE;
1400 }
1401
1402 gboolean
1403 gdk_region_point_in (GdkRegion *region,
1404                      int        x,
1405                      int        y)
1406 {
1407   int i;
1408
1409   if (region->numRects == 0)
1410     return FALSE;
1411   if (!INBOX(region->extents, x, y))
1412     return FALSE;
1413   for (i=0; i<region->numRects; i++)
1414     {
1415       if (INBOX (region->rects[i], x, y))
1416         return TRUE;
1417     }
1418   return FALSE;
1419 }
1420
1421 GdkOverlapType
1422 gdk_region_rect_in (GdkRegion    *region,
1423                     GdkRectangle *rectangle)
1424 {
1425   GdkRegionBox *pbox;
1426   GdkRegionBox *pboxEnd;
1427   GdkRegionBox  rect;
1428   GdkRegionBox *prect = &rect;
1429   gboolean      partIn, partOut;
1430
1431   gint rx = rectangle->x;
1432   gint ry = rectangle->y;
1433   
1434   prect->x1 = rx;
1435   prect->y1 = ry;
1436   prect->x2 = rx + rectangle->width;
1437   prect->y2 = ry + rectangle->height;
1438     
1439     /* this is (just) a useful optimization */
1440   if ((region->numRects == 0) || !EXTENTCHECK (&region->extents, prect))
1441     return GDK_OVERLAP_RECTANGLE_OUT;
1442
1443   partOut = FALSE;
1444   partIn = FALSE;
1445
1446     /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
1447   for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1448        pbox < pboxEnd;
1449        pbox++)
1450     {
1451
1452       if (pbox->y2 <= ry)
1453         continue;       /* getting up to speed or skipping remainder of band */
1454
1455       if (pbox->y1 > ry)
1456         {
1457           partOut = TRUE;       /* missed part of rectangle above */
1458           if (partIn || (pbox->y1 >= prect->y2))
1459             break;
1460           ry = pbox->y1;        /* x guaranteed to be == prect->x1 */
1461         }
1462
1463       if (pbox->x2 <= rx)
1464         continue;               /* not far enough over yet */
1465
1466       if (pbox->x1 > rx)
1467         {
1468           partOut = TRUE;       /* missed part of rectangle to left */
1469           if (partIn)
1470             break;
1471         }
1472
1473       if (pbox->x1 < prect->x2)
1474         {
1475           partIn = TRUE;        /* definitely overlap */
1476           if (partOut)
1477             break;
1478         }
1479
1480       if (pbox->x2 >= prect->x2)
1481         {
1482           ry = pbox->y2;        /* finished with this band */
1483           if (ry >= prect->y2)
1484             break;
1485           rx = prect->x1;       /* reset x out to left again */
1486         }
1487       else
1488         {
1489           /*
1490            * Because boxes in a band are maximal width, if the first box
1491            * to overlap the rectangle doesn't completely cover it in that
1492            * band, the rectangle must be partially out, since some of it
1493            * will be uncovered in that band. partIn will have been set true
1494            * by now...
1495            */
1496           break;
1497         }
1498
1499     }
1500
1501   return (partIn ?
1502              ((ry < prect->y2) ?
1503               GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) : 
1504           GDK_OVERLAP_RECTANGLE_OUT);
1505 }