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