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