Bug 1797755 - Part 5: Use a single initial mark stack size regardless of whether...
[gecko.git] / gfx / src / nsRegion.h
blobeb8c80960f16a8bf5fa1e38df0753c0eb10d14ee
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/. */
7 #ifndef nsRegion_h__
8 #define nsRegion_h__
10 #include <stddef.h> // for size_t
11 #include <stdint.h> // for uint32_t, uint64_t
13 #include <ostream> // for std::ostream
14 #include <utility> // for mozilla::Move
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"
28 // Uncomment this line to get additional integrity checking.
29 //#define DEBUG_REGIONS
30 #ifdef DEBUG_REGIONS
31 # include <sstream>
32 #endif
34 /* For information on the internal representation look at pixman-region.c
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.
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.
48 enum class VisitSide { TOP, BOTTOM, LEFT, RIGHT };
50 namespace regiondetails {
51 struct Band;
54 template <>
55 struct nsTArray_RelocationStrategy<regiondetails::Band> {
56 typedef nsTArray_RelocateUsingMoveConstructor<regiondetails::Band> Type;
59 namespace regiondetails {
61 template <typename T, typename E>
62 class UncheckedArray : public T {
63 public:
64 using T::Elements;
65 using T::Length;
67 UncheckedArray() = default;
68 MOZ_IMPLICIT UncheckedArray(T&& aSrc) : T(std::move(aSrc)) {}
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]; }
75 using iterator = E*;
76 using const_iterator = const E*;
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(); }
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) {}
92 bool operator!=(const Strip& aOther) const {
93 return left != aOther.left || right != aOther.right;
96 uint32_t Size() const { return right - left; }
98 int32_t left;
99 int32_t right;
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
111 MOZ_IMPLICIT Band(const nsRectAbsolute& aRect)
112 : top(aRect.Y()), bottom(aRect.YMost()) {
113 mStrips.AppendElement(Strip{aRect.X(), aRect.XMost()});
116 Band(const Band& aOther) = default;
117 Band(Band&& aOther) = default;
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;
128 if (strip.right < aStrip.left) {
129 // Current strip is before aStrip, try the next.
130 continue;
133 // Current strip intersects with aStrip, extend to the lext.
134 strip.left = std::min(strip.left, aStrip.left);
136 if (strip.right >= aStrip.right) {
137 // Current strip extends beyond aStrip, done.
138 return;
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;
147 mStrips.RemoveElementAt(next);
150 // Extend the strip in case the aStrip goes on beyond it.
151 strip.right = std::max(strip.right, aStrip.right);
152 return;
154 mStrips.AppendElement(aStrip);
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;
165 if (strip.right < aStrip.left) {
166 // Strip is entirely to the left of aStrip. Move on.
167 continue;
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;
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);
186 return;
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;
197 strip.left = aStrip.right;
198 return;
202 bool Intersects(const Strip& aStrip) const {
203 for (const Strip& strip : mStrips) {
204 if (strip.left >= aStrip.right) {
205 return false;
208 if (strip.right <= aStrip.left) {
209 continue;
212 return true;
214 return false;
217 bool IntersectStripBounds(Strip& aStrip) const {
218 bool intersected = false;
220 int32_t rightMost;
221 for (const Strip& strip : mStrips) {
222 if (strip.left > aStrip.right) {
223 break;
226 if (strip.right <= aStrip.left) {
227 continue;
230 if (!intersected) {
231 // First intersection, this is where the left side begins.
232 aStrip.left = std::max(aStrip.left, strip.left);
235 intersected = true;
236 // Expand to the right for each intersecting strip found.
237 rightMost = std::min(strip.right, aStrip.right);
240 if (intersected) {
241 aStrip.right = rightMost;
242 } else {
243 aStrip.right = aStrip.left = 0;
245 return intersected;
248 bool ContainsStrip(const Strip& aStrip) const {
249 for (const Strip& strip : mStrips) {
250 if (strip.left > aStrip.left) {
251 return false;
254 if (strip.right >= aStrip.right) {
255 return true;
258 return false;
261 bool EqualStrips(const Band& aBand) const {
262 if (mStrips.Length() != aBand.mStrips.Length()) {
263 return false;
266 for (auto iter1 = mStrips.begin(), iter2 = aBand.mStrips.begin();
267 iter1 != mStrips.end(); iter1++, iter2++) {
268 if (*iter1 != *iter2) {
269 return false;
273 return true;
276 void IntersectStrip(const Strip& aStrip) {
277 size_t i = 0;
279 while (i < mStrips.Length()) {
280 Strip& strip = mStrips[i];
281 if (strip.right <= aStrip.left) {
282 mStrips.RemoveElementAt(i);
283 continue;
286 if (strip.left >= aStrip.right) {
287 mStrips.TruncateLength(i);
288 return;
291 strip.left = std::max(aStrip.left, strip.left);
292 strip.right = std::min(aStrip.right, strip.right);
293 i++;
297 void IntersectStrips(const Band& aOther) {
298 auto iter = mStrips.begin();
299 auto iterOther = aOther.mStrips.begin();
301 StripArray newStrips;
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++;
312 if (iter == mStrips.end()) {
313 // End of our strips. Done.
314 break;
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++;
324 if (iterOther == aOther.mStrips.end()) {
325 // End of aOther's strips. Done.
326 break;
329 if (iterOther->left < iter->right) {
330 // Intersection!
331 break;
335 if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) {
336 break;
339 newStrips.AppendElement(Strip(std::max(iter->left, iterOther->left),
340 std::min(iterOther->right, iter->right)));
342 if (iterOther->right < iter->right) {
343 iterOther++;
344 if (iterOther == aOther.mStrips.end()) {
345 break;
347 } else {
348 iter++;
352 mStrips = std::move(newStrips);
355 bool Intersects(const Band& aOther) const {
356 auto iter = mStrips.begin();
357 auto iterOther = aOther.mStrips.begin();
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++;
368 if (iter == mStrips.end()) {
369 // End of our strips. Done.
370 break;
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++;
380 if (iterOther == aOther.mStrips.end()) {
381 // End of aOther's strips. Done.
382 break;
385 if (iterOther->left < iter->right) {
386 // Intersection!
387 break;
391 if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) {
392 break;
395 return true;
397 return false;
400 void SubStrips(const Band& aOther) {
401 size_t idx = 0;
402 auto iterOther = aOther.mStrips.begin();
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++;
414 if (idx == mStrips.Length()) {
415 // End of our strips. Done.
416 break;
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++;
426 if (iterOther == aOther.mStrips.end()) {
427 // End of aOther's strips. Done.
428 break;
431 if (iterOther->left < mStrips[idx].right) {
432 // Intersection!
433 break;
437 if (idx == mStrips.Length() || iterOther == aOther.mStrips.end()) {
438 break;
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++;
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;
462 continue;
465 // Our current strip is completely contained by the other strip.
466 mStrips.RemoveElementAt(idx);
470 int32_t top;
471 int32_t bottom;
472 StripArray mStrips;
474 } // namespace regiondetails
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
488 typedef nsRect RectType;
489 typedef nsPoint PointType;
490 typedef nsMargin MarginType;
492 nsRegion() = default;
493 MOZ_IMPLICIT nsRegion(const nsRect& aRect) {
494 mBounds = nsRectAbsolute::FromRect(aRect);
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]));
503 nsRegion(const nsRegion& aRegion) { Copy(aRegion); }
504 nsRegion(nsRegion&& aRegion)
505 : mBands(std::move(aRegion.mBands)), mBounds(aRegion.mBounds) {
506 aRegion.SetEmpty();
508 nsRegion& operator=(nsRegion&& aRegion) {
509 mBands = std::move(aRegion.mBands);
510 mBounds = aRegion.mBounds;
511 aRegion.SetEmpty();
512 return *this;
514 nsRegion& operator=(const nsRect& aRect) {
515 Copy(aRect);
516 return *this;
518 nsRegion& operator=(const nsRegion& aRegion) {
519 Copy(aRegion);
520 return *this;
522 bool operator==(const nsRegion& aRgn) const { return IsEqual(aRgn); }
523 bool operator!=(const nsRegion& aRgn) const { return !(*this == aRgn); }
525 friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m);
526 void OutputToStream(std::string aObjName, std::ostream& stream) const;
528 private:
529 #ifdef DEBUG_REGIONS
530 class OperationStringGenerator {
531 public:
532 virtual ~OperationStringGenerator() = default;
534 virtual void OutputOp() = 0;
536 #endif
537 public:
538 void AssertStateInternal() const;
539 void AssertState() const {
540 #ifdef DEBUG_REGIONS
541 AssertStateInternal();
542 #endif
545 private:
546 void And(BandArray& aOut, const BandArray& aIn1, const BandArray& aIn2) {
547 size_t idx = 0;
548 size_t idxOther = 0;
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++;
561 if (idx == aIn1.Length()) {
562 // This region is out of bands, the other region's future bands are
563 // ignored.
564 break;
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++;
574 if (idxOther == aIn2.Length()) {
575 // The other region's bands are all processed, all our future bands
576 // are ignored.
577 break;
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;
587 if (idx == aIn1.Length() || idxOther == aIn2.Length()) {
588 // The above loop executed a break because we're done.
589 break;
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]);
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));
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;
617 } else {
618 // No need to check here since we do at the beginning of the next
619 // iteration.
620 idx++;
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;
637 virtual ~OperationStringGeneratorAndWith() {
638 mRegion->mCurrentOpGenerator = nullptr;
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();
649 private:
650 nsRegion* mRegion;
651 nsRegion mRegionCopy;
652 nsRegion mOtherRegion;
655 OperationStringGeneratorAndWith opGenerator(*this, aRegion);
656 #endif
657 if (mBounds.IsEmpty()) {
658 // Region is empty, stays empty.
659 return *this;
662 if (aRegion.IsEmpty()) {
663 SetEmpty();
664 return *this;
667 if (aRegion.mBands.IsEmpty()) {
668 // Other region is a rect.
669 return AndWith(aRegion.mBounds);
672 if (mBands.IsEmpty()) {
673 mBands.AppendElement(mBounds);
676 BandArray newBands;
678 And(newBands, mBands, aRegion.mBands);
680 mBands = std::move(newBands);
681 if (!mBands.Length()) {
682 mBounds = nsRectAbsolute();
683 } else {
684 mBounds = CalculateBounds();
687 EnsureSimplified();
688 AssertState();
689 return *this;
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;
701 virtual ~OperationStringGeneratorAndWith() {
702 mRegion->mCurrentOpGenerator = nullptr;
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();
713 private:
714 nsRegion* mRegion;
715 nsRegion mRegionCopy;
716 nsRectAbsolute mRect;
719 OperationStringGeneratorAndWith opGenerator(*this, aRect);
720 #endif
721 if (aRect.IsEmpty()) {
722 SetEmpty();
723 return *this;
726 if (mBands.IsEmpty()) {
727 mBounds = mBounds.Intersect(aRect);
728 return *this;
731 size_t idx = 0;
733 size_t removeStart = 0;
735 // This removes all bands that do not intersect with aRect, and intersects
736 // the remaining ones with aRect.
738 // Start by figuring out how much to remove from the start.
739 while (idx != mBands.Length() && mBands[idx].bottom <= aRect.Y()) {
740 idx++;
743 // We'll remove these later to avoid needless copying in the array.
744 removeStart = idx;
746 while (idx != mBands.Length()) {
747 if (mBands[idx].top >= aRect.YMost()) {
748 mBands.TruncateLength(idx);
749 break;
752 mBands[idx].top = std::max(mBands[idx].top, aRect.Y());
753 mBands[idx].bottom = std::min(mBands[idx].bottom, aRect.YMost());
755 mBands[idx].IntersectStrip(Strip(aRect.X(), aRect.XMost()));
757 if (!mBands[idx].mStrips.Length()) {
758 mBands.RemoveElementAt(idx);
759 } else {
760 if (idx > removeStart) {
761 CompressBefore(idx);
763 idx++;
767 if (removeStart) {
768 mBands.RemoveElementsAt(0, removeStart);
771 if (mBands.Length()) {
772 mBounds = CalculateBounds();
773 } else {
774 mBounds.SetEmpty();
776 EnsureSimplified();
777 AssertState();
778 return *this;
780 nsRegion& AndWith(const nsRect& aRect) {
781 return AndWith(nsRectAbsolute::FromRect(aRect));
783 nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) {
784 if (&aRgn1 == this) {
785 return AndWith(aRgn2);
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;
795 virtual ~OperationStringGeneratorAnd() {
796 mRegion->mCurrentOpGenerator = nullptr;
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();
807 private:
808 nsRegion* mRegion;
809 nsRegion mRegion1;
810 nsRegion mRegion2;
813 OperationStringGeneratorAnd opGenerator(*this, aRgn1, aRgn2);
814 #endif
815 mBands.Clear();
817 if (aRgn1.IsEmpty() || aRgn2.IsEmpty()) {
818 mBounds.SetEmpty();
819 return *this;
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);
831 And(mBands, aRgn1.mBands, aRgn2.mBands);
833 if (!mBands.Length()) {
834 mBounds = nsRectAbsolute();
835 } else {
836 mBounds = CalculateBounds();
839 EnsureSimplified();
840 AssertState();
841 return *this;
843 nsRegion& And(const nsRect& aRect, const nsRegion& aRegion) {
844 return And(aRegion, aRect);
846 nsRegion& And(const nsRegion& aRegion, const nsRectAbsolute& aRect) {
847 if (&aRegion == this) {
848 return AndWith(aRect);
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;
859 virtual ~OperationStringGeneratorAnd() {
860 mThisRegion->mCurrentOpGenerator = nullptr;
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();
872 private:
873 nsRegion* mThisRegion;
874 nsRegion mRegion;
875 nsRectAbsolute mRect;
878 OperationStringGeneratorAnd opGenerator(*this, aRegion, aRect);
879 #endif
880 mBands.Clear();
882 if (aRect.IsEmpty()) {
883 mBounds.SetEmpty();
884 return *this;
887 if (aRegion.mBands.IsEmpty()) {
888 mBounds = aRegion.mBounds.Intersect(aRect);
889 return *this;
892 size_t idx = 0;
893 const BandArray& bands = aRegion.mBands;
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;
902 // We're done once we've reached the bottom.
903 if (bands[idx].top >= aRect.YMost()) {
904 break;
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());
912 newBand.IntersectStrip(Strip(aRect.X(), aRect.XMost()));
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));
922 idx++;
925 if (mBands.Length()) {
926 mBounds = CalculateBounds();
927 } else {
928 mBounds.SetEmpty();
931 EnsureSimplified();
932 AssertState();
933 return *this;
935 nsRegion& And(const nsRegion& aRegion, const nsRect& aRect) {
936 return And(aRegion, nsRectAbsolute::FromRect(aRect));
938 nsRegion& And(const nsRect& aRect1, const nsRect& aRect2) {
939 nsRect tmpRect;
941 tmpRect.IntersectRect(aRect1, aRect2);
942 return Copy(tmpRect);
945 nsRegion& OrWith(const nsRegion& aOther) {
946 for (RectIterator idx(aOther); !idx.Done(); idx.Next()) {
947 AddRect(idx.GetAbsolute());
949 return *this;
951 nsRegion& OrWith(const nsRect& aOther) {
952 AddRect(nsRectAbsolute::FromRect(aOther));
953 return *this;
955 nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2) {
956 if (&aRgn1 != this) {
957 *this = aRgn1;
959 for (RectIterator idx(aRgn2); !idx.Done(); idx.Next()) {
960 AddRect(idx.GetAbsolute());
962 return *this;
964 nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect) {
965 if (&aRegion != this) {
966 *this = aRegion;
968 AddRect(nsRectAbsolute::FromRect(aRect));
969 return *this;
971 nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion) {
972 return Or(aRegion, aRect);
974 nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2) {
975 Copy(aRect1);
976 return Or(*this, aRect2);
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);
990 nsRegion& Xor(const nsRegion& aRegion, const nsRect& aRect) {
991 return Xor(aRegion, nsRegion(aRect));
993 nsRegion& Xor(const nsRect& aRect, const nsRegion& aRegion) {
994 return Xor(nsRegion(aRect), aRegion);
996 nsRegion& Xor(const nsRect& aRect1, const nsRect& aRect2) {
997 return Xor(nsRegion(aRect1), nsRegion(aRect2));
1000 nsRegion ToAppUnits(nscoord aAppUnitsPerPixel) const;
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;
1013 virtual ~OperationStringGeneratorSubWith() {
1014 mRegion->mCurrentOpGenerator = nullptr;
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();
1025 private:
1026 nsRegion* mRegion;
1027 nsRegion mRegionCopy;
1028 nsRegion mOtherRegion;
1031 OperationStringGeneratorSubWith opGenerator(*this, aOther);
1032 #endif
1034 if (mBounds.IsEmpty()) {
1035 return *this;
1038 if (aOther.mBands.IsEmpty()) {
1039 return SubWith(aOther.mBounds);
1042 if (mBands.IsEmpty()) {
1043 mBands.AppendElement(Band(mBounds));
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++;
1057 if (idx == mBands.Length()) {
1058 // This region is out of bands, the other region's future bands are
1059 // ignored.
1060 break;
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++;
1070 if (idxOther == aOther.mBands.Length()) {
1071 // The other region's bands are all processed, all our future bands
1072 // are ignored.
1073 break;
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;
1083 if (idx == mBands.Length() || idxOther == aOther.mBands.Length()) {
1084 // The above loop executed a break because we're done.
1085 break;
1088 const Band& bandOther = aOther.mBands[idxOther];
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;
1099 continue;
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;
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);
1121 continue;
1124 // mBands[idx].bottom > bandOther.bottom
1125 Band newBand = mBands[idx];
1126 newBand.SubStrips(bandOther);
1128 if (!newBand.mStrips.IsEmpty()) {
1129 mBands.InsertElementAt(idx, newBand);
1130 mBands[idx].bottom = bandOther.bottom;
1131 CompressBefore(idx);
1132 idx++;
1135 mBands[idx].top = bandOther.bottom;
1137 idxOther++;
1138 if (idxOther == aOther.mBands.Length()) {
1139 break;
1143 if (mBands.IsEmpty()) {
1144 mBounds.SetEmpty();
1145 } else {
1146 mBounds = CalculateBounds();
1149 AssertState();
1150 EnsureSimplified();
1151 return *this;
1153 nsRegion& SubOut(const nsRegion& aOther) { return SubWith(aOther); }
1154 nsRegion& SubOut(const nsRect& aOther) { return SubWith(aOther); }
1156 private:
1157 void AppendOrExtend(const Band& aNewBand) {
1158 if (aNewBand.mStrips.IsEmpty()) {
1159 return;
1161 if (mBands.IsEmpty()) {
1162 mBands.AppendElement(aNewBand);
1163 return;
1166 if (mBands.LastElement().bottom == aNewBand.top &&
1167 mBands.LastElement().EqualStrips(aNewBand)) {
1168 mBands.LastElement().bottom = aNewBand.bottom;
1169 } else {
1170 mBands.AppendElement(aNewBand);
1173 void AppendOrExtend(const Band&& aNewBand) {
1174 if (aNewBand.mStrips.IsEmpty()) {
1175 return;
1177 if (mBands.IsEmpty()) {
1178 mBands.AppendElement(std::move(aNewBand));
1179 return;
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));
1190 public:
1191 nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2) {
1192 if (&aRgn1 == this) {
1193 return SubWith(aRgn2);
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;
1203 virtual ~OperationStringGeneratorSub() {
1204 mRegion->mCurrentOpGenerator = nullptr;
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();
1215 private:
1216 nsRegion* mRegion;
1217 nsRegion mRegion1;
1218 nsRegion mRegion2;
1221 OperationStringGeneratorSub opGenerator(*this, aRgn1, aRgn2);
1222 #endif
1224 mBands.Clear();
1226 if (aRgn1.mBounds.IsEmpty()) {
1227 mBounds.SetEmpty();
1228 return *this;
1231 if (aRgn2.mBounds.IsEmpty()) {
1232 Copy(aRgn1);
1233 return *this;
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);
1244 const BandArray& bands1 = aRgn1.mBands;
1245 const BandArray& bands2 = aRgn2.mBands;
1247 size_t idx = 0;
1248 size_t idxOther = 0;
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++;
1259 if (idxOther == bands2.Length()) {
1260 break;
1263 const Band& other = bands2[idxOther];
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;
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));
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);
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));
1297 lastBottom = newBand.bottom;
1298 newBand.SubStrips(other);
1299 AppendOrExtend(std::move(newBand));
1300 idxOther++;
1302 // Decrement other here so we are back at the last band in region 2
1303 // that intersected.
1304 idxOther--;
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++;
1314 idx++;
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++;
1324 while (idx < bands1.Length()) {
1325 mBands.AppendElement(bands1[idx]);
1326 idx++;
1329 if (mBands.IsEmpty()) {
1330 mBounds.SetEmpty();
1331 } else {
1332 mBounds = CalculateBounds();
1335 AssertState();
1336 EnsureSimplified();
1337 return *this;
1340 private:
1341 // Internal helper for executing subtraction.
1342 void RunSubtraction(const nsRectAbsolute& aRect) {
1343 Strip rectStrip(aRect.X(), aRect.XMost());
1345 size_t idx = 0;
1347 while (idx < mBands.Length()) {
1348 if (mBands[idx].top >= aRect.YMost()) {
1349 return;
1352 if (mBands[idx].bottom <= aRect.Y()) {
1353 // This band is entirely before aRect, move on.
1354 idx++;
1355 continue;
1358 if (!mBands[idx].Intersects(Strip(aRect.X(), aRect.XMost()))) {
1359 // This band does not intersect aRect horizontally. Move on.
1360 idx++;
1361 continue;
1364 // This band intersects with aRect.
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;
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);
1386 continue;
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();
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));
1404 return;
1408 public:
1409 nsRegion& SubWith(const nsRectAbsolute& aRect) {
1410 if (!mBounds.Intersects(aRect)) {
1411 return *this;
1414 if (aRect.Contains(mBounds)) {
1415 SetEmpty();
1416 return *this;
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;
1427 virtual ~OperationStringGeneratorSubWith() {
1428 mRegion->mCurrentOpGenerator = nullptr;
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();
1439 private:
1440 nsRegion* mRegion;
1441 nsRegion mRegionCopy;
1442 nsRectAbsolute mRect;
1445 OperationStringGeneratorSubWith opGenerator(*this, aRect);
1446 #endif
1448 if (mBands.IsEmpty()) {
1449 mBands.AppendElement(Band(mBounds));
1452 RunSubtraction(aRect);
1454 if (aRect.X() <= mBounds.X() || aRect.Y() <= mBounds.Y() ||
1455 aRect.XMost() >= mBounds.XMost() || aRect.YMost() >= mBounds.YMost()) {
1456 mBounds = CalculateBounds();
1458 EnsureSimplified();
1459 AssertState();
1460 return *this;
1462 nsRegion& Sub(const nsRegion& aRegion, const nsRectAbsolute& aRect) {
1463 if (aRect.Contains(aRegion.mBounds)) {
1464 SetEmpty();
1465 return *this;
1467 if (&aRegion == this) {
1468 return SubWith(aRect);
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;
1479 virtual ~OperationStringGeneratorSub() {
1480 mRegion->mCurrentOpGenerator = nullptr;
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();
1492 private:
1493 nsRegion* mRegion;
1494 nsRegion mRegionOther;
1495 nsRectAbsolute mRect;
1498 OperationStringGeneratorSub opGenerator(*this, aRegion, aRect);
1499 #endif
1501 mBands.Clear();
1503 if (aRegion.mBounds.IsEmpty()) {
1504 mBounds.SetEmpty();
1505 return *this;
1508 if (aRect.IsEmpty()) {
1509 Copy(aRegion);
1510 return *this;
1513 if (aRegion.mBands.IsEmpty()) {
1514 Copy(aRegion.mBounds);
1515 return SubWith(aRect);
1518 const BandArray& bands = aRegion.mBands;
1520 size_t idx = 0;
1522 Strip strip(aRect.X(), aRect.XMost());
1524 mBands.SetCapacity(bands.Length() + 3);
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++;
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++;
1540 mBands.AppendElement(std::move(newBand));
1543 // This tracks whether the band when we -exit- the next loop intersected the
1544 // rectangle.
1545 bool didIntersect = false;
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;
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++;
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));
1575 // Now process all bands beyond aRect.
1576 if (idx < bands.Length()) {
1577 AppendOrExtend(bands[idx]);
1578 idx++;
1581 mBands.AppendElements(bands.Elements() + idx, bands.Length() - idx);
1583 if (mBands.IsEmpty()) {
1584 mBounds.SetEmpty();
1585 } else {
1586 mBounds = CalculateBounds();
1589 AssertState();
1590 EnsureSimplified();
1591 return *this;
1593 nsRegion& SubWith(const nsRect& aRect) {
1594 return SubWith(nsRectAbsolute::FromRect(aRect));
1596 nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion) {
1597 Copy(aRect);
1598 return SubWith(aRegion);
1600 nsRegion& Sub(const nsRectAbsolute& aRect, const nsRegion& aRegion) {
1601 Copy(aRect);
1602 return SubWith(aRegion);
1604 nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2) {
1605 Copy(aRect1);
1606 return SubWith(aRect2);
1608 nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect) {
1609 return Sub(aRegion, nsRectAbsolute::FromRect(aRect));
1611 nsRegion& Sub(const nsRectAbsolute& aRect1, const nsRectAbsolute& aRect2) {
1612 Copy(aRect1);
1613 return SubWith(aRect2);
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.
1621 bool Contains(int aX, int aY) const {
1622 if (mBands.IsEmpty()) {
1623 return mBounds.Contains(aX, aY);
1626 auto iter = mBands.begin();
1628 while (iter != mBands.end()) {
1629 if (iter->bottom <= aY) {
1630 iter++;
1631 continue;
1634 if (iter->top > aY) {
1635 return false;
1638 if (iter->ContainsStrip(Strip(aX, aX + 1))) {
1639 return true;
1641 return false;
1643 return false;
1646 bool Contains(const nsPoint& aPoint) const {
1647 return Contains(aPoint.x, aPoint.y);
1650 bool Contains(const nsRectAbsolute& aRect) const {
1651 if (aRect.IsEmpty()) {
1652 return false;
1655 if (mBands.IsEmpty()) {
1656 return mBounds.Contains(aRect);
1659 auto iter = mBands.begin();
1661 while (iter != mBands.end()) {
1662 if (iter->bottom <= aRect.Y()) {
1663 iter++;
1664 continue;
1667 if (iter->top > aRect.Y()) {
1668 return false;
1671 // Now inside the rectangle.
1672 if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) {
1673 return false;
1676 if (iter->bottom >= aRect.YMost()) {
1677 return true;
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;
1688 if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) {
1689 return false;
1692 if (iter->bottom >= aRect.YMost()) {
1693 return true;
1696 lastY = iter->bottom;
1697 iter++;
1700 return false;
1702 bool Contains(const nsRect& aRect) const {
1703 return Contains(nsRectAbsolute::FromRect(aRect));
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));
1712 void MoveBy(int32_t aXOffset, int32_t aYOffset) {
1713 MoveBy(nsPoint(aXOffset, aYOffset));
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;
1723 virtual ~OperationStringGeneratorMoveBy() {
1724 mRegion->mCurrentOpGenerator = nullptr;
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();
1735 private:
1736 nsRegion* mRegion;
1737 nsRegion mRegionCopy;
1738 nsPoint mPoint;
1741 OperationStringGeneratorMoveBy opGenerator(*this, aPt);
1742 #endif
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();
1753 AssertState();
1755 void SetEmpty() {
1756 mBands.Clear();
1757 mBounds.SetEmpty();
1760 nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const {
1761 return MovedBy(nsPoint(aXOffset, aYOffset));
1763 nsRegion MovedBy(const nsPoint& aPt) const {
1764 nsRegion copy(*this);
1765 copy.MoveBy(aPt);
1766 return copy;
1769 nsRegion Intersect(const nsRegion& aOther) const {
1770 nsRegion intersection;
1771 intersection.And(*this, aOther);
1772 return intersection;
1775 void Inflate(const nsMargin& aMargin);
1777 nsRegion Inflated(const nsMargin& aMargin) const {
1778 nsRegion copy(*this);
1779 copy.Inflate(aMargin);
1780 return copy;
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;
1790 if (mBands.Length() != aRegion.mBands.Length()) {
1791 return false;
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;
1802 return true;
1805 uint32_t GetNumRects() const {
1806 if (mBands.IsEmpty()) {
1807 return mBounds.IsEmpty() ? 0 : 1;
1810 uint32_t rects = 0;
1812 for (const Band& band : mBands) {
1813 rects += band.mStrips.Length();
1816 return rects;
1818 const nsRect GetBounds() const { return mBounds.ToNSRect(); }
1819 const nsRectAbsolute GetAbsoluteBounds() const { return mBounds; }
1820 uint64_t Area() const;
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
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;
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)
1852 nsRect GetLargestRectangle(const nsRect& aContainingRect = nsRect()) const;
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.
1860 void SimplifyOutward(uint32_t aMaxRects);
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.
1867 void SimplifyOutwardByArea(uint32_t aThreshold);
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.
1873 void SimplifyInward(uint32_t aMaxRects);
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.
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)
1887 typedef void (*visitFn)(void* closure, VisitSide side, int x1, int y1, int x2,
1888 int y2);
1889 void VisitEdges(visitFn, void* closure) const;
1891 nsCString ToString() const;
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;
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;
1903 private:
1904 nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const;
1906 nsRegion& Copy(const nsRegion& aRegion) {
1907 mBounds = aRegion.mBounds;
1908 mBands = aRegion.mBands.Clone();
1909 return *this;
1912 nsRegion& Copy(const nsRect& aRect) {
1913 mBands.Clear();
1914 mBounds = nsRectAbsolute::FromRect(aRect);
1915 return *this;
1918 nsRegion& Copy(const nsRectAbsolute& aRect) {
1919 mBands.Clear();
1920 mBounds = aRect;
1921 return *this;
1924 void EnsureSimplified() {
1925 if (mBands.Length() == 1 && mBands.begin()->mStrips.Length() == 1) {
1926 mBands.Clear();
1930 static inline nsRectAbsolute BoxToRect(const pixman_box32_t& aBox) {
1931 return nsRectAbsolute(aBox.x1, aBox.y1, aBox.x2, aBox.y2);
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;
1943 virtual ~OperationStringGeneratorAddRect() {
1944 mRegion->mCurrentOpGenerator = nullptr;
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();
1955 private:
1956 nsRegion* mRegion;
1957 nsRegion mRegionCopy;
1958 nsRectAbsolute mRect;
1961 OperationStringGeneratorAddRect opGenerator(*this, aRect);
1962 #endif
1963 if (aRect.IsEmpty()) {
1964 return;
1967 if (mBands.IsEmpty()) {
1968 if (mBounds.IsEmpty()) {
1969 mBounds = aRect;
1970 return;
1971 } else if (mBounds.Contains(aRect)) {
1972 return;
1975 mBands.AppendElement(Band(mBounds));
1978 mBounds = aRect.UnsafeUnion(mBounds);
1980 size_t idx = 0;
1982 Strip strip(aRect.X(), aRect.XMost());
1983 Band remaining(aRect);
1985 while (idx != mBands.Length()) {
1986 if (mBands[idx].bottom <= remaining.top) {
1987 // This band lies wholly above aRect.
1988 idx++;
1989 continue;
1992 if (remaining.top >= remaining.bottom) {
1993 AssertState();
1994 EnsureSimplified();
1995 return;
1998 if (mBands[idx].top >= remaining.bottom) {
1999 // This band lies wholly below aRect.
2000 break;
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;
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;
2022 if (mBands[idx].ContainsStrip(strip)) {
2023 remaining.top = mBands[idx].bottom;
2024 idx++;
2025 continue;
2028 // mBands[idx].top <= remaining.top.
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;
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;
2045 mBands[idx].InsertStrip(strip);
2046 CompressBefore(idx);
2047 remaining.top = mBands[idx].bottom;
2048 idx++;
2049 CompressBefore(idx);
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;
2064 mBands.InsertElementAt(idx, remaining);
2065 idx++;
2066 CompressBefore(idx);
2067 } else {
2068 CompressBefore(idx);
2071 AssertState();
2072 EnsureSimplified();
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;
2082 int32_t top = mBands.begin()->top;
2083 int32_t bottom = mBands.LastElement().bottom;
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);
2092 return nsRectAbsolute(leftMost, top, rightMost, bottom);
2095 static uint32_t ComputeMergedAreaIncrease(const Band& aTopBand,
2096 const Band& aBottomBand);
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);
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;
2115 return false;
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--;
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
2138 public:
2139 class RectIterator {
2140 const nsRegion& mRegion;
2141 typename BandArray::const_iterator mCurrentBand;
2142 typename StripArray::const_iterator mCurrentStrip;
2144 public:
2145 explicit RectIterator(const nsRegion& aRegion)
2146 : mRegion(aRegion),
2147 mCurrentBand(aRegion.mBands.begin())
2148 #ifndef DEBUG
2150 mCurrentStrip(nullptr)
2151 #endif
2153 mIsDone = mRegion.mBounds.IsEmpty();
2154 if (mCurrentBand != aRegion.mBands.end()) {
2155 mCurrentStrip = mCurrentBand->mStrips.begin();
2159 bool Done() const { return mIsDone; }
2161 const nsRect Get() const {
2162 if (mRegion.mBands.IsEmpty()) {
2163 return mRegion.GetBounds();
2165 return nsRect(mCurrentStrip->left, mCurrentBand->top,
2166 mCurrentStrip->right - mCurrentStrip->left,
2167 mCurrentBand->bottom - mCurrentBand->top);
2170 const nsRectAbsolute GetAbsolute() const {
2171 if (mRegion.mBands.IsEmpty()) {
2172 return mRegion.mBounds;
2174 return nsRectAbsolute(mCurrentStrip->left, mCurrentBand->top,
2175 mCurrentStrip->right, mCurrentBand->bottom);
2178 void Next() {
2179 if (mRegion.mBands.IsEmpty()) {
2180 mIsDone = true;
2181 return;
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;
2195 bool mIsDone;
2198 RectIterator RectIter() const { return RectIterator(*this); }
2201 namespace mozilla {
2202 namespace gfx {
2205 * BaseIntRegions use int32_t coordinates.
2207 template <typename Derived, typename Rect, typename Point, typename Margin>
2208 class BaseIntRegion {
2209 friend class ::nsRegion;
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;
2216 public:
2217 typedef Rect RectType;
2218 typedef Point PointType;
2219 typedef Margin MarginType;
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();
2231 Derived& operator=(const Derived& aRegion) {
2232 mImpl = aRegion.mImpl;
2233 return This();
2235 Derived& operator=(Derived&& aRegion) {
2236 mImpl = std::move(aRegion.mImpl);
2237 return This();
2240 bool operator==(const Derived& aRgn) const { return IsEqual(aRgn); }
2241 bool operator!=(const Derived& aRgn) const { return !(*this == aRgn); }
2243 friend std::ostream& operator<<(std::ostream& stream, const Derived& m) {
2244 return stream << m.mImpl;
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();
2253 Derived& And(const Derived& aRegion, const Rect& aRect) {
2254 mImpl.And(aRegion.mImpl, ToRect(aRect));
2255 return This();
2257 Derived& And(const Rect& aRect, const Derived& aRegion) {
2258 return And(aRegion, aRect);
2260 Derived& And(const Rect& aRect1, const Rect& aRect2) {
2261 Rect TmpRect;
2263 TmpRect.IntersectRect(aRect1, aRect2);
2264 mImpl = ToRect(TmpRect);
2265 return This();
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();
2274 Derived& Or(const Derived& aRegion, const Rect& aRect) {
2275 mImpl.Or(aRegion.mImpl, ToRect(aRect));
2276 return This();
2278 Derived& Or(const Rect& aRect, const Derived& aRegion) {
2279 return Or(aRegion, aRect);
2281 Derived& Or(const Rect& aRect1, const Rect& aRect2) {
2282 mImpl = ToRect(aRect1);
2283 return Or(This(), aRect2);
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();
2292 Derived& Xor(const Derived& aRegion, const Rect& aRect) {
2293 mImpl.Xor(aRegion.mImpl, ToRect(aRect));
2294 return This();
2296 Derived& Xor(const Rect& aRect, const Derived& aRegion) {
2297 return Xor(aRegion, aRect);
2299 Derived& Xor(const Rect& aRect1, const Rect& aRect2) {
2300 mImpl = ToRect(aRect1);
2301 return Xor(This(), aRect2);
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();
2310 Derived& Sub(const Derived& aRegion, const Rect& aRect) {
2311 mImpl.Sub(aRegion.mImpl, ToRect(aRect));
2312 return This();
2314 Derived& Sub(const Rect& aRect, const Derived& aRegion) {
2315 return Sub(Derived(aRect), aRegion);
2317 Derived& Sub(const Rect& aRect1, const Rect& aRect2) {
2318 mImpl = ToRect(aRect1);
2319 return Sub(This(), aRect2);
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.
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);
2331 bool Contains(const Rect& aRect) const {
2332 return mImpl.Contains(ToRect(aRect));
2334 bool Contains(const Derived& aRgn) const {
2335 return mImpl.Contains(aRgn.mImpl);
2337 bool Intersects(const Rect& aRect) const {
2338 return mImpl.Intersects(ToRect(aRect));
2341 void MoveBy(int32_t aXOffset, int32_t aYOffset) {
2342 MoveBy(Point(aXOffset, aYOffset));
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));
2348 Derived MovedBy(const Point& aPt) const {
2349 Derived copy(This());
2350 copy.MoveBy(aPt);
2351 return copy;
2354 Derived Intersect(const Derived& aOther) const {
2355 Derived intersection;
2356 intersection.And(This(), aOther);
2357 return intersection;
2360 void Inflate(const Margin& aMargin) {
2361 mImpl.Inflate(
2362 nsMargin(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left));
2364 Derived Inflated(const Margin& aMargin) const {
2365 Derived copy(This());
2366 copy.Inflate(aMargin);
2367 return copy;
2370 void SetEmpty() { mImpl.SetEmpty(); }
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);
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);
2386 return result;
2388 Rect GetLargestRectangle(const Rect& aContainingRect = Rect()) const {
2389 return FromRect(mImpl.GetLargestRectangle(ToRect(aContainingRect)));
2392 Derived& ScaleRoundOut(float aXScale, float aYScale) {
2393 mImpl.ScaleRoundOut(aXScale, aYScale);
2394 return This();
2397 Derived& ScaleInverseRoundOut(float aXScale, float aYScale) {
2398 mImpl.ScaleInverseRoundOut(aXScale, aYScale);
2399 return This();
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();
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.
2417 void SimplifyOutward(uint32_t aMaxRects) { mImpl.SimplifyOutward(aMaxRects); }
2418 void SimplifyOutwardByArea(uint32_t aThreshold) {
2419 mImpl.SimplifyOutwardByArea(aThreshold);
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.
2426 void SimplifyInward(uint32_t aMaxRects) { mImpl.SimplifyInward(aMaxRects); }
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);
2434 nsCString ToString() const { return mImpl.ToString(); }
2436 class RectIterator {
2437 nsRegion::RectIterator mImpl; // The underlying iterator.
2438 mutable Rect mTmp; // The most recently gotten rectangle.
2440 public:
2441 explicit RectIterator(const BaseIntRegion& aRegion)
2442 : mImpl(aRegion.mImpl) {}
2444 bool Done() const { return mImpl.Done(); }
2446 const Rect& Get() const {
2447 mTmp = FromRect(mImpl.Get());
2448 return mTmp;
2451 void Next() { mImpl.Next(); }
2454 RectIterator RectIter() const { return RectIterator(*this); }
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; }
2462 private:
2463 nsRegion mImpl;
2465 static nsRect ToRect(const Rect& aRect) {
2466 return nsRect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
2468 static Rect FromRect(const nsRect& aRect) {
2469 return Rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
2472 Derived& This() { return *static_cast<Derived*>(this); }
2473 const Derived& This() const { return *static_cast<const Derived*>(this); }
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;
2484 // Make other specializations of IntRegionTyped friends.
2485 template <typename OtherUnits>
2486 friend class IntRegionTyped;
2488 static_assert(IsPixel<units>::value,
2489 "'units' must be a coordinate system tag");
2491 public:
2492 typedef IntRectTyped<units> RectType;
2493 typedef IntPointTyped<units> PointType;
2494 typedef IntMarginTyped<units> MarginType;
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)) {}
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);
2510 IntRegionTyped& operator=(IntRegionTyped&& aRegion) {
2511 return Super::operator=(std::move(aRegion));
2514 static IntRegionTyped FromUnknownRegion(const IntRegion& aRegion) {
2515 return IntRegionTyped(aRegion.Impl());
2517 IntRegion ToUnknownRegion() const {
2518 // Need |this->| because Impl() is defined in a dependent base class.
2519 return IntRegion(this->Impl());
2522 private:
2523 // This is deliberately private, so calling code uses FromUnknownRegion().
2524 explicit IntRegionTyped(const nsRegion& aRegion) : Super(aRegion) {}
2527 } // namespace gfx
2528 } // namespace mozilla
2530 typedef mozilla::gfx::IntRegion nsIntRegion;
2532 #endif