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