Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef MOZILLA_GFX_TILEDREGION_H_
8 : #define MOZILLA_GFX_TILEDREGION_H_
9 :
10 : #include "mozilla/ArrayView.h"
11 : #include "mozilla/gfx/Rect.h"
12 : #include "mozilla/Move.h"
13 : #include "nsRegion.h"
14 : #include "pixman.h"
15 :
16 : namespace mozilla {
17 : namespace gfx {
18 :
19 : // See TiledRegion.cpp for documentation on TiledRegionImpl.
20 450 : class TiledRegionImpl {
21 : public:
22 697 : void Clear() { mRects.Clear(); }
23 : bool AddRect(const pixman_box32_t& aRect);
24 : bool Intersects(const pixman_box32_t& aRect) const;
25 : bool Contains(const pixman_box32_t& aRect) const;
26 196 : operator ArrayView<pixman_box32_t>() const { return ArrayView<pixman_box32_t>(mRects); }
27 :
28 : private:
29 : nsTArray<pixman_box32_t> mRects;
30 : };
31 :
32 : /**
33 : * A auto-simplifying region type that supports one rectangle per tile.
34 : * The virtual tile grid is anchored at (0, 0) and has quadratic tiles whose
35 : * size is hard-coded as kTileSize in TiledRegion.cpp.
36 : * A TiledRegion starts out empty. You can add rectangles or (regular) regions
37 : * into it by calling Add(). Add() is a mutating union operation (similar to
38 : * OrWith on nsRegion) that's *not* exact, because it will enlarge the region as
39 : * necessary to satisfy the "one rectangle per tile" requirement.
40 : * Tiled regions convert implicitly to the underlying regular region type.
41 : * The only way to remove parts from a TiledRegion is by calling SetEmpty().
42 : */
43 : template<typename RegionT>
44 211 : class TiledRegion {
45 : public:
46 : typedef typename RegionT::RectType RectT;
47 :
48 239 : TiledRegion()
49 239 : : mCoversBounds(false)
50 239 : {}
51 :
52 : TiledRegion(const TiledRegion& aOther)
53 : : mBounds(aOther.mBounds)
54 : , mImpl(aOther.mImpl)
55 : , mCoversBounds(false)
56 : {}
57 :
58 : TiledRegion(TiledRegion&& aOther)
59 : : mBounds(aOther.mBounds)
60 : , mImpl(Move(aOther.mImpl))
61 : , mCoversBounds(false)
62 : {}
63 :
64 730 : RegionT GetRegion() const
65 : {
66 730 : if (mBounds.IsEmpty()) {
67 534 : return RegionT();
68 : }
69 196 : if (mCoversBounds) {
70 : // Rect limit hit or allocation failed, treat as 1 rect.
71 0 : return RegionT(mBounds);
72 : }
73 196 : return RegionT(mImpl);
74 : }
75 :
76 : TiledRegion& operator=(const TiledRegion& aOther)
77 : {
78 : if (&aOther != this) {
79 : mBounds = aOther.mBounds;
80 : mImpl = aOther.mImpl;
81 : mCoversBounds = aOther.mCoversBounds;
82 : }
83 : return *this;
84 : }
85 :
86 3 : void Add(const RectT& aRect)
87 : {
88 3 : if (aRect.IsEmpty()) {
89 6 : return;
90 : }
91 :
92 0 : Maybe<RectT> newBounds = mBounds.SafeUnion(aRect);
93 0 : if (!newBounds) {
94 0 : return;
95 : }
96 0 : mBounds = newBounds.value();
97 0 : MOZ_ASSERT(!mBounds.Overflows());
98 :
99 0 : if (mCoversBounds) {
100 0 : return;
101 : }
102 :
103 0 : if (!mImpl.AddRect(RectToBox(aRect))) {
104 0 : FallBackToBounds();
105 : }
106 : }
107 :
108 364 : void Add(const RegionT& aRegion)
109 : {
110 728 : Maybe<RectT> newBounds = mBounds.SafeUnion(aRegion.GetBounds());
111 364 : if (!newBounds) {
112 0 : return;
113 : }
114 364 : mBounds = newBounds.value();
115 364 : MOZ_ASSERT(!mBounds.Overflows());
116 :
117 364 : if (mCoversBounds) {
118 0 : return;
119 : }
120 :
121 670 : for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
122 306 : RectT r = iter.Get();
123 306 : if (r.IsEmpty() || r.Overflows()) {
124 : // This can happen if e.g. a negative-width rect was wrapped into a
125 : // region. Treat it the same as we would if such a rect was passed to
126 : // the Add(const RectT&) function.
127 0 : continue;
128 : }
129 306 : if (!mImpl.AddRect(RectToBox(r))) {
130 0 : FallBackToBounds();
131 0 : return;
132 : }
133 : }
134 : }
135 :
136 164 : bool IsEmpty() const { return mBounds.IsEmpty(); }
137 :
138 697 : void SetEmpty()
139 : {
140 697 : mBounds.SetEmpty();
141 697 : mImpl.Clear();
142 697 : mCoversBounds = false;
143 697 : }
144 :
145 : RectT GetBounds() const { return mBounds; }
146 : bool CoversBounds() const { return mCoversBounds; }
147 :
148 : bool Intersects(const RectT& aRect) const
149 : {
150 : if (aRect.IsEmpty()) {
151 : return true;
152 : }
153 : if (aRect.Overflows() || !mBounds.Intersects(aRect)) {
154 : return false;
155 : }
156 : if (mCoversBounds) {
157 : return true;
158 : }
159 :
160 : return mImpl.Intersects(RectToBox(aRect));
161 : }
162 :
163 : bool Contains(const RectT& aRect) const
164 : {
165 : if (aRect.IsEmpty()) {
166 : return true;
167 : }
168 : if (aRect.Overflows() || !mBounds.Contains(aRect)) {
169 : return false;
170 : }
171 : if (mCoversBounds) {
172 : return true;
173 : }
174 : return mImpl.Contains(RectToBox(aRect));
175 : }
176 :
177 : private:
178 :
179 0 : void FallBackToBounds()
180 : {
181 0 : mCoversBounds = true;
182 0 : mImpl.Clear();
183 0 : }
184 :
185 306 : static pixman_box32_t RectToBox(const RectT& aRect)
186 : {
187 306 : MOZ_ASSERT(!aRect.IsEmpty());
188 306 : MOZ_ASSERT(!aRect.Overflows());
189 306 : return { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
190 : }
191 :
192 : RectT mBounds;
193 : TiledRegionImpl mImpl;
194 :
195 : // mCoversBounds is true if we bailed out due to a large number of tiles.
196 : // mCoversBounds being true means that this TiledRegion is just a simple
197 : // rectangle (our mBounds).
198 : // Once set to true, the TiledRegion will stay in this state until SetEmpty
199 : // is called.
200 : bool mCoversBounds;
201 : };
202 :
203 : typedef TiledRegion<IntRegion> TiledIntRegion;
204 :
205 : } // namespace gfx
206 : } // namespace mozilla
207 :
208 : #endif /* MOZILLA_GFX_TILEDREGION_H_ */
|