blob: fdbc59b72c9cac3fae5307460f24318659a9e106 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkCanvas.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkPath.h"
13#include "include/core/SkRRect.h"
14#include "include/core/SkRect.h"
15#include "include/core/SkRegion.h"
Michael Ludwig4e221bd2020-06-05 11:29:36 -040016#include "include/core/SkShader.h"
Mike Reed4d170012020-02-17 16:51:14 -050017#include "include/private/SkDeque.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/core/SkClipOpPriv.h"
Ben Wagner21bca282019-05-15 10:15:52 -040019#include "src/core/SkMessageBus.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "src/core/SkTLazy.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000021
Brian Salomon19f0ed52017-01-06 13:54:58 -050022#if SK_SUPPORT_GPU
Robert Phillips427966a2018-12-20 17:20:43 -050023class GrProxyProvider;
24
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "include/private/GrResourceKey.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050026#endif
27
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000028// Because a single save/restore state can have multiple clips, this class
29// stores the stack depth (fSaveCount) and clips (fDeque) separately.
30// Each clip in fDeque stores the stack state to which it belongs
31// (i.e., the fSaveCount in force when it was added). Restores are thus
32// implemented by removing clips from fDeque that have an fSaveCount larger
33// then the freshly decremented count.
Mike Reed7ba4d712017-03-10 00:21:52 -050034class SkClipStack {
reed@google.com5c3d1472011-02-22 19:12:23 +000035public:
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000036 enum BoundsType {
37 // The bounding box contains all the pixels that can be written to
38 kNormal_BoundsType,
39 // The bounding box contains all the pixels that cannot be written to.
40 // The real bound extends out to infinity and all the pixels outside
41 // of the bound can be written to. Note that some of the pixels inside
42 // the bound may also be writeable but all pixels that cannot be
43 // written to are guaranteed to be inside.
44 kInsideOut_BoundsType
45 };
46
Brian Salomonf3b46e52017-08-30 11:37:57 -040047 /**
48 * An element of the clip stack. It represents a shape combined with the prevoius clip using a
49 * set operator. Each element can be antialiased or not.
50 */
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000051 class Element {
52 public:
Brian Salomonf3b46e52017-08-30 11:37:57 -040053 /** This indicates the shape type of the clip element in device space. */
54 enum class DeviceSpaceType {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000055 //!< This element makes the clip empty (regardless of previous elements).
Brian Salomonf3b46e52017-08-30 11:37:57 -040056 kEmpty,
57 //!< This element combines a device space rect with the current clip.
58 kRect,
59 //!< This element combines a device space round-rect with the current clip.
60 kRRect,
61 //!< This element combines a device space path with the current clip.
62 kPath,
Michael Ludwig4e221bd2020-06-05 11:29:36 -040063 //!< This element does not have geometry, but applies a shader to the clip
64 kShader,
bsalomonb6b02522014-06-09 07:59:06 -070065
Michael Ludwig4e221bd2020-06-05 11:29:36 -040066 kLastType = kShader
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000067 };
Brian Salomonf3b46e52017-08-30 11:37:57 -040068 static const int kTypeCnt = (int)DeviceSpaceType::kLastType + 1;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000069
70 Element() {
Mike Reed14113bc2017-05-10 14:13:20 -040071 this->initCommon(0, kReplace_SkClipOp, false);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000072 this->setEmpty();
73 }
74
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000075 Element(const Element&);
76
Brian Salomon2e866342017-08-28 12:38:58 -040077 Element(const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
78 this->initRect(0, rect, m, op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000079 }
80
Brian Salomon2e866342017-08-28 12:38:58 -040081 Element(const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
82 this->initRRect(0, rrect, m, op, doAA);
83 }
84
85 Element(const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
86 this->initPath(0, path, m, op, doAA);
87 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000088
Michael Ludwig4e221bd2020-06-05 11:29:36 -040089 Element(sk_sp<SkShader> shader) {
90 this->initShader(0, std::move(shader));
91 }
92
Robert Phillips427966a2018-12-20 17:20:43 -050093 ~Element();
Brian Salomon19f0ed52017-01-06 13:54:58 -050094
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000095 bool operator== (const Element& element) const;
bsalomon@google.com8182fa02012-12-04 14:06:06 +000096 bool operator!= (const Element& element) const { return !(*this == element); }
97
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000098 //!< Call to get the type of the clip element.
Brian Salomonf3b46e52017-08-30 11:37:57 -040099 DeviceSpaceType getDeviceSpaceType() const { return fDeviceSpaceType; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000100
fmalitac3b589a2014-06-05 12:40:07 -0700101 //!< Call to get the save count associated with this clip element.
102 int getSaveCount() const { return fSaveCount; }
103
Brian Salomonf3b46e52017-08-30 11:37:57 -0400104 //!< Call if getDeviceSpaceType() is kPath to get the path.
105 const SkPath& getDeviceSpacePath() const {
106 SkASSERT(DeviceSpaceType::kPath == fDeviceSpaceType);
107 return *fDeviceSpacePath.get();
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000108 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000109
Brian Salomonf3b46e52017-08-30 11:37:57 -0400110 //!< Call if getDeviceSpaceType() is kRRect to get the round-rect.
111 const SkRRect& getDeviceSpaceRRect() const {
112 SkASSERT(DeviceSpaceType::kRRect == fDeviceSpaceType);
113 return fDeviceSpaceRRect;
114 }
115
116 //!< Call if getDeviceSpaceType() is kRect to get the rect.
117 const SkRect& getDeviceSpaceRect() const {
118 SkASSERT(DeviceSpaceType::kRect == fDeviceSpaceType &&
119 (fDeviceSpaceRRect.isRect() || fDeviceSpaceRRect.isEmpty()));
120 return fDeviceSpaceRRect.getBounds();
121 }
122
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400123 //!<Call if getDeviceSpaceType() is kShader to get a reference to the clip shader.
124 sk_sp<SkShader> refShader() const {
125 return fShader;
126 }
127 const SkShader* getShader() const {
128 return fShader.get();
129 }
130
Brian Salomonf3b46e52017-08-30 11:37:57 -0400131 //!< Call if getDeviceSpaceType() is not kEmpty to get the set operation used to combine
132 //!< this element.
Mike Reedc1f77742016-12-09 09:00:50 -0500133 SkClipOp getOp() const { return fOp; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000134
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000135 //!< Call to get the element as a path, regardless of its type.
Brian Salomonf3b46e52017-08-30 11:37:57 -0400136 void asDeviceSpacePath(SkPath* path) const;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000137
cdaltonb893a4c2016-03-17 12:56:11 -0700138 //!< Call if getType() is not kPath to get the element as a round rect.
Brian Salomonf3b46e52017-08-30 11:37:57 -0400139 const SkRRect& asDeviceSpaceRRect() const {
140 SkASSERT(DeviceSpaceType::kPath != fDeviceSpaceType);
141 return fDeviceSpaceRRect;
142 }
cdaltonb893a4c2016-03-17 12:56:11 -0700143
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000144 /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
145 when it is rasterized. */
146 bool isAA() const { return fDoAA; }
147
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000148 //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
149 void invertShapeFillType();
150
151 //!< Sets the set operation represented by the element.
Mike Reedc1f77742016-12-09 09:00:50 -0500152 void setOp(SkClipOp op) { fOp = op; }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000153
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000154 /** The GenID can be used by clip stack clients to cache representations of the clip. The
155 ID corresponds to the set of clip elements up to and including this element within the
156 stack not to the element itself. That is the same clip path in different stacks will
157 have a different ID since the elements produce different clip result in the context of
158 their stacks. */
Robert Phillips806be2d2017-06-28 15:23:59 -0400159 uint32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000160
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000161 /**
162 * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
163 * is inverse filled is not considered.)
164 */
Brian Salomon5c727962017-08-21 12:37:36 -0400165 const SkRect& getBounds() const;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000166
167 /**
Brian Salomon5c727962017-08-21 12:37:36 -0400168 * Conservatively checks whether the clip shape contains the rect/rrect. (Whether the shape
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000169 * is inverse filled is not considered.)
170 */
Brian Salomon5c727962017-08-21 12:37:36 -0400171 bool contains(const SkRect& rect) const;
172 bool contains(const SkRRect& rrect) const;
bsalomon7f0d9f32016-08-15 14:49:10 -0700173
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000174 /**
175 * Is the clip shape inverse filled.
176 */
177 bool isInverseFilled() const {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400178 return DeviceSpaceType::kPath == fDeviceSpaceType &&
179 fDeviceSpacePath.get()->isInverseFillType();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000180 }
181
djsollenefe46d22016-04-29 06:41:35 -0700182#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700183 /**
184 * Dumps the element to SkDebugf. This is intended for Skia development debugging
185 * Don't rely on the existence of this function or the formatting of its output.
186 */
187 void dump() const;
188#endif
189
Brian Salomon19f0ed52017-01-06 13:54:58 -0500190#if SK_SUPPORT_GPU
191 /**
192 * This is used to purge any GPU resource cache items that become unreachable when
193 * the element is destroyed because their key is based on this element's gen ID.
194 */
Robert Phillips427966a2018-12-20 17:20:43 -0500195 void addResourceInvalidationMessage(GrProxyProvider* proxyProvider,
196 const GrUniqueKey& key) const {
197 SkASSERT(proxyProvider);
198
199 if (!fProxyProvider) {
200 fProxyProvider = proxyProvider;
201 }
202 SkASSERT(fProxyProvider == proxyProvider);
203
204 fKeysToInvalidate.push_back(key);
Brian Salomon19f0ed52017-01-06 13:54:58 -0500205 }
206#endif
207
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000208 private:
209 friend class SkClipStack;
210
Brian Salomonf3b46e52017-08-30 11:37:57 -0400211 SkTLazy<SkPath> fDeviceSpacePath;
212 SkRRect fDeviceSpaceRRect;
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400213 sk_sp<SkShader> fShader;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500214 int fSaveCount; // save count of stack when this element was added.
215 SkClipOp fOp;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400216 DeviceSpaceType fDeviceSpaceType;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500217 bool fDoAA;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000218
219 /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
220 bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
221 conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
222 drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
223 occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
224 box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
225 the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
226 infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
227 can capture the cancelling out of the extensions to infinity when two inverse filled
228 clips are Booleaned together. */
229 SkClipStack::BoundsType fFiniteBoundType;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500230 SkRect fFiniteBound;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000231
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000232 // When element is applied to the previous elements in the stack is the result known to be
233 // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
Brian Salomon19f0ed52017-01-06 13:54:58 -0500234 bool fIsIntersectionOfRects;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000235
Robert Phillips806be2d2017-06-28 15:23:59 -0400236 uint32_t fGenID;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500237#if SK_SUPPORT_GPU
Robert Phillips427966a2018-12-20 17:20:43 -0500238 mutable GrProxyProvider* fProxyProvider = nullptr;
239 mutable SkTArray<GrUniqueKey> fKeysToInvalidate;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500240#endif
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000241 Element(int saveCount) {
Mike Reed14113bc2017-05-10 14:13:20 -0400242 this->initCommon(saveCount, kReplace_SkClipOp, false);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000243 this->setEmpty();
244 }
245
Brian Salomon2e866342017-08-28 12:38:58 -0400246 Element(int saveCount, const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
247 this->initRRect(saveCount, rrect, m, op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000248 }
249
Brian Salomon2e866342017-08-28 12:38:58 -0400250 Element(int saveCount, const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
251 this->initRect(saveCount, rect, m, op, doAA);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000252 }
253
Brian Salomon2e866342017-08-28 12:38:58 -0400254 Element(int saveCount, const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
255 this->initPath(saveCount, path, m, op, doAA);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000256 }
257
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400258 Element(int saveCount, sk_sp<SkShader> shader) {
259 this->initShader(saveCount, std::move(shader));
260 }
261
Brian Salomon5c727962017-08-21 12:37:36 -0400262 void initCommon(int saveCount, SkClipOp op, bool doAA);
Brian Salomon2e866342017-08-28 12:38:58 -0400263 void initRect(int saveCount, const SkRect&, const SkMatrix&, SkClipOp, bool doAA);
264 void initRRect(int saveCount, const SkRRect&, const SkMatrix&, SkClipOp, bool doAA);
265 void initPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
266 void initAsPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400267 void initShader(int saveCount, sk_sp<SkShader>);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000268
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +0000269 void setEmpty();
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000270
271 // All Element methods below are only used within SkClipStack.cpp
272 inline void checkEmpty() const;
Mike Reedc1f77742016-12-09 09:00:50 -0500273 inline bool canBeIntersectedInPlace(int saveCount, SkClipOp op) const;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000274 /* 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 +0000275 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 +0000276 anti-aliasing. */
277 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
278 /** Determines possible finite bounds for the Element given the previous element of the
279 stack */
280 void updateBoundAndGenID(const Element* prior);
281 // The different combination of fill & inverse fill when combining bounding boxes
282 enum FillCombo {
283 kPrev_Cur_FillCombo,
284 kPrev_InvCur_FillCombo,
285 kInvPrev_Cur_FillCombo,
286 kInvPrev_InvCur_FillCombo
287 };
288 // per-set operation functions used by updateBoundAndGenID().
289 inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
290 inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
291 inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
292 inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
293 inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
294 };
295
reed@google.com5c3d1472011-02-22 19:12:23 +0000296 SkClipStack();
Mike Reedd37f22b2017-03-09 23:56:25 -0500297 SkClipStack(void* storage, size_t size);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000298 SkClipStack(const SkClipStack& b);
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000299 ~SkClipStack();
reed@google.com5c3d1472011-02-22 19:12:23 +0000300
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000301 SkClipStack& operator=(const SkClipStack& b);
302 bool operator==(const SkClipStack& b) const;
303 bool operator!=(const SkClipStack& b) const { return !(*this == b); }
304
reed@google.com5c3d1472011-02-22 19:12:23 +0000305 void reset();
306
307 int getSaveCount() const { return fSaveCount; }
308 void save();
309 void restore();
310
Mike Reedf880b682017-03-10 11:30:44 -0500311 class AutoRestore {
312 public:
313 AutoRestore(SkClipStack* cs, bool doSave)
314 : fCS(cs), fSaveCount(cs->getSaveCount())
315 {
316 if (doSave) {
317 fCS->save();
318 }
319 }
320 ~AutoRestore() {
321 SkASSERT(fCS->getSaveCount() >= fSaveCount); // no underflow
322 while (fCS->getSaveCount() > fSaveCount) {
323 fCS->restore();
324 }
325 }
326
327 private:
328 SkClipStack* fCS;
329 const int fSaveCount;
330 };
331
robertphillips@google.com607fe072012-07-24 13:54:00 +0000332 /**
333 * getBounds places the current finite bound in its first parameter. In its
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000334 * second, it indicates which kind of bound is being returned. If
robertphillips@google.com7b112892012-07-31 15:18:21 +0000335 * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000336 * pixels. If 'canvFiniteBound' is an inside out bounding box then it
robertphillips@google.com607fe072012-07-24 13:54:00 +0000337 * encloses all the un-writeable pixels and the true/normal bound is the
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000338 * infinite plane. isIntersectionOfRects is an optional parameter
robertphillips@google.com7b112892012-07-31 15:18:21 +0000339 * that is true if 'canvFiniteBound' resulted from an intersection of rects.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000340 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000341 void getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000342 BoundsType* boundType,
Ben Wagnera93a14a2017-08-28 10:34:05 -0400343 bool* isIntersectionOfRects = nullptr) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000344
Hal Canaryf3ee34f2017-02-07 16:58:28 -0500345 SkRect bounds(const SkIRect& deviceBounds) const;
346 bool isEmpty(const SkIRect& deviceBounds) const;
347
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000348 /**
bsalomon7f0d9f32016-08-15 14:49:10 -0700349 * Returns true if the input (r)rect in device space is entirely contained
350 * by the clip. A return value of false does not guarantee that the (r)rect
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000351 * is not contained by the clip.
352 */
reed4d2cce42016-08-22 13:03:47 -0700353 bool quickContains(const SkRect& devRect) const {
354 return this->isWideOpen() || this->internalQuickContains(devRect);
355 }
356
357 bool quickContains(const SkRRect& devRRect) const {
358 return this->isWideOpen() || this->internalQuickContains(devRRect);
359 }
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000360
Mike Reedc1f77742016-12-09 09:00:50 -0500361 void clipDevRect(const SkIRect& ir, SkClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000362 SkRect r;
363 r.set(ir);
Brian Salomona3b45d42016-10-03 11:36:16 -0400364 this->clipRect(r, SkMatrix::I(), op, false);
reed@google.com5c3d1472011-02-22 19:12:23 +0000365 }
Mike Reedc1f77742016-12-09 09:00:50 -0500366 void clipRect(const SkRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
367 void clipRRect(const SkRRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
368 void clipPath(const SkPath&, const SkMatrix& matrix, SkClipOp, bool doAA);
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400369 void clipShader(sk_sp<SkShader>);
reed@google.com0557d9e2012-08-16 15:59:59 +0000370 // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
371 void clipEmpty();
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500372 void setDeviceClipRestriction(const SkIRect& rect) {
Mike Reed4ec2b712016-12-13 09:13:31 -0500373 fClipRestrictionRect = SkRect::Make(rect);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500374 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000375
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000376 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000377 * isWideOpen returns true if the clip state corresponds to the infinite
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000378 * plane (i.e., draws are not limited at all)
379 */
reed4d2cce42016-08-22 13:03:47 -0700380 bool isWideOpen() const { return this->getTopmostGenID() == kWideOpenGenID; }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000381
robertphillips@google.com46f93502012-08-07 15:38:08 +0000382 /**
bsalomoncb31e512016-08-26 10:48:19 -0700383 * This method quickly and conservatively determines whether the entire stack is equivalent to
384 * intersection with a rrect given a bounds, where the rrect must not contain the entire bounds.
385 *
386 * @param bounds A bounds on what will be drawn through the clip. The clip only need be
387 * equivalent to a intersection with a rrect for draws within the bounds. The
388 * returned rrect must intersect the bounds but need not be contained by the
389 * bounds.
390 * @param rrect If return is true rrect will contain the rrect equivalent to the stack.
391 * @param aa If return is true aa will indicate whether the equivalent rrect clip is
392 * antialiased.
393 * @return true if the stack is equivalent to a single rrect intersect clip, false otherwise.
394 */
395 bool isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const;
396
397 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000398 * The generation ID has three reserved values to indicate special
bsalomon@google.come8ca6c62012-11-07 21:19:10 +0000399 * (potentially ignorable) cases
robertphillips@google.com46f93502012-08-07 15:38:08 +0000400 */
Robert Phillips806be2d2017-06-28 15:23:59 -0400401 static const uint32_t kInvalidGenID = 0; //!< Invalid id that is never returned by
402 //!< SkClipStack. Useful when caching clips
403 //!< based on GenID.
404 static const uint32_t kEmptyGenID = 1; // no pixels writeable
405 static const uint32_t kWideOpenGenID = 2; // all pixels writeable
robertphillips@google.com46f93502012-08-07 15:38:08 +0000406
Robert Phillips806be2d2017-06-28 15:23:59 -0400407 uint32_t getTopmostGenID() const;
robertphillips@google.com73e71022012-08-09 18:10:49 +0000408
djsollenefe46d22016-04-29 06:41:35 -0700409#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700410 /**
411 * Dumps the contents of the clip stack to SkDebugf. This is intended for Skia development
412 * debugging. Don't rely on the existence of this function or the formatting of its output.
413 */
414 void dump() const;
415#endif
416
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000417public:
418 class Iter {
reed@google.com5c3d1472011-02-22 19:12:23 +0000419 public:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000420 enum IterStart {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000421 kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
422 kTop_IterStart = SkDeque::Iter::kBack_IterStart
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000423 };
424
bsalomon@google.comd302f142011-03-03 13:54:13 +0000425 /**
426 * Creates an uninitialized iterator. Must be reset()
427 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000428 Iter();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000429
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000430 Iter(const SkClipStack& stack, IterStart startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000431
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000432 /**
433 * Return the clip element for this iterator. If next()/prev() returns NULL, then the
434 * iterator is done.
435 */
436 const Element* next();
437 const Element* prev();
reed@google.com5c3d1472011-02-22 19:12:23 +0000438
439 /**
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000440 * Moves the iterator to the topmost element with the specified RegionOp and returns that
441 * element. If no clip element with that op is found, the first element is returned.
reed@google.com5c3d1472011-02-22 19:12:23 +0000442 */
Mike Reedc1f77742016-12-09 09:00:50 -0500443 const Element* skipToTopmost(SkClipOp op);
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000444
445 /**
bsalomon@google.comd302f142011-03-03 13:54:13 +0000446 * Restarts the iterator on a clip stack.
447 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000448 void reset(const SkClipStack& stack, IterStart startLoc);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000449
reed@google.com5c3d1472011-02-22 19:12:23 +0000450 private:
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000451 const SkClipStack* fStack;
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000452 SkDeque::Iter fIter;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000453 };
454
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000455 /**
robertphillips@google.com80214e22012-07-20 15:33:18 +0000456 * The B2TIter iterates from the bottom of the stack to the top.
457 * It inherits privately from Iter to prevent access to reverse iteration.
458 */
459 class B2TIter : private Iter {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000460 public:
robertphillips@google.com80214e22012-07-20 15:33:18 +0000461 B2TIter() {}
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000462
463 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000464 * Wrap Iter's 2 parameter ctor to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000465 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000466 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000467 B2TIter(const SkClipStack& stack)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000468 : INHERITED(stack, kBottom_IterStart) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000469 }
470
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000471 using Iter::next;
472
473 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000474 * Wrap Iter::reset to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000475 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000476 */
477 void reset(const SkClipStack& stack) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000478 this->INHERITED::reset(stack, kBottom_IterStart);
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000479 }
480
481 private:
482
483 typedef Iter INHERITED;
reed@google.com5c3d1472011-02-22 19:12:23 +0000484 };
485
robertphillips@google.com607fe072012-07-24 13:54:00 +0000486 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000487 * GetConservativeBounds returns a conservative bound of the current clip.
488 * Since this could be the infinite plane (if inverse fills were involved) the
489 * maxWidth and maxHeight parameters can be used to limit the returned bound
robertphillips@google.com607fe072012-07-24 13:54:00 +0000490 * to the expected drawing area. Similarly, the offsetX and offsetY parameters
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000491 * allow the caller to offset the returned bound to account for translated
robertphillips@google.com607fe072012-07-24 13:54:00 +0000492 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000493 * the translation (+offsetX, +offsetY) is applied before the clamp to the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000494 * maximum rectangle: [0,maxWidth) x [0,maxHeight).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000495 * isIntersectionOfRects is an optional parameter that is true when
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000496 * 'devBounds' is the result of an intersection of rects. In this case
497 * 'devBounds' is the exact answer/clip.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000498 */
499 void getConservativeBounds(int offsetX,
500 int offsetY,
501 int maxWidth,
502 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000503 SkRect* devBounds,
Ben Wagnera93a14a2017-08-28 10:34:05 -0400504 bool* isIntersectionOfRects = nullptr) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000505
reed@google.com5c3d1472011-02-22 19:12:23 +0000506private:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000507 friend class Iter;
reed@google.com5c3d1472011-02-22 19:12:23 +0000508
509 SkDeque fDeque;
510 int fSaveCount;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000511
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500512 SkRect fClipRestrictionRect = SkRect::MakeEmpty();
robertphillips@google.com46f93502012-08-07 15:38:08 +0000513
reed4d2cce42016-08-22 13:03:47 -0700514 bool internalQuickContains(const SkRect& devRect) const;
515 bool internalQuickContains(const SkRRect& devRRect) const;
516
robertphillips@google.com46f93502012-08-07 15:38:08 +0000517 /**
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000518 * Helper for clipDevPath, etc.
519 */
520 void pushElement(const Element& element);
521
522 /**
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000523 * Restore the stack back to the specified save count.
524 */
525 void restoreTo(int saveCount);
526
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500527 inline bool hasClipRestriction(SkClipOp op) {
Mike Reed14113bc2017-05-10 14:13:20 -0400528 return op >= kUnion_SkClipOp && !fClipRestrictionRect.isEmpty();
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500529 }
530
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000531 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000532 * Return the next unique generation ID.
533 */
Robert Phillips806be2d2017-06-28 15:23:59 -0400534 static uint32_t GetNextGenID();
reed@google.com5c3d1472011-02-22 19:12:23 +0000535};
536
537#endif