Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkClipStack.h
Go to the documentation of this file.
1/*
2 * Copyright 2011 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 SkClipStack_DEFINED
9#define SkClipStack_DEFINED
10
13#include "include/core/SkPath.h"
15#include "include/core/SkRect.h"
20#include "src/base/SkTLazy.h"
21
22#include <cstddef>
23#include <cstdint>
24#include <utility>
25
26// Because a single save/restore state can have multiple clips, this class
27// stores the stack depth (fSaveCount) and clips (fDeque) separately.
28// Each clip in fDeque stores the stack state to which it belongs
29// (i.e., the fSaveCount in force when it was added). Restores are thus
30// implemented by removing clips from fDeque that have an fSaveCount larger
31// then the freshly decremented count.
33public:
35 // The bounding box contains all the pixels that can be written to
37 // The bounding box contains all the pixels that cannot be written to.
38 // The real bound extends out to infinity and all the pixels outside
39 // of the bound can be written to. Note that some of the pixels inside
40 // the bound may also be writeable but all pixels that cannot be
41 // written to are guaranteed to be inside.
43 };
44
45 /**
46 * An element of the clip stack. It represents a shape combined with the prevoius clip using a
47 * set operator. Each element can be antialiased or not.
48 */
49 class Element {
50 public:
51 /** This indicates the shape type of the clip element in device space. */
52 enum class DeviceSpaceType {
53 //!< This element makes the clip empty (regardless of previous elements).
54 kEmpty,
55 //!< This element combines a device space rect with the current clip.
56 kRect,
57 //!< This element combines a device space round-rect with the current clip.
58 kRRect,
59 //!< This element combines a device space path with the current clip.
60 kPath,
61 //!< This element does not have geometry, but applies a shader to the clip
62 kShader,
63
65 };
66 static const int kTypeCnt = (int)DeviceSpaceType::kLastType + 1;
67
69 this->initCommon(0, SkClipOp::kIntersect, false);
70 this->setEmpty();
71 }
72
73 Element(const Element&);
74
75 Element(const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
76 this->initRect(0, rect, m, op, doAA);
77 }
78
79 Element(const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
80 this->initRRect(0, rrect, m, op, doAA);
81 }
82
83 Element(const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
84 this->initPath(0, path, m, op, doAA);
85 }
86
88 this->initShader(0, std::move(shader));
89 }
90
91 Element(const SkRect& rect, bool doAA) {
92 this->initReplaceRect(0, rect, doAA);
93 }
94
96
97 bool operator== (const Element& element) const;
98 bool operator!= (const Element& element) const { return !(*this == element); }
99
100 //!< Call to get the type of the clip element.
101 DeviceSpaceType getDeviceSpaceType() const { return fDeviceSpaceType; }
102
103 //!< Call to get the save count associated with this clip element.
104 int getSaveCount() const { return fSaveCount; }
105
106 //!< Call if getDeviceSpaceType() is kPath to get the path.
107 const SkPath& getDeviceSpacePath() const {
108 SkASSERT(DeviceSpaceType::kPath == fDeviceSpaceType);
109 return *fDeviceSpacePath;
110 }
111
112 //!< Call if getDeviceSpaceType() is kRRect to get the round-rect.
114 SkASSERT(DeviceSpaceType::kRRect == fDeviceSpaceType);
115 return fDeviceSpaceRRect;
116 }
117
118 //!< Call if getDeviceSpaceType() is kRect to get the rect.
119 const SkRect& getDeviceSpaceRect() const {
120 SkASSERT(DeviceSpaceType::kRect == fDeviceSpaceType &&
121 (fDeviceSpaceRRect.isRect() || fDeviceSpaceRRect.isEmpty()));
122 return fDeviceSpaceRRect.getBounds();
123 }
124
125 //!<Call if getDeviceSpaceType() is kShader to get a reference to the clip shader.
127 return fShader;
128 }
129 const SkShader* getShader() const {
130 return fShader.get();
131 }
132
133 //!< Call if getDeviceSpaceType() is not kEmpty to get the set operation used to combine
134 //!< this element.
135 SkClipOp getOp() const { return fOp; }
136 // Augments getOps()'s behavior by requiring a clip reset before the op is applied.
137 bool isReplaceOp() const { return fIsReplace; }
138
139 //!< Call to get the element as a path, regardless of its type.
140 void asDeviceSpacePath(SkPath* path) const;
141
142 //!< Call if getType() is not kPath to get the element as a round rect.
144 SkASSERT(DeviceSpaceType::kPath != fDeviceSpaceType);
145 return fDeviceSpaceRRect;
146 }
147
148 /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
149 when it is rasterized. */
150 bool isAA() const { return fDoAA; }
151
152 //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
153 void invertShapeFillType();
154
155 /** The GenID can be used by clip stack clients to cache representations of the clip. The
156 ID corresponds to the set of clip elements up to and including this element within the
157 stack not to the element itself. That is the same clip path in different stacks will
158 have a different ID since the elements produce different clip result in the context of
159 their stacks. */
160 uint32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
161
162 /**
163 * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
164 * is inverse filled is not considered.)
165 */
166 const SkRect& getBounds() const;
167
168 /**
169 * Conservatively checks whether the clip shape contains the rect/rrect. (Whether the shape
170 * is inverse filled is not considered.)
171 */
172 bool contains(const SkRect& rect) const;
173 bool contains(const SkRRect& rrect) const;
174
175 /**
176 * Is the clip shape inverse filled.
177 */
178 bool isInverseFilled() const {
179 return DeviceSpaceType::kPath == fDeviceSpaceType &&
180 fDeviceSpacePath->isInverseFillType();
181 }
182
183#ifdef SK_DEBUG
184 /**
185 * Dumps the element to SkDebugf. This is intended for Skia development debugging
186 * Don't rely on the existence of this function or the formatting of its output.
187 */
188 void dump() const;
189#endif
190
191 private:
192 friend class SkClipStack;
193
194 SkTLazy<SkPath> fDeviceSpacePath;
195 SkRRect fDeviceSpaceRRect;
196 sk_sp<SkShader> fShader;
197 int fSaveCount; // save count of stack when this element was added.
198 SkClipOp fOp;
199 DeviceSpaceType fDeviceSpaceType;
200 bool fDoAA;
201 bool fIsReplace;
202
203 /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
204 bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
205 conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
206 drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
207 occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
208 box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
209 the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
210 infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
211 can capture the cancelling out of the extensions to infinity when two inverse filled
212 clips are Booleaned together. */
213 SkClipStack::BoundsType fFiniteBoundType;
214 SkRect fFiniteBound;
215
216 // When element is applied to the previous elements in the stack is the result known to be
217 // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
218 bool fIsIntersectionOfRects;
219
220 uint32_t fGenID;
221 Element(int saveCount) {
222 this->initCommon(saveCount, SkClipOp::kIntersect, false);
223 this->setEmpty();
224 }
225
226 Element(int saveCount, const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
227 this->initRRect(saveCount, rrect, m, op, doAA);
228 }
229
230 Element(int saveCount, const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
231 this->initRect(saveCount, rect, m, op, doAA);
232 }
233
234 Element(int saveCount, const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
235 this->initPath(saveCount, path, m, op, doAA);
236 }
237
238 Element(int saveCount, sk_sp<SkShader> shader) {
239 this->initShader(saveCount, std::move(shader));
240 }
241
242 Element(int saveCount, const SkRect& rect, bool doAA) {
243 this->initReplaceRect(saveCount, rect, doAA);
244 }
245
246 void initCommon(int saveCount, SkClipOp op, bool doAA);
247 void initRect(int saveCount, const SkRect&, const SkMatrix&, SkClipOp, bool doAA);
248 void initRRect(int saveCount, const SkRRect&, const SkMatrix&, SkClipOp, bool doAA);
249 void initPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
250 void initAsPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
251 void initShader(int saveCount, sk_sp<SkShader>);
252 void initReplaceRect(int saveCount, const SkRect&, bool doAA);
253
254 void setEmpty();
255
256 // All Element methods below are only used within SkClipStack.cpp
257 inline void checkEmpty() const;
258 inline bool canBeIntersectedInPlace(int saveCount, SkClipOp op) const;
259 /* This method checks to see if two rect clips can be safely merged into one. The issue here
260 is that to be strictly correct all the edges of the resulting rect must have the same
261 anti-aliasing. */
262 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
263 /** Determines possible finite bounds for the Element given the previous element of the
264 stack */
265 void updateBoundAndGenID(const Element* prior);
266 // The different combination of fill & inverse fill when combining bounding boxes
267 enum FillCombo {
268 kPrev_Cur_FillCombo,
269 kPrev_InvCur_FillCombo,
270 kInvPrev_Cur_FillCombo,
271 kInvPrev_InvCur_FillCombo
272 };
273 // per-set operation functions used by updateBoundAndGenID().
274 inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
275 inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
276 };
277
278 SkClipStack();
279 SkClipStack(void* storage, size_t size);
280 SkClipStack(const SkClipStack& b);
281 ~SkClipStack();
282
284 bool operator==(const SkClipStack& b) const;
285 bool operator!=(const SkClipStack& b) const { return !(*this == b); }
286
287 void reset();
288
289 int getSaveCount() const { return fSaveCount; }
290 void save();
291 void restore();
292
294 public:
295 AutoRestore(SkClipStack* cs, bool doSave)
296 : fCS(cs), fSaveCount(cs->getSaveCount())
297 {
298 if (doSave) {
299 fCS->save();
300 }
301 }
303 SkASSERT(fCS->getSaveCount() >= fSaveCount); // no underflow
304 while (fCS->getSaveCount() > fSaveCount) {
305 fCS->restore();
306 }
307 }
308
309 private:
310 SkClipStack* fCS;
311 const int fSaveCount;
312 };
313
314 /**
315 * getBounds places the current finite bound in its first parameter. In its
316 * second, it indicates which kind of bound is being returned. If
317 * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
318 * pixels. If 'canvFiniteBound' is an inside out bounding box then it
319 * encloses all the un-writeable pixels and the true/normal bound is the
320 * infinite plane. isIntersectionOfRects is an optional parameter
321 * that is true if 'canvFiniteBound' resulted from an intersection of rects.
322 */
323 void getBounds(SkRect* canvFiniteBound,
324 BoundsType* boundType,
325 bool* isIntersectionOfRects = nullptr) const;
326
327 SkRect bounds(const SkIRect& deviceBounds) const;
328 bool isEmpty(const SkIRect& deviceBounds) const;
329
330 /**
331 * Returns true if the input (r)rect in device space is entirely contained
332 * by the clip. A return value of false does not guarantee that the (r)rect
333 * is not contained by the clip.
334 */
335 bool quickContains(const SkRect& devRect) const {
336 return this->isWideOpen() || this->internalQuickContains(devRect);
337 }
338
339 bool quickContains(const SkRRect& devRRect) const {
340 return this->isWideOpen() || this->internalQuickContains(devRRect);
341 }
342
343 void clipDevRect(const SkIRect& ir, SkClipOp op) {
344 SkRect r;
345 r.set(ir);
346 this->clipRect(r, SkMatrix::I(), op, false);
347 }
348 void clipRect(const SkRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
349 void clipRRect(const SkRRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
350 void clipPath(const SkPath&, const SkMatrix& matrix, SkClipOp, bool doAA);
352 // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
353 void clipEmpty();
354
355 void replaceClip(const SkRect& devRect, bool doAA);
356
357 /**
358 * isWideOpen returns true if the clip state corresponds to the infinite
359 * plane (i.e., draws are not limited at all)
360 */
361 bool isWideOpen() const { return this->getTopmostGenID() == kWideOpenGenID; }
362
363 /**
364 * This method quickly and conservatively determines whether the entire stack is equivalent to
365 * intersection with a rrect given a bounds, where the rrect must not contain the entire bounds.
366 *
367 * @param bounds A bounds on what will be drawn through the clip. The clip only need be
368 * equivalent to a intersection with a rrect for draws within the bounds. The
369 * returned rrect must intersect the bounds but need not be contained by the
370 * bounds.
371 * @param rrect If return is true rrect will contain the rrect equivalent to the stack.
372 * @param aa If return is true aa will indicate whether the equivalent rrect clip is
373 * antialiased.
374 * @return true if the stack is equivalent to a single rrect intersect clip, false otherwise.
375 */
376 bool isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const;
377
378 /**
379 * The generation ID has three reserved values to indicate special
380 * (potentially ignorable) cases
381 */
382 static const uint32_t kInvalidGenID = 0; //!< Invalid id that is never returned by
383 //!< SkClipStack. Useful when caching clips
384 //!< based on GenID.
385 static const uint32_t kEmptyGenID = 1; // no pixels writeable
386 static const uint32_t kWideOpenGenID = 2; // all pixels writeable
387
388 uint32_t getTopmostGenID() const;
389
390#ifdef SK_DEBUG
391 /**
392 * Dumps the contents of the clip stack to SkDebugf. This is intended for Skia development
393 * debugging. Don't rely on the existence of this function or the formatting of its output.
394 */
395 void dump() const;
396#endif
397
398public:
399 class Iter {
400 public:
405
406 /**
407 * Creates an uninitialized iterator. Must be reset()
408 */
409 Iter();
410
411 Iter(const SkClipStack& stack, IterStart startLoc);
412
413 /**
414 * Return the clip element for this iterator. If next()/prev() returns NULL, then the
415 * iterator is done.
416 */
417 const Element* next();
418 const Element* prev();
419
420 /**
421 * Moves the iterator to the topmost element with the specified RegionOp and returns that
422 * element. If no clip element with that op is found, the first element is returned.
423 */
424 const Element* skipToTopmost(SkClipOp op);
425
426 /**
427 * Restarts the iterator on a clip stack.
428 */
429 void reset(const SkClipStack& stack, IterStart startLoc);
430
431 private:
432 const SkClipStack* fStack;
433 SkDeque::Iter fIter;
434 };
435
436 /**
437 * The B2TIter iterates from the bottom of the stack to the top.
438 * It inherits privately from Iter to prevent access to reverse iteration.
439 */
440 class B2TIter : private Iter {
441 public:
443
444 /**
445 * Wrap Iter's 2 parameter ctor to force initialization to the
446 * beginning of the deque/bottom of the stack
447 */
448 B2TIter(const SkClipStack& stack)
449 : INHERITED(stack, kBottom_IterStart) {
450 }
451
452 using Iter::next;
453
454 /**
455 * Wrap Iter::reset to force initialization to the
456 * beginning of the deque/bottom of the stack
457 */
458 void reset(const SkClipStack& stack) {
460 }
461
462 private:
463
464 using INHERITED = Iter;
465 };
466
467 /**
468 * GetConservativeBounds returns a conservative bound of the current clip.
469 * Since this could be the infinite plane (if inverse fills were involved) the
470 * maxWidth and maxHeight parameters can be used to limit the returned bound
471 * to the expected drawing area. Similarly, the offsetX and offsetY parameters
472 * allow the caller to offset the returned bound to account for translated
473 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
474 * the translation (+offsetX, +offsetY) is applied before the clamp to the
475 * maximum rectangle: [0,maxWidth) x [0,maxHeight).
476 * isIntersectionOfRects is an optional parameter that is true when
477 * 'devBounds' is the result of an intersection of rects. In this case
478 * 'devBounds' is the exact answer/clip.
479 */
481 int offsetY,
482 int maxWidth,
483 int maxHeight,
484 SkRect* devBounds,
485 bool* isIntersectionOfRects = nullptr) const;
486
487private:
488 friend class Iter;
489
490 SkDeque fDeque;
491 int fSaveCount;
492
493 bool internalQuickContains(const SkRect& devRect) const;
494 bool internalQuickContains(const SkRRect& devRRect) const;
495
496 /**
497 * Helper for clipDevPath, etc.
498 */
499 void pushElement(const Element& element);
500
501 /**
502 * Restore the stack back to the specified save count.
503 */
504 void restoreTo(int saveCount);
505
506 /**
507 * Return the next unique generation ID.
508 */
509 static uint32_t GetNextGenID();
510};
511
512#endif
#define SkASSERT(cond)
Definition SkAssert.h:116
SkClipOp
Definition SkClipOp.h:13
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
Type::kYUV Type::kRGBA() int(0.7 *637)
AutoRestore(SkClipStack *cs, bool doSave)
B2TIter(const SkClipStack &stack)
void reset(const SkClipStack &stack)
bool operator!=(const Element &element) const
Call to get the type of the clip element.
Definition SkClipStack.h:98
sk_sp< SkShader > refShader() const
Element(sk_sp< SkShader > shader)
Definition SkClipStack.h:87
Element(const SkRect &rect, bool doAA)
Definition SkClipStack.h:91
Element(const SkRect &rect, const SkMatrix &m, SkClipOp op, bool doAA)
Definition SkClipStack.h:75
const SkShader * getShader() const
@ kPath
This element does not have geometry, but applies a shader to the clip.
@ kEmpty
This element makes the clip empty (regardless of previous elements).
@ kRect
This element combines a device space round-rect with the current clip.
@ kRRect
This element combines a device space path with the current clip.
SkClipOp getOp() const
uint32_t getGenID() const
const SkRect & getDeviceSpaceRect() const
Call if getDeviceSpaceType() is kShader to get a reference to the clip shader.
bool isAA() const
Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
static const int kTypeCnt
Definition SkClipStack.h:66
int getSaveCount() const
Call if getDeviceSpaceType() is kPath to get the path.
const SkRRect & getDeviceSpaceRRect() const
Call if getDeviceSpaceType() is kRect to get the rect.
bool isInverseFilled() const
DeviceSpaceType getDeviceSpaceType() const
Call to get the save count associated with this clip element.
Element(const SkRRect &rrect, const SkMatrix &m, SkClipOp op, bool doAA)
Definition SkClipStack.h:79
const SkPath & getDeviceSpacePath() const
Call if getDeviceSpaceType() is kRRect to get the round-rect.
bool operator==(const Element &element) const
const SkRRect & asDeviceSpaceRRect() const
const SkRect & getBounds() const
bool contains(const SkRect &rect) const
Element(const SkPath &path, const SkMatrix &m, SkClipOp op, bool doAA)
Definition SkClipStack.h:83
bool isReplaceOp() const
Call to get the element as a path, regardless of its type.
void asDeviceSpacePath(SkPath *path) const
Call if getType() is not kPath to get the element as a round rect.
void reset(const SkClipStack &stack, IterStart startLoc)
const Element * skipToTopmost(SkClipOp op)
const Element * next()
const Element * prev()
@ kInsideOut_BoundsType
Definition SkClipStack.h:42
void clipRect(const SkRect &, const SkMatrix &matrix, SkClipOp, bool doAA)
void clipShader(sk_sp< SkShader >)
bool isRRect(const SkRect &bounds, SkRRect *rrect, bool *aa) const
bool quickContains(const SkRRect &devRRect) const
static const uint32_t kEmptyGenID
void clipRRect(const SkRRect &, const SkMatrix &matrix, SkClipOp, bool doAA)
bool quickContains(const SkRect &devRect) const
void getBounds(SkRect *canvFiniteBound, BoundsType *boundType, bool *isIntersectionOfRects=nullptr) const
bool isEmpty(const SkIRect &deviceBounds) const
void clipDevRect(const SkIRect &ir, SkClipOp op)
void getConservativeBounds(int offsetX, int offsetY, int maxWidth, int maxHeight, SkRect *devBounds, bool *isIntersectionOfRects=nullptr) const
bool operator==(const SkClipStack &b) const
SkClipStack & operator=(const SkClipStack &b)
uint32_t getTopmostGenID() const
int getSaveCount() const
static const uint32_t kInvalidGenID
static const uint32_t kWideOpenGenID
bool operator!=(const SkClipStack &b) const
bool isWideOpen() const
void clipPath(const SkPath &, const SkMatrix &matrix, SkClipOp, bool doAA)
SkRect bounds(const SkIRect &deviceBounds) const
void replaceClip(const SkRect &devRect, bool doAA)
@ kBack_IterStart
Definition SkDeque.h:66
@ kFront_IterStart
Definition SkDeque.h:65
static const SkMatrix & I()
bool isInverseFillType() const
Definition SkPath.h:244
bool isRect() const
Definition SkRRect.h:84
bool isEmpty() const
Definition SkRRect.h:83
const SkRect & getBounds() const
Definition SkRRect.h:279
T * get() const
Definition SkRefCnt.h:303
static bool b
SkScalar offsetX
SkScalar offsetY
void set(const SkIRect &src)
Definition SkRect.h:849