region.c revision 6747b715
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 xallocData(n) malloc(RegionSizeof(n)) 173#define xfreeData(reg) if ((reg)->data && (reg)->data->size) free((reg)->data) 174 175#define RECTALLOC_BAIL(pReg,n,bail) \ 176if (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \ 177 if (!RegionRectAlloc(pReg, n)) { goto bail; } 178 179#define RECTALLOC(pReg,n) \ 180if (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \ 181 if (!RegionRectAlloc(pReg, n)) { return FALSE; } 182 183#define ADDRECT(pNextRect,nx1,ny1,nx2,ny2) \ 184{ \ 185 pNextRect->x1 = nx1; \ 186 pNextRect->y1 = ny1; \ 187 pNextRect->x2 = nx2; \ 188 pNextRect->y2 = ny2; \ 189 pNextRect++; \ 190} 191 192#define NEWRECT(pReg,pNextRect,nx1,ny1,nx2,ny2) \ 193{ \ 194 if (!(pReg)->data || ((pReg)->data->numRects == (pReg)->data->size))\ 195 { \ 196 if (!RegionRectAlloc(pReg, 1)) \ 197 return FALSE; \ 198 pNextRect = RegionTop(pReg); \ 199 } \ 200 ADDRECT(pNextRect,nx1,ny1,nx2,ny2); \ 201 pReg->data->numRects++; \ 202 assert(pReg->data->numRects<=pReg->data->size); \ 203} 204 205 206#define DOWNSIZE(reg,numRects) \ 207if (((numRects) < ((reg)->data->size >> 1)) && ((reg)->data->size > 50)) \ 208{ \ 209 RegDataPtr NewData; \ 210 NewData = (RegDataPtr)realloc((reg)->data, RegionSizeof(numRects)); \ 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 339 if (!pRgn->data) 340 { 341 n++; 342 pRgn->data = xallocData(n); 343 if (!pRgn->data) 344 return RegionBreak (pRgn); 345 pRgn->data->numRects = 1; 346 *RegionBoxptr(pRgn) = pRgn->extents; 347 } 348 else if (!pRgn->data->size) 349 { 350 pRgn->data = xallocData(n); 351 if (!pRgn->data) 352 return RegionBreak (pRgn); 353 pRgn->data->numRects = 0; 354 } 355 else 356 { 357 if (n == 1) 358 { 359 n = pRgn->data->numRects; 360 if (n > 500) /* XXX pick numbers out of a hat */ 361 n = 250; 362 } 363 n += pRgn->data->numRects; 364 data = (RegDataPtr)realloc(pRgn->data, RegionSizeof(n)); 365 if (!data) 366 return RegionBreak (pRgn); 367 pRgn->data = data; 368 } 369 pRgn->data->size = n; 370 return TRUE; 371} 372 373/*====================================================================== 374 * Generic Region Operator 375 *====================================================================*/ 376 377/*- 378 *----------------------------------------------------------------------- 379 * RegionCoalesce -- 380 * Attempt to merge the boxes in the current band with those in the 381 * previous one. We are guaranteed that the current band extends to 382 * the end of the rects array. Used only by RegionOp. 383 * 384 * Results: 385 * The new index for the previous band. 386 * 387 * Side Effects: 388 * If coalescing takes place: 389 * - rectangles in the previous band will have their y2 fields 390 * altered. 391 * - pReg->data->numRects will be decreased. 392 * 393 *----------------------------------------------------------------------- 394 */ 395_X_INLINE static int 396RegionCoalesce ( 397 RegionPtr pReg, /* Region to coalesce */ 398 int prevStart, /* Index of start of previous band */ 399 int curStart) /* Index of start of current band */ 400{ 401 BoxPtr pPrevBox; /* Current box in previous band */ 402 BoxPtr pCurBox; /* Current box in current band */ 403 int numRects; /* Number rectangles in both bands */ 404 int y2; /* Bottom of current band */ 405 /* 406 * Figure out how many rectangles are in the band. 407 */ 408 numRects = curStart - prevStart; 409 assert(numRects == pReg->data->numRects - curStart); 410 411 if (!numRects) return curStart; 412 413 /* 414 * The bands may only be coalesced if the bottom of the previous 415 * matches the top scanline of the current. 416 */ 417 pPrevBox = RegionBox(pReg, prevStart); 418 pCurBox = RegionBox(pReg, curStart); 419 if (pPrevBox->y2 != pCurBox->y1) return curStart; 420 421 /* 422 * Make sure the bands have boxes in the same places. This 423 * assumes that boxes have been added in such a way that they 424 * cover the most area possible. I.e. two boxes in a band must 425 * have some horizontal space between them. 426 */ 427 y2 = pCurBox->y2; 428 429 do { 430 if ((pPrevBox->x1 != pCurBox->x1) || (pPrevBox->x2 != pCurBox->x2)) { 431 return curStart; 432 } 433 pPrevBox++; 434 pCurBox++; 435 numRects--; 436 } while (numRects); 437 438 /* 439 * The bands may be merged, so set the bottom y of each box 440 * in the previous band to the bottom y of the current band. 441 */ 442 numRects = curStart - prevStart; 443 pReg->data->numRects -= numRects; 444 do { 445 pPrevBox--; 446 pPrevBox->y2 = y2; 447 numRects--; 448 } while (numRects); 449 return prevStart; 450} 451 452 453/* Quicky macro to avoid trivial reject procedure calls to RegionCoalesce */ 454 455#define Coalesce(newReg, prevBand, curBand) \ 456 if (curBand - prevBand == newReg->data->numRects - curBand) { \ 457 prevBand = RegionCoalesce(newReg, prevBand, curBand); \ 458 } else { \ 459 prevBand = curBand; \ 460 } 461 462/*- 463 *----------------------------------------------------------------------- 464 * RegionAppendNonO -- 465 * Handle a non-overlapping band for the union and subtract operations. 466 * Just adds the (top/bottom-clipped) rectangles into the region. 467 * Doesn't have to check for subsumption or anything. 468 * 469 * Results: 470 * None. 471 * 472 * Side Effects: 473 * pReg->data->numRects is incremented and the rectangles overwritten 474 * with the rectangles we're passed. 475 * 476 *----------------------------------------------------------------------- 477 */ 478 479_X_INLINE static Bool 480RegionAppendNonO ( 481 RegionPtr pReg, 482 BoxPtr r, 483 BoxPtr rEnd, 484 int y1, 485 int y2) 486{ 487 BoxPtr pNextRect; 488 int newRects; 489 490 newRects = rEnd - r; 491 492 assert(y1 < y2); 493 assert(newRects != 0); 494 495 /* Make sure we have enough space for all rectangles to be added */ 496 RECTALLOC(pReg, newRects); 497 pNextRect = RegionTop(pReg); 498 pReg->data->numRects += newRects; 499 do { 500 assert(r->x1 < r->x2); 501 ADDRECT(pNextRect, r->x1, y1, r->x2, y2); 502 r++; 503 } while (r != rEnd); 504 505 return TRUE; 506} 507 508#define FindBand(r, rBandEnd, rEnd, ry1) \ 509{ \ 510 ry1 = r->y1; \ 511 rBandEnd = r+1; \ 512 while ((rBandEnd != rEnd) && (rBandEnd->y1 == ry1)) { \ 513 rBandEnd++; \ 514 } \ 515} 516 517#define AppendRegions(newReg, r, rEnd) \ 518{ \ 519 int newRects; \ 520 if ((newRects = rEnd - r)) { \ 521 RECTALLOC(newReg, newRects); \ 522 memmove((char *)RegionTop(newReg),(char *)r, \ 523 newRects * sizeof(BoxRec)); \ 524 newReg->data->numRects += newRects; \ 525 } \ 526} 527 528/*- 529 *----------------------------------------------------------------------- 530 * RegionOp -- 531 * Apply an operation to two regions. Called by RegionUnion, RegionInverse, 532 * RegionSubtract, RegionIntersect.... Both regions MUST have at least one 533 * rectangle, and cannot be the same object. 534 * 535 * Results: 536 * TRUE if successful. 537 * 538 * Side Effects: 539 * The new region is overwritten. 540 * pOverlap set to TRUE if overlapFunc ever returns TRUE. 541 * 542 * Notes: 543 * The idea behind this function is to view the two regions as sets. 544 * Together they cover a rectangle of area that this function divides 545 * into horizontal bands where points are covered only by one region 546 * or by both. For the first case, the nonOverlapFunc is called with 547 * each the band and the band's upper and lower extents. For the 548 * second, the overlapFunc is called to process the entire band. It 549 * is responsible for clipping the rectangles in the band, though 550 * this function provides the boundaries. 551 * At the end of each band, the new region is coalesced, if possible, 552 * to reduce the number of rectangles in the region. 553 * 554 *----------------------------------------------------------------------- 555 */ 556 557typedef Bool (*OverlapProcPtr)( 558 RegionPtr pReg, 559 BoxPtr r1, 560 BoxPtr r1End, 561 BoxPtr r2, 562 BoxPtr r2End, 563 short y1, 564 short y2, 565 Bool *pOverlap); 566 567static Bool 568RegionOp( 569 RegionPtr newReg, /* Place to store result */ 570 RegionPtr reg1, /* First region in operation */ 571 RegionPtr reg2, /* 2d region in operation */ 572 OverlapProcPtr overlapFunc, /* Function to call for over- 573 * lapping bands */ 574 Bool appendNon1, /* Append non-overlapping bands */ 575 /* in region 1 ? */ 576 Bool appendNon2, /* Append non-overlapping bands */ 577 /* in region 2 ? */ 578 Bool *pOverlap) 579{ 580 BoxPtr r1; /* Pointer into first region */ 581 BoxPtr r2; /* Pointer into 2d region */ 582 BoxPtr r1End; /* End of 1st region */ 583 BoxPtr r2End; /* End of 2d region */ 584 short ybot; /* Bottom of intersection */ 585 short ytop; /* Top of intersection */ 586 RegDataPtr oldData; /* Old data for newReg */ 587 int prevBand; /* Index of start of 588 * previous band in newReg */ 589 int curBand; /* Index of start of current 590 * band in newReg */ 591 BoxPtr r1BandEnd; /* End of current band in r1 */ 592 BoxPtr r2BandEnd; /* End of current band in r2 */ 593 short top; /* Top of non-overlapping band */ 594 short bot; /* Bottom of non-overlapping band*/ 595 int r1y1; /* Temps for r1->y1 and r2->y1 */ 596 int r2y1; 597 int newSize; 598 int numRects; 599 600 /* 601 * Break any region computed from a broken region 602 */ 603 if (RegionNar (reg1) || RegionNar(reg2)) 604 return RegionBreak (newReg); 605 606 /* 607 * Initialization: 608 * set r1, r2, r1End and r2End appropriately, save the rectangles 609 * of the destination region until the end in case it's one of 610 * the two source regions, then mark the "new" region empty, allocating 611 * another array of rectangles for it to use. 612 */ 613 614 r1 = RegionRects(reg1); 615 newSize = RegionNumRects(reg1); 616 r1End = r1 + newSize; 617 numRects = RegionNumRects(reg2); 618 r2 = RegionRects(reg2); 619 r2End = r2 + numRects; 620 assert(r1 != r1End); 621 assert(r2 != r2End); 622 623 oldData = NULL; 624 if (((newReg == reg1) && (newSize > 1)) || 625 ((newReg == reg2) && (numRects > 1))) 626 { 627 oldData = newReg->data; 628 newReg->data = &RegionEmptyData; 629 } 630 /* guess at new size */ 631 if (numRects > newSize) 632 newSize = numRects; 633 newSize <<= 1; 634 if (!newReg->data) 635 newReg->data = &RegionEmptyData; 636 else if (newReg->data->size) 637 newReg->data->numRects = 0; 638 if (newSize > newReg->data->size) 639 if (!RegionRectAlloc(newReg, newSize)) 640 return FALSE; 641 642 /* 643 * Initialize ybot. 644 * In the upcoming loop, ybot and ytop serve different functions depending 645 * on whether the band being handled is an overlapping or non-overlapping 646 * band. 647 * In the case of a non-overlapping band (only one of the regions 648 * has points in the band), ybot is the bottom of the most recent 649 * intersection and thus clips the top of the rectangles in that band. 650 * ytop is the top of the next intersection between the two regions and 651 * serves to clip the bottom of the rectangles in the current band. 652 * For an overlapping band (where the two regions intersect), ytop clips 653 * the top of the rectangles of both regions and ybot clips the bottoms. 654 */ 655 656 ybot = min(r1->y1, r2->y1); 657 658 /* 659 * prevBand serves to mark the start of the previous band so rectangles 660 * can be coalesced into larger rectangles. qv. RegionCoalesce, above. 661 * In the beginning, there is no previous band, so prevBand == curBand 662 * (curBand is set later on, of course, but the first band will always 663 * start at index 0). prevBand and curBand must be indices because of 664 * the possible expansion, and resultant moving, of the new region's 665 * array of rectangles. 666 */ 667 prevBand = 0; 668 669 do { 670 /* 671 * This algorithm proceeds one source-band (as opposed to a 672 * destination band, which is determined by where the two regions 673 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the 674 * rectangle after the last one in the current band for their 675 * respective regions. 676 */ 677 assert(r1 != r1End); 678 assert(r2 != r2End); 679 680 FindBand(r1, r1BandEnd, r1End, r1y1); 681 FindBand(r2, r2BandEnd, r2End, r2y1); 682 683 /* 684 * First handle the band that doesn't intersect, if any. 685 * 686 * Note that attention is restricted to one band in the 687 * non-intersecting region at once, so if a region has n 688 * bands between the current position and the next place it overlaps 689 * the other, this entire loop will be passed through n times. 690 */ 691 if (r1y1 < r2y1) { 692 if (appendNon1) { 693 top = max(r1y1, ybot); 694 bot = min(r1->y2, r2y1); 695 if (top != bot) { 696 curBand = newReg->data->numRects; 697 RegionAppendNonO(newReg, r1, r1BandEnd, top, bot); 698 Coalesce(newReg, prevBand, curBand); 699 } 700 } 701 ytop = r2y1; 702 } else if (r2y1 < r1y1) { 703 if (appendNon2) { 704 top = max(r2y1, ybot); 705 bot = min(r2->y2, r1y1); 706 if (top != bot) { 707 curBand = newReg->data->numRects; 708 RegionAppendNonO(newReg, r2, r2BandEnd, top, bot); 709 Coalesce(newReg, prevBand, curBand); 710 } 711 } 712 ytop = r1y1; 713 } else { 714 ytop = r1y1; 715 } 716 717 /* 718 * Now see if we've hit an intersecting band. The two bands only 719 * intersect if ybot > ytop 720 */ 721 ybot = min(r1->y2, r2->y2); 722 if (ybot > ytop) { 723 curBand = newReg->data->numRects; 724 (* overlapFunc)(newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot, 725 pOverlap); 726 Coalesce(newReg, prevBand, curBand); 727 } 728 729 /* 730 * If we've finished with a band (y2 == ybot) we skip forward 731 * in the region to the next band. 732 */ 733 if (r1->y2 == ybot) r1 = r1BandEnd; 734 if (r2->y2 == ybot) r2 = r2BandEnd; 735 736 } while (r1 != r1End && r2 != r2End); 737 738 /* 739 * Deal with whichever region (if any) still has rectangles left. 740 * 741 * We only need to worry about banding and coalescing for the very first 742 * band left. After that, we can just group all remaining boxes, 743 * regardless of how many bands, into one final append to the list. 744 */ 745 746 if ((r1 != r1End) && appendNon1) { 747 /* Do first nonOverlap1Func call, which may be able to coalesce */ 748 FindBand(r1, r1BandEnd, r1End, r1y1); 749 curBand = newReg->data->numRects; 750 RegionAppendNonO(newReg, r1, r1BandEnd, max(r1y1, ybot), r1->y2); 751 Coalesce(newReg, prevBand, curBand); 752 /* Just append the rest of the boxes */ 753 AppendRegions(newReg, r1BandEnd, r1End); 754 755 } else if ((r2 != r2End) && appendNon2) { 756 /* Do first nonOverlap2Func call, which may be able to coalesce */ 757 FindBand(r2, r2BandEnd, r2End, r2y1); 758 curBand = newReg->data->numRects; 759 RegionAppendNonO(newReg, r2, r2BandEnd, max(r2y1, ybot), r2->y2); 760 Coalesce(newReg, prevBand, curBand); 761 /* Append rest of boxes */ 762 AppendRegions(newReg, r2BandEnd, r2End); 763 } 764 765 free(oldData); 766 767 if (!(numRects = newReg->data->numRects)) 768 { 769 xfreeData(newReg); 770 newReg->data = &RegionEmptyData; 771 } 772 else if (numRects == 1) 773 { 774 newReg->extents = *RegionBoxptr(newReg); 775 xfreeData(newReg); 776 newReg->data = NULL; 777 } 778 else 779 { 780 DOWNSIZE(newReg, numRects); 781 } 782 783 return TRUE; 784} 785 786/*- 787 *----------------------------------------------------------------------- 788 * RegionSetExtents -- 789 * Reset the extents of a region to what they should be. Called by 790 * Subtract and Intersect as they can't figure it out along the 791 * way or do so easily, as Union can. 792 * 793 * Results: 794 * None. 795 * 796 * Side Effects: 797 * The region's 'extents' structure is overwritten. 798 * 799 *----------------------------------------------------------------------- 800 */ 801static void 802RegionSetExtents (RegionPtr pReg) 803{ 804 BoxPtr pBox, pBoxEnd; 805 806 if (!pReg->data) 807 return; 808 if (!pReg->data->size) 809 { 810 pReg->extents.x2 = pReg->extents.x1; 811 pReg->extents.y2 = pReg->extents.y1; 812 return; 813 } 814 815 pBox = RegionBoxptr(pReg); 816 pBoxEnd = RegionEnd(pReg); 817 818 /* 819 * Since pBox is the first rectangle in the region, it must have the 820 * smallest y1 and since pBoxEnd is the last rectangle in the region, 821 * it must have the largest y2, because of banding. Initialize x1 and 822 * x2 from pBox and pBoxEnd, resp., as good things to initialize them 823 * to... 824 */ 825 pReg->extents.x1 = pBox->x1; 826 pReg->extents.y1 = pBox->y1; 827 pReg->extents.x2 = pBoxEnd->x2; 828 pReg->extents.y2 = pBoxEnd->y2; 829 830 assert(pReg->extents.y1 < pReg->extents.y2); 831 while (pBox <= pBoxEnd) { 832 if (pBox->x1 < pReg->extents.x1) 833 pReg->extents.x1 = pBox->x1; 834 if (pBox->x2 > pReg->extents.x2) 835 pReg->extents.x2 = pBox->x2; 836 pBox++; 837 }; 838 839 assert(pReg->extents.x1 < pReg->extents.x2); 840} 841 842/*====================================================================== 843 * Region Intersection 844 *====================================================================*/ 845/*- 846 *----------------------------------------------------------------------- 847 * RegionIntersectO -- 848 * Handle an overlapping band for RegionIntersect. 849 * 850 * Results: 851 * TRUE if successful. 852 * 853 * Side Effects: 854 * Rectangles may be added to the region. 855 * 856 *----------------------------------------------------------------------- 857 */ 858/*ARGSUSED*/ 859 860#define MERGERECT(r) \ 861{ \ 862 if (r->x1 <= x2) { \ 863 /* Merge with current rectangle */ \ 864 if (r->x1 < x2) *pOverlap = TRUE; \ 865 if (x2 < r->x2) x2 = r->x2; \ 866 } else { \ 867 /* Add current rectangle, start new one */ \ 868 NEWRECT(pReg, pNextRect, x1, y1, x2, y2); \ 869 x1 = r->x1; \ 870 x2 = r->x2; \ 871 } \ 872 r++; \ 873} 874 875/*====================================================================== 876 * Region Union 877 *====================================================================*/ 878 879/*- 880 *----------------------------------------------------------------------- 881 * RegionUnionO -- 882 * Handle an overlapping band for the union operation. Picks the 883 * left-most rectangle each time and merges it into the region. 884 * 885 * Results: 886 * TRUE if successful. 887 * 888 * Side Effects: 889 * pReg is overwritten. 890 * pOverlap is set to TRUE if any boxes overlap. 891 * 892 *----------------------------------------------------------------------- 893 */ 894static Bool 895RegionUnionO ( 896 RegionPtr pReg, 897 BoxPtr r1, 898 BoxPtr r1End, 899 BoxPtr r2, 900 BoxPtr r2End, 901 short y1, 902 short y2, 903 Bool *pOverlap) 904{ 905 BoxPtr pNextRect; 906 int x1; /* left and right side of current union */ 907 int x2; 908 909 assert (y1 < y2); 910 assert(r1 != r1End && r2 != r2End); 911 912 pNextRect = RegionTop(pReg); 913 914 /* Start off current rectangle */ 915 if (r1->x1 < r2->x1) 916 { 917 x1 = r1->x1; 918 x2 = r1->x2; 919 r1++; 920 } 921 else 922 { 923 x1 = r2->x1; 924 x2 = r2->x2; 925 r2++; 926 } 927 while (r1 != r1End && r2 != r2End) 928 { 929 if (r1->x1 < r2->x1) MERGERECT(r1) else MERGERECT(r2); 930 } 931 932 /* Finish off whoever (if any) is left */ 933 if (r1 != r1End) 934 { 935 do 936 { 937 MERGERECT(r1); 938 } while (r1 != r1End); 939 } 940 else if (r2 != r2End) 941 { 942 do 943 { 944 MERGERECT(r2); 945 } while (r2 != r2End); 946 } 947 948 /* Add current rectangle */ 949 NEWRECT(pReg, pNextRect, x1, y1, x2, y2); 950 951 return TRUE; 952} 953 954/*====================================================================== 955 * Batch Rectangle Union 956 *====================================================================*/ 957 958/*- 959 *----------------------------------------------------------------------- 960 * RegionAppend -- 961 * 962 * "Append" the rgn rectangles onto the end of dstrgn, maintaining 963 * knowledge of YX-banding when it's easy. Otherwise, dstrgn just 964 * becomes a non-y-x-banded random collection of rectangles, and not 965 * yet a true region. After a sequence of appends, the caller must 966 * call RegionValidate to ensure that a valid region is constructed. 967 * 968 * Results: 969 * TRUE if successful. 970 * 971 * Side Effects: 972 * dstrgn is modified if rgn has rectangles. 973 * 974 */ 975Bool 976RegionAppend(RegionPtr dstrgn, RegionPtr rgn) 977{ 978 int numRects, dnumRects, size; 979 BoxPtr new, old; 980 Bool prepend; 981 982 if (RegionNar(rgn)) 983 return RegionBreak (dstrgn); 984 985 if (!rgn->data && (dstrgn->data == &RegionEmptyData)) 986 { 987 dstrgn->extents = rgn->extents; 988 dstrgn->data = NULL; 989 return TRUE; 990 } 991 992 numRects = RegionNumRects(rgn); 993 if (!numRects) 994 return TRUE; 995 prepend = FALSE; 996 size = numRects; 997 dnumRects = RegionNumRects(dstrgn); 998 if (!dnumRects && (size < 200)) 999 size = 200; /* XXX pick numbers out of a hat */ 1000 RECTALLOC(dstrgn, size); 1001 old = RegionRects(rgn); 1002 if (!dnumRects) 1003 dstrgn->extents = rgn->extents; 1004 else if (dstrgn->extents.x2 > dstrgn->extents.x1) 1005 { 1006 BoxPtr first, last; 1007 1008 first = old; 1009 last = RegionBoxptr(dstrgn) + (dnumRects - 1); 1010 if ((first->y1 > last->y2) || 1011 ((first->y1 == last->y1) && (first->y2 == last->y2) && 1012 (first->x1 > last->x2))) 1013 { 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.y2 = rgn->extents.y2; 1019 } 1020 else 1021 { 1022 first = RegionBoxptr(dstrgn); 1023 last = old + (numRects - 1); 1024 if ((first->y1 > last->y2) || 1025 ((first->y1 == last->y1) && (first->y2 == last->y2) && 1026 (first->x1 > last->x2))) 1027 { 1028 prepend = TRUE; 1029 if (rgn->extents.x1 < dstrgn->extents.x1) 1030 dstrgn->extents.x1 = rgn->extents.x1; 1031 if (rgn->extents.x2 > dstrgn->extents.x2) 1032 dstrgn->extents.x2 = rgn->extents.x2; 1033 dstrgn->extents.y1 = rgn->extents.y1; 1034 } 1035 else 1036 dstrgn->extents.x2 = dstrgn->extents.x1; 1037 } 1038 } 1039 if (prepend) 1040 { 1041 new = RegionBox(dstrgn, numRects); 1042 if (dnumRects == 1) 1043 *new = *RegionBoxptr(dstrgn); 1044 else 1045 memmove((char *)new,(char *)RegionBoxptr(dstrgn), 1046 dnumRects * sizeof(BoxRec)); 1047 new = RegionBoxptr(dstrgn); 1048 } 1049 else 1050 new = RegionBoxptr(dstrgn) + dnumRects; 1051 if (numRects == 1) 1052 *new = *old; 1053 else 1054 memmove((char *)new, (char *)old, numRects * sizeof(BoxRec)); 1055 dstrgn->data->numRects += numRects; 1056 return TRUE; 1057} 1058 1059 1060#define ExchangeRects(a, b) \ 1061{ \ 1062 BoxRec t; \ 1063 t = rects[a]; \ 1064 rects[a] = rects[b]; \ 1065 rects[b] = t; \ 1066} 1067 1068static void 1069QuickSortRects( 1070 BoxRec rects[], 1071 int numRects) 1072{ 1073 int y1; 1074 int x1; 1075 int i, j; 1076 BoxPtr r; 1077 1078 /* Always called with numRects > 1 */ 1079 1080 do 1081 { 1082 if (numRects == 2) 1083 { 1084 if (rects[0].y1 > rects[1].y1 || 1085 (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1)) 1086 ExchangeRects(0, 1); 1087 return; 1088 } 1089 1090 /* Choose partition element, stick in location 0 */ 1091 ExchangeRects(0, numRects >> 1); 1092 y1 = rects[0].y1; 1093 x1 = rects[0].x1; 1094 1095 /* Partition array */ 1096 i = 0; 1097 j = numRects; 1098 do 1099 { 1100 r = &(rects[i]); 1101 do 1102 { 1103 r++; 1104 i++; 1105 } while (i != numRects && 1106 (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1))); 1107 r = &(rects[j]); 1108 do 1109 { 1110 r--; 1111 j--; 1112 } while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1)); 1113 if (i < j) 1114 ExchangeRects(i, j); 1115 } while (i < j); 1116 1117 /* Move partition element back to middle */ 1118 ExchangeRects(0, j); 1119 1120 /* Recurse */ 1121 if (numRects-j-1 > 1) 1122 QuickSortRects(&rects[j+1], numRects-j-1); 1123 numRects = j; 1124 } while (numRects > 1); 1125} 1126 1127/*- 1128 *----------------------------------------------------------------------- 1129 * RegionValidate -- 1130 * 1131 * Take a ``region'' which is a non-y-x-banded random collection of 1132 * rectangles, and compute a nice region which is the union of all the 1133 * rectangles. 1134 * 1135 * Results: 1136 * TRUE if successful. 1137 * 1138 * Side Effects: 1139 * The passed-in ``region'' may be modified. 1140 * pOverlap set to TRUE if any retangles overlapped, else FALSE; 1141 * 1142 * Strategy: 1143 * Step 1. Sort the rectangles into ascending order with primary key y1 1144 * and secondary key x1. 1145 * 1146 * Step 2. Split the rectangles into the minimum number of proper y-x 1147 * banded regions. This may require horizontally merging 1148 * rectangles, and vertically coalescing bands. With any luck, 1149 * this step in an identity tranformation (ala the Box widget), 1150 * or a coalescing into 1 box (ala Menus). 1151 * 1152 * Step 3. Merge the separate regions down to a single region by calling 1153 * Union. Maximize the work each Union call does by using 1154 * a binary merge. 1155 * 1156 *----------------------------------------------------------------------- 1157 */ 1158 1159Bool 1160RegionValidate(RegionPtr badreg, Bool *pOverlap) 1161{ 1162 /* Descriptor for regions under construction in Step 2. */ 1163 typedef struct { 1164 RegionRec reg; 1165 int prevBand; 1166 int curBand; 1167 } RegionInfo; 1168 1169 int numRects; /* Original numRects for badreg */ 1170 RegionInfo *ri; /* Array of current regions */ 1171 int numRI; /* Number of entries used in ri */ 1172 int sizeRI; /* Number of entries available in ri */ 1173 int i; /* Index into rects */ 1174 int j; /* Index into ri */ 1175 RegionInfo *rit; /* &ri[j] */ 1176 RegionPtr reg; /* ri[j].reg */ 1177 BoxPtr box; /* Current box in rects */ 1178 BoxPtr riBox; /* Last box in ri[j].reg */ 1179 RegionPtr hreg; /* ri[j_half].reg */ 1180 Bool ret = TRUE; 1181 1182 *pOverlap = FALSE; 1183 if (!badreg->data) 1184 { 1185 good(badreg); 1186 return TRUE; 1187 } 1188 numRects = badreg->data->numRects; 1189 if (!numRects) 1190 { 1191 if (RegionNar(badreg)) 1192 return FALSE; 1193 good(badreg); 1194 return TRUE; 1195 } 1196 if (badreg->extents.x1 < badreg->extents.x2) 1197 { 1198 if ((numRects) == 1) 1199 { 1200 xfreeData(badreg); 1201 badreg->data = (RegDataPtr) NULL; 1202 } 1203 else 1204 { 1205 DOWNSIZE(badreg, numRects); 1206 } 1207 good(badreg); 1208 return TRUE; 1209 } 1210 1211 /* Step 1: Sort the rects array into ascending (y1, x1) order */ 1212 QuickSortRects(RegionBoxptr(badreg), numRects); 1213 1214 /* Step 2: Scatter the sorted array into the minimum number of regions */ 1215 1216 /* Set up the first region to be the first rectangle in badreg */ 1217 /* Note that step 2 code will never overflow the ri[0].reg rects array */ 1218 ri = (RegionInfo *) malloc(4 * sizeof(RegionInfo)); 1219 if (!ri) 1220 return RegionBreak (badreg); 1221 sizeRI = 4; 1222 numRI = 1; 1223 ri[0].prevBand = 0; 1224 ri[0].curBand = 0; 1225 ri[0].reg = *badreg; 1226 box = RegionBoxptr(&ri[0].reg); 1227 ri[0].reg.extents = *box; 1228 ri[0].reg.data->numRects = 1; 1229 1230 /* Now scatter rectangles into the minimum set of valid regions. If the 1231 next rectangle to be added to a region would force an existing rectangle 1232 in the region to be split up in order to maintain y-x banding, just 1233 forget it. Try the next region. If it doesn't fit cleanly into any 1234 region, make a new one. */ 1235 1236 for (i = numRects; --i > 0;) 1237 { 1238 box++; 1239 /* Look for a region to append box to */ 1240 for (j = numRI, rit = ri; --j >= 0; rit++) 1241 { 1242 reg = &rit->reg; 1243 riBox = RegionEnd(reg); 1244 1245 if (box->y1 == riBox->y1 && box->y2 == riBox->y2) 1246 { 1247 /* box is in same band as riBox. Merge or append it */ 1248 if (box->x1 <= riBox->x2) 1249 { 1250 /* Merge it with riBox */ 1251 if (box->x1 < riBox->x2) *pOverlap = TRUE; 1252 if (box->x2 > riBox->x2) riBox->x2 = box->x2; 1253 } 1254 else 1255 { 1256 RECTALLOC_BAIL(reg, 1, bail); 1257 *RegionTop(reg) = *box; 1258 reg->data->numRects++; 1259 } 1260 goto NextRect; /* So sue me */ 1261 } 1262 else if (box->y1 >= riBox->y2) 1263 { 1264 /* Put box into new band */ 1265 if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2; 1266 if (reg->extents.x1 > box->x1) reg->extents.x1 = box->x1; 1267 Coalesce(reg, rit->prevBand, rit->curBand); 1268 rit->curBand = reg->data->numRects; 1269 RECTALLOC_BAIL(reg, 1, bail); 1270 *RegionTop(reg) = *box; 1271 reg->data->numRects++; 1272 goto NextRect; 1273 } 1274 /* Well, this region was inappropriate. Try the next one. */ 1275 } /* for j */ 1276 1277 /* Uh-oh. No regions were appropriate. Create a new one. */ 1278 if (sizeRI == numRI) 1279 { 1280 /* Oops, allocate space for new region information */ 1281 sizeRI <<= 1; 1282 rit = (RegionInfo *) realloc(ri, sizeRI * sizeof(RegionInfo)); 1283 if (!rit) 1284 goto bail; 1285 ri = rit; 1286 rit = &ri[numRI]; 1287 } 1288 numRI++; 1289 rit->prevBand = 0; 1290 rit->curBand = 0; 1291 rit->reg.extents = *box; 1292 rit->reg.data = NULL; 1293 if (!RegionRectAlloc(&rit->reg, (i+numRI) / numRI)) /* MUST force allocation */ 1294 goto bail; 1295NextRect: ; 1296 } /* for i */ 1297 1298 /* Make a final pass over each region in order to Coalesce and set 1299 extents.x2 and extents.y2 */ 1300 1301 for (j = numRI, rit = ri; --j >= 0; rit++) 1302 { 1303 reg = &rit->reg; 1304 riBox = RegionEnd(reg); 1305 reg->extents.y2 = riBox->y2; 1306 if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2; 1307 Coalesce(reg, rit->prevBand, rit->curBand); 1308 if (reg->data->numRects == 1) /* keep unions happy below */ 1309 { 1310 xfreeData(reg); 1311 reg->data = NULL; 1312 } 1313 } 1314 1315 /* Step 3: Union all regions into a single region */ 1316 while (numRI > 1) 1317 { 1318 int half = numRI/2; 1319 for (j = numRI & 1; j < (half + (numRI & 1)); j++) 1320 { 1321 reg = &ri[j].reg; 1322 hreg = &ri[j+half].reg; 1323 if (!RegionOp(reg, reg, hreg, RegionUnionO, TRUE, TRUE, pOverlap)) 1324 ret = FALSE; 1325 if (hreg->extents.x1 < reg->extents.x1) 1326 reg->extents.x1 = hreg->extents.x1; 1327 if (hreg->extents.y1 < reg->extents.y1) 1328 reg->extents.y1 = hreg->extents.y1; 1329 if (hreg->extents.x2 > reg->extents.x2) 1330 reg->extents.x2 = hreg->extents.x2; 1331 if (hreg->extents.y2 > reg->extents.y2) 1332 reg->extents.y2 = hreg->extents.y2; 1333 xfreeData(hreg); 1334 } 1335 numRI -= half; 1336 } 1337 *badreg = ri[0].reg; 1338 free(ri); 1339 good(badreg); 1340 return ret; 1341bail: 1342 for (i = 0; i < numRI; i++) 1343 xfreeData(&ri[i].reg); 1344 free(ri); 1345 return RegionBreak (badreg); 1346} 1347 1348RegionPtr 1349RegionFromRects(int nrects, xRectangle *prect, int ctype) 1350{ 1351 1352 RegionPtr pRgn; 1353 RegDataPtr pData; 1354 BoxPtr pBox; 1355 int i; 1356 int x1, y1, x2, y2; 1357 1358 pRgn = RegionCreate(NullBox, 0); 1359 if (RegionNar (pRgn)) 1360 return pRgn; 1361 if (!nrects) 1362 return pRgn; 1363 if (nrects == 1) 1364 { 1365 x1 = prect->x; 1366 y1 = prect->y; 1367 if ((x2 = x1 + (int) prect->width) > MAXSHORT) 1368 x2 = MAXSHORT; 1369 if ((y2 = y1 + (int) prect->height) > MAXSHORT) 1370 y2 = MAXSHORT; 1371 if (x1 != x2 && y1 != y2) 1372 { 1373 pRgn->extents.x1 = x1; 1374 pRgn->extents.y1 = y1; 1375 pRgn->extents.x2 = x2; 1376 pRgn->extents.y2 = y2; 1377 pRgn->data = NULL; 1378 } 1379 return pRgn; 1380 } 1381 pData = xallocData(nrects); 1382 if (!pData) 1383 { 1384 RegionBreak (pRgn); 1385 return pRgn; 1386 } 1387 pBox = (BoxPtr) (pData + 1); 1388 for (i = nrects; --i >= 0; prect++) 1389 { 1390 x1 = prect->x; 1391 y1 = prect->y; 1392 if ((x2 = x1 + (int) prect->width) > MAXSHORT) 1393 x2 = MAXSHORT; 1394 if ((y2 = y1 + (int) prect->height) > MAXSHORT) 1395 y2 = MAXSHORT; 1396 if (x1 != x2 && y1 != y2) 1397 { 1398 pBox->x1 = x1; 1399 pBox->y1 = y1; 1400 pBox->x2 = x2; 1401 pBox->y2 = y2; 1402 pBox++; 1403 } 1404 } 1405 if (pBox != (BoxPtr) (pData + 1)) 1406 { 1407 pData->size = nrects; 1408 pData->numRects = pBox - (BoxPtr) (pData + 1); 1409 pRgn->data = pData; 1410 if (ctype != CT_YXBANDED) 1411 { 1412 Bool overlap; /* result ignored */ 1413 pRgn->extents.x1 = pRgn->extents.x2 = 0; 1414 RegionValidate(pRgn, &overlap); 1415 } 1416 else 1417 RegionSetExtents(pRgn); 1418 good(pRgn); 1419 } 1420 else 1421 { 1422 free(pData); 1423 } 1424 return pRgn; 1425} 1426 1427#define ExchangeSpans(a, b) \ 1428{ \ 1429 DDXPointRec tpt; \ 1430 int tw; \ 1431 \ 1432 tpt = spans[a]; spans[a] = spans[b]; spans[b] = tpt; \ 1433 tw = widths[a]; widths[a] = widths[b]; widths[b] = tw; \ 1434} 1435 1436/* ||| I should apply the merge sort code to rectangle sorting above, and see 1437 if mapping time can be improved. But right now I've been at work 12 hours, 1438 so forget it. 1439*/ 1440 1441static void QuickSortSpans( 1442 DDXPointRec spans[], 1443 int widths[], 1444 int numSpans) 1445{ 1446 int y; 1447 int i, j, m; 1448 DDXPointPtr r; 1449 1450 /* Always called with numSpans > 1 */ 1451 /* Sorts only by y, doesn't bother to sort by x */ 1452 1453 do 1454 { 1455 if (numSpans < 9) 1456 { 1457 /* Do insertion sort */ 1458 int yprev; 1459 1460 yprev = spans[0].y; 1461 i = 1; 1462 do 1463 { /* while i != numSpans */ 1464 y = spans[i].y; 1465 if (yprev > y) 1466 { 1467 /* spans[i] is out of order. Move into proper location. */ 1468 DDXPointRec tpt; 1469 int tw, k; 1470 1471 for (j = 0; y >= spans[j].y; j++) {} 1472 tpt = spans[i]; 1473 tw = widths[i]; 1474 for (k = i; k != j; k--) 1475 { 1476 spans[k] = spans[k-1]; 1477 widths[k] = widths[k-1]; 1478 } 1479 spans[j] = tpt; 1480 widths[j] = tw; 1481 y = spans[i].y; 1482 } /* if out of order */ 1483 yprev = y; 1484 i++; 1485 } while (i != numSpans); 1486 return; 1487 } 1488 1489 /* Choose partition element, stick in location 0 */ 1490 m = numSpans / 2; 1491 if (spans[m].y > spans[0].y) ExchangeSpans(m, 0); 1492 if (spans[m].y > spans[numSpans-1].y) ExchangeSpans(m, numSpans-1); 1493 if (spans[m].y > spans[0].y) ExchangeSpans(m, 0); 1494 y = spans[0].y; 1495 1496 /* Partition array */ 1497 i = 0; 1498 j = numSpans; 1499 do 1500 { 1501 r = &(spans[i]); 1502 do 1503 { 1504 r++; 1505 i++; 1506 } while (i != numSpans && r->y < y); 1507 r = &(spans[j]); 1508 do 1509 { 1510 r--; 1511 j--; 1512 } while (y < r->y); 1513 if (i < j) 1514 ExchangeSpans(i, j); 1515 } while (i < j); 1516 1517 /* Move partition element back to middle */ 1518 ExchangeSpans(0, j); 1519 1520 /* Recurse */ 1521 if (numSpans-j-1 > 1) 1522 QuickSortSpans(&spans[j+1], &widths[j+1], numSpans-j-1); 1523 numSpans = j; 1524 } while (numSpans > 1); 1525} 1526 1527#define NextBand() \ 1528{ \ 1529 clipy1 = pboxBandStart->y1; \ 1530 clipy2 = pboxBandStart->y2; \ 1531 pboxBandEnd = pboxBandStart + 1; \ 1532 while (pboxBandEnd != pboxLast && pboxBandEnd->y1 == clipy1) { \ 1533 pboxBandEnd++; \ 1534 } \ 1535 for (; ppt != pptLast && ppt->y < clipy1; ppt++, pwidth++) {} \ 1536} 1537 1538/* 1539 Clip a list of scanlines to a region. The caller has allocated the 1540 space. FSorted is non-zero if the scanline origins are in ascending 1541 order. 1542 returns the number of new, clipped scanlines. 1543*/ 1544 1545int 1546RegionClipSpans( 1547 RegionPtr prgnDst, 1548 DDXPointPtr ppt, 1549 int *pwidth, 1550 int nspans, 1551 DDXPointPtr pptNew, 1552 int *pwidthNew, 1553 int fSorted) 1554{ 1555 DDXPointPtr pptLast; 1556 int *pwidthNewStart; /* the vengeance of Xerox! */ 1557 int y, x1, x2; 1558 int numRects; 1559 1560 good(prgnDst); 1561 pptLast = ppt + nspans; 1562 pwidthNewStart = pwidthNew; 1563 1564 if (!prgnDst->data) 1565 { 1566 /* Do special fast code with clip boundaries in registers(?) */ 1567 /* It doesn't pay much to make use of fSorted in this case, 1568 so we lump everything together. */ 1569 1570 int clipx1, clipx2, clipy1, clipy2; 1571 1572 clipx1 = prgnDst->extents.x1; 1573 clipy1 = prgnDst->extents.y1; 1574 clipx2 = prgnDst->extents.x2; 1575 clipy2 = prgnDst->extents.y2; 1576 1577 for (; ppt != pptLast; ppt++, pwidth++) 1578 { 1579 y = ppt->y; 1580 x1 = ppt->x; 1581 if (clipy1 <= y && y < clipy2) 1582 { 1583 x2 = x1 + *pwidth; 1584 if (x1 < clipx1) x1 = clipx1; 1585 if (x2 > clipx2) x2 = clipx2; 1586 if (x1 < x2) 1587 { 1588 /* part of span in clip rectangle */ 1589 pptNew->x = x1; 1590 pptNew->y = y; 1591 *pwidthNew = x2 - x1; 1592 pptNew++; 1593 pwidthNew++; 1594 } 1595 } 1596 } /* end for */ 1597 1598 } 1599 else if ((numRects = prgnDst->data->numRects)) 1600 { 1601 /* Have to clip against many boxes */ 1602 BoxPtr pboxBandStart, pboxBandEnd; 1603 BoxPtr pbox; 1604 BoxPtr pboxLast; 1605 int clipy1, clipy2; 1606 1607 /* In this case, taking advantage of sorted spans gains more than 1608 the sorting costs. */ 1609 if ((! fSorted) && (nspans > 1)) 1610 QuickSortSpans(ppt, pwidth, nspans); 1611 1612 pboxBandStart = RegionBoxptr(prgnDst); 1613 pboxLast = pboxBandStart + numRects; 1614 1615 NextBand(); 1616 1617 for (; ppt != pptLast; ) 1618 { 1619 y = ppt->y; 1620 if (y < clipy2) 1621 { 1622 /* span is in the current band */ 1623 pbox = pboxBandStart; 1624 x1 = ppt->x; 1625 x2 = x1 + *pwidth; 1626 do 1627 { /* For each box in band */ 1628 int newx1, newx2; 1629 1630 newx1 = x1; 1631 newx2 = x2; 1632 if (newx1 < pbox->x1) newx1 = pbox->x1; 1633 if (newx2 > pbox->x2) newx2 = pbox->x2; 1634 if (newx1 < newx2) 1635 { 1636 /* Part of span in clip rectangle */ 1637 pptNew->x = newx1; 1638 pptNew->y = y; 1639 *pwidthNew = newx2 - newx1; 1640 pptNew++; 1641 pwidthNew++; 1642 } 1643 pbox++; 1644 } while (pbox != pboxBandEnd); 1645 ppt++; 1646 pwidth++; 1647 } 1648 else 1649 { 1650 /* Move to next band, adjust ppt as needed */ 1651 pboxBandStart = pboxBandEnd; 1652 if (pboxBandStart == pboxLast) 1653 break; /* We're completely done */ 1654 NextBand(); 1655 } 1656 } 1657 } 1658 return pwidthNew - pwidthNewStart; 1659} 1660