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 "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.
     24 class SK_API SkClipStack {
     25 public:
     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 { 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      * Add a callback function that will be called whenever a clip state
    320      * is no longer viable. This will occur whenever restore
    321      * is called or when a clipDevRect or clipDevPath call updates the
    322      * clip within an existing save/restore state. Each clip state is
    323      * represented by a unique generation ID.
    324      */
    325     typedef void (*PFPurgeClipCB)(int genID, void* data);
    326     void addPurgeClipCallback(PFPurgeClipCB callback, void* data) const;
    327 
    328     /**
    329      * Remove a callback added earlier via addPurgeClipCallback
    330      */
    331     void removePurgeClipCallback(PFPurgeClipCB callback, void* data) const;
    332 
    333     /**
    334      * The generation ID has three reserved values to indicate special
    335      * (potentially ignorable) cases
    336      */
    337     static const int32_t kInvalidGenID = 0;
    338     static const int32_t kEmptyGenID = 1;       // no pixels writeable
    339     static const int32_t kWideOpenGenID = 2;    // all pixels writeable
    340 
    341     int32_t getTopmostGenID() const;
    342 
    343 public:
    344     class Iter {
    345     public:
    346         enum IterStart {
    347             kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
    348             kTop_IterStart = SkDeque::Iter::kBack_IterStart
    349         };
    350 
    351         /**
    352          * Creates an uninitialized iterator. Must be reset()
    353          */
    354         Iter();
    355 
    356         Iter(const SkClipStack& stack, IterStart startLoc);
    357 
    358         /**
    359          *  Return the clip element for this iterator. If next()/prev() returns NULL, then the
    360          *  iterator is done.
    361          */
    362         const Element* next();
    363         const Element* prev();
    364 
    365         /**
    366          * Moves the iterator to the topmost element with the specified RegionOp and returns that
    367          * element. If no clip element with that op is found, the first element is returned.
    368          */
    369         const Element* skipToTopmost(SkRegion::Op op);
    370 
    371         /**
    372          * Restarts the iterator on a clip stack.
    373          */
    374         void reset(const SkClipStack& stack, IterStart startLoc);
    375 
    376     private:
    377         const SkClipStack* fStack;
    378         SkDeque::Iter      fIter;
    379     };
    380 
    381     /**
    382      * The B2TIter iterates from the bottom of the stack to the top.
    383      * It inherits privately from Iter to prevent access to reverse iteration.
    384      */
    385     class B2TIter : private Iter {
    386     public:
    387         B2TIter() {}
    388 
    389         /**
    390          * Wrap Iter's 2 parameter ctor to force initialization to the
    391          * beginning of the deque/bottom of the stack
    392          */
    393         B2TIter(const SkClipStack& stack)
    394         : INHERITED(stack, kBottom_IterStart) {
    395         }
    396 
    397         using Iter::next;
    398 
    399         /**
    400          * Wrap Iter::reset to force initialization to the
    401          * beginning of the deque/bottom of the stack
    402          */
    403         void reset(const SkClipStack& stack) {
    404             this->INHERITED::reset(stack, kBottom_IterStart);
    405         }
    406 
    407     private:
    408 
    409         typedef Iter INHERITED;
    410     };
    411 
    412     /**
    413      * GetConservativeBounds returns a conservative bound of the current clip.
    414      * Since this could be the infinite plane (if inverse fills were involved) the
    415      * maxWidth and maxHeight parameters can be used to limit the returned bound
    416      * to the expected drawing area. Similarly, the offsetX and offsetY parameters
    417      * allow the caller to offset the returned bound to account for translated
    418      * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
    419      * the translation (+offsetX, +offsetY) is applied before the clamp to the
    420      * maximum rectangle: [0,maxWidth) x [0,maxHeight).
    421      * isIntersectionOfRects is an optional parameter that is true when
    422      * 'devBounds' is the result of an intersection of rects. In this case
    423      * 'devBounds' is the exact answer/clip.
    424      */
    425     void getConservativeBounds(int offsetX,
    426                                int offsetY,
    427                                int maxWidth,
    428                                int maxHeight,
    429                                SkRect* devBounds,
    430                                bool* isIntersectionOfRects = NULL) const;
    431 
    432 private:
    433     friend class Iter;
    434 
    435     SkDeque fDeque;
    436     int     fSaveCount;
    437 
    438     // Generation ID for the clip stack. This is incremented for each
    439     // clipDevRect and clipDevPath call. 0 is reserved to indicate an
    440     // invalid ID.
    441     static int32_t     gGenID;
    442 
    443     struct ClipCallbackData {
    444         PFPurgeClipCB   fCallback;
    445         void*           fData;
    446 
    447         friend bool operator==(const ClipCallbackData& a,
    448                                const ClipCallbackData& b) {
    449             return a.fCallback == b.fCallback && a.fData == b.fData;
    450         }
    451     };
    452 
    453     mutable SkTDArray<ClipCallbackData> fCallbackData;
    454 
    455     /**
    456      * Invoke all the purge callbacks passing in element's generation ID.
    457      */
    458     void purgeClip(Element* element);
    459 
    460     /**
    461      * Return the next unique generation ID.
    462      */
    463     static int32_t GetNextGenID();
    464 };
    465 
    466 #endif
    467