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