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