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