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