blob: 1fb7b5c9daac5f015a739f23e7b213ebc4ff83ce [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
reed73603f32016-09-20 08:42:38 -07007
reed@google.com5c3d1472011-02-22 19:12:23 +00008#ifndef SkClipStack_DEFINED
9#define SkClipStack_DEFINED
10
reed73603f32016-09-20 08:42:38 -070011#include "SkCanvas.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000012#include "SkDeque.h"
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000013#include "SkPath.h"
14#include "SkRect.h"
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000015#include "SkRRect.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000016#include "SkRegion.h"
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000017#include "SkTLazy.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000018
fmalitac3b589a2014-06-05 12:40:07 -070019class SkCanvasClipVisitor;
reed@google.com5c3d1472011-02-22 19:12:23 +000020
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000021// Because a single save/restore state can have multiple clips, this class
22// stores the stack depth (fSaveCount) and clips (fDeque) separately.
23// Each clip in fDeque stores the stack state to which it belongs
24// (i.e., the fSaveCount in force when it was added). Restores are thus
25// implemented by removing clips from fDeque that have an fSaveCount larger
26// then the freshly decremented count.
joshualittde358a92015-02-05 08:19:35 -080027class SK_API SkClipStack : public SkNVRefCnt<SkClipStack> {
reed@google.com5c3d1472011-02-22 19:12:23 +000028public:
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000029 enum BoundsType {
30 // The bounding box contains all the pixels that can be written to
31 kNormal_BoundsType,
32 // The bounding box contains all the pixels that cannot be written to.
33 // The real bound extends out to infinity and all the pixels outside
34 // of the bound can be written to. Note that some of the pixels inside
35 // the bound may also be writeable but all pixels that cannot be
36 // written to are guaranteed to be inside.
37 kInsideOut_BoundsType
38 };
39
40 class Element {
41 public:
42 enum Type {
43 //!< This element makes the clip empty (regardless of previous elements).
44 kEmpty_Type,
45 //!< This element combines a rect with the current clip using a set operation
46 kRect_Type,
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000047 //!< This element combines a round-rect with the current clip using a set operation
48 kRRect_Type,
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000049 //!< This element combines a path with the current clip using a set operation
50 kPath_Type,
bsalomonb6b02522014-06-09 07:59:06 -070051
52 kLastType = kPath_Type
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000053 };
bsalomonb6b02522014-06-09 07:59:06 -070054 static const int kTypeCnt = kLastType + 1;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000055
56 Element() {
reed73603f32016-09-20 08:42:38 -070057 this->initCommon(0, SkCanvas::kReplace_Op, false);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000058 this->setEmpty();
59 }
60
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000061 Element(const Element&);
62
reed73603f32016-09-20 08:42:38 -070063 Element(const SkRect& rect, SkCanvas::ClipOp op, bool doAA) {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000064 this->initRect(0, rect, op, doAA);
65 }
66
reed73603f32016-09-20 08:42:38 -070067 Element(const SkRRect& rrect, SkCanvas::ClipOp op, bool doAA) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000068 this->initRRect(0, rrect, op, doAA);
69 }
70
reed73603f32016-09-20 08:42:38 -070071 Element(const SkPath& path, SkCanvas::ClipOp op, bool doAA) {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000072 this->initPath(0, path, op, doAA);
73 }
74
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000075 bool operator== (const Element& element) const;
bsalomon@google.com8182fa02012-12-04 14:06:06 +000076 bool operator!= (const Element& element) const { return !(*this == element); }
77
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000078 //!< Call to get the type of the clip element.
79 Type getType() const { return fType; }
80
fmalitac3b589a2014-06-05 12:40:07 -070081 //!< Call to get the save count associated with this clip element.
82 int getSaveCount() const { return fSaveCount; }
83
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000084 //!< Call if getType() is kPath to get the path.
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000085 const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return *fPath.get(); }
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000086
87 //!< Call if getType() is kRRect to get the round-rect.
88 const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000089
90 //!< Call if getType() is kRect to get the rect.
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +000091 const SkRect& getRect() const {
92 SkASSERT(kRect_Type == fType && (fRRect.isRect() || fRRect.isEmpty()));
93 return fRRect.getBounds();
94 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000095
96 //!< Call if getType() is not kEmpty to get the set operation used to combine this element.
reed73603f32016-09-20 08:42:38 -070097 SkCanvas::ClipOp getOp() const { return fOp; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000098
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000099 //!< Call to get the element as a path, regardless of its type.
100 void asPath(SkPath* path) const;
101
cdaltonb893a4c2016-03-17 12:56:11 -0700102 //!< Call if getType() is not kPath to get the element as a round rect.
103 const SkRRect& asRRect() const { SkASSERT(kPath_Type != fType); return fRRect; }
104
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000105 /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
106 when it is rasterized. */
107 bool isAA() const { return fDoAA; }
108
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000109 //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
110 void invertShapeFillType();
111
112 //!< Sets the set operation represented by the element.
reed73603f32016-09-20 08:42:38 -0700113 void setOp(SkCanvas::ClipOp op) { fOp = op; }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000114
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000115 /** The GenID can be used by clip stack clients to cache representations of the clip. The
116 ID corresponds to the set of clip elements up to and including this element within the
117 stack not to the element itself. That is the same clip path in different stacks will
118 have a different ID since the elements produce different clip result in the context of
119 their stacks. */
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000120 int32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000121
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000122 /**
123 * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
124 * is inverse filled is not considered.)
125 */
126 const SkRect& getBounds() const {
127 static const SkRect kEmpty = { 0, 0, 0, 0 };
128 switch (fType) {
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000129 case kRect_Type: // fallthrough
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000130 case kRRect_Type:
131 return fRRect.getBounds();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000132 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000133 return fPath.get()->getBounds();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000134 case kEmpty_Type:
135 return kEmpty;
136 default:
137 SkDEBUGFAIL("Unexpected type.");
138 return kEmpty;
139 }
140 }
141
142 /**
143 * Conservatively checks whether the clip shape contains the rect param. (Whether the shape
144 * is inverse filled is not considered.)
145 */
146 bool contains(const SkRect& rect) const {
147 switch (fType) {
148 case kRect_Type:
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000149 return this->getRect().contains(rect);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000150 case kRRect_Type:
151 return fRRect.contains(rect);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000152 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000153 return fPath.get()->conservativelyContainsRect(rect);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000154 case kEmpty_Type:
155 return false;
156 default:
157 SkDEBUGFAIL("Unexpected type.");
158 return false;
159 }
160 }
161
bsalomon7f0d9f32016-08-15 14:49:10 -0700162 bool contains(const SkRRect& rrect) const {
163 switch (fType) {
164 case kRect_Type:
165 return this->getRect().contains(rrect.getBounds());
166 case kRRect_Type:
167 // We don't currently have a generalized rrect-rrect containment.
168 return fRRect.contains(rrect.getBounds()) || rrect == fRRect;
169 case kPath_Type:
170 return fPath.get()->conservativelyContainsRect(rrect.getBounds());
171 case kEmpty_Type:
172 return false;
173 default:
174 SkDEBUGFAIL("Unexpected type.");
175 return false;
176 }
177 }
178
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000179 /**
180 * Is the clip shape inverse filled.
181 */
182 bool isInverseFilled() const {
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000183 return kPath_Type == fType && fPath.get()->isInverseFillType();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000184 }
185
fmalitac3b589a2014-06-05 12:40:07 -0700186 /**
187 * Replay this clip into the visitor.
188 */
189 void replay(SkCanvasClipVisitor*) const;
190
djsollenefe46d22016-04-29 06:41:35 -0700191#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700192 /**
193 * Dumps the element to SkDebugf. This is intended for Skia development debugging
194 * Don't rely on the existence of this function or the formatting of its output.
195 */
196 void dump() const;
197#endif
198
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000199 private:
200 friend class SkClipStack;
201
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000202 SkTLazy<SkPath> fPath;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000203 SkRRect fRRect;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000204 int fSaveCount; // save count of stack when this element was added.
reed73603f32016-09-20 08:42:38 -0700205 SkCanvas::ClipOp fOp;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000206 Type fType;
207 bool fDoAA;
208
209 /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
210 bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
211 conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
212 drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
213 occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
214 box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
215 the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
216 infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
217 can capture the cancelling out of the extensions to infinity when two inverse filled
218 clips are Booleaned together. */
219 SkClipStack::BoundsType fFiniteBoundType;
220 SkRect fFiniteBound;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000221
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000222 // When element is applied to the previous elements in the stack is the result known to be
223 // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
224 bool fIsIntersectionOfRects;
225
226 int fGenID;
227
228 Element(int saveCount) {
reed73603f32016-09-20 08:42:38 -0700229 this->initCommon(saveCount, SkCanvas::kReplace_Op, false);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000230 this->setEmpty();
231 }
232
reed73603f32016-09-20 08:42:38 -0700233 Element(int saveCount, const SkRRect& rrect, SkCanvas::ClipOp op, bool doAA) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000234 this->initRRect(saveCount, rrect, op, doAA);
235 }
236
reed73603f32016-09-20 08:42:38 -0700237 Element(int saveCount, const SkRect& rect, SkCanvas::ClipOp op, bool doAA) {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000238 this->initRect(saveCount, rect, op, doAA);
239 }
240
reed73603f32016-09-20 08:42:38 -0700241 Element(int saveCount, const SkPath& path, SkCanvas::ClipOp op, bool doAA) {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000242 this->initPath(saveCount, path, op, doAA);
243 }
244
reed73603f32016-09-20 08:42:38 -0700245 void initCommon(int saveCount, SkCanvas::ClipOp op, bool doAA) {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000246 fSaveCount = saveCount;
247 fOp = op;
248 fDoAA = doAA;
249 // A default of inside-out and empty bounds means the bounds are effectively void as it
250 // indicates that nothing is known to be outside the clip.
251 fFiniteBoundType = kInsideOut_BoundsType;
252 fFiniteBound.setEmpty();
253 fIsIntersectionOfRects = false;
254 fGenID = kInvalidGenID;
255 }
256
reed73603f32016-09-20 08:42:38 -0700257 void initRect(int saveCount, const SkRect& rect, SkCanvas::ClipOp op, bool doAA) {
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000258 fRRect.setRect(rect);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000259 fType = kRect_Type;
260 this->initCommon(saveCount, op, doAA);
261 }
262
reed73603f32016-09-20 08:42:38 -0700263 void initRRect(int saveCount, const SkRRect& rrect, SkCanvas::ClipOp op, bool doAA) {
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000264 SkRRect::Type type = rrect.getType();
265 fRRect = rrect;
266 if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000267 fType = kRect_Type;
268 } else {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000269 fType = kRRect_Type;
270 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000271 this->initCommon(saveCount, op, doAA);
272 }
273
reed73603f32016-09-20 08:42:38 -0700274 void initPath(int saveCount, const SkPath& path, SkCanvas::ClipOp op, bool doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000275
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +0000276 void setEmpty();
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000277
278 // All Element methods below are only used within SkClipStack.cpp
279 inline void checkEmpty() const;
reed73603f32016-09-20 08:42:38 -0700280 inline bool canBeIntersectedInPlace(int saveCount, SkCanvas::ClipOp op) const;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000281 /* This method checks to see if two rect clips can be safely merged into one. The issue here
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000282 is that to be strictly correct all the edges of the resulting rect must have the same
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000283 anti-aliasing. */
284 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
285 /** Determines possible finite bounds for the Element given the previous element of the
286 stack */
287 void updateBoundAndGenID(const Element* prior);
288 // The different combination of fill & inverse fill when combining bounding boxes
289 enum FillCombo {
290 kPrev_Cur_FillCombo,
291 kPrev_InvCur_FillCombo,
292 kInvPrev_Cur_FillCombo,
293 kInvPrev_InvCur_FillCombo
294 };
295 // per-set operation functions used by updateBoundAndGenID().
296 inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
297 inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
298 inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
299 inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
300 inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
301 };
302
reed@google.com5c3d1472011-02-22 19:12:23 +0000303 SkClipStack();
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000304 SkClipStack(const SkClipStack& b);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000305 explicit SkClipStack(const SkRect& r);
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000306 explicit SkClipStack(const SkIRect& r);
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000307 ~SkClipStack();
reed@google.com5c3d1472011-02-22 19:12:23 +0000308
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000309 SkClipStack& operator=(const SkClipStack& b);
310 bool operator==(const SkClipStack& b) const;
311 bool operator!=(const SkClipStack& b) const { return !(*this == b); }
312
reed@google.com5c3d1472011-02-22 19:12:23 +0000313 void reset();
314
315 int getSaveCount() const { return fSaveCount; }
316 void save();
317 void restore();
318
robertphillips@google.com607fe072012-07-24 13:54:00 +0000319 /**
320 * getBounds places the current finite bound in its first parameter. In its
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000321 * second, it indicates which kind of bound is being returned. If
robertphillips@google.com7b112892012-07-31 15:18:21 +0000322 * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000323 * pixels. If 'canvFiniteBound' is an inside out bounding box then it
robertphillips@google.com607fe072012-07-24 13:54:00 +0000324 * encloses all the un-writeable pixels and the true/normal bound is the
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000325 * infinite plane. isIntersectionOfRects is an optional parameter
robertphillips@google.com7b112892012-07-31 15:18:21 +0000326 * that is true if 'canvFiniteBound' resulted from an intersection of rects.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000327 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000328 void getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000329 BoundsType* boundType,
330 bool* isIntersectionOfRects = NULL) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000331
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000332 /**
bsalomon7f0d9f32016-08-15 14:49:10 -0700333 * Returns true if the input (r)rect in device space is entirely contained
334 * by the clip. A return value of false does not guarantee that the (r)rect
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000335 * is not contained by the clip.
336 */
reed4d2cce42016-08-22 13:03:47 -0700337 bool quickContains(const SkRect& devRect) const {
338 return this->isWideOpen() || this->internalQuickContains(devRect);
339 }
340
341 bool quickContains(const SkRRect& devRRect) const {
342 return this->isWideOpen() || this->internalQuickContains(devRRect);
343 }
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000344
fmalita1a481fe2015-02-04 07:39:34 -0800345 /**
346 * Flattens the clip stack into a single SkPath. Returns true if any of
347 * the clip stack components requires anti-aliasing.
348 */
349 bool asPath(SkPath* path) const;
350
reed73603f32016-09-20 08:42:38 -0700351 void clipDevRect(const SkIRect& ir, SkCanvas::ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000352 SkRect r;
353 r.set(ir);
reed@google.com00177082011-10-12 14:34:30 +0000354 this->clipDevRect(r, op, false);
reed@google.com5c3d1472011-02-22 19:12:23 +0000355 }
reed73603f32016-09-20 08:42:38 -0700356 void clipDevRect(const SkRect&, SkCanvas::ClipOp, bool doAA);
357 void clipDevRRect(const SkRRect&, SkCanvas::ClipOp, bool doAA);
358 void clipDevPath(const SkPath&, SkCanvas::ClipOp, bool doAA);
reed@google.com0557d9e2012-08-16 15:59:59 +0000359 // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
360 void clipEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000361
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000362 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000363 * isWideOpen returns true if the clip state corresponds to the infinite
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000364 * plane (i.e., draws are not limited at all)
365 */
reed4d2cce42016-08-22 13:03:47 -0700366 bool isWideOpen() const { return this->getTopmostGenID() == kWideOpenGenID; }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000367
robertphillips@google.com46f93502012-08-07 15:38:08 +0000368 /**
bsalomoncb31e512016-08-26 10:48:19 -0700369 * This method quickly and conservatively determines whether the entire stack is equivalent to
370 * intersection with a rrect given a bounds, where the rrect must not contain the entire bounds.
371 *
372 * @param bounds A bounds on what will be drawn through the clip. The clip only need be
373 * equivalent to a intersection with a rrect for draws within the bounds. The
374 * returned rrect must intersect the bounds but need not be contained by the
375 * bounds.
376 * @param rrect If return is true rrect will contain the rrect equivalent to the stack.
377 * @param aa If return is true aa will indicate whether the equivalent rrect clip is
378 * antialiased.
379 * @return true if the stack is equivalent to a single rrect intersect clip, false otherwise.
380 */
381 bool isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const;
382
383 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000384 * The generation ID has three reserved values to indicate special
bsalomon@google.come8ca6c62012-11-07 21:19:10 +0000385 * (potentially ignorable) cases
robertphillips@google.com46f93502012-08-07 15:38:08 +0000386 */
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000387 static const int32_t kInvalidGenID = 0; //!< Invalid id that is never returned by
388 //!< SkClipStack. Useful when caching clips
389 //!< based on GenID.
robertphillips@google.com46f93502012-08-07 15:38:08 +0000390 static const int32_t kEmptyGenID = 1; // no pixels writeable
391 static const int32_t kWideOpenGenID = 2; // all pixels writeable
392
robertphillips@google.com73e71022012-08-09 18:10:49 +0000393 int32_t getTopmostGenID() const;
394
djsollenefe46d22016-04-29 06:41:35 -0700395#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700396 /**
397 * Dumps the contents of the clip stack to SkDebugf. This is intended for Skia development
398 * debugging. Don't rely on the existence of this function or the formatting of its output.
399 */
400 void dump() const;
401#endif
402
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000403public:
404 class Iter {
reed@google.com5c3d1472011-02-22 19:12:23 +0000405 public:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000406 enum IterStart {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000407 kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
408 kTop_IterStart = SkDeque::Iter::kBack_IterStart
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000409 };
410
bsalomon@google.comd302f142011-03-03 13:54:13 +0000411 /**
412 * Creates an uninitialized iterator. Must be reset()
413 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000414 Iter();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000415
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000416 Iter(const SkClipStack& stack, IterStart startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000417
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000418 /**
419 * Return the clip element for this iterator. If next()/prev() returns NULL, then the
420 * iterator is done.
421 */
422 const Element* next();
423 const Element* prev();
reed@google.com5c3d1472011-02-22 19:12:23 +0000424
425 /**
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000426 * Moves the iterator to the topmost element with the specified RegionOp and returns that
427 * element. If no clip element with that op is found, the first element is returned.
reed@google.com5c3d1472011-02-22 19:12:23 +0000428 */
reed73603f32016-09-20 08:42:38 -0700429 const Element* skipToTopmost(SkCanvas::ClipOp op);
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000430
431 /**
bsalomon@google.comd302f142011-03-03 13:54:13 +0000432 * Restarts the iterator on a clip stack.
433 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000434 void reset(const SkClipStack& stack, IterStart startLoc);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000435
reed@google.com5c3d1472011-02-22 19:12:23 +0000436 private:
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000437 const SkClipStack* fStack;
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000438 SkDeque::Iter fIter;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000439 };
440
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000441 /**
robertphillips@google.com80214e22012-07-20 15:33:18 +0000442 * The B2TIter iterates from the bottom of the stack to the top.
443 * It inherits privately from Iter to prevent access to reverse iteration.
444 */
445 class B2TIter : private Iter {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000446 public:
robertphillips@google.com80214e22012-07-20 15:33:18 +0000447 B2TIter() {}
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000448
449 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000450 * Wrap Iter's 2 parameter ctor to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000451 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000452 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000453 B2TIter(const SkClipStack& stack)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000454 : INHERITED(stack, kBottom_IterStart) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000455 }
456
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000457 using Iter::next;
458
459 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000460 * Wrap Iter::reset to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000461 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000462 */
463 void reset(const SkClipStack& stack) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000464 this->INHERITED::reset(stack, kBottom_IterStart);
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000465 }
466
467 private:
468
469 typedef Iter INHERITED;
reed@google.com5c3d1472011-02-22 19:12:23 +0000470 };
471
robertphillips@google.com607fe072012-07-24 13:54:00 +0000472 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000473 * GetConservativeBounds returns a conservative bound of the current clip.
474 * Since this could be the infinite plane (if inverse fills were involved) the
475 * maxWidth and maxHeight parameters can be used to limit the returned bound
robertphillips@google.com607fe072012-07-24 13:54:00 +0000476 * to the expected drawing area. Similarly, the offsetX and offsetY parameters
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000477 * allow the caller to offset the returned bound to account for translated
robertphillips@google.com607fe072012-07-24 13:54:00 +0000478 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000479 * the translation (+offsetX, +offsetY) is applied before the clamp to the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000480 * maximum rectangle: [0,maxWidth) x [0,maxHeight).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000481 * isIntersectionOfRects is an optional parameter that is true when
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000482 * 'devBounds' is the result of an intersection of rects. In this case
483 * 'devBounds' is the exact answer/clip.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000484 */
485 void getConservativeBounds(int offsetX,
486 int offsetY,
487 int maxWidth,
488 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000489 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000490 bool* isIntersectionOfRects = NULL) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000491
reed@google.com5c3d1472011-02-22 19:12:23 +0000492private:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000493 friend class Iter;
reed@google.com5c3d1472011-02-22 19:12:23 +0000494
495 SkDeque fDeque;
496 int fSaveCount;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000497
498 // Generation ID for the clip stack. This is incremented for each
499 // clipDevRect and clipDevPath call. 0 is reserved to indicate an
500 // invalid ID.
501 static int32_t gGenID;
502
reed4d2cce42016-08-22 13:03:47 -0700503 bool internalQuickContains(const SkRect& devRect) const;
504 bool internalQuickContains(const SkRRect& devRRect) const;
505
robertphillips@google.com46f93502012-08-07 15:38:08 +0000506 /**
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000507 * Helper for clipDevPath, etc.
508 */
509 void pushElement(const Element& element);
510
511 /**
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000512 * Restore the stack back to the specified save count.
513 */
514 void restoreTo(int saveCount);
515
516 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000517 * Return the next unique generation ID.
518 */
519 static int32_t GetNextGenID();
reed@google.com5c3d1472011-02-22 19:12:23 +0000520};
521
522#endif