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