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