blob: 6af60a24221b18f74a4f008e1a979aefc0727db5 [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"
reed@google.com5c3d1472011-02-22 19:12:23 +000014#include "SkRegion.h"
robertphillips@google.com46f93502012-08-07 15:38:08 +000015#include "SkTDArray.h"
reed@google.com5c3d1472011-02-22 19:12:23 +000016
reed@google.com5c3d1472011-02-22 19:12:23 +000017
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000018// Because a single save/restore state can have multiple clips, this class
19// stores the stack depth (fSaveCount) and clips (fDeque) separately.
20// Each clip in fDeque stores the stack state to which it belongs
21// (i.e., the fSaveCount in force when it was added). Restores are thus
22// implemented by removing clips from fDeque that have an fSaveCount larger
23// then the freshly decremented count.
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +000024class SK_API SkClipStack {
reed@google.com5c3d1472011-02-22 19:12:23 +000025public:
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000026 enum BoundsType {
27 // The bounding box contains all the pixels that can be written to
28 kNormal_BoundsType,
29 // The bounding box contains all the pixels that cannot be written to.
30 // The real bound extends out to infinity and all the pixels outside
31 // of the bound can be written to. Note that some of the pixels inside
32 // the bound may also be writeable but all pixels that cannot be
33 // written to are guaranteed to be inside.
34 kInsideOut_BoundsType
35 };
36
37 class Element {
38 public:
39 enum Type {
40 //!< This element makes the clip empty (regardless of previous elements).
41 kEmpty_Type,
42 //!< This element combines a rect with the current clip using a set operation
43 kRect_Type,
44 //!< This element combines a path with the current clip using a set operation
45 kPath_Type,
46 };
47
48 Element() {
49 this->initCommon(0, SkRegion::kReplace_Op, false);
50 this->setEmpty();
51 }
52
53 Element(const SkRect& rect, SkRegion::Op op, bool doAA) {
54 this->initRect(0, rect, op, doAA);
55 }
56
57 Element(const SkPath& path, SkRegion::Op op, bool doAA) {
58 this->initPath(0, path, op, doAA);
59 }
60
bsalomon@google.com8182fa02012-12-04 14:06:06 +000061 bool operator== (const Element& element) const {
62 if (this == &element) {
63 return true;
64 }
65 if (fOp != element.fOp ||
66 fType != element.fType ||
67 fDoAA != element.fDoAA ||
68 fSaveCount != element.fSaveCount) {
69 return false;
70 }
71 switch (fType) {
72 case kPath_Type:
73 return fPath == element.fPath;
74 case kRect_Type:
75 return fRect == element.fRect;
76 case kEmpty_Type:
77 return true;
78 default:
79 SkDEBUGFAIL("Unexpected type.");
80 return false;
81 }
82 }
83 bool operator!= (const Element& element) const { return !(*this == element); }
84
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +000085 //!< Call to get the type of the clip element.
86 Type getType() const { return fType; }
87
88 //!< Call if getType() is kPath to get the path.
89 const SkPath& getPath() const { return fPath; }
90
91 //!< Call if getType() is kRect to get the rect.
92 const SkRect& getRect() const { return fRect; }
93
94 //!< Call if getType() is not kEmpty to get the set operation used to combine this element.
95 SkRegion::Op getOp() const { return fOp; }
96
97 /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
98 when it is rasterized. */
99 bool isAA() const { return fDoAA; }
100
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000101 void setOp(SkRegion::Op op) { fOp = op; }
102
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000103 /** The GenID can be used by clip stack clients to cache representations of the clip. The
104 ID corresponds to the set of clip elements up to and including this element within the
105 stack not to the element itself. That is the same clip path in different stacks will
106 have a different ID since the elements produce different clip result in the context of
107 their stacks. */
108 int32_t getGenID() const { return fGenID; }
109
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000110 /**
111 * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
112 * is inverse filled is not considered.)
113 */
114 const SkRect& getBounds() const {
115 static const SkRect kEmpty = { 0, 0, 0, 0 };
116 switch (fType) {
117 case kRect_Type:
118 return fRect;
119 case kPath_Type:
120 return fPath.getBounds();
121 case kEmpty_Type:
122 return kEmpty;
123 default:
124 SkDEBUGFAIL("Unexpected type.");
125 return kEmpty;
126 }
127 }
128
129 /**
130 * Conservatively checks whether the clip shape contains the rect param. (Whether the shape
131 * is inverse filled is not considered.)
132 */
133 bool contains(const SkRect& rect) const {
134 switch (fType) {
135 case kRect_Type:
136 return fRect.contains(rect);
137 case kPath_Type:
138 return fPath.conservativelyContainsRect(rect);
139 case kEmpty_Type:
140 return false;
141 default:
142 SkDEBUGFAIL("Unexpected type.");
143 return false;
144 }
145 }
146
147 /**
148 * Is the clip shape inverse filled.
149 */
150 bool isInverseFilled() const {
151 return kPath_Type == fType && fPath.isInverseFillType();
152 }
153
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000154 private:
155 friend class SkClipStack;
156
157 SkPath fPath;
158 SkRect fRect;
159 int fSaveCount; // save count of stack when this element was added.
160 SkRegion::Op fOp;
161 Type fType;
162 bool fDoAA;
163
164 /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
165 bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
166 conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
167 drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
168 occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
169 box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
170 the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
171 infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
172 can capture the cancelling out of the extensions to infinity when two inverse filled
173 clips are Booleaned together. */
174 SkClipStack::BoundsType fFiniteBoundType;
175 SkRect fFiniteBound;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000176
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000177 // When element is applied to the previous elements in the stack is the result known to be
178 // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
179 bool fIsIntersectionOfRects;
180
181 int fGenID;
182
183 Element(int saveCount) {
184 this->initCommon(saveCount, SkRegion::kReplace_Op, false);
185 this->setEmpty();
186 }
187
188 Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
189 this->initRect(saveCount, rect, op, doAA);
190 }
191
192 Element(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) {
193 this->initPath(saveCount, path, op, doAA);
194 }
195
196 void initCommon(int saveCount, SkRegion::Op op, bool doAA) {
197 fSaveCount = saveCount;
198 fOp = op;
199 fDoAA = doAA;
200 // A default of inside-out and empty bounds means the bounds are effectively void as it
201 // indicates that nothing is known to be outside the clip.
202 fFiniteBoundType = kInsideOut_BoundsType;
203 fFiniteBound.setEmpty();
204 fIsIntersectionOfRects = false;
205 fGenID = kInvalidGenID;
206 }
207
208 void initRect(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
209 fRect = rect;
210 fType = kRect_Type;
211 this->initCommon(saveCount, op, doAA);
212 }
213
214 void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) {
215 fPath = path;
216 fType = kPath_Type;
217 this->initCommon(saveCount, op, doAA);
218 }
219
220 void setEmpty() {
221 fType = kEmpty_Type;
222 fFiniteBound.setEmpty();
223 fFiniteBoundType = kNormal_BoundsType;
224 fIsIntersectionOfRects = false;
225 fRect.setEmpty();
226 fPath.reset();
227 fGenID = kEmptyGenID;
228 }
229
230 // All Element methods below are only used within SkClipStack.cpp
231 inline void checkEmpty() const;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000232 inline bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const;
233 /* 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 +0000234 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 +0000235 anti-aliasing. */
236 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
237 /** Determines possible finite bounds for the Element given the previous element of the
238 stack */
239 void updateBoundAndGenID(const Element* prior);
240 // The different combination of fill & inverse fill when combining bounding boxes
241 enum FillCombo {
242 kPrev_Cur_FillCombo,
243 kPrev_InvCur_FillCombo,
244 kInvPrev_Cur_FillCombo,
245 kInvPrev_InvCur_FillCombo
246 };
247 // per-set operation functions used by updateBoundAndGenID().
248 inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
249 inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
250 inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
251 inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
252 inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
253 };
254
reed@google.com5c3d1472011-02-22 19:12:23 +0000255 SkClipStack();
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000256 SkClipStack(const SkClipStack& b);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000257 explicit SkClipStack(const SkRect& r);
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000258 explicit SkClipStack(const SkIRect& r);
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000259 ~SkClipStack();
reed@google.com5c3d1472011-02-22 19:12:23 +0000260
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000261 SkClipStack& operator=(const SkClipStack& b);
262 bool operator==(const SkClipStack& b) const;
263 bool operator!=(const SkClipStack& b) const { return !(*this == b); }
264
reed@google.com5c3d1472011-02-22 19:12:23 +0000265 void reset();
266
267 int getSaveCount() const { return fSaveCount; }
268 void save();
269 void restore();
270
robertphillips@google.com607fe072012-07-24 13:54:00 +0000271 /**
272 * getBounds places the current finite bound in its first parameter. In its
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000273 * second, it indicates which kind of bound is being returned. If
robertphillips@google.com7b112892012-07-31 15:18:21 +0000274 * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000275 * pixels. If 'canvFiniteBound' is an inside out bounding box then it
robertphillips@google.com607fe072012-07-24 13:54:00 +0000276 * encloses all the un-writeable pixels and the true/normal bound is the
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000277 * infinite plane. isIntersectionOfRects is an optional parameter
robertphillips@google.com7b112892012-07-31 15:18:21 +0000278 * that is true if 'canvFiniteBound' resulted from an intersection of rects.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000279 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000280 void getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000281 BoundsType* boundType,
282 bool* isIntersectionOfRects = NULL) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000283
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000284 /**
285 * Takes an input rect in device space and conservatively clips it to the
286 * clip-stack. If false is returned then the rect does not intersect the
287 * clip and is unmodified.
288 */
289 bool intersectRectWithClip(SkRect* devRect) const;
290
reed@google.com115d9312012-05-16 18:50:40 +0000291 void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000292 SkRect r;
293 r.set(ir);
reed@google.com00177082011-10-12 14:34:30 +0000294 this->clipDevRect(r, op, false);
reed@google.com5c3d1472011-02-22 19:12:23 +0000295 }
reed@google.com00177082011-10-12 14:34:30 +0000296 void clipDevRect(const SkRect&, SkRegion::Op, bool doAA);
297 void clipDevPath(const SkPath&, SkRegion::Op, bool doAA);
reed@google.com0557d9e2012-08-16 15:59:59 +0000298 // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
299 void clipEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000300
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000301 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000302 * isWideOpen returns true if the clip state corresponds to the infinite
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000303 * plane (i.e., draws are not limited at all)
304 */
305 bool isWideOpen() const;
306
robertphillips@google.com46f93502012-08-07 15:38:08 +0000307 /**
308 * Add a callback function that will be called whenever a clip state
309 * is no longer viable. This will occur whenever restore
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000310 * is called or when a clipDevRect or clipDevPath call updates the
robertphillips@google.com46f93502012-08-07 15:38:08 +0000311 * clip within an existing save/restore state. Each clip state is
312 * represented by a unique generation ID.
313 */
314 typedef void (*PFPurgeClipCB)(int genID, void* data);
315 void addPurgeClipCallback(PFPurgeClipCB callback, void* data) const;
316
317 /**
318 * Remove a callback added earlier via addPurgeClipCallback
319 */
320 void removePurgeClipCallback(PFPurgeClipCB callback, void* data) const;
321
322 /**
323 * The generation ID has three reserved values to indicate special
bsalomon@google.come8ca6c62012-11-07 21:19:10 +0000324 * (potentially ignorable) cases
robertphillips@google.com46f93502012-08-07 15:38:08 +0000325 */
326 static const int32_t kInvalidGenID = 0;
327 static const int32_t kEmptyGenID = 1; // no pixels writeable
328 static const int32_t kWideOpenGenID = 2; // all pixels writeable
329
robertphillips@google.com73e71022012-08-09 18:10:49 +0000330 int32_t getTopmostGenID() const;
331
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000332public:
333 class Iter {
reed@google.com5c3d1472011-02-22 19:12:23 +0000334 public:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000335 enum IterStart {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000336 kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
337 kTop_IterStart = SkDeque::Iter::kBack_IterStart
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000338 };
339
bsalomon@google.comd302f142011-03-03 13:54:13 +0000340 /**
341 * Creates an uninitialized iterator. Must be reset()
342 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000343 Iter();
bsalomon@google.comd302f142011-03-03 13:54:13 +0000344
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000345 Iter(const SkClipStack& stack, IterStart startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000346
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000347 /**
348 * Return the clip element for this iterator. If next()/prev() returns NULL, then the
349 * iterator is done.
350 */
351 const Element* next();
352 const Element* prev();
reed@google.com5c3d1472011-02-22 19:12:23 +0000353
354 /**
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000355 * Moves the iterator to the topmost element with the specified RegionOp and returns that
356 * element. If no clip element with that op is found, the first element is returned.
reed@google.com5c3d1472011-02-22 19:12:23 +0000357 */
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000358 const Element* skipToTopmost(SkRegion::Op op);
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000359
360 /**
bsalomon@google.comd302f142011-03-03 13:54:13 +0000361 * Restarts the iterator on a clip stack.
362 */
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000363 void reset(const SkClipStack& stack, IterStart startLoc);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000364
reed@google.com5c3d1472011-02-22 19:12:23 +0000365 private:
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000366 const SkClipStack* fStack;
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000367 SkDeque::Iter fIter;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000368 };
369
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000370 /**
robertphillips@google.com80214e22012-07-20 15:33:18 +0000371 * The B2TIter iterates from the bottom of the stack to the top.
372 * It inherits privately from Iter to prevent access to reverse iteration.
373 */
374 class B2TIter : private Iter {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000375 public:
robertphillips@google.com80214e22012-07-20 15:33:18 +0000376 B2TIter() {}
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000377
378 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000379 * Wrap Iter's 2 parameter ctor to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000380 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000381 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000382 B2TIter(const SkClipStack& stack)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000383 : INHERITED(stack, kBottom_IterStart) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000384 }
385
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000386 using Iter::next;
387
388 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000389 * Wrap Iter::reset to force initialization to the
robertphillips@google.com80214e22012-07-20 15:33:18 +0000390 * beginning of the deque/bottom of the stack
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000391 */
392 void reset(const SkClipStack& stack) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000393 this->INHERITED::reset(stack, kBottom_IterStart);
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000394 }
395
396 private:
397
398 typedef Iter INHERITED;
reed@google.com5c3d1472011-02-22 19:12:23 +0000399 };
400
robertphillips@google.com607fe072012-07-24 13:54:00 +0000401 /**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000402 * GetConservativeBounds returns a conservative bound of the current clip.
403 * Since this could be the infinite plane (if inverse fills were involved) the
404 * maxWidth and maxHeight parameters can be used to limit the returned bound
robertphillips@google.com607fe072012-07-24 13:54:00 +0000405 * to the expected drawing area. Similarly, the offsetX and offsetY parameters
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000406 * allow the caller to offset the returned bound to account for translated
robertphillips@google.com607fe072012-07-24 13:54:00 +0000407 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000408 * the translation (+offsetX, +offsetY) is applied before the clamp to the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000409 * maximum rectangle: [0,maxWidth) x [0,maxHeight).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000410 * isIntersectionOfRects is an optional parameter that is true when
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000411 * 'devBounds' is the result of an intersection of rects. In this case
412 * 'devBounds' is the exact answer/clip.
robertphillips@google.com607fe072012-07-24 13:54:00 +0000413 */
414 void getConservativeBounds(int offsetX,
415 int offsetY,
416 int maxWidth,
417 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000418 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000419 bool* isIntersectionOfRects = NULL) const;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000420
reed@google.com5c3d1472011-02-22 19:12:23 +0000421private:
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000422 friend class Iter;
reed@google.com5c3d1472011-02-22 19:12:23 +0000423
424 SkDeque fDeque;
425 int fSaveCount;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000426
427 // Generation ID for the clip stack. This is incremented for each
428 // clipDevRect and clipDevPath call. 0 is reserved to indicate an
429 // invalid ID.
430 static int32_t gGenID;
431
432 struct ClipCallbackData {
433 PFPurgeClipCB fCallback;
434 void* fData;
435
436 friend bool operator==(const ClipCallbackData& a,
437 const ClipCallbackData& b) {
438 return a.fCallback == b.fCallback && a.fData == b.fData;
439 }
440 };
441
442 mutable SkTDArray<ClipCallbackData> fCallbackData;
443
444 /**
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000445 * Invoke all the purge callbacks passing in element's generation ID.
robertphillips@google.com46f93502012-08-07 15:38:08 +0000446 */
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000447 void purgeClip(Element* element);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000448
449 /**
450 * Return the next unique generation ID.
451 */
452 static int32_t GetNextGenID();
reed@google.com5c3d1472011-02-22 19:12:23 +0000453};
454
455#endif
456