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
19class 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.
27class SK_API SkClipStack {
28public:
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
56        Element() {
57            this->initCommon(0, SkRegion::kReplace_Op, false);
58            this->setEmpty();
59        }
60
61        Element(const Element&);
62
63        Element(const SkRect& rect, SkRegion::Op op, bool doAA) {
64            this->initRect(0, rect, op, doAA);
65        }
66
67        Element(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
68            this->initRRect(0, rrect, op, doAA);
69        }
70
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.
79        Type getType() const { return fType; }
80
81        //!< Call to get the save count associated with this clip element.
82        int getSaveCount() const { return fSaveCount; }
83
84        //!< Call if getType() is kPath to get the path.
85        const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return *fPath.get(); }
86
87        //!< Call if getType() is kRRect to get the round-rect.
88        const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; }
89
90        //!< Call if getType() is kRect to get the rect.
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.
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. */
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.
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. */
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         */
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         */
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         */
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
208        Element(int saveCount) {
209            this->initCommon(saveCount, SkRegion::kReplace_Op, false);
210            this->setEmpty();
211        }
212
213        Element(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) {
214            this->initRRect(saveCount, rrect, op, doAA);
215        }
216
217        Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
218            this->initRect(saveCount, rect, op, doAA);
219        }
220
221        Element(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) {
222            this->initPath(saveCount, path, op, doAA);
223        }
224
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
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
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
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
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
363public:
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:
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         */
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         */
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
452private:
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