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