Home | History | Annotate | Download | only in core
      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 
     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 
    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:
    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 
    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