1 /***********************************************************
2
3 Copyright 1987, 1988, 1989, 1998 The Open Group
4
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
24
25
26 Copyright 1987, 1988, 1989 by
27 Digital Equipment Corporation, Maynard, Massachusetts.
28
29 All Rights Reserved
30
31 Permission to use, copy, modify, and distribute this software and its
32 documentation for any purpose and without fee is hereby granted,
33 provided that the above copyright notice appear in all copies and that
34 both that copyright notice and this permission notice appear in
35 supporting documentation, and that the name of Digital not be
36 used in advertising or publicity pertaining to distribution of the
37 software without specific, written prior permission.
38
39 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45 SOFTWARE.
46
47 ******************************************************************/
48
49 /* The panoramix components contained the following notice */
50 /*****************************************************************
51
52 Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts.
53
54 Permission is hereby granted, free of charge, to any person obtaining a copy
55 of this software and associated documentation files (the "Software"), to deal
56 in the Software without restriction, including without limitation the rights
57 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58 copies of the Software.
59
60 The above copyright notice and this permission notice shall be included in
61 all copies or substantial portions of the Software.
62
63 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
66 DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
67 BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
68 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
69 IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
70
71 Except as contained in this notice, the name of Digital Equipment Corporation
72 shall not be used in advertising or otherwise to promote the sale, use or other
73 dealings in this Software without prior written authorization from Digital
74 Equipment 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) \
175 if (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \
176 if (!RegionRectAlloc(pReg, n)) { goto bail; }
177
178 #define RECTALLOC(pReg,n) \
179 if (!(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 #define DOWNSIZE(reg,numRects) \
205 if (((numRects) < ((reg)->data->size >> 1)) && ((reg)->data->size > 50)) \
206 { \
207 size_t NewSize = RegionSizeof(numRects); \
208 RegDataPtr NewData = \
209 (NewSize > 0) ? realloc((reg)->data, NewSize) : NULL ; \
210 if (NewData) \
211 { \
212 NewData->size = (numRects); \
213 (reg)->data = NewData; \
214 } \
215 }
216
217 BoxRec RegionEmptyBox = { 0, 0, 0, 0 };
218 RegDataRec RegionEmptyData = { 0, 0 };
219
220 RegDataRec RegionBrokenData = { 0, 0 };
221 static RegionRec RegionBrokenRegion = { {0, 0, 0, 0}, &RegionBrokenData };
222
223 void
InitRegions(void)224 InitRegions(void)
225 {
226 pixman_region_set_static_pointers(&RegionEmptyBox, &RegionEmptyData,
227 &RegionBrokenData);
228 }
229
230 /*****************************************************************
231 * RegionCreate(rect, size)
232 * This routine does a simple malloc to make a structure of
233 * REGION of "size" number of rectangles.
234 *****************************************************************/
235
236 RegionPtr
RegionCreate(BoxPtr rect,int size)237 RegionCreate(BoxPtr rect, int size)
238 {
239 RegionPtr pReg;
240
241 pReg = (RegionPtr) malloc(sizeof(RegionRec));
242 if (!pReg)
243 return &RegionBrokenRegion;
244
245 RegionInit(pReg, rect, size);
246
247 return pReg;
248 }
249
250 void
RegionDestroy(RegionPtr pReg)251 RegionDestroy(RegionPtr pReg)
252 {
253 pixman_region_fini(pReg);
254 if (pReg != &RegionBrokenRegion)
255 free(pReg);
256 }
257
258 RegionPtr
RegionDuplicate(RegionPtr pOld)259 RegionDuplicate(RegionPtr pOld)
260 {
261 RegionPtr pNew;
262
263 pNew = RegionCreate(&pOld->extents, 0);
264 if (!pNew)
265 return NULL;
266 if (!RegionCopy(pNew, pOld)) {
267 RegionDestroy(pNew);
268 return NULL;
269 }
270 return pNew;
271 }
272
273 void
RegionPrint(RegionPtr rgn)274 RegionPrint(RegionPtr rgn)
275 {
276 int num, size;
277 int i;
278 BoxPtr rects;
279
280 num = RegionNumRects(rgn);
281 size = RegionSize(rgn);
282 rects = RegionRects(rgn);
283 ErrorF("[mi] num: %d size: %d\n", num, size);
284 ErrorF("[mi] extents: %d %d %d %d\n",
285 rgn->extents.x1, rgn->extents.y1, rgn->extents.x2, rgn->extents.y2);
286 for (i = 0; i < num; i++)
287 ErrorF("[mi] %d %d %d %d \n",
288 rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2);
289 ErrorF("[mi] \n");
290 }
291
292 #ifdef DEBUG
293 Bool
RegionIsValid(RegionPtr reg)294 RegionIsValid(RegionPtr reg)
295 {
296 int i, numRects;
297
298 if ((reg->extents.x1 > reg->extents.x2) ||
299 (reg->extents.y1 > reg->extents.y2))
300 return FALSE;
301 numRects = RegionNumRects(reg);
302 if (!numRects)
303 return ((reg->extents.x1 == reg->extents.x2) &&
304 (reg->extents.y1 == reg->extents.y2) &&
305 (reg->data->size || (reg->data == &RegionEmptyData)));
306 else if (numRects == 1)
307 return !reg->data;
308 else {
309 BoxPtr pboxP, pboxN;
310 BoxRec box;
311
312 pboxP = RegionRects(reg);
313 box = *pboxP;
314 box.y2 = pboxP[numRects - 1].y2;
315 pboxN = pboxP + 1;
316 for (i = numRects; --i > 0; pboxP++, pboxN++) {
317 if ((pboxN->x1 >= pboxN->x2) || (pboxN->y1 >= pboxN->y2))
318 return FALSE;
319 if (pboxN->x1 < box.x1)
320 box.x1 = pboxN->x1;
321 if (pboxN->x2 > box.x2)
322 box.x2 = pboxN->x2;
323 if ((pboxN->y1 < pboxP->y1) ||
324 ((pboxN->y1 == pboxP->y1) &&
325 ((pboxN->x1 < pboxP->x2) || (pboxN->y2 != pboxP->y2))))
326 return FALSE;
327 }
328 return ((box.x1 == reg->extents.x1) &&
329 (box.x2 == reg->extents.x2) &&
330 (box.y1 == reg->extents.y1) && (box.y2 == reg->extents.y2));
331 }
332 }
333 #endif /* DEBUG */
334
335 Bool
RegionBreak(RegionPtr pReg)336 RegionBreak(RegionPtr pReg)
337 {
338 xfreeData(pReg);
339 pReg->extents = RegionEmptyBox;
340 pReg->data = &RegionBrokenData;
341 return FALSE;
342 }
343
344 Bool
RegionRectAlloc(RegionPtr pRgn,int n)345 RegionRectAlloc(RegionPtr pRgn, int n)
346 {
347 RegDataPtr data;
348 size_t rgnSize;
349
350 if (!pRgn->data) {
351 n++;
352 rgnSize = RegionSizeof(n);
353 pRgn->data = (rgnSize > 0) ? malloc(rgnSize) : NULL;
354 if (!pRgn->data)
355 return RegionBreak(pRgn);
356 pRgn->data->numRects = 1;
357 *RegionBoxptr(pRgn) = pRgn->extents;
358 }
359 else if (!pRgn->data->size) {
360 rgnSize = RegionSizeof(n);
361 pRgn->data = (rgnSize > 0) ? malloc(rgnSize) : NULL;
362 if (!pRgn->data)
363 return RegionBreak(pRgn);
364 pRgn->data->numRects = 0;
365 }
366 else {
367 if (n == 1) {
368 n = pRgn->data->numRects;
369 if (n > 500) /* XXX pick numbers out of a hat */
370 n = 250;
371 }
372 n += pRgn->data->numRects;
373 rgnSize = RegionSizeof(n);
374 data = (rgnSize > 0) ? realloc(pRgn->data, rgnSize) : NULL;
375 if (!data)
376 return RegionBreak(pRgn);
377 pRgn->data = data;
378 }
379 pRgn->data->size = n;
380 return TRUE;
381 }
382
383 /*======================================================================
384 * Generic Region Operator
385 *====================================================================*/
386
387 /*-
388 *-----------------------------------------------------------------------
389 * RegionCoalesce --
390 * Attempt to merge the boxes in the current band with those in the
391 * previous one. We are guaranteed that the current band extends to
392 * the end of the rects array. Used only by RegionOp.
393 *
394 * Results:
395 * The new index for the previous band.
396 *
397 * Side Effects:
398 * If coalescing takes place:
399 * - rectangles in the previous band will have their y2 fields
400 * altered.
401 * - pReg->data->numRects will be decreased.
402 *
403 *-----------------------------------------------------------------------
404 */
405 _X_INLINE static int
RegionCoalesce(RegionPtr pReg,int prevStart,int curStart)406 RegionCoalesce(RegionPtr pReg, /* Region to coalesce */
407 int prevStart, /* Index of start of previous band */
408 int curStart)
409 { /* Index of start of current band */
410 BoxPtr pPrevBox; /* Current box in previous band */
411 BoxPtr pCurBox; /* Current box in current band */
412 int numRects; /* Number rectangles in both bands */
413 int y2; /* Bottom of current band */
414
415 /*
416 * Figure out how many rectangles are in the band.
417 */
418 numRects = curStart - prevStart;
419 assert(numRects == pReg->data->numRects - curStart);
420
421 if (!numRects)
422 return curStart;
423
424 /*
425 * The bands may only be coalesced if the bottom of the previous
426 * matches the top scanline of the current.
427 */
428 pPrevBox = RegionBox(pReg, prevStart);
429 pCurBox = RegionBox(pReg, curStart);
430 if (pPrevBox->y2 != pCurBox->y1)
431 return curStart;
432
433 /*
434 * Make sure the bands have boxes in the same places. This
435 * assumes that boxes have been added in such a way that they
436 * cover the most area possible. I.e. two boxes in a band must
437 * have some horizontal space between them.
438 */
439 y2 = pCurBox->y2;
440
441 do {
442 if ((pPrevBox->x1 != pCurBox->x1) || (pPrevBox->x2 != pCurBox->x2)) {
443 return curStart;
444 }
445 pPrevBox++;
446 pCurBox++;
447 numRects--;
448 } while (numRects);
449
450 /*
451 * The bands may be merged, so set the bottom y of each box
452 * in the previous band to the bottom y of the current band.
453 */
454 numRects = curStart - prevStart;
455 pReg->data->numRects -= numRects;
456 do {
457 pPrevBox--;
458 pPrevBox->y2 = y2;
459 numRects--;
460 } while (numRects);
461 return prevStart;
462 }
463
464 /* Quicky macro to avoid trivial reject procedure calls to RegionCoalesce */
465
466 #define Coalesce(newReg, prevBand, curBand) \
467 if (curBand - prevBand == newReg->data->numRects - curBand) { \
468 prevBand = RegionCoalesce(newReg, prevBand, curBand); \
469 } else { \
470 prevBand = curBand; \
471 }
472
473 /*-
474 *-----------------------------------------------------------------------
475 * RegionAppendNonO --
476 * Handle a non-overlapping band for the union and subtract operations.
477 * Just adds the (top/bottom-clipped) rectangles into the region.
478 * Doesn't have to check for subsumption or anything.
479 *
480 * Results:
481 * None.
482 *
483 * Side Effects:
484 * pReg->data->numRects is incremented and the rectangles overwritten
485 * with the rectangles we're passed.
486 *
487 *-----------------------------------------------------------------------
488 */
489
490 _X_INLINE static Bool
RegionAppendNonO(RegionPtr pReg,BoxPtr r,BoxPtr rEnd,int y1,int y2)491 RegionAppendNonO(RegionPtr pReg, BoxPtr r, BoxPtr rEnd, int y1, int y2)
492 {
493 BoxPtr pNextRect;
494 int newRects;
495
496 newRects = rEnd - r;
497
498 assert(y1 < y2);
499 assert(newRects != 0);
500
501 /* Make sure we have enough space for all rectangles to be added */
502 RECTALLOC(pReg, newRects);
503 pNextRect = RegionTop(pReg);
504 pReg->data->numRects += newRects;
505 do {
506 assert(r->x1 < r->x2);
507 ADDRECT(pNextRect, r->x1, y1, r->x2, y2);
508 r++;
509 } while (r != rEnd);
510
511 return TRUE;
512 }
513
514 #define FindBand(r, rBandEnd, rEnd, ry1) \
515 { \
516 ry1 = r->y1; \
517 rBandEnd = r+1; \
518 while ((rBandEnd != rEnd) && (rBandEnd->y1 == ry1)) { \
519 rBandEnd++; \
520 } \
521 }
522
523 #define AppendRegions(newReg, r, rEnd) \
524 { \
525 int newRects; \
526 if ((newRects = rEnd - r)) { \
527 RECTALLOC(newReg, newRects); \
528 memmove((char *)RegionTop(newReg),(char *)r, \
529 newRects * sizeof(BoxRec)); \
530 newReg->data->numRects += newRects; \
531 } \
532 }
533
534 /*-
535 *-----------------------------------------------------------------------
536 * RegionOp --
537 * Apply an operation to two regions. Called by RegionUnion, RegionInverse,
538 * RegionSubtract, RegionIntersect.... Both regions MUST have at least one
539 * rectangle, and cannot be the same object.
540 *
541 * Results:
542 * TRUE if successful.
543 *
544 * Side Effects:
545 * The new region is overwritten.
546 * pOverlap set to TRUE if overlapFunc ever returns TRUE.
547 *
548 * Notes:
549 * The idea behind this function is to view the two regions as sets.
550 * Together they cover a rectangle of area that this function divides
551 * into horizontal bands where points are covered only by one region
552 * or by both. For the first case, the nonOverlapFunc is called with
553 * each the band and the band's upper and lower extents. For the
554 * second, the overlapFunc is called to process the entire band. It
555 * is responsible for clipping the rectangles in the band, though
556 * this function provides the boundaries.
557 * At the end of each band, the new region is coalesced, if possible,
558 * to reduce the number of rectangles in the region.
559 *
560 *-----------------------------------------------------------------------
561 */
562
563 typedef Bool (*OverlapProcPtr) (RegionPtr pReg,
564 BoxPtr r1,
565 BoxPtr r1End,
566 BoxPtr r2,
567 BoxPtr r2End,
568 short y1, short y2, Bool *pOverlap);
569
570 static Bool
RegionOp(RegionPtr newReg,RegionPtr reg1,RegionPtr reg2,OverlapProcPtr overlapFunc,Bool appendNon1,Bool appendNon2,Bool * pOverlap)571 RegionOp(RegionPtr newReg, /* Place to store result */
572 RegionPtr reg1, /* First region in operation */
573 RegionPtr reg2, /* 2d region in operation */
574 OverlapProcPtr overlapFunc, /* Function to call for over-
575 * lapping bands */
576 Bool appendNon1, /* Append non-overlapping bands */
577 /* in region 1 ? */
578 Bool appendNon2, /* Append non-overlapping bands */
579 /* in region 2 ? */
580 Bool *pOverlap)
581 {
582 BoxPtr r1; /* Pointer into first region */
583 BoxPtr r2; /* Pointer into 2d region */
584 BoxPtr r1End; /* End of 1st region */
585 BoxPtr r2End; /* End of 2d region */
586 short ybot; /* Bottom of intersection */
587 short ytop; /* Top of intersection */
588 RegDataPtr oldData; /* Old data for newReg */
589 int prevBand; /* Index of start of
590 * previous band in newReg */
591 int curBand; /* Index of start of current
592 * band in newReg */
593 BoxPtr r1BandEnd; /* End of current band in r1 */
594 BoxPtr r2BandEnd; /* End of current band in r2 */
595 short top; /* Top of non-overlapping band */
596 short bot; /* Bottom of non-overlapping band */
597 int r1y1; /* Temps for r1->y1 and r2->y1 */
598 int r2y1;
599 int newSize;
600 int numRects;
601
602 /*
603 * Break any region computed from a broken region
604 */
605 if (RegionNar(reg1) || RegionNar(reg2))
606 return RegionBreak(newReg);
607
608 /*
609 * Initialization:
610 * set r1, r2, r1End and r2End appropriately, save the rectangles
611 * of the destination region until the end in case it's one of
612 * the two source regions, then mark the "new" region empty, allocating
613 * another array of rectangles for it to use.
614 */
615
616 r1 = RegionRects(reg1);
617 newSize = RegionNumRects(reg1);
618 r1End = r1 + newSize;
619 numRects = RegionNumRects(reg2);
620 r2 = RegionRects(reg2);
621 r2End = r2 + numRects;
622 assert(r1 != r1End);
623 assert(r2 != r2End);
624
625 oldData = NULL;
626 if (((newReg == reg1) && (newSize > 1)) ||
627 ((newReg == reg2) && (numRects > 1))) {
628 oldData = newReg->data;
629 newReg->data = &RegionEmptyData;
630 }
631 /* guess at new size */
632 if (numRects > newSize)
633 newSize = numRects;
634 newSize <<= 1;
635 if (!newReg->data)
636 newReg->data = &RegionEmptyData;
637 else if (newReg->data->size)
638 newReg->data->numRects = 0;
639 if (newSize > newReg->data->size)
640 if (!RegionRectAlloc(newReg, newSize))
641 return FALSE;
642
643 /*
644 * Initialize ybot.
645 * In the upcoming loop, ybot and ytop serve different functions depending
646 * on whether the band being handled is an overlapping or non-overlapping
647 * band.
648 * In the case of a non-overlapping band (only one of the regions
649 * has points in the band), ybot is the bottom of the most recent
650 * intersection and thus clips the top of the rectangles in that band.
651 * ytop is the top of the next intersection between the two regions and
652 * serves to clip the bottom of the rectangles in the current band.
653 * For an overlapping band (where the two regions intersect), ytop clips
654 * the top of the rectangles of both regions and ybot clips the bottoms.
655 */
656
657 ybot = min(r1->y1, r2->y1);
658
659 /*
660 * prevBand serves to mark the start of the previous band so rectangles
661 * can be coalesced into larger rectangles. qv. RegionCoalesce, above.
662 * In the beginning, there is no previous band, so prevBand == curBand
663 * (curBand is set later on, of course, but the first band will always
664 * start at index 0). prevBand and curBand must be indices because of
665 * the possible expansion, and resultant moving, of the new region's
666 * array of rectangles.
667 */
668 prevBand = 0;
669
670 do {
671 /*
672 * This algorithm proceeds one source-band (as opposed to a
673 * destination band, which is determined by where the two regions
674 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
675 * rectangle after the last one in the current band for their
676 * respective regions.
677 */
678 assert(r1 != r1End);
679 assert(r2 != r2End);
680
681 FindBand(r1, r1BandEnd, r1End, r1y1);
682 FindBand(r2, r2BandEnd, r2End, r2y1);
683
684 /*
685 * First handle the band that doesn't intersect, if any.
686 *
687 * Note that attention is restricted to one band in the
688 * non-intersecting region at once, so if a region has n
689 * bands between the current position and the next place it overlaps
690 * the other, this entire loop will be passed through n times.
691 */
692 if (r1y1 < r2y1) {
693 if (appendNon1) {
694 top = max(r1y1, ybot);
695 bot = min(r1->y2, r2y1);
696 if (top != bot) {
697 curBand = newReg->data->numRects;
698 RegionAppendNonO(newReg, r1, r1BandEnd, top, bot);
699 Coalesce(newReg, prevBand, curBand);
700 }
701 }
702 ytop = r2y1;
703 }
704 else if (r2y1 < r1y1) {
705 if (appendNon2) {
706 top = max(r2y1, ybot);
707 bot = min(r2->y2, r1y1);
708 if (top != bot) {
709 curBand = newReg->data->numRects;
710 RegionAppendNonO(newReg, r2, r2BandEnd, top, bot);
711 Coalesce(newReg, prevBand, curBand);
712 }
713 }
714 ytop = r1y1;
715 }
716 else {
717 ytop = r1y1;
718 }
719
720 /*
721 * Now see if we've hit an intersecting band. The two bands only
722 * intersect if ybot > ytop
723 */
724 ybot = min(r1->y2, r2->y2);
725 if (ybot > ytop) {
726 curBand = newReg->data->numRects;
727 (*overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot,
728 pOverlap);
729 Coalesce(newReg, prevBand, curBand);
730 }
731
732 /*
733 * If we've finished with a band (y2 == ybot) we skip forward
734 * in the region to the next band.
735 */
736 if (r1->y2 == ybot)
737 r1 = r1BandEnd;
738 if (r2->y2 == ybot)
739 r2 = r2BandEnd;
740
741 } while (r1 != r1End && r2 != r2End);
742
743 /*
744 * Deal with whichever region (if any) still has rectangles left.
745 *
746 * We only need to worry about banding and coalescing for the very first
747 * band left. After that, we can just group all remaining boxes,
748 * regardless of how many bands, into one final append to the list.
749 */
750
751 if ((r1 != r1End) && appendNon1) {
752 /* Do first nonOverlap1Func call, which may be able to coalesce */
753 FindBand(r1, r1BandEnd, r1End, r1y1);
754 curBand = newReg->data->numRects;
755 RegionAppendNonO(newReg, r1, r1BandEnd, max(r1y1, ybot), r1->y2);
756 Coalesce(newReg, prevBand, curBand);
757 /* Just append the rest of the boxes */
758 AppendRegions(newReg, r1BandEnd, r1End);
759
760 }
761 else if ((r2 != r2End) && appendNon2) {
762 /* Do first nonOverlap2Func call, which may be able to coalesce */
763 FindBand(r2, r2BandEnd, r2End, r2y1);
764 curBand = newReg->data->numRects;
765 RegionAppendNonO(newReg, r2, r2BandEnd, max(r2y1, ybot), r2->y2);
766 Coalesce(newReg, prevBand, curBand);
767 /* Append rest of boxes */
768 AppendRegions(newReg, r2BandEnd, r2End);
769 }
770
771 free(oldData);
772
773 if (!(numRects = newReg->data->numRects)) {
774 xfreeData(newReg);
775 newReg->data = &RegionEmptyData;
776 }
777 else if (numRects == 1) {
778 newReg->extents = *RegionBoxptr(newReg);
779 xfreeData(newReg);
780 newReg->data = NULL;
781 }
782 else {
783 DOWNSIZE(newReg, numRects);
784 }
785
786 return TRUE;
787 }
788
789 /*-
790 *-----------------------------------------------------------------------
791 * RegionSetExtents --
792 * Reset the extents of a region to what they should be. Called by
793 * Subtract and Intersect as they can't figure it out along the
794 * way or do so easily, as Union can.
795 *
796 * Results:
797 * None.
798 *
799 * Side Effects:
800 * The region's 'extents' structure is overwritten.
801 *
802 *-----------------------------------------------------------------------
803 */
804 static void
RegionSetExtents(RegionPtr pReg)805 RegionSetExtents(RegionPtr pReg)
806 {
807 BoxPtr pBox, pBoxEnd;
808
809 if (!pReg->data)
810 return;
811 if (!pReg->data->size) {
812 pReg->extents.x2 = pReg->extents.x1;
813 pReg->extents.y2 = pReg->extents.y1;
814 return;
815 }
816
817 pBox = RegionBoxptr(pReg);
818 pBoxEnd = RegionEnd(pReg);
819
820 /*
821 * Since pBox is the first rectangle in the region, it must have the
822 * smallest y1 and since pBoxEnd is the last rectangle in the region,
823 * it must have the largest y2, because of banding. Initialize x1 and
824 * x2 from pBox and pBoxEnd, resp., as good things to initialize them
825 * to...
826 */
827 pReg->extents.x1 = pBox->x1;
828 pReg->extents.y1 = pBox->y1;
829 pReg->extents.x2 = pBoxEnd->x2;
830 pReg->extents.y2 = pBoxEnd->y2;
831
832 assert(pReg->extents.y1 < pReg->extents.y2);
833 while (pBox <= pBoxEnd) {
834 if (pBox->x1 < pReg->extents.x1)
835 pReg->extents.x1 = pBox->x1;
836 if (pBox->x2 > pReg->extents.x2)
837 pReg->extents.x2 = pBox->x2;
838 pBox++;
839 };
840
841 assert(pReg->extents.x1 < pReg->extents.x2);
842 }
843
844 /*======================================================================
845 * Region Intersection
846 *====================================================================*/
847 /*-
848 *-----------------------------------------------------------------------
849 * RegionIntersectO --
850 * Handle an overlapping band for RegionIntersect.
851 *
852 * Results:
853 * TRUE if successful.
854 *
855 * Side Effects:
856 * Rectangles may be added to the region.
857 *
858 *-----------------------------------------------------------------------
859 */
860 /*ARGSUSED*/
861 #define MERGERECT(r) \
862 { \
863 if (r->x1 <= x2) { \
864 /* Merge with current rectangle */ \
865 if (r->x1 < x2) *pOverlap = TRUE; \
866 if (x2 < r->x2) x2 = r->x2; \
867 } else { \
868 /* Add current rectangle, start new one */ \
869 NEWRECT(pReg, pNextRect, x1, y1, x2, y2); \
870 x1 = r->x1; \
871 x2 = r->x2; \
872 } \
873 r++; \
874 }
875 /*======================================================================
876 * Region Union
877 *====================================================================*/
878 /*-
879 *-----------------------------------------------------------------------
880 * RegionUnionO --
881 * Handle an overlapping band for the union operation. Picks the
882 * left-most rectangle each time and merges it into the region.
883 *
884 * Results:
885 * TRUE if successful.
886 *
887 * Side Effects:
888 * pReg is overwritten.
889 * pOverlap is set to TRUE if any boxes overlap.
890 *
891 *-----------------------------------------------------------------------
892 */
893 static Bool
RegionUnionO(RegionPtr pReg,BoxPtr r1,BoxPtr r1End,BoxPtr r2,BoxPtr r2End,short y1,short y2,Bool * pOverlap)894 RegionUnionO(RegionPtr pReg,
895 BoxPtr r1,
896 BoxPtr r1End,
897 BoxPtr r2, BoxPtr r2End, short y1, short y2, Bool *pOverlap)
898 {
899 BoxPtr pNextRect;
900 int x1; /* left and right side of current union */
901 int x2;
902
903 assert(y1 < y2);
904 assert(r1 != r1End && r2 != r2End);
905
906 pNextRect = RegionTop(pReg);
907
908 /* Start off current rectangle */
909 if (r1->x1 < r2->x1) {
910 x1 = r1->x1;
911 x2 = r1->x2;
912 r1++;
913 }
914 else {
915 x1 = r2->x1;
916 x2 = r2->x2;
917 r2++;
918 }
919 while (r1 != r1End && r2 != r2End) {
920 if (r1->x1 < r2->x1)
921 MERGERECT(r1)
922 else
923 MERGERECT(r2);
924 }
925
926 /* Finish off whoever (if any) is left */
927 if (r1 != r1End) {
928 do {
929 MERGERECT(r1);
930 } while (r1 != r1End);
931 }
932 else if (r2 != r2End) {
933 do {
934 MERGERECT(r2);
935 } while (r2 != r2End);
936 }
937
938 /* Add current rectangle */
939 NEWRECT(pReg, pNextRect, x1, y1, x2, y2);
940
941 return TRUE;
942 }
943
944 /*======================================================================
945 * Batch Rectangle Union
946 *====================================================================*/
947
948 /*-
949 *-----------------------------------------------------------------------
950 * RegionAppend --
951 *
952 * "Append" the rgn rectangles onto the end of dstrgn, maintaining
953 * knowledge of YX-banding when it's easy. Otherwise, dstrgn just
954 * becomes a non-y-x-banded random collection of rectangles, and not
955 * yet a true region. After a sequence of appends, the caller must
956 * call RegionValidate to ensure that a valid region is constructed.
957 *
958 * Results:
959 * TRUE if successful.
960 *
961 * Side Effects:
962 * dstrgn is modified if rgn has rectangles.
963 *
964 */
965 Bool
RegionAppend(RegionPtr dstrgn,RegionPtr rgn)966 RegionAppend(RegionPtr dstrgn, RegionPtr rgn)
967 {
968 int numRects, dnumRects, size;
969 BoxPtr new, old;
970 Bool prepend;
971
972 if (RegionNar(rgn))
973 return RegionBreak(dstrgn);
974
975 if (!rgn->data && (dstrgn->data == &RegionEmptyData)) {
976 dstrgn->extents = rgn->extents;
977 dstrgn->data = NULL;
978 return TRUE;
979 }
980
981 numRects = RegionNumRects(rgn);
982 if (!numRects)
983 return TRUE;
984 prepend = FALSE;
985 size = numRects;
986 dnumRects = RegionNumRects(dstrgn);
987 if (!dnumRects && (size < 200))
988 size = 200; /* XXX pick numbers out of a hat */
989 RECTALLOC(dstrgn, size);
990 old = RegionRects(rgn);
991 if (!dnumRects)
992 dstrgn->extents = rgn->extents;
993 else if (dstrgn->extents.x2 > dstrgn->extents.x1) {
994 BoxPtr first, last;
995
996 first = old;
997 last = RegionBoxptr(dstrgn) + (dnumRects - 1);
998 if ((first->y1 > last->y2) ||
999 ((first->y1 == last->y1) && (first->y2 == last->y2) &&
1000 (first->x1 > last->x2))) {
1001 if (rgn->extents.x1 < dstrgn->extents.x1)
1002 dstrgn->extents.x1 = rgn->extents.x1;
1003 if (rgn->extents.x2 > dstrgn->extents.x2)
1004 dstrgn->extents.x2 = rgn->extents.x2;
1005 dstrgn->extents.y2 = rgn->extents.y2;
1006 }
1007 else {
1008 first = RegionBoxptr(dstrgn);
1009 last = old + (numRects - 1);
1010 if ((first->y1 > last->y2) ||
1011 ((first->y1 == last->y1) && (first->y2 == last->y2) &&
1012 (first->x1 > last->x2))) {
1013 prepend = TRUE;
1014 if (rgn->extents.x1 < dstrgn->extents.x1)
1015 dstrgn->extents.x1 = rgn->extents.x1;
1016 if (rgn->extents.x2 > dstrgn->extents.x2)
1017 dstrgn->extents.x2 = rgn->extents.x2;
1018 dstrgn->extents.y1 = rgn->extents.y1;
1019 }
1020 else
1021 dstrgn->extents.x2 = dstrgn->extents.x1;
1022 }
1023 }
1024 if (prepend) {
1025 new = RegionBox(dstrgn, numRects);
1026 if (dnumRects == 1)
1027 *new = *RegionBoxptr(dstrgn);
1028 else
1029 memmove((char *) new, (char *) RegionBoxptr(dstrgn),
1030 dnumRects * sizeof(BoxRec));
1031 new = RegionBoxptr(dstrgn);
1032 }
1033 else
1034 new = RegionBoxptr(dstrgn) + dnumRects;
1035 if (numRects == 1)
1036 *new = *old;
1037 else
1038 memmove((char *) new, (char *) old, numRects * sizeof(BoxRec));
1039 dstrgn->data->numRects += numRects;
1040 return TRUE;
1041 }
1042
1043 #define ExchangeRects(a, b) \
1044 { \
1045 BoxRec t; \
1046 t = rects[a]; \
1047 rects[a] = rects[b]; \
1048 rects[b] = t; \
1049 }
1050
1051 static void
QuickSortRects(BoxRec rects[],int numRects)1052 QuickSortRects(BoxRec rects[], int numRects)
1053 {
1054 int y1;
1055 int x1;
1056 int i, j;
1057 BoxPtr r;
1058
1059 /* Always called with numRects > 1 */
1060
1061 do {
1062 if (numRects == 2) {
1063 if (rects[0].y1 > rects[1].y1 ||
1064 (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1))
1065 ExchangeRects(0, 1);
1066 return;
1067 }
1068
1069 /* Choose partition element, stick in location 0 */
1070 ExchangeRects(0, numRects >> 1);
1071 y1 = rects[0].y1;
1072 x1 = rects[0].x1;
1073
1074 /* Partition array */
1075 i = 0;
1076 j = numRects;
1077 do {
1078 r = &(rects[i]);
1079 do {
1080 r++;
1081 i++;
1082 } while (i != numRects &&
1083 (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1)));
1084 r = &(rects[j]);
1085 do {
1086 r--;
1087 j--;
1088 } while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1));
1089 if (i < j)
1090 ExchangeRects(i, j);
1091 } while (i < j);
1092
1093 /* Move partition element back to middle */
1094 ExchangeRects(0, j);
1095
1096 /* Recurse */
1097 if (numRects - j - 1 > 1)
1098 QuickSortRects(&rects[j + 1], numRects - j - 1);
1099 numRects = j;
1100 } while (numRects > 1);
1101 }
1102
1103 /*-
1104 *-----------------------------------------------------------------------
1105 * RegionValidate --
1106 *
1107 * Take a ``region'' which is a non-y-x-banded random collection of
1108 * rectangles, and compute a nice region which is the union of all the
1109 * rectangles.
1110 *
1111 * Results:
1112 * TRUE if successful.
1113 *
1114 * Side Effects:
1115 * The passed-in ``region'' may be modified.
1116 * pOverlap set to TRUE if any retangles overlapped, else FALSE;
1117 *
1118 * Strategy:
1119 * Step 1. Sort the rectangles into ascending order with primary key y1
1120 * and secondary key x1.
1121 *
1122 * Step 2. Split the rectangles into the minimum number of proper y-x
1123 * banded regions. This may require horizontally merging
1124 * rectangles, and vertically coalescing bands. With any luck,
1125 * this step in an identity tranformation (ala the Box widget),
1126 * or a coalescing into 1 box (ala Menus).
1127 *
1128 * Step 3. Merge the separate regions down to a single region by calling
1129 * Union. Maximize the work each Union call does by using
1130 * a binary merge.
1131 *
1132 *-----------------------------------------------------------------------
1133 */
1134
1135 Bool
RegionValidate(RegionPtr badreg,Bool * pOverlap)1136 RegionValidate(RegionPtr badreg, Bool *pOverlap)
1137 {
1138 /* Descriptor for regions under construction in Step 2. */
1139 typedef struct {
1140 RegionRec reg;
1141 int prevBand;
1142 int curBand;
1143 } RegionInfo;
1144
1145 int numRects; /* Original numRects for badreg */
1146 RegionInfo *ri; /* Array of current regions */
1147 int numRI; /* Number of entries used in ri */
1148 int sizeRI; /* Number of entries available in ri */
1149 int i; /* Index into rects */
1150 int j; /* Index into ri */
1151 RegionInfo *rit; /* &ri[j] */
1152 RegionPtr reg; /* ri[j].reg */
1153 BoxPtr box; /* Current box in rects */
1154 BoxPtr riBox; /* Last box in ri[j].reg */
1155 RegionPtr hreg; /* ri[j_half].reg */
1156 Bool ret = TRUE;
1157
1158 *pOverlap = FALSE;
1159 if (!badreg->data) {
1160 good(badreg);
1161 return TRUE;
1162 }
1163 numRects = badreg->data->numRects;
1164 if (!numRects) {
1165 if (RegionNar(badreg))
1166 return FALSE;
1167 good(badreg);
1168 return TRUE;
1169 }
1170 if (badreg->extents.x1 < badreg->extents.x2) {
1171 if ((numRects) == 1) {
1172 xfreeData(badreg);
1173 badreg->data = (RegDataPtr) NULL;
1174 }
1175 else {
1176 DOWNSIZE(badreg, numRects);
1177 }
1178 good(badreg);
1179 return TRUE;
1180 }
1181
1182 /* Step 1: Sort the rects array into ascending (y1, x1) order */
1183 QuickSortRects(RegionBoxptr(badreg), numRects);
1184
1185 /* Step 2: Scatter the sorted array into the minimum number of regions */
1186
1187 /* Set up the first region to be the first rectangle in badreg */
1188 /* Note that step 2 code will never overflow the ri[0].reg rects array */
1189 ri = (RegionInfo *) malloc(4 * sizeof(RegionInfo));
1190 if (!ri)
1191 return RegionBreak(badreg);
1192 sizeRI = 4;
1193 numRI = 1;
1194 ri[0].prevBand = 0;
1195 ri[0].curBand = 0;
1196 ri[0].reg = *badreg;
1197 box = RegionBoxptr(&ri[0].reg);
1198 ri[0].reg.extents = *box;
1199 ri[0].reg.data->numRects = 1;
1200
1201 /* Now scatter rectangles into the minimum set of valid regions. If the
1202 next rectangle to be added to a region would force an existing rectangle
1203 in the region to be split up in order to maintain y-x banding, just
1204 forget it. Try the next region. If it doesn't fit cleanly into any
1205 region, make a new one. */
1206
1207 for (i = numRects; --i > 0;) {
1208 box++;
1209 /* Look for a region to append box to */
1210 for (j = numRI, rit = ri; --j >= 0; rit++) {
1211 reg = &rit->reg;
1212 riBox = RegionEnd(reg);
1213
1214 if (box->y1 == riBox->y1 && box->y2 == riBox->y2) {
1215 /* box is in same band as riBox. Merge or append it */
1216 if (box->x1 <= riBox->x2) {
1217 /* Merge it with riBox */
1218 if (box->x1 < riBox->x2)
1219 *pOverlap = TRUE;
1220 if (box->x2 > riBox->x2)
1221 riBox->x2 = box->x2;
1222 }
1223 else {
1224 RECTALLOC_BAIL(reg, 1, bail);
1225 *RegionTop(reg) = *box;
1226 reg->data->numRects++;
1227 }
1228 goto NextRect; /* So sue me */
1229 }
1230 else if (box->y1 >= riBox->y2) {
1231 /* Put box into new band */
1232 if (reg->extents.x2 < riBox->x2)
1233 reg->extents.x2 = riBox->x2;
1234 if (reg->extents.x1 > box->x1)
1235 reg->extents.x1 = box->x1;
1236 Coalesce(reg, rit->prevBand, rit->curBand);
1237 rit->curBand = reg->data->numRects;
1238 RECTALLOC_BAIL(reg, 1, bail);
1239 *RegionTop(reg) = *box;
1240 reg->data->numRects++;
1241 goto NextRect;
1242 }
1243 /* Well, this region was inappropriate. Try the next one. */
1244 } /* for j */
1245
1246 /* Uh-oh. No regions were appropriate. Create a new one. */
1247 if (sizeRI == numRI) {
1248 /* Oops, allocate space for new region information */
1249 sizeRI <<= 1;
1250 rit = (RegionInfo *) reallocarray(ri, sizeRI, sizeof(RegionInfo));
1251 if (!rit)
1252 goto bail;
1253 ri = rit;
1254 rit = &ri[numRI];
1255 }
1256 numRI++;
1257 rit->prevBand = 0;
1258 rit->curBand = 0;
1259 rit->reg.extents = *box;
1260 rit->reg.data = NULL;
1261 if (!RegionRectAlloc(&rit->reg, (i + numRI) / numRI)) /* MUST force allocation */
1262 goto bail;
1263 NextRect:;
1264 } /* for i */
1265
1266 /* Make a final pass over each region in order to Coalesce and set
1267 extents.x2 and extents.y2 */
1268
1269 for (j = numRI, rit = ri; --j >= 0; rit++) {
1270 reg = &rit->reg;
1271 riBox = RegionEnd(reg);
1272 reg->extents.y2 = riBox->y2;
1273 if (reg->extents.x2 < riBox->x2)
1274 reg->extents.x2 = riBox->x2;
1275 Coalesce(reg, rit->prevBand, rit->curBand);
1276 if (reg->data->numRects == 1) { /* keep unions happy below */
1277 xfreeData(reg);
1278 reg->data = NULL;
1279 }
1280 }
1281
1282 /* Step 3: Union all regions into a single region */
1283 while (numRI > 1) {
1284 int half = numRI / 2;
1285
1286 for (j = numRI & 1; j < (half + (numRI & 1)); j++) {
1287 reg = &ri[j].reg;
1288 hreg = &ri[j + half].reg;
1289 if (!RegionOp(reg, reg, hreg, RegionUnionO, TRUE, TRUE, pOverlap))
1290 ret = FALSE;
1291 if (hreg->extents.x1 < reg->extents.x1)
1292 reg->extents.x1 = hreg->extents.x1;
1293 if (hreg->extents.y1 < reg->extents.y1)
1294 reg->extents.y1 = hreg->extents.y1;
1295 if (hreg->extents.x2 > reg->extents.x2)
1296 reg->extents.x2 = hreg->extents.x2;
1297 if (hreg->extents.y2 > reg->extents.y2)
1298 reg->extents.y2 = hreg->extents.y2;
1299 xfreeData(hreg);
1300 }
1301 numRI -= half;
1302 }
1303 *badreg = ri[0].reg;
1304 free(ri);
1305 good(badreg);
1306 return ret;
1307 bail:
1308 for (i = 0; i < numRI; i++)
1309 xfreeData(&ri[i].reg);
1310 free(ri);
1311 return RegionBreak(badreg);
1312 }
1313
1314 RegionPtr
RegionFromRects(int nrects,xRectangle * prect,int ctype)1315 RegionFromRects(int nrects, xRectangle *prect, int ctype)
1316 {
1317
1318 RegionPtr pRgn;
1319 size_t rgnSize;
1320 RegDataPtr pData;
1321 BoxPtr pBox;
1322 int i;
1323 int x1, y1, x2, y2;
1324
1325 pRgn = RegionCreate(NullBox, 0);
1326 if (RegionNar(pRgn))
1327 return pRgn;
1328 if (!nrects)
1329 return pRgn;
1330 if (nrects == 1) {
1331 x1 = prect->x;
1332 y1 = prect->y;
1333 if ((x2 = x1 + (int) prect->width) > MAXSHORT)
1334 x2 = MAXSHORT;
1335 if ((y2 = y1 + (int) prect->height) > MAXSHORT)
1336 y2 = MAXSHORT;
1337 if (x1 != x2 && y1 != y2) {
1338 pRgn->extents.x1 = x1;
1339 pRgn->extents.y1 = y1;
1340 pRgn->extents.x2 = x2;
1341 pRgn->extents.y2 = y2;
1342 pRgn->data = NULL;
1343 }
1344 return pRgn;
1345 }
1346 rgnSize = RegionSizeof(nrects);
1347 pData = (rgnSize > 0) ? malloc(rgnSize) : NULL;
1348 if (!pData) {
1349 RegionBreak(pRgn);
1350 return pRgn;
1351 }
1352 pBox = (BoxPtr) (pData + 1);
1353 for (i = nrects; --i >= 0; prect++) {
1354 x1 = prect->x;
1355 y1 = prect->y;
1356 if ((x2 = x1 + (int) prect->width) > MAXSHORT)
1357 x2 = MAXSHORT;
1358 if ((y2 = y1 + (int) prect->height) > MAXSHORT)
1359 y2 = MAXSHORT;
1360 if (x1 != x2 && y1 != y2) {
1361 pBox->x1 = x1;
1362 pBox->y1 = y1;
1363 pBox->x2 = x2;
1364 pBox->y2 = y2;
1365 pBox++;
1366 }
1367 }
1368 if (pBox != (BoxPtr) (pData + 1)) {
1369 pData->size = nrects;
1370 pData->numRects = pBox - (BoxPtr) (pData + 1);
1371 pRgn->data = pData;
1372 if (ctype != CT_YXBANDED) {
1373 Bool overlap; /* result ignored */
1374
1375 pRgn->extents.x1 = pRgn->extents.x2 = 0;
1376 RegionValidate(pRgn, &overlap);
1377 }
1378 else
1379 RegionSetExtents(pRgn);
1380 good(pRgn);
1381 }
1382 else {
1383 free(pData);
1384 }
1385 return pRgn;
1386 }
1387