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))) {
1645 bool Contains(const nsRectAbsolute
& aRect
) const {
1646 if (aRect
.IsEmpty()) {
1650 if (mBands
.IsEmpty()) {
1651 return mBounds
.Contains(aRect
);
1654 auto iter
= mBands
.begin();
1656 while (iter
!= mBands
.end()) {
1657 if (iter
->bottom
<= aRect
.Y()) {
1662 if (iter
->top
> aRect
.Y()) {
1666 // Now inside the rectangle.
1667 if (!iter
->ContainsStrip(Strip(aRect
.X(), aRect
.XMost()))) {
1671 if (iter
->bottom
>= aRect
.YMost()) {
1675 int32_t lastY
= iter
->bottom
;
1677 while (iter
!= mBands
.end()) {
1678 // Bands do not connect.
1679 if (iter
->top
!= lastY
) {
1683 if (!iter
->ContainsStrip(Strip(aRect
.X(), aRect
.XMost()))) {
1687 if (iter
->bottom
>= aRect
.YMost()) {
1691 lastY
= iter
->bottom
;
1697 bool Contains(const nsRect
& aRect
) const {
1698 return Contains(nsRectAbsolute::FromRect(aRect
));
1701 bool Contains(const nsRegion
& aRgn
) const;
1702 bool Intersects(const nsRectAbsolute
& aRect
) const;
1703 bool Intersects(const nsRect
& aRect
) const {
1704 return Intersects(nsRectAbsolute::FromRect(aRect
));
1707 void MoveBy(int32_t aXOffset
, int32_t aYOffset
) {
1708 MoveBy(nsPoint(aXOffset
, aYOffset
));
1710 void MoveBy(nsPoint aPt
) {
1711 #ifdef DEBUG_REGIONS
1712 class OperationStringGeneratorMoveBy
: public OperationStringGenerator
{
1714 OperationStringGeneratorMoveBy(nsRegion
& aRegion
, const nsPoint
& aPoint
)
1715 : mRegion(&aRegion
), mRegionCopy(aRegion
), mPoint(aPoint
) {
1716 aRegion
.mCurrentOpGenerator
= this;
1718 virtual ~OperationStringGeneratorMoveBy() {
1719 mRegion
->mCurrentOpGenerator
= nullptr;
1722 virtual void OutputOp() override
{
1723 std::stringstream stream
;
1724 mRegionCopy
.OutputToStream("r", stream
);
1725 stream
<< "r.MoveBy(nsPoint(" << mPoint
.x
<< ", " << mPoint
.y
1727 gfxCriticalError() << stream
.str();
1732 nsRegion mRegionCopy
;
1736 OperationStringGeneratorMoveBy
opGenerator(*this, aPt
);
1739 mBounds
.MoveBy(aPt
);
1740 for (Band
& band
: mBands
) {
1741 band
.top
+= aPt
.Y();
1742 band
.bottom
+= aPt
.Y();
1743 for (Strip
& strip
: band
.mStrips
) {
1744 strip
.left
+= aPt
.X();
1745 strip
.right
+= aPt
.X();
1755 nsRegion
MovedBy(int32_t aXOffset
, int32_t aYOffset
) const {
1756 return MovedBy(nsPoint(aXOffset
, aYOffset
));
1758 nsRegion
MovedBy(const nsPoint
& aPt
) const {
1759 nsRegion
copy(*this);
1764 nsRegion
Intersect(const nsRegion
& aOther
) const {
1765 nsRegion intersection
;
1766 intersection
.And(*this, aOther
);
1767 return intersection
;
1770 void Inflate(const nsMargin
& aMargin
);
1772 nsRegion
Inflated(const nsMargin
& aMargin
) const {
1773 nsRegion
copy(*this);
1774 copy
.Inflate(aMargin
);
1778 bool IsEmpty() const { return mBounds
.IsEmpty(); }
1779 bool IsComplex() const { return GetNumRects() > 1; }
1780 bool IsEqual(const nsRegion
& aRegion
) const {
1781 if (!mBounds
.IsEqualInterior(aRegion
.mBounds
)) {
1785 if (mBands
.Length() != aRegion
.mBands
.Length()) {
1789 for (auto iter1
= mBands
.begin(), iter2
= aRegion
.mBands
.begin();
1790 iter1
!= mBands
.end(); iter1
++, iter2
++) {
1791 if (iter1
->top
!= iter2
->top
|| iter1
->bottom
!= iter2
->bottom
||
1792 !iter1
->EqualStrips(*iter2
)) {
1800 uint32_t GetNumRects() const {
1801 if (mBands
.IsEmpty()) {
1802 return mBounds
.IsEmpty() ? 0 : 1;
1807 for (const Band
& band
: mBands
) {
1808 rects
+= band
.mStrips
.Length();
1813 const nsRect
GetBounds() const { return mBounds
.ToNSRect(); }
1814 const nsRectAbsolute
GetAbsoluteBounds() const { return mBounds
; }
1815 uint64_t Area() const;
1818 * Return this region scaled to a different appunits per pixel (APP) ratio.
1819 * This applies nsRect::ScaleToOtherAppUnitsRoundOut/In to each rect of the
1821 * @param aFromAPP the APP to scale from
1822 * @param aToAPP the APP to scale to
1823 * @note this can turn an empty region into a non-empty region
1825 [[nodiscard
]] nsRegion
ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP
,
1826 int32_t aToAPP
) const;
1827 [[nodiscard
]] nsRegion
ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP
,
1828 int32_t aToAPP
) const;
1829 nsRegion
& ScaleRoundOut(float aXScale
, float aYScale
);
1830 nsRegion
& ScaleInverseRoundOut(float aXScale
, float aYScale
);
1831 nsRegion
& Transform(const mozilla::gfx::Matrix4x4
& aTransform
);
1832 nsIntRegion
ScaleToOutsidePixels(float aXScale
, float aYScale
,
1833 nscoord aAppUnitsPerPixel
) const;
1834 nsIntRegion
ScaleToInsidePixels(float aXScale
, float aYScale
,
1835 nscoord aAppUnitsPerPixel
) const;
1836 nsIntRegion
ScaleToNearestPixels(float aXScale
, float aYScale
,
1837 nscoord aAppUnitsPerPixel
) const;
1838 nsIntRegion
ToOutsidePixels(nscoord aAppUnitsPerPixel
) const;
1839 nsIntRegion
ToNearestPixels(nscoord aAppUnitsPerPixel
) const;
1842 * Gets the largest rectangle contained in the region.
1843 * @param aContainingRect if non-empty, we choose a rectangle that
1844 * maximizes the area intersecting with aContainingRect (and break ties by
1845 * then choosing the largest rectangle overall)
1847 nsRect
GetLargestRectangle(const nsRect
& aContainingRect
= nsRect()) const;
1850 * Make sure the region has at most aMaxRects by adding area to it
1851 * if necessary. The simplified region will be a superset of the
1852 * original region. The simplified region's bounding box will be
1853 * the same as for the current region.
1855 void SimplifyOutward(uint32_t aMaxRects
);
1857 * Simplify the region by adding at most aThreshold area between spans of
1858 * rects. The simplified region will be a superset of the original region.
1859 * The simplified region's bounding box will be the same as for the current
1862 void SimplifyOutwardByArea(uint32_t aThreshold
);
1864 * Make sure the region has at most aMaxRects by removing area from
1865 * it if necessary. The simplified region will be a subset of the
1868 void SimplifyInward(uint32_t aMaxRects
);
1871 * VisitEdges is a weird kind of function that we use for padding
1872 * out surfaces to prevent texture filtering artifacts.
1873 * It calls the visitFn callback for each of the exterior edges of
1874 * the regions. The top and bottom edges will be expanded 1 pixel
1875 * to the left and right if there's an outside corner. The order
1876 * the edges are visited is not guaranteed.
1878 * visitFn has a side parameter that can be TOP,BOTTOM,LEFT,RIGHT
1879 * and specifies which kind of edge is being visited. x1, y1, x2, y2
1880 * are the coordinates of the line. (x1 == x2) || (y1 == y2)
1882 typedef void (*visitFn
)(void* closure
, VisitSide side
, int x1
, int y1
, int x2
,
1884 void VisitEdges(visitFn
, void* closure
) const;
1886 nsCString
ToString() const;
1888 static inline pixman_box32_t
RectToBox(const nsRect
& aRect
) {
1889 pixman_box32_t box
= {aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost()};
1893 static inline pixman_box32_t
RectToBox(const mozilla::gfx::IntRect
& aRect
) {
1894 pixman_box32_t box
= {aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost()};
1899 nsIntRegion
ToPixels(nscoord aAppUnitsPerPixel
, bool aOutsidePixels
) const;
1901 nsRegion
& Copy(const nsRegion
& aRegion
) {
1902 mBounds
= aRegion
.mBounds
;
1903 mBands
= aRegion
.mBands
.Clone();
1907 nsRegion
& Copy(const nsRect
& aRect
) {
1909 mBounds
= nsRectAbsolute::FromRect(aRect
);
1913 nsRegion
& Copy(const nsRectAbsolute
& aRect
) {
1919 void EnsureSimplified() {
1920 if (mBands
.Length() == 1 && mBands
.begin()->mStrips
.Length() == 1) {
1925 static inline nsRectAbsolute
BoxToRect(const pixman_box32_t
& aBox
) {
1926 return nsRectAbsolute(aBox
.x1
, aBox
.y1
, aBox
.x2
, aBox
.y2
);
1929 void AddRect(const nsRectAbsolute
& aRect
) {
1930 #ifdef DEBUG_REGIONS
1931 class OperationStringGeneratorAddRect
: public OperationStringGenerator
{
1933 OperationStringGeneratorAddRect(nsRegion
& aRegion
,
1934 const nsRectAbsolute
& aRect
)
1935 : mRegion(&aRegion
), mRegionCopy(aRegion
), mRect(aRect
) {
1936 aRegion
.mCurrentOpGenerator
= this;
1938 virtual ~OperationStringGeneratorAddRect() {
1939 mRegion
->mCurrentOpGenerator
= nullptr;
1942 virtual void OutputOp() override
{
1943 std::stringstream stream
;
1944 mRegionCopy
.OutputToStream("r", stream
);
1945 stream
<< "r.OrWith(nsRect(" << mRect
.X() << ", " << mRect
.Y() << ", "
1946 << mRect
.Width() << ", " << mRect
.Height() << "));\n";
1947 gfxCriticalError() << stream
.str();
1952 nsRegion mRegionCopy
;
1953 nsRectAbsolute mRect
;
1956 OperationStringGeneratorAddRect
opGenerator(*this, aRect
);
1958 if (aRect
.IsEmpty()) {
1962 if (mBands
.IsEmpty()) {
1963 if (mBounds
.IsEmpty()) {
1966 } else if (mBounds
.Contains(aRect
)) {
1970 mBands
.AppendElement(Band(mBounds
));
1973 mBounds
= aRect
.UnsafeUnion(mBounds
);
1977 Strip
strip(aRect
.X(), aRect
.XMost());
1978 Band
remaining(aRect
);
1980 while (idx
!= mBands
.Length()) {
1981 if (mBands
[idx
].bottom
<= remaining
.top
) {
1982 // This band lies wholly above aRect.
1987 if (remaining
.top
>= remaining
.bottom
) {
1993 if (mBands
[idx
].top
>= remaining
.bottom
) {
1994 // This band lies wholly below aRect.
1998 if (mBands
[idx
].EqualStrips(remaining
)) {
1999 mBands
[idx
].top
= std::min(mBands
[idx
].top
, remaining
.top
);
2000 // Nothing to do for this band. Just expand.
2001 remaining
.top
= mBands
[idx
].bottom
;
2002 CompressBefore(idx
);
2007 if (mBands
[idx
].top
> remaining
.top
) {
2008 auto before
= mBands
.InsertElementAt(idx
, remaining
);
2009 before
->bottom
= mBands
[idx
+ 1].top
;
2010 remaining
.top
= before
->bottom
;
2011 CompressBefore(idx
);
2013 CompressBefore(idx
);
2017 if (mBands
[idx
].ContainsStrip(strip
)) {
2018 remaining
.top
= mBands
[idx
].bottom
;
2023 // mBands[idx].top <= remaining.top.
2025 if (mBands
[idx
].top
< remaining
.top
) {
2026 auto before
= mBands
.InsertElementAt(idx
, Band(mBands
[idx
]));
2027 before
->bottom
= remaining
.top
;
2029 mBands
[idx
].top
= remaining
.top
;
2033 // mBands[idx].top == remaining.top
2034 if (mBands
[idx
].bottom
> remaining
.bottom
) {
2035 auto below
= mBands
.InsertElementAt(idx
+ 1, Band(mBands
[idx
]));
2036 below
->top
= remaining
.bottom
;
2037 mBands
[idx
].bottom
= remaining
.bottom
;
2040 mBands
[idx
].InsertStrip(strip
);
2041 CompressBefore(idx
);
2042 remaining
.top
= mBands
[idx
].bottom
;
2044 CompressBefore(idx
);
2047 if (remaining
.top
< remaining
.bottom
) {
2048 // We didn't find any bands that overlapped aRect.
2050 if (mBands
[idx
- 1].bottom
== remaining
.top
&&
2051 mBands
[idx
- 1].EqualStrips(remaining
)) {
2052 mBands
[idx
- 1].bottom
= remaining
.bottom
;
2053 CompressBefore(idx
);
2059 mBands
.InsertElementAt(idx
, remaining
);
2061 CompressBefore(idx
);
2063 CompressBefore(idx
);
2070 // Most callers could probably do this on the fly, if this ever shows up
2071 // in profiles we could optimize this.
2072 nsRectAbsolute
CalculateBounds() const {
2073 if (mBands
.IsEmpty()) {
2077 int32_t top
= mBands
.begin()->top
;
2078 int32_t bottom
= mBands
.LastElement().bottom
;
2080 int32_t leftMost
= mBands
.begin()->mStrips
.begin()->left
;
2081 int32_t rightMost
= mBands
.begin()->mStrips
.LastElement().right
;
2082 for (const Band
& band
: mBands
) {
2083 leftMost
= std::min(leftMost
, band
.mStrips
.begin()->left
);
2084 rightMost
= std::max(rightMost
, band
.mStrips
.LastElement().right
);
2087 return nsRectAbsolute(leftMost
, top
, rightMost
, bottom
);
2090 static uint32_t ComputeMergedAreaIncrease(const Band
& aTopBand
,
2091 const Band
& aBottomBand
);
2093 // Returns true if idx is now referring to the 'next' band
2094 bool CompressAdjacentBands(size_t& aIdx
) {
2095 if ((aIdx
+ 1) < mBands
.Length()) {
2096 if (mBands
[aIdx
+ 1].top
== mBands
[aIdx
].bottom
&&
2097 mBands
[aIdx
+ 1].EqualStrips(mBands
[aIdx
])) {
2098 mBands
[aIdx
].bottom
= mBands
[aIdx
+ 1].bottom
;
2099 mBands
.RemoveElementAt(aIdx
+ 1);
2103 if (mBands
[aIdx
- 1].bottom
== mBands
[aIdx
].top
&&
2104 mBands
[aIdx
].EqualStrips(mBands
[aIdx
- 1])) {
2105 mBands
[aIdx
- 1].bottom
= mBands
[aIdx
].bottom
;
2106 mBands
.RemoveElementAt(aIdx
);
2113 void CompressBefore(size_t& aIdx
) {
2114 if (aIdx
&& aIdx
< mBands
.Length()) {
2115 if (mBands
[aIdx
- 1].bottom
== mBands
[aIdx
].top
&&
2116 mBands
[aIdx
- 1].EqualStrips(mBands
[aIdx
])) {
2117 mBands
[aIdx
].top
= mBands
[aIdx
- 1].top
;
2118 mBands
.RemoveElementAt(aIdx
- 1);
2125 // Considering we only ever OR with nsRects, the bounds should fit in an
2127 nsRectAbsolute mBounds
;
2128 #ifdef DEBUG_REGIONS
2129 friend class OperationStringGenerator
;
2130 OperationStringGenerator
* mCurrentOpGenerator
;
2134 class RectIterator
{
2135 const nsRegion
& mRegion
;
2136 typename
BandArray::const_iterator mCurrentBand
;
2137 typename
StripArray::const_iterator mCurrentStrip
;
2140 explicit RectIterator(const nsRegion
& aRegion
)
2142 mCurrentBand(aRegion
.mBands
.begin())
2145 mCurrentStrip(nullptr)
2148 mIsDone
= mRegion
.mBounds
.IsEmpty();
2149 if (mCurrentBand
!= aRegion
.mBands
.end()) {
2150 mCurrentStrip
= mCurrentBand
->mStrips
.begin();
2154 bool Done() const { return mIsDone
; }
2156 const nsRect
Get() const {
2157 if (mRegion
.mBands
.IsEmpty()) {
2158 return mRegion
.GetBounds();
2160 return nsRect(mCurrentStrip
->left
, mCurrentBand
->top
,
2161 mCurrentStrip
->right
- mCurrentStrip
->left
,
2162 mCurrentBand
->bottom
- mCurrentBand
->top
);
2165 const nsRectAbsolute
GetAbsolute() const {
2166 if (mRegion
.mBands
.IsEmpty()) {
2167 return mRegion
.mBounds
;
2169 return nsRectAbsolute(mCurrentStrip
->left
, mCurrentBand
->top
,
2170 mCurrentStrip
->right
, mCurrentBand
->bottom
);
2174 if (mRegion
.mBands
.IsEmpty()) {
2180 if (mCurrentStrip
== mCurrentBand
->mStrips
.end()) {
2182 if (mCurrentBand
!= mRegion
.mBands
.end()) {
2183 mCurrentStrip
= mCurrentBand
->mStrips
.begin();
2193 RectIterator
RectIter() const { return RectIterator(*this); }
2200 * BaseIntRegions use int32_t coordinates.
2202 template <typename Derived
, typename Rect
, typename Point
, typename Margin
>
2203 class BaseIntRegion
{
2204 friend class ::nsRegion
;
2206 // Give access to all specializations of IntRegionTyped, not just ones that
2207 // derive from this specialization of BaseIntRegion.
2208 template <typename units
>
2209 friend class IntRegionTyped
;
2212 typedef Rect RectType
;
2213 typedef Point PointType
;
2214 typedef Margin MarginType
;
2216 BaseIntRegion() = default;
2217 MOZ_IMPLICIT
BaseIntRegion(const Rect
& aRect
) : mImpl(ToRect(aRect
)) {}
2218 explicit BaseIntRegion(mozilla::gfx::ArrayView
<pixman_box32_t
> aRects
)
2220 BaseIntRegion(const BaseIntRegion
& aRegion
) : mImpl(aRegion
.mImpl
) {}
2221 BaseIntRegion(BaseIntRegion
&& aRegion
) : mImpl(std::move(aRegion
.mImpl
)) {}
2222 Derived
& operator=(const Rect
& aRect
) {
2223 mImpl
= ToRect(aRect
);
2226 Derived
& operator=(const Derived
& aRegion
) {
2227 mImpl
= aRegion
.mImpl
;
2230 Derived
& operator=(Derived
&& aRegion
) {
2231 mImpl
= std::move(aRegion
.mImpl
);
2235 bool operator==(const Derived
& aRgn
) const { return IsEqual(aRgn
); }
2236 bool operator!=(const Derived
& aRgn
) const { return !(*this == aRgn
); }
2238 friend std::ostream
& operator<<(std::ostream
& stream
, const Derived
& m
) {
2239 return stream
<< m
.mImpl
;
2242 void AndWith(const Derived
& aOther
) { And(This(), aOther
); }
2243 void AndWith(const Rect
& aOther
) { And(This(), aOther
); }
2244 Derived
& And(const Derived
& aRgn1
, const Derived
& aRgn2
) {
2245 mImpl
.And(aRgn1
.mImpl
, aRgn2
.mImpl
);
2248 Derived
& And(const Derived
& aRegion
, const Rect
& aRect
) {
2249 mImpl
.And(aRegion
.mImpl
, ToRect(aRect
));
2252 Derived
& And(const Rect
& aRect
, const Derived
& aRegion
) {
2253 return And(aRegion
, aRect
);
2255 Derived
& And(const Rect
& aRect1
, const Rect
& aRect2
) {
2258 TmpRect
.IntersectRect(aRect1
, aRect2
);
2259 mImpl
= ToRect(TmpRect
);
2263 Derived
& OrWith(const Derived
& aOther
) { return Or(This(), aOther
); }
2264 Derived
& OrWith(const Rect
& aOther
) { return Or(This(), aOther
); }
2265 Derived
& Or(const Derived
& aRgn1
, const Derived
& aRgn2
) {
2266 mImpl
.Or(aRgn1
.mImpl
, aRgn2
.mImpl
);
2269 Derived
& Or(const Derived
& aRegion
, const Rect
& aRect
) {
2270 mImpl
.Or(aRegion
.mImpl
, ToRect(aRect
));
2273 Derived
& Or(const Rect
& aRect
, const Derived
& aRegion
) {
2274 return Or(aRegion
, aRect
);
2276 Derived
& Or(const Rect
& aRect1
, const Rect
& aRect2
) {
2277 mImpl
= ToRect(aRect1
);
2278 return Or(This(), aRect2
);
2281 Derived
& XorWith(const Derived
& aOther
) { return Xor(This(), aOther
); }
2282 Derived
& XorWith(const Rect
& aOther
) { return Xor(This(), aOther
); }
2283 Derived
& Xor(const Derived
& aRgn1
, const Derived
& aRgn2
) {
2284 mImpl
.Xor(aRgn1
.mImpl
, aRgn2
.mImpl
);
2287 Derived
& Xor(const Derived
& aRegion
, const Rect
& aRect
) {
2288 mImpl
.Xor(aRegion
.mImpl
, ToRect(aRect
));
2291 Derived
& Xor(const Rect
& aRect
, const Derived
& aRegion
) {
2292 return Xor(aRegion
, aRect
);
2294 Derived
& Xor(const Rect
& aRect1
, const Rect
& aRect2
) {
2295 mImpl
= ToRect(aRect1
);
2296 return Xor(This(), aRect2
);
2299 Derived
& SubOut(const Derived
& aOther
) { return Sub(This(), aOther
); }
2300 Derived
& SubOut(const Rect
& aOther
) { return Sub(This(), aOther
); }
2301 Derived
& Sub(const Derived
& aRgn1
, const Derived
& aRgn2
) {
2302 mImpl
.Sub(aRgn1
.mImpl
, aRgn2
.mImpl
);
2305 Derived
& Sub(const Derived
& aRegion
, const Rect
& aRect
) {
2306 mImpl
.Sub(aRegion
.mImpl
, ToRect(aRect
));
2309 Derived
& Sub(const Rect
& aRect
, const Derived
& aRegion
) {
2310 return Sub(Derived(aRect
), aRegion
);
2312 Derived
& Sub(const Rect
& aRect1
, const Rect
& aRect2
) {
2313 mImpl
= ToRect(aRect1
);
2314 return Sub(This(), aRect2
);
2318 * Returns true iff the given point is inside the region. A region
2319 * created from a rect (x=0, y=0, w=100, h=100) will NOT contain
2320 * the point x=100, y=100.
2322 bool Contains(int aX
, int aY
) const { return mImpl
.Contains(aX
, aY
); }
2323 bool Contains(const Rect
& aRect
) const {
2324 return mImpl
.Contains(ToRect(aRect
));
2326 bool Contains(const Derived
& aRgn
) const {
2327 return mImpl
.Contains(aRgn
.mImpl
);
2329 bool Intersects(const Rect
& aRect
) const {
2330 return mImpl
.Intersects(ToRect(aRect
));
2333 void MoveBy(int32_t aXOffset
, int32_t aYOffset
) {
2334 MoveBy(Point(aXOffset
, aYOffset
));
2336 void MoveBy(Point aPt
) { mImpl
.MoveBy(aPt
.X(), aPt
.Y()); }
2337 Derived
MovedBy(int32_t aXOffset
, int32_t aYOffset
) const {
2338 return MovedBy(Point(aXOffset
, aYOffset
));
2340 Derived
MovedBy(const Point
& aPt
) const {
2341 Derived
copy(This());
2346 Derived
Intersect(const Derived
& aOther
) const {
2347 Derived intersection
;
2348 intersection
.And(This(), aOther
);
2349 return intersection
;
2352 void Inflate(const Margin
& aMargin
) {
2354 nsMargin(aMargin
.top
, aMargin
.right
, aMargin
.bottom
, aMargin
.left
));
2356 Derived
Inflated(const Margin
& aMargin
) const {
2357 Derived
copy(This());
2358 copy
.Inflate(aMargin
);
2362 void SetEmpty() { mImpl
.SetEmpty(); }
2364 bool IsEmpty() const { return mImpl
.IsEmpty(); }
2365 bool IsComplex() const { return mImpl
.IsComplex(); }
2366 bool IsEqual(const Derived
& aRegion
) const {
2367 return mImpl
.IsEqual(aRegion
.mImpl
);
2369 uint32_t GetNumRects() const { return mImpl
.GetNumRects(); }
2370 Rect
GetBounds() const { return FromRect(mImpl
.GetBounds()); }
2371 uint64_t Area() const { return mImpl
.Area(); }
2372 nsRegion
ToAppUnits(nscoord aAppUnitsPerPixel
) const {
2374 for (auto iter
= RectIterator(*this); !iter
.Done(); iter
.Next()) {
2375 nsRect appRect
= ::ToAppUnits(iter
.Get(), aAppUnitsPerPixel
);
2376 result
.Or(result
, appRect
);
2380 Rect
GetLargestRectangle(const Rect
& aContainingRect
= Rect()) const {
2381 return FromRect(mImpl
.GetLargestRectangle(ToRect(aContainingRect
)));
2384 Derived
& ScaleRoundOut(float aXScale
, float aYScale
) {
2385 mImpl
.ScaleRoundOut(aXScale
, aYScale
);
2389 Derived
& ScaleInverseRoundOut(float aXScale
, float aYScale
) {
2390 mImpl
.ScaleInverseRoundOut(aXScale
, aYScale
);
2394 // Prefer using TransformBy(matrix, region) from UnitTransforms.h,
2395 // as applying the transform should typically change the unit system.
2396 // TODO(botond): Move this to IntRegionTyped and disable it for
2397 // unit != UnknownUnits.
2398 Derived
& Transform(const mozilla::gfx::Matrix4x4
& aTransform
) {
2399 mImpl
.Transform(aTransform
);
2404 * Make sure the region has at most aMaxRects by adding area to it
2405 * if necessary. The simplified region will be a superset of the
2406 * original region. The simplified region's bounding box will be
2407 * the same as for the current region.
2409 void SimplifyOutward(uint32_t aMaxRects
) { mImpl
.SimplifyOutward(aMaxRects
); }
2410 void SimplifyOutwardByArea(uint32_t aThreshold
) {
2411 mImpl
.SimplifyOutwardByArea(aThreshold
);
2414 * Make sure the region has at most aMaxRects by removing area from
2415 * it if necessary. The simplified region will be a subset of the
2418 void SimplifyInward(uint32_t aMaxRects
) { mImpl
.SimplifyInward(aMaxRects
); }
2420 typedef void (*visitFn
)(void* closure
, VisitSide side
, int x1
, int y1
, int x2
,
2422 void VisitEdges(visitFn visit
, void* closure
) const {
2423 mImpl
.VisitEdges(visit
, closure
);
2426 nsCString
ToString() const { return mImpl
.ToString(); }
2428 class RectIterator
{
2429 nsRegion::RectIterator mImpl
; // The underlying iterator.
2430 mutable Rect mTmp
; // The most recently gotten rectangle.
2433 explicit RectIterator(const BaseIntRegion
& aRegion
)
2434 : mImpl(aRegion
.mImpl
) {}
2436 bool Done() const { return mImpl
.Done(); }
2438 const Rect
& Get() const {
2439 mTmp
= FromRect(mImpl
.Get());
2443 void Next() { mImpl
.Next(); }
2446 RectIterator
RectIter() const { return RectIterator(*this); }
2449 // Expose enough to derived classes from them to define conversions
2450 // between different types of BaseIntRegions.
2451 explicit BaseIntRegion(const nsRegion
& aImpl
) : mImpl(aImpl
) {}
2452 const nsRegion
& Impl() const { return mImpl
; }
2457 static nsRect
ToRect(const Rect
& aRect
) {
2458 return nsRect(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height());
2460 static Rect
FromRect(const nsRect
& aRect
) {
2461 return Rect(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height());
2464 Derived
& This() { return *static_cast<Derived
*>(this); }
2465 const Derived
& This() const { return *static_cast<const Derived
*>(this); }
2468 template <class units
>
2469 class IntRegionTyped
2470 : public BaseIntRegion
<IntRegionTyped
<units
>, IntRectTyped
<units
>,
2471 IntPointTyped
<units
>, IntMarginTyped
<units
>> {
2472 typedef BaseIntRegion
<IntRegionTyped
<units
>, IntRectTyped
<units
>,
2473 IntPointTyped
<units
>, IntMarginTyped
<units
>>
2476 // Make other specializations of IntRegionTyped friends.
2477 template <typename OtherUnits
>
2478 friend class IntRegionTyped
;
2480 static_assert(IsPixel
<units
>::value
,
2481 "'units' must be a coordinate system tag");
2484 typedef IntRectTyped
<units
> RectType
;
2485 typedef IntPointTyped
<units
> PointType
;
2486 typedef IntMarginTyped
<units
> MarginType
;
2488 // Forward constructors.
2489 IntRegionTyped() = default;
2490 MOZ_IMPLICIT
IntRegionTyped(const IntRectTyped
<units
>& aRect
)
2492 IntRegionTyped(const IntRegionTyped
& aRegion
) : Super(aRegion
) {}
2493 explicit IntRegionTyped(mozilla::gfx::ArrayView
<pixman_box32_t
> aRects
)
2495 IntRegionTyped(IntRegionTyped
&& aRegion
) : Super(std::move(aRegion
)) {}
2497 // Assignment operators need to be forwarded as well, otherwise the compiler
2498 // will declare deleted ones.
2499 IntRegionTyped
& operator=(const IntRegionTyped
& aRegion
) {
2500 return Super::operator=(aRegion
);
2502 IntRegionTyped
& operator=(IntRegionTyped
&& aRegion
) {
2503 return Super::operator=(std::move(aRegion
));
2506 static IntRegionTyped
FromUnknownRegion(const IntRegion
& aRegion
) {
2507 return IntRegionTyped(aRegion
.Impl());
2509 IntRegion
ToUnknownRegion() const {
2510 // Need |this->| because Impl() is defined in a dependent base class.
2511 return IntRegion(this->Impl());
2515 // This is deliberately private, so calling code uses FromUnknownRegion().
2516 explicit IntRegionTyped(const nsRegion
& aRegion
) : Super(aRegion
) {}
2520 } // namespace mozilla
2522 typedef mozilla::gfx::IntRegion nsIntRegion
;