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