|
1 |
|
2 /* |
|
3 * Copyright 2011 Google Inc. |
|
4 * |
|
5 * Use of this source code is governed by a BSD-style license that can be |
|
6 * found in the LICENSE file. |
|
7 */ |
|
8 #ifndef SkClipStack_DEFINED |
|
9 #define SkClipStack_DEFINED |
|
10 |
|
11 #include "SkDeque.h" |
|
12 #include "SkPath.h" |
|
13 #include "SkRect.h" |
|
14 #include "SkRRect.h" |
|
15 #include "SkRegion.h" |
|
16 #include "SkTDArray.h" |
|
17 #include "SkTLazy.h" |
|
18 |
|
19 |
|
20 // Because a single save/restore state can have multiple clips, this class |
|
21 // stores the stack depth (fSaveCount) and clips (fDeque) separately. |
|
22 // Each clip in fDeque stores the stack state to which it belongs |
|
23 // (i.e., the fSaveCount in force when it was added). Restores are thus |
|
24 // implemented by removing clips from fDeque that have an fSaveCount larger |
|
25 // then the freshly decremented count. |
|
26 class SK_API SkClipStack { |
|
27 public: |
|
28 enum BoundsType { |
|
29 // The bounding box contains all the pixels that can be written to |
|
30 kNormal_BoundsType, |
|
31 // The bounding box contains all the pixels that cannot be written to. |
|
32 // The real bound extends out to infinity and all the pixels outside |
|
33 // of the bound can be written to. Note that some of the pixels inside |
|
34 // the bound may also be writeable but all pixels that cannot be |
|
35 // written to are guaranteed to be inside. |
|
36 kInsideOut_BoundsType |
|
37 }; |
|
38 |
|
39 class Element { |
|
40 public: |
|
41 enum Type { |
|
42 //!< This element makes the clip empty (regardless of previous elements). |
|
43 kEmpty_Type, |
|
44 //!< This element combines a rect with the current clip using a set operation |
|
45 kRect_Type, |
|
46 //!< This element combines a round-rect with the current clip using a set operation |
|
47 kRRect_Type, |
|
48 //!< This element combines a path with the current clip using a set operation |
|
49 kPath_Type, |
|
50 }; |
|
51 |
|
52 Element() { |
|
53 this->initCommon(0, SkRegion::kReplace_Op, false); |
|
54 this->setEmpty(); |
|
55 } |
|
56 |
|
57 Element(const Element&); |
|
58 |
|
59 Element(const SkRect& rect, SkRegion::Op op, bool doAA) { |
|
60 this->initRect(0, rect, op, doAA); |
|
61 } |
|
62 |
|
63 Element(const SkRRect& rrect, SkRegion::Op op, bool doAA) { |
|
64 this->initRRect(0, rrect, op, doAA); |
|
65 } |
|
66 |
|
67 Element(const SkPath& path, SkRegion::Op op, bool doAA) { |
|
68 this->initPath(0, path, op, doAA); |
|
69 } |
|
70 |
|
71 bool operator== (const Element& element) const; |
|
72 bool operator!= (const Element& element) const { return !(*this == element); } |
|
73 |
|
74 //!< Call to get the type of the clip element. |
|
75 Type getType() const { return fType; } |
|
76 |
|
77 //!< Call if getType() is kPath to get the path. |
|
78 const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return *fPath.get(); } |
|
79 |
|
80 //!< Call if getType() is kRRect to get the round-rect. |
|
81 const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; } |
|
82 |
|
83 //!< Call if getType() is kRect to get the rect. |
|
84 const SkRect& getRect() const { |
|
85 SkASSERT(kRect_Type == fType && (fRRect.isRect() || fRRect.isEmpty())); |
|
86 return fRRect.getBounds(); |
|
87 } |
|
88 |
|
89 //!< Call if getType() is not kEmpty to get the set operation used to combine this element. |
|
90 SkRegion::Op getOp() const { return fOp; } |
|
91 |
|
92 //!< Call to get the element as a path, regardless of its type. |
|
93 void asPath(SkPath* path) const; |
|
94 |
|
95 /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased |
|
96 when it is rasterized. */ |
|
97 bool isAA() const { return fDoAA; } |
|
98 |
|
99 //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty. |
|
100 void invertShapeFillType(); |
|
101 |
|
102 //!< Sets the set operation represented by the element. |
|
103 void setOp(SkRegion::Op op) { fOp = op; } |
|
104 |
|
105 /** The GenID can be used by clip stack clients to cache representations of the clip. The |
|
106 ID corresponds to the set of clip elements up to and including this element within the |
|
107 stack not to the element itself. That is the same clip path in different stacks will |
|
108 have a different ID since the elements produce different clip result in the context of |
|
109 their stacks. */ |
|
110 int32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; } |
|
111 |
|
112 /** |
|
113 * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape |
|
114 * is inverse filled is not considered.) |
|
115 */ |
|
116 const SkRect& getBounds() const { |
|
117 static const SkRect kEmpty = { 0, 0, 0, 0 }; |
|
118 switch (fType) { |
|
119 case kRect_Type: // fallthrough |
|
120 case kRRect_Type: |
|
121 return fRRect.getBounds(); |
|
122 case kPath_Type: |
|
123 return fPath.get()->getBounds(); |
|
124 case kEmpty_Type: |
|
125 return kEmpty; |
|
126 default: |
|
127 SkDEBUGFAIL("Unexpected type."); |
|
128 return kEmpty; |
|
129 } |
|
130 } |
|
131 |
|
132 /** |
|
133 * Conservatively checks whether the clip shape contains the rect param. (Whether the shape |
|
134 * is inverse filled is not considered.) |
|
135 */ |
|
136 bool contains(const SkRect& rect) const { |
|
137 switch (fType) { |
|
138 case kRect_Type: |
|
139 return this->getRect().contains(rect); |
|
140 case kRRect_Type: |
|
141 return fRRect.contains(rect); |
|
142 case kPath_Type: |
|
143 return fPath.get()->conservativelyContainsRect(rect); |
|
144 case kEmpty_Type: |
|
145 return false; |
|
146 default: |
|
147 SkDEBUGFAIL("Unexpected type."); |
|
148 return false; |
|
149 } |
|
150 } |
|
151 |
|
152 /** |
|
153 * Is the clip shape inverse filled. |
|
154 */ |
|
155 bool isInverseFilled() const { |
|
156 return kPath_Type == fType && fPath.get()->isInverseFillType(); |
|
157 } |
|
158 |
|
159 private: |
|
160 friend class SkClipStack; |
|
161 |
|
162 SkTLazy<SkPath> fPath; |
|
163 SkRRect fRRect; |
|
164 int fSaveCount; // save count of stack when this element was added. |
|
165 SkRegion::Op fOp; |
|
166 Type fType; |
|
167 bool fDoAA; |
|
168 |
|
169 /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's |
|
170 bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the |
|
171 conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be |
|
172 drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which |
|
173 occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding |
|
174 box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside |
|
175 the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the |
|
176 infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we |
|
177 can capture the cancelling out of the extensions to infinity when two inverse filled |
|
178 clips are Booleaned together. */ |
|
179 SkClipStack::BoundsType fFiniteBoundType; |
|
180 SkRect fFiniteBound; |
|
181 |
|
182 // When element is applied to the previous elements in the stack is the result known to be |
|
183 // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle. |
|
184 bool fIsIntersectionOfRects; |
|
185 |
|
186 int fGenID; |
|
187 |
|
188 Element(int saveCount) { |
|
189 this->initCommon(saveCount, SkRegion::kReplace_Op, false); |
|
190 this->setEmpty(); |
|
191 } |
|
192 |
|
193 Element(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) { |
|
194 this->initRRect(saveCount, rrect, op, doAA); |
|
195 } |
|
196 |
|
197 Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) { |
|
198 this->initRect(saveCount, rect, op, doAA); |
|
199 } |
|
200 |
|
201 Element(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) { |
|
202 this->initPath(saveCount, path, op, doAA); |
|
203 } |
|
204 |
|
205 void initCommon(int saveCount, SkRegion::Op op, bool doAA) { |
|
206 fSaveCount = saveCount; |
|
207 fOp = op; |
|
208 fDoAA = doAA; |
|
209 // A default of inside-out and empty bounds means the bounds are effectively void as it |
|
210 // indicates that nothing is known to be outside the clip. |
|
211 fFiniteBoundType = kInsideOut_BoundsType; |
|
212 fFiniteBound.setEmpty(); |
|
213 fIsIntersectionOfRects = false; |
|
214 fGenID = kInvalidGenID; |
|
215 } |
|
216 |
|
217 void initRect(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) { |
|
218 fRRect.setRect(rect); |
|
219 fType = kRect_Type; |
|
220 this->initCommon(saveCount, op, doAA); |
|
221 } |
|
222 |
|
223 void initRRect(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) { |
|
224 SkRRect::Type type = rrect.getType(); |
|
225 fRRect = rrect; |
|
226 if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) { |
|
227 fType = kRect_Type; |
|
228 } else { |
|
229 fType = kRRect_Type; |
|
230 } |
|
231 this->initCommon(saveCount, op, doAA); |
|
232 } |
|
233 |
|
234 void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA); |
|
235 |
|
236 void setEmpty(); |
|
237 |
|
238 // All Element methods below are only used within SkClipStack.cpp |
|
239 inline void checkEmpty() const; |
|
240 inline bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const; |
|
241 /* This method checks to see if two rect clips can be safely merged into one. The issue here |
|
242 is that to be strictly correct all the edges of the resulting rect must have the same |
|
243 anti-aliasing. */ |
|
244 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const; |
|
245 /** Determines possible finite bounds for the Element given the previous element of the |
|
246 stack */ |
|
247 void updateBoundAndGenID(const Element* prior); |
|
248 // The different combination of fill & inverse fill when combining bounding boxes |
|
249 enum FillCombo { |
|
250 kPrev_Cur_FillCombo, |
|
251 kPrev_InvCur_FillCombo, |
|
252 kInvPrev_Cur_FillCombo, |
|
253 kInvPrev_InvCur_FillCombo |
|
254 }; |
|
255 // per-set operation functions used by updateBoundAndGenID(). |
|
256 inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite); |
|
257 inline void combineBoundsXOR(int combination, const SkRect& prevFinite); |
|
258 inline void combineBoundsUnion(int combination, const SkRect& prevFinite); |
|
259 inline void combineBoundsIntersection(int combination, const SkRect& prevFinite); |
|
260 inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite); |
|
261 }; |
|
262 |
|
263 SkClipStack(); |
|
264 SkClipStack(const SkClipStack& b); |
|
265 explicit SkClipStack(const SkRect& r); |
|
266 explicit SkClipStack(const SkIRect& r); |
|
267 ~SkClipStack(); |
|
268 |
|
269 SkClipStack& operator=(const SkClipStack& b); |
|
270 bool operator==(const SkClipStack& b) const; |
|
271 bool operator!=(const SkClipStack& b) const { return !(*this == b); } |
|
272 |
|
273 void reset(); |
|
274 |
|
275 int getSaveCount() const { return fSaveCount; } |
|
276 void save(); |
|
277 void restore(); |
|
278 |
|
279 /** |
|
280 * getBounds places the current finite bound in its first parameter. In its |
|
281 * second, it indicates which kind of bound is being returned. If |
|
282 * 'canvFiniteBound' is a normal bounding box then it encloses all writeable |
|
283 * pixels. If 'canvFiniteBound' is an inside out bounding box then it |
|
284 * encloses all the un-writeable pixels and the true/normal bound is the |
|
285 * infinite plane. isIntersectionOfRects is an optional parameter |
|
286 * that is true if 'canvFiniteBound' resulted from an intersection of rects. |
|
287 */ |
|
288 void getBounds(SkRect* canvFiniteBound, |
|
289 BoundsType* boundType, |
|
290 bool* isIntersectionOfRects = NULL) const; |
|
291 |
|
292 /** |
|
293 * Takes an input rect in device space and conservatively clips it to the |
|
294 * clip-stack. If false is returned then the rect does not intersect the |
|
295 * clip and is unmodified. |
|
296 */ |
|
297 bool intersectRectWithClip(SkRect* devRect) const; |
|
298 |
|
299 /** |
|
300 * Returns true if the input rect in device space is entirely contained |
|
301 * by the clip. A return value of false does not guarantee that the rect |
|
302 * is not contained by the clip. |
|
303 */ |
|
304 bool quickContains(const SkRect& devRect) const; |
|
305 |
|
306 void clipDevRect(const SkIRect& ir, SkRegion::Op op) { |
|
307 SkRect r; |
|
308 r.set(ir); |
|
309 this->clipDevRect(r, op, false); |
|
310 } |
|
311 void clipDevRect(const SkRect&, SkRegion::Op, bool doAA); |
|
312 void clipDevRRect(const SkRRect&, SkRegion::Op, bool doAA); |
|
313 void clipDevPath(const SkPath&, SkRegion::Op, bool doAA); |
|
314 // An optimized version of clipDevRect(emptyRect, kIntersect, ...) |
|
315 void clipEmpty(); |
|
316 |
|
317 /** |
|
318 * isWideOpen returns true if the clip state corresponds to the infinite |
|
319 * plane (i.e., draws are not limited at all) |
|
320 */ |
|
321 bool isWideOpen() const; |
|
322 |
|
323 /** |
|
324 * The generation ID has three reserved values to indicate special |
|
325 * (potentially ignorable) cases |
|
326 */ |
|
327 static const int32_t kInvalidGenID = 0; //!< Invalid id that is never returned by |
|
328 //!< SkClipStack. Useful when caching clips |
|
329 //!< based on GenID. |
|
330 static const int32_t kEmptyGenID = 1; // no pixels writeable |
|
331 static const int32_t kWideOpenGenID = 2; // all pixels writeable |
|
332 |
|
333 int32_t getTopmostGenID() const; |
|
334 |
|
335 public: |
|
336 class Iter { |
|
337 public: |
|
338 enum IterStart { |
|
339 kBottom_IterStart = SkDeque::Iter::kFront_IterStart, |
|
340 kTop_IterStart = SkDeque::Iter::kBack_IterStart |
|
341 }; |
|
342 |
|
343 /** |
|
344 * Creates an uninitialized iterator. Must be reset() |
|
345 */ |
|
346 Iter(); |
|
347 |
|
348 Iter(const SkClipStack& stack, IterStart startLoc); |
|
349 |
|
350 /** |
|
351 * Return the clip element for this iterator. If next()/prev() returns NULL, then the |
|
352 * iterator is done. |
|
353 */ |
|
354 const Element* next(); |
|
355 const Element* prev(); |
|
356 |
|
357 /** |
|
358 * Moves the iterator to the topmost element with the specified RegionOp and returns that |
|
359 * element. If no clip element with that op is found, the first element is returned. |
|
360 */ |
|
361 const Element* skipToTopmost(SkRegion::Op op); |
|
362 |
|
363 /** |
|
364 * Restarts the iterator on a clip stack. |
|
365 */ |
|
366 void reset(const SkClipStack& stack, IterStart startLoc); |
|
367 |
|
368 private: |
|
369 const SkClipStack* fStack; |
|
370 SkDeque::Iter fIter; |
|
371 }; |
|
372 |
|
373 /** |
|
374 * The B2TIter iterates from the bottom of the stack to the top. |
|
375 * It inherits privately from Iter to prevent access to reverse iteration. |
|
376 */ |
|
377 class B2TIter : private Iter { |
|
378 public: |
|
379 B2TIter() {} |
|
380 |
|
381 /** |
|
382 * Wrap Iter's 2 parameter ctor to force initialization to the |
|
383 * beginning of the deque/bottom of the stack |
|
384 */ |
|
385 B2TIter(const SkClipStack& stack) |
|
386 : INHERITED(stack, kBottom_IterStart) { |
|
387 } |
|
388 |
|
389 using Iter::next; |
|
390 |
|
391 /** |
|
392 * Wrap Iter::reset to force initialization to the |
|
393 * beginning of the deque/bottom of the stack |
|
394 */ |
|
395 void reset(const SkClipStack& stack) { |
|
396 this->INHERITED::reset(stack, kBottom_IterStart); |
|
397 } |
|
398 |
|
399 private: |
|
400 |
|
401 typedef Iter INHERITED; |
|
402 }; |
|
403 |
|
404 /** |
|
405 * GetConservativeBounds returns a conservative bound of the current clip. |
|
406 * Since this could be the infinite plane (if inverse fills were involved) the |
|
407 * maxWidth and maxHeight parameters can be used to limit the returned bound |
|
408 * to the expected drawing area. Similarly, the offsetX and offsetY parameters |
|
409 * allow the caller to offset the returned bound to account for translated |
|
410 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds, |
|
411 * the translation (+offsetX, +offsetY) is applied before the clamp to the |
|
412 * maximum rectangle: [0,maxWidth) x [0,maxHeight). |
|
413 * isIntersectionOfRects is an optional parameter that is true when |
|
414 * 'devBounds' is the result of an intersection of rects. In this case |
|
415 * 'devBounds' is the exact answer/clip. |
|
416 */ |
|
417 void getConservativeBounds(int offsetX, |
|
418 int offsetY, |
|
419 int maxWidth, |
|
420 int maxHeight, |
|
421 SkRect* devBounds, |
|
422 bool* isIntersectionOfRects = NULL) const; |
|
423 |
|
424 private: |
|
425 friend class Iter; |
|
426 |
|
427 SkDeque fDeque; |
|
428 int fSaveCount; |
|
429 |
|
430 // Generation ID for the clip stack. This is incremented for each |
|
431 // clipDevRect and clipDevPath call. 0 is reserved to indicate an |
|
432 // invalid ID. |
|
433 static int32_t gGenID; |
|
434 |
|
435 /** |
|
436 * Helper for clipDevPath, etc. |
|
437 */ |
|
438 void pushElement(const Element& element); |
|
439 |
|
440 /** |
|
441 * Restore the stack back to the specified save count. |
|
442 */ |
|
443 void restoreTo(int saveCount); |
|
444 |
|
445 /** |
|
446 * Return the next unique generation ID. |
|
447 */ |
|
448 static int32_t GetNextGenID(); |
|
449 }; |
|
450 |
|
451 #endif |