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