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/. */
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
28 // Uncomment this line to get additional integrity checking.
29 // #define DEBUG_REGIONS
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
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
{
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
{
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]; }
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(); }
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
; }
103 using Strip
= regiondetails::Strip
;
106 regiondetails::UncheckedArray
<CopyableAutoTArray
<Strip
, 2>, Strip
>;
108 using StripArray
= CopyableAutoTArray
<Strip
, 2>;
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
);
128 if (strip
.right
< aStrip
.left
) {
129 // Current strip is before aStrip, try the next.
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.
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
);
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.
165 if (strip
.right
< aStrip
.left
) {
166 // Strip is entirely to the left of aStrip. Move on.
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.
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()) {
182 mStrips
.InsertElementAt(i
, newStrip
);
184 mStrips
.AppendElement(newStrip
);
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.
197 strip
.left
= aStrip
.right
;
202 bool Intersects(const Strip
& aStrip
) const {
203 for (const Strip
& strip
: mStrips
) {
204 if (strip
.left
>= aStrip
.right
) {
208 if (strip
.right
<= aStrip
.left
) {
217 bool IntersectStripBounds(Strip
& aStrip
) const {
218 bool intersected
= false;
221 for (const Strip
& strip
: mStrips
) {
222 if (strip
.left
> aStrip
.right
) {
226 if (strip
.right
<= aStrip
.left
) {
231 // First intersection, this is where the left side begins.
232 aStrip
.left
= std::max(aStrip
.left
, strip
.left
);
236 // Expand to the right for each intersecting strip found.
237 rightMost
= std::min(strip
.right
, aStrip
.right
);
241 aStrip
.right
= rightMost
;
243 aStrip
.right
= aStrip
.left
= 0;
248 bool ContainsStrip(const Strip
& aStrip
) const {
249 for (const Strip
& strip
: mStrips
) {
250 if (strip
.left
> aStrip
.left
) {
254 if (strip
.right
>= aStrip
.right
) {
261 bool EqualStrips(const Band
& aBand
) const {
262 if (mStrips
.Length() != aBand
.mStrips
.Length()) {
266 for (auto iter1
= mStrips
.begin(), iter2
= aBand
.mStrips
.begin();
267 iter1
!= mStrips
.end(); iter1
++, iter2
++) {
268 if (*iter1
!= *iter2
) {
276 void IntersectStrip(const Strip
& aStrip
) {
279 while (i
< mStrips
.Length()) {
280 Strip
& strip
= mStrips
[i
];
281 if (strip
.right
<= aStrip
.left
) {
282 mStrips
.RemoveElementAt(i
);
286 if (strip
.left
>= aStrip
.right
) {
287 mStrips
.TruncateLength(i
);
291 strip
.left
= std::max(aStrip
.left
, strip
.left
);
292 strip
.right
= std::min(aStrip
.right
, strip
.right
);
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.
306 while (iter
!= mStrips
.end() && iter
->right
<= iterOther
->left
) {
307 // Increment our current strip until it ends beyond aOther's current
312 if (iter
== mStrips
.end()) {
313 // End of our strips. Done.
317 while (iterOther
!= aOther
.mStrips
.end() &&
318 iterOther
->right
<= iter
->left
) {
319 // Increment aOther's current strip until it lies beyond our current
324 if (iterOther
== aOther
.mStrips
.end()) {
325 // End of aOther's strips. Done.
329 if (iterOther
->left
< iter
->right
) {
335 if (iter
== mStrips
.end() || iterOther
== aOther
.mStrips
.end()) {
339 newStrips
.AppendElement(Strip(std::max(iter
->left
, iterOther
->left
),
340 std::min(iterOther
->right
, iter
->right
)));
342 if (iterOther
->right
< iter
->right
) {
344 if (iterOther
== aOther
.mStrips
.end()) {
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.
362 while (iter
!= mStrips
.end() && iter
->right
<= iterOther
->left
) {
363 // Increment our current strip until it ends beyond aOther's current
368 if (iter
== mStrips
.end()) {
369 // End of our strips. Done.
373 while (iterOther
!= aOther
.mStrips
.end() &&
374 iterOther
->right
<= iter
->left
) {
375 // Increment aOther's current strip until it lies beyond our current
380 if (iterOther
== aOther
.mStrips
.end()) {
381 // End of aOther's strips. Done.
385 if (iterOther
->left
< iter
->right
) {
391 if (iter
== mStrips
.end() || iterOther
== aOther
.mStrips
.end()) {
400 void SubStrips(const Band
& aOther
) {
402 auto iterOther
= aOther
.mStrips
.begin();
404 // This function finds the intersection between two sets of strips.
407 while (idx
< mStrips
.Length() &&
408 mStrips
[idx
].right
<= iterOther
->left
) {
409 // Increment our current strip until it ends beyond aOther's current
414 if (idx
== mStrips
.Length()) {
415 // End of our strips. Done.
419 while (iterOther
!= aOther
.mStrips
.end() &&
420 iterOther
->right
<= mStrips
[idx
].left
) {
421 // Increment aOther's current strip until it lies beyond our current
426 if (iterOther
== aOther
.mStrips
.end()) {
427 // End of aOther's strips. Done.
431 if (iterOther
->left
< mStrips
[idx
].right
) {
437 if (idx
== mStrips
.Length() || iterOther
== aOther
.mStrips
.end()) {
441 if (mStrips
[idx
].left
< iterOther
->left
) {
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
);
451 mStrips
[oldIdx
].right
= iterOther
->left
;
452 // Either idx was just incremented, or the current index no longer
453 // intersects with iterOther.
455 } else if (mStrips
[idx
].right
> iterOther
->right
) {
456 mStrips
[idx
].left
= iterOther
->right
;
457 // Current strip no longer intersects, continue.
459 if (iterOther
== aOther
.mStrips
.end()) {
465 // Our current strip is completely contained by the other strip.
466 mStrips
.RemoveElementAt(idx
);
474 } // namespace regiondetails
478 using Band
= regiondetails::Band
;
479 using Strip
= regiondetails::Strip
;
481 using BandArray
= regiondetails::UncheckedArray
<nsTArray
<Band
>, Band
>;
482 using StripArray
= regiondetails::UncheckedArray
<AutoTArray
<Strip
, 2>, Strip
>;
484 using BandArray
= nsTArray
<Band
>;
485 using StripArray
= AutoTArray
<Strip
, 2>;
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
) {
508 nsRegion
& operator=(nsRegion
&& aRegion
) {
509 mBands
= std::move(aRegion
.mBands
);
510 mBounds
= aRegion
.mBounds
;
514 nsRegion
& operator=(const nsRect
& aRect
) {
518 nsRegion
& operator=(const nsRegion
& aRegion
) {
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;
530 class OperationStringGenerator
{
532 virtual ~OperationStringGenerator() = default;
534 virtual void OutputOp() = 0;
538 void AssertStateInternal() const;
539 void AssertState() const {
541 AssertStateInternal();
546 void And(BandArray
& aOut
, const BandArray
& aIn1
, const BandArray
& aIn2
) {
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.
555 while (idx
!= aIn1
.Length() && aIn1
[idx
].bottom
<= aIn2
[idxOther
].top
) {
556 // Increment our current band until it ends beyond aOther's current
561 if (idx
== aIn1
.Length()) {
562 // This region is out of bands, the other region's future bands are
567 while (idxOther
!= aIn2
.Length() &&
568 aIn2
[idxOther
].bottom
<= aIn1
[idx
].top
) {
569 // Increment aOther's current band until it ends beyond our current
574 if (idxOther
== aIn2
.Length()) {
575 // The other region's bands are all processed, all our future bands
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.
587 if (idx
== aIn1
.Length() || idxOther
== aIn2
.Length()) {
588 // The above loop executed a break because we're done.
592 Band
newBand(aIn1
[idx
]);
593 // The new band is the intersection of the two current bands from both
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
;
606 aOut
.AppendElement(std::move(newBand
));
610 if (aIn2
[idxOther
].bottom
< aIn1
[idx
].bottom
) {
612 if (idxOther
== aIn2
.Length()) {
613 // Since we will access idxOther the next iteration, check if we're
618 // No need to check here since we do at the beginning of the next
626 nsRegion
& AndWith(const nsRegion
& aRegion
) {
628 class OperationStringGeneratorAndWith
: public OperationStringGenerator
{
630 OperationStringGeneratorAndWith(nsRegion
& aRegion
,
631 const nsRegion
& aOtherRegion
)
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();
651 nsRegion mRegionCopy
;
652 nsRegion mOtherRegion
;
655 OperationStringGeneratorAndWith
opGenerator(*this, aRegion
);
657 if (mBounds
.IsEmpty()) {
658 // Region is empty, stays empty.
662 if (aRegion
.IsEmpty()) {
667 if (aRegion
.mBands
.IsEmpty()) {
668 // Other region is a rect.
669 return AndWith(aRegion
.mBounds
);
672 if (mBands
.IsEmpty()) {
673 mBands
.AppendElement(mBounds
);
678 And(newBands
, mBands
, aRegion
.mBands
);
680 mBands
= std::move(newBands
);
681 if (!mBands
.Length()) {
682 mBounds
= nsRectAbsolute();
684 mBounds
= CalculateBounds();
692 nsRegion
& AndWith(const nsRectAbsolute
& aRect
) {
694 class OperationStringGeneratorAndWith
: public OperationStringGenerator
{
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();
715 nsRegion mRegionCopy
;
716 nsRectAbsolute mRect
;
719 OperationStringGeneratorAndWith
opGenerator(*this, aRect
);
721 if (aRect
.IsEmpty()) {
726 if (mBands
.IsEmpty()) {
727 mBounds
= mBounds
.Intersect(aRect
);
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()) {
743 // We'll remove these later to avoid needless copying in the array.
746 while (idx
!= mBands
.Length()) {
747 if (mBands
[idx
].top
>= aRect
.YMost()) {
748 mBands
.TruncateLength(idx
);
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
);
760 if (idx
> removeStart
) {
768 mBands
.RemoveElementsAt(0, removeStart
);
771 if (mBands
.Length()) {
772 mBounds
= CalculateBounds();
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
);
788 class OperationStringGeneratorAnd
: public OperationStringGenerator
{
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();
813 OperationStringGeneratorAnd
opGenerator(*this, aRgn1
, aRgn2
);
817 if (aRgn1
.IsEmpty() || aRgn2
.IsEmpty()) {
822 if (aRgn1
.mBands
.IsEmpty() && aRgn2
.mBands
.IsEmpty()) {
823 mBounds
= aRgn1
.mBounds
.Intersect(aRgn2
.mBounds
);
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();
836 mBounds
= CalculateBounds();
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
);
851 class OperationStringGeneratorAnd
: public OperationStringGenerator
{
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()
869 gfxCriticalError() << stream
.str();
873 nsRegion
* mThisRegion
;
875 nsRectAbsolute mRect
;
878 OperationStringGeneratorAnd
opGenerator(*this, aRegion
, aRect
);
882 if (aRect
.IsEmpty()) {
887 if (aRegion
.mBands
.IsEmpty()) {
888 mBounds
= aRegion
.mBounds
.Intersect(aRect
);
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()) {
902 // We're done once we've reached the bottom.
903 if (bands
[idx
].top
>= aRect
.YMost()) {
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
;
919 mBands
.AppendElement(std::move(newBand
));
925 if (mBands
.Length()) {
926 mBounds
= CalculateBounds();
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
) {
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());
951 nsRegion
& OrWith(const nsRect
& aOther
) {
952 AddRect(nsRectAbsolute::FromRect(aOther
));
955 nsRegion
& Or(const nsRegion
& aRgn1
, const nsRegion
& aRgn2
) {
956 if (&aRgn1
!= this) {
959 for (RectIterator
idx(aRgn2
); !idx
.Done(); idx
.Next()) {
960 AddRect(idx
.GetAbsolute());
964 nsRegion
& Or(const nsRegion
& aRegion
, const nsRect
& aRect
) {
965 if (&aRegion
!= this) {
968 AddRect(nsRectAbsolute::FromRect(aRect
));
971 nsRegion
& Or(const nsRect
& aRect
, const nsRegion
& aRegion
) {
972 return Or(aRegion
, aRect
);
974 nsRegion
& Or(const nsRect
& aRect1
, const nsRect
& aRect2
) {
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.
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
{
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();
1027 nsRegion mRegionCopy
;
1028 nsRegion mOtherRegion
;
1031 OperationStringGeneratorSubWith
opGenerator(*this, aOther
);
1034 if (mBounds
.IsEmpty()) {
1038 if (aOther
.mBands
.IsEmpty()) {
1039 return SubWith(aOther
.mBounds
);
1042 if (mBands
.IsEmpty()) {
1043 mBands
.AppendElement(Band(mBounds
));
1047 size_t idxOther
= 0;
1048 while (idx
< mBands
.Length()) {
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
1057 if (idx
== mBands
.Length()) {
1058 // This region is out of bands, the other region's future bands are
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
1070 if (idxOther
== aOther
.mBands
.Length()) {
1071 // The other region's bands are all processed, all our future bands
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.
1083 if (idx
== mBands
.Length() || idxOther
== aOther
.mBands
.Length()) {
1084 // The above loop executed a break because we're done.
1088 const Band
& bandOther
= aOther
.mBands
[idxOther
];
1090 if (!mBands
[idx
].Intersects(bandOther
)) {
1091 if (mBands
[idx
].bottom
< bandOther
.bottom
) {
1095 if (idxOther
== aOther
.mBands
.Length()) {
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
;
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
);
1116 CompressBefore(idx
);
1118 // The band before us just changed, it may be identical now.
1119 CompressBefore(idx
);
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
);
1135 mBands
[idx
].top
= bandOther
.bottom
;
1138 if (idxOther
== aOther
.mBands
.Length()) {
1143 if (mBands
.IsEmpty()) {
1146 mBounds
= CalculateBounds();
1153 nsRegion
& SubOut(const nsRegion
& aOther
) { return SubWith(aOther
); }
1154 nsRegion
& SubOut(const nsRect
& aOther
) { return SubWith(aOther
); }
1157 void AppendOrExtend(const Band
& aNewBand
) {
1158 if (aNewBand
.mStrips
.IsEmpty()) {
1161 if (mBands
.IsEmpty()) {
1162 mBands
.AppendElement(aNewBand
);
1166 if (mBands
.LastElement().bottom
== aNewBand
.top
&&
1167 mBands
.LastElement().EqualStrips(aNewBand
)) {
1168 mBands
.LastElement().bottom
= aNewBand
.bottom
;
1170 mBands
.AppendElement(aNewBand
);
1173 void AppendOrExtend(const Band
&& aNewBand
) {
1174 if (aNewBand
.mStrips
.IsEmpty()) {
1177 if (mBands
.IsEmpty()) {
1178 mBands
.AppendElement(std::move(aNewBand
));
1182 if (mBands
.LastElement().bottom
== aNewBand
.top
&&
1183 mBands
.LastElement().EqualStrips(aNewBand
)) {
1184 mBands
.LastElement().bottom
= aNewBand
.bottom
;
1186 mBands
.AppendElement(std::move(aNewBand
));
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
{
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();
1221 OperationStringGeneratorSub
opGenerator(*this, aRgn1
, aRgn2
);
1226 if (aRgn1
.mBounds
.IsEmpty()) {
1231 if (aRgn2
.mBounds
.IsEmpty()) {
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
;
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.
1259 if (idxOther
== bands2
.Length()) {
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
);
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
));
1302 // Decrement other here so we are back at the last band in region 2
1303 // that intersected.
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
));
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
]);
1324 while (idx
< bands1
.Length()) {
1325 mBands
.AppendElement(bands1
[idx
]);
1329 if (mBands
.IsEmpty()) {
1332 mBounds
= CalculateBounds();
1341 // Internal helper for executing subtraction.
1342 void RunSubtraction(const nsRectAbsolute
& aRect
) {
1343 Strip
rectStrip(aRect
.X(), aRect
.XMost());
1347 while (idx
< mBands
.Length()) {
1348 if (mBands
[idx
].top
>= aRect
.YMost()) {
1352 if (mBands
[idx
].bottom
<= aRect
.Y()) {
1353 // This band is entirely before aRect, move on.
1358 if (!mBands
[idx
].Intersects(Strip(aRect
.X(), aRect
.XMost()))) {
1359 // This band does not intersect aRect horizontally. Move on.
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();
1373 mBands
[idx
].top
= aRect
.Y();
1374 // Continue to run the loop for the next band.
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
);
1384 mBands
.RemoveElementAt(idx
);
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();
1400 mBands
.InsertElementAt(idx
, std::move(newBand
));
1409 nsRegion
& SubWith(const nsRectAbsolute
& aRect
) {
1410 if (!mBounds
.Intersects(aRect
)) {
1414 if (aRect
.Contains(mBounds
)) {
1419 #ifdef DEBUG_REGIONS
1420 class OperationStringGeneratorSubWith
: public OperationStringGenerator
{
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();
1441 nsRegion mRegionCopy
;
1442 nsRectAbsolute mRect
;
1445 OperationStringGeneratorSubWith
opGenerator(*this, aRect
);
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();
1462 nsRegion
& Sub(const nsRegion
& aRegion
, const nsRectAbsolute
& aRect
) {
1463 if (aRect
.Contains(aRegion
.mBounds
)) {
1467 if (&aRegion
== this) {
1468 return SubWith(aRect
);
1470 #ifdef DEBUG_REGIONS
1471 class OperationStringGeneratorSub
: public OperationStringGenerator
{
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()
1489 gfxCriticalError() << stream
.str();
1494 nsRegion mRegionOther
;
1495 nsRectAbsolute mRect
;
1498 OperationStringGeneratorSub
opGenerator(*this, aRegion
, aRect
);
1503 if (aRegion
.mBounds
.IsEmpty()) {
1508 if (aRect
.IsEmpty()) {
1513 if (aRegion
.mBands
.IsEmpty()) {
1514 Copy(aRegion
.mBounds
);
1515 return SubWith(aRect
);
1518 const BandArray
& bands
= aRegion
.mBands
;
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
]);
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();
1540 mBands
.AppendElement(std::move(newBand
));
1543 // This tracks whether the band when we -exit- the next loop intersected the
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
]);
1552 didIntersect
= false;
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
));
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
]);
1581 mBands
.AppendElements(bands
.Elements() + idx
, bands
.Length() - idx
);
1583 if (mBands
.IsEmpty()) {
1586 mBounds
= CalculateBounds();
1593 nsRegion
& SubWith(const nsRect
& aRect
) {
1594 return SubWith(nsRectAbsolute::FromRect(aRect
));
1596 nsRegion
& Sub(const nsRect
& aRect
, const nsRegion
& aRegion
) {
1598 return SubWith(aRegion
);
1600 nsRegion
& Sub(const nsRectAbsolute
& aRect
, const nsRegion
& aRegion
) {
1602 return SubWith(aRegion
);
1604 nsRegion
& Sub(const nsRect
& aRect1
, const nsRect
& aRect2
) {
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
) {
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
) {
1634 if (iter
->top
> aY
) {
1638 if (iter
->ContainsStrip(Strip(aX
, aX
+ 1))) {
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()) {
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()) {
1667 if (iter
->top
> aRect
.Y()) {
1671 // Now inside the rectangle.
1672 if (!iter
->ContainsStrip(Strip(aRect
.X(), aRect
.XMost()))) {
1676 if (iter
->bottom
>= aRect
.YMost()) {
1680 int32_t lastY
= iter
->bottom
;
1682 while (iter
!= mBands
.end()) {
1683 // Bands do not connect.
1684 if (iter
->top
!= lastY
) {
1688 if (!iter
->ContainsStrip(Strip(aRect
.X(), aRect
.XMost()))) {
1692 if (iter
->bottom
>= aRect
.YMost()) {
1696 lastY
= iter
->bottom
;
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
{
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
1732 gfxCriticalError() << stream
.str();
1737 nsRegion mRegionCopy
;
1741 OperationStringGeneratorMoveBy
opGenerator(*this, aPt
);
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();
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);
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
);
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
)) {
1790 if (mBands
.Length() != aRegion
.mBands
.Length()) {
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
)) {
1805 uint32_t GetNumRects() const {
1806 if (mBands
.IsEmpty()) {
1807 return mBounds
.IsEmpty() ? 0 : 1;
1812 for (const Band
& band
: mBands
) {
1813 rects
+= band
.mStrips
.Length();
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
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
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
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
,
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()};
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()};
1904 nsIntRegion
ToPixels(nscoord aAppUnitsPerPixel
, bool aOutsidePixels
) const;
1906 nsRegion
& Copy(const nsRegion
& aRegion
) {
1907 mBounds
= aRegion
.mBounds
;
1908 mBands
= aRegion
.mBands
.Clone();
1912 nsRegion
& Copy(const nsRect
& aRect
) {
1914 mBounds
= nsRectAbsolute::FromRect(aRect
);
1918 nsRegion
& Copy(const nsRectAbsolute
& aRect
) {
1924 void EnsureSimplified() {
1925 if (mBands
.Length() == 1 && mBands
.begin()->mStrips
.Length() == 1) {
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
{
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();
1957 nsRegion mRegionCopy
;
1958 nsRectAbsolute mRect
;
1961 OperationStringGeneratorAddRect
opGenerator(*this, aRect
);
1963 if (aRect
.IsEmpty()) {
1967 if (mBands
.IsEmpty()) {
1968 if (mBounds
.IsEmpty()) {
1971 } else if (mBounds
.Contains(aRect
)) {
1975 mBands
.AppendElement(Band(mBounds
));
1978 mBounds
= aRect
.UnsafeUnion(mBounds
);
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.
1992 if (remaining
.top
>= remaining
.bottom
) {
1998 if (mBands
[idx
].top
>= remaining
.bottom
) {
1999 // This band lies wholly below aRect.
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
);
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
);
2018 CompressBefore(idx
);
2022 if (mBands
[idx
].ContainsStrip(strip
)) {
2023 remaining
.top
= mBands
[idx
].bottom
;
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
;
2034 mBands
[idx
].top
= remaining
.top
;
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
;
2049 CompressBefore(idx
);
2052 if (remaining
.top
< remaining
.bottom
) {
2053 // We didn't find any bands that overlapped aRect.
2055 if (mBands
[idx
- 1].bottom
== remaining
.top
&&
2056 mBands
[idx
- 1].EqualStrips(remaining
)) {
2057 mBands
[idx
- 1].bottom
= remaining
.bottom
;
2058 CompressBefore(idx
);
2064 mBands
.InsertElementAt(idx
, remaining
);
2066 CompressBefore(idx
);
2068 CompressBefore(idx
);
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()) {
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);
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
);
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);
2130 // Considering we only ever OR with nsRects, the bounds should fit in an
2132 nsRectAbsolute mBounds
;
2133 #ifdef DEBUG_REGIONS
2134 friend class OperationStringGenerator
;
2135 OperationStringGenerator
* mCurrentOpGenerator
;
2139 class RectIterator
{
2140 const nsRegion
& mRegion
;
2141 typename
BandArray::const_iterator mCurrentBand
;
2142 typename
StripArray::const_iterator mCurrentStrip
;
2145 explicit RectIterator(const nsRegion
& aRegion
)
2147 mCurrentBand(aRegion
.mBands
.begin())
2150 mCurrentStrip(nullptr)
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
);
2179 if (mRegion
.mBands
.IsEmpty()) {
2185 if (mCurrentStrip
== mCurrentBand
->mStrips
.end()) {
2187 if (mCurrentBand
!= mRegion
.mBands
.end()) {
2188 mCurrentStrip
= mCurrentBand
->mStrips
.begin();
2198 RectIterator
RectIter() const { return RectIterator(*this); }
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
;
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
)
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
);
2231 Derived
& operator=(const Derived
& aRegion
) {
2232 mImpl
= aRegion
.mImpl
;
2235 Derived
& operator=(Derived
&& aRegion
) {
2236 mImpl
= std::move(aRegion
.mImpl
);
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
);
2253 Derived
& And(const Derived
& aRegion
, const Rect
& aRect
) {
2254 mImpl
.And(aRegion
.mImpl
, ToRect(aRect
));
2257 Derived
& And(const Rect
& aRect
, const Derived
& aRegion
) {
2258 return And(aRegion
, aRect
);
2260 Derived
& And(const Rect
& aRect1
, const Rect
& aRect2
) {
2263 TmpRect
.IntersectRect(aRect1
, aRect2
);
2264 mImpl
= ToRect(TmpRect
);
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
);
2274 Derived
& Or(const Derived
& aRegion
, const Rect
& aRect
) {
2275 mImpl
.Or(aRegion
.mImpl
, ToRect(aRect
));
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
);
2292 Derived
& Xor(const Derived
& aRegion
, const Rect
& aRect
) {
2293 mImpl
.Xor(aRegion
.mImpl
, ToRect(aRect
));
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
);
2310 Derived
& Sub(const Derived
& aRegion
, const Rect
& aRect
) {
2311 mImpl
.Sub(aRegion
.mImpl
, ToRect(aRect
));
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());
2354 Derived
Intersect(const Derived
& aOther
) const {
2355 Derived intersection
;
2356 intersection
.And(This(), aOther
);
2357 return intersection
;
2360 void Inflate(const Margin
& aMargin
) {
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
);
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 {
2382 for (auto iter
= RectIterator(*this); !iter
.Done(); iter
.Next()) {
2383 nsRect appRect
= ::ToAppUnits(iter
.Get(), aAppUnitsPerPixel
);
2384 result
.Or(result
, appRect
);
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
);
2397 Derived
& ScaleInverseRoundOut(float aXScale
, float aYScale
) {
2398 mImpl
.ScaleInverseRoundOut(aXScale
, aYScale
);
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
);
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
2426 void SimplifyInward(uint32_t aMaxRects
) { mImpl
.SimplifyInward(aMaxRects
); }
2428 typedef void (*visitFn
)(void* closure
, VisitSide side
, int x1
, int y1
, int x2
,
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.
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());
2451 void Next() { mImpl
.Next(); }
2454 RectIterator
RectIter() const { return RectIterator(*this); }
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
; }
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
>>
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");
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
)
2500 IntRegionTyped(const IntRegionTyped
& aRegion
) : Super(aRegion
) {}
2501 explicit IntRegionTyped(mozilla::gfx::ArrayView
<pixman_box32_t
> 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());
2523 // This is deliberately private, so calling code uses FromUnknownRegion().
2524 explicit IntRegionTyped(const nsRegion
& aRegion
) : Super(aRegion
) {}
2528 } // namespace mozilla
2530 typedef mozilla::gfx::IntRegion nsIntRegion
;