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