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