region.c revision 706f2543
1/***********************************************************
2
3Copyright 1987, 1988, 1989, 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, 1989 by
27Digital Equipment Corporation, Maynard, Massachusetts.
28
29                        All Rights Reserved
30
31Permission to use, copy, modify, and distribute this software and its
32documentation for any purpose and without fee is hereby granted,
33provided that the above copyright notice appear in all copies and that
34both that copyright notice and this permission notice appear in
35supporting documentation, and that the name of Digital not be
36used in advertising or publicity pertaining to distribution of the
37software without specific, written prior permission.
38
39DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45SOFTWARE.
46
47******************************************************************/
48
49/* The panoramix components contained the following notice */
50/*****************************************************************
51
52Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts.
53
54Permission is hereby granted, free of charge, to any person obtaining a copy
55of this software and associated documentation files (the "Software"), to deal
56in the Software without restriction, including without limitation the rights
57to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58copies of the Software.
59
60The above copyright notice and this permission notice shall be included in
61all copies or substantial portions of the Software.
62
63THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
66DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
67BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
68WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
69IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
70
71Except as contained in this notice, the name of Digital Equipment Corporation
72shall not be used in advertising or otherwise to promote the sale, use or other
73dealings in this Software without prior written authorization from Digital
74Equipment Corporation.
75
76******************************************************************/
77
78#ifdef HAVE_DIX_CONFIG_H
79#include <dix-config.h>
80#endif
81
82#include "regionstr.h"
83#include <X11/Xprotostr.h>
84#include <X11/Xfuncproto.h>
85#include "gc.h"
86#include <pixman.h>
87
88#undef assert
89#ifdef REGION_DEBUG
90#define assert(expr) { \
91            CARD32 *foo = NULL; \
92            if (!(expr)) { \
93                ErrorF("Assertion failed file %s, line %d: %s\n", \
94                       __FILE__, __LINE__, #expr); \
95                *foo = 0xdeadbeef; /* to get a backtrace */ \
96            } \
97        }
98#else
99#define assert(expr)
100#endif
101
102#define good(reg) assert(RegionIsValid(reg))
103
104/*
105 * The functions in this file implement the Region abstraction used extensively
106 * throughout the X11 sample server. A Region is simply a set of disjoint
107 * (non-overlapping) rectangles, plus an "extent" rectangle which is the
108 * smallest single rectangle that contains all the non-overlapping rectangles.
109 *
110 * A Region is implemented as a "y-x-banded" array of rectangles.  This array
111 * imposes two degrees of order.  First, all rectangles are sorted by top side
112 * y coordinate first (y1), and then by left side x coordinate (x1).
113 *
114 * Furthermore, the rectangles are grouped into "bands".  Each rectangle in a
115 * band has the same top y coordinate (y1), and each has the same bottom y
116 * coordinate (y2).  Thus all rectangles in a band differ only in their left
117 * and right side (x1 and x2).  Bands are implicit in the array of rectangles:
118 * there is no separate list of band start pointers.
119 *
120 * The y-x band representation does not minimize rectangles.  In particular,
121 * if a rectangle vertically crosses a band (the rectangle has scanlines in
122 * the y1 to y2 area spanned by the band), then the rectangle may be broken
123 * down into two or more smaller rectangles stacked one atop the other.
124 *
125 *  -----------				    -----------
126 *  |         |				    |         |		    band 0
127 *  |         |  --------		    -----------  --------
128 *  |         |  |      |  in y-x banded    |         |  |      |   band 1
129 *  |         |  |      |  form is	    |         |  |      |
130 *  -----------  |      |		    -----------  --------
131 *               |      |				 |      |   band 2
132 *               --------				 --------
133 *
134 * An added constraint on the rectangles is that they must cover as much
135 * horizontal area as possible: no two rectangles within a band are allowed
136 * to touch.
137 *
138 * Whenever possible, bands will be merged together to cover a greater vertical
139 * distance (and thus reduce the number of rectangles). Two bands can be merged
140 * only if the bottom of one touches the top of the other and they have
141 * rectangles in the same places (of the same width, of course).
142 *
143 * Adam de Boor wrote most of the original region code.  Joel McCormack
144 * substantially modified or rewrote most of the core arithmetic routines,
145 * and added RegionValidate in order to support several speed improvements
146 * to miValidateTree.  Bob Scheifler changed the representation to be more
147 * compact when empty or a single rectangle, and did a bunch of gratuitous
148 * reformatting.
149 */
150
151/*  true iff two Boxes overlap */
152#define EXTENTCHECK(r1,r2) \
153      (!( ((r1)->x2 <= (r2)->x1)  || \
154          ((r1)->x1 >= (r2)->x2)  || \
155          ((r1)->y2 <= (r2)->y1)  || \
156          ((r1)->y1 >= (r2)->y2) ) )
157
158/* true iff (x,y) is in Box */
159#define INBOX(r,x,y) \
160      ( ((r)->x2 >  x) && \
161        ((r)->x1 <= x) && \
162        ((r)->y2 >  y) && \
163        ((r)->y1 <= y) )
164
165/* true iff Box r1 contains Box r2 */
166#define SUBSUMES(r1,r2) \
167      ( ((r1)->x1 <= (r2)->x1) && \
168        ((r1)->x2 >= (r2)->x2) && \
169        ((r1)->y1 <= (r2)->y1) && \
170        ((r1)->y2 >= (r2)->y2) )
171
172#define xfreeData(reg) if ((reg)->data && (reg)->data->size) free((reg)->data)
173
174#define RECTALLOC_BAIL(pReg,n,bail) \
175if (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \
176    if (!RegionRectAlloc(pReg, n)) { goto bail; }
177
178#define RECTALLOC(pReg,n) \
179if (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \
180    if (!RegionRectAlloc(pReg, n)) { return FALSE; }
181
182#define ADDRECT(pNextRect,nx1,ny1,nx2,ny2)	\
183{						\
184    pNextRect->x1 = nx1;			\
185    pNextRect->y1 = ny1;			\
186    pNextRect->x2 = nx2;			\
187    pNextRect->y2 = ny2;			\
188    pNextRect++;				\
189}
190
191#define NEWRECT(pReg,pNextRect,nx1,ny1,nx2,ny2)			\
192{									\
193    if (!(pReg)->data || ((pReg)->data->numRects == (pReg)->data->size))\
194    {									\
195	if (!RegionRectAlloc(pReg, 1))					\
196	    return FALSE;						\
197	pNextRect = RegionTop(pReg);					\
198    }									\
199    ADDRECT(pNextRect,nx1,ny1,nx2,ny2);					\
200    pReg->data->numRects++;						\
201    assert(pReg->data->numRects<=pReg->data->size);			\
202}
203
204
205#define DOWNSIZE(reg,numRects)						 \
206if (((numRects) < ((reg)->data->size >> 1)) && ((reg)->data->size > 50)) \
207{									 \
208    size_t NewSize = RegionSizeof(numRects);				 \
209    RegDataPtr NewData =						 \
210        (NewSize > 0) ? realloc((reg)->data, NewSize) : NULL ;		 \
211    if (NewData)							 \
212    {									 \
213	NewData->size = (numRects);					 \
214	(reg)->data = NewData;						 \
215    }									 \
216}
217
218
219BoxRec RegionEmptyBox = {0, 0, 0, 0};
220RegDataRec RegionEmptyData = {0, 0};
221
222RegDataRec  RegionBrokenData = {0, 0};
223static RegionRec   RegionBrokenRegion = { { 0, 0, 0, 0 }, &RegionBrokenData };
224
225void
226InitRegions (void)
227{
228    pixman_region_set_static_pointers (&RegionEmptyBox, &RegionEmptyData, &RegionBrokenData);
229}
230
231/*****************************************************************
232 *   RegionCreate(rect, size)
233 *     This routine does a simple malloc to make a structure of
234 *     REGION of "size" number of rectangles.
235 *****************************************************************/
236
237RegionPtr
238RegionCreate(BoxPtr rect, int size)
239{
240    RegionPtr pReg;
241
242    pReg = (RegionPtr)malloc(sizeof(RegionRec));
243    if (!pReg)
244	return &RegionBrokenRegion;
245
246    RegionInit (pReg, rect, size);
247
248    return pReg;
249}
250
251void
252RegionDestroy(RegionPtr pReg)
253{
254    pixman_region_fini (pReg);
255    if (pReg != &RegionBrokenRegion)
256	free(pReg);
257}
258
259void
260RegionPrint(RegionPtr rgn)
261{
262    int num, size;
263    int i;
264    BoxPtr rects;
265
266    num = RegionNumRects(rgn);
267    size = RegionSize(rgn);
268    rects = RegionRects(rgn);
269    ErrorF("[mi] num: %d size: %d\n", num, size);
270    ErrorF("[mi] extents: %d %d %d %d\n",
271	   rgn->extents.x1, rgn->extents.y1, rgn->extents.x2, rgn->extents.y2);
272    for (i = 0; i < num; i++)
273      ErrorF("[mi] %d %d %d %d \n",
274	     rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2);
275    ErrorF("[mi] \n");
276}
277
278#ifdef DEBUG
279Bool
280RegionIsValid(RegionPtr reg)
281{
282    int i, numRects;
283
284    if ((reg->extents.x1 > reg->extents.x2) ||
285	(reg->extents.y1 > reg->extents.y2))
286	return FALSE;
287    numRects = RegionNumRects(reg);
288    if (!numRects)
289	return ((reg->extents.x1 == reg->extents.x2) &&
290		(reg->extents.y1 == reg->extents.y2) &&
291		(reg->data->size || (reg->data == &RegionEmptyData)));
292    else if (numRects == 1)
293	return !reg->data;
294    else
295    {
296	BoxPtr pboxP, pboxN;
297	BoxRec box;
298
299	pboxP = RegionRects(reg);
300	box = *pboxP;
301	box.y2 = pboxP[numRects-1].y2;
302	pboxN = pboxP + 1;
303	for (i = numRects; --i > 0; pboxP++, pboxN++)
304	{
305	    if ((pboxN->x1 >= pboxN->x2) ||
306		(pboxN->y1 >= pboxN->y2))
307		return FALSE;
308	    if (pboxN->x1 < box.x1)
309		box.x1 = pboxN->x1;
310	    if (pboxN->x2 > box.x2)
311		box.x2 = pboxN->x2;
312	    if ((pboxN->y1 < pboxP->y1) ||
313		((pboxN->y1 == pboxP->y1) &&
314		 ((pboxN->x1 < pboxP->x2) || (pboxN->y2 != pboxP->y2))))
315		return FALSE;
316	}
317	return ((box.x1 == reg->extents.x1) &&
318		(box.x2 == reg->extents.x2) &&
319		(box.y1 == reg->extents.y1) &&
320		(box.y2 == reg->extents.y2));
321    }
322}
323#endif /* DEBUG */
324
325Bool
326RegionBreak (RegionPtr pReg)
327{
328    xfreeData (pReg);
329    pReg->extents = RegionEmptyBox;
330    pReg->data = &RegionBrokenData;
331    return FALSE;
332}
333
334Bool
335RegionRectAlloc(RegionPtr pRgn, int n)
336{
337    RegDataPtr	data;
338    size_t rgnSize;
339
340    if (!pRgn->data)
341    {
342	n++;
343	rgnSize = RegionSizeof(n);
344	pRgn->data = (rgnSize > 0) ? malloc(rgnSize) : NULL;
345	if (!pRgn->data)
346	    return RegionBreak (pRgn);
347	pRgn->data->numRects = 1;
348	*RegionBoxptr(pRgn) = pRgn->extents;
349    }
350    else if (!pRgn->data->size)
351    {
352	rgnSize = RegionSizeof(n);
353	pRgn->data = (rgnSize > 0) ? malloc(rgnSize) : NULL;
354	if (!pRgn->data)
355	    return RegionBreak (pRgn);
356	pRgn->data->numRects = 0;
357    }
358    else
359    {
360	if (n == 1)
361	{
362	    n = pRgn->data->numRects;
363	    if (n > 500) /* XXX pick numbers out of a hat */
364		n = 250;
365	}
366	n += pRgn->data->numRects;
367	rgnSize = RegionSizeof(n);
368	data = (rgnSize > 0) ? realloc(pRgn->data, rgnSize) : NULL;
369	if (!data)
370	    return RegionBreak (pRgn);
371	pRgn->data = data;
372    }
373    pRgn->data->size = n;
374    return TRUE;
375}
376
377/*======================================================================
378 *	    Generic Region Operator
379 *====================================================================*/
380
381/*-
382 *-----------------------------------------------------------------------
383 * RegionCoalesce --
384 *	Attempt to merge the boxes in the current band with those in the
385 *	previous one.  We are guaranteed that the current band extends to
386 *      the end of the rects array.  Used only by RegionOp.
387 *
388 * Results:
389 *	The new index for the previous band.
390 *
391 * Side Effects:
392 *	If coalescing takes place:
393 *	    - rectangles in the previous band will have their y2 fields
394 *	      altered.
395 *	    - pReg->data->numRects will be decreased.
396 *
397 *-----------------------------------------------------------------------
398 */
399_X_INLINE static int
400RegionCoalesce (
401    RegionPtr	pReg,	    	/* Region to coalesce		     */
402    int	    	  	prevStart,  	/* Index of start of previous band   */
403    int	    	  	curStart)   	/* Index of start of current band    */
404{
405    BoxPtr	pPrevBox;   	/* Current box in previous band	     */
406    BoxPtr	pCurBox;    	/* Current box in current band       */
407    int  	numRects;	/* Number rectangles in both bands   */
408    int		y2;		/* Bottom of current band	     */
409    /*
410     * Figure out how many rectangles are in the band.
411     */
412    numRects = curStart - prevStart;
413    assert(numRects == pReg->data->numRects - curStart);
414
415    if (!numRects) return curStart;
416
417    /*
418     * The bands may only be coalesced if the bottom of the previous
419     * matches the top scanline of the current.
420     */
421    pPrevBox = RegionBox(pReg, prevStart);
422    pCurBox = RegionBox(pReg, curStart);
423    if (pPrevBox->y2 != pCurBox->y1) return curStart;
424
425    /*
426     * Make sure the bands have boxes in the same places. This
427     * assumes that boxes have been added in such a way that they
428     * cover the most area possible. I.e. two boxes in a band must
429     * have some horizontal space between them.
430     */
431    y2 = pCurBox->y2;
432
433    do {
434	if ((pPrevBox->x1 != pCurBox->x1) || (pPrevBox->x2 != pCurBox->x2)) {
435	    return curStart;
436	}
437	pPrevBox++;
438	pCurBox++;
439	numRects--;
440    } while (numRects);
441
442    /*
443     * The bands may be merged, so set the bottom y of each box
444     * in the previous band to the bottom y of the current band.
445     */
446    numRects = curStart - prevStart;
447    pReg->data->numRects -= numRects;
448    do {
449	pPrevBox--;
450	pPrevBox->y2 = y2;
451	numRects--;
452    } while (numRects);
453    return prevStart;
454}
455
456
457/* Quicky macro to avoid trivial reject procedure calls to RegionCoalesce */
458
459#define Coalesce(newReg, prevBand, curBand)				\
460    if (curBand - prevBand == newReg->data->numRects - curBand) {	\
461	prevBand = RegionCoalesce(newReg, prevBand, curBand);		\
462    } else {								\
463	prevBand = curBand;						\
464    }
465
466/*-
467 *-----------------------------------------------------------------------
468 * RegionAppendNonO --
469 *	Handle a non-overlapping band for the union and subtract operations.
470 *      Just adds the (top/bottom-clipped) rectangles into the region.
471 *      Doesn't have to check for subsumption or anything.
472 *
473 * Results:
474 *	None.
475 *
476 * Side Effects:
477 *	pReg->data->numRects is incremented and the rectangles overwritten
478 *	with the rectangles we're passed.
479 *
480 *-----------------------------------------------------------------------
481 */
482
483_X_INLINE static Bool
484RegionAppendNonO (
485    RegionPtr	pReg,
486    BoxPtr	r,
487    BoxPtr  	rEnd,
488    int  	y1,
489    int  	y2)
490{
491    BoxPtr	pNextRect;
492    int		newRects;
493
494    newRects = rEnd - r;
495
496    assert(y1 < y2);
497    assert(newRects != 0);
498
499    /* Make sure we have enough space for all rectangles to be added */
500    RECTALLOC(pReg, newRects);
501    pNextRect = RegionTop(pReg);
502    pReg->data->numRects += newRects;
503    do {
504	assert(r->x1 < r->x2);
505	ADDRECT(pNextRect, r->x1, y1, r->x2, y2);
506	r++;
507    } while (r != rEnd);
508
509    return TRUE;
510}
511
512#define FindBand(r, rBandEnd, rEnd, ry1)		    \
513{							    \
514    ry1 = r->y1;					    \
515    rBandEnd = r+1;					    \
516    while ((rBandEnd != rEnd) && (rBandEnd->y1 == ry1)) {   \
517	rBandEnd++;					    \
518    }							    \
519}
520
521#define	AppendRegions(newReg, r, rEnd)					\
522{									\
523    int newRects;							\
524    if ((newRects = rEnd - r)) {					\
525	RECTALLOC(newReg, newRects);					\
526	memmove((char *)RegionTop(newReg),(char *)r, 			\
527              newRects * sizeof(BoxRec));				\
528	newReg->data->numRects += newRects;				\
529    }									\
530}
531
532/*-
533 *-----------------------------------------------------------------------
534 * RegionOp --
535 *	Apply an operation to two regions. Called by RegionUnion, RegionInverse,
536 *	RegionSubtract, RegionIntersect....  Both regions MUST have at least one
537 *      rectangle, and cannot be the same object.
538 *
539 * Results:
540 *	TRUE if successful.
541 *
542 * Side Effects:
543 *	The new region is overwritten.
544 *	pOverlap set to TRUE if overlapFunc ever returns TRUE.
545 *
546 * Notes:
547 *	The idea behind this function is to view the two regions as sets.
548 *	Together they cover a rectangle of area that this function divides
549 *	into horizontal bands where points are covered only by one region
550 *	or by both. For the first case, the nonOverlapFunc is called with
551 *	each the band and the band's upper and lower extents. For the
552 *	second, the overlapFunc is called to process the entire band. It
553 *	is responsible for clipping the rectangles in the band, though
554 *	this function provides the boundaries.
555 *	At the end of each band, the new region is coalesced, if possible,
556 *	to reduce the number of rectangles in the region.
557 *
558 *-----------------------------------------------------------------------
559 */
560
561typedef Bool (*OverlapProcPtr)(
562    RegionPtr	pReg,
563    BoxPtr	r1,
564    BoxPtr   	r1End,
565    BoxPtr	r2,
566    BoxPtr   	r2End,
567    short    	y1,
568    short    	y2,
569    Bool	*pOverlap);
570
571static Bool
572RegionOp(
573    RegionPtr       newReg,		    /* Place to store result	     */
574    RegionPtr       reg1,		    /* First region in operation     */
575    RegionPtr       reg2,		    /* 2d region in operation        */
576    OverlapProcPtr  overlapFunc,            /* Function to call for over-
577					     * lapping bands		     */
578    Bool	    appendNon1,		    /* Append non-overlapping bands  */
579					    /* in region 1 ? */
580    Bool	    appendNon2,		    /* Append non-overlapping bands  */
581					    /* in region 2 ? */
582    Bool	    *pOverlap)
583{
584    BoxPtr 	r1;		    /* Pointer into first region     */
585    BoxPtr 	r2;		    /* Pointer into 2d region	     */
586    BoxPtr	r1End;		    /* End of 1st region	     */
587    BoxPtr	r2End;		    /* End of 2d region		     */
588    short	ybot;		    /* Bottom of intersection	     */
589    short	ytop;		    /* Top of intersection	     */
590    RegDataPtr	oldData;	    /* Old data for newReg	     */
591    int		prevBand;	    /* Index of start of
592				     * previous band in newReg       */
593    int		curBand;	    /* Index of start of current
594				     * band in newReg		     */
595    BoxPtr  	r1BandEnd;	    /* End of current band in r1     */
596    BoxPtr  	r2BandEnd;	    /* End of current band in r2     */
597    short   	top;		    /* Top of non-overlapping band   */
598    short   	bot;	    	    /* Bottom of non-overlapping band*/
599    int     	r1y1;	    	    /* Temps for r1->y1 and r2->y1   */
600    int     	r2y1;
601    int	    	newSize;
602    int	    	numRects;
603
604    /*
605     * Break any region computed from a broken region
606     */
607    if (RegionNar (reg1) || RegionNar(reg2))
608	return RegionBreak (newReg);
609
610    /*
611     * Initialization:
612     *	set r1, r2, r1End and r2End appropriately, save the rectangles
613     * of the destination region until the end in case it's one of
614     * the two source regions, then mark the "new" region empty, allocating
615     * another array of rectangles for it to use.
616     */
617
618    r1 = RegionRects(reg1);
619    newSize = RegionNumRects(reg1);
620    r1End = r1 + newSize;
621    numRects = RegionNumRects(reg2);
622    r2 = RegionRects(reg2);
623    r2End = r2 + numRects;
624    assert(r1 != r1End);
625    assert(r2 != r2End);
626
627    oldData = NULL;
628    if (((newReg == reg1) && (newSize > 1)) ||
629	((newReg == reg2) && (numRects > 1)))
630    {
631	oldData = newReg->data;
632	newReg->data = &RegionEmptyData;
633    }
634    /* guess at new size */
635    if (numRects > newSize)
636	newSize = numRects;
637    newSize <<= 1;
638    if (!newReg->data)
639	newReg->data = &RegionEmptyData;
640    else if (newReg->data->size)
641	newReg->data->numRects = 0;
642    if (newSize > newReg->data->size)
643	if (!RegionRectAlloc(newReg, newSize))
644	    return FALSE;
645
646    /*
647     * Initialize ybot.
648     * In the upcoming loop, ybot and ytop serve different functions depending
649     * on whether the band being handled is an overlapping or non-overlapping
650     * band.
651     * 	In the case of a non-overlapping band (only one of the regions
652     * has points in the band), ybot is the bottom of the most recent
653     * intersection and thus clips the top of the rectangles in that band.
654     * ytop is the top of the next intersection between the two regions and
655     * serves to clip the bottom of the rectangles in the current band.
656     *	For an overlapping band (where the two regions intersect), ytop clips
657     * the top of the rectangles of both regions and ybot clips the bottoms.
658     */
659
660    ybot = min(r1->y1, r2->y1);
661
662    /*
663     * prevBand serves to mark the start of the previous band so rectangles
664     * can be coalesced into larger rectangles. qv. RegionCoalesce, above.
665     * In the beginning, there is no previous band, so prevBand == curBand
666     * (curBand is set later on, of course, but the first band will always
667     * start at index 0). prevBand and curBand must be indices because of
668     * the possible expansion, and resultant moving, of the new region's
669     * array of rectangles.
670     */
671    prevBand = 0;
672
673    do {
674	/*
675	 * This algorithm proceeds one source-band (as opposed to a
676	 * destination band, which is determined by where the two regions
677	 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
678	 * rectangle after the last one in the current band for their
679	 * respective regions.
680	 */
681	assert(r1 != r1End);
682	assert(r2 != r2End);
683
684	FindBand(r1, r1BandEnd, r1End, r1y1);
685	FindBand(r2, r2BandEnd, r2End, r2y1);
686
687	/*
688	 * First handle the band that doesn't intersect, if any.
689	 *
690	 * Note that attention is restricted to one band in the
691	 * non-intersecting region at once, so if a region has n
692	 * bands between the current position and the next place it overlaps
693	 * the other, this entire loop will be passed through n times.
694	 */
695	if (r1y1 < r2y1) {
696	    if (appendNon1) {
697		top = max(r1y1, ybot);
698		bot = min(r1->y2, r2y1);
699		if (top != bot)	{
700		    curBand = newReg->data->numRects;
701		    RegionAppendNonO(newReg, r1, r1BandEnd, top, bot);
702		    Coalesce(newReg, prevBand, curBand);
703		}
704	    }
705	    ytop = r2y1;
706	} else if (r2y1 < r1y1) {
707	    if (appendNon2) {
708		top = max(r2y1, ybot);
709		bot = min(r2->y2, r1y1);
710		if (top != bot) {
711		    curBand = newReg->data->numRects;
712		    RegionAppendNonO(newReg, r2, r2BandEnd, top, bot);
713		    Coalesce(newReg, prevBand, curBand);
714		}
715	    }
716	    ytop = r1y1;
717	} else {
718	    ytop = r1y1;
719	}
720
721	/*
722	 * Now see if we've hit an intersecting band. The two bands only
723	 * intersect if ybot > ytop
724	 */
725	ybot = min(r1->y2, r2->y2);
726	if (ybot > ytop) {
727	    curBand = newReg->data->numRects;
728	    (* overlapFunc)(newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot,
729			    pOverlap);
730	    Coalesce(newReg, prevBand, curBand);
731	}
732
733	/*
734	 * If we've finished with a band (y2 == ybot) we skip forward
735	 * in the region to the next band.
736	 */
737	if (r1->y2 == ybot) r1 = r1BandEnd;
738	if (r2->y2 == ybot) r2 = r2BandEnd;
739
740    } while (r1 != r1End && r2 != r2End);
741
742    /*
743     * Deal with whichever region (if any) still has rectangles left.
744     *
745     * We only need to worry about banding and coalescing for the very first
746     * band left.  After that, we can just group all remaining boxes,
747     * regardless of how many bands, into one final append to the list.
748     */
749
750    if ((r1 != r1End) && appendNon1) {
751	/* Do first nonOverlap1Func call, which may be able to coalesce */
752	FindBand(r1, r1BandEnd, r1End, r1y1);
753	curBand = newReg->data->numRects;
754	RegionAppendNonO(newReg, r1, r1BandEnd, max(r1y1, ybot), r1->y2);
755	Coalesce(newReg, prevBand, curBand);
756	/* Just append the rest of the boxes  */
757	AppendRegions(newReg, r1BandEnd, r1End);
758
759    } else if ((r2 != r2End) && appendNon2) {
760	/* Do first nonOverlap2Func call, which may be able to coalesce */
761	FindBand(r2, r2BandEnd, r2End, r2y1);
762	curBand = newReg->data->numRects;
763	RegionAppendNonO(newReg, r2, r2BandEnd, max(r2y1, ybot), r2->y2);
764	Coalesce(newReg, prevBand, curBand);
765	/* Append rest of boxes */
766	AppendRegions(newReg, r2BandEnd, r2End);
767    }
768
769    free(oldData);
770
771    if (!(numRects = newReg->data->numRects))
772    {
773	xfreeData(newReg);
774	newReg->data = &RegionEmptyData;
775    }
776    else if (numRects == 1)
777    {
778	newReg->extents = *RegionBoxptr(newReg);
779	xfreeData(newReg);
780	newReg->data = NULL;
781    }
782    else
783    {
784	DOWNSIZE(newReg, numRects);
785    }
786
787    return TRUE;
788}
789
790/*-
791 *-----------------------------------------------------------------------
792 * RegionSetExtents --
793 *	Reset the extents of a region to what they should be. Called by
794 *	Subtract and Intersect as they can't figure it out along the
795 *	way or do so easily, as Union can.
796 *
797 * Results:
798 *	None.
799 *
800 * Side Effects:
801 *	The region's 'extents' structure is overwritten.
802 *
803 *-----------------------------------------------------------------------
804 */
805static void
806RegionSetExtents (RegionPtr pReg)
807{
808    BoxPtr pBox, pBoxEnd;
809
810    if (!pReg->data)
811	return;
812    if (!pReg->data->size)
813    {
814	pReg->extents.x2 = pReg->extents.x1;
815	pReg->extents.y2 = pReg->extents.y1;
816	return;
817    }
818
819    pBox = RegionBoxptr(pReg);
820    pBoxEnd = RegionEnd(pReg);
821
822    /*
823     * Since pBox is the first rectangle in the region, it must have the
824     * smallest y1 and since pBoxEnd is the last rectangle in the region,
825     * it must have the largest y2, because of banding. Initialize x1 and
826     * x2 from  pBox and pBoxEnd, resp., as good things to initialize them
827     * to...
828     */
829    pReg->extents.x1 = pBox->x1;
830    pReg->extents.y1 = pBox->y1;
831    pReg->extents.x2 = pBoxEnd->x2;
832    pReg->extents.y2 = pBoxEnd->y2;
833
834    assert(pReg->extents.y1 < pReg->extents.y2);
835    while (pBox <= pBoxEnd) {
836	if (pBox->x1 < pReg->extents.x1)
837	    pReg->extents.x1 = pBox->x1;
838	if (pBox->x2 > pReg->extents.x2)
839	    pReg->extents.x2 = pBox->x2;
840	pBox++;
841    };
842
843    assert(pReg->extents.x1 < pReg->extents.x2);
844}
845
846/*======================================================================
847 *	    Region Intersection
848 *====================================================================*/
849/*-
850 *-----------------------------------------------------------------------
851 * RegionIntersectO --
852 *	Handle an overlapping band for RegionIntersect.
853 *
854 * Results:
855 *	TRUE if successful.
856 *
857 * Side Effects:
858 *	Rectangles may be added to the region.
859 *
860 *-----------------------------------------------------------------------
861 */
862/*ARGSUSED*/
863
864#define MERGERECT(r)						\
865{								\
866    if (r->x1 <= x2) {						\
867	/* Merge with current rectangle */			\
868	if (r->x1 < x2) *pOverlap = TRUE;				\
869	if (x2 < r->x2) x2 = r->x2;				\
870    } else {							\
871	/* Add current rectangle, start new one */		\
872	NEWRECT(pReg, pNextRect, x1, y1, x2, y2);		\
873	x1 = r->x1;						\
874	x2 = r->x2;						\
875    }								\
876    r++;							\
877}
878
879/*======================================================================
880 *	    Region Union
881 *====================================================================*/
882
883/*-
884 *-----------------------------------------------------------------------
885 * RegionUnionO --
886 *	Handle an overlapping band for the union operation. Picks the
887 *	left-most rectangle each time and merges it into the region.
888 *
889 * Results:
890 *	TRUE if successful.
891 *
892 * Side Effects:
893 *	pReg is overwritten.
894 *	pOverlap is set to TRUE if any boxes overlap.
895 *
896 *-----------------------------------------------------------------------
897 */
898static Bool
899RegionUnionO (
900    RegionPtr	pReg,
901    BoxPtr	r1,
902    BoxPtr  	r1End,
903    BoxPtr	r2,
904    BoxPtr  	r2End,
905    short	y1,
906    short	y2,
907    Bool	*pOverlap)
908{
909    BoxPtr     pNextRect;
910    int        x1;     /* left and right side of current union */
911    int        x2;
912
913    assert (y1 < y2);
914    assert(r1 != r1End && r2 != r2End);
915
916    pNextRect = RegionTop(pReg);
917
918    /* Start off current rectangle */
919    if (r1->x1 < r2->x1)
920    {
921	x1 = r1->x1;
922	x2 = r1->x2;
923	r1++;
924    }
925    else
926    {
927	x1 = r2->x1;
928	x2 = r2->x2;
929	r2++;
930    }
931    while (r1 != r1End && r2 != r2End)
932    {
933	if (r1->x1 < r2->x1) MERGERECT(r1) else MERGERECT(r2);
934    }
935
936    /* Finish off whoever (if any) is left */
937    if (r1 != r1End)
938    {
939	do
940	{
941	    MERGERECT(r1);
942	} while (r1 != r1End);
943    }
944    else if (r2 != r2End)
945    {
946	do
947	{
948	    MERGERECT(r2);
949	} while (r2 != r2End);
950    }
951
952    /* Add current rectangle */
953    NEWRECT(pReg, pNextRect, x1, y1, x2, y2);
954
955    return TRUE;
956}
957
958/*======================================================================
959 *	    Batch Rectangle Union
960 *====================================================================*/
961
962/*-
963 *-----------------------------------------------------------------------
964 * RegionAppend --
965 *
966 *      "Append" the rgn rectangles onto the end of dstrgn, maintaining
967 *      knowledge of YX-banding when it's easy.  Otherwise, dstrgn just
968 *      becomes a non-y-x-banded random collection of rectangles, and not
969 *      yet a true region.  After a sequence of appends, the caller must
970 *      call RegionValidate to ensure that a valid region is constructed.
971 *
972 * Results:
973 *	TRUE if successful.
974 *
975 * Side Effects:
976 *      dstrgn is modified if rgn has rectangles.
977 *
978 */
979Bool
980RegionAppend(RegionPtr dstrgn, RegionPtr rgn)
981{
982    int numRects, dnumRects, size;
983    BoxPtr new, old;
984    Bool prepend;
985
986    if (RegionNar(rgn))
987	return RegionBreak (dstrgn);
988
989    if (!rgn->data && (dstrgn->data == &RegionEmptyData))
990    {
991	dstrgn->extents = rgn->extents;
992	dstrgn->data = NULL;
993	return TRUE;
994    }
995
996    numRects = RegionNumRects(rgn);
997    if (!numRects)
998	return TRUE;
999    prepend = FALSE;
1000    size = numRects;
1001    dnumRects = RegionNumRects(dstrgn);
1002    if (!dnumRects && (size < 200))
1003	size = 200; /* XXX pick numbers out of a hat */
1004    RECTALLOC(dstrgn, size);
1005    old = RegionRects(rgn);
1006    if (!dnumRects)
1007	dstrgn->extents = rgn->extents;
1008    else if (dstrgn->extents.x2 > dstrgn->extents.x1)
1009    {
1010	BoxPtr first, last;
1011
1012	first = old;
1013	last = RegionBoxptr(dstrgn) + (dnumRects - 1);
1014	if ((first->y1 > last->y2) ||
1015	    ((first->y1 == last->y1) && (first->y2 == last->y2) &&
1016	     (first->x1 > last->x2)))
1017	{
1018	    if (rgn->extents.x1 < dstrgn->extents.x1)
1019		dstrgn->extents.x1 = rgn->extents.x1;
1020	    if (rgn->extents.x2 > dstrgn->extents.x2)
1021		dstrgn->extents.x2 = rgn->extents.x2;
1022	    dstrgn->extents.y2 = rgn->extents.y2;
1023	}
1024	else
1025	{
1026	    first = RegionBoxptr(dstrgn);
1027	    last = old + (numRects - 1);
1028	    if ((first->y1 > last->y2) ||
1029		((first->y1 == last->y1) && (first->y2 == last->y2) &&
1030		 (first->x1 > last->x2)))
1031	    {
1032		prepend = TRUE;
1033		if (rgn->extents.x1 < dstrgn->extents.x1)
1034		    dstrgn->extents.x1 = rgn->extents.x1;
1035		if (rgn->extents.x2 > dstrgn->extents.x2)
1036		    dstrgn->extents.x2 = rgn->extents.x2;
1037		dstrgn->extents.y1 = rgn->extents.y1;
1038	    }
1039	    else
1040		dstrgn->extents.x2 = dstrgn->extents.x1;
1041	}
1042    }
1043    if (prepend)
1044    {
1045	new = RegionBox(dstrgn, numRects);
1046	if (dnumRects == 1)
1047	    *new = *RegionBoxptr(dstrgn);
1048	else
1049	    memmove((char *)new,(char *)RegionBoxptr(dstrgn),
1050		  dnumRects * sizeof(BoxRec));
1051	new = RegionBoxptr(dstrgn);
1052    }
1053    else
1054	new = RegionBoxptr(dstrgn) + dnumRects;
1055    if (numRects == 1)
1056	*new = *old;
1057    else
1058	memmove((char *)new, (char *)old, numRects * sizeof(BoxRec));
1059    dstrgn->data->numRects += numRects;
1060    return TRUE;
1061}
1062
1063
1064#define ExchangeRects(a, b) \
1065{			    \
1066    BoxRec     t;	    \
1067    t = rects[a];	    \
1068    rects[a] = rects[b];    \
1069    rects[b] = t;	    \
1070}
1071
1072static void
1073QuickSortRects(
1074    BoxRec     rects[],
1075    int        numRects)
1076{
1077    int	y1;
1078    int	x1;
1079    int        i, j;
1080    BoxPtr     r;
1081
1082    /* Always called with numRects > 1 */
1083
1084    do
1085    {
1086	if (numRects == 2)
1087	{
1088	    if (rects[0].y1 > rects[1].y1 ||
1089		    (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1))
1090		ExchangeRects(0, 1);
1091	    return;
1092	}
1093
1094	/* Choose partition element, stick in location 0 */
1095        ExchangeRects(0, numRects >> 1);
1096	y1 = rects[0].y1;
1097	x1 = rects[0].x1;
1098
1099        /* Partition array */
1100        i = 0;
1101        j = numRects;
1102        do
1103	{
1104	    r = &(rects[i]);
1105	    do
1106	    {
1107		r++;
1108		i++;
1109            } while (i != numRects &&
1110		     (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1)));
1111	    r = &(rects[j]);
1112	    do
1113	    {
1114		r--;
1115		j--;
1116            } while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1));
1117            if (i < j)
1118		ExchangeRects(i, j);
1119        } while (i < j);
1120
1121        /* Move partition element back to middle */
1122        ExchangeRects(0, j);
1123
1124	/* Recurse */
1125        if (numRects-j-1 > 1)
1126	    QuickSortRects(&rects[j+1], numRects-j-1);
1127        numRects = j;
1128    } while (numRects > 1);
1129}
1130
1131/*-
1132 *-----------------------------------------------------------------------
1133 * RegionValidate --
1134 *
1135 *      Take a ``region'' which is a non-y-x-banded random collection of
1136 *      rectangles, and compute a nice region which is the union of all the
1137 *      rectangles.
1138 *
1139 * Results:
1140 *	TRUE if successful.
1141 *
1142 * Side Effects:
1143 *      The passed-in ``region'' may be modified.
1144 *	pOverlap set to TRUE if any retangles overlapped, else FALSE;
1145 *
1146 * Strategy:
1147 *      Step 1. Sort the rectangles into ascending order with primary key y1
1148 *		and secondary key x1.
1149 *
1150 *      Step 2. Split the rectangles into the minimum number of proper y-x
1151 *		banded regions.  This may require horizontally merging
1152 *		rectangles, and vertically coalescing bands.  With any luck,
1153 *		this step in an identity tranformation (ala the Box widget),
1154 *		or a coalescing into 1 box (ala Menus).
1155 *
1156 *	Step 3. Merge the separate regions down to a single region by calling
1157 *		Union.  Maximize the work each Union call does by using
1158 *		a binary merge.
1159 *
1160 *-----------------------------------------------------------------------
1161 */
1162
1163Bool
1164RegionValidate(RegionPtr badreg, Bool *pOverlap)
1165{
1166    /* Descriptor for regions under construction  in Step 2. */
1167    typedef struct {
1168	RegionRec   reg;
1169	int	    prevBand;
1170	int	    curBand;
1171    } RegionInfo;
1172
1173    int	numRects;   /* Original numRects for badreg	    */
1174    RegionInfo *ri;	    /* Array of current regions		    */
1175    int	numRI;      /* Number of entries used in ri	    */
1176    int	sizeRI;	    /* Number of entries available in ri    */
1177    int	i;	    /* Index into rects			    */
1178    int	j;	    /* Index into ri			    */
1179    RegionInfo *rit;       /* &ri[j]				    */
1180    RegionPtr  reg;        /* ri[j].reg			    */
1181    BoxPtr	box;	    /* Current box in rects		    */
1182    BoxPtr	riBox;      /* Last box in ri[j].reg		    */
1183    RegionPtr  hreg;       /* ri[j_half].reg			    */
1184    Bool		ret = TRUE;
1185
1186    *pOverlap = FALSE;
1187    if (!badreg->data)
1188    {
1189	good(badreg);
1190	return TRUE;
1191    }
1192    numRects = badreg->data->numRects;
1193    if (!numRects)
1194    {
1195	if (RegionNar(badreg))
1196	    return FALSE;
1197	good(badreg);
1198	return TRUE;
1199    }
1200    if (badreg->extents.x1 < badreg->extents.x2)
1201    {
1202	if ((numRects) == 1)
1203	{
1204	    xfreeData(badreg);
1205	    badreg->data = (RegDataPtr) NULL;
1206	}
1207	else
1208	{
1209	    DOWNSIZE(badreg, numRects);
1210	}
1211	good(badreg);
1212	return TRUE;
1213    }
1214
1215    /* Step 1: Sort the rects array into ascending (y1, x1) order */
1216    QuickSortRects(RegionBoxptr(badreg), numRects);
1217
1218    /* Step 2: Scatter the sorted array into the minimum number of regions */
1219
1220    /* Set up the first region to be the first rectangle in badreg */
1221    /* Note that step 2 code will never overflow the ri[0].reg rects array */
1222    ri = (RegionInfo *) malloc(4 * sizeof(RegionInfo));
1223    if (!ri)
1224	return RegionBreak (badreg);
1225    sizeRI = 4;
1226    numRI = 1;
1227    ri[0].prevBand = 0;
1228    ri[0].curBand = 0;
1229    ri[0].reg = *badreg;
1230    box = RegionBoxptr(&ri[0].reg);
1231    ri[0].reg.extents = *box;
1232    ri[0].reg.data->numRects = 1;
1233
1234    /* Now scatter rectangles into the minimum set of valid regions.  If the
1235       next rectangle to be added to a region would force an existing rectangle
1236       in the region to be split up in order to maintain y-x banding, just
1237       forget it.  Try the next region.  If it doesn't fit cleanly into any
1238       region, make a new one. */
1239
1240    for (i = numRects; --i > 0;)
1241    {
1242	box++;
1243	/* Look for a region to append box to */
1244	for (j = numRI, rit = ri; --j >= 0; rit++)
1245	{
1246	    reg = &rit->reg;
1247	    riBox = RegionEnd(reg);
1248
1249	    if (box->y1 == riBox->y1 && box->y2 == riBox->y2)
1250	    {
1251		/* box is in same band as riBox.  Merge or append it */
1252		if (box->x1 <= riBox->x2)
1253		{
1254		    /* Merge it with riBox */
1255		    if (box->x1 < riBox->x2) *pOverlap = TRUE;
1256		    if (box->x2 > riBox->x2) riBox->x2 = box->x2;
1257		}
1258		else
1259		{
1260		    RECTALLOC_BAIL(reg, 1, bail);
1261		    *RegionTop(reg) = *box;
1262		    reg->data->numRects++;
1263		}
1264		goto NextRect;   /* So sue me */
1265	    }
1266	    else if (box->y1 >= riBox->y2)
1267	    {
1268		/* Put box into new band */
1269		if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2;
1270		if (reg->extents.x1 > box->x1)   reg->extents.x1 = box->x1;
1271		Coalesce(reg, rit->prevBand, rit->curBand);
1272		rit->curBand = reg->data->numRects;
1273		RECTALLOC_BAIL(reg, 1, bail);
1274		*RegionTop(reg) = *box;
1275		reg->data->numRects++;
1276		goto NextRect;
1277	    }
1278	    /* Well, this region was inappropriate.  Try the next one. */
1279	} /* for j */
1280
1281	/* Uh-oh.  No regions were appropriate.  Create a new one. */
1282	if (sizeRI == numRI)
1283	{
1284	    /* Oops, allocate space for new region information */
1285	    sizeRI <<= 1;
1286	    rit = (RegionInfo *) realloc(ri, sizeRI * sizeof(RegionInfo));
1287	    if (!rit)
1288		goto bail;
1289	    ri = rit;
1290	    rit = &ri[numRI];
1291	}
1292	numRI++;
1293	rit->prevBand = 0;
1294	rit->curBand = 0;
1295	rit->reg.extents = *box;
1296	rit->reg.data = NULL;
1297	if (!RegionRectAlloc(&rit->reg, (i+numRI) / numRI)) /* MUST force allocation */
1298	    goto bail;
1299NextRect: ;
1300    } /* for i */
1301
1302    /* Make a final pass over each region in order to Coalesce and set
1303       extents.x2 and extents.y2 */
1304
1305    for (j = numRI, rit = ri; --j >= 0; rit++)
1306    {
1307	reg = &rit->reg;
1308	riBox = RegionEnd(reg);
1309	reg->extents.y2 = riBox->y2;
1310	if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2;
1311	Coalesce(reg, rit->prevBand, rit->curBand);
1312	if (reg->data->numRects == 1) /* keep unions happy below */
1313	{
1314	    xfreeData(reg);
1315	    reg->data = NULL;
1316	}
1317    }
1318
1319    /* Step 3: Union all regions into a single region */
1320    while (numRI > 1)
1321    {
1322	int half = numRI/2;
1323	for (j = numRI & 1; j < (half + (numRI & 1)); j++)
1324	{
1325	    reg = &ri[j].reg;
1326	    hreg = &ri[j+half].reg;
1327	    if (!RegionOp(reg, reg, hreg, RegionUnionO, TRUE, TRUE, pOverlap))
1328		ret = FALSE;
1329	    if (hreg->extents.x1 < reg->extents.x1)
1330		reg->extents.x1 = hreg->extents.x1;
1331	    if (hreg->extents.y1 < reg->extents.y1)
1332		reg->extents.y1 = hreg->extents.y1;
1333	    if (hreg->extents.x2 > reg->extents.x2)
1334		reg->extents.x2 = hreg->extents.x2;
1335	    if (hreg->extents.y2 > reg->extents.y2)
1336		reg->extents.y2 = hreg->extents.y2;
1337	    xfreeData(hreg);
1338	}
1339	numRI -= half;
1340    }
1341    *badreg = ri[0].reg;
1342    free(ri);
1343    good(badreg);
1344    return ret;
1345bail:
1346    for (i = 0; i < numRI; i++)
1347	xfreeData(&ri[i].reg);
1348    free(ri);
1349    return RegionBreak (badreg);
1350}
1351
1352RegionPtr
1353RegionFromRects(int nrects, xRectangle *prect, int ctype)
1354{
1355
1356    RegionPtr		pRgn;
1357    size_t		rgnSize;
1358    RegDataPtr		pData;
1359    BoxPtr		pBox;
1360    int        		i;
1361    int			x1, y1, x2, y2;
1362
1363    pRgn = RegionCreate(NullBox, 0);
1364    if (RegionNar (pRgn))
1365	return pRgn;
1366    if (!nrects)
1367	return pRgn;
1368    if (nrects == 1)
1369    {
1370	x1 = prect->x;
1371	y1 = prect->y;
1372	if ((x2 = x1 + (int) prect->width) > MAXSHORT)
1373	    x2 = MAXSHORT;
1374	if ((y2 = y1 + (int) prect->height) > MAXSHORT)
1375	    y2 = MAXSHORT;
1376	if (x1 != x2 && y1 != y2)
1377	{
1378	    pRgn->extents.x1 = x1;
1379	    pRgn->extents.y1 = y1;
1380	    pRgn->extents.x2 = x2;
1381	    pRgn->extents.y2 = y2;
1382	    pRgn->data = NULL;
1383	}
1384	return pRgn;
1385    }
1386    rgnSize = RegionSizeof(nrects);
1387    pData = (rgnSize > 0) ? malloc(rgnSize) : NULL;
1388    if (!pData)
1389    {
1390	RegionBreak (pRgn);
1391	return pRgn;
1392    }
1393    pBox = (BoxPtr) (pData + 1);
1394    for (i = nrects; --i >= 0; prect++)
1395    {
1396	x1 = prect->x;
1397	y1 = prect->y;
1398	if ((x2 = x1 + (int) prect->width) > MAXSHORT)
1399	    x2 = MAXSHORT;
1400	if ((y2 = y1 + (int) prect->height) > MAXSHORT)
1401	    y2 = MAXSHORT;
1402	if (x1 != x2 && y1 != y2)
1403	{
1404	    pBox->x1 = x1;
1405	    pBox->y1 = y1;
1406	    pBox->x2 = x2;
1407	    pBox->y2 = y2;
1408	    pBox++;
1409	}
1410    }
1411    if (pBox != (BoxPtr) (pData + 1))
1412    {
1413	pData->size = nrects;
1414	pData->numRects = pBox - (BoxPtr) (pData + 1);
1415    	pRgn->data = pData;
1416    	if (ctype != CT_YXBANDED)
1417    	{
1418	    Bool overlap; /* result ignored */
1419	    pRgn->extents.x1 = pRgn->extents.x2 = 0;
1420	    RegionValidate(pRgn, &overlap);
1421    	}
1422    	else
1423	    RegionSetExtents(pRgn);
1424    	good(pRgn);
1425    }
1426    else
1427    {
1428	free(pData);
1429    }
1430    return pRgn;
1431}
1432
1433#define ExchangeSpans(a, b)				    \
1434{							    \
1435    DDXPointRec	tpt;				    	    \
1436    int    	tw;					    \
1437							    \
1438    tpt = spans[a]; spans[a] = spans[b]; spans[b] = tpt;    \
1439    tw = widths[a]; widths[a] = widths[b]; widths[b] = tw;  \
1440}
1441
1442/* ||| I should apply the merge sort code to rectangle sorting above, and see
1443   if mapping time can be improved.  But right now I've been at work 12 hours,
1444   so forget it.
1445*/
1446
1447static void QuickSortSpans(
1448    DDXPointRec spans[],
1449    int	    	widths[],
1450    int	    	numSpans)
1451{
1452    int	    y;
1453    int	    i, j, m;
1454    DDXPointPtr    r;
1455
1456    /* Always called with numSpans > 1 */
1457    /* Sorts only by y, doesn't bother to sort by x */
1458
1459    do
1460    {
1461	if (numSpans < 9)
1462	{
1463	    /* Do insertion sort */
1464	    int yprev;
1465
1466	    yprev = spans[0].y;
1467	    i = 1;
1468	    do
1469	    { /* while i != numSpans */
1470		y = spans[i].y;
1471		if (yprev > y)
1472		{
1473		    /* spans[i] is out of order.  Move into proper location. */
1474		    DDXPointRec tpt;
1475		    int	    tw, k;
1476
1477		    for (j = 0; y >= spans[j].y; j++) {}
1478		    tpt = spans[i];
1479		    tw  = widths[i];
1480		    for (k = i; k != j; k--)
1481		    {
1482			spans[k] = spans[k-1];
1483			widths[k] = widths[k-1];
1484		    }
1485		    spans[j] = tpt;
1486		    widths[j] = tw;
1487		    y = spans[i].y;
1488		} /* if out of order */
1489		yprev = y;
1490		i++;
1491	    } while (i != numSpans);
1492	    return;
1493	}
1494
1495	/* Choose partition element, stick in location 0 */
1496	m = numSpans / 2;
1497	if (spans[m].y > spans[0].y)		ExchangeSpans(m, 0);
1498	if (spans[m].y > spans[numSpans-1].y)   ExchangeSpans(m, numSpans-1);
1499	if (spans[m].y > spans[0].y)		ExchangeSpans(m, 0);
1500	y = spans[0].y;
1501
1502        /* Partition array */
1503        i = 0;
1504        j = numSpans;
1505        do
1506	{
1507	    r = &(spans[i]);
1508	    do
1509	    {
1510		r++;
1511		i++;
1512            } while (i != numSpans && r->y < y);
1513	    r = &(spans[j]);
1514	    do
1515	    {
1516		r--;
1517		j--;
1518            } while (y < r->y);
1519            if (i < j)
1520		ExchangeSpans(i, j);
1521        } while (i < j);
1522
1523        /* Move partition element back to middle */
1524        ExchangeSpans(0, j);
1525
1526	/* Recurse */
1527        if (numSpans-j-1 > 1)
1528	    QuickSortSpans(&spans[j+1], &widths[j+1], numSpans-j-1);
1529        numSpans = j;
1530    } while (numSpans > 1);
1531}
1532
1533#define NextBand()						    \
1534{								    \
1535    clipy1 = pboxBandStart->y1;					    \
1536    clipy2 = pboxBandStart->y2;					    \
1537    pboxBandEnd = pboxBandStart + 1;				    \
1538    while (pboxBandEnd != pboxLast && pboxBandEnd->y1 == clipy1) {  \
1539	pboxBandEnd++;						    \
1540    }								    \
1541    for (; ppt != pptLast && ppt->y < clipy1; ppt++, pwidth++) {} \
1542}
1543
1544/*
1545    Clip a list of scanlines to a region.  The caller has allocated the
1546    space.  FSorted is non-zero if the scanline origins are in ascending
1547    order.
1548    returns the number of new, clipped scanlines.
1549*/
1550
1551int
1552RegionClipSpans(
1553    RegionPtr	prgnDst,
1554    DDXPointPtr ppt,
1555    int	    	*pwidth,
1556    int		nspans,
1557    DDXPointPtr	pptNew,
1558    int		*pwidthNew,
1559    int		fSorted)
1560{
1561    DDXPointPtr pptLast;
1562    int	*pwidthNewStart;	/* the vengeance of Xerox! */
1563    int	y, x1, x2;
1564    int	numRects;
1565
1566    good(prgnDst);
1567    pptLast = ppt + nspans;
1568    pwidthNewStart = pwidthNew;
1569
1570    if (!prgnDst->data)
1571    {
1572	/* Do special fast code with clip boundaries in registers(?) */
1573	/* It doesn't pay much to make use of fSorted in this case,
1574	   so we lump everything together. */
1575
1576	int clipx1, clipx2, clipy1, clipy2;
1577
1578	clipx1 = prgnDst->extents.x1;
1579	clipy1 = prgnDst->extents.y1;
1580	clipx2 = prgnDst->extents.x2;
1581	clipy2 = prgnDst->extents.y2;
1582
1583	for (; ppt != pptLast; ppt++, pwidth++)
1584	{
1585	    y = ppt->y;
1586	    x1 = ppt->x;
1587	    if (clipy1 <= y && y < clipy2)
1588	    {
1589		x2 = x1 + *pwidth;
1590		if (x1 < clipx1)    x1 = clipx1;
1591		if (x2 > clipx2)    x2 = clipx2;
1592		if (x1 < x2)
1593		{
1594		    /* part of span in clip rectangle */
1595		    pptNew->x = x1;
1596		    pptNew->y = y;
1597		    *pwidthNew = x2 - x1;
1598		    pptNew++;
1599		    pwidthNew++;
1600		}
1601	    }
1602	} /* end for */
1603
1604    }
1605    else if ((numRects = prgnDst->data->numRects))
1606    {
1607	/* Have to clip against many boxes */
1608	BoxPtr pboxBandStart, pboxBandEnd;
1609	BoxPtr pbox;
1610	BoxPtr pboxLast;
1611	int clipy1, clipy2;
1612
1613	/* In this case, taking advantage of sorted spans gains more than
1614	   the sorting costs. */
1615	if ((! fSorted) && (nspans > 1))
1616	    QuickSortSpans(ppt, pwidth, nspans);
1617
1618	pboxBandStart = RegionBoxptr(prgnDst);
1619	pboxLast = pboxBandStart + numRects;
1620
1621	NextBand();
1622
1623	for (; ppt != pptLast; )
1624	{
1625	    y = ppt->y;
1626	    if (y < clipy2)
1627	    {
1628		/* span is in the current band */
1629		pbox = pboxBandStart;
1630		x1 = ppt->x;
1631		x2 = x1 + *pwidth;
1632		do
1633		{ /* For each box in band */
1634		    int newx1, newx2;
1635
1636		    newx1 = x1;
1637		    newx2 = x2;
1638		    if (newx1 < pbox->x1)   newx1 = pbox->x1;
1639		    if (newx2 > pbox->x2)   newx2 = pbox->x2;
1640		    if (newx1 < newx2)
1641		    {
1642			/* Part of span in clip rectangle */
1643			pptNew->x = newx1;
1644			pptNew->y = y;
1645			*pwidthNew = newx2 - newx1;
1646			pptNew++;
1647			pwidthNew++;
1648		    }
1649		    pbox++;
1650		} while (pbox != pboxBandEnd);
1651		ppt++;
1652		pwidth++;
1653	    }
1654	    else
1655	    {
1656		/* Move to next band, adjust ppt as needed */
1657		pboxBandStart = pboxBandEnd;
1658		if (pboxBandStart == pboxLast)
1659		    break; /* We're completely done */
1660		NextBand();
1661	    }
1662	}
1663    }
1664    return pwidthNew - pwidthNewStart;
1665}
1666