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