Line data Source code
1 : /*
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 : */
7 :
8 : #ifndef SkClipStack_DEFINED
9 : #define SkClipStack_DEFINED
10 :
11 : #include "../private/SkMessageBus.h"
12 : #include "SkCanvas.h"
13 : #include "SkDeque.h"
14 : #include "SkPath.h"
15 : #include "SkRRect.h"
16 : #include "SkRect.h"
17 : #include "SkRegion.h"
18 : #include "SkTLazy.h"
19 :
20 : #if SK_SUPPORT_GPU
21 : #include "GrResourceKey.h"
22 : #endif
23 :
24 : #ifdef SK_SUPPORT_OBSOLETE_REPLAYCLIP
25 : class SkCanvasClipVisitor;
26 : #endif
27 :
28 : // 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.
34 : class SkClipStack {
35 : public:
36 : 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 :
47 : class Element {
48 : public:
49 : enum Type {
50 : //!< This element makes the clip empty (regardless of previous elements).
51 : kEmpty_Type,
52 : //!< This element combines a rect with the current clip using a set operation
53 : kRect_Type,
54 : //!< This element combines a round-rect with the current clip using a set operation
55 : kRRect_Type,
56 : //!< This element combines a path with the current clip using a set operation
57 : kPath_Type,
58 :
59 : kLastType = kPath_Type
60 : };
61 : static const int kTypeCnt = kLastType + 1;
62 :
63 : Element() {
64 : this->initCommon(0, SkClipOp::kReplace_deprecated, false);
65 : this->setEmpty();
66 : }
67 :
68 : Element(const Element&);
69 :
70 0 : Element(const SkRect& rect, SkClipOp op, bool doAA) {
71 0 : this->initRect(0, rect, op, doAA);
72 0 : }
73 :
74 : Element(const SkRRect& rrect, SkClipOp op, bool doAA) {
75 : this->initRRect(0, rrect, op, doAA);
76 : }
77 :
78 : Element(const SkPath& path, SkClipOp op, bool doAA) {
79 : this->initPath(0, path, op, doAA);
80 : }
81 :
82 0 : ~Element() {
83 : #if SK_SUPPORT_GPU
84 0 : for (int i = 0; i < fMessages.count(); ++i) {
85 0 : SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(*fMessages[i]);
86 : }
87 : #endif
88 0 : }
89 :
90 : bool operator== (const Element& element) const;
91 0 : bool operator!= (const Element& element) const { return !(*this == element); }
92 :
93 : //!< Call to get the type of the clip element.
94 0 : Type getType() const { return fType; }
95 :
96 : //!< Call to get the save count associated with this clip element.
97 : int getSaveCount() const { return fSaveCount; }
98 :
99 : //!< Call if getType() is kPath to get the path.
100 0 : const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return *fPath.get(); }
101 :
102 : //!< Call if getType() is kRRect to get the round-rect.
103 0 : const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; }
104 :
105 : //!< Call if getType() is kRect to get the rect.
106 0 : const SkRect& getRect() const {
107 0 : SkASSERT(kRect_Type == fType && (fRRect.isRect() || fRRect.isEmpty()));
108 0 : return fRRect.getBounds();
109 : }
110 :
111 : //!< Call if getType() is not kEmpty to get the set operation used to combine this element.
112 0 : SkClipOp getOp() const { return fOp; }
113 :
114 : //!< Call to get the element as a path, regardless of its type.
115 : void asPath(SkPath* path) const;
116 :
117 : //!< Call if getType() is not kPath to get the element as a round rect.
118 0 : const SkRRect& asRRect() const { SkASSERT(kPath_Type != fType); return fRRect; }
119 :
120 : /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
121 : when it is rasterized. */
122 0 : bool isAA() const { return fDoAA; }
123 :
124 : //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
125 : void invertShapeFillType();
126 :
127 : //!< Sets the set operation represented by the element.
128 0 : void setOp(SkClipOp op) { fOp = op; }
129 :
130 : /** The GenID can be used by clip stack clients to cache representations of the clip. The
131 : ID corresponds to the set of clip elements up to and including this element within the
132 : stack not to the element itself. That is the same clip path in different stacks will
133 : have a different ID since the elements produce different clip result in the context of
134 : their stacks. */
135 0 : int32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
136 :
137 : /**
138 : * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
139 : * is inverse filled is not considered.)
140 : */
141 0 : const SkRect& getBounds() const {
142 : static const SkRect kEmpty = { 0, 0, 0, 0 };
143 0 : switch (fType) {
144 : case kRect_Type: // fallthrough
145 : case kRRect_Type:
146 0 : return fRRect.getBounds();
147 : case kPath_Type:
148 0 : return fPath.get()->getBounds();
149 : case kEmpty_Type:
150 0 : return kEmpty;
151 : default:
152 0 : SkDEBUGFAIL("Unexpected type.");
153 0 : return kEmpty;
154 : }
155 : }
156 :
157 : /**
158 : * Conservatively checks whether the clip shape contains the rect param. (Whether the shape
159 : * is inverse filled is not considered.)
160 : */
161 0 : bool contains(const SkRect& rect) const {
162 0 : switch (fType) {
163 : case kRect_Type:
164 0 : return this->getRect().contains(rect);
165 : case kRRect_Type:
166 0 : return fRRect.contains(rect);
167 : case kPath_Type:
168 0 : return fPath.get()->conservativelyContainsRect(rect);
169 : case kEmpty_Type:
170 0 : return false;
171 : default:
172 0 : SkDEBUGFAIL("Unexpected type.");
173 0 : return false;
174 : }
175 : }
176 :
177 0 : bool contains(const SkRRect& rrect) const {
178 0 : switch (fType) {
179 : case kRect_Type:
180 0 : return this->getRect().contains(rrect.getBounds());
181 : case kRRect_Type:
182 : // We don't currently have a generalized rrect-rrect containment.
183 0 : return fRRect.contains(rrect.getBounds()) || rrect == fRRect;
184 : case kPath_Type:
185 0 : return fPath.get()->conservativelyContainsRect(rrect.getBounds());
186 : case kEmpty_Type:
187 0 : return false;
188 : default:
189 0 : SkDEBUGFAIL("Unexpected type.");
190 0 : return false;
191 : }
192 : }
193 :
194 : /**
195 : * Is the clip shape inverse filled.
196 : */
197 0 : bool isInverseFilled() const {
198 0 : return kPath_Type == fType && fPath.get()->isInverseFillType();
199 : }
200 :
201 : #ifdef SK_SUPPORT_OBSOLETE_REPLAYCLIP
202 : /**
203 : * Replay this clip into the visitor.
204 : */
205 : void replay(SkCanvasClipVisitor*) const;
206 : #endif
207 :
208 : #ifdef SK_DEBUG
209 : /**
210 : * Dumps the element to SkDebugf. This is intended for Skia development debugging
211 : * Don't rely on the existence of this function or the formatting of its output.
212 : */
213 : void dump() const;
214 : #endif
215 :
216 : #if SK_SUPPORT_GPU
217 : /**
218 : * This is used to purge any GPU resource cache items that become unreachable when
219 : * the element is destroyed because their key is based on this element's gen ID.
220 : */
221 0 : void addResourceInvalidationMessage(
222 : std::unique_ptr<GrUniqueKeyInvalidatedMessage> msg) const {
223 0 : fMessages.emplace_back(std::move(msg));
224 0 : }
225 : #endif
226 :
227 : private:
228 : friend class SkClipStack;
229 :
230 : SkTLazy<SkPath> fPath;
231 : SkRRect fRRect;
232 : int fSaveCount; // save count of stack when this element was added.
233 : SkClipOp fOp;
234 : Type fType;
235 : bool fDoAA;
236 :
237 : /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
238 : bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
239 : conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
240 : drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
241 : occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
242 : box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
243 : the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
244 : infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
245 : can capture the cancelling out of the extensions to infinity when two inverse filled
246 : clips are Booleaned together. */
247 : SkClipStack::BoundsType fFiniteBoundType;
248 : SkRect fFiniteBound;
249 :
250 : // When element is applied to the previous elements in the stack is the result known to be
251 : // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
252 : bool fIsIntersectionOfRects;
253 :
254 : int fGenID;
255 : #if SK_SUPPORT_GPU
256 : mutable SkTArray<std::unique_ptr<GrUniqueKeyInvalidatedMessage>> fMessages;
257 : #endif
258 0 : Element(int saveCount) {
259 0 : this->initCommon(saveCount, SkClipOp::kReplace_deprecated, false);
260 0 : this->setEmpty();
261 0 : }
262 :
263 0 : Element(int saveCount, const SkRRect& rrect, SkClipOp op, bool doAA) {
264 0 : this->initRRect(saveCount, rrect, op, doAA);
265 0 : }
266 :
267 0 : Element(int saveCount, const SkRect& rect, SkClipOp op, bool doAA) {
268 0 : this->initRect(saveCount, rect, op, doAA);
269 0 : }
270 :
271 0 : Element(int saveCount, const SkPath& path, SkClipOp op, bool doAA) {
272 0 : this->initPath(saveCount, path, op, doAA);
273 0 : }
274 :
275 0 : void initCommon(int saveCount, SkClipOp op, bool doAA) {
276 0 : fSaveCount = saveCount;
277 0 : fOp = op;
278 0 : fDoAA = doAA;
279 : // A default of inside-out and empty bounds means the bounds are effectively void as it
280 : // indicates that nothing is known to be outside the clip.
281 0 : fFiniteBoundType = kInsideOut_BoundsType;
282 0 : fFiniteBound.setEmpty();
283 0 : fIsIntersectionOfRects = false;
284 0 : fGenID = kInvalidGenID;
285 0 : }
286 :
287 0 : void initRect(int saveCount, const SkRect& rect, SkClipOp op, bool doAA) {
288 0 : fRRect.setRect(rect);
289 0 : fType = kRect_Type;
290 0 : this->initCommon(saveCount, op, doAA);
291 0 : }
292 :
293 0 : void initRRect(int saveCount, const SkRRect& rrect, SkClipOp op, bool doAA) {
294 0 : SkRRect::Type type = rrect.getType();
295 0 : fRRect = rrect;
296 0 : if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
297 0 : fType = kRect_Type;
298 : } else {
299 0 : fType = kRRect_Type;
300 : }
301 0 : this->initCommon(saveCount, op, doAA);
302 0 : }
303 :
304 : void initPath(int saveCount, const SkPath& path, SkClipOp op, bool doAA);
305 :
306 : void setEmpty();
307 :
308 : // All Element methods below are only used within SkClipStack.cpp
309 : inline void checkEmpty() const;
310 : inline bool canBeIntersectedInPlace(int saveCount, SkClipOp op) const;
311 : /* This method checks to see if two rect clips can be safely merged into one. The issue here
312 : is that to be strictly correct all the edges of the resulting rect must have the same
313 : anti-aliasing. */
314 : bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
315 : /** Determines possible finite bounds for the Element given the previous element of the
316 : stack */
317 : void updateBoundAndGenID(const Element* prior);
318 : // The different combination of fill & inverse fill when combining bounding boxes
319 : enum FillCombo {
320 : kPrev_Cur_FillCombo,
321 : kPrev_InvCur_FillCombo,
322 : kInvPrev_Cur_FillCombo,
323 : kInvPrev_InvCur_FillCombo
324 : };
325 : // per-set operation functions used by updateBoundAndGenID().
326 : inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
327 : inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
328 : inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
329 : inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
330 : inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
331 : };
332 :
333 : SkClipStack();
334 : SkClipStack(void* storage, size_t size);
335 : SkClipStack(const SkClipStack& b);
336 : ~SkClipStack();
337 :
338 : SkClipStack& operator=(const SkClipStack& b);
339 : bool operator==(const SkClipStack& b) const;
340 : bool operator!=(const SkClipStack& b) const { return !(*this == b); }
341 :
342 : void reset();
343 :
344 : int getSaveCount() const { return fSaveCount; }
345 : void save();
346 : void restore();
347 :
348 : class AutoRestore {
349 : public:
350 : AutoRestore(SkClipStack* cs, bool doSave)
351 : : fCS(cs), fSaveCount(cs->getSaveCount())
352 : {
353 : if (doSave) {
354 : fCS->save();
355 : }
356 : }
357 : ~AutoRestore() {
358 : SkASSERT(fCS->getSaveCount() >= fSaveCount); // no underflow
359 : while (fCS->getSaveCount() > fSaveCount) {
360 : fCS->restore();
361 : }
362 : }
363 :
364 : private:
365 : SkClipStack* fCS;
366 : const int fSaveCount;
367 : };
368 :
369 : /**
370 : * getBounds places the current finite bound in its first parameter. In its
371 : * second, it indicates which kind of bound is being returned. If
372 : * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
373 : * pixels. If 'canvFiniteBound' is an inside out bounding box then it
374 : * encloses all the un-writeable pixels and the true/normal bound is the
375 : * infinite plane. isIntersectionOfRects is an optional parameter
376 : * that is true if 'canvFiniteBound' resulted from an intersection of rects.
377 : */
378 : void getBounds(SkRect* canvFiniteBound,
379 : BoundsType* boundType,
380 : bool* isIntersectionOfRects = NULL) const;
381 :
382 : SkRect bounds(const SkIRect& deviceBounds) const;
383 : bool isEmpty(const SkIRect& deviceBounds) const;
384 :
385 : /**
386 : * Returns true if the input (r)rect in device space is entirely contained
387 : * by the clip. A return value of false does not guarantee that the (r)rect
388 : * is not contained by the clip.
389 : */
390 0 : bool quickContains(const SkRect& devRect) const {
391 0 : return this->isWideOpen() || this->internalQuickContains(devRect);
392 : }
393 :
394 0 : bool quickContains(const SkRRect& devRRect) const {
395 0 : return this->isWideOpen() || this->internalQuickContains(devRRect);
396 : }
397 :
398 : /**
399 : * Flattens the clip stack into a single SkPath. Returns true if any of
400 : * the clip stack components requires anti-aliasing.
401 : */
402 : bool asPath(SkPath* path) const;
403 :
404 0 : void clipDevRect(const SkIRect& ir, SkClipOp op) {
405 : SkRect r;
406 0 : r.set(ir);
407 0 : this->clipRect(r, SkMatrix::I(), op, false);
408 0 : }
409 : void clipRect(const SkRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
410 : void clipRRect(const SkRRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
411 : void clipPath(const SkPath&, const SkMatrix& matrix, SkClipOp, bool doAA);
412 : // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
413 : void clipEmpty();
414 0 : void setDeviceClipRestriction(const SkIRect& rect) {
415 0 : fClipRestrictionRect = SkRect::Make(rect);
416 0 : }
417 :
418 : /**
419 : * isWideOpen returns true if the clip state corresponds to the infinite
420 : * plane (i.e., draws are not limited at all)
421 : */
422 0 : bool isWideOpen() const { return this->getTopmostGenID() == kWideOpenGenID; }
423 :
424 : /**
425 : * This method quickly and conservatively determines whether the entire stack is equivalent to
426 : * intersection with a rrect given a bounds, where the rrect must not contain the entire bounds.
427 : *
428 : * @param bounds A bounds on what will be drawn through the clip. The clip only need be
429 : * equivalent to a intersection with a rrect for draws within the bounds. The
430 : * returned rrect must intersect the bounds but need not be contained by the
431 : * bounds.
432 : * @param rrect If return is true rrect will contain the rrect equivalent to the stack.
433 : * @param aa If return is true aa will indicate whether the equivalent rrect clip is
434 : * antialiased.
435 : * @return true if the stack is equivalent to a single rrect intersect clip, false otherwise.
436 : */
437 : bool isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const;
438 :
439 : /**
440 : * The generation ID has three reserved values to indicate special
441 : * (potentially ignorable) cases
442 : */
443 : static const int32_t kInvalidGenID = 0; //!< Invalid id that is never returned by
444 : //!< SkClipStack. Useful when caching clips
445 : //!< based on GenID.
446 : static const int32_t kEmptyGenID = 1; // no pixels writeable
447 : static const int32_t kWideOpenGenID = 2; // all pixels writeable
448 :
449 : int32_t getTopmostGenID() const;
450 :
451 : #ifdef SK_DEBUG
452 : /**
453 : * Dumps the contents of the clip stack to SkDebugf. This is intended for Skia development
454 : * debugging. Don't rely on the existence of this function or the formatting of its output.
455 : */
456 : void dump() const;
457 : #endif
458 :
459 : public:
460 : class Iter {
461 : public:
462 : enum IterStart {
463 : kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
464 : kTop_IterStart = SkDeque::Iter::kBack_IterStart
465 : };
466 :
467 : /**
468 : * Creates an uninitialized iterator. Must be reset()
469 : */
470 : Iter();
471 :
472 : Iter(const SkClipStack& stack, IterStart startLoc);
473 :
474 : /**
475 : * Return the clip element for this iterator. If next()/prev() returns NULL, then the
476 : * iterator is done.
477 : */
478 : const Element* next();
479 : const Element* prev();
480 :
481 : /**
482 : * Moves the iterator to the topmost element with the specified RegionOp and returns that
483 : * element. If no clip element with that op is found, the first element is returned.
484 : */
485 : const Element* skipToTopmost(SkClipOp op);
486 :
487 : /**
488 : * Restarts the iterator on a clip stack.
489 : */
490 : void reset(const SkClipStack& stack, IterStart startLoc);
491 :
492 : private:
493 : const SkClipStack* fStack;
494 : SkDeque::Iter fIter;
495 : };
496 :
497 : /**
498 : * The B2TIter iterates from the bottom of the stack to the top.
499 : * It inherits privately from Iter to prevent access to reverse iteration.
500 : */
501 : class B2TIter : private Iter {
502 : public:
503 : B2TIter() {}
504 :
505 : /**
506 : * Wrap Iter's 2 parameter ctor to force initialization to the
507 : * beginning of the deque/bottom of the stack
508 : */
509 0 : B2TIter(const SkClipStack& stack)
510 0 : : INHERITED(stack, kBottom_IterStart) {
511 0 : }
512 :
513 : using Iter::next;
514 :
515 : /**
516 : * Wrap Iter::reset to force initialization to the
517 : * beginning of the deque/bottom of the stack
518 : */
519 : void reset(const SkClipStack& stack) {
520 : this->INHERITED::reset(stack, kBottom_IterStart);
521 : }
522 :
523 : private:
524 :
525 : typedef Iter INHERITED;
526 : };
527 :
528 : /**
529 : * GetConservativeBounds returns a conservative bound of the current clip.
530 : * Since this could be the infinite plane (if inverse fills were involved) the
531 : * maxWidth and maxHeight parameters can be used to limit the returned bound
532 : * to the expected drawing area. Similarly, the offsetX and offsetY parameters
533 : * allow the caller to offset the returned bound to account for translated
534 : * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
535 : * the translation (+offsetX, +offsetY) is applied before the clamp to the
536 : * maximum rectangle: [0,maxWidth) x [0,maxHeight).
537 : * isIntersectionOfRects is an optional parameter that is true when
538 : * 'devBounds' is the result of an intersection of rects. In this case
539 : * 'devBounds' is the exact answer/clip.
540 : */
541 : void getConservativeBounds(int offsetX,
542 : int offsetY,
543 : int maxWidth,
544 : int maxHeight,
545 : SkRect* devBounds,
546 : bool* isIntersectionOfRects = NULL) const;
547 :
548 : private:
549 : friend class Iter;
550 :
551 : SkDeque fDeque;
552 : int fSaveCount;
553 :
554 : // Generation ID for the clip stack. This is incremented for each
555 : // clipDevRect and clipDevPath call. 0 is reserved to indicate an
556 : // invalid ID.
557 : static int32_t gGenID;
558 : SkRect fClipRestrictionRect = SkRect::MakeEmpty();
559 :
560 : bool internalQuickContains(const SkRect& devRect) const;
561 : bool internalQuickContains(const SkRRect& devRRect) const;
562 :
563 : /**
564 : * Helper for clipDevPath, etc.
565 : */
566 : void pushElement(const Element& element);
567 :
568 : /**
569 : * Restore the stack back to the specified save count.
570 : */
571 : void restoreTo(int saveCount);
572 :
573 0 : inline bool hasClipRestriction(SkClipOp op) {
574 0 : return op >= SkClipOp::kUnion_deprecated && !fClipRestrictionRect.isEmpty();
575 : }
576 :
577 : /**
578 : * Return the next unique generation ID.
579 : */
580 : static int32_t GetNextGenID();
581 : };
582 :
583 : #endif
|