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