1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2003-2004, OpenBeOS 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: RegionSupport.cpp 23 // Author: Stefano Ceccherini (burton666@libero.it) 24 // Description: Class that does the dirty work for BRegion. 25 // 26 //------------------------------------------------------------------------------ 27 28 // TODO: check for possible performance issue in ROr() and RSub(). 29 // Check if inlining some methods can make us be faster. 30 31 // Standard Includes ----------------------------------------------------------- 32 #include <cstring> 33 #include <new> 34 35 // System Includes ------------------------------------------------------------- 36 #include <Debug.h> 37 #include <Region.h> 38 39 // Private Includes ------------------------------------------------------------- 40 #include <clipping.h> 41 #include <RegionSupport.h> 42 43 // Constants -------------------------------------------------------------------- 44 const int32 kMaxPoints = 1024; 45 const int32 kMaxVerticalExtent = 0x10000000; 46 47 48 #define TRACE_REGION 0 49 #define ARGS (const char *, ...) 50 #if TRACE_REGION 51 #define RTRACE(ARGS) printf ARGS 52 #define CALLED() printf("%s\n", __PRETTY_FUNCTION__) 53 #else 54 #define RTRACE(ARGS) ; 55 #define CALLED() 56 #endif 57 58 using namespace std; 59 60 /*! \brief zeroes the given region, setting its rect count to 0, 61 and invalidating its bound rectangle. 62 \param region The region to be zeroed. 63 */ 64 void 65 BRegion::Support::ZeroRegion(BRegion *region) 66 { 67 CALLED(); 68 69 region->count = 0; 70 region->bound.left = 0x7ffffffd; 71 region->bound.top = 0x7ffffffd; 72 region->bound.right = 0x80000003; 73 region->bound.bottom = 0x80000003; 74 } 75 76 77 /*! \brief clear the given region, setting its rect count to 0, 78 and setting its bound rectangle to 0xFFFFFFF, 0xFFFFFFF, 0xF0000001, 0xF0000001. 79 \param region The region to be cleared. 80 */ 81 void 82 BRegion::Support::ClearRegion(BRegion *region) 83 { 84 CALLED(); 85 86 // XXX: What is it used for ? 87 // Could be that a cleared region represents an infinite one ? 88 89 region->count = 0; 90 region->bound.left = 0xfffffff; 91 region->bound.top = 0xfffffff; 92 region->bound.right = 0xf0000001; 93 region->bound.bottom = 0xf0000001; 94 } 95 96 97 /*! \brief Copy a region to another. 98 \param source The region to be copied. 99 \param dest The destination region. 100 */ 101 void 102 BRegion::Support::CopyRegion(BRegion *source, BRegion *dest) 103 { 104 CALLED(); 105 ASSERT(source); 106 ASSERT(dest); 107 ASSERT(source != dest); 108 109 // If there is not enough memory, allocate 110 if (dest->data_size < source->count) { 111 free(dest->data); 112 dest->data_size = source->count + 8; 113 dest->data = (clipping_rect *)malloc(dest->data_size * sizeof(clipping_rect)); 114 } 115 116 dest->count = source->count; 117 118 // Copy rectangles and bounds. 119 memcpy(dest->data, source->data, source->count * sizeof(clipping_rect)); 120 dest->bound = source->bound; 121 } 122 123 124 /*! \brief Modify the destination region to be the intersection of the two given regions. 125 \param first The first region to be intersected. 126 \param second The second region to be intersected. 127 \param dest The destination region. 128 129 This function is a sort of method selector. It checks for some special 130 cases, then it calls the appropriate specialized function. 131 */ 132 void 133 BRegion::Support::AndRegion(BRegion *first, BRegion *second, BRegion *dest) 134 { 135 CALLED(); 136 ASSERT(first); 137 ASSERT(second); 138 ASSERT(dest); 139 140 clipping_rect intersection = sect_rect(first->bound, second->bound); 141 142 if (first->count == 0 || second->count == 0 || !valid_rect(intersection)) 143 ZeroRegion(dest); 144 145 else if (first->count == 1 && second->count == 1) { 146 dest->data[0] = intersection; 147 dest->bound = intersection; 148 dest->count = 1; 149 } 150 else if (first->count > 1 && second->count == 1) 151 AndRegion1ToN(second, first, dest); 152 153 else if (first->count == 1 && second->count > 1) 154 AndRegion1ToN(first, second, dest); 155 156 else 157 AndRegionComplex(first, second, dest); 158 } 159 160 161 /*! \brief Modify the destination region to be the union of the two given regions. 162 \param first The first region to be merged. 163 \param second The second region to be merged. 164 \param dest The destination region. 165 166 This function is a sort of method selector. It checks for some special 167 cases, then it calls the appropriate specialized function. 168 */ 169 void 170 BRegion::Support::OrRegion(BRegion *first, BRegion *second, BRegion *dest) 171 { 172 CALLED(); 173 ASSERT(first); 174 ASSERT(second); 175 ASSERT(dest); 176 177 BRegion *regionA, *regionB; 178 179 // A little trick, to save some work... 180 if (first->count != 0) { 181 regionA = first; 182 regionB = second; 183 } else { 184 regionA = second; 185 regionB = first; 186 } 187 188 if (regionB->count == 0) 189 CopyRegion(regionA, dest); 190 else { 191 if (regionB->bound.top > regionA->bound.bottom) 192 AppendRegion(regionA, regionB, dest); 193 194 else if (regionA->bound.top > regionB->bound.bottom) 195 AppendRegion(regionB, regionA, dest); 196 197 else if (regionA->bound.left > regionB->bound.right) 198 OrRegionNoX(regionB, regionA, dest); 199 200 else if (regionB->bound.left > regionA->bound.right) 201 OrRegionNoX(regionA, regionB, dest); 202 203 else if (regionA->count == 1) 204 OrRegion1ToN(regionA, regionB, dest); 205 206 else if (regionB->count == 1) 207 OrRegion1ToN(regionB, regionA, dest); 208 209 else 210 OrRegionComplex(regionA, regionB, dest); 211 } 212 } 213 214 215 /*! \brief Modify the destination region to be the difference of the two given regions. 216 \param first The subtraend region. 217 \param second The minuend region. 218 \param dest The destination region. 219 220 This function is a sort of method selector. It checks for some special 221 cases, then it calls the appropriate specialized function. 222 */ 223 void 224 BRegion::Support::SubRegion(BRegion *first, BRegion *second, BRegion *dest) 225 { 226 CALLED(); 227 ASSERT(first); 228 ASSERT(second); 229 ASSERT(dest); 230 231 if (first->count == 0) 232 ZeroRegion(dest); 233 234 else if (second->count == 0 || !rects_intersect(first->bound, second->bound)) 235 CopyRegion(first, dest); 236 237 else 238 SubRegionComplex(second, first, dest); 239 } 240 241 242 /*! \brief Cleanup the region, by merging rects that can be merged. 243 \param region The region to be cleaned. 244 */ 245 void 246 BRegion::Support::CleanupRegion(BRegion *region) 247 { 248 CALLED(); 249 250 long oldCount; 251 252 do { 253 oldCount = region->count; 254 CleanupRegionVertical(region); 255 CleanupRegionHorizontal(region); 256 } while (region->count < oldCount); 257 } 258 259 260 /*! \brief Cleanup the region vertically. 261 \param region The region to be cleaned. 262 */ 263 void 264 BRegion::Support::CleanupRegionVertical(BRegion *region) 265 { 266 CALLED(); 267 268 clipping_rect testRect = { 1, 1, -1, -2 }; 269 long newCount = -1; 270 271 for (long x = 0; x < region->count; x++) { 272 clipping_rect &rect = region->data[x]; 273 274 if (rect.left == testRect.left && rect.right == testRect.right 275 && rect.top == testRect.bottom + 1) { 276 277 ASSERT(newCount >= 0); 278 region->data[newCount].bottom = rect.bottom; 279 280 } else { 281 newCount++; 282 region->data[newCount] = region->data[x]; 283 testRect = region->data[x]; 284 } 285 } 286 region->count = newCount + 1; 287 } 288 289 290 /*! \brief Cleanup the region horizontally. 291 \param region The region to be cleaned. 292 */ 293 void 294 BRegion::Support::CleanupRegionHorizontal(BRegion *region) 295 { 296 CALLED(); 297 298 clipping_rect testRect = { 1, 1, -2, -1 }; 299 long newCount = -1; 300 301 for (long x = 0; x < region->count; x++) { 302 clipping_rect &rect = region->data[x]; 303 304 if (rect.top == testRect.top && rect.bottom == testRect.bottom 305 && rect.left == testRect.right + 1) { 306 307 ASSERT(newCount >= 0); 308 region->data[newCount].right = rect.right; 309 310 } else { 311 newCount++; 312 region->data[newCount] = rect; 313 } 314 testRect = region->data[newCount]; 315 } 316 region->count = newCount + 1; 317 } 318 319 320 // Helper method to swap two rects 321 static inline void 322 SwapRects(clipping_rect &rect, clipping_rect &anotherRect) 323 { 324 clipping_rect tmpRect; 325 tmpRect = rect; 326 rect = anotherRect; 327 anotherRect = tmpRect; 328 } 329 330 331 /*! \brief Sorts the given rects by their top value. 332 \param rects A pointer to an array of clipping_rects. 333 \param count The number of rectangles in the array. 334 */ 335 void 336 BRegion::Support::SortRects(clipping_rect *rects, long count) 337 { 338 CALLED(); 339 340 bool again; //flag that tells we changed rects positions 341 342 if (count == 2) { 343 if (rects[0].top > rects[1].top) 344 SwapRects(rects[0], rects[1]); 345 346 } else if (count > 2) { 347 do { 348 again = false; 349 for (long c = 1; c < count; c++) { 350 if (rects[c - 1].top > rects[c].top) { 351 SwapRects(rects[c - 1], rects[c]); 352 again = true; 353 } 354 } 355 } while (again); 356 } 357 } 358 359 360 // Helper methods to swap transition points in two given arrays 361 static inline void 362 SwapTrans(long *leftPoints, long *rightPoints, long index1, long index2) 363 { 364 // First, swap the left points 365 long tmp = leftPoints[index1]; 366 leftPoints[index1] = leftPoints[index2]; 367 leftPoints[index2] = tmp; 368 369 // then the right points 370 tmp = rightPoints[index1]; 371 rightPoints[index1] = rightPoints[index2]; 372 rightPoints[index2] = tmp; 373 } 374 375 376 void 377 BRegion::Support::SortTrans(long *lptr1, long *lptr2, long count) 378 { 379 CALLED(); 380 381 bool again; //flag that tells we changed trans positions 382 383 if (count == 2) { 384 if (lptr1[0] > lptr1[1]) 385 SwapTrans(lptr1, lptr2, 0, 1); 386 387 } else if (count > 2) { 388 do { 389 again = false; 390 for (long c = 1; c < count; c++) { 391 if (lptr1[c - 1] > lptr1[c]) { 392 SwapTrans(lptr1, lptr2, c - 1, c); 393 again = true; 394 } 395 } 396 } while (again); 397 } 398 } 399 400 401 /*! \brief Copy a region to another, allocating some additional memory in the destination region. 402 \param source The region to be copied. 403 \param dest The destination region. 404 \param count Amount of additional memory to be allocated in the destination region. 405 */ 406 void 407 BRegion::Support::CopyRegionMore(BRegion *source, BRegion *dest, long count) 408 { 409 CALLED(); 410 ASSERT(source); 411 ASSERT(dest); 412 ASSERT(source != dest); 413 414 // If there is not enough memory, allocate 415 if (dest->data_size < source->count) { 416 free(dest->data); 417 dest->data_size = source->count + count; 418 dest->data = (clipping_rect *)malloc(dest->data_size * sizeof(clipping_rect)); 419 } 420 421 dest->count = source->count; 422 423 // Copy rectangles and bounds. 424 memcpy(dest->data, source->data, source->count * sizeof(clipping_rect)); 425 dest->bound = source->bound; 426 } 427 428 429 /*! \brief Modify the destination region to be the intersection of the two given regions. 430 \param first The first region to be intersected. 431 \param second The second region to be intersected. 432 \param dest The destination region. 433 434 Called by and_region() when the intersection is complex. 435 */ 436 void 437 BRegion::Support::AndRegionComplex(BRegion *first, BRegion *second, BRegion *dest) 438 { 439 CALLED(); 440 ASSERT(first); 441 ASSERT(second); 442 ASSERT(dest); 443 444 ZeroRegion(dest); 445 446 for (long f = 0; f < first->count; f++) { 447 for (long s = 0; s < second->count; s++) { 448 clipping_rect testRect = sect_rect(first->data[f], second->data[s]); 449 if (valid_rect(testRect)) 450 dest->_AddRect(testRect); 451 } 452 } 453 454 if (dest->count > 1) 455 SortRects(dest->data, dest->count); 456 } 457 458 459 /*! \brief Modify the destination region to be the intersection of the two given regions. 460 \param first The first region to be intersected. 461 \param second The second region to be intersected. 462 \param dest The destination region. 463 464 Called by and_region() when one of the two region contains just one rect. 465 */ 466 void 467 BRegion::Support::AndRegion1ToN(BRegion *first, BRegion *second, BRegion *dest) 468 { 469 CALLED(); 470 ASSERT(first); 471 ASSERT(second); 472 ASSERT(dest); 473 474 // The easy case first: We already know that the regions intersect, 475 // so we check if the first region contains the second. 476 // If it's the case, the intersection is exactly the second region. 477 if (first->bound.top <= second->bound.top 478 && first->bound.bottom >= second->bound.bottom 479 && first->bound.left <= second->bound.left 480 && first->bound.right >= second->bound.right) 481 CopyRegion(second, dest); 482 else { 483 // Otherwise, we check the rect of the first region against the rects 484 // of the second, and we add their intersections to the destination region 485 ZeroRegion(dest); 486 for (long x = 0; x < second->count; x++) { 487 clipping_rect testRect = sect_rect(first->data[0], second->data[x]); 488 if (valid_rect(testRect)) 489 dest->_AddRect(testRect); 490 } 491 } 492 } 493 494 495 /*! \brief Modify the destination region to be the union of the two given regions. 496 \param first The first region to be or-ed. 497 \param second The second region to be or-ed. 498 \param dest The destination region. 499 500 This function is called by or_region when the two regions don't intersect, 501 and when the second region top coordinate is bigger than first region's bottom 502 coordinate. 503 */ 504 void 505 BRegion::Support::AppendRegion(BRegion *first, BRegion *second, BRegion *dest) 506 { 507 CALLED(); 508 ASSERT(first); 509 ASSERT(second); 510 ASSERT(dest); 511 512 CopyRegion(first, dest); 513 514 for (long c = 0; c < second->count; c++) 515 dest->_AddRect(second->data[c]); 516 } 517 518 519 void 520 BRegion::Support::ROr(long top, long bottom, BRegion *first, BRegion *second, BRegion *dest, long *indexA, long *indexB) 521 { 522 CALLED(); 523 524 int32 stackLefts[kMaxPoints]; 525 int32 stackRights[kMaxPoints]; 526 527 int32 *lefts = stackLefts; 528 int32 *rights = stackRights; 529 530 long i1 = *indexA; 531 long i2 = *indexB; 532 533 *indexA = -1; 534 *indexB = -1; 535 536 long foundCount = 0; 537 long x = 0; 538 539 // allocate arrays on the heap, if the ones one the stack are too small 540 int32 *allocatedBuffer = NULL; 541 int32 maxCount = first->count - i1 + second->count - i2; 542 543 if (maxCount > kMaxPoints) { 544 RTRACE(("Stack space isn't sufficient. Allocating %ld bytes on the heap...\n", 545 2 * maxCount)); 546 lefts = allocatedBuffer = new(nothrow) int32[2 * maxCount]; 547 if (!allocatedBuffer) 548 return; 549 rights = allocatedBuffer + maxCount; 550 } 551 552 // Store left and right points to the appropriate array 553 for (x = i1; x < first->count; x++) { 554 555 // Look if this rect can be used next time we are called, 556 // thus correctly maintaining the "index" parameters. 557 if (first->data[x].bottom >= top && *indexA == -1) 558 *indexA = x; 559 560 if (first->data[x].top <= top && first->data[x].bottom >= bottom) { 561 lefts[foundCount] = first->data[x].left; 562 rights[foundCount] = first->data[x].right; 563 foundCount++; 564 } else if (first->data[x].top > bottom) 565 break; 566 } 567 568 if (*indexA == -1) 569 *indexA = i1; 570 571 for (x = i2; x < second->count; x++) { 572 if (second->data[x].bottom >= top && *indexB == -1) 573 *indexB = x; 574 575 if (second->data[x].top <= top && second->data[x].bottom >= bottom) { 576 lefts[foundCount] = second->data[x].left; 577 rights[foundCount] = second->data[x].right; 578 foundCount++; 579 } else if (second->data[x].top > bottom) 580 break; 581 } 582 583 if (*indexB == -1) 584 *indexB = i2; 585 586 if (foundCount > 1) 587 SortTrans(lefts, rights, foundCount); 588 589 ASSERT(foundCount > 0); 590 591 clipping_rect rect; 592 rect.top = top; 593 rect.bottom = bottom; 594 595 // Check if a rect intersects with the next one. 596 // If so, merge the two rects, if not, just add the rect. 597 long current = 0; 598 while (current < foundCount) { 599 long next = current + 1; 600 601 rect.left = lefts[current]; 602 rect.right = rights[current]; 603 604 while (next < foundCount && rect.right >= lefts[next]) { 605 if (rect.right < rights[next]) 606 rect.right = rights[next]; 607 next++; 608 } 609 610 dest->_AddRect(rect); 611 current = next; 612 } 613 614 if (allocatedBuffer) { 615 RTRACE(("Freeing heap...\n")); 616 delete[] allocatedBuffer; 617 } 618 } 619 620 621 /*! \brief Divides the plane into horizontal bands, then passes those bands to r_or 622 which does the real work. 623 \param first The first region to be or-ed. 624 \param second The second region to be or-ed. 625 \param dest The destination region. 626 */ 627 void 628 BRegion::Support::OrRegionComplex(BRegion *first, BRegion *second, BRegion *dest) 629 { 630 CALLED(); 631 long a = 0, b = 0; 632 633 int32 top; 634 int32 bottom = min_c(first->bound.top, second->bound.top) - 1; 635 do { 636 long x; 637 top = bottom + 1; 638 bottom = kMaxVerticalExtent; 639 640 for (x = a; x < first->count; x++) { 641 int32 n = first->data[x].top - 1; 642 if (n >= top && n < bottom) 643 bottom = n; 644 if (first->data[x].bottom >= top && first->data[x].bottom < bottom) 645 bottom = first->data[x].bottom; 646 } 647 648 for (x = b; x < second->count; x++) { 649 int32 n = second->data[x].top - 1; 650 if (n >= top && n < bottom) 651 bottom = n; 652 if (second->data[x].bottom >= top && second->data[x].bottom < bottom) 653 bottom = second->data[x].bottom; 654 } 655 656 // We can stand a region which extends to kMaxVerticalExtent, not more 657 if (bottom >= kMaxVerticalExtent) 658 break; 659 660 ROr(top, bottom, first, second, dest, &a, &b); 661 662 } while (true); 663 664 CleanupRegion(dest); 665 } 666 667 668 /*! \brief Modify the destination region to be the union of the two given regions. 669 \param first The first region to be or-ed. 670 \param second The second region to be or-ed. 671 \param dest The destination region. 672 673 This function is called by or_region when one of the two regions contains just 674 one rect. 675 */ 676 void 677 BRegion::Support::OrRegion1ToN(BRegion *first, BRegion *second, BRegion *dest) 678 { 679 CALLED(); 680 ASSERT(first); 681 ASSERT(second); 682 ASSERT(dest); 683 684 // The easy case first: if the first region contains the second, 685 // the union is exactly the first region, since its bound is the 686 // only rectangle. 687 if (first->bound.top <= second->bound.top 688 && first->bound.bottom >= second->bound.bottom 689 && first->bound.left <= second->bound.left 690 && first->bound.right >= second->bound.right) 691 CopyRegion(first, dest); 692 else 693 OrRegionComplex(first, second, dest); 694 } 695 696 697 /*! \brief Modify the destination region to be the union of the two given regions. 698 \param first The first region to be or-ed. 699 \param second The second region to be or-ed. 700 \param dest The destination region. 701 702 This function is called by or_region when the two regions don't intersect. 703 */ 704 void 705 BRegion::Support::OrRegionNoX(BRegion *first, BRegion *second, BRegion *dest) 706 { 707 CALLED(); 708 ASSERT(first); 709 ASSERT(second); 710 ASSERT(dest); 711 712 ZeroRegion(dest); 713 714 long x; 715 716 if (first->count == 0) 717 for (x = 0; x < second->count; x++) 718 dest->_AddRect(second->data[x]); 719 720 else if (second->count == 0) 721 for (x = 0; x < first->count; x++) 722 dest->_AddRect(first->data[x]); 723 724 else { 725 long f = 0, s = 0; 726 727 while (f < first->count && s < second->count) { 728 729 if (first->data[f].top < second->data[s].top) { 730 dest->_AddRect(first->data[f]); 731 f++; 732 733 } else { 734 dest->_AddRect(second->data[s]); 735 s++; 736 } 737 } 738 739 if (f == first->count) 740 for (; s < second->count; s++) 741 dest->_AddRect(second->data[s]); 742 743 else if (s == second->count) 744 for (; f < first->count; f++) 745 dest->_AddRect(first->data[f]); 746 } 747 } 748 749 750 /*! \brief Divides the plane into horizontal bands, then passes those bands to r_sub 751 which does the real work. 752 \param first The subtraend region. 753 \param second The minuend region. 754 \param dest The destination region. 755 */ 756 void 757 BRegion::Support::SubRegionComplex(BRegion *first, BRegion *second, BRegion *dest) 758 { 759 CALLED(); 760 long a = 0, b = 0; 761 762 int32 top; 763 int32 bottom = min_c(first->bound.top, second->bound.top) - 1; 764 765 do { 766 long x; 767 top = bottom + 1; 768 bottom = kMaxVerticalExtent; 769 770 for (x = a; x < first->count; x++) { 771 int32 n = first->data[x].top - 1; 772 if (n >= top && n < bottom) 773 bottom = n; 774 if (first->data[x].bottom >= top && first->data[x].bottom < bottom) 775 bottom = first->data[x].bottom; 776 } 777 778 for (x = b; x < second->count; x++) { 779 int32 n = second->data[x].top - 1; 780 if (n >= top && n < bottom) 781 bottom = n; 782 if (second->data[x].bottom >= top && second->data[x].bottom < bottom) 783 bottom = second->data[x].bottom; 784 } 785 786 if (bottom >= kMaxVerticalExtent) 787 break; 788 789 RSub(top, bottom, first, second, dest, &a, &b); 790 791 } while (true); 792 793 CleanupRegion(dest); 794 } 795 796 797 void 798 BRegion::Support::RSub(long top, long bottom, BRegion *first, BRegion *second, BRegion *dest, long *indexA, long *indexB) 799 { 800 CALLED(); 801 802 int32 stackLeftsA[kMaxPoints / 2]; 803 int32 stackLeftsB[kMaxPoints / 2]; 804 int32 stackRightsA[kMaxPoints / 2]; 805 int32 stackRightsB[kMaxPoints / 2]; 806 807 int32 *leftsA = stackLeftsA; 808 int32 *leftsB = stackLeftsB; 809 int32 *rightsA = stackRightsA; 810 int32 *rightsB = stackRightsB; 811 812 long i1 = *indexA; 813 long i2 = *indexB; 814 815 *indexA = -1; 816 *indexB = -1; 817 818 long foundA = 0; 819 long foundB = 0; 820 long x = 0; 821 822 // allocate arrays on the heap, if the ones one the stack are too small 823 int32 *allocatedBuffer = NULL; 824 int32 maxCountA = first->count - i1; 825 int32 maxCountB = second->count - i2; 826 827 if (maxCountA + maxCountB > kMaxPoints) { 828 RTRACE(("Stack space isn't sufficient. Allocating %ld bytes on the heap...\n", 829 2 * (maxCountA + maxCountB))); 830 leftsA = allocatedBuffer = new(nothrow) int32[2 * (maxCountA + maxCountB)]; 831 if (!allocatedBuffer) 832 return; 833 rightsA = allocatedBuffer + maxCountA; 834 leftsB = rightsA + maxCountA; 835 rightsB = leftsB + maxCountB; 836 } 837 838 // Store left and right points to the appropriate array 839 for (x = i1; x < first->count; x++) { 840 841 // Look if this rect can be used next time we are called, 842 // thus correctly maintaining the "index" parameters. 843 if (first->data[x].bottom >= top && *indexA == -1) 844 *indexA = x; 845 846 if (first->data[x].top <= top && first->data[x].bottom >= bottom) { 847 leftsA[foundA] = first->data[x].left; 848 rightsA[foundA] = first->data[x].right; 849 foundA++; 850 } else if (first->data[x].top > bottom) 851 break; 852 } 853 854 if (*indexA == -1) 855 *indexA = i1; 856 857 if (foundA > 1) 858 SortTrans(leftsA, rightsA, foundA); 859 860 for (x = i2; x < second->count; x++) { 861 if (second->data[x].bottom >= top && *indexB == -1) 862 *indexB = x; 863 864 if (second->data[x].top <= top && second->data[x].bottom >= bottom) { 865 leftsB[foundB] = second->data[x].left; 866 rightsB[foundB] = second->data[x].right; 867 foundB++; 868 } else if (second->data[x].top > bottom) 869 break; 870 } 871 872 if (*indexB == -1) 873 *indexB = i2; 874 875 if (foundB > 1) 876 SortTrans(leftsB, rightsB, foundB); 877 878 // No minuend's rect, just add all the subtraend's rects. 879 if (foundA == 0) 880 for (x = 0; x < foundB; x++) { 881 clipping_rect rect = { leftsB[x], top, rightsB[x], bottom }; 882 dest->_AddRect(rect); 883 } 884 else if (foundB > 0) { 885 long f = 0, s = 0; 886 887 clipping_rect minuendRect; 888 minuendRect.top = top; 889 minuendRect.bottom = bottom; 890 minuendRect.left = 0x80000003; 891 892 clipping_rect subRect; 893 subRect.top = top; 894 subRect.bottom = bottom; 895 896 // We take the empty spaces between the minuend rects, and intersect 897 // these with the subtraend rects. We then add their intersection 898 // to the destination region. 899 do { 900 subRect.left = leftsB[s]; 901 subRect.right = rightsB[s]; 902 903 if (f != 0) 904 minuendRect.left = rightsA[f - 1] + 1; 905 906 minuendRect.right = leftsA[f] - 1; 907 908 if (leftsB[s] > minuendRect.right) { 909 if (++f > foundA) 910 break; 911 else 912 continue; 913 } 914 915 clipping_rect intersection = sect_rect(minuendRect, subRect); 916 917 if (valid_rect(intersection)) 918 dest->_AddRect(intersection); 919 920 if (rightsB[s] < minuendRect.left) 921 s++; 922 923 if (s >= foundB) 924 break; 925 926 f++; 927 928 } while (f < foundA); 929 930 // Last rect: we take the right coordinate of the last minuend rect 931 // as left coordinate of the rect to intersect, and the maximum possible 932 // value as the right coordinate. 933 minuendRect.left = rightsA[foundA - 1] + 1; 934 minuendRect.right = 0x7ffffffd; 935 936 // Skip the subtraend rects that could never intersect. 937 while (s < foundB && rightsB[s] < minuendRect.left) 938 s++; 939 940 for (long c = s; c < foundB; c++) { 941 subRect.left = leftsB[c]; 942 subRect.right = rightsB[c]; 943 944 clipping_rect intersection = sect_rect(minuendRect, subRect); 945 946 if (valid_rect(intersection)) 947 dest->_AddRect(intersection); 948 } 949 } 950 951 if (allocatedBuffer) { 952 RTRACE(("Freeing heap...\n")); 953 delete[] allocatedBuffer; 954 } 955 } 956 957 958 #undef TRACE_REGION 959