blob: f34283dc279a5de7f57e6b0f2006100eab245a17 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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 */
reed@google.com5c3d1472011-02-22 19:12:23 +00008#ifndef SkClipStack_DEFINED
9#define SkClipStack_DEFINED
10
11#include "SkDeque.h"
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000012#include "SkPath.h"
13#include "SkRect.h"
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000014#include "SkRRect.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000015#include "SkRegion.h"
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000016#include "SkTLazy.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000017
fmalitac3b589a2014-06-05 12:40:07 -070018class SkCanvasClipVisitor;
reed@google.com5c3d1472011-02-22 19:12:23 +000019
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000020// 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.
joshualittde358a92015-02-05 08:19:35 -080026class SK_API SkClipStack : public SkNVRefCnt<SkClipStack> {
reed@google.com5c3d1472011-02-22 19:12:23 +000027public:
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000028 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,
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000046 //!< This element combines a round-rect with the current clip using a set operation
47 kRRect_Type,
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000048 //!< This element combines a path with the current clip using a set operation
49 kPath_Type,
bsalomonb6b02522014-06-09 07:59:06 -070050
51 kLastType = kPath_Type
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000052 };
bsalomonb6b02522014-06-09 07:59:06 -070053 static const int kTypeCnt = kLastType + 1;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000054
55 Element() {
56 this->initCommon(0, SkRegion::kReplace_Op, false);
57 this->setEmpty();
58 }
59
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000060 Element(const Element&);
61
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000062 Element(const SkRect& rect, SkRegion::Op op, bool doAA) {
63 this->initRect(0, rect, op, doAA);
64 }
65
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000066 Element(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
67 this->initRRect(0, rrect, op, doAA);
68 }
69
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000070 Element(const SkPath& path, SkRegion::Op op, bool doAA) {
71 this->initPath(0, path, op, doAA);
72 }
73
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000074 bool operator== (const Element& element) const;
bsalomon@google.com8182fa02012-12-04 14:06:06 +000075 bool operator!= (const Element& element) const { return !(*this == element); }
76
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000077 //!< Call to get the type of the clip element.
78 Type getType() const { return fType; }
79
fmalitac3b589a2014-06-05 12:40:07 -070080 //!< Call to get the save count associated with this clip element.
81 int getSaveCount() const { return fSaveCount; }
82
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000083 //!< Call if getType() is kPath to get the path.
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000084 const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return *fPath.get(); }
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000085
86 //!< Call if getType() is kRRect to get the round-rect.
87 const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000088
89 //!< Call if getType() is kRect to get the rect.
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +000090 const SkRect& getRect() const {
91 SkASSERT(kRect_Type == fType && (fRRect.isRect() || fRRect.isEmpty()));
92 return fRRect.getBounds();
93 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000094
95 //!< Call if getType() is not kEmpty to get the set operation used to combine this element.
96 SkRegion::Op getOp() const { return fOp; }
97
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000098 //!< Call to get the element as a path, regardless of its type.
99 void asPath(SkPath* path) const;
100
cdaltonb893a4c2016-03-17 12:56:11 -0700101 //!< Call if getType() is not kPath to get the element as a round rect.
102 const SkRRect& asRRect() const { SkASSERT(kPath_Type != fType); return fRRect; }
103
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000104 /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
105 when it is rasterized. */
106 bool isAA() const { return fDoAA; }
107
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000108 //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
109 void invertShapeFillType();
110
111 //!< Sets the set operation represented by the element.
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000112 void setOp(SkRegion::Op op) { fOp = op; }
113
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000114 /** The GenID can be used by clip stack clients to cache representations of the clip. The
115 ID corresponds to the set of clip elements up to and including this element within the
116 stack not to the element itself. That is the same clip path in different stacks will
117 have a different ID since the elements produce different clip result in the context of
118 their stacks. */
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000119 int32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000120
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000121 /**
122 * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
123 * is inverse filled is not considered.)
124 */
125 const SkRect& getBounds() const {
126 static const SkRect kEmpty = { 0, 0, 0, 0 };
127 switch (fType) {
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000128 case kRect_Type: // fallthrough
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000129 case kRRect_Type:
130 return fRRect.getBounds();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000131 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000132 return fPath.get()->getBounds();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000133 case kEmpty_Type:
134 return kEmpty;
135 default:
136 SkDEBUGFAIL("Unexpected type.");
137 return kEmpty;
138 }
139 }
140
141 /**
142 * Conservatively checks whether the clip shape contains the rect param. (Whether the shape
143 * is inverse filled is not considered.)
144 */
145 bool contains(const SkRect& rect) const {
146 switch (fType) {
147 case kRect_Type:
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000148 return this->getRect().contains(rect);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000149 case kRRect_Type:
150 return fRRect.contains(rect);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000151 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000152 return fPath.get()->conservativelyContainsRect(rect);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000153 case kEmpty_Type:
154 return false;
155 default:
156 SkDEBUGFAIL("Unexpected type.");
157 return false;
158 }
159 }
160
bsalomon7f0d9f32016-08-15 14:49:10 -0700161 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
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000178 /**
179 * Is the clip shape inverse filled.
180 */
181 bool isInverseFilled() const {
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000182 return kPath_Type == fType && fPath.get()->isInverseFillType();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000183 }
184
fmalitac3b589a2014-06-05 12:40:07 -0700185 /**
186 * Replay this clip into the visitor.
187 */
188 void replay(SkCanvasClipVisitor*) const;
189
djsollenefe46d22016-04-29 06:41:35 -0700190#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700191 /**
192 * Dumps the element to SkDebugf. This is intended for Skia development debugging
193 * Don't rely on the existence of this function or the formatting of its output.
194 */
195 void dump() const;
196#endif
197
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000198 private:
199 friend class SkClipStack;
200
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000201 SkTLazy<SkPath> fPath;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000202 SkRRect fRRect;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000203 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;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000220
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000221 // 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
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000232 Element(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) {
233 this->initRRect(saveCount, rrect, op, doAA);
234 }
235
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000236 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) {
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000257 fRRect.setRect(rect);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000258 fType = kRect_Type;
259 this->initCommon(saveCount, op, doAA);
260 }
261
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000262 void initRRect(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) {
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000263 SkRRect::Type type = rrect.getType();
264 fRRect = rrect;
265 if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000266 fType = kRect_Type;
267 } else {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000268 fType = kRRect_Type;
269 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000270 this->initCommon(saveCount, op, doAA);
271 }
272
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000273 void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA);
274
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +0000275 void setEmpty();
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000276
277 // All Element methods below are only used within SkClipStack.cpp
278 inline void checkEmpty() const;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000279 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
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000281 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 +0000282 anti-aliasing. */
283 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
284 /** Determines possible finite bounds for the Element given the previous element of the
285 stack */
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
reed@google.com5c3d1472011-02-22 19:12:23 +0000302 SkClipStack();
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000303 SkClipStack(const SkClipStack& b);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000304 explicit SkClipStack(const SkRect& r);
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000305 explicit SkClipStack(const SkIRect& r);
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000306 ~SkClipStack();
reed@google.com5c3d1472011-02-22 19:12:23 +0000307
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000308 SkClipStack& operator=(const SkClipStack& b);
309 bool operator==(const SkClipStack& b) const;
310 bool operator!=(const SkClipStack& b) const { return !(*this == b); }
311
reed@google.com5c3d1472011-02-22 19:12:23 +0000312 void reset();
313
314 int getSaveCount() const { return fSaveCount; }
315 void save();
316 void restore();
317
robertphillips@google.com607fe072012-07-24 13:54:00 +0000318 /**
319 * getBounds places the current finite bound in its first parameter. In its
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000320 * second, it indicates which kind of bound is being returned. If
robertphillips@google.com7b112892012-07-31 15:18:21 +0000321 * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000322 * pixels. If 'canvFiniteBound' is an inside out bounding box then it
robertphillips@google.com607fe072012-07-24 13:54:00 +0000323 * encloses all the un-writeable pixels and the true/normal bound is the
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000324 * infinite plane. isIntersectionOfRects is an optional parameter
robertphillips@google.com7b112892012-07-31 15:18:21 +0000325 * that is true if 'canvFiniteBound' resulted from an intersection of rects.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000326 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000327 void getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000328 BoundsType* boundType,
329 bool* isIntersectionOfRects = NULL) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000330
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000331 /**
bsalomon7f0d9f32016-08-15 14:49:10 -0700332 * Returns true if the input (r)rect in device space is entirely contained
333 * by the clip. A return value of false does not guarantee that the (r)rect
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000334 * is not contained by the clip.
335 */
reed4d2cce42016-08-22 13:03:47 -0700336 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 }
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000343
fmalita1a481fe2015-02-04 07:39:34 -0800344 /**
345 * Flattens the clip stack into a single SkPath. Returns true if any of
346 * the clip stack components requires anti-aliasing.
347 */
348 bool asPath(SkPath* path) const;
349
reed@google.com115d9312012-05-16 18:50:40 +0000350 void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000351 SkRect r;
352 r.set(ir);
reed@google.com00177082011-10-12 14:34:30 +0000353 this->clipDevRect(r, op, false);
reed@google.com5c3d1472011-02-22 19:12:23 +0000354 }
reed@google.com00177082011-10-12 14:34:30 +0000355 void clipDevRect(const SkRect&, SkRegion::Op, bool doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000356 void clipDevRRect(const SkRRect&, SkRegion::Op, bool doAA);
reed@google.com00177082011-10-12 14:34:30 +0000357 void clipDevPath(const SkPath&, SkRegion::Op, bool doAA);
reed@google.com0557d9e2012-08-16 15:59:59 +0000358 // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
359 void clipEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000360
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000361 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000362 * isWideOpen returns true if the clip state corresponds to the infinite
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000363 * plane (i.e., draws are not limited at all)
364 */
reed4d2cce42016-08-22 13:03:47 -0700365 bool isWideOpen() const { return this->getTopmostGenID() == kWideOpenGenID; }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000366
robertphillips@google.com46f93502012-08-07 15:38:08 +0000367 /**
bsalomoncb31e512016-08-26 10:48:19 -0700368 * This method quickly and conservatively determines whether the entire stack is equivalent to
369 * intersection with a rrect given a bounds, where the rrect must not contain the entire bounds.
370 *
371 * @param bounds A bounds on what will be drawn through the clip. The clip only need be
372 * equivalent to a intersection with a rrect for draws within the bounds. The
373 * returned rrect must intersect the bounds but need not be contained by the
374 * bounds.
375 * @param rrect If return is true rrect will contain the rrect equivalent to the stack.
376 * @param aa If return is true aa will indicate whether the equivalent rrect clip is
377 * antialiased.
378 * @return true if the stack is equivalent to a single rrect intersect clip, false otherwise.
379 */
380 bool isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const;
381
382 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000383 * The generation ID has three reserved values to indicate special
bsalomon@google.come8ca6c62012-11-07 21:19:10 +0000384 * (potentially ignorable) cases
robertphillips@google.com46f93502012-08-07 15:38:08 +0000385 */
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000386 static const int32_t kInvalidGenID = 0; //!< Invalid id that is never returned by
387 //!< SkClipStack. Useful when caching clips
388 //!< based on GenID.
robertphillips@google.com46f93502012-08-07 15:38:08 +0000389 static const int32_t kEmptyGenID = 1; // no pixels writeable
390 static const int32_t kWideOpenGenID = 2; // all pixels writeable
391
robertphillips@google.com73e71022012-08-09 18:10:49 +0000392 int32_t getTopmostGenID() const;
393
djsollenefe46d22016-04-29 06:41:35 -0700394#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700395 /**
396 * Dumps the contents of the clip stack to SkDebugf. This is intended for Skia development
397 * debugging. Don't rely on the existence of this function or the formatting of its output.
398 */
399 void dump() const;
400#endif
401
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000402public:
403 class Iter {
reed@google.com5c3d1472011-02-22 19:12:23 +0000404 public:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000405 enum IterStart {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000406 kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
407 kTop_IterStart = SkDeque::Iter::kBack_IterStart
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000408 };
409
bsalomon@google.comd302f142011-03-03 13:54:13 +0000410 /**
411 * Creates an uninitialized iterator. Must be reset()
412 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000413 Iter();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000414
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000415 Iter(const SkClipStack& stack, IterStart startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000416
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000417 /**
418 * Return the clip element for this iterator. If next()/prev() returns NULL, then the
419 * iterator is done.
420 */
421 const Element* next();
422 const Element* prev();
reed@google.com5c3d1472011-02-22 19:12:23 +0000423
424 /**
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000425 * Moves the iterator to the topmost element with the specified RegionOp and returns that
426 * element. If no clip element with that op is found, the first element is returned.
reed@google.com5c3d1472011-02-22 19:12:23 +0000427 */
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000428 const Element* skipToTopmost(SkRegion::Op op);
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000429
430 /**
bsalomon@google.comd302f142011-03-03 13:54:13 +0000431 * Restarts the iterator on a clip stack.
432 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000433 void reset(const SkClipStack& stack, IterStart startLoc);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000434
reed@google.com5c3d1472011-02-22 19:12:23 +0000435 private:
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000436 const SkClipStack* fStack;
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000437 SkDeque::Iter fIter;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000438 };
439
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000440 /**
robertphillips@google.com80214e22012-07-20 15:33:18 +0000441 * The B2TIter iterates from the bottom of the stack to the top.
442 * It inherits privately from Iter to prevent access to reverse iteration.
443 */
444 class B2TIter : private Iter {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000445 public:
robertphillips@google.com80214e22012-07-20 15:33:18 +0000446 B2TIter() {}
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000447
448 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000449 * Wrap Iter's 2 parameter ctor to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000450 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000451 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000452 B2TIter(const SkClipStack& stack)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000453 : INHERITED(stack, kBottom_IterStart) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000454 }
455
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000456 using Iter::next;
457
458 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000459 * Wrap Iter::reset to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000460 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000461 */
462 void reset(const SkClipStack& stack) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000463 this->INHERITED::reset(stack, kBottom_IterStart);
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000464 }
465
466 private:
467
468 typedef Iter INHERITED;
reed@google.com5c3d1472011-02-22 19:12:23 +0000469 };
470
robertphillips@google.com607fe072012-07-24 13:54:00 +0000471 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000472 * GetConservativeBounds returns a conservative bound of the current clip.
473 * Since this could be the infinite plane (if inverse fills were involved) the
474 * maxWidth and maxHeight parameters can be used to limit the returned bound
robertphillips@google.com607fe072012-07-24 13:54:00 +0000475 * to the expected drawing area. Similarly, the offsetX and offsetY parameters
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000476 * allow the caller to offset the returned bound to account for translated
robertphillips@google.com607fe072012-07-24 13:54:00 +0000477 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000478 * the translation (+offsetX, +offsetY) is applied before the clamp to the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000479 * maximum rectangle: [0,maxWidth) x [0,maxHeight).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000480 * isIntersectionOfRects is an optional parameter that is true when
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000481 * 'devBounds' is the result of an intersection of rects. In this case
482 * 'devBounds' is the exact answer/clip.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000483 */
484 void getConservativeBounds(int offsetX,
485 int offsetY,
486 int maxWidth,
487 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000488 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000489 bool* isIntersectionOfRects = NULL) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000490
reed@google.com5c3d1472011-02-22 19:12:23 +0000491private:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000492 friend class Iter;
reed@google.com5c3d1472011-02-22 19:12:23 +0000493
494 SkDeque fDeque;
495 int fSaveCount;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000496
497 // Generation ID for the clip stack. This is incremented for each
498 // clipDevRect and clipDevPath call. 0 is reserved to indicate an
499 // invalid ID.
500 static int32_t gGenID;
501
reed4d2cce42016-08-22 13:03:47 -0700502 bool internalQuickContains(const SkRect& devRect) const;
503 bool internalQuickContains(const SkRRect& devRRect) const;
504
robertphillips@google.com46f93502012-08-07 15:38:08 +0000505 /**
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000506 * Helper for clipDevPath, etc.
507 */
508 void pushElement(const Element& element);
509
510 /**
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000511 * Restore the stack back to the specified save count.
512 */
513 void restoreTo(int saveCount);
514
515 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000516 * Return the next unique generation ID.
517 */
518 static int32_t GetNextGenID();
reed@google.com5c3d1472011-02-22 19:12:23 +0000519};
520
521#endif