1 | /* |
2 | * Copyright 2010 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #ifndef SkRasterClip_DEFINED |
9 | #define SkRasterClip_DEFINED |
10 | |
11 | #include "include/core/SkRegion.h" |
12 | #include "include/core/SkShader.h" |
13 | #include "include/private/SkMacros.h" |
14 | #include "src/core/SkAAClip.h" |
15 | |
16 | class SkRRect; |
17 | |
18 | class SkConservativeClip { |
19 | SkIRect fBounds; |
20 | const SkIRect* fClipRestrictionRect; |
21 | |
22 | inline void applyClipRestriction(SkRegion::Op op, SkIRect* bounds) { |
23 | if (op >= SkRegion::kUnion_Op && fClipRestrictionRect |
24 | && !fClipRestrictionRect->isEmpty()) { |
25 | if (!bounds->intersect(*fClipRestrictionRect)) { |
26 | bounds->setEmpty(); |
27 | } |
28 | } |
29 | } |
30 | |
31 | public: |
32 | SkConservativeClip() : fBounds(SkIRect::MakeEmpty()), fClipRestrictionRect(nullptr) {} |
33 | |
34 | bool isEmpty() const { return fBounds.isEmpty(); } |
35 | bool isRect() const { return true; } |
36 | const SkIRect& getBounds() const { return fBounds; } |
37 | |
38 | void setEmpty() { fBounds.setEmpty(); } |
39 | void setRect(const SkIRect& r) { fBounds = r; } |
40 | void setDeviceClipRestriction(const SkIRect* rect) { |
41 | fClipRestrictionRect = rect; |
42 | } |
43 | |
44 | void opRect(const SkRect&, const SkMatrix&, const SkIRect& limit, SkRegion::Op, bool isAA); |
45 | void opRRect(const SkRRect&, const SkMatrix&, const SkIRect& limit, SkRegion::Op, bool isAA); |
46 | void opPath(const SkPath&, const SkMatrix&, const SkIRect& limit, SkRegion::Op, bool isAA); |
47 | void opRegion(const SkRegion&, SkRegion::Op); |
48 | void opIRect(const SkIRect&, SkRegion::Op); |
49 | }; |
50 | |
51 | /** |
52 | * Wraps a SkRegion and SkAAClip, so we have a single object that can represent either our |
53 | * BW or antialiased clips. |
54 | * |
55 | * This class is optimized for the raster backend of canvas, but can be expense to keep up2date, |
56 | * so it supports a runtime option (force-conservative-rects) to turn it into a super-fast |
57 | * rect-only tracker. The gpu backend uses this since it does not need the result (it uses |
58 | * SkClipStack instead). |
59 | */ |
60 | class SkRasterClip { |
61 | public: |
62 | SkRasterClip(); |
63 | SkRasterClip(const SkIRect&); |
64 | SkRasterClip(const SkRegion&); |
65 | SkRasterClip(const SkRasterClip&); |
66 | SkRasterClip& operator=(const SkRasterClip&); |
67 | ~SkRasterClip(); |
68 | |
69 | // Only compares the current state. Does not compare isForceConservativeRects(), so that field |
70 | // could be different but this could still return true. |
71 | bool operator==(const SkRasterClip&) const; |
72 | bool operator!=(const SkRasterClip& other) const { |
73 | return !(*this == other); |
74 | } |
75 | |
76 | bool isBW() const { return fIsBW; } |
77 | bool isAA() const { return !fIsBW; } |
78 | const SkRegion& bwRgn() const { SkASSERT(fIsBW); return fBW; } |
79 | const SkAAClip& aaRgn() const { SkASSERT(!fIsBW); return fAA; } |
80 | |
81 | bool isEmpty() const { |
82 | SkASSERT(this->computeIsEmpty() == fIsEmpty); |
83 | return fIsEmpty; |
84 | } |
85 | |
86 | bool isRect() const { |
87 | SkASSERT(this->computeIsRect() == fIsRect); |
88 | return fIsRect; |
89 | } |
90 | |
91 | bool isComplex() const; |
92 | const SkIRect& getBounds() const; |
93 | |
94 | bool setEmpty(); |
95 | bool setRect(const SkIRect&); |
96 | |
97 | bool op(const SkIRect&, SkRegion::Op); |
98 | bool op(const SkRegion&, SkRegion::Op); |
99 | bool op(const SkRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA); |
100 | bool op(const SkRRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA); |
101 | bool op(const SkPath&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA); |
102 | bool op(sk_sp<SkShader>); |
103 | |
104 | void translate(int dx, int dy, SkRasterClip* dst) const; |
105 | void translate(int dx, int dy) { |
106 | this->translate(dx, dy, this); |
107 | } |
108 | |
109 | bool quickContains(const SkIRect& rect) const; |
110 | bool quickContains(int left, int top, int right, int bottom) const { |
111 | return quickContains(SkIRect::MakeLTRB(left, top, right, bottom)); |
112 | } |
113 | |
114 | /** |
115 | * Return true if this region is empty, or if the specified rectangle does |
116 | * not intersect the region. Returning false is not a guarantee that they |
117 | * intersect, but returning true is a guarantee that they do not. |
118 | */ |
119 | bool quickReject(const SkIRect& rect) const { |
120 | return !SkIRect::Intersects(this->getBounds(), rect); |
121 | } |
122 | |
123 | // hack for SkCanvas::getTotalClip |
124 | const SkRegion& forceGetBW(); |
125 | |
126 | #ifdef SK_DEBUG |
127 | void validate() const; |
128 | #else |
129 | void validate() const {} |
130 | #endif |
131 | |
132 | void setDeviceClipRestriction(const SkIRect* rect) { |
133 | fClipRestrictionRect = rect; |
134 | } |
135 | |
136 | sk_sp<SkShader> clipShader() const { return fShader; } |
137 | |
138 | private: |
139 | SkRegion fBW; |
140 | SkAAClip fAA; |
141 | bool fIsBW; |
142 | // these 2 are caches based on querying the right obj based on fIsBW |
143 | bool fIsEmpty; |
144 | bool fIsRect; |
145 | const SkIRect* fClipRestrictionRect = nullptr; |
146 | // if present, this augments the clip, not replaces it |
147 | sk_sp<SkShader> fShader; |
148 | |
149 | bool computeIsEmpty() const { |
150 | return fIsBW ? fBW.isEmpty() : fAA.isEmpty(); |
151 | } |
152 | |
153 | bool computeIsRect() const { |
154 | return fIsBW ? fBW.isRect() : fAA.isRect(); |
155 | } |
156 | |
157 | bool updateCacheAndReturnNonEmpty(bool detectAARect = true) { |
158 | fIsEmpty = this->computeIsEmpty(); |
159 | |
160 | // detect that our computed AA is really just a (hard-edged) rect |
161 | if (detectAARect && !fIsEmpty && !fIsBW && fAA.isRect()) { |
162 | fBW.setRect(fAA.getBounds()); |
163 | fAA.setEmpty(); // don't need this guy anymore |
164 | fIsBW = true; |
165 | } |
166 | |
167 | fIsRect = this->computeIsRect(); |
168 | return !fIsEmpty; |
169 | } |
170 | |
171 | void convertToAA(); |
172 | |
173 | bool setPath(const SkPath& path, const SkRegion& clip, bool doAA); |
174 | bool setPath(const SkPath& path, const SkIRect& clip, bool doAA); |
175 | bool op(const SkRasterClip&, SkRegion::Op); |
176 | bool setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse); |
177 | |
178 | inline void applyClipRestriction(SkRegion::Op op, SkIRect* bounds) { |
179 | if (op >= SkRegion::kUnion_Op && fClipRestrictionRect |
180 | && !fClipRestrictionRect->isEmpty()) { |
181 | if (!bounds->intersect(*fClipRestrictionRect)) { |
182 | bounds->setEmpty(); |
183 | } |
184 | } |
185 | } |
186 | |
187 | inline void applyClipRestriction(SkRegion::Op op, SkRect* bounds) { |
188 | if (op >= SkRegion::kUnion_Op && fClipRestrictionRect |
189 | && !fClipRestrictionRect->isEmpty()) { |
190 | if (!bounds->intersect(SkRect::Make(*fClipRestrictionRect))) { |
191 | bounds->setEmpty(); |
192 | } |
193 | } |
194 | } |
195 | }; |
196 | |
197 | class SkAutoRasterClipValidate : SkNoncopyable { |
198 | public: |
199 | SkAutoRasterClipValidate(const SkRasterClip& rc) : fRC(rc) { |
200 | fRC.validate(); |
201 | } |
202 | ~SkAutoRasterClipValidate() { |
203 | fRC.validate(); |
204 | } |
205 | private: |
206 | const SkRasterClip& fRC; |
207 | }; |
208 | #define SkAutoRasterClipValidate(...) SK_REQUIRE_LOCAL_VAR(SkAutoRasterClipValidate) |
209 | |
210 | #ifdef SK_DEBUG |
211 | #define AUTO_RASTERCLIP_VALIDATE(rc) SkAutoRasterClipValidate arcv(rc) |
212 | #else |
213 | #define AUTO_RASTERCLIP_VALIDATE(rc) |
214 | #endif |
215 | |
216 | /////////////////////////////////////////////////////////////////////////////// |
217 | |
218 | /** |
219 | * Encapsulates the logic of deciding if we need to change/wrap the blitter |
220 | * for aaclipping. If so, getRgn and getBlitter return modified values. If |
221 | * not, they return the raw blitter and (bw) clip region. |
222 | * |
223 | * We need to keep the constructor/destructor cost as small as possible, so we |
224 | * can freely put this guy on the stack, and not pay too much for the case when |
225 | * we're really BW anyways. |
226 | */ |
227 | class SkAAClipBlitterWrapper { |
228 | public: |
229 | SkAAClipBlitterWrapper(); |
230 | SkAAClipBlitterWrapper(const SkRasterClip&, SkBlitter*); |
231 | SkAAClipBlitterWrapper(const SkAAClip*, SkBlitter*); |
232 | |
233 | void init(const SkRasterClip&, SkBlitter*); |
234 | |
235 | const SkIRect& getBounds() const { |
236 | SkASSERT(fClipRgn); |
237 | return fClipRgn->getBounds(); |
238 | } |
239 | const SkRegion& getRgn() const { |
240 | SkASSERT(fClipRgn); |
241 | return *fClipRgn; |
242 | } |
243 | SkBlitter* getBlitter() { |
244 | SkASSERT(fBlitter); |
245 | return fBlitter; |
246 | } |
247 | |
248 | private: |
249 | SkRegion fBWRgn; |
250 | SkAAClipBlitter fAABlitter; |
251 | // what we return |
252 | const SkRegion* fClipRgn; |
253 | SkBlitter* fBlitter; |
254 | }; |
255 | |
256 | #endif |
257 | |