blob: 050f30ed4ef3f003c6b4fabb0266a5474de2c3f0 [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
Brian Salomon19f0ed52017-01-06 13:54:58 -050011#include "../private/SkMessageBus.h"
reed73603f32016-09-20 08:42:38 -070012#include "SkCanvas.h"
Mike Reed14113bc2017-05-10 14:13:20 -040013#include "SkClipOpPriv.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000014#include "SkDeque.h"
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000015#include "SkPath.h"
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000016#include "SkRRect.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050017#include "SkRect.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000018#include "SkRegion.h"
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000019#include "SkTLazy.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000020
Brian Salomon19f0ed52017-01-06 13:54:58 -050021#if SK_SUPPORT_GPU
22#include "GrResourceKey.h"
23#endif
24
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000025// Because a single save/restore state can have multiple clips, this class
26// stores the stack depth (fSaveCount) and clips (fDeque) separately.
27// Each clip in fDeque stores the stack state to which it belongs
28// (i.e., the fSaveCount in force when it was added). Restores are thus
29// implemented by removing clips from fDeque that have an fSaveCount larger
30// then the freshly decremented count.
Mike Reed7ba4d712017-03-10 00:21:52 -050031class SkClipStack {
reed@google.com5c3d1472011-02-22 19:12:23 +000032public:
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000033 enum BoundsType {
34 // The bounding box contains all the pixels that can be written to
35 kNormal_BoundsType,
36 // The bounding box contains all the pixels that cannot be written to.
37 // The real bound extends out to infinity and all the pixels outside
38 // of the bound can be written to. Note that some of the pixels inside
39 // the bound may also be writeable but all pixels that cannot be
40 // written to are guaranteed to be inside.
41 kInsideOut_BoundsType
42 };
43
Brian Salomonf3b46e52017-08-30 11:37:57 -040044 /**
45 * An element of the clip stack. It represents a shape combined with the prevoius clip using a
46 * set operator. Each element can be antialiased or not.
47 */
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000048 class Element {
49 public:
Brian Salomonf3b46e52017-08-30 11:37:57 -040050 /** This indicates the shape type of the clip element in device space. */
51 enum class DeviceSpaceType {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000052 //!< This element makes the clip empty (regardless of previous elements).
Brian Salomonf3b46e52017-08-30 11:37:57 -040053 kEmpty,
54 //!< This element combines a device space rect with the current clip.
55 kRect,
56 //!< This element combines a device space round-rect with the current clip.
57 kRRect,
58 //!< This element combines a device space path with the current clip.
59 kPath,
bsalomonb6b02522014-06-09 07:59:06 -070060
Brian Salomonf3b46e52017-08-30 11:37:57 -040061 kLastType = kPath
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000062 };
Brian Salomonf3b46e52017-08-30 11:37:57 -040063 static const int kTypeCnt = (int)DeviceSpaceType::kLastType + 1;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000064
65 Element() {
Mike Reed14113bc2017-05-10 14:13:20 -040066 this->initCommon(0, kReplace_SkClipOp, false);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000067 this->setEmpty();
68 }
69
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000070 Element(const Element&);
71
Brian Salomon2e866342017-08-28 12:38:58 -040072 Element(const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
73 this->initRect(0, rect, m, op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000074 }
75
Brian Salomon2e866342017-08-28 12:38:58 -040076 Element(const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
77 this->initRRect(0, rrect, m, op, doAA);
78 }
79
80 Element(const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
81 this->initPath(0, path, m, op, doAA);
82 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000083
Brian Salomon19f0ed52017-01-06 13:54:58 -050084 ~Element() {
85#if SK_SUPPORT_GPU
86 for (int i = 0; i < fMessages.count(); ++i) {
87 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(*fMessages[i]);
88 }
89#endif
90 }
91
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000092 bool operator== (const Element& element) const;
bsalomon@google.com8182fa02012-12-04 14:06:06 +000093 bool operator!= (const Element& element) const { return !(*this == element); }
94
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000095 //!< Call to get the type of the clip element.
Brian Salomonf3b46e52017-08-30 11:37:57 -040096 DeviceSpaceType getDeviceSpaceType() const { return fDeviceSpaceType; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000097
fmalitac3b589a2014-06-05 12:40:07 -070098 //!< Call to get the save count associated with this clip element.
99 int getSaveCount() const { return fSaveCount; }
100
Brian Salomonf3b46e52017-08-30 11:37:57 -0400101 //!< Call if getDeviceSpaceType() is kPath to get the path.
102 const SkPath& getDeviceSpacePath() const {
103 SkASSERT(DeviceSpaceType::kPath == fDeviceSpaceType);
104 return *fDeviceSpacePath.get();
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000105 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000106
Brian Salomonf3b46e52017-08-30 11:37:57 -0400107 //!< Call if getDeviceSpaceType() is kRRect to get the round-rect.
108 const SkRRect& getDeviceSpaceRRect() const {
109 SkASSERT(DeviceSpaceType::kRRect == fDeviceSpaceType);
110 return fDeviceSpaceRRect;
111 }
112
113 //!< Call if getDeviceSpaceType() is kRect to get the rect.
114 const SkRect& getDeviceSpaceRect() const {
115 SkASSERT(DeviceSpaceType::kRect == fDeviceSpaceType &&
116 (fDeviceSpaceRRect.isRect() || fDeviceSpaceRRect.isEmpty()));
117 return fDeviceSpaceRRect.getBounds();
118 }
119
120 //!< Call if getDeviceSpaceType() is not kEmpty to get the set operation used to combine
121 //!< this element.
Mike Reedc1f77742016-12-09 09:00:50 -0500122 SkClipOp getOp() const { return fOp; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000123
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000124 //!< Call to get the element as a path, regardless of its type.
Brian Salomonf3b46e52017-08-30 11:37:57 -0400125 void asDeviceSpacePath(SkPath* path) const;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000126
cdaltonb893a4c2016-03-17 12:56:11 -0700127 //!< Call if getType() is not kPath to get the element as a round rect.
Brian Salomonf3b46e52017-08-30 11:37:57 -0400128 const SkRRect& asDeviceSpaceRRect() const {
129 SkASSERT(DeviceSpaceType::kPath != fDeviceSpaceType);
130 return fDeviceSpaceRRect;
131 }
cdaltonb893a4c2016-03-17 12:56:11 -0700132
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000133 /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
134 when it is rasterized. */
135 bool isAA() const { return fDoAA; }
136
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000137 //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
138 void invertShapeFillType();
139
140 //!< Sets the set operation represented by the element.
Mike Reedc1f77742016-12-09 09:00:50 -0500141 void setOp(SkClipOp op) { fOp = op; }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000142
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000143 /** The GenID can be used by clip stack clients to cache representations of the clip. The
144 ID corresponds to the set of clip elements up to and including this element within the
145 stack not to the element itself. That is the same clip path in different stacks will
146 have a different ID since the elements produce different clip result in the context of
147 their stacks. */
Robert Phillips806be2d2017-06-28 15:23:59 -0400148 uint32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000149
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000150 /**
151 * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
152 * is inverse filled is not considered.)
153 */
Brian Salomon5c727962017-08-21 12:37:36 -0400154 const SkRect& getBounds() const;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000155
156 /**
Brian Salomon5c727962017-08-21 12:37:36 -0400157 * Conservatively checks whether the clip shape contains the rect/rrect. (Whether the shape
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000158 * is inverse filled is not considered.)
159 */
Brian Salomon5c727962017-08-21 12:37:36 -0400160 bool contains(const SkRect& rect) const;
161 bool contains(const SkRRect& rrect) const;
bsalomon7f0d9f32016-08-15 14:49:10 -0700162
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000163 /**
164 * Is the clip shape inverse filled.
165 */
166 bool isInverseFilled() const {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400167 return DeviceSpaceType::kPath == fDeviceSpaceType &&
168 fDeviceSpacePath.get()->isInverseFillType();
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000169 }
170
djsollenefe46d22016-04-29 06:41:35 -0700171#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700172 /**
173 * Dumps the element to SkDebugf. This is intended for Skia development debugging
174 * Don't rely on the existence of this function or the formatting of its output.
175 */
176 void dump() const;
177#endif
178
Brian Salomon19f0ed52017-01-06 13:54:58 -0500179#if SK_SUPPORT_GPU
180 /**
181 * This is used to purge any GPU resource cache items that become unreachable when
182 * the element is destroyed because their key is based on this element's gen ID.
183 */
184 void addResourceInvalidationMessage(
185 std::unique_ptr<GrUniqueKeyInvalidatedMessage> msg) const {
186 fMessages.emplace_back(std::move(msg));
187 }
188#endif
189
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000190 private:
191 friend class SkClipStack;
192
Brian Salomonf3b46e52017-08-30 11:37:57 -0400193 SkTLazy<SkPath> fDeviceSpacePath;
194 SkRRect fDeviceSpaceRRect;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500195 int fSaveCount; // save count of stack when this element was added.
196 SkClipOp fOp;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400197 DeviceSpaceType fDeviceSpaceType;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500198 bool fDoAA;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000199
200 /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
201 bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
202 conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
203 drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
204 occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
205 box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
206 the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
207 infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
208 can capture the cancelling out of the extensions to infinity when two inverse filled
209 clips are Booleaned together. */
210 SkClipStack::BoundsType fFiniteBoundType;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500211 SkRect fFiniteBound;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000212
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000213 // When element is applied to the previous elements in the stack is the result known to be
214 // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
Brian Salomon19f0ed52017-01-06 13:54:58 -0500215 bool fIsIntersectionOfRects;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000216
Robert Phillips806be2d2017-06-28 15:23:59 -0400217 uint32_t fGenID;
Brian Salomon19f0ed52017-01-06 13:54:58 -0500218#if SK_SUPPORT_GPU
219 mutable SkTArray<std::unique_ptr<GrUniqueKeyInvalidatedMessage>> fMessages;
220#endif
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000221 Element(int saveCount) {
Mike Reed14113bc2017-05-10 14:13:20 -0400222 this->initCommon(saveCount, kReplace_SkClipOp, false);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000223 this->setEmpty();
224 }
225
Brian Salomon2e866342017-08-28 12:38:58 -0400226 Element(int saveCount, const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) {
227 this->initRRect(saveCount, rrect, m, op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000228 }
229
Brian Salomon2e866342017-08-28 12:38:58 -0400230 Element(int saveCount, const SkRect& rect, const SkMatrix& m, SkClipOp op, bool doAA) {
231 this->initRect(saveCount, rect, m, op, doAA);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000232 }
233
Brian Salomon2e866342017-08-28 12:38:58 -0400234 Element(int saveCount, const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) {
235 this->initPath(saveCount, path, m, op, doAA);
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000236 }
237
Brian Salomon5c727962017-08-21 12:37:36 -0400238 void initCommon(int saveCount, SkClipOp op, bool doAA);
Brian Salomon2e866342017-08-28 12:38:58 -0400239 void initRect(int saveCount, const SkRect&, const SkMatrix&, SkClipOp, bool doAA);
240 void initRRect(int saveCount, const SkRRect&, const SkMatrix&, SkClipOp, bool doAA);
241 void initPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
242 void initAsPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000243
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +0000244 void setEmpty();
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000245
246 // All Element methods below are only used within SkClipStack.cpp
247 inline void checkEmpty() const;
Mike Reedc1f77742016-12-09 09:00:50 -0500248 inline bool canBeIntersectedInPlace(int saveCount, SkClipOp op) const;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000249 /* 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 +0000250 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 +0000251 anti-aliasing. */
252 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
253 /** Determines possible finite bounds for the Element given the previous element of the
254 stack */
255 void updateBoundAndGenID(const Element* prior);
256 // The different combination of fill & inverse fill when combining bounding boxes
257 enum FillCombo {
258 kPrev_Cur_FillCombo,
259 kPrev_InvCur_FillCombo,
260 kInvPrev_Cur_FillCombo,
261 kInvPrev_InvCur_FillCombo
262 };
263 // per-set operation functions used by updateBoundAndGenID().
264 inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
265 inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
266 inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
267 inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
268 inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
269 };
270
reed@google.com5c3d1472011-02-22 19:12:23 +0000271 SkClipStack();
Mike Reedd37f22b2017-03-09 23:56:25 -0500272 SkClipStack(void* storage, size_t size);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000273 SkClipStack(const SkClipStack& b);
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000274 ~SkClipStack();
reed@google.com5c3d1472011-02-22 19:12:23 +0000275
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000276 SkClipStack& operator=(const SkClipStack& b);
277 bool operator==(const SkClipStack& b) const;
278 bool operator!=(const SkClipStack& b) const { return !(*this == b); }
279
reed@google.com5c3d1472011-02-22 19:12:23 +0000280 void reset();
281
282 int getSaveCount() const { return fSaveCount; }
283 void save();
284 void restore();
285
Mike Reedf880b682017-03-10 11:30:44 -0500286 class AutoRestore {
287 public:
288 AutoRestore(SkClipStack* cs, bool doSave)
289 : fCS(cs), fSaveCount(cs->getSaveCount())
290 {
291 if (doSave) {
292 fCS->save();
293 }
294 }
295 ~AutoRestore() {
296 SkASSERT(fCS->getSaveCount() >= fSaveCount); // no underflow
297 while (fCS->getSaveCount() > fSaveCount) {
298 fCS->restore();
299 }
300 }
301
302 private:
303 SkClipStack* fCS;
304 const int fSaveCount;
305 };
306
robertphillips@google.com607fe072012-07-24 13:54:00 +0000307 /**
308 * getBounds places the current finite bound in its first parameter. In its
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000309 * second, it indicates which kind of bound is being returned. If
robertphillips@google.com7b112892012-07-31 15:18:21 +0000310 * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000311 * pixels. If 'canvFiniteBound' is an inside out bounding box then it
robertphillips@google.com607fe072012-07-24 13:54:00 +0000312 * encloses all the un-writeable pixels and the true/normal bound is the
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000313 * infinite plane. isIntersectionOfRects is an optional parameter
robertphillips@google.com7b112892012-07-31 15:18:21 +0000314 * that is true if 'canvFiniteBound' resulted from an intersection of rects.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000315 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000316 void getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000317 BoundsType* boundType,
Ben Wagnera93a14a2017-08-28 10:34:05 -0400318 bool* isIntersectionOfRects = nullptr) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000319
Hal Canaryf3ee34f2017-02-07 16:58:28 -0500320 SkRect bounds(const SkIRect& deviceBounds) const;
321 bool isEmpty(const SkIRect& deviceBounds) const;
322
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000323 /**
bsalomon7f0d9f32016-08-15 14:49:10 -0700324 * Returns true if the input (r)rect in device space is entirely contained
325 * by the clip. A return value of false does not guarantee that the (r)rect
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000326 * is not contained by the clip.
327 */
reed4d2cce42016-08-22 13:03:47 -0700328 bool quickContains(const SkRect& devRect) const {
329 return this->isWideOpen() || this->internalQuickContains(devRect);
330 }
331
332 bool quickContains(const SkRRect& devRRect) const {
333 return this->isWideOpen() || this->internalQuickContains(devRRect);
334 }
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000335
fmalita1a481fe2015-02-04 07:39:34 -0800336 /**
337 * Flattens the clip stack into a single SkPath. Returns true if any of
338 * the clip stack components requires anti-aliasing.
339 */
340 bool asPath(SkPath* path) const;
341
Mike Reedc1f77742016-12-09 09:00:50 -0500342 void clipDevRect(const SkIRect& ir, SkClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000343 SkRect r;
344 r.set(ir);
Brian Salomona3b45d42016-10-03 11:36:16 -0400345 this->clipRect(r, SkMatrix::I(), op, false);
reed@google.com5c3d1472011-02-22 19:12:23 +0000346 }
Mike Reedc1f77742016-12-09 09:00:50 -0500347 void clipRect(const SkRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
348 void clipRRect(const SkRRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
349 void clipPath(const SkPath&, const SkMatrix& matrix, SkClipOp, bool doAA);
reed@google.com0557d9e2012-08-16 15:59:59 +0000350 // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
351 void clipEmpty();
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500352 void setDeviceClipRestriction(const SkIRect& rect) {
Mike Reed4ec2b712016-12-13 09:13:31 -0500353 fClipRestrictionRect = SkRect::Make(rect);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500354 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000355
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000356 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000357 * isWideOpen returns true if the clip state corresponds to the infinite
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000358 * plane (i.e., draws are not limited at all)
359 */
reed4d2cce42016-08-22 13:03:47 -0700360 bool isWideOpen() const { return this->getTopmostGenID() == kWideOpenGenID; }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000361
robertphillips@google.com46f93502012-08-07 15:38:08 +0000362 /**
bsalomoncb31e512016-08-26 10:48:19 -0700363 * This method quickly and conservatively determines whether the entire stack is equivalent to
364 * intersection with a rrect given a bounds, where the rrect must not contain the entire bounds.
365 *
366 * @param bounds A bounds on what will be drawn through the clip. The clip only need be
367 * equivalent to a intersection with a rrect for draws within the bounds. The
368 * returned rrect must intersect the bounds but need not be contained by the
369 * bounds.
370 * @param rrect If return is true rrect will contain the rrect equivalent to the stack.
371 * @param aa If return is true aa will indicate whether the equivalent rrect clip is
372 * antialiased.
373 * @return true if the stack is equivalent to a single rrect intersect clip, false otherwise.
374 */
375 bool isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const;
376
377 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000378 * The generation ID has three reserved values to indicate special
bsalomon@google.come8ca6c62012-11-07 21:19:10 +0000379 * (potentially ignorable) cases
robertphillips@google.com46f93502012-08-07 15:38:08 +0000380 */
Robert Phillips806be2d2017-06-28 15:23:59 -0400381 static const uint32_t kInvalidGenID = 0; //!< Invalid id that is never returned by
382 //!< SkClipStack. Useful when caching clips
383 //!< based on GenID.
384 static const uint32_t kEmptyGenID = 1; // no pixels writeable
385 static const uint32_t kWideOpenGenID = 2; // all pixels writeable
robertphillips@google.com46f93502012-08-07 15:38:08 +0000386
Robert Phillips806be2d2017-06-28 15:23:59 -0400387 uint32_t getTopmostGenID() const;
robertphillips@google.com73e71022012-08-09 18:10:49 +0000388
djsollenefe46d22016-04-29 06:41:35 -0700389#ifdef SK_DEBUG
bsalomonb6b02522014-06-09 07:59:06 -0700390 /**
391 * Dumps the contents of the clip stack to SkDebugf. This is intended for Skia development
392 * debugging. Don't rely on the existence of this function or the formatting of its output.
393 */
394 void dump() const;
395#endif
396
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000397public:
398 class Iter {
reed@google.com5c3d1472011-02-22 19:12:23 +0000399 public:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000400 enum IterStart {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000401 kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
402 kTop_IterStart = SkDeque::Iter::kBack_IterStart
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000403 };
404
bsalomon@google.comd302f142011-03-03 13:54:13 +0000405 /**
406 * Creates an uninitialized iterator. Must be reset()
407 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000408 Iter();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000409
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000410 Iter(const SkClipStack& stack, IterStart startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000411
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000412 /**
413 * Return the clip element for this iterator. If next()/prev() returns NULL, then the
414 * iterator is done.
415 */
416 const Element* next();
417 const Element* prev();
reed@google.com5c3d1472011-02-22 19:12:23 +0000418
419 /**
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000420 * Moves the iterator to the topmost element with the specified RegionOp and returns that
421 * element. If no clip element with that op is found, the first element is returned.
reed@google.com5c3d1472011-02-22 19:12:23 +0000422 */
Mike Reedc1f77742016-12-09 09:00:50 -0500423 const Element* skipToTopmost(SkClipOp op);
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000424
425 /**
bsalomon@google.comd302f142011-03-03 13:54:13 +0000426 * Restarts the iterator on a clip stack.
427 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000428 void reset(const SkClipStack& stack, IterStart startLoc);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000429
reed@google.com5c3d1472011-02-22 19:12:23 +0000430 private:
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000431 const SkClipStack* fStack;
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000432 SkDeque::Iter fIter;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000433 };
434
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000435 /**
robertphillips@google.com80214e22012-07-20 15:33:18 +0000436 * The B2TIter iterates from the bottom of the stack to the top.
437 * It inherits privately from Iter to prevent access to reverse iteration.
438 */
439 class B2TIter : private Iter {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000440 public:
robertphillips@google.com80214e22012-07-20 15:33:18 +0000441 B2TIter() {}
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000442
443 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000444 * Wrap Iter's 2 parameter ctor to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000445 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000446 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000447 B2TIter(const SkClipStack& stack)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000448 : INHERITED(stack, kBottom_IterStart) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000449 }
450
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000451 using Iter::next;
452
453 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000454 * Wrap Iter::reset to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000455 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000456 */
457 void reset(const SkClipStack& stack) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000458 this->INHERITED::reset(stack, kBottom_IterStart);
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000459 }
460
461 private:
462
463 typedef Iter INHERITED;
reed@google.com5c3d1472011-02-22 19:12:23 +0000464 };
465
robertphillips@google.com607fe072012-07-24 13:54:00 +0000466 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000467 * GetConservativeBounds returns a conservative bound of the current clip.
468 * Since this could be the infinite plane (if inverse fills were involved) the
469 * maxWidth and maxHeight parameters can be used to limit the returned bound
robertphillips@google.com607fe072012-07-24 13:54:00 +0000470 * to the expected drawing area. Similarly, the offsetX and offsetY parameters
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000471 * allow the caller to offset the returned bound to account for translated
robertphillips@google.com607fe072012-07-24 13:54:00 +0000472 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000473 * the translation (+offsetX, +offsetY) is applied before the clamp to the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000474 * maximum rectangle: [0,maxWidth) x [0,maxHeight).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000475 * isIntersectionOfRects is an optional parameter that is true when
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000476 * 'devBounds' is the result of an intersection of rects. In this case
477 * 'devBounds' is the exact answer/clip.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000478 */
479 void getConservativeBounds(int offsetX,
480 int offsetY,
481 int maxWidth,
482 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000483 SkRect* devBounds,
Ben Wagnera93a14a2017-08-28 10:34:05 -0400484 bool* isIntersectionOfRects = nullptr) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000485
reed@google.com5c3d1472011-02-22 19:12:23 +0000486private:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000487 friend class Iter;
reed@google.com5c3d1472011-02-22 19:12:23 +0000488
489 SkDeque fDeque;
490 int fSaveCount;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000491
492 // Generation ID for the clip stack. This is incremented for each
493 // clipDevRect and clipDevPath call. 0 is reserved to indicate an
494 // invalid ID.
495 static int32_t gGenID;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500496 SkRect fClipRestrictionRect = SkRect::MakeEmpty();
robertphillips@google.com46f93502012-08-07 15:38:08 +0000497
reed4d2cce42016-08-22 13:03:47 -0700498 bool internalQuickContains(const SkRect& devRect) const;
499 bool internalQuickContains(const SkRRect& devRRect) const;
500
robertphillips@google.com46f93502012-08-07 15:38:08 +0000501 /**
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000502 * Helper for clipDevPath, etc.
503 */
504 void pushElement(const Element& element);
505
506 /**
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000507 * Restore the stack back to the specified save count.
508 */
509 void restoreTo(int saveCount);
510
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500511 inline bool hasClipRestriction(SkClipOp op) {
Mike Reed14113bc2017-05-10 14:13:20 -0400512 return op >= kUnion_SkClipOp && !fClipRestrictionRect.isEmpty();
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500513 }
514
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000515 /**
robertphillips@google.com46f93502012-08-07 15:38:08 +0000516 * Return the next unique generation ID.
517 */
Robert Phillips806be2d2017-06-28 15:23:59 -0400518 static uint32_t GetNextGenID();
reed@google.com5c3d1472011-02-22 19:12:23 +0000519};
520
521#endif