gfx/skia/trunk/include/core/SkClipStack.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/include/core/SkClipStack.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,451 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2011 Google Inc.
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +#ifndef SkClipStack_DEFINED
    1.12 +#define SkClipStack_DEFINED
    1.13 +
    1.14 +#include "SkDeque.h"
    1.15 +#include "SkPath.h"
    1.16 +#include "SkRect.h"
    1.17 +#include "SkRRect.h"
    1.18 +#include "SkRegion.h"
    1.19 +#include "SkTDArray.h"
    1.20 +#include "SkTLazy.h"
    1.21 +
    1.22 +
    1.23 +// Because a single save/restore state can have multiple clips, this class
    1.24 +// stores the stack depth (fSaveCount) and clips (fDeque) separately.
    1.25 +// Each clip in fDeque stores the stack state to which it belongs
    1.26 +// (i.e., the fSaveCount in force when it was added). Restores are thus
    1.27 +// implemented by removing clips from fDeque that have an fSaveCount larger
    1.28 +// then the freshly decremented count.
    1.29 +class SK_API SkClipStack {
    1.30 +public:
    1.31 +    enum BoundsType {
    1.32 +        // The bounding box contains all the pixels that can be written to
    1.33 +        kNormal_BoundsType,
    1.34 +        // The bounding box contains all the pixels that cannot be written to.
    1.35 +        // The real bound extends out to infinity and all the pixels outside
    1.36 +        // of the bound can be written to. Note that some of the pixels inside
    1.37 +        // the bound may also be writeable but all pixels that cannot be
    1.38 +        // written to are guaranteed to be inside.
    1.39 +        kInsideOut_BoundsType
    1.40 +    };
    1.41 +
    1.42 +    class Element {
    1.43 +    public:
    1.44 +        enum Type {
    1.45 +            //!< This element makes the clip empty (regardless of previous elements).
    1.46 +            kEmpty_Type,
    1.47 +            //!< This element combines a rect with the current clip using a set operation
    1.48 +            kRect_Type,
    1.49 +            //!< This element combines a round-rect with the current clip using a set operation
    1.50 +            kRRect_Type,
    1.51 +            //!< This element combines a path with the current clip using a set operation
    1.52 +            kPath_Type,
    1.53 +        };
    1.54 +
    1.55 +        Element() {
    1.56 +            this->initCommon(0, SkRegion::kReplace_Op, false);
    1.57 +            this->setEmpty();
    1.58 +        }
    1.59 +
    1.60 +        Element(const Element&);
    1.61 +
    1.62 +        Element(const SkRect& rect, SkRegion::Op op, bool doAA) {
    1.63 +            this->initRect(0, rect, op, doAA);
    1.64 +        }
    1.65 +
    1.66 +        Element(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
    1.67 +            this->initRRect(0, rrect, op, doAA);
    1.68 +        }
    1.69 +
    1.70 +        Element(const SkPath& path, SkRegion::Op op, bool doAA) {
    1.71 +            this->initPath(0, path, op, doAA);
    1.72 +        }
    1.73 +
    1.74 +        bool operator== (const Element& element) const;
    1.75 +        bool operator!= (const Element& element) const { return !(*this == element); }
    1.76 +
    1.77 +        //!< Call to get the type of the clip element.
    1.78 +        Type getType() const { return fType; }
    1.79 +
    1.80 +        //!< Call if getType() is kPath to get the path.
    1.81 +        const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return *fPath.get(); }
    1.82 +
    1.83 +        //!< Call if getType() is kRRect to get the round-rect.
    1.84 +        const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; }
    1.85 +
    1.86 +        //!< Call if getType() is kRect to get the rect.
    1.87 +        const SkRect& getRect() const {
    1.88 +            SkASSERT(kRect_Type == fType && (fRRect.isRect() || fRRect.isEmpty()));
    1.89 +            return fRRect.getBounds();
    1.90 +        }
    1.91 +
    1.92 +        //!< Call if getType() is not kEmpty to get the set operation used to combine this element.
    1.93 +        SkRegion::Op getOp() const { return fOp; }
    1.94 +
    1.95 +        //!< Call to get the element as a path, regardless of its type.
    1.96 +        void asPath(SkPath* path) const;
    1.97 +
    1.98 +        /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
    1.99 +            when it is rasterized. */
   1.100 +        bool isAA() const { return fDoAA; }
   1.101 +
   1.102 +        //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
   1.103 +        void invertShapeFillType();
   1.104 +
   1.105 +        //!< Sets the set operation represented by the element.
   1.106 +        void setOp(SkRegion::Op op) { fOp = op; }
   1.107 +
   1.108 +        /** The GenID can be used by clip stack clients to cache representations of the clip. The
   1.109 +            ID corresponds to the set of clip elements up to and including this element within the
   1.110 +            stack not to the element itself. That is the same clip path in different stacks will
   1.111 +            have a different ID since the elements produce different clip result in the context of
   1.112 +            their stacks. */
   1.113 +        int32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); return fGenID; }
   1.114 +
   1.115 +        /**
   1.116 +         * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
   1.117 +         * is inverse filled is not considered.)
   1.118 +         */
   1.119 +        const SkRect& getBounds() const {
   1.120 +            static const SkRect kEmpty = { 0, 0, 0, 0 };
   1.121 +            switch (fType) {
   1.122 +                case kRect_Type:  // fallthrough
   1.123 +                case kRRect_Type:
   1.124 +                    return fRRect.getBounds();
   1.125 +                case kPath_Type:
   1.126 +                    return fPath.get()->getBounds();
   1.127 +                case kEmpty_Type:
   1.128 +                    return kEmpty;
   1.129 +                default:
   1.130 +                    SkDEBUGFAIL("Unexpected type.");
   1.131 +                    return kEmpty;
   1.132 +            }
   1.133 +        }
   1.134 +
   1.135 +        /**
   1.136 +         * Conservatively checks whether the clip shape contains the rect param. (Whether the shape
   1.137 +         * is inverse filled is not considered.)
   1.138 +         */
   1.139 +        bool contains(const SkRect& rect) const {
   1.140 +            switch (fType) {
   1.141 +                case kRect_Type:
   1.142 +                    return this->getRect().contains(rect);
   1.143 +                case kRRect_Type:
   1.144 +                    return fRRect.contains(rect);
   1.145 +                case kPath_Type:
   1.146 +                    return fPath.get()->conservativelyContainsRect(rect);
   1.147 +                case kEmpty_Type:
   1.148 +                    return false;
   1.149 +                default:
   1.150 +                    SkDEBUGFAIL("Unexpected type.");
   1.151 +                    return false;
   1.152 +            }
   1.153 +        }
   1.154 +
   1.155 +        /**
   1.156 +         * Is the clip shape inverse filled.
   1.157 +         */
   1.158 +        bool isInverseFilled() const {
   1.159 +            return kPath_Type == fType && fPath.get()->isInverseFillType();
   1.160 +        }
   1.161 +
   1.162 +    private:
   1.163 +        friend class SkClipStack;
   1.164 +
   1.165 +        SkTLazy<SkPath> fPath;
   1.166 +        SkRRect         fRRect;
   1.167 +        int             fSaveCount; // save count of stack when this element was added.
   1.168 +        SkRegion::Op    fOp;
   1.169 +        Type            fType;
   1.170 +        bool            fDoAA;
   1.171 +
   1.172 +        /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
   1.173 +           bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
   1.174 +           conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
   1.175 +           drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
   1.176 +           occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
   1.177 +           box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
   1.178 +           the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
   1.179 +           infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
   1.180 +           can capture the cancelling out of the extensions to infinity when two inverse filled
   1.181 +           clips are Booleaned together. */
   1.182 +        SkClipStack::BoundsType fFiniteBoundType;
   1.183 +        SkRect                  fFiniteBound;
   1.184 +
   1.185 +        // When element is applied to the previous elements in the stack is the result known to be
   1.186 +        // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
   1.187 +        bool                    fIsIntersectionOfRects;
   1.188 +
   1.189 +        int                     fGenID;
   1.190 +
   1.191 +        Element(int saveCount) {
   1.192 +            this->initCommon(saveCount, SkRegion::kReplace_Op, false);
   1.193 +            this->setEmpty();
   1.194 +        }
   1.195 +
   1.196 +        Element(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) {
   1.197 +            this->initRRect(saveCount, rrect, op, doAA);
   1.198 +        }
   1.199 +
   1.200 +        Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
   1.201 +            this->initRect(saveCount, rect, op, doAA);
   1.202 +        }
   1.203 +
   1.204 +        Element(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) {
   1.205 +            this->initPath(saveCount, path, op, doAA);
   1.206 +        }
   1.207 +
   1.208 +        void initCommon(int saveCount, SkRegion::Op op, bool doAA) {
   1.209 +            fSaveCount = saveCount;
   1.210 +            fOp = op;
   1.211 +            fDoAA = doAA;
   1.212 +            // A default of inside-out and empty bounds means the bounds are effectively void as it
   1.213 +            // indicates that nothing is known to be outside the clip.
   1.214 +            fFiniteBoundType = kInsideOut_BoundsType;
   1.215 +            fFiniteBound.setEmpty();
   1.216 +            fIsIntersectionOfRects = false;
   1.217 +            fGenID = kInvalidGenID;
   1.218 +        }
   1.219 +
   1.220 +        void initRect(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
   1.221 +            fRRect.setRect(rect);
   1.222 +            fType = kRect_Type;
   1.223 +            this->initCommon(saveCount, op, doAA);
   1.224 +        }
   1.225 +
   1.226 +        void initRRect(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) {
   1.227 +            SkRRect::Type type = rrect.getType();
   1.228 +            fRRect = rrect;
   1.229 +            if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
   1.230 +                fType = kRect_Type;
   1.231 +            } else {
   1.232 +                fType = kRRect_Type;
   1.233 +            }
   1.234 +            this->initCommon(saveCount, op, doAA);
   1.235 +        }
   1.236 +
   1.237 +        void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA);
   1.238 +
   1.239 +        void setEmpty();
   1.240 +
   1.241 +        // All Element methods below are only used within SkClipStack.cpp
   1.242 +        inline void checkEmpty() const;
   1.243 +        inline bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const;
   1.244 +        /* This method checks to see if two rect clips can be safely merged into one. The issue here
   1.245 +          is that to be strictly correct all the edges of the resulting rect must have the same
   1.246 +          anti-aliasing. */
   1.247 +        bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
   1.248 +        /** Determines possible finite bounds for the Element given the previous element of the
   1.249 +            stack */
   1.250 +        void updateBoundAndGenID(const Element* prior);
   1.251 +        // The different combination of fill & inverse fill when combining bounding boxes
   1.252 +        enum FillCombo {
   1.253 +            kPrev_Cur_FillCombo,
   1.254 +            kPrev_InvCur_FillCombo,
   1.255 +            kInvPrev_Cur_FillCombo,
   1.256 +            kInvPrev_InvCur_FillCombo
   1.257 +        };
   1.258 +        // per-set operation functions used by updateBoundAndGenID().
   1.259 +        inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
   1.260 +        inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
   1.261 +        inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
   1.262 +        inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
   1.263 +        inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
   1.264 +    };
   1.265 +
   1.266 +    SkClipStack();
   1.267 +    SkClipStack(const SkClipStack& b);
   1.268 +    explicit SkClipStack(const SkRect& r);
   1.269 +    explicit SkClipStack(const SkIRect& r);
   1.270 +    ~SkClipStack();
   1.271 +
   1.272 +    SkClipStack& operator=(const SkClipStack& b);
   1.273 +    bool operator==(const SkClipStack& b) const;
   1.274 +    bool operator!=(const SkClipStack& b) const { return !(*this == b); }
   1.275 +
   1.276 +    void reset();
   1.277 +
   1.278 +    int getSaveCount() const { return fSaveCount; }
   1.279 +    void save();
   1.280 +    void restore();
   1.281 +
   1.282 +    /**
   1.283 +     * getBounds places the current finite bound in its first parameter. In its
   1.284 +     * second, it indicates which kind of bound is being returned. If
   1.285 +     * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
   1.286 +     * pixels. If 'canvFiniteBound' is an inside out bounding box then it
   1.287 +     * encloses all the un-writeable pixels and the true/normal bound is the
   1.288 +     * infinite plane. isIntersectionOfRects is an optional parameter
   1.289 +     * that is true if 'canvFiniteBound' resulted from an intersection of rects.
   1.290 +     */
   1.291 +    void getBounds(SkRect* canvFiniteBound,
   1.292 +                   BoundsType* boundType,
   1.293 +                   bool* isIntersectionOfRects = NULL) const;
   1.294 +
   1.295 +    /**
   1.296 +     * Takes an input rect in device space and conservatively clips it to the
   1.297 +     * clip-stack. If false is returned then the rect does not intersect the
   1.298 +     * clip and is unmodified.
   1.299 +     */
   1.300 +    bool intersectRectWithClip(SkRect* devRect) const;
   1.301 +
   1.302 +    /**
   1.303 +     * Returns true if the input rect in device space is entirely contained
   1.304 +     * by the clip. A return value of false does not guarantee that the rect
   1.305 +     * is not contained by the clip.
   1.306 +     */
   1.307 +    bool quickContains(const SkRect& devRect) const;
   1.308 +
   1.309 +    void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
   1.310 +        SkRect r;
   1.311 +        r.set(ir);
   1.312 +        this->clipDevRect(r, op, false);
   1.313 +    }
   1.314 +    void clipDevRect(const SkRect&, SkRegion::Op, bool doAA);
   1.315 +    void clipDevRRect(const SkRRect&, SkRegion::Op, bool doAA);
   1.316 +    void clipDevPath(const SkPath&, SkRegion::Op, bool doAA);
   1.317 +    // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
   1.318 +    void clipEmpty();
   1.319 +
   1.320 +    /**
   1.321 +     * isWideOpen returns true if the clip state corresponds to the infinite
   1.322 +     * plane (i.e., draws are not limited at all)
   1.323 +     */
   1.324 +    bool isWideOpen() const;
   1.325 +
   1.326 +    /**
   1.327 +     * The generation ID has three reserved values to indicate special
   1.328 +     * (potentially ignorable) cases
   1.329 +     */
   1.330 +    static const int32_t kInvalidGenID = 0;     //!< Invalid id that is never returned by
   1.331 +                                                //!< SkClipStack. Useful when caching clips
   1.332 +                                                //!< based on GenID.
   1.333 +    static const int32_t kEmptyGenID = 1;       // no pixels writeable
   1.334 +    static const int32_t kWideOpenGenID = 2;    // all pixels writeable
   1.335 +
   1.336 +    int32_t getTopmostGenID() const;
   1.337 +
   1.338 +public:
   1.339 +    class Iter {
   1.340 +    public:
   1.341 +        enum IterStart {
   1.342 +            kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
   1.343 +            kTop_IterStart = SkDeque::Iter::kBack_IterStart
   1.344 +        };
   1.345 +
   1.346 +        /**
   1.347 +         * Creates an uninitialized iterator. Must be reset()
   1.348 +         */
   1.349 +        Iter();
   1.350 +
   1.351 +        Iter(const SkClipStack& stack, IterStart startLoc);
   1.352 +
   1.353 +        /**
   1.354 +         *  Return the clip element for this iterator. If next()/prev() returns NULL, then the
   1.355 +         *  iterator is done.
   1.356 +         */
   1.357 +        const Element* next();
   1.358 +        const Element* prev();
   1.359 +
   1.360 +        /**
   1.361 +         * Moves the iterator to the topmost element with the specified RegionOp and returns that
   1.362 +         * element. If no clip element with that op is found, the first element is returned.
   1.363 +         */
   1.364 +        const Element* skipToTopmost(SkRegion::Op op);
   1.365 +
   1.366 +        /**
   1.367 +         * Restarts the iterator on a clip stack.
   1.368 +         */
   1.369 +        void reset(const SkClipStack& stack, IterStart startLoc);
   1.370 +
   1.371 +    private:
   1.372 +        const SkClipStack* fStack;
   1.373 +        SkDeque::Iter      fIter;
   1.374 +    };
   1.375 +
   1.376 +    /**
   1.377 +     * The B2TIter iterates from the bottom of the stack to the top.
   1.378 +     * It inherits privately from Iter to prevent access to reverse iteration.
   1.379 +     */
   1.380 +    class B2TIter : private Iter {
   1.381 +    public:
   1.382 +        B2TIter() {}
   1.383 +
   1.384 +        /**
   1.385 +         * Wrap Iter's 2 parameter ctor to force initialization to the
   1.386 +         * beginning of the deque/bottom of the stack
   1.387 +         */
   1.388 +        B2TIter(const SkClipStack& stack)
   1.389 +        : INHERITED(stack, kBottom_IterStart) {
   1.390 +        }
   1.391 +
   1.392 +        using Iter::next;
   1.393 +
   1.394 +        /**
   1.395 +         * Wrap Iter::reset to force initialization to the
   1.396 +         * beginning of the deque/bottom of the stack
   1.397 +         */
   1.398 +        void reset(const SkClipStack& stack) {
   1.399 +            this->INHERITED::reset(stack, kBottom_IterStart);
   1.400 +        }
   1.401 +
   1.402 +    private:
   1.403 +
   1.404 +        typedef Iter INHERITED;
   1.405 +    };
   1.406 +
   1.407 +    /**
   1.408 +     * GetConservativeBounds returns a conservative bound of the current clip.
   1.409 +     * Since this could be the infinite plane (if inverse fills were involved) the
   1.410 +     * maxWidth and maxHeight parameters can be used to limit the returned bound
   1.411 +     * to the expected drawing area. Similarly, the offsetX and offsetY parameters
   1.412 +     * allow the caller to offset the returned bound to account for translated
   1.413 +     * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
   1.414 +     * the translation (+offsetX, +offsetY) is applied before the clamp to the
   1.415 +     * maximum rectangle: [0,maxWidth) x [0,maxHeight).
   1.416 +     * isIntersectionOfRects is an optional parameter that is true when
   1.417 +     * 'devBounds' is the result of an intersection of rects. In this case
   1.418 +     * 'devBounds' is the exact answer/clip.
   1.419 +     */
   1.420 +    void getConservativeBounds(int offsetX,
   1.421 +                               int offsetY,
   1.422 +                               int maxWidth,
   1.423 +                               int maxHeight,
   1.424 +                               SkRect* devBounds,
   1.425 +                               bool* isIntersectionOfRects = NULL) const;
   1.426 +
   1.427 +private:
   1.428 +    friend class Iter;
   1.429 +
   1.430 +    SkDeque fDeque;
   1.431 +    int     fSaveCount;
   1.432 +
   1.433 +    // Generation ID for the clip stack. This is incremented for each
   1.434 +    // clipDevRect and clipDevPath call. 0 is reserved to indicate an
   1.435 +    // invalid ID.
   1.436 +    static int32_t     gGenID;
   1.437 +
   1.438 +    /**
   1.439 +     * Helper for clipDevPath, etc.
   1.440 +     */
   1.441 +    void pushElement(const Element& element);
   1.442 +
   1.443 +    /**
   1.444 +     * Restore the stack back to the specified save count.
   1.445 +     */
   1.446 +    void restoreTo(int saveCount);
   1.447 +
   1.448 +    /**
   1.449 +     * Return the next unique generation ID.
   1.450 +     */
   1.451 +    static int32_t GetNextGenID();
   1.452 +};
   1.453 +
   1.454 +#endif

mercurial