Region.c revision eb411b4b
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 = Xmalloc(sizeof( REGION ))))
143	return (Region) NULL;
144    if (! (temp->rects = Xmalloc(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()) )
388	return 0;
389    if (! (t = XCreateRegion()) ) {
390	XDestroyRegion(s);
391	return 0;
392    }
393    if ((grow = (dx < 0))) dx = -dx;
394    if (dx) Compress(r, s, t, (unsigned) 2*dx, TRUE, grow);
395    if ((grow = (dy < 0))) dy = -dy;
396    if (dy) Compress(r, s, t, (unsigned) 2*dy, FALSE, grow);
397    XOffsetRegion(r, dx, dy);
398    XDestroyRegion(s);
399    XDestroyRegion(t);
400    return 0;
401}
402
403
404/*======================================================================
405 *	    Region Intersection
406 *====================================================================*/
407/*-
408 *-----------------------------------------------------------------------
409 * miIntersectO --
410 *	Handle an overlapping band for miIntersect.
411 *
412 * Results:
413 *	None.
414 *
415 * Side Effects:
416 *	Rectangles may be added to the region.
417 *
418 *-----------------------------------------------------------------------
419 */
420/* static void*/
421static int
422miIntersectO (
423    register Region	pReg,
424    register BoxPtr	r1,
425    BoxPtr  	  	r1End,
426    register BoxPtr	r2,
427    BoxPtr  	  	r2End,
428    short    	  	y1,
429    short    	  	y2)
430{
431    register short  	x1;
432    register short  	x2;
433    register BoxPtr	pNextRect;
434
435    pNextRect = &pReg->rects[pReg->numRects];
436
437    while ((r1 != r1End) && (r2 != r2End))
438    {
439	x1 = max(r1->x1,r2->x1);
440	x2 = min(r1->x2,r2->x2);
441
442	/*
443	 * If there's any overlap between the two rectangles, add that
444	 * overlap to the new region.
445	 * There's no need to check for subsumption because the only way
446	 * such a need could arise is if some region has two rectangles
447	 * right next to each other. Since that should never happen...
448	 */
449	if (x1 < x2)
450	{
451	    assert(y1<y2);
452
453	    MEMCHECK(pReg, pNextRect, pReg->rects);
454	    pNextRect->x1 = x1;
455	    pNextRect->y1 = y1;
456	    pNextRect->x2 = x2;
457	    pNextRect->y2 = y2;
458	    pReg->numRects += 1;
459	    pNextRect++;
460	    assert(pReg->numRects <= pReg->size);
461	}
462
463	/*
464	 * Need to advance the pointers. Shift the one that extends
465	 * to the right the least, since the other still has a chance to
466	 * overlap with that region's next rectangle, if you see what I mean.
467	 */
468	if (r1->x2 < r2->x2)
469	{
470	    r1++;
471	}
472	else if (r2->x2 < r1->x2)
473	{
474	    r2++;
475	}
476	else
477	{
478	    r1++;
479	    r2++;
480	}
481    }
482    return 0;	/* lint */
483}
484
485int
486XIntersectRegion(
487    Region 	  	reg1,
488    Region	  	reg2,          /* source regions     */
489    register Region 	newReg)               /* destination Region */
490{
491   /* check for trivial reject */
492    if ( (!(reg1->numRects)) || (!(reg2->numRects))  ||
493	(!EXTENTCHECK(&reg1->extents, &reg2->extents)))
494        newReg->numRects = 0;
495    else
496	miRegionOp (newReg, reg1, reg2,
497    		miIntersectO, NULL, NULL);
498
499    /*
500     * Can't alter newReg's extents before we call miRegionOp because
501     * it might be one of the source regions and miRegionOp depends
502     * on the extents of those regions being the same. Besides, this
503     * way there's no checking against rectangles that will be nuked
504     * due to coalescing, so we have to examine fewer rectangles.
505     */
506    miSetExtents(newReg);
507    return 1;
508}
509
510static void
511miRegionCopy(
512    register Region dstrgn,
513    register Region rgn)
514
515{
516    if (dstrgn != rgn) /*  don't want to copy to itself */
517    {
518        if (dstrgn->size < rgn->numRects)
519        {
520            if (dstrgn->rects)
521            {
522		BOX *prevRects = dstrgn->rects;
523
524		dstrgn->rects = Xrealloc(dstrgn->rects,
525					 rgn->numRects * (sizeof(BOX)));
526		if (! dstrgn->rects) {
527		    Xfree(prevRects);
528		    return;
529		}
530            }
531            dstrgn->size = rgn->numRects;
532	}
533        dstrgn->numRects = rgn->numRects;
534        dstrgn->extents.x1 = rgn->extents.x1;
535        dstrgn->extents.y1 = rgn->extents.y1;
536        dstrgn->extents.x2 = rgn->extents.x2;
537        dstrgn->extents.y2 = rgn->extents.y2;
538
539	memcpy((char *) dstrgn->rects, (char *) rgn->rects,
540	       (int) (rgn->numRects * sizeof(BOX)));
541    }
542}
543
544/*======================================================================
545 *	    Generic Region Operator
546 *====================================================================*/
547
548/*-
549 *-----------------------------------------------------------------------
550 * miCoalesce --
551 *	Attempt to merge the boxes in the current band with those in the
552 *	previous one. Used only by miRegionOp.
553 *
554 * Results:
555 *	The new index for the previous band.
556 *
557 * Side Effects:
558 *	If coalescing takes place:
559 *	    - rectangles in the previous band will have their y2 fields
560 *	      altered.
561 *	    - pReg->numRects will be decreased.
562 *
563 *-----------------------------------------------------------------------
564 */
565/* static int*/
566static int
567miCoalesce(
568    register Region	pReg,	    	/* Region to coalesce */
569    int	    	  	prevStart,  	/* Index of start of previous band */
570    int	    	  	curStart)   	/* Index of start of current band */
571{
572    register BoxPtr	pPrevBox;   	/* Current box in previous band */
573    register BoxPtr	pCurBox;    	/* Current box in current band */
574    register BoxPtr	pRegEnd;    	/* End of region */
575    int	    	  	curNumRects;	/* Number of rectangles in current
576					 * band */
577    int	    	  	prevNumRects;	/* Number of rectangles in previous
578					 * band */
579    int	    	  	bandY1;	    	/* Y1 coordinate for current band */
580
581    pRegEnd = &pReg->rects[pReg->numRects];
582
583    pPrevBox = &pReg->rects[prevStart];
584    prevNumRects = curStart - prevStart;
585
586    /*
587     * Figure out how many rectangles are in the current band. Have to do
588     * this because multiple bands could have been added in miRegionOp
589     * at the end when one region has been exhausted.
590     */
591    pCurBox = &pReg->rects[curStart];
592    bandY1 = pCurBox->y1;
593    for (curNumRects = 0;
594	 (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
595	 curNumRects++)
596    {
597	pCurBox++;
598    }
599
600    if (pCurBox != pRegEnd)
601    {
602	/*
603	 * If more than one band was added, we have to find the start
604	 * of the last band added so the next coalescing job can start
605	 * at the right place... (given when multiple bands are added,
606	 * this may be pointless -- see above).
607	 */
608	pRegEnd--;
609	while (pRegEnd[-1].y1 == pRegEnd->y1)
610	{
611	    pRegEnd--;
612	}
613	curStart = pRegEnd - pReg->rects;
614	pRegEnd = pReg->rects + pReg->numRects;
615    }
616
617    if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
618	pCurBox -= curNumRects;
619	/*
620	 * The bands may only be coalesced if the bottom of the previous
621	 * matches the top scanline of the current.
622	 */
623	if (pPrevBox->y2 == pCurBox->y1)
624	{
625	    /*
626	     * Make sure the bands have boxes in the same places. This
627	     * assumes that boxes have been added in such a way that they
628	     * cover the most area possible. I.e. two boxes in a band must
629	     * have some horizontal space between them.
630	     */
631	    do
632	    {
633		if ((pPrevBox->x1 != pCurBox->x1) ||
634		    (pPrevBox->x2 != pCurBox->x2))
635		{
636		    /*
637		     * The bands don't line up so they can't be coalesced.
638		     */
639		    return (curStart);
640		}
641		pPrevBox++;
642		pCurBox++;
643		prevNumRects -= 1;
644	    } while (prevNumRects != 0);
645
646	    pReg->numRects -= curNumRects;
647	    pCurBox -= curNumRects;
648	    pPrevBox -= curNumRects;
649
650	    /*
651	     * The bands may be merged, so set the bottom y of each box
652	     * in the previous band to that of the corresponding box in
653	     * the current band.
654	     */
655	    do
656	    {
657		pPrevBox->y2 = pCurBox->y2;
658		pPrevBox++;
659		pCurBox++;
660		curNumRects -= 1;
661	    } while (curNumRects != 0);
662
663	    /*
664	     * If only one band was added to the region, we have to backup
665	     * curStart to the start of the previous band.
666	     *
667	     * If more than one band was added to the region, copy the
668	     * other bands down. The assumption here is that the other bands
669	     * came from the same region as the current one and no further
670	     * coalescing can be done on them since it's all been done
671	     * already... curStart is already in the right place.
672	     */
673	    if (pCurBox == pRegEnd)
674	    {
675		curStart = prevStart;
676	    }
677	    else
678	    {
679		do
680		{
681		    *pPrevBox++ = *pCurBox++;
682		} while (pCurBox != pRegEnd);
683	    }
684
685	}
686    }
687    return (curStart);
688}
689
690/*-
691 *-----------------------------------------------------------------------
692 * miRegionOp --
693 *	Apply an operation to two regions. Called by miUnion, miInverse,
694 *	miSubtract, miIntersect...
695 *
696 * Results:
697 *	None.
698 *
699 * Side Effects:
700 *	The new region is overwritten.
701 *
702 * Notes:
703 *	The idea behind this function is to view the two regions as sets.
704 *	Together they cover a rectangle of area that this function divides
705 *	into horizontal bands where points are covered only by one region
706 *	or by both. For the first case, the nonOverlapFunc is called with
707 *	each the band and the band's upper and lower extents. For the
708 *	second, the overlapFunc is called to process the entire band. It
709 *	is responsible for clipping the rectangles in the band, though
710 *	this function provides the boundaries.
711 *	At the end of each band, the new region is coalesced, if possible,
712 *	to reduce the number of rectangles in the region.
713 *
714 *-----------------------------------------------------------------------
715 */
716/* static void*/
717static void
718miRegionOp(
719    register Region 	newReg,	    	    	/* Place to store result */
720    Region	  	reg1,	    	    	/* First region in operation */
721    Region	  	reg2,	    	    	/* 2d region in operation */
722    int    	  	(*overlapFunc)(
723        register Region     pReg,
724        register BoxPtr     r1,
725        BoxPtr              r1End,
726        register BoxPtr     r2,
727        BoxPtr              r2End,
728        short               y1,
729        short               y2),                /* Function to call for over-
730						 * lapping bands */
731    int    	  	(*nonOverlap1Func)(
732        register Region     pReg,
733        register BoxPtr     r,
734        BoxPtr              rEnd,
735        register short      y1,
736        register short      y2),                /* Function to call for non-
737						 * overlapping bands in region
738						 * 1 */
739    int    	  	(*nonOverlap2Func)(
740        register Region     pReg,
741        register BoxPtr     r,
742        BoxPtr              rEnd,
743        register short      y1,
744        register short      y2))                /* Function to call for non-
745						 * overlapping bands in region
746						 * 2 */
747{
748    register BoxPtr	r1; 	    	    	/* Pointer into first region */
749    register BoxPtr	r2; 	    	    	/* Pointer into 2d region */
750    BoxPtr  	  	r1End;	    	    	/* End of 1st region */
751    BoxPtr  	  	r2End;	    	    	/* End of 2d region */
752    register short  	ybot;	    	    	/* Bottom of intersection */
753    register short  	ytop;	    	    	/* Top of intersection */
754    BoxPtr  	  	oldRects;   	    	/* Old rects for newReg */
755    int	    	  	prevBand;   	    	/* Index of start of
756						 * previous band in newReg */
757    int	    	  	curBand;    	    	/* Index of start of current
758						 * band in newReg */
759    register BoxPtr 	r1BandEnd;  	    	/* End of current band in r1 */
760    register BoxPtr 	r2BandEnd;  	    	/* End of current band in r2 */
761    short     	  	top;	    	    	/* Top of non-overlapping
762						 * band */
763    short     	  	bot;	    	    	/* Bottom of non-overlapping
764						 * band */
765
766    /*
767     * Initialization:
768     *	set r1, r2, r1End and r2End appropriately, preserve the important
769     * parts of the destination region until the end in case it's one of
770     * the two source regions, then mark the "new" region empty, allocating
771     * another array of rectangles for it to use.
772     */
773    r1 = reg1->rects;
774    r2 = reg2->rects;
775    r1End = r1 + reg1->numRects;
776    r2End = r2 + reg2->numRects;
777
778    oldRects = newReg->rects;
779
780    EMPTY_REGION(newReg);
781
782    /*
783     * Allocate a reasonable number of rectangles for the new region. The idea
784     * is to allocate enough so the individual functions don't need to
785     * reallocate and copy the array, which is time consuming, yet we don't
786     * have to worry about using too much memory. I hope to be able to
787     * nuke the Xrealloc() at the end of this function eventually.
788     */
789    newReg->size = max(reg1->numRects,reg2->numRects) * 2;
790
791    if (! (newReg->rects = Xmalloc (sizeof(BoxRec) * newReg->size))) {
792	newReg->size = 0;
793	return;
794    }
795
796    /*
797     * Initialize ybot and ytop.
798     * In the upcoming loop, ybot and ytop serve different functions depending
799     * on whether the band being handled is an overlapping or non-overlapping
800     * band.
801     * 	In the case of a non-overlapping band (only one of the regions
802     * has points in the band), ybot is the bottom of the most recent
803     * intersection and thus clips the top of the rectangles in that band.
804     * ytop is the top of the next intersection between the two regions and
805     * serves to clip the bottom of the rectangles in the current band.
806     *	For an overlapping band (where the two regions intersect), ytop clips
807     * the top of the rectangles of both regions and ybot clips the bottoms.
808     */
809    if (reg1->extents.y1 < reg2->extents.y1)
810	ybot = reg1->extents.y1;
811    else
812	ybot = reg2->extents.y1;
813
814    /*
815     * prevBand serves to mark the start of the previous band so rectangles
816     * can be coalesced into larger rectangles. qv. miCoalesce, above.
817     * In the beginning, there is no previous band, so prevBand == curBand
818     * (curBand is set later on, of course, but the first band will always
819     * start at index 0). prevBand and curBand must be indices because of
820     * the possible expansion, and resultant moving, of the new region's
821     * array of rectangles.
822     */
823    prevBand = 0;
824
825    do
826    {
827	curBand = newReg->numRects;
828
829	/*
830	 * This algorithm proceeds one source-band (as opposed to a
831	 * destination band, which is determined by where the two regions
832	 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
833	 * rectangle after the last one in the current band for their
834	 * respective regions.
835	 */
836	r1BandEnd = r1;
837	while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
838	{
839	    r1BandEnd++;
840	}
841
842	r2BandEnd = r2;
843	while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
844	{
845	    r2BandEnd++;
846	}
847
848	/*
849	 * First handle the band that doesn't intersect, if any.
850	 *
851	 * Note that attention is restricted to one band in the
852	 * non-intersecting region at once, so if a region has n
853	 * bands between the current position and the next place it overlaps
854	 * the other, this entire loop will be passed through n times.
855	 */
856	if (r1->y1 < r2->y1)
857	{
858	    top = max(r1->y1,ybot);
859	    bot = min(r1->y2,r2->y1);
860
861	    if ((top != bot) && (nonOverlap1Func != NULL))
862	    {
863		(* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
864	    }
865
866	    ytop = r2->y1;
867	}
868	else if (r2->y1 < r1->y1)
869	{
870	    top = max(r2->y1,ybot);
871	    bot = min(r2->y2,r1->y1);
872
873	    if ((top != bot) && (nonOverlap2Func != NULL))
874	    {
875		(* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
876	    }
877
878	    ytop = r1->y1;
879	}
880	else
881	{
882	    ytop = r1->y1;
883	}
884
885	/*
886	 * If any rectangles got added to the region, try and coalesce them
887	 * with rectangles from the previous band. Note we could just do
888	 * this test in miCoalesce, but some machines incur a not
889	 * inconsiderable cost for function calls, so...
890	 */
891	if (newReg->numRects != curBand)
892	{
893	    prevBand = miCoalesce (newReg, prevBand, curBand);
894	}
895
896	/*
897	 * Now see if we've hit an intersecting band. The two bands only
898	 * intersect if ybot > ytop
899	 */
900	ybot = min(r1->y2, r2->y2);
901	curBand = newReg->numRects;
902	if (ybot > ytop)
903	{
904	    (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
905
906	}
907
908	if (newReg->numRects != curBand)
909	{
910	    prevBand = miCoalesce (newReg, prevBand, curBand);
911	}
912
913	/*
914	 * If we've finished with a band (y2 == ybot) we skip forward
915	 * in the region to the next band.
916	 */
917	if (r1->y2 == ybot)
918	{
919	    r1 = r1BandEnd;
920	}
921	if (r2->y2 == ybot)
922	{
923	    r2 = r2BandEnd;
924	}
925    } while ((r1 != r1End) && (r2 != r2End));
926
927    /*
928     * Deal with whichever region still has rectangles left.
929     */
930    curBand = newReg->numRects;
931    if (r1 != r1End)
932    {
933	if (nonOverlap1Func != NULL)
934	{
935	    do
936	    {
937		r1BandEnd = r1;
938		while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
939		{
940		    r1BandEnd++;
941		}
942		(* nonOverlap1Func) (newReg, r1, r1BandEnd,
943				     max(r1->y1,ybot), r1->y2);
944		r1 = r1BandEnd;
945	    } while (r1 != r1End);
946	}
947    }
948    else if ((r2 != r2End) && (nonOverlap2Func != NULL))
949    {
950	do
951	{
952	    r2BandEnd = r2;
953	    while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
954	    {
955		 r2BandEnd++;
956	    }
957	    (* nonOverlap2Func) (newReg, r2, r2BandEnd,
958				max(r2->y1,ybot), r2->y2);
959	    r2 = r2BandEnd;
960	} while (r2 != r2End);
961    }
962
963    if (newReg->numRects != curBand)
964    {
965	(void) miCoalesce (newReg, prevBand, curBand);
966    }
967
968    /*
969     * A bit of cleanup. To keep regions from growing without bound,
970     * we shrink the array of rectangles to match the new number of
971     * rectangles in the region. This never goes to 0, however...
972     *
973     * Only do this stuff if the number of rectangles allocated is more than
974     * twice the number of rectangles in the region (a simple optimization...).
975     */
976    if (newReg->numRects < (newReg->size >> 1))
977    {
978	if (REGION_NOT_EMPTY(newReg))
979	{
980	    BoxPtr prev_rects = newReg->rects;
981	    newReg->size = newReg->numRects;
982	    newReg->rects = Xrealloc (newReg->rects,
983				      sizeof(BoxRec) * newReg->size);
984	    if (! newReg->rects)
985		newReg->rects = prev_rects;
986	}
987	else
988	{
989	    /*
990	     * No point in doing the extra work involved in an Xrealloc if
991	     * the region is empty
992	     */
993	    newReg->size = 1;
994	    Xfree((char *) newReg->rects);
995	    newReg->rects = Xmalloc(sizeof(BoxRec));
996	}
997    }
998    Xfree ((char *) oldRects);
999    return;
1000}
1001
1002
1003/*======================================================================
1004 *	    Region Union
1005 *====================================================================*/
1006
1007/*-
1008 *-----------------------------------------------------------------------
1009 * miUnionNonO --
1010 *	Handle a non-overlapping band for the union operation. Just
1011 *	Adds the rectangles into the region. Doesn't have to check for
1012 *	subsumption or anything.
1013 *
1014 * Results:
1015 *	None.
1016 *
1017 * Side Effects:
1018 *	pReg->numRects is incremented and the final rectangles overwritten
1019 *	with the rectangles we're passed.
1020 *
1021 *-----------------------------------------------------------------------
1022 */
1023/* static void*/
1024static int
1025miUnionNonO (
1026    register Region	pReg,
1027    register BoxPtr	r,
1028    BoxPtr  	  	rEnd,
1029    register short  	y1,
1030    register short  	y2)
1031{
1032    register BoxPtr	pNextRect;
1033
1034    pNextRect = &pReg->rects[pReg->numRects];
1035
1036    assert(y1 < y2);
1037
1038    while (r != rEnd)
1039    {
1040	assert(r->x1 < r->x2);
1041	MEMCHECK(pReg, pNextRect, pReg->rects);
1042	pNextRect->x1 = r->x1;
1043	pNextRect->y1 = y1;
1044	pNextRect->x2 = r->x2;
1045	pNextRect->y2 = y2;
1046	pReg->numRects += 1;
1047	pNextRect++;
1048
1049	assert(pReg->numRects<=pReg->size);
1050	r++;
1051    }
1052    return 0;	/* lint */
1053}
1054
1055
1056/*-
1057 *-----------------------------------------------------------------------
1058 * miUnionO --
1059 *	Handle an overlapping band for the union operation. Picks the
1060 *	left-most rectangle each time and merges it into the region.
1061 *
1062 * Results:
1063 *	None.
1064 *
1065 * Side Effects:
1066 *	Rectangles are overwritten in pReg->rects and pReg->numRects will
1067 *	be changed.
1068 *
1069 *-----------------------------------------------------------------------
1070 */
1071
1072/* static void*/
1073static int
1074miUnionO (
1075    register Region	pReg,
1076    register BoxPtr	r1,
1077    BoxPtr  	  	r1End,
1078    register BoxPtr	r2,
1079    BoxPtr  	  	r2End,
1080    register short	y1,
1081    register short	y2)
1082{
1083    register BoxPtr	pNextRect;
1084
1085    pNextRect = &pReg->rects[pReg->numRects];
1086
1087#define MERGERECT(r) \
1088    if ((pReg->numRects != 0) &&  \
1089	(pNextRect[-1].y1 == y1) &&  \
1090	(pNextRect[-1].y2 == y2) &&  \
1091	(pNextRect[-1].x2 >= r->x1))  \
1092    {  \
1093	if (pNextRect[-1].x2 < r->x2)  \
1094	{  \
1095	    pNextRect[-1].x2 = r->x2;  \
1096	    assert(pNextRect[-1].x1<pNextRect[-1].x2); \
1097	}  \
1098    }  \
1099    else  \
1100    {  \
1101	MEMCHECK(pReg, pNextRect, pReg->rects);  \
1102	pNextRect->y1 = y1;  \
1103	pNextRect->y2 = y2;  \
1104	pNextRect->x1 = r->x1;  \
1105	pNextRect->x2 = r->x2;  \
1106	pReg->numRects += 1;  \
1107        pNextRect += 1;  \
1108    }  \
1109    assert(pReg->numRects<=pReg->size);\
1110    r++;
1111
1112    assert (y1<y2);
1113    while ((r1 != r1End) && (r2 != r2End))
1114    {
1115	if (r1->x1 < r2->x1)
1116	{
1117	    MERGERECT(r1);
1118	}
1119	else
1120	{
1121	    MERGERECT(r2);
1122	}
1123    }
1124
1125    if (r1 != r1End)
1126    {
1127	do
1128	{
1129	    MERGERECT(r1);
1130	} while (r1 != r1End);
1131    }
1132    else while (r2 != r2End)
1133    {
1134	MERGERECT(r2);
1135    }
1136    return 0;	/* lint */
1137}
1138
1139int
1140XUnionRegion(
1141    Region 	  reg1,
1142    Region	  reg2,             /* source regions     */
1143    Region 	  newReg)                  /* destination Region */
1144{
1145    /*  checks all the simple cases */
1146
1147    /*
1148     * Region 1 and 2 are the same or region 1 is empty
1149     */
1150    if ( (reg1 == reg2) || (!(reg1->numRects)) )
1151    {
1152        if (newReg != reg2)
1153            miRegionCopy(newReg, reg2);
1154        return 1;
1155    }
1156
1157    /*
1158     * if nothing to union (region 2 empty)
1159     */
1160    if (!(reg2->numRects))
1161    {
1162        if (newReg != reg1)
1163            miRegionCopy(newReg, reg1);
1164        return 1;
1165    }
1166
1167    /*
1168     * Region 1 completely subsumes region 2
1169     */
1170    if ((reg1->numRects == 1) &&
1171	(reg1->extents.x1 <= reg2->extents.x1) &&
1172	(reg1->extents.y1 <= reg2->extents.y1) &&
1173	(reg1->extents.x2 >= reg2->extents.x2) &&
1174	(reg1->extents.y2 >= reg2->extents.y2))
1175    {
1176        if (newReg != reg1)
1177            miRegionCopy(newReg, reg1);
1178        return 1;
1179    }
1180
1181    /*
1182     * Region 2 completely subsumes region 1
1183     */
1184    if ((reg2->numRects == 1) &&
1185	(reg2->extents.x1 <= reg1->extents.x1) &&
1186	(reg2->extents.y1 <= reg1->extents.y1) &&
1187	(reg2->extents.x2 >= reg1->extents.x2) &&
1188	(reg2->extents.y2 >= reg1->extents.y2))
1189    {
1190        if (newReg != reg2)
1191            miRegionCopy(newReg, reg2);
1192        return 1;
1193    }
1194
1195    miRegionOp (newReg, reg1, reg2, miUnionO,
1196    		miUnionNonO, miUnionNonO);
1197
1198    newReg->extents.x1 = min(reg1->extents.x1, reg2->extents.x1);
1199    newReg->extents.y1 = min(reg1->extents.y1, reg2->extents.y1);
1200    newReg->extents.x2 = max(reg1->extents.x2, reg2->extents.x2);
1201    newReg->extents.y2 = max(reg1->extents.y2, reg2->extents.y2);
1202
1203    return 1;
1204}
1205
1206
1207/*======================================================================
1208 * 	    	  Region Subtraction
1209 *====================================================================*/
1210
1211/*-
1212 *-----------------------------------------------------------------------
1213 * miSubtractNonO --
1214 *	Deal with non-overlapping band for subtraction. Any parts from
1215 *	region 2 we discard. Anything from region 1 we add to the region.
1216 *
1217 * Results:
1218 *	None.
1219 *
1220 * Side Effects:
1221 *	pReg may be affected.
1222 *
1223 *-----------------------------------------------------------------------
1224 */
1225/* static void*/
1226static int
1227miSubtractNonO1 (
1228    register Region	pReg,
1229    register BoxPtr	r,
1230    BoxPtr  	  	rEnd,
1231    register short  	y1,
1232    register short   	y2)
1233{
1234    register BoxPtr	pNextRect;
1235
1236    pNextRect = &pReg->rects[pReg->numRects];
1237
1238    assert(y1<y2);
1239
1240    while (r != rEnd)
1241    {
1242	assert(r->x1<r->x2);
1243	MEMCHECK(pReg, pNextRect, pReg->rects);
1244	pNextRect->x1 = r->x1;
1245	pNextRect->y1 = y1;
1246	pNextRect->x2 = r->x2;
1247	pNextRect->y2 = y2;
1248	pReg->numRects += 1;
1249	pNextRect++;
1250
1251	assert(pReg->numRects <= pReg->size);
1252
1253	r++;
1254    }
1255    return 0;	/* lint */
1256}
1257
1258/*-
1259 *-----------------------------------------------------------------------
1260 * miSubtractO --
1261 *	Overlapping band subtraction. x1 is the left-most point not yet
1262 *	checked.
1263 *
1264 * Results:
1265 *	None.
1266 *
1267 * Side Effects:
1268 *	pReg may have rectangles added to it.
1269 *
1270 *-----------------------------------------------------------------------
1271 */
1272/* static void*/
1273static int
1274miSubtractO (
1275    register Region	pReg,
1276    register BoxPtr	r1,
1277    BoxPtr  	  	r1End,
1278    register BoxPtr	r2,
1279    BoxPtr  	  	r2End,
1280    register short  	y1,
1281    register short  	y2)
1282{
1283    register BoxPtr	pNextRect;
1284    register int  	x1;
1285
1286    x1 = r1->x1;
1287
1288    assert(y1<y2);
1289    pNextRect = &pReg->rects[pReg->numRects];
1290
1291    while ((r1 != r1End) && (r2 != r2End))
1292    {
1293	if (r2->x2 <= x1)
1294	{
1295	    /*
1296	     * Subtrahend missed the boat: go to next subtrahend.
1297	     */
1298	    r2++;
1299	}
1300	else if (r2->x1 <= x1)
1301	{
1302	    /*
1303	     * Subtrahend preceeds minuend: nuke left edge of minuend.
1304	     */
1305	    x1 = r2->x2;
1306	    if (x1 >= r1->x2)
1307	    {
1308		/*
1309		 * Minuend completely covered: advance to next minuend and
1310		 * reset left fence to edge of new minuend.
1311		 */
1312		r1++;
1313		if (r1 != r1End)
1314		    x1 = r1->x1;
1315	    }
1316	    else
1317	    {
1318		/*
1319		 * Subtrahend now used up since it doesn't extend beyond
1320		 * minuend
1321		 */
1322		r2++;
1323	    }
1324	}
1325	else if (r2->x1 < r1->x2)
1326	{
1327	    /*
1328	     * Left part of subtrahend covers part of minuend: add uncovered
1329	     * part of minuend to region and skip to next subtrahend.
1330	     */
1331	    assert(x1<r2->x1);
1332	    MEMCHECK(pReg, pNextRect, pReg->rects);
1333	    pNextRect->x1 = x1;
1334	    pNextRect->y1 = y1;
1335	    pNextRect->x2 = r2->x1;
1336	    pNextRect->y2 = y2;
1337	    pReg->numRects += 1;
1338	    pNextRect++;
1339
1340	    assert(pReg->numRects<=pReg->size);
1341
1342	    x1 = r2->x2;
1343	    if (x1 >= r1->x2)
1344	    {
1345		/*
1346		 * Minuend used up: advance to new...
1347		 */
1348		r1++;
1349		if (r1 != r1End)
1350		    x1 = r1->x1;
1351	    }
1352	    else
1353	    {
1354		/*
1355		 * Subtrahend used up
1356		 */
1357		r2++;
1358	    }
1359	}
1360	else
1361	{
1362	    /*
1363	     * Minuend used up: add any remaining piece before advancing.
1364	     */
1365	    if (r1->x2 > x1)
1366	    {
1367		MEMCHECK(pReg, pNextRect, pReg->rects);
1368		pNextRect->x1 = x1;
1369		pNextRect->y1 = y1;
1370		pNextRect->x2 = r1->x2;
1371		pNextRect->y2 = y2;
1372		pReg->numRects += 1;
1373		pNextRect++;
1374		assert(pReg->numRects<=pReg->size);
1375	    }
1376	    r1++;
1377	    if (r1 != r1End)
1378		x1 = r1->x1;
1379	}
1380    }
1381
1382    /*
1383     * Add remaining minuend rectangles to region.
1384     */
1385    while (r1 != r1End)
1386    {
1387	assert(x1<r1->x2);
1388	MEMCHECK(pReg, pNextRect, pReg->rects);
1389	pNextRect->x1 = x1;
1390	pNextRect->y1 = y1;
1391	pNextRect->x2 = r1->x2;
1392	pNextRect->y2 = y2;
1393	pReg->numRects += 1;
1394	pNextRect++;
1395
1396	assert(pReg->numRects<=pReg->size);
1397
1398	r1++;
1399	if (r1 != r1End)
1400	{
1401	    x1 = r1->x1;
1402	}
1403    }
1404    return 0;	/* lint */
1405}
1406
1407/*-
1408 *-----------------------------------------------------------------------
1409 * miSubtract --
1410 *	Subtract regS from regM and leave the result in regD.
1411 *	S stands for subtrahend, M for minuend and D for difference.
1412 *
1413 * Results:
1414 *	TRUE.
1415 *
1416 * Side Effects:
1417 *	regD is overwritten.
1418 *
1419 *-----------------------------------------------------------------------
1420 */
1421
1422int
1423XSubtractRegion(
1424    Region 	  	regM,
1425    Region	  	regS,
1426    register Region	regD)
1427{
1428   /* check for trivial reject */
1429    if ( (!(regM->numRects)) || (!(regS->numRects))  ||
1430	(!EXTENTCHECK(&regM->extents, &regS->extents)) )
1431    {
1432	miRegionCopy(regD, regM);
1433        return 1;
1434    }
1435
1436    miRegionOp (regD, regM, regS, miSubtractO,
1437    		miSubtractNonO1, NULL);
1438
1439    /*
1440     * Can't alter newReg's extents before we call miRegionOp because
1441     * it might be one of the source regions and miRegionOp depends
1442     * on the extents of those regions being the unaltered. Besides, this
1443     * way there's no checking against rectangles that will be nuked
1444     * due to coalescing, so we have to examine fewer rectangles.
1445     */
1446    miSetExtents (regD);
1447    return 1;
1448}
1449
1450int
1451XXorRegion(Region sra, Region srb, Region dr)
1452{
1453    Region tra, trb;
1454
1455    if (! (tra = XCreateRegion()) )
1456	return 0;
1457    if (! (trb = XCreateRegion()) ) {
1458	XDestroyRegion(tra);
1459	return 0;
1460    }
1461    (void) XSubtractRegion(sra,srb,tra);
1462    (void) XSubtractRegion(srb,sra,trb);
1463    (void) XUnionRegion(tra,trb,dr);
1464    XDestroyRegion(tra);
1465    XDestroyRegion(trb);
1466    return 0;
1467}
1468
1469/*
1470 * Check to see if the region is empty.  Assumes a region is passed
1471 * as a parameter
1472 */
1473int
1474XEmptyRegion(
1475    Region r)
1476{
1477    if( r->numRects == 0 ) return TRUE;
1478    else  return FALSE;
1479}
1480
1481/*
1482 *	Check to see if two regions are equal
1483 */
1484int
1485XEqualRegion(Region r1, Region r2)
1486{
1487    int i;
1488
1489    if( r1->numRects != r2->numRects ) return FALSE;
1490    else if( r1->numRects == 0 ) return TRUE;
1491    else if ( r1->extents.x1 != r2->extents.x1 ) return FALSE;
1492    else if ( r1->extents.x2 != r2->extents.x2 ) return FALSE;
1493    else if ( r1->extents.y1 != r2->extents.y1 ) return FALSE;
1494    else if ( r1->extents.y2 != r2->extents.y2 ) return FALSE;
1495    else for( i=0; i < r1->numRects; i++ ) {
1496    	if ( r1->rects[i].x1 != r2->rects[i].x1 ) return FALSE;
1497    	else if ( r1->rects[i].x2 != r2->rects[i].x2 ) return FALSE;
1498    	else if ( r1->rects[i].y1 != r2->rects[i].y1 ) return FALSE;
1499    	else if ( r1->rects[i].y2 != r2->rects[i].y2 ) return FALSE;
1500    }
1501    return TRUE;
1502}
1503
1504int
1505XPointInRegion(
1506    Region pRegion,
1507    int x, int y)
1508{
1509    int i;
1510
1511    if (pRegion->numRects == 0)
1512        return FALSE;
1513    if (!INBOX(pRegion->extents, x, y))
1514        return FALSE;
1515    for (i=0; i<pRegion->numRects; i++)
1516    {
1517        if (INBOX (pRegion->rects[i], x, y))
1518	    return TRUE;
1519    }
1520    return FALSE;
1521}
1522
1523int
1524XRectInRegion(
1525    register Region	region,
1526    int rx, int ry,
1527    unsigned int rwidth, unsigned int rheight)
1528{
1529    register BoxPtr pbox;
1530    register BoxPtr pboxEnd;
1531    Box rect;
1532    register BoxPtr prect = &rect;
1533    int      partIn, partOut;
1534
1535    prect->x1 = rx;
1536    prect->y1 = ry;
1537    prect->x2 = rwidth + rx;
1538    prect->y2 = rheight + ry;
1539
1540    /* this is (just) a useful optimization */
1541    if ((region->numRects == 0) || !EXTENTCHECK(&region->extents, prect))
1542        return(RectangleOut);
1543
1544    partOut = FALSE;
1545    partIn = FALSE;
1546
1547    /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
1548    for (pbox = region->rects, pboxEnd = pbox + region->numRects;
1549	 pbox < pboxEnd;
1550	 pbox++)
1551    {
1552
1553	if (pbox->y2 <= ry)
1554	   continue;	/* getting up to speed or skipping remainder of band */
1555
1556	if (pbox->y1 > ry)
1557	{
1558	   partOut = TRUE;	/* missed part of rectangle above */
1559	   if (partIn || (pbox->y1 >= prect->y2))
1560	      break;
1561	   ry = pbox->y1;	/* x guaranteed to be == prect->x1 */
1562	}
1563
1564	if (pbox->x2 <= rx)
1565	   continue;		/* not far enough over yet */
1566
1567	if (pbox->x1 > rx)
1568	{
1569	   partOut = TRUE;	/* missed part of rectangle to left */
1570	   if (partIn)
1571	      break;
1572	}
1573
1574	if (pbox->x1 < prect->x2)
1575	{
1576	    partIn = TRUE;	/* definitely overlap */
1577	    if (partOut)
1578	       break;
1579	}
1580
1581	if (pbox->x2 >= prect->x2)
1582	{
1583	   ry = pbox->y2;	/* finished with this band */
1584	   if (ry >= prect->y2)
1585	      break;
1586	   rx = prect->x1;	/* reset x out to left again */
1587	} else
1588	{
1589	    /*
1590	     * Because boxes in a band are maximal width, if the first box
1591	     * to overlap the rectangle doesn't completely cover it in that
1592	     * band, the rectangle must be partially out, since some of it
1593	     * will be uncovered in that band. partIn will have been set true
1594	     * by now...
1595	     */
1596	    break;
1597	}
1598
1599    }
1600
1601    return(partIn ? ((ry < prect->y2) ? RectanglePart : RectangleIn) :
1602		RectangleOut);
1603}
1604