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