nsRegion.h (73483B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef nsRegion_h__ 8 #define nsRegion_h__ 9 10 #include <stddef.h> // for size_t 11 #include <stdint.h> // for uint32_t, uint64_t 12 13 #include <ostream> // for std::ostream 14 #include <utility> // for mozilla::Move 15 16 #include "mozilla/ArrayView.h" // for ArrayView 17 #include "mozilla/gfx/MatrixFwd.h" // for mozilla::gfx::Matrix4x4 18 #include "nsCoord.h" // for nscoord 19 #include "nsMargin.h" // for nsIntMargin 20 #include "nsPoint.h" // for nsIntPoint, nsPoint 21 #include "nsRect.h" // for mozilla::gfx::IntRect, nsRect 22 #include "nsRectAbsolute.h" 23 #include "nsRegionFwd.h" // for nsIntRegion 24 #include "nsString.h" // for nsCString 25 #include "nsTArray.h" 26 #include "pixman.h" 27 28 // Uncomment this line to get additional integrity checking. 29 // #define DEBUG_REGIONS 30 #ifdef DEBUG_REGIONS 31 # include <sstream> 32 #endif 33 34 /* For information on the internal representation look at pixman-region.c 35 * 36 * This replaces an older homebrew implementation of nsRegion. The 37 * representation used here may use more rectangles than nsRegion however, the 38 * representation is canonical. This means that there's no need for an 39 * Optimize() method because for a paticular region there is only one 40 * representation. This means that nsIntRegion will have more predictable 41 * performance characteristics than the old nsRegion and should not become 42 * degenerate. 43 * 44 * The pixman region code originates from X11 which has spread to a variety of 45 * projects including Qt, Gtk, Wine. It should perform reasonably well. 46 */ 47 48 enum class VisitSide { TOP, BOTTOM, LEFT, RIGHT }; 49 50 namespace regiondetails { 51 struct Band; 52 } 53 54 template <> 55 struct nsTArray_RelocationStrategy<regiondetails::Band> { 56 typedef nsTArray_RelocateUsingMoveConstructor<regiondetails::Band> Type; 57 }; 58 59 namespace regiondetails { 60 61 template <typename T, typename E> 62 class UncheckedArray : public T { 63 public: 64 using T::Elements; 65 using T::Length; 66 67 UncheckedArray() = default; 68 MOZ_IMPLICIT UncheckedArray(T&& aSrc) : T(std::move(aSrc)) {} 69 70 E& operator[](size_t aIndex) { return Elements()[aIndex]; } 71 const E& operator[](size_t aIndex) const { return Elements()[aIndex]; } 72 E& LastElement() { return Elements()[Length() - 1]; } 73 const E& LastElement() const { return Elements()[Length() - 1]; } 74 75 using iterator = E*; 76 using const_iterator = const E*; 77 78 iterator begin() { return iterator(Elements()); } 79 const_iterator begin() const { return const_iterator(Elements()); } 80 const_iterator cbegin() const { return begin(); } 81 iterator end() { return iterator(Elements() + Length()); } 82 const_iterator end() const { return const_iterator(Elements() + Length()); } 83 const_iterator cend() const { return end(); } 84 }; 85 86 struct Strip { 87 // Default constructor should never be called, but is required for 88 // vector::resize to compile. 89 Strip() { MOZ_CRASH(); } 90 Strip(int32_t aLeft, int32_t aRight) : left(aLeft), right(aRight) {} 91 92 bool operator!=(const Strip& aOther) const { 93 return left != aOther.left || right != aOther.right; 94 } 95 96 uint32_t Size() const { return right - left; } 97 98 int32_t left; 99 int32_t right; 100 }; 101 102 struct Band { 103 using Strip = regiondetails::Strip; 104 #ifndef DEBUG 105 using StripArray = 106 regiondetails::UncheckedArray<CopyableAutoTArray<Strip, 2>, Strip>; 107 #else 108 using StripArray = CopyableAutoTArray<Strip, 2>; 109 #endif 110 111 MOZ_IMPLICIT Band(const nsRectAbsolute& aRect) 112 : top(aRect.Y()), bottom(aRect.YMost()) { 113 mStrips.AppendElement(Strip{aRect.X(), aRect.XMost()}); 114 } 115 116 Band(const Band& aOther) = default; 117 Band(Band&& aOther) = default; 118 119 void InsertStrip(const Strip& aStrip) { 120 for (size_t i = 0; i < mStrips.Length(); i++) { 121 Strip& strip = mStrips[i]; 122 if (strip.left > aStrip.right) { 123 // Current strip is beyond aStrip, insert aStrip before. 124 mStrips.InsertElementAt(i, aStrip); 125 return; 126 } 127 128 if (strip.right < aStrip.left) { 129 // Current strip is before aStrip, try the next. 130 continue; 131 } 132 133 // Current strip intersects with aStrip, extend to the lext. 134 strip.left = std::min(strip.left, aStrip.left); 135 136 if (strip.right >= aStrip.right) { 137 // Current strip extends beyond aStrip, done. 138 return; 139 } 140 141 size_t next = i; 142 next++; 143 // Consume any subsequent strips intersecting with aStrip. 144 while (next < mStrips.Length() && mStrips[next].left <= aStrip.right) { 145 strip.right = mStrips[next].right; 146 147 mStrips.RemoveElementAt(next); 148 } 149 150 // Extend the strip in case the aStrip goes on beyond it. 151 strip.right = std::max(strip.right, aStrip.right); 152 return; 153 } 154 mStrips.AppendElement(aStrip); 155 } 156 157 void SubStrip(const Strip& aStrip) { 158 for (size_t i = 0; i < mStrips.Length(); i++) { 159 Strip& strip = mStrips[i]; 160 if (strip.left > aStrip.right) { 161 // Strip is entirely to the right of aStrip. Done. 162 return; 163 } 164 165 if (strip.right < aStrip.left) { 166 // Strip is entirely to the left of aStrip. Move on. 167 continue; 168 } 169 170 if (strip.left < aStrip.left) { 171 if (strip.right <= aStrip.right) { 172 strip.right = aStrip.left; 173 // This strip lies to the left of the start of aStrip. 174 continue; 175 } 176 177 // aStrip is completely contained by this strip. 178 Strip newStrip(aStrip.right, strip.right); 179 strip.right = aStrip.left; 180 if (i < mStrips.Length()) { 181 i++; 182 mStrips.InsertElementAt(i, newStrip); 183 } else { 184 mStrips.AppendElement(newStrip); 185 } 186 return; 187 } 188 189 // This strip lies to the right of the start of aStrip. 190 if (strip.right <= aStrip.right) { 191 // aStrip completely contains this strip. 192 mStrips.RemoveElementAt(i); 193 // Make sure we evaluate the strip now at i. This loop will increment. 194 i--; 195 continue; 196 } 197 strip.left = aStrip.right; 198 return; 199 } 200 } 201 202 bool Intersects(const Strip& aStrip) const { 203 for (const Strip& strip : mStrips) { 204 if (strip.left >= aStrip.right) { 205 return false; 206 } 207 208 if (strip.right <= aStrip.left) { 209 continue; 210 } 211 212 return true; 213 } 214 return false; 215 } 216 217 bool IntersectStripBounds(Strip& aStrip) const { 218 bool intersected = false; 219 220 int32_t rightMost; 221 for (const Strip& strip : mStrips) { 222 if (strip.left > aStrip.right) { 223 break; 224 } 225 226 if (strip.right <= aStrip.left) { 227 continue; 228 } 229 230 if (!intersected) { 231 // First intersection, this is where the left side begins. 232 aStrip.left = std::max(aStrip.left, strip.left); 233 } 234 235 intersected = true; 236 // Expand to the right for each intersecting strip found. 237 rightMost = std::min(strip.right, aStrip.right); 238 } 239 240 if (intersected) { 241 aStrip.right = rightMost; 242 } else { 243 aStrip.right = aStrip.left = 0; 244 } 245 return intersected; 246 } 247 248 bool ContainsStrip(const Strip& aStrip) const { 249 for (const Strip& strip : mStrips) { 250 if (strip.left > aStrip.left) { 251 return false; 252 } 253 254 if (strip.right >= aStrip.right) { 255 return true; 256 } 257 } 258 return false; 259 } 260 261 bool EqualStrips(const Band& aBand) const { 262 if (mStrips.Length() != aBand.mStrips.Length()) { 263 return false; 264 } 265 266 for (auto iter1 = mStrips.begin(), iter2 = aBand.mStrips.begin(); 267 iter1 != mStrips.end(); iter1++, iter2++) { 268 if (*iter1 != *iter2) { 269 return false; 270 } 271 } 272 273 return true; 274 } 275 276 void IntersectStrip(const Strip& aStrip) { 277 size_t i = 0; 278 279 while (i < mStrips.Length()) { 280 Strip& strip = mStrips[i]; 281 if (strip.right <= aStrip.left) { 282 mStrips.RemoveElementAt(i); 283 continue; 284 } 285 286 if (strip.left >= aStrip.right) { 287 mStrips.TruncateLength(i); 288 return; 289 } 290 291 strip.left = std::max(aStrip.left, strip.left); 292 strip.right = std::min(aStrip.right, strip.right); 293 i++; 294 } 295 } 296 297 void IntersectStrips(const Band& aOther) { 298 auto iter = mStrips.begin(); 299 auto iterOther = aOther.mStrips.begin(); 300 301 StripArray newStrips; 302 303 // This function finds the intersection between two sets of strips. 304 while (true) { 305 while (true) { 306 while (iter != mStrips.end() && iter->right <= iterOther->left) { 307 // Increment our current strip until it ends beyond aOther's current 308 // strip. 309 iter++; 310 } 311 312 if (iter == mStrips.end()) { 313 // End of our strips. Done. 314 break; 315 } 316 317 while (iterOther != aOther.mStrips.end() && 318 iterOther->right <= iter->left) { 319 // Increment aOther's current strip until it lies beyond our current 320 // strip. 321 iterOther++; 322 } 323 324 if (iterOther == aOther.mStrips.end()) { 325 // End of aOther's strips. Done. 326 break; 327 } 328 329 if (iterOther->left < iter->right) { 330 // Intersection! 331 break; 332 } 333 } 334 335 if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) { 336 break; 337 } 338 339 newStrips.AppendElement(Strip(std::max(iter->left, iterOther->left), 340 std::min(iterOther->right, iter->right))); 341 342 if (iterOther->right < iter->right) { 343 iterOther++; 344 if (iterOther == aOther.mStrips.end()) { 345 break; 346 } 347 } else { 348 iter++; 349 } 350 } 351 352 mStrips = std::move(newStrips); 353 } 354 355 bool Intersects(const Band& aOther) const { 356 auto iter = mStrips.begin(); 357 auto iterOther = aOther.mStrips.begin(); 358 359 // This function finds the intersection between two sets of strips. 360 while (true) { 361 while (true) { 362 while (iter != mStrips.end() && iter->right <= iterOther->left) { 363 // Increment our current strip until it ends beyond aOther's current 364 // strip. 365 iter++; 366 } 367 368 if (iter == mStrips.end()) { 369 // End of our strips. Done. 370 break; 371 } 372 373 while (iterOther != aOther.mStrips.end() && 374 iterOther->right <= iter->left) { 375 // Increment aOther's current strip until it lies beyond our current 376 // strip. 377 iterOther++; 378 } 379 380 if (iterOther == aOther.mStrips.end()) { 381 // End of aOther's strips. Done. 382 break; 383 } 384 385 if (iterOther->left < iter->right) { 386 // Intersection! 387 break; 388 } 389 } 390 391 if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) { 392 break; 393 } 394 395 return true; 396 } 397 return false; 398 } 399 400 void SubStrips(const Band& aOther) { 401 size_t idx = 0; 402 auto iterOther = aOther.mStrips.begin(); 403 404 // This function finds the intersection between two sets of strips. 405 while (true) { 406 while (true) { 407 while (idx < mStrips.Length() && 408 mStrips[idx].right <= iterOther->left) { 409 // Increment our current strip until it ends beyond aOther's current 410 // strip. 411 idx++; 412 } 413 414 if (idx == mStrips.Length()) { 415 // End of our strips. Done. 416 break; 417 } 418 419 while (iterOther != aOther.mStrips.end() && 420 iterOther->right <= mStrips[idx].left) { 421 // Increment aOther's current strip until it lies beyond our current 422 // strip. 423 iterOther++; 424 } 425 426 if (iterOther == aOther.mStrips.end()) { 427 // End of aOther's strips. Done. 428 break; 429 } 430 431 if (iterOther->left < mStrips[idx].right) { 432 // Intersection! 433 break; 434 } 435 } 436 437 if (idx == mStrips.Length() || iterOther == aOther.mStrips.end()) { 438 break; 439 } 440 441 if (mStrips[idx].left < iterOther->left) { 442 size_t oldIdx = idx; 443 // Our strip starts beyond other's 444 if (mStrips[idx].right > iterOther->right) { 445 // Our strip ends beyond other's as well. 446 Strip newStrip(mStrips[idx]); 447 newStrip.left = iterOther->right; 448 mStrips.InsertElementAt(idx + 1, newStrip); 449 idx++; 450 } 451 mStrips[oldIdx].right = iterOther->left; 452 // Either idx was just incremented, or the current index no longer 453 // intersects with iterOther. 454 continue; 455 } else if (mStrips[idx].right > iterOther->right) { 456 mStrips[idx].left = iterOther->right; 457 // Current strip no longer intersects, continue. 458 iterOther++; 459 if (iterOther == aOther.mStrips.end()) { 460 break; 461 } 462 continue; 463 } 464 465 // Our current strip is completely contained by the other strip. 466 mStrips.RemoveElementAt(idx); 467 } 468 } 469 470 int32_t top; 471 int32_t bottom; 472 StripArray mStrips; 473 }; 474 } // namespace regiondetails 475 476 class nsRegion { 477 public: 478 using Band = regiondetails::Band; 479 using Strip = regiondetails::Strip; 480 #ifndef DEBUG 481 using BandArray = regiondetails::UncheckedArray<nsTArray<Band>, Band>; 482 using StripArray = regiondetails::UncheckedArray<AutoTArray<Strip, 2>, Strip>; 483 #else 484 using BandArray = nsTArray<Band>; 485 using StripArray = AutoTArray<Strip, 2>; 486 #endif 487 488 typedef nsRect RectType; 489 typedef nsPoint PointType; 490 typedef nsMargin MarginType; 491 492 nsRegion() = default; 493 MOZ_IMPLICIT nsRegion(const nsRect& aRect) { 494 mBounds = nsRectAbsolute::FromRect(aRect); 495 } 496 MOZ_IMPLICIT nsRegion(const nsRectAbsolute& aRect) { mBounds = aRect; } 497 explicit nsRegion(mozilla::gfx::ArrayView<pixman_box32_t> aRects) { 498 for (uint32_t i = 0; i < aRects.Length(); i++) { 499 AddRect(BoxToRect(aRects[i])); 500 } 501 } 502 503 nsRegion(const nsRegion& aRegion) { Copy(aRegion); } 504 nsRegion(nsRegion&& aRegion) 505 : mBands(std::move(aRegion.mBands)), mBounds(aRegion.mBounds) { 506 aRegion.SetEmpty(); 507 } 508 nsRegion& operator=(nsRegion&& aRegion) { 509 mBands = std::move(aRegion.mBands); 510 mBounds = aRegion.mBounds; 511 aRegion.SetEmpty(); 512 return *this; 513 } 514 nsRegion& operator=(const nsRect& aRect) { 515 Copy(aRect); 516 return *this; 517 } 518 nsRegion& operator=(const nsRegion& aRegion) { 519 Copy(aRegion); 520 return *this; 521 } 522 bool operator==(const nsRegion& aRgn) const { return IsEqual(aRgn); } 523 bool operator!=(const nsRegion& aRgn) const { return !(*this == aRgn); } 524 525 friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m); 526 void OutputToStream(std::string aObjName, std::ostream& stream) const; 527 528 private: 529 #ifdef DEBUG_REGIONS 530 class OperationStringGenerator { 531 public: 532 virtual ~OperationStringGenerator() = default; 533 534 virtual void OutputOp() = 0; 535 }; 536 #endif 537 public: 538 void AssertStateInternal() const; 539 void AssertState() const { 540 #ifdef DEBUG_REGIONS 541 AssertStateInternal(); 542 #endif 543 } 544 545 private: 546 void And(BandArray& aOut, const BandArray& aIn1, const BandArray& aIn2) { 547 size_t idx = 0; 548 size_t idxOther = 0; 549 550 // This algorithm essentially forms a new list of bands, by iterating over 551 // both regions' lists of band simultaneously, and building a new band 552 // wherever the two regions intersect. 553 while (true) { 554 while (true) { 555 while (idx != aIn1.Length() && aIn1[idx].bottom <= aIn2[idxOther].top) { 556 // Increment our current band until it ends beyond aOther's current 557 // band. 558 idx++; 559 } 560 561 if (idx == aIn1.Length()) { 562 // This region is out of bands, the other region's future bands are 563 // ignored. 564 break; 565 } 566 567 while (idxOther != aIn2.Length() && 568 aIn2[idxOther].bottom <= aIn1[idx].top) { 569 // Increment aOther's current band until it ends beyond our current 570 // band. 571 idxOther++; 572 } 573 574 if (idxOther == aIn2.Length()) { 575 // The other region's bands are all processed, all our future bands 576 // are ignored. 577 break; 578 } 579 580 if (aIn2[idxOther].top < aIn1[idx].bottom) { 581 // We know the other band's bottom lies beyond our band's top because 582 // otherwise we would've incremented above. Intersecting bands found. 583 break; 584 } 585 } 586 587 if (idx == aIn1.Length() || idxOther == aIn2.Length()) { 588 // The above loop executed a break because we're done. 589 break; 590 } 591 592 Band newBand(aIn1[idx]); 593 // The new band is the intersection of the two current bands from both 594 // regions. 595 newBand.top = std::max(aIn1[idx].top, aIn2[idxOther].top); 596 newBand.bottom = std::min(aIn1[idx].bottom, aIn2[idxOther].bottom); 597 newBand.IntersectStrips(aIn2[idxOther]); 598 599 if (newBand.mStrips.Length()) { 600 // The intersecting area of the bands had overlapping strips, if it is 601 // identical to the band above it merge, otherwise append. 602 if (aOut.Length() && aOut.LastElement().bottom == newBand.top && 603 aOut.LastElement().EqualStrips(newBand)) { 604 aOut.LastElement().bottom = newBand.bottom; 605 } else { 606 aOut.AppendElement(std::move(newBand)); 607 } 608 } 609 610 if (aIn2[idxOther].bottom < aIn1[idx].bottom) { 611 idxOther++; 612 if (idxOther == aIn2.Length()) { 613 // Since we will access idxOther the next iteration, check if we're 614 // not done. 615 break; 616 } 617 } else { 618 // No need to check here since we do at the beginning of the next 619 // iteration. 620 idx++; 621 } 622 } 623 } 624 625 public: 626 nsRegion& AndWith(const nsRegion& aRegion) { 627 #ifdef DEBUG_REGIONS 628 class OperationStringGeneratorAndWith : public OperationStringGenerator { 629 public: 630 OperationStringGeneratorAndWith(nsRegion& aRegion, 631 const nsRegion& aOtherRegion) 632 : mRegion(&aRegion), 633 mRegionCopy(aRegion), 634 mOtherRegion(aOtherRegion) { 635 aRegion.mCurrentOpGenerator = this; 636 } 637 virtual ~OperationStringGeneratorAndWith() { 638 mRegion->mCurrentOpGenerator = nullptr; 639 } 640 641 virtual void OutputOp() override { 642 std::stringstream stream; 643 mRegionCopy.OutputToStream("r1", stream); 644 mOtherRegion.OutputToStream("r2", stream); 645 stream << "r1.AndWith(r2);\n"; 646 gfxCriticalError() << stream.str(); 647 } 648 649 private: 650 nsRegion* mRegion; 651 nsRegion mRegionCopy; 652 nsRegion mOtherRegion; 653 }; 654 655 OperationStringGeneratorAndWith opGenerator(*this, aRegion); 656 #endif 657 if (mBounds.IsEmpty()) { 658 // Region is empty, stays empty. 659 return *this; 660 } 661 662 if (aRegion.IsEmpty()) { 663 SetEmpty(); 664 return *this; 665 } 666 667 if (aRegion.mBands.IsEmpty()) { 668 // Other region is a rect. 669 return AndWith(aRegion.mBounds); 670 } 671 672 if (mBands.IsEmpty()) { 673 mBands.AppendElement(mBounds); 674 } 675 676 BandArray newBands; 677 678 And(newBands, mBands, aRegion.mBands); 679 680 mBands = std::move(newBands); 681 if (!mBands.Length()) { 682 mBounds = nsRectAbsolute(); 683 } else { 684 mBounds = CalculateBounds(); 685 } 686 687 EnsureSimplified(); 688 AssertState(); 689 return *this; 690 } 691 692 nsRegion& AndWith(const nsRectAbsolute& aRect) { 693 #ifdef DEBUG_REGIONS 694 class OperationStringGeneratorAndWith : public OperationStringGenerator { 695 public: 696 OperationStringGeneratorAndWith(nsRegion& aRegion, 697 const nsRectAbsolute& aRect) 698 : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) { 699 aRegion.mCurrentOpGenerator = this; 700 } 701 virtual ~OperationStringGeneratorAndWith() { 702 mRegion->mCurrentOpGenerator = nullptr; 703 } 704 705 virtual void OutputOp() override { 706 std::stringstream stream; 707 mRegionCopy.OutputToStream("r", stream); 708 stream << "r.AndWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " 709 << mRect.Width() << ", " << mRect.Height() << "));\n"; 710 gfxCriticalError() << stream.str(); 711 } 712 713 private: 714 nsRegion* mRegion; 715 nsRegion mRegionCopy; 716 nsRectAbsolute mRect; 717 }; 718 719 OperationStringGeneratorAndWith opGenerator(*this, aRect); 720 #endif 721 if (aRect.IsEmpty()) { 722 SetEmpty(); 723 return *this; 724 } 725 726 if (mBands.IsEmpty()) { 727 mBounds = mBounds.Intersect(aRect); 728 return *this; 729 } 730 731 size_t idx = 0; 732 733 size_t removeStart = 0; 734 735 // This removes all bands that do not intersect with aRect, and intersects 736 // the remaining ones with aRect. 737 738 // Start by figuring out how much to remove from the start. 739 while (idx != mBands.Length() && mBands[idx].bottom <= aRect.Y()) { 740 idx++; 741 } 742 743 // We'll remove these later to avoid needless copying in the array. 744 removeStart = idx; 745 746 while (idx != mBands.Length()) { 747 if (mBands[idx].top >= aRect.YMost()) { 748 mBands.TruncateLength(idx); 749 break; 750 } 751 752 mBands[idx].top = std::max(mBands[idx].top, aRect.Y()); 753 mBands[idx].bottom = std::min(mBands[idx].bottom, aRect.YMost()); 754 755 mBands[idx].IntersectStrip(Strip(aRect.X(), aRect.XMost())); 756 757 if (!mBands[idx].mStrips.Length()) { 758 mBands.RemoveElementAt(idx); 759 } else { 760 if (idx > removeStart) { 761 CompressBefore(idx); 762 } 763 idx++; 764 } 765 } 766 767 if (removeStart) { 768 mBands.RemoveElementsAt(0, removeStart); 769 } 770 771 if (mBands.Length()) { 772 mBounds = CalculateBounds(); 773 } else { 774 mBounds.SetEmpty(); 775 } 776 EnsureSimplified(); 777 AssertState(); 778 return *this; 779 } 780 nsRegion& AndWith(const nsRect& aRect) { 781 return AndWith(nsRectAbsolute::FromRect(aRect)); 782 } 783 nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) { 784 if (&aRgn1 == this) { 785 return AndWith(aRgn2); 786 } 787 #ifdef DEBUG_REGIONS 788 class OperationStringGeneratorAnd : public OperationStringGenerator { 789 public: 790 OperationStringGeneratorAnd(nsRegion& aRegion, const nsRegion& aRegion1, 791 const nsRegion& aRegion2) 792 : mRegion(&aRegion), mRegion1(aRegion1), mRegion2(aRegion2) { 793 aRegion.mCurrentOpGenerator = this; 794 } 795 virtual ~OperationStringGeneratorAnd() { 796 mRegion->mCurrentOpGenerator = nullptr; 797 } 798 799 virtual void OutputOp() override { 800 std::stringstream stream; 801 mRegion1.OutputToStream("r1", stream); 802 mRegion2.OutputToStream("r2", stream); 803 stream << "nsRegion r3;\nr3.And(r1, r2);\n"; 804 gfxCriticalError() << stream.str(); 805 } 806 807 private: 808 nsRegion* mRegion; 809 nsRegion mRegion1; 810 nsRegion mRegion2; 811 }; 812 813 OperationStringGeneratorAnd opGenerator(*this, aRgn1, aRgn2); 814 #endif 815 mBands.Clear(); 816 817 if (aRgn1.IsEmpty() || aRgn2.IsEmpty()) { 818 mBounds.SetEmpty(); 819 return *this; 820 } 821 822 if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) { 823 mBounds = aRgn1.mBounds.Intersect(aRgn2.mBounds); 824 return *this; 825 } else if (aRgn1.mBands.IsEmpty()) { 826 return And(aRgn2, aRgn1.mBounds); 827 } else if (aRgn2.mBands.IsEmpty()) { 828 return And(aRgn1, aRgn2.mBounds); 829 } 830 831 And(mBands, aRgn1.mBands, aRgn2.mBands); 832 833 if (!mBands.Length()) { 834 mBounds = nsRectAbsolute(); 835 } else { 836 mBounds = CalculateBounds(); 837 } 838 839 EnsureSimplified(); 840 AssertState(); 841 return *this; 842 } 843 nsRegion& And(const nsRect& aRect, const nsRegion& aRegion) { 844 return And(aRegion, aRect); 845 } 846 nsRegion& And(const nsRegion& aRegion, const nsRectAbsolute& aRect) { 847 if (&aRegion == this) { 848 return AndWith(aRect); 849 } 850 #ifdef DEBUG_REGIONS 851 class OperationStringGeneratorAnd : public OperationStringGenerator { 852 public: 853 OperationStringGeneratorAnd(nsRegion& aThisRegion, 854 const nsRegion& aRegion, 855 const nsRectAbsolute& aRect) 856 : mThisRegion(&aThisRegion), mRegion(aRegion), mRect(aRect) { 857 aThisRegion.mCurrentOpGenerator = this; 858 } 859 virtual ~OperationStringGeneratorAnd() { 860 mThisRegion->mCurrentOpGenerator = nullptr; 861 } 862 863 virtual void OutputOp() override { 864 std::stringstream stream; 865 mRegion.OutputToStream("r", stream); 866 stream << "nsRegion r2;\nr.And(r2, nsRect(" << mRect.X() << ", " 867 << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() 868 << "));\n"; 869 gfxCriticalError() << stream.str(); 870 } 871 872 private: 873 nsRegion* mThisRegion; 874 nsRegion mRegion; 875 nsRectAbsolute mRect; 876 }; 877 878 OperationStringGeneratorAnd opGenerator(*this, aRegion, aRect); 879 #endif 880 mBands.Clear(); 881 882 if (aRect.IsEmpty()) { 883 mBounds.SetEmpty(); 884 return *this; 885 } 886 887 if (aRegion.mBands.IsEmpty()) { 888 mBounds = aRegion.mBounds.Intersect(aRect); 889 return *this; 890 } 891 892 size_t idx = 0; 893 const BandArray& bands = aRegion.mBands; 894 895 mBands.SetCapacity(bands.Length() + 3); 896 while (idx != bands.Length()) { 897 // Ignore anything before. 898 if (bands[idx].bottom <= aRect.Y()) { 899 idx++; 900 continue; 901 } 902 // We're done once we've reached the bottom. 903 if (bands[idx].top >= aRect.YMost()) { 904 break; 905 } 906 907 // Now deal with bands actually intersecting the rectangle. 908 Band newBand(bands[idx]); 909 newBand.top = std::max(bands[idx].top, aRect.Y()); 910 newBand.bottom = std::min(bands[idx].bottom, aRect.YMost()); 911 912 newBand.IntersectStrip(Strip(aRect.X(), aRect.XMost())); 913 914 if (newBand.mStrips.Length()) { 915 if (!mBands.IsEmpty() && newBand.top == mBands.LastElement().bottom && 916 newBand.EqualStrips(mBands.LastElement())) { 917 mBands.LastElement().bottom = newBand.bottom; 918 } else { 919 mBands.AppendElement(std::move(newBand)); 920 } 921 } 922 idx++; 923 } 924 925 if (mBands.Length()) { 926 mBounds = CalculateBounds(); 927 } else { 928 mBounds.SetEmpty(); 929 } 930 931 EnsureSimplified(); 932 AssertState(); 933 return *this; 934 } 935 nsRegion& And(const nsRegion& aRegion, const nsRect& aRect) { 936 return And(aRegion, nsRectAbsolute::FromRect(aRect)); 937 } 938 nsRegion& And(const nsRect& aRect1, const nsRect& aRect2) { 939 nsRect tmpRect; 940 941 tmpRect.IntersectRect(aRect1, aRect2); 942 return Copy(tmpRect); 943 } 944 945 nsRegion& OrWith(const nsRegion& aOther) { 946 for (RectIterator idx(aOther); !idx.Done(); idx.Next()) { 947 AddRect(idx.GetAbsolute()); 948 } 949 return *this; 950 } 951 nsRegion& OrWith(const nsRect& aOther) { 952 AddRect(nsRectAbsolute::FromRect(aOther)); 953 return *this; 954 } 955 nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2) { 956 if (&aRgn1 != this) { 957 *this = aRgn1; 958 } 959 for (RectIterator idx(aRgn2); !idx.Done(); idx.Next()) { 960 AddRect(idx.GetAbsolute()); 961 } 962 return *this; 963 } 964 nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect) { 965 if (&aRegion != this) { 966 *this = aRegion; 967 } 968 AddRect(nsRectAbsolute::FromRect(aRect)); 969 return *this; 970 } 971 nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion) { 972 return Or(aRegion, aRect); 973 } 974 nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2) { 975 Copy(aRect1); 976 return Or(*this, aRect2); 977 } 978 979 nsRegion& XorWith(const nsRegion& aOther) { return Xor(*this, aOther); } 980 nsRegion& XorWith(const nsRect& aOther) { return Xor(*this, aOther); } 981 nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2) { 982 // this could be implemented better if pixman had direct 983 // support for xoring regions. 984 nsRegion p; 985 p.Sub(aRgn1, aRgn2); 986 nsRegion q; 987 q.Sub(aRgn2, aRgn1); 988 return Or(p, q); 989 } 990 nsRegion& Xor(const nsRegion& aRegion, const nsRect& aRect) { 991 return Xor(aRegion, nsRegion(aRect)); 992 } 993 nsRegion& Xor(const nsRect& aRect, const nsRegion& aRegion) { 994 return Xor(nsRegion(aRect), aRegion); 995 } 996 nsRegion& Xor(const nsRect& aRect1, const nsRect& aRect2) { 997 return Xor(nsRegion(aRect1), nsRegion(aRect2)); 998 } 999 1000 nsRegion ToAppUnits(nscoord aAppUnitsPerPixel) const; 1001 1002 nsRegion& SubWith(const nsRegion& aOther) { 1003 #ifdef DEBUG_REGIONS 1004 class OperationStringGeneratorSubWith : public OperationStringGenerator { 1005 public: 1006 OperationStringGeneratorSubWith(nsRegion& aRegion, 1007 const nsRegion& aOtherRegion) 1008 : mRegion(&aRegion), 1009 mRegionCopy(aRegion), 1010 mOtherRegion(aOtherRegion) { 1011 aRegion.mCurrentOpGenerator = this; 1012 } 1013 virtual ~OperationStringGeneratorSubWith() { 1014 mRegion->mCurrentOpGenerator = nullptr; 1015 } 1016 1017 virtual void OutputOp() override { 1018 std::stringstream stream; 1019 mRegionCopy.OutputToStream("r1", stream); 1020 mOtherRegion.OutputToStream("r2", stream); 1021 stream << "r1.SubWith(r2);\n"; 1022 gfxCriticalError() << stream.str(); 1023 } 1024 1025 private: 1026 nsRegion* mRegion; 1027 nsRegion mRegionCopy; 1028 nsRegion mOtherRegion; 1029 }; 1030 1031 OperationStringGeneratorSubWith opGenerator(*this, aOther); 1032 #endif 1033 1034 if (mBounds.IsEmpty()) { 1035 return *this; 1036 } 1037 1038 if (aOther.mBands.IsEmpty()) { 1039 return SubWith(aOther.mBounds); 1040 } 1041 1042 if (mBands.IsEmpty()) { 1043 mBands.AppendElement(Band(mBounds)); 1044 } 1045 1046 size_t idx = 0; 1047 size_t idxOther = 0; 1048 while (idx < mBands.Length()) { 1049 while (true) { 1050 while (idx != mBands.Length() && 1051 mBands[idx].bottom <= aOther.mBands[idxOther].top) { 1052 // Increment our current band until it ends beyond aOther's current 1053 // band. 1054 idx++; 1055 } 1056 1057 if (idx == mBands.Length()) { 1058 // This region is out of bands, the other region's future bands are 1059 // ignored. 1060 break; 1061 } 1062 1063 while (idxOther != aOther.mBands.Length() && 1064 aOther.mBands[idxOther].bottom <= mBands[idx].top) { 1065 // Increment aOther's current band until it ends beyond our current 1066 // band. 1067 idxOther++; 1068 } 1069 1070 if (idxOther == aOther.mBands.Length()) { 1071 // The other region's bands are all processed, all our future bands 1072 // are ignored. 1073 break; 1074 } 1075 1076 if (aOther.mBands[idxOther].top < mBands[idx].bottom) { 1077 // We know the other band's bottom lies beyond our band's top because 1078 // otherwise we would've incremented above. Intersecting bands found. 1079 break; 1080 } 1081 } 1082 1083 if (idx == mBands.Length() || idxOther == aOther.mBands.Length()) { 1084 // The above loop executed a break because we're done. 1085 break; 1086 } 1087 1088 const Band& bandOther = aOther.mBands[idxOther]; 1089 1090 if (!mBands[idx].Intersects(bandOther)) { 1091 if (mBands[idx].bottom < bandOther.bottom) { 1092 idx++; 1093 } else { 1094 idxOther++; 1095 if (idxOther == aOther.mBands.Length()) { 1096 break; 1097 } 1098 } 1099 continue; 1100 } 1101 1102 // These bands actually intersect. 1103 if (mBands[idx].top < bandOther.top) { 1104 mBands.InsertElementAt(idx + 1, Band(mBands[idx])); 1105 mBands[idx].bottom = bandOther.top; 1106 idx++; 1107 mBands[idx].top = bandOther.top; 1108 } 1109 1110 // mBands[idx].top >= bandOther.top; 1111 if (mBands[idx].bottom <= bandOther.bottom) { 1112 mBands[idx].SubStrips(bandOther); 1113 if (mBands[idx].mStrips.IsEmpty()) { 1114 mBands.RemoveElementAt(idx); 1115 } else { 1116 CompressBefore(idx); 1117 idx++; 1118 // The band before us just changed, it may be identical now. 1119 CompressBefore(idx); 1120 } 1121 continue; 1122 } 1123 1124 // mBands[idx].bottom > bandOther.bottom 1125 Band newBand = mBands[idx]; 1126 newBand.SubStrips(bandOther); 1127 1128 if (!newBand.mStrips.IsEmpty()) { 1129 mBands.InsertElementAt(idx, newBand); 1130 mBands[idx].bottom = bandOther.bottom; 1131 CompressBefore(idx); 1132 idx++; 1133 } 1134 1135 mBands[idx].top = bandOther.bottom; 1136 1137 idxOther++; 1138 if (idxOther == aOther.mBands.Length()) { 1139 break; 1140 } 1141 } 1142 1143 if (mBands.IsEmpty()) { 1144 mBounds.SetEmpty(); 1145 } else { 1146 mBounds = CalculateBounds(); 1147 } 1148 1149 AssertState(); 1150 EnsureSimplified(); 1151 return *this; 1152 } 1153 nsRegion& SubOut(const nsRegion& aOther) { return SubWith(aOther); } 1154 nsRegion& SubOut(const nsRect& aOther) { return SubWith(aOther); } 1155 1156 private: 1157 void AppendOrExtend(const Band& aNewBand) { 1158 if (aNewBand.mStrips.IsEmpty()) { 1159 return; 1160 } 1161 if (mBands.IsEmpty()) { 1162 mBands.AppendElement(aNewBand); 1163 return; 1164 } 1165 1166 if (mBands.LastElement().bottom == aNewBand.top && 1167 mBands.LastElement().EqualStrips(aNewBand)) { 1168 mBands.LastElement().bottom = aNewBand.bottom; 1169 } else { 1170 mBands.AppendElement(aNewBand); 1171 } 1172 } 1173 void AppendOrExtend(const Band&& aNewBand) { 1174 if (aNewBand.mStrips.IsEmpty()) { 1175 return; 1176 } 1177 if (mBands.IsEmpty()) { 1178 mBands.AppendElement(std::move(aNewBand)); 1179 return; 1180 } 1181 1182 if (mBands.LastElement().bottom == aNewBand.top && 1183 mBands.LastElement().EqualStrips(aNewBand)) { 1184 mBands.LastElement().bottom = aNewBand.bottom; 1185 } else { 1186 mBands.AppendElement(std::move(aNewBand)); 1187 } 1188 } 1189 1190 public: 1191 nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2) { 1192 if (&aRgn1 == this) { 1193 return SubWith(aRgn2); 1194 } 1195 #ifdef DEBUG_REGIONS 1196 class OperationStringGeneratorSub : public OperationStringGenerator { 1197 public: 1198 OperationStringGeneratorSub(nsRegion& aRegion, const nsRegion& aRgn1, 1199 const nsRegion& aRgn2) 1200 : mRegion(&aRegion), mRegion1(aRgn1), mRegion2(aRgn2) { 1201 aRegion.mCurrentOpGenerator = this; 1202 } 1203 virtual ~OperationStringGeneratorSub() { 1204 mRegion->mCurrentOpGenerator = nullptr; 1205 } 1206 1207 virtual void OutputOp() override { 1208 std::stringstream stream; 1209 mRegion1.OutputToStream("r1", stream); 1210 mRegion2.OutputToStream("r2", stream); 1211 stream << "nsRegion r3;\nr3.Sub(r1, r2);\n"; 1212 gfxCriticalError() << stream.str(); 1213 } 1214 1215 private: 1216 nsRegion* mRegion; 1217 nsRegion mRegion1; 1218 nsRegion mRegion2; 1219 }; 1220 1221 OperationStringGeneratorSub opGenerator(*this, aRgn1, aRgn2); 1222 #endif 1223 1224 mBands.Clear(); 1225 1226 if (aRgn1.mBounds.IsEmpty()) { 1227 mBounds.SetEmpty(); 1228 return *this; 1229 } 1230 1231 if (aRgn2.mBounds.IsEmpty()) { 1232 Copy(aRgn1); 1233 return *this; 1234 } 1235 1236 if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) { 1237 return Sub(aRgn1.mBounds, aRgn2.mBounds); 1238 } else if (aRgn1.mBands.IsEmpty()) { 1239 return Sub(aRgn1.mBounds, aRgn2); 1240 } else if (aRgn2.mBands.IsEmpty()) { 1241 return Sub(aRgn1, aRgn2.mBounds); 1242 } 1243 1244 const BandArray& bands1 = aRgn1.mBands; 1245 const BandArray& bands2 = aRgn2.mBands; 1246 1247 size_t idx = 0; 1248 size_t idxOther = 0; 1249 1250 // We iterate the source region's bands, subtracting the other regions bands 1251 // from them as we move them into ours. 1252 while (idx < bands1.Length()) { 1253 while (idxOther < bands2.Length() && 1254 bands2[idxOther].bottom <= bands1[idx].top) { 1255 // These other bands are irrelevant as they don't intersect with the 1256 // band we're currently processing. 1257 idxOther++; 1258 } 1259 if (idxOther == bands2.Length()) { 1260 break; 1261 } 1262 1263 const Band& other = bands2[idxOther]; 1264 1265 // bands2[idxOther].bottom >= bands1[idx].top 1266 Band origBand(bands1[idx]); 1267 if (other.top >= origBand.bottom) { 1268 // No intersecting bands, append and continue. 1269 AppendOrExtend(origBand); 1270 idx++; 1271 continue; 1272 } 1273 1274 // Push a band for an uncovered region above our band. 1275 if (origBand.top < other.top) { 1276 Band newBand(origBand); 1277 newBand.bottom = other.top; 1278 AppendOrExtend(std::move(newBand)); 1279 } 1280 1281 int32_t lastBottom = std::max(other.top, origBand.top); 1282 while (idxOther < bands2.Length() && 1283 bands2[idxOther].top < origBand.bottom) { 1284 const Band& other = bands2[idxOther]; 1285 Band newBand(origBand); 1286 newBand.top = std::max(origBand.top, other.top); 1287 newBand.bottom = std::min(origBand.bottom, other.bottom); 1288 1289 // If there was a gap, we need to add the original band there. 1290 if (newBand.top > lastBottom) { 1291 Band betweenBand(newBand); 1292 betweenBand.top = lastBottom; 1293 betweenBand.bottom = newBand.top; 1294 AppendOrExtend(std::move(betweenBand)); 1295 } 1296 1297 lastBottom = newBand.bottom; 1298 newBand.SubStrips(other); 1299 AppendOrExtend(std::move(newBand)); 1300 idxOther++; 1301 } 1302 // Decrement other here so we are back at the last band in region 2 1303 // that intersected. 1304 idxOther--; 1305 1306 if (bands2[idxOther].bottom < origBand.bottom) { 1307 // The last band in region2 that intersected ended before this one, 1308 // we can copy the rest. 1309 Band newBand(origBand); 1310 newBand.top = bands2[idxOther].bottom; 1311 AppendOrExtend(std::move(newBand)); 1312 idxOther++; 1313 } 1314 idx++; 1315 } 1316 1317 // Copy any remaining bands, the first one may have to be extended to fit 1318 // the last one added before. The rest can be unconditionally appended. 1319 if (idx < bands1.Length()) { 1320 AppendOrExtend(bands1[idx]); 1321 idx++; 1322 } 1323 1324 while (idx < bands1.Length()) { 1325 mBands.AppendElement(bands1[idx]); 1326 idx++; 1327 } 1328 1329 if (mBands.IsEmpty()) { 1330 mBounds.SetEmpty(); 1331 } else { 1332 mBounds = CalculateBounds(); 1333 } 1334 1335 AssertState(); 1336 EnsureSimplified(); 1337 return *this; 1338 } 1339 1340 private: 1341 // Internal helper for executing subtraction. 1342 void RunSubtraction(const nsRectAbsolute& aRect) { 1343 Strip rectStrip(aRect.X(), aRect.XMost()); 1344 1345 size_t idx = 0; 1346 1347 while (idx < mBands.Length()) { 1348 if (mBands[idx].top >= aRect.YMost()) { 1349 return; 1350 } 1351 1352 if (mBands[idx].bottom <= aRect.Y()) { 1353 // This band is entirely before aRect, move on. 1354 idx++; 1355 continue; 1356 } 1357 1358 if (!mBands[idx].Intersects(Strip(aRect.X(), aRect.XMost()))) { 1359 // This band does not intersect aRect horizontally. Move on. 1360 idx++; 1361 continue; 1362 } 1363 1364 // This band intersects with aRect. 1365 1366 if (mBands[idx].top < aRect.Y()) { 1367 // This band starts above the start of aRect, split the band into two 1368 // along the intersection, and continue to the next iteration to process 1369 // the one that now intersects exactly. 1370 auto above = mBands.InsertElementAt(idx, Band(mBands[idx])); 1371 above->bottom = aRect.Y(); 1372 idx++; 1373 mBands[idx].top = aRect.Y(); 1374 // Continue to run the loop for the next band. 1375 continue; 1376 } 1377 1378 if (mBands[idx].bottom <= aRect.YMost()) { 1379 // This band ends before the end of aRect. 1380 mBands[idx].SubStrip(rectStrip); 1381 if (mBands[idx].mStrips.Length()) { 1382 CompressAdjacentBands(idx); 1383 } else { 1384 mBands.RemoveElementAt(idx); 1385 } 1386 continue; 1387 } 1388 1389 // This band extends beyond aRect. 1390 Band newBand = mBands[idx]; 1391 newBand.SubStrip(rectStrip); 1392 newBand.bottom = aRect.YMost(); 1393 mBands[idx].top = aRect.YMost(); 1394 1395 if (newBand.mStrips.Length()) { 1396 if (idx && mBands[idx - 1].bottom == newBand.top && 1397 newBand.EqualStrips(mBands[idx - 1])) { 1398 mBands[idx - 1].bottom = aRect.YMost(); 1399 } else { 1400 mBands.InsertElementAt(idx, std::move(newBand)); 1401 } 1402 } 1403 1404 return; 1405 } 1406 } 1407 1408 public: 1409 nsRegion& SubWith(const nsRectAbsolute& aRect) { 1410 if (!mBounds.Intersects(aRect)) { 1411 return *this; 1412 } 1413 1414 if (aRect.Contains(mBounds)) { 1415 SetEmpty(); 1416 return *this; 1417 } 1418 1419 #ifdef DEBUG_REGIONS 1420 class OperationStringGeneratorSubWith : public OperationStringGenerator { 1421 public: 1422 OperationStringGeneratorSubWith(nsRegion& aRegion, 1423 const nsRectAbsolute& aRect) 1424 : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) { 1425 aRegion.mCurrentOpGenerator = this; 1426 } 1427 virtual ~OperationStringGeneratorSubWith() { 1428 mRegion->mCurrentOpGenerator = nullptr; 1429 } 1430 1431 virtual void OutputOp() override { 1432 std::stringstream stream; 1433 mRegionCopy.OutputToStream("r", stream); 1434 stream << "r.SubWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " 1435 << mRect.Width() << ", " << mRect.Height() << "));\n"; 1436 gfxCriticalError() << stream.str(); 1437 } 1438 1439 private: 1440 nsRegion* mRegion; 1441 nsRegion mRegionCopy; 1442 nsRectAbsolute mRect; 1443 }; 1444 1445 OperationStringGeneratorSubWith opGenerator(*this, aRect); 1446 #endif 1447 1448 if (mBands.IsEmpty()) { 1449 mBands.AppendElement(Band(mBounds)); 1450 } 1451 1452 RunSubtraction(aRect); 1453 1454 if (aRect.X() <= mBounds.X() || aRect.Y() <= mBounds.Y() || 1455 aRect.XMost() >= mBounds.XMost() || aRect.YMost() >= mBounds.YMost()) { 1456 mBounds = CalculateBounds(); 1457 } 1458 EnsureSimplified(); 1459 AssertState(); 1460 return *this; 1461 } 1462 nsRegion& Sub(const nsRegion& aRegion, const nsRectAbsolute& aRect) { 1463 if (aRect.Contains(aRegion.mBounds)) { 1464 SetEmpty(); 1465 return *this; 1466 } 1467 if (&aRegion == this) { 1468 return SubWith(aRect); 1469 } 1470 #ifdef DEBUG_REGIONS 1471 class OperationStringGeneratorSub : public OperationStringGenerator { 1472 public: 1473 OperationStringGeneratorSub(nsRegion& aRegion, 1474 const nsRegion& aRegionOther, 1475 const nsRectAbsolute& aRect) 1476 : mRegion(&aRegion), mRegionOther(aRegionOther), mRect(aRect) { 1477 aRegion.mCurrentOpGenerator = this; 1478 } 1479 virtual ~OperationStringGeneratorSub() { 1480 mRegion->mCurrentOpGenerator = nullptr; 1481 } 1482 1483 virtual void OutputOp() override { 1484 std::stringstream stream; 1485 mRegionOther.OutputToStream("r1", stream); 1486 stream << "nsRegion r2;\nr2.Sub(r1, nsRect(" << mRect.X() << ", " 1487 << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height() 1488 << "));\n"; 1489 gfxCriticalError() << stream.str(); 1490 } 1491 1492 private: 1493 nsRegion* mRegion; 1494 nsRegion mRegionOther; 1495 nsRectAbsolute mRect; 1496 }; 1497 1498 OperationStringGeneratorSub opGenerator(*this, aRegion, aRect); 1499 #endif 1500 1501 mBands.Clear(); 1502 1503 if (aRegion.mBounds.IsEmpty()) { 1504 mBounds.SetEmpty(); 1505 return *this; 1506 } 1507 1508 if (aRect.IsEmpty()) { 1509 Copy(aRegion); 1510 return *this; 1511 } 1512 1513 if (aRegion.mBands.IsEmpty()) { 1514 Copy(aRegion.mBounds); 1515 return SubWith(aRect); 1516 } 1517 1518 const BandArray& bands = aRegion.mBands; 1519 1520 size_t idx = 0; 1521 1522 Strip strip(aRect.X(), aRect.XMost()); 1523 1524 mBands.SetCapacity(bands.Length() + 3); 1525 1526 // Process all bands that lie before aRect. 1527 while (idx < bands.Length() && bands[idx].bottom <= aRect.Y()) { 1528 mBands.AppendElement(bands[idx]); 1529 idx++; 1530 } 1531 1532 // This band's bottom lies beyond aRect. 1533 if (idx < bands.Length() && bands[idx].top < aRect.Y()) { 1534 Band newBand(bands[idx]); 1535 if (bands[idx].Intersects(strip)) { 1536 newBand.bottom = aRect.Y(); 1537 } else { 1538 idx++; 1539 } 1540 mBands.AppendElement(std::move(newBand)); 1541 } 1542 1543 // This tracks whether the band when we -exit- the next loop intersected the 1544 // rectangle. 1545 bool didIntersect = false; 1546 1547 while (idx < bands.Length() && bands[idx].top < aRect.YMost()) { 1548 // Process all bands intersecting with aRect. 1549 if (!bands[idx].Intersects(strip)) { 1550 AppendOrExtend(bands[idx]); 1551 idx++; 1552 didIntersect = false; 1553 continue; 1554 } 1555 1556 didIntersect = true; 1557 Band newBand(bands[idx]); 1558 newBand.top = std::max(newBand.top, aRect.Y()); 1559 newBand.bottom = std::min(newBand.bottom, aRect.YMost()); 1560 newBand.SubStrip(strip); 1561 AppendOrExtend(std::move(newBand)); 1562 idx++; 1563 } 1564 1565 if (didIntersect) { 1566 if (aRect.YMost() < bands[idx - 1].bottom) { 1567 // If this band does not intersect the loop above has already added the 1568 // whole unmodified band. 1569 Band newBand(bands[idx - 1]); 1570 newBand.top = aRect.YMost(); 1571 AppendOrExtend(std::move(newBand)); 1572 } 1573 } 1574 1575 // Now process all bands beyond aRect. 1576 if (idx < bands.Length()) { 1577 AppendOrExtend(bands[idx]); 1578 idx++; 1579 } 1580 1581 mBands.AppendElements(bands.Elements() + idx, bands.Length() - idx); 1582 1583 if (mBands.IsEmpty()) { 1584 mBounds.SetEmpty(); 1585 } else { 1586 mBounds = CalculateBounds(); 1587 } 1588 1589 AssertState(); 1590 EnsureSimplified(); 1591 return *this; 1592 } 1593 nsRegion& SubWith(const nsRect& aRect) { 1594 return SubWith(nsRectAbsolute::FromRect(aRect)); 1595 } 1596 nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion) { 1597 Copy(aRect); 1598 return SubWith(aRegion); 1599 } 1600 nsRegion& Sub(const nsRectAbsolute& aRect, const nsRegion& aRegion) { 1601 Copy(aRect); 1602 return SubWith(aRegion); 1603 } 1604 nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2) { 1605 Copy(aRect1); 1606 return SubWith(aRect2); 1607 } 1608 nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect) { 1609 return Sub(aRegion, nsRectAbsolute::FromRect(aRect)); 1610 } 1611 nsRegion& Sub(const nsRectAbsolute& aRect1, const nsRectAbsolute& aRect2) { 1612 Copy(aRect1); 1613 return SubWith(aRect2); 1614 } 1615 1616 /** 1617 * Returns true if the given point is inside the region. A region 1618 * created from a rect (x=0, y=0, w=100, h=100) will NOT contain 1619 * the point x=100, y=100. 1620 */ 1621 bool Contains(int aX, int aY) const { 1622 if (mBands.IsEmpty()) { 1623 return mBounds.Contains(aX, aY); 1624 } 1625 1626 auto iter = mBands.begin(); 1627 1628 while (iter != mBands.end()) { 1629 if (iter->bottom <= aY) { 1630 iter++; 1631 continue; 1632 } 1633 1634 if (iter->top > aY) { 1635 return false; 1636 } 1637 1638 if (iter->ContainsStrip(Strip(aX, aX + 1))) { 1639 return true; 1640 } 1641 return false; 1642 } 1643 return false; 1644 } 1645 1646 bool Contains(const nsPoint& aPoint) const { 1647 return Contains(aPoint.x, aPoint.y); 1648 } 1649 1650 bool Contains(const nsRectAbsolute& aRect) const { 1651 if (aRect.IsEmpty()) { 1652 return false; 1653 } 1654 1655 if (mBands.IsEmpty()) { 1656 return mBounds.Contains(aRect); 1657 } 1658 1659 auto iter = mBands.begin(); 1660 1661 while (iter != mBands.end()) { 1662 if (iter->bottom <= aRect.Y()) { 1663 iter++; 1664 continue; 1665 } 1666 1667 if (iter->top > aRect.Y()) { 1668 return false; 1669 } 1670 1671 // Now inside the rectangle. 1672 if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) { 1673 return false; 1674 } 1675 1676 if (iter->bottom >= aRect.YMost()) { 1677 return true; 1678 } 1679 1680 int32_t lastY = iter->bottom; 1681 iter++; 1682 while (iter != mBands.end()) { 1683 // Bands do not connect. 1684 if (iter->top != lastY) { 1685 return false; 1686 } 1687 1688 if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) { 1689 return false; 1690 } 1691 1692 if (iter->bottom >= aRect.YMost()) { 1693 return true; 1694 } 1695 1696 lastY = iter->bottom; 1697 iter++; 1698 } 1699 } 1700 return false; 1701 } 1702 bool Contains(const nsRect& aRect) const { 1703 return Contains(nsRectAbsolute::FromRect(aRect)); 1704 } 1705 1706 bool Contains(const nsRegion& aRgn) const; 1707 bool Intersects(const nsRectAbsolute& aRect) const; 1708 bool Intersects(const nsRect& aRect) const { 1709 return Intersects(nsRectAbsolute::FromRect(aRect)); 1710 } 1711 1712 void MoveBy(int32_t aXOffset, int32_t aYOffset) { 1713 MoveBy(nsPoint(aXOffset, aYOffset)); 1714 } 1715 void MoveBy(nsPoint aPt) { 1716 #ifdef DEBUG_REGIONS 1717 class OperationStringGeneratorMoveBy : public OperationStringGenerator { 1718 public: 1719 OperationStringGeneratorMoveBy(nsRegion& aRegion, const nsPoint& aPoint) 1720 : mRegion(&aRegion), mRegionCopy(aRegion), mPoint(aPoint) { 1721 aRegion.mCurrentOpGenerator = this; 1722 } 1723 virtual ~OperationStringGeneratorMoveBy() { 1724 mRegion->mCurrentOpGenerator = nullptr; 1725 } 1726 1727 virtual void OutputOp() override { 1728 std::stringstream stream; 1729 mRegionCopy.OutputToStream("r", stream); 1730 stream << "r.MoveBy(nsPoint(" << mPoint.x << ", " << mPoint.y 1731 << "));\n"; 1732 gfxCriticalError() << stream.str(); 1733 } 1734 1735 private: 1736 nsRegion* mRegion; 1737 nsRegion mRegionCopy; 1738 nsPoint mPoint; 1739 }; 1740 1741 OperationStringGeneratorMoveBy opGenerator(*this, aPt); 1742 #endif 1743 1744 mBounds.MoveBy(aPt); 1745 for (Band& band : mBands) { 1746 band.top += aPt.Y(); 1747 band.bottom += aPt.Y(); 1748 for (Strip& strip : band.mStrips) { 1749 strip.left += aPt.X(); 1750 strip.right += aPt.X(); 1751 } 1752 } 1753 AssertState(); 1754 } 1755 void SetEmpty() { 1756 mBands.Clear(); 1757 mBounds.SetEmpty(); 1758 } 1759 1760 nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const { 1761 return MovedBy(nsPoint(aXOffset, aYOffset)); 1762 } 1763 nsRegion MovedBy(const nsPoint& aPt) const { 1764 nsRegion copy(*this); 1765 copy.MoveBy(aPt); 1766 return copy; 1767 } 1768 1769 nsRegion Intersect(const nsRegion& aOther) const { 1770 nsRegion intersection; 1771 intersection.And(*this, aOther); 1772 return intersection; 1773 } 1774 1775 void Inflate(const nsMargin& aMargin); 1776 1777 nsRegion Inflated(const nsMargin& aMargin) const { 1778 nsRegion copy(*this); 1779 copy.Inflate(aMargin); 1780 return copy; 1781 } 1782 1783 bool IsEmpty() const { return mBounds.IsEmpty(); } 1784 bool IsComplex() const { return GetNumRects() > 1; } 1785 bool IsEqual(const nsRegion& aRegion) const { 1786 if (!mBounds.IsEqualInterior(aRegion.mBounds)) { 1787 return false; 1788 } 1789 1790 if (mBands.Length() != aRegion.mBands.Length()) { 1791 return false; 1792 } 1793 1794 for (auto iter1 = mBands.begin(), iter2 = aRegion.mBands.begin(); 1795 iter1 != mBands.end(); iter1++, iter2++) { 1796 if (iter1->top != iter2->top || iter1->bottom != iter2->bottom || 1797 !iter1->EqualStrips(*iter2)) { 1798 return false; 1799 } 1800 } 1801 1802 return true; 1803 } 1804 1805 uint32_t GetNumRects() const { 1806 if (mBands.IsEmpty()) { 1807 return mBounds.IsEmpty() ? 0 : 1; 1808 } 1809 1810 uint32_t rects = 0; 1811 1812 for (const Band& band : mBands) { 1813 rects += band.mStrips.Length(); 1814 } 1815 1816 return rects; 1817 } 1818 const nsRect GetBounds() const { return mBounds.ToNSRect(); } 1819 const nsRectAbsolute GetAbsoluteBounds() const { return mBounds; } 1820 uint64_t Area() const; 1821 1822 /** 1823 * Return this region scaled to a different appunits per pixel (APP) ratio. 1824 * This applies nsRect::ScaleToOtherAppUnitsRoundOut/In to each rect of the 1825 * region. 1826 * @param aFromAPP the APP to scale from 1827 * @param aToAPP the APP to scale to 1828 * @note this can turn an empty region into a non-empty region 1829 */ 1830 [[nodiscard]] nsRegion ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, 1831 int32_t aToAPP) const; 1832 [[nodiscard]] nsRegion ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP, 1833 int32_t aToAPP) const; 1834 nsRegion& ScaleRoundOut(float aXScale, float aYScale); 1835 nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale); 1836 nsRegion& Transform(const mozilla::gfx::Matrix4x4& aTransform); 1837 nsIntRegion ScaleToOutsidePixels(float aXScale, float aYScale, 1838 nscoord aAppUnitsPerPixel) const; 1839 nsIntRegion ScaleToInsidePixels(float aXScale, float aYScale, 1840 nscoord aAppUnitsPerPixel) const; 1841 nsIntRegion ScaleToNearestPixels(float aXScale, float aYScale, 1842 nscoord aAppUnitsPerPixel) const; 1843 nsIntRegion ToOutsidePixels(nscoord aAppUnitsPerPixel) const; 1844 nsIntRegion ToNearestPixels(nscoord aAppUnitsPerPixel) const; 1845 1846 /** 1847 * Gets the largest rectangle contained in the region. 1848 * @param aContainingRect if non-empty, we choose a rectangle that 1849 * maximizes the area intersecting with aContainingRect (and break ties by 1850 * then choosing the largest rectangle overall) 1851 */ 1852 nsRect GetLargestRectangle(const nsRect& aContainingRect = nsRect()) const; 1853 1854 /** 1855 * Make sure the region has at most aMaxRects by adding area to it 1856 * if necessary. The simplified region will be a superset of the 1857 * original region. The simplified region's bounding box will be 1858 * the same as for the current region. 1859 */ 1860 void SimplifyOutward(uint32_t aMaxRects); 1861 /** 1862 * Simplify the region by adding at most aThreshold area between spans of 1863 * rects. The simplified region will be a superset of the original region. 1864 * The simplified region's bounding box will be the same as for the current 1865 * region. 1866 */ 1867 void SimplifyOutwardByArea(uint32_t aThreshold); 1868 /** 1869 * Make sure the region has at most aMaxRects by removing area from 1870 * it if necessary. The simplified region will be a subset of the 1871 * original region. 1872 */ 1873 void SimplifyInward(uint32_t aMaxRects); 1874 1875 /** 1876 * VisitEdges is a weird kind of function that we use for padding 1877 * out surfaces to prevent texture filtering artifacts. 1878 * It calls the visitFn callback for each of the exterior edges of 1879 * the regions. The top and bottom edges will be expanded 1 pixel 1880 * to the left and right if there's an outside corner. The order 1881 * the edges are visited is not guaranteed. 1882 * 1883 * visitFn has a side parameter that can be TOP,BOTTOM,LEFT,RIGHT 1884 * and specifies which kind of edge is being visited. x1, y1, x2, y2 1885 * are the coordinates of the line. (x1 == x2) || (y1 == y2) 1886 */ 1887 typedef void (*visitFn)(void* closure, VisitSide side, int x1, int y1, int x2, 1888 int y2); 1889 void VisitEdges(visitFn, void* closure) const; 1890 1891 nsCString ToString() const; 1892 1893 static inline pixman_box32_t RectToBox(const nsRect& aRect) { 1894 pixman_box32_t box = {aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost()}; 1895 return box; 1896 } 1897 1898 static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect& aRect) { 1899 pixman_box32_t box = {aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost()}; 1900 return box; 1901 } 1902 1903 private: 1904 nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const; 1905 1906 nsRegion& Copy(const nsRegion& aRegion) { 1907 mBounds = aRegion.mBounds; 1908 mBands = aRegion.mBands.Clone(); 1909 return *this; 1910 } 1911 1912 nsRegion& Copy(const nsRect& aRect) { 1913 mBands.Clear(); 1914 mBounds = nsRectAbsolute::FromRect(aRect); 1915 return *this; 1916 } 1917 1918 nsRegion& Copy(const nsRectAbsolute& aRect) { 1919 mBands.Clear(); 1920 mBounds = aRect; 1921 return *this; 1922 } 1923 1924 void EnsureSimplified() { 1925 if (mBands.Length() == 1 && mBands.begin()->mStrips.Length() == 1) { 1926 mBands.Clear(); 1927 } 1928 } 1929 1930 static inline nsRectAbsolute BoxToRect(const pixman_box32_t& aBox) { 1931 return nsRectAbsolute(aBox.x1, aBox.y1, aBox.x2, aBox.y2); 1932 } 1933 1934 void AddRect(const nsRectAbsolute& aRect) { 1935 #ifdef DEBUG_REGIONS 1936 class OperationStringGeneratorAddRect : public OperationStringGenerator { 1937 public: 1938 OperationStringGeneratorAddRect(nsRegion& aRegion, 1939 const nsRectAbsolute& aRect) 1940 : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) { 1941 aRegion.mCurrentOpGenerator = this; 1942 } 1943 virtual ~OperationStringGeneratorAddRect() { 1944 mRegion->mCurrentOpGenerator = nullptr; 1945 } 1946 1947 virtual void OutputOp() override { 1948 std::stringstream stream; 1949 mRegionCopy.OutputToStream("r", stream); 1950 stream << "r.OrWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", " 1951 << mRect.Width() << ", " << mRect.Height() << "));\n"; 1952 gfxCriticalError() << stream.str(); 1953 } 1954 1955 private: 1956 nsRegion* mRegion; 1957 nsRegion mRegionCopy; 1958 nsRectAbsolute mRect; 1959 }; 1960 1961 OperationStringGeneratorAddRect opGenerator(*this, aRect); 1962 #endif 1963 if (aRect.IsEmpty()) { 1964 return; 1965 } 1966 1967 if (mBands.IsEmpty()) { 1968 if (mBounds.IsEmpty()) { 1969 mBounds = aRect; 1970 return; 1971 } else if (mBounds.Contains(aRect)) { 1972 return; 1973 } 1974 1975 mBands.AppendElement(Band(mBounds)); 1976 } 1977 1978 mBounds = aRect.UnsafeUnion(mBounds); 1979 1980 size_t idx = 0; 1981 1982 Strip strip(aRect.X(), aRect.XMost()); 1983 Band remaining(aRect); 1984 1985 while (idx != mBands.Length()) { 1986 if (mBands[idx].bottom <= remaining.top) { 1987 // This band lies wholly above aRect. 1988 idx++; 1989 continue; 1990 } 1991 1992 if (remaining.top >= remaining.bottom) { 1993 AssertState(); 1994 EnsureSimplified(); 1995 return; 1996 } 1997 1998 if (mBands[idx].top >= remaining.bottom) { 1999 // This band lies wholly below aRect. 2000 break; 2001 } 2002 2003 if (mBands[idx].EqualStrips(remaining)) { 2004 mBands[idx].top = std::min(mBands[idx].top, remaining.top); 2005 // Nothing to do for this band. Just expand. 2006 remaining.top = mBands[idx].bottom; 2007 CompressBefore(idx); 2008 idx++; 2009 continue; 2010 } 2011 2012 if (mBands[idx].top > remaining.top) { 2013 auto before = mBands.InsertElementAt(idx, remaining); 2014 before->bottom = mBands[idx + 1].top; 2015 remaining.top = before->bottom; 2016 CompressBefore(idx); 2017 idx++; 2018 CompressBefore(idx); 2019 continue; 2020 } 2021 2022 if (mBands[idx].ContainsStrip(strip)) { 2023 remaining.top = mBands[idx].bottom; 2024 idx++; 2025 continue; 2026 } 2027 2028 // mBands[idx].top <= remaining.top. 2029 2030 if (mBands[idx].top < remaining.top) { 2031 auto before = mBands.InsertElementAt(idx, Band(mBands[idx])); 2032 before->bottom = remaining.top; 2033 idx++; 2034 mBands[idx].top = remaining.top; 2035 continue; 2036 } 2037 2038 // mBands[idx].top == remaining.top 2039 if (mBands[idx].bottom > remaining.bottom) { 2040 auto below = mBands.InsertElementAt(idx + 1, Band(mBands[idx])); 2041 below->top = remaining.bottom; 2042 mBands[idx].bottom = remaining.bottom; 2043 } 2044 2045 mBands[idx].InsertStrip(strip); 2046 CompressBefore(idx); 2047 remaining.top = mBands[idx].bottom; 2048 idx++; 2049 CompressBefore(idx); 2050 } 2051 2052 if (remaining.top < remaining.bottom) { 2053 // We didn't find any bands that overlapped aRect. 2054 if (idx) { 2055 if (mBands[idx - 1].bottom == remaining.top && 2056 mBands[idx - 1].EqualStrips(remaining)) { 2057 mBands[idx - 1].bottom = remaining.bottom; 2058 CompressBefore(idx); 2059 AssertState(); 2060 EnsureSimplified(); 2061 return; 2062 } 2063 } 2064 mBands.InsertElementAt(idx, remaining); 2065 idx++; 2066 CompressBefore(idx); 2067 } else { 2068 CompressBefore(idx); 2069 } 2070 2071 AssertState(); 2072 EnsureSimplified(); 2073 } 2074 2075 // Most callers could probably do this on the fly, if this ever shows up 2076 // in profiles we could optimize this. 2077 nsRectAbsolute CalculateBounds() const { 2078 if (mBands.IsEmpty()) { 2079 return mBounds; 2080 } 2081 2082 int32_t top = mBands.begin()->top; 2083 int32_t bottom = mBands.LastElement().bottom; 2084 2085 int32_t leftMost = mBands.begin()->mStrips.begin()->left; 2086 int32_t rightMost = mBands.begin()->mStrips.LastElement().right; 2087 for (const Band& band : mBands) { 2088 leftMost = std::min(leftMost, band.mStrips.begin()->left); 2089 rightMost = std::max(rightMost, band.mStrips.LastElement().right); 2090 } 2091 2092 return nsRectAbsolute(leftMost, top, rightMost, bottom); 2093 } 2094 2095 static uint32_t ComputeMergedAreaIncrease(const Band& aTopBand, 2096 const Band& aBottomBand); 2097 2098 // Returns true if idx is now referring to the 'next' band 2099 bool CompressAdjacentBands(size_t& aIdx) { 2100 if ((aIdx + 1) < mBands.Length()) { 2101 if (mBands[aIdx + 1].top == mBands[aIdx].bottom && 2102 mBands[aIdx + 1].EqualStrips(mBands[aIdx])) { 2103 mBands[aIdx].bottom = mBands[aIdx + 1].bottom; 2104 mBands.RemoveElementAt(aIdx + 1); 2105 } 2106 } 2107 if (aIdx) { 2108 if (mBands[aIdx - 1].bottom == mBands[aIdx].top && 2109 mBands[aIdx].EqualStrips(mBands[aIdx - 1])) { 2110 mBands[aIdx - 1].bottom = mBands[aIdx].bottom; 2111 mBands.RemoveElementAt(aIdx); 2112 return true; 2113 } 2114 } 2115 return false; 2116 } 2117 2118 void CompressBefore(size_t& aIdx) { 2119 if (aIdx && aIdx < mBands.Length()) { 2120 if (mBands[aIdx - 1].bottom == mBands[aIdx].top && 2121 mBands[aIdx - 1].EqualStrips(mBands[aIdx])) { 2122 mBands[aIdx].top = mBands[aIdx - 1].top; 2123 mBands.RemoveElementAt(aIdx - 1); 2124 aIdx--; 2125 } 2126 } 2127 } 2128 2129 BandArray mBands; 2130 // Considering we only ever OR with nsRects, the bounds should fit in an 2131 // nsRect as well. 2132 nsRectAbsolute mBounds; 2133 #ifdef DEBUG_REGIONS 2134 friend class OperationStringGenerator; 2135 OperationStringGenerator* mCurrentOpGenerator; 2136 #endif 2137 2138 public: 2139 class RectIterator { 2140 const nsRegion& mRegion; 2141 typename BandArray::const_iterator mCurrentBand; 2142 typename StripArray::const_iterator mCurrentStrip; 2143 2144 public: 2145 explicit RectIterator(const nsRegion& aRegion) 2146 : mRegion(aRegion), 2147 mCurrentBand(aRegion.mBands.begin()) 2148 #ifndef DEBUG 2149 , 2150 mCurrentStrip(nullptr) 2151 #endif 2152 { 2153 mIsDone = mRegion.mBounds.IsEmpty(); 2154 if (mCurrentBand != aRegion.mBands.end()) { 2155 mCurrentStrip = mCurrentBand->mStrips.begin(); 2156 } 2157 } 2158 2159 bool Done() const { return mIsDone; } 2160 2161 const nsRect Get() const { 2162 if (mRegion.mBands.IsEmpty()) { 2163 return mRegion.GetBounds(); 2164 } 2165 return nsRect(mCurrentStrip->left, mCurrentBand->top, 2166 mCurrentStrip->right - mCurrentStrip->left, 2167 mCurrentBand->bottom - mCurrentBand->top); 2168 } 2169 2170 const nsRectAbsolute GetAbsolute() const { 2171 if (mRegion.mBands.IsEmpty()) { 2172 return mRegion.mBounds; 2173 } 2174 return nsRectAbsolute(mCurrentStrip->left, mCurrentBand->top, 2175 mCurrentStrip->right, mCurrentBand->bottom); 2176 } 2177 2178 void Next() { 2179 if (mRegion.mBands.IsEmpty()) { 2180 mIsDone = true; 2181 return; 2182 } 2183 2184 mCurrentStrip++; 2185 if (mCurrentStrip == mCurrentBand->mStrips.end()) { 2186 mCurrentBand++; 2187 if (mCurrentBand != mRegion.mBands.end()) { 2188 mCurrentStrip = mCurrentBand->mStrips.begin(); 2189 } else { 2190 mIsDone = true; 2191 } 2192 } 2193 } 2194 2195 bool mIsDone; 2196 }; 2197 2198 RectIterator RectIter() const { return RectIterator(*this); } 2199 }; 2200 2201 namespace mozilla { 2202 namespace gfx { 2203 2204 /** 2205 * BaseIntRegions use int32_t coordinates. 2206 */ 2207 template <typename Derived, typename Rect, typename Point, typename Margin> 2208 class BaseIntRegion { 2209 friend class ::nsRegion; 2210 2211 // Give access to all specializations of IntRegionTyped, not just ones that 2212 // derive from this specialization of BaseIntRegion. 2213 template <typename units> 2214 friend class IntRegionTyped; 2215 2216 public: 2217 typedef Rect RectType; 2218 typedef Point PointType; 2219 typedef Margin MarginType; 2220 2221 BaseIntRegion() = default; 2222 MOZ_IMPLICIT BaseIntRegion(const Rect& aRect) : mImpl(ToRect(aRect)) {} 2223 explicit BaseIntRegion(mozilla::gfx::ArrayView<pixman_box32_t> aRects) 2224 : mImpl(aRects) {} 2225 BaseIntRegion(const BaseIntRegion& aRegion) : mImpl(aRegion.mImpl) {} 2226 BaseIntRegion(BaseIntRegion&& aRegion) : mImpl(std::move(aRegion.mImpl)) {} 2227 Derived& operator=(const Rect& aRect) { 2228 mImpl = ToRect(aRect); 2229 return This(); 2230 } 2231 Derived& operator=(const Derived& aRegion) { 2232 mImpl = aRegion.mImpl; 2233 return This(); 2234 } 2235 Derived& operator=(Derived&& aRegion) { 2236 mImpl = std::move(aRegion.mImpl); 2237 return This(); 2238 } 2239 2240 bool operator==(const Derived& aRgn) const { return IsEqual(aRgn); } 2241 bool operator!=(const Derived& aRgn) const { return !(*this == aRgn); } 2242 2243 friend std::ostream& operator<<(std::ostream& stream, const Derived& m) { 2244 return stream << m.mImpl; 2245 } 2246 2247 void AndWith(const Derived& aOther) { And(This(), aOther); } 2248 void AndWith(const Rect& aOther) { And(This(), aOther); } 2249 Derived& And(const Derived& aRgn1, const Derived& aRgn2) { 2250 mImpl.And(aRgn1.mImpl, aRgn2.mImpl); 2251 return This(); 2252 } 2253 Derived& And(const Derived& aRegion, const Rect& aRect) { 2254 mImpl.And(aRegion.mImpl, ToRect(aRect)); 2255 return This(); 2256 } 2257 Derived& And(const Rect& aRect, const Derived& aRegion) { 2258 return And(aRegion, aRect); 2259 } 2260 Derived& And(const Rect& aRect1, const Rect& aRect2) { 2261 Rect TmpRect; 2262 2263 TmpRect.IntersectRect(aRect1, aRect2); 2264 mImpl = ToRect(TmpRect); 2265 return This(); 2266 } 2267 2268 Derived& OrWith(const Derived& aOther) { return Or(This(), aOther); } 2269 Derived& OrWith(const Rect& aOther) { return Or(This(), aOther); } 2270 Derived& Or(const Derived& aRgn1, const Derived& aRgn2) { 2271 mImpl.Or(aRgn1.mImpl, aRgn2.mImpl); 2272 return This(); 2273 } 2274 Derived& Or(const Derived& aRegion, const Rect& aRect) { 2275 mImpl.Or(aRegion.mImpl, ToRect(aRect)); 2276 return This(); 2277 } 2278 Derived& Or(const Rect& aRect, const Derived& aRegion) { 2279 return Or(aRegion, aRect); 2280 } 2281 Derived& Or(const Rect& aRect1, const Rect& aRect2) { 2282 mImpl = ToRect(aRect1); 2283 return Or(This(), aRect2); 2284 } 2285 2286 Derived& XorWith(const Derived& aOther) { return Xor(This(), aOther); } 2287 Derived& XorWith(const Rect& aOther) { return Xor(This(), aOther); } 2288 Derived& Xor(const Derived& aRgn1, const Derived& aRgn2) { 2289 mImpl.Xor(aRgn1.mImpl, aRgn2.mImpl); 2290 return This(); 2291 } 2292 Derived& Xor(const Derived& aRegion, const Rect& aRect) { 2293 mImpl.Xor(aRegion.mImpl, ToRect(aRect)); 2294 return This(); 2295 } 2296 Derived& Xor(const Rect& aRect, const Derived& aRegion) { 2297 return Xor(aRegion, aRect); 2298 } 2299 Derived& Xor(const Rect& aRect1, const Rect& aRect2) { 2300 mImpl = ToRect(aRect1); 2301 return Xor(This(), aRect2); 2302 } 2303 2304 Derived& SubOut(const Derived& aOther) { return Sub(This(), aOther); } 2305 Derived& SubOut(const Rect& aOther) { return Sub(This(), aOther); } 2306 Derived& Sub(const Derived& aRgn1, const Derived& aRgn2) { 2307 mImpl.Sub(aRgn1.mImpl, aRgn2.mImpl); 2308 return This(); 2309 } 2310 Derived& Sub(const Derived& aRegion, const Rect& aRect) { 2311 mImpl.Sub(aRegion.mImpl, ToRect(aRect)); 2312 return This(); 2313 } 2314 Derived& Sub(const Rect& aRect, const Derived& aRegion) { 2315 return Sub(Derived(aRect), aRegion); 2316 } 2317 Derived& Sub(const Rect& aRect1, const Rect& aRect2) { 2318 mImpl = ToRect(aRect1); 2319 return Sub(This(), aRect2); 2320 } 2321 2322 /** 2323 * Returns true iff the given point is inside the region. A region 2324 * created from a rect (x=0, y=0, w=100, h=100) will NOT contain 2325 * the point x=100, y=100. 2326 */ 2327 bool Contains(int aX, int aY) const { return mImpl.Contains(aX, aY); } 2328 bool Contains(const Point& aPoint) const { 2329 return mImpl.Contains(aPoint.x, aPoint.y); 2330 } 2331 bool Contains(const Rect& aRect) const { 2332 return mImpl.Contains(ToRect(aRect)); 2333 } 2334 bool Contains(const Derived& aRgn) const { 2335 return mImpl.Contains(aRgn.mImpl); 2336 } 2337 bool Intersects(const Rect& aRect) const { 2338 return mImpl.Intersects(ToRect(aRect)); 2339 } 2340 2341 void MoveBy(int32_t aXOffset, int32_t aYOffset) { 2342 MoveBy(Point(aXOffset, aYOffset)); 2343 } 2344 void MoveBy(Point aPt) { mImpl.MoveBy(aPt.X(), aPt.Y()); } 2345 Derived MovedBy(int32_t aXOffset, int32_t aYOffset) const { 2346 return MovedBy(Point(aXOffset, aYOffset)); 2347 } 2348 Derived MovedBy(const Point& aPt) const { 2349 Derived copy(This()); 2350 copy.MoveBy(aPt); 2351 return copy; 2352 } 2353 2354 Derived Intersect(const Derived& aOther) const { 2355 Derived intersection; 2356 intersection.And(This(), aOther); 2357 return intersection; 2358 } 2359 2360 void Inflate(const Margin& aMargin) { 2361 mImpl.Inflate( 2362 nsMargin(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left)); 2363 } 2364 Derived Inflated(const Margin& aMargin) const { 2365 Derived copy(This()); 2366 copy.Inflate(aMargin); 2367 return copy; 2368 } 2369 2370 void SetEmpty() { mImpl.SetEmpty(); } 2371 2372 bool IsEmpty() const { return mImpl.IsEmpty(); } 2373 bool IsComplex() const { return mImpl.IsComplex(); } 2374 bool IsEqual(const Derived& aRegion) const { 2375 return mImpl.IsEqual(aRegion.mImpl); 2376 } 2377 uint32_t GetNumRects() const { return mImpl.GetNumRects(); } 2378 Rect GetBounds() const { return FromRect(mImpl.GetBounds()); } 2379 uint64_t Area() const { return mImpl.Area(); } 2380 nsRegion ToAppUnits(nscoord aAppUnitsPerPixel) const { 2381 nsRegion result; 2382 for (auto iter = RectIterator(*this); !iter.Done(); iter.Next()) { 2383 nsRect appRect = ::ToAppUnits(iter.Get(), aAppUnitsPerPixel); 2384 result.Or(result, appRect); 2385 } 2386 return result; 2387 } 2388 Rect GetLargestRectangle(const Rect& aContainingRect = Rect()) const { 2389 return FromRect(mImpl.GetLargestRectangle(ToRect(aContainingRect))); 2390 } 2391 2392 Derived& ScaleRoundOut(float aXScale, float aYScale) { 2393 mImpl.ScaleRoundOut(aXScale, aYScale); 2394 return This(); 2395 } 2396 2397 Derived& ScaleInverseRoundOut(float aXScale, float aYScale) { 2398 mImpl.ScaleInverseRoundOut(aXScale, aYScale); 2399 return This(); 2400 } 2401 2402 // Prefer using TransformBy(matrix, region) from UnitTransforms.h, 2403 // as applying the transform should typically change the unit system. 2404 // TODO(botond): Move this to IntRegionTyped and disable it for 2405 // unit != UnknownUnits. 2406 Derived& Transform(const mozilla::gfx::Matrix4x4& aTransform) { 2407 mImpl.Transform(aTransform); 2408 return This(); 2409 } 2410 2411 /** 2412 * Make sure the region has at most aMaxRects by adding area to it 2413 * if necessary. The simplified region will be a superset of the 2414 * original region. The simplified region's bounding box will be 2415 * the same as for the current region. 2416 */ 2417 void SimplifyOutward(uint32_t aMaxRects) { mImpl.SimplifyOutward(aMaxRects); } 2418 void SimplifyOutwardByArea(uint32_t aThreshold) { 2419 mImpl.SimplifyOutwardByArea(aThreshold); 2420 } 2421 /** 2422 * Make sure the region has at most aMaxRects by removing area from 2423 * it if necessary. The simplified region will be a subset of the 2424 * original region. 2425 */ 2426 void SimplifyInward(uint32_t aMaxRects) { mImpl.SimplifyInward(aMaxRects); } 2427 2428 typedef void (*visitFn)(void* closure, VisitSide side, int x1, int y1, int x2, 2429 int y2); 2430 void VisitEdges(visitFn visit, void* closure) const { 2431 mImpl.VisitEdges(visit, closure); 2432 } 2433 2434 nsCString ToString() const { return mImpl.ToString(); } 2435 2436 class RectIterator { 2437 nsRegion::RectIterator mImpl; // The underlying iterator. 2438 mutable Rect mTmp; // The most recently gotten rectangle. 2439 2440 public: 2441 explicit RectIterator(const BaseIntRegion& aRegion) 2442 : mImpl(aRegion.mImpl) {} 2443 2444 bool Done() const { return mImpl.Done(); } 2445 2446 const Rect& Get() const { 2447 mTmp = FromRect(mImpl.Get()); 2448 return mTmp; 2449 } 2450 2451 void Next() { mImpl.Next(); } 2452 }; 2453 2454 RectIterator RectIter() const { return RectIterator(*this); } 2455 2456 protected: 2457 // Expose enough to derived classes from them to define conversions 2458 // between different types of BaseIntRegions. 2459 explicit BaseIntRegion(const nsRegion& aImpl) : mImpl(aImpl) {} 2460 const nsRegion& Impl() const { return mImpl; } 2461 2462 private: 2463 nsRegion mImpl; 2464 2465 static nsRect ToRect(const Rect& aRect) { 2466 return nsRect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); 2467 } 2468 static Rect FromRect(const nsRect& aRect) { 2469 return Rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); 2470 } 2471 2472 Derived& This() { return *static_cast<Derived*>(this); } 2473 const Derived& This() const { return *static_cast<const Derived*>(this); } 2474 }; 2475 2476 template <class units> 2477 class IntRegionTyped 2478 : public BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, 2479 IntPointTyped<units>, IntMarginTyped<units>> { 2480 typedef BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, 2481 IntPointTyped<units>, IntMarginTyped<units>> 2482 Super; 2483 2484 // Make other specializations of IntRegionTyped friends. 2485 template <typename OtherUnits> 2486 friend class IntRegionTyped; 2487 2488 static_assert(IsPixel<units>::value, 2489 "'units' must be a coordinate system tag"); 2490 2491 public: 2492 typedef IntRectTyped<units> RectType; 2493 typedef IntPointTyped<units> PointType; 2494 typedef IntMarginTyped<units> MarginType; 2495 2496 // Forward constructors. 2497 IntRegionTyped() = default; 2498 MOZ_IMPLICIT IntRegionTyped(const IntRectTyped<units>& aRect) 2499 : Super(aRect) {} 2500 IntRegionTyped(const IntRegionTyped& aRegion) : Super(aRegion) {} 2501 explicit IntRegionTyped(mozilla::gfx::ArrayView<pixman_box32_t> aRects) 2502 : Super(aRects) {} 2503 IntRegionTyped(IntRegionTyped&& aRegion) : Super(std::move(aRegion)) {} 2504 2505 // Assignment operators need to be forwarded as well, otherwise the compiler 2506 // will declare deleted ones. 2507 IntRegionTyped& operator=(const IntRegionTyped& aRegion) { 2508 return Super::operator=(aRegion); 2509 } 2510 IntRegionTyped& operator=(IntRegionTyped&& aRegion) { 2511 return Super::operator=(std::move(aRegion)); 2512 } 2513 2514 static IntRegionTyped FromUnknownRegion(const IntRegion& aRegion) { 2515 return IntRegionTyped(aRegion.Impl()); 2516 } 2517 IntRegion ToUnknownRegion() const { 2518 // Need |this->| because Impl() is defined in a dependent base class. 2519 return IntRegion(this->Impl()); 2520 } 2521 2522 private: 2523 // This is deliberately private, so calling code uses FromUnknownRegion(). 2524 explicit IntRegionTyped(const nsRegion& aRegion) : Super(aRegion) {} 2525 }; 2526 2527 } // namespace gfx 2528 } // namespace mozilla 2529 2530 typedef mozilla::gfx::IntRegion nsIntRegion; 2531 2532 #endif