1706f2543Smrg/***********************************************************
2706f2543Smrg
3706f2543SmrgCopyright 1987, 1988, 1989, 1998  The Open Group
4706f2543Smrg
5706f2543SmrgPermission to use, copy, modify, distribute, and sell this software and its
6706f2543Smrgdocumentation for any purpose is hereby granted without fee, provided that
7706f2543Smrgthe above copyright notice appear in all copies and that both that
8706f2543Smrgcopyright notice and this permission notice appear in supporting
9706f2543Smrgdocumentation.
10706f2543Smrg
11706f2543SmrgThe above copyright notice and this permission notice shall be included in
12706f2543Smrgall copies or substantial portions of the Software.
13706f2543Smrg
14706f2543SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15706f2543SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16706f2543SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17706f2543SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18706f2543SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19706f2543SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20706f2543Smrg
21706f2543SmrgExcept as contained in this notice, the name of The Open Group shall not be
22706f2543Smrgused in advertising or otherwise to promote the sale, use or other dealings
23706f2543Smrgin this Software without prior written authorization from The Open Group.
24706f2543Smrg
25706f2543Smrg
26706f2543SmrgCopyright 1987, 1988, 1989 by
27706f2543SmrgDigital Equipment Corporation, Maynard, Massachusetts.
28706f2543Smrg
29706f2543Smrg                        All Rights Reserved
30706f2543Smrg
31706f2543SmrgPermission to use, copy, modify, and distribute this software and its
32706f2543Smrgdocumentation for any purpose and without fee is hereby granted,
33706f2543Smrgprovided that the above copyright notice appear in all copies and that
34706f2543Smrgboth that copyright notice and this permission notice appear in
35706f2543Smrgsupporting documentation, and that the name of Digital not be
36706f2543Smrgused in advertising or publicity pertaining to distribution of the
37706f2543Smrgsoftware without specific, written prior permission.
38706f2543Smrg
39706f2543SmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40706f2543SmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41706f2543SmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42706f2543SmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43706f2543SmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44706f2543SmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45706f2543SmrgSOFTWARE.
46706f2543Smrg
47706f2543Smrg******************************************************************/
48706f2543Smrg
49706f2543Smrg/* The panoramix components contained the following notice */
50706f2543Smrg/*****************************************************************
51706f2543Smrg
52706f2543SmrgCopyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts.
53706f2543Smrg
54706f2543SmrgPermission is hereby granted, free of charge, to any person obtaining a copy
55706f2543Smrgof this software and associated documentation files (the "Software"), to deal
56706f2543Smrgin the Software without restriction, including without limitation the rights
57706f2543Smrgto use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58706f2543Smrgcopies of the Software.
59706f2543Smrg
60706f2543SmrgThe above copyright notice and this permission notice shall be included in
61706f2543Smrgall copies or substantial portions of the Software.
62706f2543Smrg
63706f2543SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64706f2543SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65706f2543SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
66706f2543SmrgDIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
67706f2543SmrgBUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
68706f2543SmrgWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
69706f2543SmrgIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
70706f2543Smrg
71706f2543SmrgExcept as contained in this notice, the name of Digital Equipment Corporation
72706f2543Smrgshall not be used in advertising or otherwise to promote the sale, use or other
73706f2543Smrgdealings in this Software without prior written authorization from Digital
74706f2543SmrgEquipment Corporation.
75706f2543Smrg
76706f2543Smrg******************************************************************/
77706f2543Smrg
78706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
79706f2543Smrg#include <dix-config.h>
80706f2543Smrg#endif
81706f2543Smrg
82706f2543Smrg#include "regionstr.h"
83706f2543Smrg#include <X11/Xprotostr.h>
84706f2543Smrg#include <X11/Xfuncproto.h>
85706f2543Smrg#include "gc.h"
86706f2543Smrg#include <pixman.h>
87706f2543Smrg
88706f2543Smrg#undef assert
89706f2543Smrg#ifdef REGION_DEBUG
90706f2543Smrg#define assert(expr) { \
91706f2543Smrg            CARD32 *foo = NULL; \
92706f2543Smrg            if (!(expr)) { \
93706f2543Smrg                ErrorF("Assertion failed file %s, line %d: %s\n", \
94706f2543Smrg                       __FILE__, __LINE__, #expr); \
95706f2543Smrg                *foo = 0xdeadbeef; /* to get a backtrace */ \
96706f2543Smrg            } \
97706f2543Smrg        }
98706f2543Smrg#else
99706f2543Smrg#define assert(expr)
100706f2543Smrg#endif
101706f2543Smrg
102706f2543Smrg#define good(reg) assert(RegionIsValid(reg))
103706f2543Smrg
104706f2543Smrg/*
105706f2543Smrg * The functions in this file implement the Region abstraction used extensively
106706f2543Smrg * throughout the X11 sample server. A Region is simply a set of disjoint
107706f2543Smrg * (non-overlapping) rectangles, plus an "extent" rectangle which is the
108706f2543Smrg * smallest single rectangle that contains all the non-overlapping rectangles.
109706f2543Smrg *
110706f2543Smrg * A Region is implemented as a "y-x-banded" array of rectangles.  This array
111706f2543Smrg * imposes two degrees of order.  First, all rectangles are sorted by top side
112706f2543Smrg * y coordinate first (y1), and then by left side x coordinate (x1).
113706f2543Smrg *
114706f2543Smrg * Furthermore, the rectangles are grouped into "bands".  Each rectangle in a
115706f2543Smrg * band has the same top y coordinate (y1), and each has the same bottom y
116706f2543Smrg * coordinate (y2).  Thus all rectangles in a band differ only in their left
117706f2543Smrg * and right side (x1 and x2).  Bands are implicit in the array of rectangles:
118706f2543Smrg * there is no separate list of band start pointers.
119706f2543Smrg *
120706f2543Smrg * The y-x band representation does not minimize rectangles.  In particular,
121706f2543Smrg * if a rectangle vertically crosses a band (the rectangle has scanlines in
122706f2543Smrg * the y1 to y2 area spanned by the band), then the rectangle may be broken
123706f2543Smrg * down into two or more smaller rectangles stacked one atop the other.
124706f2543Smrg *
125706f2543Smrg *  -----------				    -----------
126706f2543Smrg *  |         |				    |         |		    band 0
127706f2543Smrg *  |         |  --------		    -----------  --------
128706f2543Smrg *  |         |  |      |  in y-x banded    |         |  |      |   band 1
129706f2543Smrg *  |         |  |      |  form is	    |         |  |      |
130706f2543Smrg *  -----------  |      |		    -----------  --------
131706f2543Smrg *               |      |				 |      |   band 2
132706f2543Smrg *               --------				 --------
133706f2543Smrg *
134706f2543Smrg * An added constraint on the rectangles is that they must cover as much
135706f2543Smrg * horizontal area as possible: no two rectangles within a band are allowed
136706f2543Smrg * to touch.
137706f2543Smrg *
138706f2543Smrg * Whenever possible, bands will be merged together to cover a greater vertical
139706f2543Smrg * distance (and thus reduce the number of rectangles). Two bands can be merged
140706f2543Smrg * only if the bottom of one touches the top of the other and they have
141706f2543Smrg * rectangles in the same places (of the same width, of course).
142706f2543Smrg *
143706f2543Smrg * Adam de Boor wrote most of the original region code.  Joel McCormack
144706f2543Smrg * substantially modified or rewrote most of the core arithmetic routines,
145706f2543Smrg * and added RegionValidate in order to support several speed improvements
146706f2543Smrg * to miValidateTree.  Bob Scheifler changed the representation to be more
147706f2543Smrg * compact when empty or a single rectangle, and did a bunch of gratuitous
148706f2543Smrg * reformatting.
149706f2543Smrg */
150706f2543Smrg
151706f2543Smrg/*  true iff two Boxes overlap */
152706f2543Smrg#define EXTENTCHECK(r1,r2) \
153706f2543Smrg      (!( ((r1)->x2 <= (r2)->x1)  || \
154706f2543Smrg          ((r1)->x1 >= (r2)->x2)  || \
155706f2543Smrg          ((r1)->y2 <= (r2)->y1)  || \
156706f2543Smrg          ((r1)->y1 >= (r2)->y2) ) )
157706f2543Smrg
158706f2543Smrg/* true iff (x,y) is in Box */
159706f2543Smrg#define INBOX(r,x,y) \
160706f2543Smrg      ( ((r)->x2 >  x) && \
161706f2543Smrg        ((r)->x1 <= x) && \
162706f2543Smrg        ((r)->y2 >  y) && \
163706f2543Smrg        ((r)->y1 <= y) )
164706f2543Smrg
165706f2543Smrg/* true iff Box r1 contains Box r2 */
166706f2543Smrg#define SUBSUMES(r1,r2) \
167706f2543Smrg      ( ((r1)->x1 <= (r2)->x1) && \
168706f2543Smrg        ((r1)->x2 >= (r2)->x2) && \
169706f2543Smrg        ((r1)->y1 <= (r2)->y1) && \
170706f2543Smrg        ((r1)->y2 >= (r2)->y2) )
171706f2543Smrg
172706f2543Smrg#define xfreeData(reg) if ((reg)->data && (reg)->data->size) free((reg)->data)
173706f2543Smrg
174706f2543Smrg#define RECTALLOC_BAIL(pReg,n,bail) \
175706f2543Smrgif (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \
176706f2543Smrg    if (!RegionRectAlloc(pReg, n)) { goto bail; }
177706f2543Smrg
178706f2543Smrg#define RECTALLOC(pReg,n) \
179706f2543Smrgif (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \
180706f2543Smrg    if (!RegionRectAlloc(pReg, n)) { return FALSE; }
181706f2543Smrg
182706f2543Smrg#define ADDRECT(pNextRect,nx1,ny1,nx2,ny2)	\
183706f2543Smrg{						\
184706f2543Smrg    pNextRect->x1 = nx1;			\
185706f2543Smrg    pNextRect->y1 = ny1;			\
186706f2543Smrg    pNextRect->x2 = nx2;			\
187706f2543Smrg    pNextRect->y2 = ny2;			\
188706f2543Smrg    pNextRect++;				\
189706f2543Smrg}
190706f2543Smrg
191706f2543Smrg#define NEWRECT(pReg,pNextRect,nx1,ny1,nx2,ny2)			\
192706f2543Smrg{									\
193706f2543Smrg    if (!(pReg)->data || ((pReg)->data->numRects == (pReg)->data->size))\
194706f2543Smrg    {									\
195706f2543Smrg	if (!RegionRectAlloc(pReg, 1))					\
196706f2543Smrg	    return FALSE;						\
197706f2543Smrg	pNextRect = RegionTop(pReg);					\
198706f2543Smrg    }									\
199706f2543Smrg    ADDRECT(pNextRect,nx1,ny1,nx2,ny2);					\
200706f2543Smrg    pReg->data->numRects++;						\
201706f2543Smrg    assert(pReg->data->numRects<=pReg->data->size);			\
202706f2543Smrg}
203706f2543Smrg
204706f2543Smrg
205706f2543Smrg#define DOWNSIZE(reg,numRects)						 \
206706f2543Smrgif (((numRects) < ((reg)->data->size >> 1)) && ((reg)->data->size > 50)) \
207706f2543Smrg{									 \
208706f2543Smrg    size_t NewSize = RegionSizeof(numRects);				 \
209706f2543Smrg    RegDataPtr NewData =						 \
210706f2543Smrg        (NewSize > 0) ? realloc((reg)->data, NewSize) : NULL ;		 \
211706f2543Smrg    if (NewData)							 \
212706f2543Smrg    {									 \
213706f2543Smrg	NewData->size = (numRects);					 \
214706f2543Smrg	(reg)->data = NewData;						 \
215706f2543Smrg    }									 \
216706f2543Smrg}
217706f2543Smrg
218706f2543Smrg
219706f2543SmrgBoxRec RegionEmptyBox = {0, 0, 0, 0};
220706f2543SmrgRegDataRec RegionEmptyData = {0, 0};
221706f2543Smrg
222706f2543SmrgRegDataRec  RegionBrokenData = {0, 0};
223706f2543Smrgstatic RegionRec   RegionBrokenRegion = { { 0, 0, 0, 0 }, &RegionBrokenData };
224706f2543Smrg
225706f2543Smrgvoid
226706f2543SmrgInitRegions (void)
227706f2543Smrg{
228706f2543Smrg    pixman_region_set_static_pointers (&RegionEmptyBox, &RegionEmptyData, &RegionBrokenData);
229706f2543Smrg}
230706f2543Smrg
231706f2543Smrg/*****************************************************************
232706f2543Smrg *   RegionCreate(rect, size)
233706f2543Smrg *     This routine does a simple malloc to make a structure of
234706f2543Smrg *     REGION of "size" number of rectangles.
235706f2543Smrg *****************************************************************/
236706f2543Smrg
237706f2543SmrgRegionPtr
238706f2543SmrgRegionCreate(BoxPtr rect, int size)
239706f2543Smrg{
240706f2543Smrg    RegionPtr pReg;
241706f2543Smrg
242706f2543Smrg    pReg = (RegionPtr)malloc(sizeof(RegionRec));
243706f2543Smrg    if (!pReg)
244706f2543Smrg	return &RegionBrokenRegion;
245706f2543Smrg
246706f2543Smrg    RegionInit (pReg, rect, size);
247706f2543Smrg
248706f2543Smrg    return pReg;
249706f2543Smrg}
250706f2543Smrg
251706f2543Smrgvoid
252706f2543SmrgRegionDestroy(RegionPtr pReg)
253706f2543Smrg{
254706f2543Smrg    pixman_region_fini (pReg);
255706f2543Smrg    if (pReg != &RegionBrokenRegion)
256706f2543Smrg	free(pReg);
257706f2543Smrg}
258706f2543Smrg
259706f2543Smrgvoid
260706f2543SmrgRegionPrint(RegionPtr rgn)
261706f2543Smrg{
262706f2543Smrg    int num, size;
263706f2543Smrg    int i;
264706f2543Smrg    BoxPtr rects;
265706f2543Smrg
266706f2543Smrg    num = RegionNumRects(rgn);
267706f2543Smrg    size = RegionSize(rgn);
268706f2543Smrg    rects = RegionRects(rgn);
269706f2543Smrg    ErrorF("[mi] num: %d size: %d\n", num, size);
270706f2543Smrg    ErrorF("[mi] extents: %d %d %d %d\n",
271706f2543Smrg	   rgn->extents.x1, rgn->extents.y1, rgn->extents.x2, rgn->extents.y2);
272706f2543Smrg    for (i = 0; i < num; i++)
273706f2543Smrg      ErrorF("[mi] %d %d %d %d \n",
274706f2543Smrg	     rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2);
275706f2543Smrg    ErrorF("[mi] \n");
276706f2543Smrg}
277706f2543Smrg
278706f2543Smrg#ifdef DEBUG
279706f2543SmrgBool
280706f2543SmrgRegionIsValid(RegionPtr reg)
281706f2543Smrg{
282706f2543Smrg    int i, numRects;
283706f2543Smrg
284706f2543Smrg    if ((reg->extents.x1 > reg->extents.x2) ||
285706f2543Smrg	(reg->extents.y1 > reg->extents.y2))
286706f2543Smrg	return FALSE;
287706f2543Smrg    numRects = RegionNumRects(reg);
288706f2543Smrg    if (!numRects)
289706f2543Smrg	return ((reg->extents.x1 == reg->extents.x2) &&
290706f2543Smrg		(reg->extents.y1 == reg->extents.y2) &&
291706f2543Smrg		(reg->data->size || (reg->data == &RegionEmptyData)));
292706f2543Smrg    else if (numRects == 1)
293706f2543Smrg	return !reg->data;
294706f2543Smrg    else
295706f2543Smrg    {
296706f2543Smrg	BoxPtr pboxP, pboxN;
297706f2543Smrg	BoxRec box;
298706f2543Smrg
299706f2543Smrg	pboxP = RegionRects(reg);
300706f2543Smrg	box = *pboxP;
301706f2543Smrg	box.y2 = pboxP[numRects-1].y2;
302706f2543Smrg	pboxN = pboxP + 1;
303706f2543Smrg	for (i = numRects; --i > 0; pboxP++, pboxN++)
304706f2543Smrg	{
305706f2543Smrg	    if ((pboxN->x1 >= pboxN->x2) ||
306706f2543Smrg		(pboxN->y1 >= pboxN->y2))
307706f2543Smrg		return FALSE;
308706f2543Smrg	    if (pboxN->x1 < box.x1)
309706f2543Smrg		box.x1 = pboxN->x1;
310706f2543Smrg	    if (pboxN->x2 > box.x2)
311706f2543Smrg		box.x2 = pboxN->x2;
312706f2543Smrg	    if ((pboxN->y1 < pboxP->y1) ||
313706f2543Smrg		((pboxN->y1 == pboxP->y1) &&
314706f2543Smrg		 ((pboxN->x1 < pboxP->x2) || (pboxN->y2 != pboxP->y2))))
315706f2543Smrg		return FALSE;
316706f2543Smrg	}
317706f2543Smrg	return ((box.x1 == reg->extents.x1) &&
318706f2543Smrg		(box.x2 == reg->extents.x2) &&
319706f2543Smrg		(box.y1 == reg->extents.y1) &&
320706f2543Smrg		(box.y2 == reg->extents.y2));
321706f2543Smrg    }
322706f2543Smrg}
323706f2543Smrg#endif /* DEBUG */
324706f2543Smrg
325706f2543SmrgBool
326706f2543SmrgRegionBreak (RegionPtr pReg)
327706f2543Smrg{
328706f2543Smrg    xfreeData (pReg);
329706f2543Smrg    pReg->extents = RegionEmptyBox;
330706f2543Smrg    pReg->data = &RegionBrokenData;
331706f2543Smrg    return FALSE;
332706f2543Smrg}
333706f2543Smrg
334706f2543SmrgBool
335706f2543SmrgRegionRectAlloc(RegionPtr pRgn, int n)
336706f2543Smrg{
337706f2543Smrg    RegDataPtr	data;
338706f2543Smrg    size_t rgnSize;
339706f2543Smrg
340706f2543Smrg    if (!pRgn->data)
341706f2543Smrg    {
342706f2543Smrg	n++;
343706f2543Smrg	rgnSize = RegionSizeof(n);
344706f2543Smrg	pRgn->data = (rgnSize > 0) ? malloc(rgnSize) : NULL;
345706f2543Smrg	if (!pRgn->data)
346706f2543Smrg	    return RegionBreak (pRgn);
347706f2543Smrg	pRgn->data->numRects = 1;
348706f2543Smrg	*RegionBoxptr(pRgn) = pRgn->extents;
349706f2543Smrg    }
350706f2543Smrg    else if (!pRgn->data->size)
351706f2543Smrg    {
352706f2543Smrg	rgnSize = RegionSizeof(n);
353706f2543Smrg	pRgn->data = (rgnSize > 0) ? malloc(rgnSize) : NULL;
354706f2543Smrg	if (!pRgn->data)
355706f2543Smrg	    return RegionBreak (pRgn);
356706f2543Smrg	pRgn->data->numRects = 0;
357706f2543Smrg    }
358706f2543Smrg    else
359706f2543Smrg    {
360706f2543Smrg	if (n == 1)
361706f2543Smrg	{
362706f2543Smrg	    n = pRgn->data->numRects;
363706f2543Smrg	    if (n > 500) /* XXX pick numbers out of a hat */
364706f2543Smrg		n = 250;
365706f2543Smrg	}
366706f2543Smrg	n += pRgn->data->numRects;
367706f2543Smrg	rgnSize = RegionSizeof(n);
368706f2543Smrg	data = (rgnSize > 0) ? realloc(pRgn->data, rgnSize) : NULL;
369706f2543Smrg	if (!data)
370706f2543Smrg	    return RegionBreak (pRgn);
371706f2543Smrg	pRgn->data = data;
372706f2543Smrg    }
373706f2543Smrg    pRgn->data->size = n;
374706f2543Smrg    return TRUE;
375706f2543Smrg}
376706f2543Smrg
377706f2543Smrg/*======================================================================
378706f2543Smrg *	    Generic Region Operator
379706f2543Smrg *====================================================================*/
380706f2543Smrg
381706f2543Smrg/*-
382706f2543Smrg *-----------------------------------------------------------------------
383706f2543Smrg * RegionCoalesce --
384706f2543Smrg *	Attempt to merge the boxes in the current band with those in the
385706f2543Smrg *	previous one.  We are guaranteed that the current band extends to
386706f2543Smrg *      the end of the rects array.  Used only by RegionOp.
387706f2543Smrg *
388706f2543Smrg * Results:
389706f2543Smrg *	The new index for the previous band.
390706f2543Smrg *
391706f2543Smrg * Side Effects:
392706f2543Smrg *	If coalescing takes place:
393706f2543Smrg *	    - rectangles in the previous band will have their y2 fields
394706f2543Smrg *	      altered.
395706f2543Smrg *	    - pReg->data->numRects will be decreased.
396706f2543Smrg *
397706f2543Smrg *-----------------------------------------------------------------------
398706f2543Smrg */
399706f2543Smrg_X_INLINE static int
400706f2543SmrgRegionCoalesce (
401706f2543Smrg    RegionPtr	pReg,	    	/* Region to coalesce		     */
402706f2543Smrg    int	    	  	prevStart,  	/* Index of start of previous band   */
403706f2543Smrg    int	    	  	curStart)   	/* Index of start of current band    */
404706f2543Smrg{
405706f2543Smrg    BoxPtr	pPrevBox;   	/* Current box in previous band	     */
406706f2543Smrg    BoxPtr	pCurBox;    	/* Current box in current band       */
407706f2543Smrg    int  	numRects;	/* Number rectangles in both bands   */
408706f2543Smrg    int		y2;		/* Bottom of current band	     */
409706f2543Smrg    /*
410706f2543Smrg     * Figure out how many rectangles are in the band.
411706f2543Smrg     */
412706f2543Smrg    numRects = curStart - prevStart;
413706f2543Smrg    assert(numRects == pReg->data->numRects - curStart);
414706f2543Smrg
415706f2543Smrg    if (!numRects) return curStart;
416706f2543Smrg
417706f2543Smrg    /*
418706f2543Smrg     * The bands may only be coalesced if the bottom of the previous
419706f2543Smrg     * matches the top scanline of the current.
420706f2543Smrg     */
421706f2543Smrg    pPrevBox = RegionBox(pReg, prevStart);
422706f2543Smrg    pCurBox = RegionBox(pReg, curStart);
423706f2543Smrg    if (pPrevBox->y2 != pCurBox->y1) return curStart;
424706f2543Smrg
425706f2543Smrg    /*
426706f2543Smrg     * Make sure the bands have boxes in the same places. This
427706f2543Smrg     * assumes that boxes have been added in such a way that they
428706f2543Smrg     * cover the most area possible. I.e. two boxes in a band must
429706f2543Smrg     * have some horizontal space between them.
430706f2543Smrg     */
431706f2543Smrg    y2 = pCurBox->y2;
432706f2543Smrg
433706f2543Smrg    do {
434706f2543Smrg	if ((pPrevBox->x1 != pCurBox->x1) || (pPrevBox->x2 != pCurBox->x2)) {
435706f2543Smrg	    return curStart;
436706f2543Smrg	}
437706f2543Smrg	pPrevBox++;
438706f2543Smrg	pCurBox++;
439706f2543Smrg	numRects--;
440706f2543Smrg    } while (numRects);
441706f2543Smrg
442706f2543Smrg    /*
443706f2543Smrg     * The bands may be merged, so set the bottom y of each box
444706f2543Smrg     * in the previous band to the bottom y of the current band.
445706f2543Smrg     */
446706f2543Smrg    numRects = curStart - prevStart;
447706f2543Smrg    pReg->data->numRects -= numRects;
448706f2543Smrg    do {
449706f2543Smrg	pPrevBox--;
450706f2543Smrg	pPrevBox->y2 = y2;
451706f2543Smrg	numRects--;
452706f2543Smrg    } while (numRects);
453706f2543Smrg    return prevStart;
454706f2543Smrg}
455706f2543Smrg
456706f2543Smrg
457706f2543Smrg/* Quicky macro to avoid trivial reject procedure calls to RegionCoalesce */
458706f2543Smrg
459706f2543Smrg#define Coalesce(newReg, prevBand, curBand)				\
460706f2543Smrg    if (curBand - prevBand == newReg->data->numRects - curBand) {	\
461706f2543Smrg	prevBand = RegionCoalesce(newReg, prevBand, curBand);		\
462706f2543Smrg    } else {								\
463706f2543Smrg	prevBand = curBand;						\
464706f2543Smrg    }
465706f2543Smrg
466706f2543Smrg/*-
467706f2543Smrg *-----------------------------------------------------------------------
468706f2543Smrg * RegionAppendNonO --
469706f2543Smrg *	Handle a non-overlapping band for the union and subtract operations.
470706f2543Smrg *      Just adds the (top/bottom-clipped) rectangles into the region.
471706f2543Smrg *      Doesn't have to check for subsumption or anything.
472706f2543Smrg *
473706f2543Smrg * Results:
474706f2543Smrg *	None.
475706f2543Smrg *
476706f2543Smrg * Side Effects:
477706f2543Smrg *	pReg->data->numRects is incremented and the rectangles overwritten
478706f2543Smrg *	with the rectangles we're passed.
479706f2543Smrg *
480706f2543Smrg *-----------------------------------------------------------------------
481706f2543Smrg */
482706f2543Smrg
483706f2543Smrg_X_INLINE static Bool
484706f2543SmrgRegionAppendNonO (
485706f2543Smrg    RegionPtr	pReg,
486706f2543Smrg    BoxPtr	r,
487706f2543Smrg    BoxPtr  	rEnd,
488706f2543Smrg    int  	y1,
489706f2543Smrg    int  	y2)
490706f2543Smrg{
491706f2543Smrg    BoxPtr	pNextRect;
492706f2543Smrg    int		newRects;
493706f2543Smrg
494706f2543Smrg    newRects = rEnd - r;
495706f2543Smrg
496706f2543Smrg    assert(y1 < y2);
497706f2543Smrg    assert(newRects != 0);
498706f2543Smrg
499706f2543Smrg    /* Make sure we have enough space for all rectangles to be added */
500706f2543Smrg    RECTALLOC(pReg, newRects);
501706f2543Smrg    pNextRect = RegionTop(pReg);
502706f2543Smrg    pReg->data->numRects += newRects;
503706f2543Smrg    do {
504706f2543Smrg	assert(r->x1 < r->x2);
505706f2543Smrg	ADDRECT(pNextRect, r->x1, y1, r->x2, y2);
506706f2543Smrg	r++;
507706f2543Smrg    } while (r != rEnd);
508706f2543Smrg
509706f2543Smrg    return TRUE;
510706f2543Smrg}
511706f2543Smrg
512706f2543Smrg#define FindBand(r, rBandEnd, rEnd, ry1)		    \
513706f2543Smrg{							    \
514706f2543Smrg    ry1 = r->y1;					    \
515706f2543Smrg    rBandEnd = r+1;					    \
516706f2543Smrg    while ((rBandEnd != rEnd) && (rBandEnd->y1 == ry1)) {   \
517706f2543Smrg	rBandEnd++;					    \
518706f2543Smrg    }							    \
519706f2543Smrg}
520706f2543Smrg
521706f2543Smrg#define	AppendRegions(newReg, r, rEnd)					\
522706f2543Smrg{									\
523706f2543Smrg    int newRects;							\
524706f2543Smrg    if ((newRects = rEnd - r)) {					\
525706f2543Smrg	RECTALLOC(newReg, newRects);					\
526706f2543Smrg	memmove((char *)RegionTop(newReg),(char *)r, 			\
527706f2543Smrg              newRects * sizeof(BoxRec));				\
528706f2543Smrg	newReg->data->numRects += newRects;				\
529706f2543Smrg    }									\
530706f2543Smrg}
531706f2543Smrg
532706f2543Smrg/*-
533706f2543Smrg *-----------------------------------------------------------------------
534706f2543Smrg * RegionOp --
535706f2543Smrg *	Apply an operation to two regions. Called by RegionUnion, RegionInverse,
536706f2543Smrg *	RegionSubtract, RegionIntersect....  Both regions MUST have at least one
537706f2543Smrg *      rectangle, and cannot be the same object.
538706f2543Smrg *
539706f2543Smrg * Results:
540706f2543Smrg *	TRUE if successful.
541706f2543Smrg *
542706f2543Smrg * Side Effects:
543706f2543Smrg *	The new region is overwritten.
544706f2543Smrg *	pOverlap set to TRUE if overlapFunc ever returns TRUE.
545706f2543Smrg *
546706f2543Smrg * Notes:
547706f2543Smrg *	The idea behind this function is to view the two regions as sets.
548706f2543Smrg *	Together they cover a rectangle of area that this function divides
549706f2543Smrg *	into horizontal bands where points are covered only by one region
550706f2543Smrg *	or by both. For the first case, the nonOverlapFunc is called with
551706f2543Smrg *	each the band and the band's upper and lower extents. For the
552706f2543Smrg *	second, the overlapFunc is called to process the entire band. It
553706f2543Smrg *	is responsible for clipping the rectangles in the band, though
554706f2543Smrg *	this function provides the boundaries.
555706f2543Smrg *	At the end of each band, the new region is coalesced, if possible,
556706f2543Smrg *	to reduce the number of rectangles in the region.
557706f2543Smrg *
558706f2543Smrg *-----------------------------------------------------------------------
559706f2543Smrg */
560706f2543Smrg
561706f2543Smrgtypedef Bool (*OverlapProcPtr)(
562706f2543Smrg    RegionPtr	pReg,
563706f2543Smrg    BoxPtr	r1,
564706f2543Smrg    BoxPtr   	r1End,
565706f2543Smrg    BoxPtr	r2,
566706f2543Smrg    BoxPtr   	r2End,
567706f2543Smrg    short    	y1,
568706f2543Smrg    short    	y2,
569706f2543Smrg    Bool	*pOverlap);
570706f2543Smrg
571706f2543Smrgstatic Bool
572706f2543SmrgRegionOp(
573706f2543Smrg    RegionPtr       newReg,		    /* Place to store result	     */
574706f2543Smrg    RegionPtr       reg1,		    /* First region in operation     */
575706f2543Smrg    RegionPtr       reg2,		    /* 2d region in operation        */
576706f2543Smrg    OverlapProcPtr  overlapFunc,            /* Function to call for over-
577706f2543Smrg					     * lapping bands		     */
578706f2543Smrg    Bool	    appendNon1,		    /* Append non-overlapping bands  */
579706f2543Smrg					    /* in region 1 ? */
580706f2543Smrg    Bool	    appendNon2,		    /* Append non-overlapping bands  */
581706f2543Smrg					    /* in region 2 ? */
582706f2543Smrg    Bool	    *pOverlap)
583706f2543Smrg{
584706f2543Smrg    BoxPtr 	r1;		    /* Pointer into first region     */
585706f2543Smrg    BoxPtr 	r2;		    /* Pointer into 2d region	     */
586706f2543Smrg    BoxPtr	r1End;		    /* End of 1st region	     */
587706f2543Smrg    BoxPtr	r2End;		    /* End of 2d region		     */
588706f2543Smrg    short	ybot;		    /* Bottom of intersection	     */
589706f2543Smrg    short	ytop;		    /* Top of intersection	     */
590706f2543Smrg    RegDataPtr	oldData;	    /* Old data for newReg	     */
591706f2543Smrg    int		prevBand;	    /* Index of start of
592706f2543Smrg				     * previous band in newReg       */
593706f2543Smrg    int		curBand;	    /* Index of start of current
594706f2543Smrg				     * band in newReg		     */
595706f2543Smrg    BoxPtr  	r1BandEnd;	    /* End of current band in r1     */
596706f2543Smrg    BoxPtr  	r2BandEnd;	    /* End of current band in r2     */
597706f2543Smrg    short   	top;		    /* Top of non-overlapping band   */
598706f2543Smrg    short   	bot;	    	    /* Bottom of non-overlapping band*/
599706f2543Smrg    int     	r1y1;	    	    /* Temps for r1->y1 and r2->y1   */
600706f2543Smrg    int     	r2y1;
601706f2543Smrg    int	    	newSize;
602706f2543Smrg    int	    	numRects;
603706f2543Smrg
604706f2543Smrg    /*
605706f2543Smrg     * Break any region computed from a broken region
606706f2543Smrg     */
607706f2543Smrg    if (RegionNar (reg1) || RegionNar(reg2))
608706f2543Smrg	return RegionBreak (newReg);
609706f2543Smrg
610706f2543Smrg    /*
611706f2543Smrg     * Initialization:
612706f2543Smrg     *	set r1, r2, r1End and r2End appropriately, save the rectangles
613706f2543Smrg     * of the destination region until the end in case it's one of
614706f2543Smrg     * the two source regions, then mark the "new" region empty, allocating
615706f2543Smrg     * another array of rectangles for it to use.
616706f2543Smrg     */
617706f2543Smrg
618706f2543Smrg    r1 = RegionRects(reg1);
619706f2543Smrg    newSize = RegionNumRects(reg1);
620706f2543Smrg    r1End = r1 + newSize;
621706f2543Smrg    numRects = RegionNumRects(reg2);
622706f2543Smrg    r2 = RegionRects(reg2);
623706f2543Smrg    r2End = r2 + numRects;
624706f2543Smrg    assert(r1 != r1End);
625706f2543Smrg    assert(r2 != r2End);
626706f2543Smrg
627706f2543Smrg    oldData = NULL;
628706f2543Smrg    if (((newReg == reg1) && (newSize > 1)) ||
629706f2543Smrg	((newReg == reg2) && (numRects > 1)))
630706f2543Smrg    {
631706f2543Smrg	oldData = newReg->data;
632706f2543Smrg	newReg->data = &RegionEmptyData;
633706f2543Smrg    }
634706f2543Smrg    /* guess at new size */
635706f2543Smrg    if (numRects > newSize)
636706f2543Smrg	newSize = numRects;
637706f2543Smrg    newSize <<= 1;
638706f2543Smrg    if (!newReg->data)
639706f2543Smrg	newReg->data = &RegionEmptyData;
640706f2543Smrg    else if (newReg->data->size)
641706f2543Smrg	newReg->data->numRects = 0;
642706f2543Smrg    if (newSize > newReg->data->size)
643706f2543Smrg	if (!RegionRectAlloc(newReg, newSize))
644706f2543Smrg	    return FALSE;
645706f2543Smrg
646706f2543Smrg    /*
647706f2543Smrg     * Initialize ybot.
648706f2543Smrg     * In the upcoming loop, ybot and ytop serve different functions depending
649706f2543Smrg     * on whether the band being handled is an overlapping or non-overlapping
650706f2543Smrg     * band.
651706f2543Smrg     * 	In the case of a non-overlapping band (only one of the regions
652706f2543Smrg     * has points in the band), ybot is the bottom of the most recent
653706f2543Smrg     * intersection and thus clips the top of the rectangles in that band.
654706f2543Smrg     * ytop is the top of the next intersection between the two regions and
655706f2543Smrg     * serves to clip the bottom of the rectangles in the current band.
656706f2543Smrg     *	For an overlapping band (where the two regions intersect), ytop clips
657706f2543Smrg     * the top of the rectangles of both regions and ybot clips the bottoms.
658706f2543Smrg     */
659706f2543Smrg
660706f2543Smrg    ybot = min(r1->y1, r2->y1);
661706f2543Smrg
662706f2543Smrg    /*
663706f2543Smrg     * prevBand serves to mark the start of the previous band so rectangles
664706f2543Smrg     * can be coalesced into larger rectangles. qv. RegionCoalesce, above.
665706f2543Smrg     * In the beginning, there is no previous band, so prevBand == curBand
666706f2543Smrg     * (curBand is set later on, of course, but the first band will always
667706f2543Smrg     * start at index 0). prevBand and curBand must be indices because of
668706f2543Smrg     * the possible expansion, and resultant moving, of the new region's
669706f2543Smrg     * array of rectangles.
670706f2543Smrg     */
671706f2543Smrg    prevBand = 0;
672706f2543Smrg
673706f2543Smrg    do {
674706f2543Smrg	/*
675706f2543Smrg	 * This algorithm proceeds one source-band (as opposed to a
676706f2543Smrg	 * destination band, which is determined by where the two regions
677706f2543Smrg	 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
678706f2543Smrg	 * rectangle after the last one in the current band for their
679706f2543Smrg	 * respective regions.
680706f2543Smrg	 */
681706f2543Smrg	assert(r1 != r1End);
682706f2543Smrg	assert(r2 != r2End);
683706f2543Smrg
684706f2543Smrg	FindBand(r1, r1BandEnd, r1End, r1y1);
685706f2543Smrg	FindBand(r2, r2BandEnd, r2End, r2y1);
686706f2543Smrg
687706f2543Smrg	/*
688706f2543Smrg	 * First handle the band that doesn't intersect, if any.
689706f2543Smrg	 *
690706f2543Smrg	 * Note that attention is restricted to one band in the
691706f2543Smrg	 * non-intersecting region at once, so if a region has n
692706f2543Smrg	 * bands between the current position and the next place it overlaps
693706f2543Smrg	 * the other, this entire loop will be passed through n times.
694706f2543Smrg	 */
695706f2543Smrg	if (r1y1 < r2y1) {
696706f2543Smrg	    if (appendNon1) {
697706f2543Smrg		top = max(r1y1, ybot);
698706f2543Smrg		bot = min(r1->y2, r2y1);
699706f2543Smrg		if (top != bot)	{
700706f2543Smrg		    curBand = newReg->data->numRects;
701706f2543Smrg		    RegionAppendNonO(newReg, r1, r1BandEnd, top, bot);
702706f2543Smrg		    Coalesce(newReg, prevBand, curBand);
703706f2543Smrg		}
704706f2543Smrg	    }
705706f2543Smrg	    ytop = r2y1;
706706f2543Smrg	} else if (r2y1 < r1y1) {
707706f2543Smrg	    if (appendNon2) {
708706f2543Smrg		top = max(r2y1, ybot);
709706f2543Smrg		bot = min(r2->y2, r1y1);
710706f2543Smrg		if (top != bot) {
711706f2543Smrg		    curBand = newReg->data->numRects;
712706f2543Smrg		    RegionAppendNonO(newReg, r2, r2BandEnd, top, bot);
713706f2543Smrg		    Coalesce(newReg, prevBand, curBand);
714706f2543Smrg		}
715706f2543Smrg	    }
716706f2543Smrg	    ytop = r1y1;
717706f2543Smrg	} else {
718706f2543Smrg	    ytop = r1y1;
719706f2543Smrg	}
720706f2543Smrg
721706f2543Smrg	/*
722706f2543Smrg	 * Now see if we've hit an intersecting band. The two bands only
723706f2543Smrg	 * intersect if ybot > ytop
724706f2543Smrg	 */
725706f2543Smrg	ybot = min(r1->y2, r2->y2);
726706f2543Smrg	if (ybot > ytop) {
727706f2543Smrg	    curBand = newReg->data->numRects;
728706f2543Smrg	    (* overlapFunc)(newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot,
729706f2543Smrg			    pOverlap);
730706f2543Smrg	    Coalesce(newReg, prevBand, curBand);
731706f2543Smrg	}
732706f2543Smrg
733706f2543Smrg	/*
734706f2543Smrg	 * If we've finished with a band (y2 == ybot) we skip forward
735706f2543Smrg	 * in the region to the next band.
736706f2543Smrg	 */
737706f2543Smrg	if (r1->y2 == ybot) r1 = r1BandEnd;
738706f2543Smrg	if (r2->y2 == ybot) r2 = r2BandEnd;
739706f2543Smrg
740706f2543Smrg    } while (r1 != r1End && r2 != r2End);
741706f2543Smrg
742706f2543Smrg    /*
743706f2543Smrg     * Deal with whichever region (if any) still has rectangles left.
744706f2543Smrg     *
745706f2543Smrg     * We only need to worry about banding and coalescing for the very first
746706f2543Smrg     * band left.  After that, we can just group all remaining boxes,
747706f2543Smrg     * regardless of how many bands, into one final append to the list.
748706f2543Smrg     */
749706f2543Smrg
750706f2543Smrg    if ((r1 != r1End) && appendNon1) {
751706f2543Smrg	/* Do first nonOverlap1Func call, which may be able to coalesce */
752706f2543Smrg	FindBand(r1, r1BandEnd, r1End, r1y1);
753706f2543Smrg	curBand = newReg->data->numRects;
754706f2543Smrg	RegionAppendNonO(newReg, r1, r1BandEnd, max(r1y1, ybot), r1->y2);
755706f2543Smrg	Coalesce(newReg, prevBand, curBand);
756706f2543Smrg	/* Just append the rest of the boxes  */
757706f2543Smrg	AppendRegions(newReg, r1BandEnd, r1End);
758706f2543Smrg
759706f2543Smrg    } else if ((r2 != r2End) && appendNon2) {
760706f2543Smrg	/* Do first nonOverlap2Func call, which may be able to coalesce */
761706f2543Smrg	FindBand(r2, r2BandEnd, r2End, r2y1);
762706f2543Smrg	curBand = newReg->data->numRects;
763706f2543Smrg	RegionAppendNonO(newReg, r2, r2BandEnd, max(r2y1, ybot), r2->y2);
764706f2543Smrg	Coalesce(newReg, prevBand, curBand);
765706f2543Smrg	/* Append rest of boxes */
766706f2543Smrg	AppendRegions(newReg, r2BandEnd, r2End);
767706f2543Smrg    }
768706f2543Smrg
769706f2543Smrg    free(oldData);
770706f2543Smrg
771706f2543Smrg    if (!(numRects = newReg->data->numRects))
772706f2543Smrg    {
773706f2543Smrg	xfreeData(newReg);
774706f2543Smrg	newReg->data = &RegionEmptyData;
775706f2543Smrg    }
776706f2543Smrg    else if (numRects == 1)
777706f2543Smrg    {
778706f2543Smrg	newReg->extents = *RegionBoxptr(newReg);
779706f2543Smrg	xfreeData(newReg);
780706f2543Smrg	newReg->data = NULL;
781706f2543Smrg    }
782706f2543Smrg    else
783706f2543Smrg    {
784706f2543Smrg	DOWNSIZE(newReg, numRects);
785706f2543Smrg    }
786706f2543Smrg
787706f2543Smrg    return TRUE;
788706f2543Smrg}
789706f2543Smrg
790706f2543Smrg/*-
791706f2543Smrg *-----------------------------------------------------------------------
792706f2543Smrg * RegionSetExtents --
793706f2543Smrg *	Reset the extents of a region to what they should be. Called by
794706f2543Smrg *	Subtract and Intersect as they can't figure it out along the
795706f2543Smrg *	way or do so easily, as Union can.
796706f2543Smrg *
797706f2543Smrg * Results:
798706f2543Smrg *	None.
799706f2543Smrg *
800706f2543Smrg * Side Effects:
801706f2543Smrg *	The region's 'extents' structure is overwritten.
802706f2543Smrg *
803706f2543Smrg *-----------------------------------------------------------------------
804706f2543Smrg */
805706f2543Smrgstatic void
806706f2543SmrgRegionSetExtents (RegionPtr pReg)
807706f2543Smrg{
808706f2543Smrg    BoxPtr pBox, pBoxEnd;
809706f2543Smrg
810706f2543Smrg    if (!pReg->data)
811706f2543Smrg	return;
812706f2543Smrg    if (!pReg->data->size)
813706f2543Smrg    {
814706f2543Smrg	pReg->extents.x2 = pReg->extents.x1;
815706f2543Smrg	pReg->extents.y2 = pReg->extents.y1;
816706f2543Smrg	return;
817706f2543Smrg    }
818706f2543Smrg
819706f2543Smrg    pBox = RegionBoxptr(pReg);
820706f2543Smrg    pBoxEnd = RegionEnd(pReg);
821706f2543Smrg
822706f2543Smrg    /*
823706f2543Smrg     * Since pBox is the first rectangle in the region, it must have the
824706f2543Smrg     * smallest y1 and since pBoxEnd is the last rectangle in the region,
825706f2543Smrg     * it must have the largest y2, because of banding. Initialize x1 and
826706f2543Smrg     * x2 from  pBox and pBoxEnd, resp., as good things to initialize them
827706f2543Smrg     * to...
828706f2543Smrg     */
829706f2543Smrg    pReg->extents.x1 = pBox->x1;
830706f2543Smrg    pReg->extents.y1 = pBox->y1;
831706f2543Smrg    pReg->extents.x2 = pBoxEnd->x2;
832706f2543Smrg    pReg->extents.y2 = pBoxEnd->y2;
833706f2543Smrg
834706f2543Smrg    assert(pReg->extents.y1 < pReg->extents.y2);
835706f2543Smrg    while (pBox <= pBoxEnd) {
836706f2543Smrg	if (pBox->x1 < pReg->extents.x1)
837706f2543Smrg	    pReg->extents.x1 = pBox->x1;
838706f2543Smrg	if (pBox->x2 > pReg->extents.x2)
839706f2543Smrg	    pReg->extents.x2 = pBox->x2;
840706f2543Smrg	pBox++;
841706f2543Smrg    };
842706f2543Smrg
843706f2543Smrg    assert(pReg->extents.x1 < pReg->extents.x2);
844706f2543Smrg}
845706f2543Smrg
846706f2543Smrg/*======================================================================
847706f2543Smrg *	    Region Intersection
848706f2543Smrg *====================================================================*/
849706f2543Smrg/*-
850706f2543Smrg *-----------------------------------------------------------------------
851706f2543Smrg * RegionIntersectO --
852706f2543Smrg *	Handle an overlapping band for RegionIntersect.
853706f2543Smrg *
854706f2543Smrg * Results:
855706f2543Smrg *	TRUE if successful.
856706f2543Smrg *
857706f2543Smrg * Side Effects:
858706f2543Smrg *	Rectangles may be added to the region.
859706f2543Smrg *
860706f2543Smrg *-----------------------------------------------------------------------
861706f2543Smrg */
862706f2543Smrg/*ARGSUSED*/
863706f2543Smrg
864706f2543Smrg#define MERGERECT(r)						\
865706f2543Smrg{								\
866706f2543Smrg    if (r->x1 <= x2) {						\
867706f2543Smrg	/* Merge with current rectangle */			\
868706f2543Smrg	if (r->x1 < x2) *pOverlap = TRUE;				\
869706f2543Smrg	if (x2 < r->x2) x2 = r->x2;				\
870706f2543Smrg    } else {							\
871706f2543Smrg	/* Add current rectangle, start new one */		\
872706f2543Smrg	NEWRECT(pReg, pNextRect, x1, y1, x2, y2);		\
873706f2543Smrg	x1 = r->x1;						\
874706f2543Smrg	x2 = r->x2;						\
875706f2543Smrg    }								\
876706f2543Smrg    r++;							\
877706f2543Smrg}
878706f2543Smrg
879706f2543Smrg/*======================================================================
880706f2543Smrg *	    Region Union
881706f2543Smrg *====================================================================*/
882706f2543Smrg
883706f2543Smrg/*-
884706f2543Smrg *-----------------------------------------------------------------------
885706f2543Smrg * RegionUnionO --
886706f2543Smrg *	Handle an overlapping band for the union operation. Picks the
887706f2543Smrg *	left-most rectangle each time and merges it into the region.
888706f2543Smrg *
889706f2543Smrg * Results:
890706f2543Smrg *	TRUE if successful.
891706f2543Smrg *
892706f2543Smrg * Side Effects:
893706f2543Smrg *	pReg is overwritten.
894706f2543Smrg *	pOverlap is set to TRUE if any boxes overlap.
895706f2543Smrg *
896706f2543Smrg *-----------------------------------------------------------------------
897706f2543Smrg */
898706f2543Smrgstatic Bool
899706f2543SmrgRegionUnionO (
900706f2543Smrg    RegionPtr	pReg,
901706f2543Smrg    BoxPtr	r1,
902706f2543Smrg    BoxPtr  	r1End,
903706f2543Smrg    BoxPtr	r2,
904706f2543Smrg    BoxPtr  	r2End,
905706f2543Smrg    short	y1,
906706f2543Smrg    short	y2,
907706f2543Smrg    Bool	*pOverlap)
908706f2543Smrg{
909706f2543Smrg    BoxPtr     pNextRect;
910706f2543Smrg    int        x1;     /* left and right side of current union */
911706f2543Smrg    int        x2;
912706f2543Smrg
913706f2543Smrg    assert (y1 < y2);
914706f2543Smrg    assert(r1 != r1End && r2 != r2End);
915706f2543Smrg
916706f2543Smrg    pNextRect = RegionTop(pReg);
917706f2543Smrg
918706f2543Smrg    /* Start off current rectangle */
919706f2543Smrg    if (r1->x1 < r2->x1)
920706f2543Smrg    {
921706f2543Smrg	x1 = r1->x1;
922706f2543Smrg	x2 = r1->x2;
923706f2543Smrg	r1++;
924706f2543Smrg    }
925706f2543Smrg    else
926706f2543Smrg    {
927706f2543Smrg	x1 = r2->x1;
928706f2543Smrg	x2 = r2->x2;
929706f2543Smrg	r2++;
930706f2543Smrg    }
931706f2543Smrg    while (r1 != r1End && r2 != r2End)
932706f2543Smrg    {
933706f2543Smrg	if (r1->x1 < r2->x1) MERGERECT(r1) else MERGERECT(r2);
934706f2543Smrg    }
935706f2543Smrg
936706f2543Smrg    /* Finish off whoever (if any) is left */
937706f2543Smrg    if (r1 != r1End)
938706f2543Smrg    {
939706f2543Smrg	do
940706f2543Smrg	{
941706f2543Smrg	    MERGERECT(r1);
942706f2543Smrg	} while (r1 != r1End);
943706f2543Smrg    }
944706f2543Smrg    else if (r2 != r2End)
945706f2543Smrg    {
946706f2543Smrg	do
947706f2543Smrg	{
948706f2543Smrg	    MERGERECT(r2);
949706f2543Smrg	} while (r2 != r2End);
950706f2543Smrg    }
951706f2543Smrg
952706f2543Smrg    /* Add current rectangle */
953706f2543Smrg    NEWRECT(pReg, pNextRect, x1, y1, x2, y2);
954706f2543Smrg
955706f2543Smrg    return TRUE;
956706f2543Smrg}
957706f2543Smrg
958706f2543Smrg/*======================================================================
959706f2543Smrg *	    Batch Rectangle Union
960706f2543Smrg *====================================================================*/
961706f2543Smrg
962706f2543Smrg/*-
963706f2543Smrg *-----------------------------------------------------------------------
964706f2543Smrg * RegionAppend --
965706f2543Smrg *
966706f2543Smrg *      "Append" the rgn rectangles onto the end of dstrgn, maintaining
967706f2543Smrg *      knowledge of YX-banding when it's easy.  Otherwise, dstrgn just
968706f2543Smrg *      becomes a non-y-x-banded random collection of rectangles, and not
969706f2543Smrg *      yet a true region.  After a sequence of appends, the caller must
970706f2543Smrg *      call RegionValidate to ensure that a valid region is constructed.
971706f2543Smrg *
972706f2543Smrg * Results:
973706f2543Smrg *	TRUE if successful.
974706f2543Smrg *
975706f2543Smrg * Side Effects:
976706f2543Smrg *      dstrgn is modified if rgn has rectangles.
977706f2543Smrg *
978706f2543Smrg */
979706f2543SmrgBool
980706f2543SmrgRegionAppend(RegionPtr dstrgn, RegionPtr rgn)
981706f2543Smrg{
982706f2543Smrg    int numRects, dnumRects, size;
983706f2543Smrg    BoxPtr new, old;
984706f2543Smrg    Bool prepend;
985706f2543Smrg
986706f2543Smrg    if (RegionNar(rgn))
987706f2543Smrg	return RegionBreak (dstrgn);
988706f2543Smrg
989706f2543Smrg    if (!rgn->data && (dstrgn->data == &RegionEmptyData))
990706f2543Smrg    {
991706f2543Smrg	dstrgn->extents = rgn->extents;
992706f2543Smrg	dstrgn->data = NULL;
993706f2543Smrg	return TRUE;
994706f2543Smrg    }
995706f2543Smrg
996706f2543Smrg    numRects = RegionNumRects(rgn);
997706f2543Smrg    if (!numRects)
998706f2543Smrg	return TRUE;
999706f2543Smrg    prepend = FALSE;
1000706f2543Smrg    size = numRects;
1001706f2543Smrg    dnumRects = RegionNumRects(dstrgn);
1002706f2543Smrg    if (!dnumRects && (size < 200))
1003706f2543Smrg	size = 200; /* XXX pick numbers out of a hat */
1004706f2543Smrg    RECTALLOC(dstrgn, size);
1005706f2543Smrg    old = RegionRects(rgn);
1006706f2543Smrg    if (!dnumRects)
1007706f2543Smrg	dstrgn->extents = rgn->extents;
1008706f2543Smrg    else if (dstrgn->extents.x2 > dstrgn->extents.x1)
1009706f2543Smrg    {
1010706f2543Smrg	BoxPtr first, last;
1011706f2543Smrg
1012706f2543Smrg	first = old;
1013706f2543Smrg	last = RegionBoxptr(dstrgn) + (dnumRects - 1);
1014706f2543Smrg	if ((first->y1 > last->y2) ||
1015706f2543Smrg	    ((first->y1 == last->y1) && (first->y2 == last->y2) &&
1016706f2543Smrg	     (first->x1 > last->x2)))
1017706f2543Smrg	{
1018706f2543Smrg	    if (rgn->extents.x1 < dstrgn->extents.x1)
1019706f2543Smrg		dstrgn->extents.x1 = rgn->extents.x1;
1020706f2543Smrg	    if (rgn->extents.x2 > dstrgn->extents.x2)
1021706f2543Smrg		dstrgn->extents.x2 = rgn->extents.x2;
1022706f2543Smrg	    dstrgn->extents.y2 = rgn->extents.y2;
1023706f2543Smrg	}
1024706f2543Smrg	else
1025706f2543Smrg	{
1026706f2543Smrg	    first = RegionBoxptr(dstrgn);
1027706f2543Smrg	    last = old + (numRects - 1);
1028706f2543Smrg	    if ((first->y1 > last->y2) ||
1029706f2543Smrg		((first->y1 == last->y1) && (first->y2 == last->y2) &&
1030706f2543Smrg		 (first->x1 > last->x2)))
1031706f2543Smrg	    {
1032706f2543Smrg		prepend = TRUE;
1033706f2543Smrg		if (rgn->extents.x1 < dstrgn->extents.x1)
1034706f2543Smrg		    dstrgn->extents.x1 = rgn->extents.x1;
1035706f2543Smrg		if (rgn->extents.x2 > dstrgn->extents.x2)
1036706f2543Smrg		    dstrgn->extents.x2 = rgn->extents.x2;
1037706f2543Smrg		dstrgn->extents.y1 = rgn->extents.y1;
1038706f2543Smrg	    }
1039706f2543Smrg	    else
1040706f2543Smrg		dstrgn->extents.x2 = dstrgn->extents.x1;
1041706f2543Smrg	}
1042706f2543Smrg    }
1043706f2543Smrg    if (prepend)
1044706f2543Smrg    {
1045706f2543Smrg	new = RegionBox(dstrgn, numRects);
1046706f2543Smrg	if (dnumRects == 1)
1047706f2543Smrg	    *new = *RegionBoxptr(dstrgn);
1048706f2543Smrg	else
1049706f2543Smrg	    memmove((char *)new,(char *)RegionBoxptr(dstrgn),
1050706f2543Smrg		  dnumRects * sizeof(BoxRec));
1051706f2543Smrg	new = RegionBoxptr(dstrgn);
1052706f2543Smrg    }
1053706f2543Smrg    else
1054706f2543Smrg	new = RegionBoxptr(dstrgn) + dnumRects;
1055706f2543Smrg    if (numRects == 1)
1056706f2543Smrg	*new = *old;
1057706f2543Smrg    else
1058706f2543Smrg	memmove((char *)new, (char *)old, numRects * sizeof(BoxRec));
1059706f2543Smrg    dstrgn->data->numRects += numRects;
1060706f2543Smrg    return TRUE;
1061706f2543Smrg}
1062706f2543Smrg
1063706f2543Smrg
1064706f2543Smrg#define ExchangeRects(a, b) \
1065706f2543Smrg{			    \
1066706f2543Smrg    BoxRec     t;	    \
1067706f2543Smrg    t = rects[a];	    \
1068706f2543Smrg    rects[a] = rects[b];    \
1069706f2543Smrg    rects[b] = t;	    \
1070706f2543Smrg}
1071706f2543Smrg
1072706f2543Smrgstatic void
1073706f2543SmrgQuickSortRects(
1074706f2543Smrg    BoxRec     rects[],
1075706f2543Smrg    int        numRects)
1076706f2543Smrg{
1077706f2543Smrg    int	y1;
1078706f2543Smrg    int	x1;
1079706f2543Smrg    int        i, j;
1080706f2543Smrg    BoxPtr     r;
1081706f2543Smrg
1082706f2543Smrg    /* Always called with numRects > 1 */
1083706f2543Smrg
1084706f2543Smrg    do
1085706f2543Smrg    {
1086706f2543Smrg	if (numRects == 2)
1087706f2543Smrg	{
1088706f2543Smrg	    if (rects[0].y1 > rects[1].y1 ||
1089706f2543Smrg		    (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1))
1090706f2543Smrg		ExchangeRects(0, 1);
1091706f2543Smrg	    return;
1092706f2543Smrg	}
1093706f2543Smrg
1094706f2543Smrg	/* Choose partition element, stick in location 0 */
1095706f2543Smrg        ExchangeRects(0, numRects >> 1);
1096706f2543Smrg	y1 = rects[0].y1;
1097706f2543Smrg	x1 = rects[0].x1;
1098706f2543Smrg
1099706f2543Smrg        /* Partition array */
1100706f2543Smrg        i = 0;
1101706f2543Smrg        j = numRects;
1102706f2543Smrg        do
1103706f2543Smrg	{
1104706f2543Smrg	    r = &(rects[i]);
1105706f2543Smrg	    do
1106706f2543Smrg	    {
1107706f2543Smrg		r++;
1108706f2543Smrg		i++;
1109706f2543Smrg            } while (i != numRects &&
1110706f2543Smrg		     (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1)));
1111706f2543Smrg	    r = &(rects[j]);
1112706f2543Smrg	    do
1113706f2543Smrg	    {
1114706f2543Smrg		r--;
1115706f2543Smrg		j--;
1116706f2543Smrg            } while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1));
1117706f2543Smrg            if (i < j)
1118706f2543Smrg		ExchangeRects(i, j);
1119706f2543Smrg        } while (i < j);
1120706f2543Smrg
1121706f2543Smrg        /* Move partition element back to middle */
1122706f2543Smrg        ExchangeRects(0, j);
1123706f2543Smrg
1124706f2543Smrg	/* Recurse */
1125706f2543Smrg        if (numRects-j-1 > 1)
1126706f2543Smrg	    QuickSortRects(&rects[j+1], numRects-j-1);
1127706f2543Smrg        numRects = j;
1128706f2543Smrg    } while (numRects > 1);
1129706f2543Smrg}
1130706f2543Smrg
1131706f2543Smrg/*-
1132706f2543Smrg *-----------------------------------------------------------------------
1133706f2543Smrg * RegionValidate --
1134706f2543Smrg *
1135706f2543Smrg *      Take a ``region'' which is a non-y-x-banded random collection of
1136706f2543Smrg *      rectangles, and compute a nice region which is the union of all the
1137706f2543Smrg *      rectangles.
1138706f2543Smrg *
1139706f2543Smrg * Results:
1140706f2543Smrg *	TRUE if successful.
1141706f2543Smrg *
1142706f2543Smrg * Side Effects:
1143706f2543Smrg *      The passed-in ``region'' may be modified.
1144706f2543Smrg *	pOverlap set to TRUE if any retangles overlapped, else FALSE;
1145706f2543Smrg *
1146706f2543Smrg * Strategy:
1147706f2543Smrg *      Step 1. Sort the rectangles into ascending order with primary key y1
1148706f2543Smrg *		and secondary key x1.
1149706f2543Smrg *
1150706f2543Smrg *      Step 2. Split the rectangles into the minimum number of proper y-x
1151706f2543Smrg *		banded regions.  This may require horizontally merging
1152706f2543Smrg *		rectangles, and vertically coalescing bands.  With any luck,
1153706f2543Smrg *		this step in an identity tranformation (ala the Box widget),
1154706f2543Smrg *		or a coalescing into 1 box (ala Menus).
1155706f2543Smrg *
1156706f2543Smrg *	Step 3. Merge the separate regions down to a single region by calling
1157706f2543Smrg *		Union.  Maximize the work each Union call does by using
1158706f2543Smrg *		a binary merge.
1159706f2543Smrg *
1160706f2543Smrg *-----------------------------------------------------------------------
1161706f2543Smrg */
1162706f2543Smrg
1163706f2543SmrgBool
1164706f2543SmrgRegionValidate(RegionPtr badreg, Bool *pOverlap)
1165706f2543Smrg{
1166706f2543Smrg    /* Descriptor for regions under construction  in Step 2. */
1167706f2543Smrg    typedef struct {
1168706f2543Smrg	RegionRec   reg;
1169706f2543Smrg	int	    prevBand;
1170706f2543Smrg	int	    curBand;
1171706f2543Smrg    } RegionInfo;
1172706f2543Smrg
1173706f2543Smrg    int	numRects;   /* Original numRects for badreg	    */
1174706f2543Smrg    RegionInfo *ri;	    /* Array of current regions		    */
1175706f2543Smrg    int	numRI;      /* Number of entries used in ri	    */
1176706f2543Smrg    int	sizeRI;	    /* Number of entries available in ri    */
1177706f2543Smrg    int	i;	    /* Index into rects			    */
1178706f2543Smrg    int	j;	    /* Index into ri			    */
1179706f2543Smrg    RegionInfo *rit;       /* &ri[j]				    */
1180706f2543Smrg    RegionPtr  reg;        /* ri[j].reg			    */
1181706f2543Smrg    BoxPtr	box;	    /* Current box in rects		    */
1182706f2543Smrg    BoxPtr	riBox;      /* Last box in ri[j].reg		    */
1183706f2543Smrg    RegionPtr  hreg;       /* ri[j_half].reg			    */
1184706f2543Smrg    Bool		ret = TRUE;
1185706f2543Smrg
1186706f2543Smrg    *pOverlap = FALSE;
1187706f2543Smrg    if (!badreg->data)
1188706f2543Smrg    {
1189706f2543Smrg	good(badreg);
1190706f2543Smrg	return TRUE;
1191706f2543Smrg    }
1192706f2543Smrg    numRects = badreg->data->numRects;
1193706f2543Smrg    if (!numRects)
1194706f2543Smrg    {
1195706f2543Smrg	if (RegionNar(badreg))
1196706f2543Smrg	    return FALSE;
1197706f2543Smrg	good(badreg);
1198706f2543Smrg	return TRUE;
1199706f2543Smrg    }
1200706f2543Smrg    if (badreg->extents.x1 < badreg->extents.x2)
1201706f2543Smrg    {
1202706f2543Smrg	if ((numRects) == 1)
1203706f2543Smrg	{
1204706f2543Smrg	    xfreeData(badreg);
1205706f2543Smrg	    badreg->data = (RegDataPtr) NULL;
1206706f2543Smrg	}
1207706f2543Smrg	else
1208706f2543Smrg	{
1209706f2543Smrg	    DOWNSIZE(badreg, numRects);
1210706f2543Smrg	}
1211706f2543Smrg	good(badreg);
1212706f2543Smrg	return TRUE;
1213706f2543Smrg    }
1214706f2543Smrg
1215706f2543Smrg    /* Step 1: Sort the rects array into ascending (y1, x1) order */
1216706f2543Smrg    QuickSortRects(RegionBoxptr(badreg), numRects);
1217706f2543Smrg
1218706f2543Smrg    /* Step 2: Scatter the sorted array into the minimum number of regions */
1219706f2543Smrg
1220706f2543Smrg    /* Set up the first region to be the first rectangle in badreg */
1221706f2543Smrg    /* Note that step 2 code will never overflow the ri[0].reg rects array */
1222706f2543Smrg    ri = (RegionInfo *) malloc(4 * sizeof(RegionInfo));
1223706f2543Smrg    if (!ri)
1224706f2543Smrg	return RegionBreak (badreg);
1225706f2543Smrg    sizeRI = 4;
1226706f2543Smrg    numRI = 1;
1227706f2543Smrg    ri[0].prevBand = 0;
1228706f2543Smrg    ri[0].curBand = 0;
1229706f2543Smrg    ri[0].reg = *badreg;
1230706f2543Smrg    box = RegionBoxptr(&ri[0].reg);
1231706f2543Smrg    ri[0].reg.extents = *box;
1232706f2543Smrg    ri[0].reg.data->numRects = 1;
1233706f2543Smrg
1234706f2543Smrg    /* Now scatter rectangles into the minimum set of valid regions.  If the
1235706f2543Smrg       next rectangle to be added to a region would force an existing rectangle
1236706f2543Smrg       in the region to be split up in order to maintain y-x banding, just
1237706f2543Smrg       forget it.  Try the next region.  If it doesn't fit cleanly into any
1238706f2543Smrg       region, make a new one. */
1239706f2543Smrg
1240706f2543Smrg    for (i = numRects; --i > 0;)
1241706f2543Smrg    {
1242706f2543Smrg	box++;
1243706f2543Smrg	/* Look for a region to append box to */
1244706f2543Smrg	for (j = numRI, rit = ri; --j >= 0; rit++)
1245706f2543Smrg	{
1246706f2543Smrg	    reg = &rit->reg;
1247706f2543Smrg	    riBox = RegionEnd(reg);
1248706f2543Smrg
1249706f2543Smrg	    if (box->y1 == riBox->y1 && box->y2 == riBox->y2)
1250706f2543Smrg	    {
1251706f2543Smrg		/* box is in same band as riBox.  Merge or append it */
1252706f2543Smrg		if (box->x1 <= riBox->x2)
1253706f2543Smrg		{
1254706f2543Smrg		    /* Merge it with riBox */
1255706f2543Smrg		    if (box->x1 < riBox->x2) *pOverlap = TRUE;
1256706f2543Smrg		    if (box->x2 > riBox->x2) riBox->x2 = box->x2;
1257706f2543Smrg		}
1258706f2543Smrg		else
1259706f2543Smrg		{
1260706f2543Smrg		    RECTALLOC_BAIL(reg, 1, bail);
1261706f2543Smrg		    *RegionTop(reg) = *box;
1262706f2543Smrg		    reg->data->numRects++;
1263706f2543Smrg		}
1264706f2543Smrg		goto NextRect;   /* So sue me */
1265706f2543Smrg	    }
1266706f2543Smrg	    else if (box->y1 >= riBox->y2)
1267706f2543Smrg	    {
1268706f2543Smrg		/* Put box into new band */
1269706f2543Smrg		if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2;
1270706f2543Smrg		if (reg->extents.x1 > box->x1)   reg->extents.x1 = box->x1;
1271706f2543Smrg		Coalesce(reg, rit->prevBand, rit->curBand);
1272706f2543Smrg		rit->curBand = reg->data->numRects;
1273706f2543Smrg		RECTALLOC_BAIL(reg, 1, bail);
1274706f2543Smrg		*RegionTop(reg) = *box;
1275706f2543Smrg		reg->data->numRects++;
1276706f2543Smrg		goto NextRect;
1277706f2543Smrg	    }
1278706f2543Smrg	    /* Well, this region was inappropriate.  Try the next one. */
1279706f2543Smrg	} /* for j */
1280706f2543Smrg
1281706f2543Smrg	/* Uh-oh.  No regions were appropriate.  Create a new one. */
1282706f2543Smrg	if (sizeRI == numRI)
1283706f2543Smrg	{
1284706f2543Smrg	    /* Oops, allocate space for new region information */
1285706f2543Smrg	    sizeRI <<= 1;
1286706f2543Smrg	    rit = (RegionInfo *) realloc(ri, sizeRI * sizeof(RegionInfo));
1287706f2543Smrg	    if (!rit)
1288706f2543Smrg		goto bail;
1289706f2543Smrg	    ri = rit;
1290706f2543Smrg	    rit = &ri[numRI];
1291706f2543Smrg	}
1292706f2543Smrg	numRI++;
1293706f2543Smrg	rit->prevBand = 0;
1294706f2543Smrg	rit->curBand = 0;
1295706f2543Smrg	rit->reg.extents = *box;
1296706f2543Smrg	rit->reg.data = NULL;
1297706f2543Smrg	if (!RegionRectAlloc(&rit->reg, (i+numRI) / numRI)) /* MUST force allocation */
1298706f2543Smrg	    goto bail;
1299706f2543SmrgNextRect: ;
1300706f2543Smrg    } /* for i */
1301706f2543Smrg
1302706f2543Smrg    /* Make a final pass over each region in order to Coalesce and set
1303706f2543Smrg       extents.x2 and extents.y2 */
1304706f2543Smrg
1305706f2543Smrg    for (j = numRI, rit = ri; --j >= 0; rit++)
1306706f2543Smrg    {
1307706f2543Smrg	reg = &rit->reg;
1308706f2543Smrg	riBox = RegionEnd(reg);
1309706f2543Smrg	reg->extents.y2 = riBox->y2;
1310706f2543Smrg	if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2;
1311706f2543Smrg	Coalesce(reg, rit->prevBand, rit->curBand);
1312706f2543Smrg	if (reg->data->numRects == 1) /* keep unions happy below */
1313706f2543Smrg	{
1314706f2543Smrg	    xfreeData(reg);
1315706f2543Smrg	    reg->data = NULL;
1316706f2543Smrg	}
1317706f2543Smrg    }
1318706f2543Smrg
1319706f2543Smrg    /* Step 3: Union all regions into a single region */
1320706f2543Smrg    while (numRI > 1)
1321706f2543Smrg    {
1322706f2543Smrg	int half = numRI/2;
1323706f2543Smrg	for (j = numRI & 1; j < (half + (numRI & 1)); j++)
1324706f2543Smrg	{
1325706f2543Smrg	    reg = &ri[j].reg;
1326706f2543Smrg	    hreg = &ri[j+half].reg;
1327706f2543Smrg	    if (!RegionOp(reg, reg, hreg, RegionUnionO, TRUE, TRUE, pOverlap))
1328706f2543Smrg		ret = FALSE;
1329706f2543Smrg	    if (hreg->extents.x1 < reg->extents.x1)
1330706f2543Smrg		reg->extents.x1 = hreg->extents.x1;
1331706f2543Smrg	    if (hreg->extents.y1 < reg->extents.y1)
1332706f2543Smrg		reg->extents.y1 = hreg->extents.y1;
1333706f2543Smrg	    if (hreg->extents.x2 > reg->extents.x2)
1334706f2543Smrg		reg->extents.x2 = hreg->extents.x2;
1335706f2543Smrg	    if (hreg->extents.y2 > reg->extents.y2)
1336706f2543Smrg		reg->extents.y2 = hreg->extents.y2;
1337706f2543Smrg	    xfreeData(hreg);
1338706f2543Smrg	}
1339706f2543Smrg	numRI -= half;
1340706f2543Smrg    }
1341706f2543Smrg    *badreg = ri[0].reg;
1342706f2543Smrg    free(ri);
1343706f2543Smrg    good(badreg);
1344706f2543Smrg    return ret;
1345706f2543Smrgbail:
1346706f2543Smrg    for (i = 0; i < numRI; i++)
1347706f2543Smrg	xfreeData(&ri[i].reg);
1348706f2543Smrg    free(ri);
1349706f2543Smrg    return RegionBreak (badreg);
1350706f2543Smrg}
1351706f2543Smrg
1352706f2543SmrgRegionPtr
1353706f2543SmrgRegionFromRects(int nrects, xRectangle *prect, int ctype)
1354706f2543Smrg{
1355706f2543Smrg
1356706f2543Smrg    RegionPtr		pRgn;
1357706f2543Smrg    size_t		rgnSize;
1358706f2543Smrg    RegDataPtr		pData;
1359706f2543Smrg    BoxPtr		pBox;
1360706f2543Smrg    int        		i;
1361706f2543Smrg    int			x1, y1, x2, y2;
1362706f2543Smrg
1363706f2543Smrg    pRgn = RegionCreate(NullBox, 0);
1364706f2543Smrg    if (RegionNar (pRgn))
1365706f2543Smrg	return pRgn;
1366706f2543Smrg    if (!nrects)
1367706f2543Smrg	return pRgn;
1368706f2543Smrg    if (nrects == 1)
1369706f2543Smrg    {
1370706f2543Smrg	x1 = prect->x;
1371706f2543Smrg	y1 = prect->y;
1372706f2543Smrg	if ((x2 = x1 + (int) prect->width) > MAXSHORT)
1373706f2543Smrg	    x2 = MAXSHORT;
1374706f2543Smrg	if ((y2 = y1 + (int) prect->height) > MAXSHORT)
1375706f2543Smrg	    y2 = MAXSHORT;
1376706f2543Smrg	if (x1 != x2 && y1 != y2)
1377706f2543Smrg	{
1378706f2543Smrg	    pRgn->extents.x1 = x1;
1379706f2543Smrg	    pRgn->extents.y1 = y1;
1380706f2543Smrg	    pRgn->extents.x2 = x2;
1381706f2543Smrg	    pRgn->extents.y2 = y2;
1382706f2543Smrg	    pRgn->data = NULL;
1383706f2543Smrg	}
1384706f2543Smrg	return pRgn;
1385706f2543Smrg    }
1386706f2543Smrg    rgnSize = RegionSizeof(nrects);
1387706f2543Smrg    pData = (rgnSize > 0) ? malloc(rgnSize) : NULL;
1388706f2543Smrg    if (!pData)
1389706f2543Smrg    {
1390706f2543Smrg	RegionBreak (pRgn);
1391706f2543Smrg	return pRgn;
1392706f2543Smrg    }
1393706f2543Smrg    pBox = (BoxPtr) (pData + 1);
1394706f2543Smrg    for (i = nrects; --i >= 0; prect++)
1395706f2543Smrg    {
1396706f2543Smrg	x1 = prect->x;
1397706f2543Smrg	y1 = prect->y;
1398706f2543Smrg	if ((x2 = x1 + (int) prect->width) > MAXSHORT)
1399706f2543Smrg	    x2 = MAXSHORT;
1400706f2543Smrg	if ((y2 = y1 + (int) prect->height) > MAXSHORT)
1401706f2543Smrg	    y2 = MAXSHORT;
1402706f2543Smrg	if (x1 != x2 && y1 != y2)
1403706f2543Smrg	{
1404706f2543Smrg	    pBox->x1 = x1;
1405706f2543Smrg	    pBox->y1 = y1;
1406706f2543Smrg	    pBox->x2 = x2;
1407706f2543Smrg	    pBox->y2 = y2;
1408706f2543Smrg	    pBox++;
1409706f2543Smrg	}
1410706f2543Smrg    }
1411706f2543Smrg    if (pBox != (BoxPtr) (pData + 1))
1412706f2543Smrg    {
1413706f2543Smrg	pData->size = nrects;
1414706f2543Smrg	pData->numRects = pBox - (BoxPtr) (pData + 1);
1415706f2543Smrg    	pRgn->data = pData;
1416706f2543Smrg    	if (ctype != CT_YXBANDED)
1417706f2543Smrg    	{
1418706f2543Smrg	    Bool overlap; /* result ignored */
1419706f2543Smrg	    pRgn->extents.x1 = pRgn->extents.x2 = 0;
1420706f2543Smrg	    RegionValidate(pRgn, &overlap);
1421706f2543Smrg    	}
1422706f2543Smrg    	else
1423706f2543Smrg	    RegionSetExtents(pRgn);
1424706f2543Smrg    	good(pRgn);
1425706f2543Smrg    }
1426706f2543Smrg    else
1427706f2543Smrg    {
1428706f2543Smrg	free(pData);
1429706f2543Smrg    }
1430706f2543Smrg    return pRgn;
1431706f2543Smrg}
1432706f2543Smrg
1433706f2543Smrg#define ExchangeSpans(a, b)				    \
1434706f2543Smrg{							    \
1435706f2543Smrg    DDXPointRec	tpt;				    	    \
1436706f2543Smrg    int    	tw;					    \
1437706f2543Smrg							    \
1438706f2543Smrg    tpt = spans[a]; spans[a] = spans[b]; spans[b] = tpt;    \
1439706f2543Smrg    tw = widths[a]; widths[a] = widths[b]; widths[b] = tw;  \
1440706f2543Smrg}
1441706f2543Smrg
1442706f2543Smrg/* ||| I should apply the merge sort code to rectangle sorting above, and see
1443706f2543Smrg   if mapping time can be improved.  But right now I've been at work 12 hours,
1444706f2543Smrg   so forget it.
1445706f2543Smrg*/
1446706f2543Smrg
1447706f2543Smrgstatic void QuickSortSpans(
1448706f2543Smrg    DDXPointRec spans[],
1449706f2543Smrg    int	    	widths[],
1450706f2543Smrg    int	    	numSpans)
1451706f2543Smrg{
1452706f2543Smrg    int	    y;
1453706f2543Smrg    int	    i, j, m;
1454706f2543Smrg    DDXPointPtr    r;
1455706f2543Smrg
1456706f2543Smrg    /* Always called with numSpans > 1 */
1457706f2543Smrg    /* Sorts only by y, doesn't bother to sort by x */
1458706f2543Smrg
1459706f2543Smrg    do
1460706f2543Smrg    {
1461706f2543Smrg	if (numSpans < 9)
1462706f2543Smrg	{
1463706f2543Smrg	    /* Do insertion sort */
1464706f2543Smrg	    int yprev;
1465706f2543Smrg
1466706f2543Smrg	    yprev = spans[0].y;
1467706f2543Smrg	    i = 1;
1468706f2543Smrg	    do
1469706f2543Smrg	    { /* while i != numSpans */
1470706f2543Smrg		y = spans[i].y;
1471706f2543Smrg		if (yprev > y)
1472706f2543Smrg		{
1473706f2543Smrg		    /* spans[i] is out of order.  Move into proper location. */
1474706f2543Smrg		    DDXPointRec tpt;
1475706f2543Smrg		    int	    tw, k;
1476706f2543Smrg
1477706f2543Smrg		    for (j = 0; y >= spans[j].y; j++) {}
1478706f2543Smrg		    tpt = spans[i];
1479706f2543Smrg		    tw  = widths[i];
1480706f2543Smrg		    for (k = i; k != j; k--)
1481706f2543Smrg		    {
1482706f2543Smrg			spans[k] = spans[k-1];
1483706f2543Smrg			widths[k] = widths[k-1];
1484706f2543Smrg		    }
1485706f2543Smrg		    spans[j] = tpt;
1486706f2543Smrg		    widths[j] = tw;
1487706f2543Smrg		    y = spans[i].y;
1488706f2543Smrg		} /* if out of order */
1489706f2543Smrg		yprev = y;
1490706f2543Smrg		i++;
1491706f2543Smrg	    } while (i != numSpans);
1492706f2543Smrg	    return;
1493706f2543Smrg	}
1494706f2543Smrg
1495706f2543Smrg	/* Choose partition element, stick in location 0 */
1496706f2543Smrg	m = numSpans / 2;
1497706f2543Smrg	if (spans[m].y > spans[0].y)		ExchangeSpans(m, 0);
1498706f2543Smrg	if (spans[m].y > spans[numSpans-1].y)   ExchangeSpans(m, numSpans-1);
1499706f2543Smrg	if (spans[m].y > spans[0].y)		ExchangeSpans(m, 0);
1500706f2543Smrg	y = spans[0].y;
1501706f2543Smrg
1502706f2543Smrg        /* Partition array */
1503706f2543Smrg        i = 0;
1504706f2543Smrg        j = numSpans;
1505706f2543Smrg        do
1506706f2543Smrg	{
1507706f2543Smrg	    r = &(spans[i]);
1508706f2543Smrg	    do
1509706f2543Smrg	    {
1510706f2543Smrg		r++;
1511706f2543Smrg		i++;
1512706f2543Smrg            } while (i != numSpans && r->y < y);
1513706f2543Smrg	    r = &(spans[j]);
1514706f2543Smrg	    do
1515706f2543Smrg	    {
1516706f2543Smrg		r--;
1517706f2543Smrg		j--;
1518706f2543Smrg            } while (y < r->y);
1519706f2543Smrg            if (i < j)
1520706f2543Smrg		ExchangeSpans(i, j);
1521706f2543Smrg        } while (i < j);
1522706f2543Smrg
1523706f2543Smrg        /* Move partition element back to middle */
1524706f2543Smrg        ExchangeSpans(0, j);
1525706f2543Smrg
1526706f2543Smrg	/* Recurse */
1527706f2543Smrg        if (numSpans-j-1 > 1)
1528706f2543Smrg	    QuickSortSpans(&spans[j+1], &widths[j+1], numSpans-j-1);
1529706f2543Smrg        numSpans = j;
1530706f2543Smrg    } while (numSpans > 1);
1531706f2543Smrg}
1532706f2543Smrg
1533706f2543Smrg#define NextBand()						    \
1534706f2543Smrg{								    \
1535706f2543Smrg    clipy1 = pboxBandStart->y1;					    \
1536706f2543Smrg    clipy2 = pboxBandStart->y2;					    \
1537706f2543Smrg    pboxBandEnd = pboxBandStart + 1;				    \
1538706f2543Smrg    while (pboxBandEnd != pboxLast && pboxBandEnd->y1 == clipy1) {  \
1539706f2543Smrg	pboxBandEnd++;						    \
1540706f2543Smrg    }								    \
1541706f2543Smrg    for (; ppt != pptLast && ppt->y < clipy1; ppt++, pwidth++) {} \
1542706f2543Smrg}
1543706f2543Smrg
1544706f2543Smrg/*
1545706f2543Smrg    Clip a list of scanlines to a region.  The caller has allocated the
1546706f2543Smrg    space.  FSorted is non-zero if the scanline origins are in ascending
1547706f2543Smrg    order.
1548706f2543Smrg    returns the number of new, clipped scanlines.
1549706f2543Smrg*/
1550706f2543Smrg
1551706f2543Smrgint
1552706f2543SmrgRegionClipSpans(
1553706f2543Smrg    RegionPtr	prgnDst,
1554706f2543Smrg    DDXPointPtr ppt,
1555706f2543Smrg    int	    	*pwidth,
1556706f2543Smrg    int		nspans,
1557706f2543Smrg    DDXPointPtr	pptNew,
1558706f2543Smrg    int		*pwidthNew,
1559706f2543Smrg    int		fSorted)
1560706f2543Smrg{
1561706f2543Smrg    DDXPointPtr pptLast;
1562706f2543Smrg    int	*pwidthNewStart;	/* the vengeance of Xerox! */
1563706f2543Smrg    int	y, x1, x2;
1564706f2543Smrg    int	numRects;
1565706f2543Smrg
1566706f2543Smrg    good(prgnDst);
1567706f2543Smrg    pptLast = ppt + nspans;
1568706f2543Smrg    pwidthNewStart = pwidthNew;
1569706f2543Smrg
1570706f2543Smrg    if (!prgnDst->data)
1571706f2543Smrg    {
1572706f2543Smrg	/* Do special fast code with clip boundaries in registers(?) */
1573706f2543Smrg	/* It doesn't pay much to make use of fSorted in this case,
1574706f2543Smrg	   so we lump everything together. */
1575706f2543Smrg
1576706f2543Smrg	int clipx1, clipx2, clipy1, clipy2;
1577706f2543Smrg
1578706f2543Smrg	clipx1 = prgnDst->extents.x1;
1579706f2543Smrg	clipy1 = prgnDst->extents.y1;
1580706f2543Smrg	clipx2 = prgnDst->extents.x2;
1581706f2543Smrg	clipy2 = prgnDst->extents.y2;
1582706f2543Smrg
1583706f2543Smrg	for (; ppt != pptLast; ppt++, pwidth++)
1584706f2543Smrg	{
1585706f2543Smrg	    y = ppt->y;
1586706f2543Smrg	    x1 = ppt->x;
1587706f2543Smrg	    if (clipy1 <= y && y < clipy2)
1588706f2543Smrg	    {
1589706f2543Smrg		x2 = x1 + *pwidth;
1590706f2543Smrg		if (x1 < clipx1)    x1 = clipx1;
1591706f2543Smrg		if (x2 > clipx2)    x2 = clipx2;
1592706f2543Smrg		if (x1 < x2)
1593706f2543Smrg		{
1594706f2543Smrg		    /* part of span in clip rectangle */
1595706f2543Smrg		    pptNew->x = x1;
1596706f2543Smrg		    pptNew->y = y;
1597706f2543Smrg		    *pwidthNew = x2 - x1;
1598706f2543Smrg		    pptNew++;
1599706f2543Smrg		    pwidthNew++;
1600706f2543Smrg		}
1601706f2543Smrg	    }
1602706f2543Smrg	} /* end for */
1603706f2543Smrg
1604706f2543Smrg    }
1605706f2543Smrg    else if ((numRects = prgnDst->data->numRects))
1606706f2543Smrg    {
1607706f2543Smrg	/* Have to clip against many boxes */
1608706f2543Smrg	BoxPtr pboxBandStart, pboxBandEnd;
1609706f2543Smrg	BoxPtr pbox;
1610706f2543Smrg	BoxPtr pboxLast;
1611706f2543Smrg	int clipy1, clipy2;
1612706f2543Smrg
1613706f2543Smrg	/* In this case, taking advantage of sorted spans gains more than
1614706f2543Smrg	   the sorting costs. */
1615706f2543Smrg	if ((! fSorted) && (nspans > 1))
1616706f2543Smrg	    QuickSortSpans(ppt, pwidth, nspans);
1617706f2543Smrg
1618706f2543Smrg	pboxBandStart = RegionBoxptr(prgnDst);
1619706f2543Smrg	pboxLast = pboxBandStart + numRects;
1620706f2543Smrg
1621706f2543Smrg	NextBand();
1622706f2543Smrg
1623706f2543Smrg	for (; ppt != pptLast; )
1624706f2543Smrg	{
1625706f2543Smrg	    y = ppt->y;
1626706f2543Smrg	    if (y < clipy2)
1627706f2543Smrg	    {
1628706f2543Smrg		/* span is in the current band */
1629706f2543Smrg		pbox = pboxBandStart;
1630706f2543Smrg		x1 = ppt->x;
1631706f2543Smrg		x2 = x1 + *pwidth;
1632706f2543Smrg		do
1633706f2543Smrg		{ /* For each box in band */
1634706f2543Smrg		    int newx1, newx2;
1635706f2543Smrg
1636706f2543Smrg		    newx1 = x1;
1637706f2543Smrg		    newx2 = x2;
1638706f2543Smrg		    if (newx1 < pbox->x1)   newx1 = pbox->x1;
1639706f2543Smrg		    if (newx2 > pbox->x2)   newx2 = pbox->x2;
1640706f2543Smrg		    if (newx1 < newx2)
1641706f2543Smrg		    {
1642706f2543Smrg			/* Part of span in clip rectangle */
1643706f2543Smrg			pptNew->x = newx1;
1644706f2543Smrg			pptNew->y = y;
1645706f2543Smrg			*pwidthNew = newx2 - newx1;
1646706f2543Smrg			pptNew++;
1647706f2543Smrg			pwidthNew++;
1648706f2543Smrg		    }
1649706f2543Smrg		    pbox++;
1650706f2543Smrg		} while (pbox != pboxBandEnd);
1651706f2543Smrg		ppt++;
1652706f2543Smrg		pwidth++;
1653706f2543Smrg	    }
1654706f2543Smrg	    else
1655706f2543Smrg	    {
1656706f2543Smrg		/* Move to next band, adjust ppt as needed */
1657706f2543Smrg		pboxBandStart = pboxBandEnd;
1658706f2543Smrg		if (pboxBandStart == pboxLast)
1659706f2543Smrg		    break; /* We're completely done */
1660706f2543Smrg		NextBand();
1661706f2543Smrg	    }
1662706f2543Smrg	}
1663706f2543Smrg    }
1664706f2543Smrg    return pwidthNew - pwidthNewStart;
1665706f2543Smrg}
1666