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