• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
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      * Takes an input rect in device space and conservatively clips it to the
314      * clip-stack. If false is returned then the rect does not intersect the
315      * clip and is unmodified.
316      */
317     bool intersectRectWithClip(SkRect* devRect) const;
318 
319     /**
320      * Returns true if the input rect in device space is entirely contained
321      * by the clip. A return value of false does not guarantee that the rect
322      * is not contained by the clip.
323      */
324     bool quickContains(const SkRect& devRect) const;
325 
clipDevRect(const SkIRect & ir,SkRegion::Op op)326     void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
327         SkRect r;
328         r.set(ir);
329         this->clipDevRect(r, op, false);
330     }
331     void clipDevRect(const SkRect&, SkRegion::Op, bool doAA);
332     void clipDevRRect(const SkRRect&, SkRegion::Op, bool doAA);
333     void clipDevPath(const SkPath&, SkRegion::Op, bool doAA);
334     // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
335     void clipEmpty();
336 
337     /**
338      * isWideOpen returns true if the clip state corresponds to the infinite
339      * plane (i.e., draws are not limited at all)
340      */
341     bool isWideOpen() const;
342 
343     /**
344      * The generation ID has three reserved values to indicate special
345      * (potentially ignorable) cases
346      */
347     static const int32_t kInvalidGenID = 0;     //!< Invalid id that is never returned by
348                                                 //!< SkClipStack. Useful when caching clips
349                                                 //!< based on GenID.
350     static const int32_t kEmptyGenID = 1;       // no pixels writeable
351     static const int32_t kWideOpenGenID = 2;    // all pixels writeable
352 
353     int32_t getTopmostGenID() const;
354 
355 #ifdef SK_DEVELOPER
356     /**
357      * Dumps the contents of the clip stack to SkDebugf. This is intended for Skia development
358      * debugging. Don't rely on the existence of this function or the formatting of its output.
359      */
360     void dump() const;
361 #endif
362 
363 public:
364     class Iter {
365     public:
366         enum IterStart {
367             kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
368             kTop_IterStart = SkDeque::Iter::kBack_IterStart
369         };
370 
371         /**
372          * Creates an uninitialized iterator. Must be reset()
373          */
374         Iter();
375 
376         Iter(const SkClipStack& stack, IterStart startLoc);
377 
378         /**
379          *  Return the clip element for this iterator. If next()/prev() returns NULL, then the
380          *  iterator is done.
381          */
382         const Element* next();
383         const Element* prev();
384 
385         /**
386          * Moves the iterator to the topmost element with the specified RegionOp and returns that
387          * element. If no clip element with that op is found, the first element is returned.
388          */
389         const Element* skipToTopmost(SkRegion::Op op);
390 
391         /**
392          * Restarts the iterator on a clip stack.
393          */
394         void reset(const SkClipStack& stack, IterStart startLoc);
395 
396     private:
397         const SkClipStack* fStack;
398         SkDeque::Iter      fIter;
399     };
400 
401     /**
402      * The B2TIter iterates from the bottom of the stack to the top.
403      * It inherits privately from Iter to prevent access to reverse iteration.
404      */
405     class B2TIter : private Iter {
406     public:
B2TIter()407         B2TIter() {}
408 
409         /**
410          * Wrap Iter's 2 parameter ctor to force initialization to the
411          * beginning of the deque/bottom of the stack
412          */
B2TIter(const SkClipStack & stack)413         B2TIter(const SkClipStack& stack)
414         : INHERITED(stack, kBottom_IterStart) {
415         }
416 
417         using Iter::next;
418 
419         /**
420          * Wrap Iter::reset to force initialization to the
421          * beginning of the deque/bottom of the stack
422          */
reset(const SkClipStack & stack)423         void reset(const SkClipStack& stack) {
424             this->INHERITED::reset(stack, kBottom_IterStart);
425         }
426 
427     private:
428 
429         typedef Iter INHERITED;
430     };
431 
432     /**
433      * GetConservativeBounds returns a conservative bound of the current clip.
434      * Since this could be the infinite plane (if inverse fills were involved) the
435      * maxWidth and maxHeight parameters can be used to limit the returned bound
436      * to the expected drawing area. Similarly, the offsetX and offsetY parameters
437      * allow the caller to offset the returned bound to account for translated
438      * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
439      * the translation (+offsetX, +offsetY) is applied before the clamp to the
440      * maximum rectangle: [0,maxWidth) x [0,maxHeight).
441      * isIntersectionOfRects is an optional parameter that is true when
442      * 'devBounds' is the result of an intersection of rects. In this case
443      * 'devBounds' is the exact answer/clip.
444      */
445     void getConservativeBounds(int offsetX,
446                                int offsetY,
447                                int maxWidth,
448                                int maxHeight,
449                                SkRect* devBounds,
450                                bool* isIntersectionOfRects = NULL) const;
451 
452 private:
453     friend class Iter;
454 
455     SkDeque fDeque;
456     int     fSaveCount;
457 
458     // Generation ID for the clip stack. This is incremented for each
459     // clipDevRect and clipDevPath call. 0 is reserved to indicate an
460     // invalid ID.
461     static int32_t     gGenID;
462 
463     /**
464      * Helper for clipDevPath, etc.
465      */
466     void pushElement(const Element& element);
467 
468     /**
469      * Restore the stack back to the specified save count.
470      */
471     void restoreTo(int saveCount);
472 
473     /**
474      * Return the next unique generation ID.
475      */
476     static int32_t GetNextGenID();
477 };
478 
479 #endif
480