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