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 48 Element() { 49 this->initCommon(0, SkRegion::kReplace_Op, false); 50 this->setEmpty(); 51 } 52 53 Element(const SkRect& rect, SkRegion::Op op, bool doAA) { 54 this->initRect(0, rect, op, doAA); 55 } 56 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. 86 Type getType() const { return fType; } 87 88 //!< Call if getType() is kPath to get the path. 89 const SkPath& getPath() const { return fPath; } 90 91 //!< Call if getType() is kRect to get the rect. 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. 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. */ 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. 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. */ 112 int32_t getGenID() const { SkASSERT(kInvalidGenID != fGenID); 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 */ 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 */ 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 */ 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 187 Element(int saveCount) { 188 this->initCommon(saveCount, SkRegion::kReplace_Op, false); 189 this->setEmpty(); 190 } 191 192 Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) { 193 this->initRect(saveCount, rect, op, doAA); 194 } 195 196 Element(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) { 197 this->initPath(saveCount, path, op, doAA); 198 } 199 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 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 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 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 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 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 * The generation ID has three reserved values to indicate special 320 * (potentially ignorable) cases 321 */ 322 static const int32_t kInvalidGenID = 0; //!< Invalid id that is never returned by 323 //!< SkClipStack. Useful when caching clips 324 //!< based on GenID. 325 static const int32_t kEmptyGenID = 1; // no pixels writeable 326 static const int32_t kWideOpenGenID = 2; // all pixels writeable 327 328 int32_t getTopmostGenID() const; 329 330 public: 331 class Iter { 332 public: 333 enum IterStart { 334 kBottom_IterStart = SkDeque::Iter::kFront_IterStart, 335 kTop_IterStart = SkDeque::Iter::kBack_IterStart 336 }; 337 338 /** 339 * Creates an uninitialized iterator. Must be reset() 340 */ 341 Iter(); 342 343 Iter(const SkClipStack& stack, IterStart startLoc); 344 345 /** 346 * Return the clip element for this iterator. If next()/prev() returns NULL, then the 347 * iterator is done. 348 */ 349 const Element* next(); 350 const Element* prev(); 351 352 /** 353 * Moves the iterator to the topmost element with the specified RegionOp and returns that 354 * element. If no clip element with that op is found, the first element is returned. 355 */ 356 const Element* skipToTopmost(SkRegion::Op op); 357 358 /** 359 * Restarts the iterator on a clip stack. 360 */ 361 void reset(const SkClipStack& stack, IterStart startLoc); 362 363 private: 364 const SkClipStack* fStack; 365 SkDeque::Iter fIter; 366 }; 367 368 /** 369 * The B2TIter iterates from the bottom of the stack to the top. 370 * It inherits privately from Iter to prevent access to reverse iteration. 371 */ 372 class B2TIter : private Iter { 373 public: 374 B2TIter() {} 375 376 /** 377 * Wrap Iter's 2 parameter ctor to force initialization to the 378 * beginning of the deque/bottom of the stack 379 */ 380 B2TIter(const SkClipStack& stack) 381 : INHERITED(stack, kBottom_IterStart) { 382 } 383 384 using Iter::next; 385 386 /** 387 * Wrap Iter::reset to force initialization to the 388 * beginning of the deque/bottom of the stack 389 */ 390 void reset(const SkClipStack& stack) { 391 this->INHERITED::reset(stack, kBottom_IterStart); 392 } 393 394 private: 395 396 typedef Iter INHERITED; 397 }; 398 399 /** 400 * GetConservativeBounds returns a conservative bound of the current clip. 401 * Since this could be the infinite plane (if inverse fills were involved) the 402 * maxWidth and maxHeight parameters can be used to limit the returned bound 403 * to the expected drawing area. Similarly, the offsetX and offsetY parameters 404 * allow the caller to offset the returned bound to account for translated 405 * drawing areas (i.e., those resulting from a saveLayer). For finite bounds, 406 * the translation (+offsetX, +offsetY) is applied before the clamp to the 407 * maximum rectangle: [0,maxWidth) x [0,maxHeight). 408 * isIntersectionOfRects is an optional parameter that is true when 409 * 'devBounds' is the result of an intersection of rects. In this case 410 * 'devBounds' is the exact answer/clip. 411 */ 412 void getConservativeBounds(int offsetX, 413 int offsetY, 414 int maxWidth, 415 int maxHeight, 416 SkRect* devBounds, 417 bool* isIntersectionOfRects = NULL) const; 418 419 private: 420 friend class Iter; 421 422 SkDeque fDeque; 423 int fSaveCount; 424 425 // Generation ID for the clip stack. This is incremented for each 426 // clipDevRect and clipDevPath call. 0 is reserved to indicate an 427 // invalid ID. 428 static int32_t gGenID; 429 430 /** 431 * Restore the stack back to the specified save count. 432 */ 433 void restoreTo(int saveCount); 434 435 /** 436 * Return the next unique generation ID. 437 */ 438 static int32_t GetNextGenID(); 439 }; 440 441 #endif 442