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