Skia
2DGraphicsLibrary
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SkClipStack.h
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 "SkTLazy.h"
17 
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 : public SkNVRefCnt<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 {
43  kEmpty_Type,
45  kRect_Type,
47  kRRect_Type,
49  kPath_Type,
50 
51  kLastType = kPath_Type
52  };
53  static const int kTypeCnt = kLastType + 1;
54 
55  Element() {
56  this->initCommon(0, SkRegion::kReplace_Op, false);
57  this->setEmpty();
58  }
59 
60  Element(const Element&);
61 
62  Element(const SkRect& rect, SkRegion::Op op, bool doAA) {
63  this->initRect(0, rect, op, doAA);
64  }
65 
66  Element(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
67  this->initRRect(0, rrect, op, doAA);
68  }
69 
70  Element(const SkPath& path, SkRegion::Op op, bool doAA) {
71  this->initPath(0, path, op, doAA);
72  }
73 
74  bool operator== (const Element& element) const;
75  bool operator!= (const Element& element) const { return !(*this == element); }
76 
78  Type getType() const { return fType; }
79 
81  int getSaveCount() const { return fSaveCount; }
82 
84  const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return *fPath.get(); }
85 
87  const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; }
88 
90  const SkRect& getRect() const {
91  SkASSERT(kRect_Type == fType && (fRRect.isRect() || fRRect.isEmpty()));
92  return fRRect.getBounds();
93  }
94 
96  SkRegion::Op getOp() const { return fOp; }
97 
99  void asPath(SkPath* path) const;
100 
102  const SkRRect& asRRect() const { SkASSERT(kPath_Type != fType); return fRRect; }
103 
106  bool isAA() const { return fDoAA; }
107 
109  void invertShapeFillType();
110 
112  void setOp(SkRegion::Op op) { fOp = op; }
113 
119  int32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
120 
125  const SkRect& getBounds() const {
126  static const SkRect kEmpty = { 0, 0, 0, 0 };
127  switch (fType) {
128  case kRect_Type: // fallthrough
129  case kRRect_Type:
130  return fRRect.getBounds();
131  case kPath_Type:
132  return fPath.get()->getBounds();
133  case kEmpty_Type:
134  return kEmpty;
135  default:
136  SkDEBUGFAIL("Unexpected type.");
137  return kEmpty;
138  }
139  }
140 
145  bool contains(const SkRect& rect) const {
146  switch (fType) {
147  case kRect_Type:
148  return this->getRect().contains(rect);
149  case kRRect_Type:
150  return fRRect.contains(rect);
151  case kPath_Type:
152  return fPath.get()->conservativelyContainsRect(rect);
153  case kEmpty_Type:
154  return false;
155  default:
156  SkDEBUGFAIL("Unexpected type.");
157  return false;
158  }
159  }
160 
161  bool contains(const SkRRect& rrect) const {
162  switch (fType) {
163  case kRect_Type:
164  return this->getRect().contains(rrect.getBounds());
165  case kRRect_Type:
166  // We don't currently have a generalized rrect-rrect containment.
167  return fRRect.contains(rrect.getBounds()) || rrect == fRRect;
168  case kPath_Type:
169  return fPath.get()->conservativelyContainsRect(rrect.getBounds());
170  case kEmpty_Type:
171  return false;
172  default:
173  SkDEBUGFAIL("Unexpected type.");
174  return false;
175  }
176  }
177 
181  bool isInverseFilled() const {
182  return kPath_Type == fType && fPath.get()->isInverseFillType();
183  }
184 
188  void replay(SkCanvasClipVisitor*) const;
189 
190 #ifdef SK_DEBUG
191 
195  void dump() const;
196 #endif
197 
198  private:
199  friend class SkClipStack;
200 
201  SkTLazy<SkPath> fPath;
202  SkRRect fRRect;
203  int fSaveCount; // save count of stack when this element was added.
204  SkRegion::Op fOp;
205  Type fType;
206  bool fDoAA;
207 
208  /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
209  bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
210  conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
211  drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
212  occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
213  box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
214  the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
215  infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
216  can capture the cancelling out of the extensions to infinity when two inverse filled
217  clips are Booleaned together. */
218  SkClipStack::BoundsType fFiniteBoundType;
219  SkRect fFiniteBound;
220 
221  // When element is applied to the previous elements in the stack is the result known to be
222  // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
223  bool fIsIntersectionOfRects;
224 
225  int fGenID;
226 
227  Element(int saveCount) {
228  this->initCommon(saveCount, SkRegion::kReplace_Op, false);
229  this->setEmpty();
230  }
231 
232  Element(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) {
233  this->initRRect(saveCount, rrect, op, doAA);
234  }
235 
236  Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
237  this->initRect(saveCount, rect, op, doAA);
238  }
239 
240  Element(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) {
241  this->initPath(saveCount, path, op, doAA);
242  }
243 
244  void initCommon(int saveCount, SkRegion::Op op, bool doAA) {
245  fSaveCount = saveCount;
246  fOp = op;
247  fDoAA = doAA;
248  // A default of inside-out and empty bounds means the bounds are effectively void as it
249  // indicates that nothing is known to be outside the clip.
250  fFiniteBoundType = kInsideOut_BoundsType;
251  fFiniteBound.setEmpty();
252  fIsIntersectionOfRects = false;
253  fGenID = kInvalidGenID;
254  }
255 
256  void initRect(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
257  fRRect.setRect(rect);
258  fType = kRect_Type;
259  this->initCommon(saveCount, op, doAA);
260  }
261 
262  void initRRect(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) {
263  SkRRect::Type type = rrect.getType();
264  fRRect = rrect;
265  if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
266  fType = kRect_Type;
267  } else {
268  fType = kRRect_Type;
269  }
270  this->initCommon(saveCount, op, doAA);
271  }
272 
273  void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA);
274 
275  void setEmpty();
276 
277  // All Element methods below are only used within SkClipStack.cpp
278  inline void checkEmpty() const;
279  inline bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const;
280  /* This method checks to see if two rect clips can be safely merged into one. The issue here
281  is that to be strictly correct all the edges of the resulting rect must have the same
282  anti-aliasing. */
283  bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
286  void updateBoundAndGenID(const Element* prior);
287  // The different combination of fill & inverse fill when combining bounding boxes
288  enum FillCombo {
289  kPrev_Cur_FillCombo,
290  kPrev_InvCur_FillCombo,
291  kInvPrev_Cur_FillCombo,
292  kInvPrev_InvCur_FillCombo
293  };
294  // per-set operation functions used by updateBoundAndGenID().
295  inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
296  inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
297  inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
298  inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
299  inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
300  };
301 
302  SkClipStack();
303  SkClipStack(const SkClipStack& b);
304  explicit SkClipStack(const SkRect& r);
305  explicit SkClipStack(const SkIRect& r);
306  ~SkClipStack();
307 
308  SkClipStack& operator=(const SkClipStack& b);
309  bool operator==(const SkClipStack& b) const;
310  bool operator!=(const SkClipStack& b) const { return !(*this == b); }
311 
312  void reset();
313 
314  int getSaveCount() const { return fSaveCount; }
315  void save();
316  void restore();
317 
327  void getBounds(SkRect* canvFiniteBound,
328  BoundsType* boundType,
329  bool* isIntersectionOfRects = NULL) const;
330 
336  bool quickContains(const SkRect& devRect) const {
337  return this->isWideOpen() || this->internalQuickContains(devRect);
338  }
339 
340  bool quickContains(const SkRRect& devRRect) const {
341  return this->isWideOpen() || this->internalQuickContains(devRRect);
342  }
343 
348  bool asPath(SkPath* path) const;
349 
350  void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
351  SkRect r;
352  r.set(ir);
353  this->clipDevRect(r, op, false);
354  }
355  void clipDevRect(const SkRect&, SkRegion::Op, bool doAA);
356  void clipDevRRect(const SkRRect&, SkRegion::Op, bool doAA);
357  void clipDevPath(const SkPath&, SkRegion::Op, bool doAA);
358  // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
359  void clipEmpty();
360 
365  bool isWideOpen() const { return this->getTopmostGenID() == kWideOpenGenID; }
366 
371  static const int32_t kInvalidGenID = 0;
372  static const int32_t kEmptyGenID = 1; // no pixels writeable
375  static const int32_t kWideOpenGenID = 2; // all pixels writeable
376 
377  int32_t getTopmostGenID() const;
378 
379 #ifdef SK_DEBUG
380 
384  void dump() const;
385 #endif
386 
387 public:
388  class Iter {
389  public:
390  enum IterStart {
391  kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
392  kTop_IterStart = SkDeque::Iter::kBack_IterStart
393  };
394 
398  Iter();
399 
400  Iter(const SkClipStack& stack, IterStart startLoc);
401 
406  const Element* next();
407  const Element* prev();
408 
413  const Element* skipToTopmost(SkRegion::Op op);
414 
418  void reset(const SkClipStack& stack, IterStart startLoc);
419 
420  private:
421  const SkClipStack* fStack;
422  SkDeque::Iter fIter;
423  };
424 
429  class B2TIter : private Iter {
430  public:
431  B2TIter() {}
432 
437  B2TIter(const SkClipStack& stack)
438  : INHERITED(stack, kBottom_IterStart) {
439  }
440 
441  using Iter::next;
442 
447  void reset(const SkClipStack& stack) {
448  this->INHERITED::reset(stack, kBottom_IterStart);
449  }
450 
451  private:
452 
453  typedef Iter INHERITED;
454  };
455 
469  void getConservativeBounds(int offsetX,
470  int offsetY,
471  int maxWidth,
472  int maxHeight,
473  SkRect* devBounds,
474  bool* isIntersectionOfRects = NULL) const;
475 
476 private:
477  friend class Iter;
478 
479  SkDeque fDeque;
480  int fSaveCount;
481 
482  // Generation ID for the clip stack. This is incremented for each
483  // clipDevRect and clipDevPath call. 0 is reserved to indicate an
484  // invalid ID.
485  static int32_t gGenID;
486 
487  bool internalQuickContains(const SkRect& devRect) const;
488  bool internalQuickContains(const SkRRect& devRRect) const;
489 
493  void pushElement(const Element& element);
494 
498  void restoreTo(int saveCount);
499 
503  static int32_t GetNextGenID();
504 };
505 
506 #endif
const SkPath & getPath() const
Call if getType() is kRRect to get the round-rect.
Definition: SkClipStack.h:84
The SkRRect class represents a rounded rect with a potentially different radii for each corner...
Definition: SkRRect.h:48
The SkPath class encapsulates compound (multiple contour) geometric paths consisting of straight line...
Definition: SkPath.h:27
SkRegion::Op getOp() const
Call to get the element as a path, regardless of its type.
Definition: SkClipStack.h:96
const SkRect & getBounds() const
Gets the bounds of the clip element, either the rect or path bounds.
Definition: SkClipStack.h:125
void reset(const SkClipStack &stack)
Wrap Iter::reset to force initialization to the beginning of the deque/bottom of the stack...
Definition: SkClipStack.h:447
Type
Enum to capture the various possible subtypes of RR.
Definition: SkRRect.h:58
bool isAA() const
If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased when it is ra...
Definition: SkClipStack.h:106
bool isInverseFilled() const
Is the clip shape inverse filled.
Definition: SkClipStack.h:181
bool quickContains(const SkRect &devRect) const
Returns true if the input (r)rect in device space is entirely contained by the clip.
Definition: SkClipStack.h:336
Definition: SkClipStack.h:388
int32_t getGenID() const
The GenID can be used by clip stack clients to cache representations of the clip. ...
Definition: SkClipStack.h:119
Type getType() const
Call to get the save count associated with this clip element.
Definition: SkClipStack.h:78
static const int32_t kInvalidGenID
The generation ID has three reserved values to indicate special (potentially ignorable) cases...
Definition: SkClipStack.h:371
The RR is actually a (non-empty) oval (i.e., all x radii are equal and >= width/2 and all the y radii...
Definition: SkRRect.h:64
const SkRect & getRect() const
Call if getType() is not kEmpty to get the set operation used to combine this element.
Definition: SkClipStack.h:90
Type
Definition: SkClipStack.h:41
void dump() const
Dumps the contents of the clip stack to SkDebugf.
bool contains(const SkRect &rect) const
Conservatively checks whether the clip shape contains the rect param.
Definition: SkClipStack.h:145
Definition: SkDeque.h:26
bool isWideOpen() const
isWideOpen returns true if the clip state corresponds to the infinite plane (i.e., draws are not limited at all)
Definition: SkClipStack.h:365
replace the dst region with the op region
Definition: SkRegion.h:246
Type getType() const
Returns the RR's sub type.
Definition: SkRRect.h:92
Definition: SkRect.h:390
The RR is actually a (non-empty) rect (i.e., at least one radius at each corner is zero) ...
Definition: SkRRect.h:60
const SkRRect & getRRect() const
Call if getType() is kRect to get the rect.
Definition: SkClipStack.h:87
Definition: SkClipStack.h:39
The B2TIter iterates from the bottom of the stack to the top.
Definition: SkClipStack.h:429
Definition: SkDeque.h:65
SkIRect holds four 32 bit integer coordinates for a rectangle.
Definition: SkRect.h:20
B2TIter(const SkClipStack &stack)
Wrap Iter's 2 parameter ctor to force initialization to the beginning of the deque/bottom of the stac...
Definition: SkClipStack.h:437
Definition: SkRefCnt.h:221
Definition: SkClipStack.h:26
Op
The logical operations that can be performed when combining two regions.
Definition: SkRegion.h:239
int getSaveCount() const
Call if getType() is kPath to get the path.
Definition: SkClipStack.h:81
Definition: SkCanvas.h:1687
void getBounds(SkRect *canvFiniteBound, BoundsType *boundType, bool *isIntersectionOfRects=NULL) const
getBounds places the current finite bound in its first parameter.
bool asPath(SkPath *path) const
Flattens the clip stack into a single SkPath.