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