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 #include "SkClipStack.h"
9 #include "SkPath.h"
10 #include "SkThread.h"
11
12 #include <new>
13
14
15 // 0-2 are reserved for invalid, empty & wide-open
16 static const int32_t kFirstUnreservedGenID = 3;
17 int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
18
invertShapeFillType()19 void SkClipStack::Element::invertShapeFillType() {
20 switch (fType) {
21 case kRect_Type:
22 fPath.reset();
23 fPath.addRect(fRect);
24 fPath.setFillType(SkPath::kInverseWinding_FillType);
25 fType = kPath_Type;
26 break;
27 case kPath_Type:
28 fPath.toggleInverseFillType();
29 case kEmpty_Type:
30 break;
31 }
32 }
33
checkEmpty() const34 void SkClipStack::Element::checkEmpty() const {
35 SkASSERT(fFiniteBound.isEmpty());
36 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
37 SkASSERT(!fIsIntersectionOfRects);
38 SkASSERT(kEmptyGenID == fGenID);
39 SkASSERT(fPath.isEmpty());
40 }
41
canBeIntersectedInPlace(int saveCount,SkRegion::Op op) const42 bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
43 if (kEmpty_Type == fType &&
44 (SkRegion::kDifference_Op == op || SkRegion::kIntersect_Op == op)) {
45 return true;
46 }
47 // Only clips within the same save/restore frame (as captured by
48 // the save count) can be merged
49 return fSaveCount == saveCount &&
50 SkRegion::kIntersect_Op == op &&
51 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
52 }
53
rectRectIntersectAllowed(const SkRect & newR,bool newAA) const54 bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
55 SkASSERT(kRect_Type == fType);
56
57 if (fDoAA == newAA) {
58 // if the AA setting is the same there is no issue
59 return true;
60 }
61
62 if (!SkRect::Intersects(fRect, newR)) {
63 // The calling code will correctly set the result to the empty clip
64 return true;
65 }
66
67 if (fRect.contains(newR)) {
68 // if the new rect carves out a portion of the old one there is no
69 // issue
70 return true;
71 }
72
73 // So either the two overlap in some complex manner or newR contains oldR.
74 // In the first, case the edges will require different AA. In the second,
75 // the AA setting that would be carried forward is incorrect (e.g., oldR
76 // is AA while newR is BW but since newR contains oldR, oldR will be
77 // drawn BW) since the new AA setting will predominate.
78 return false;
79 }
80
81 // a mirror of combineBoundsRevDiff
combineBoundsDiff(FillCombo combination,const SkRect & prevFinite)82 void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
83 switch (combination) {
84 case kInvPrev_InvCur_FillCombo:
85 // In this case the only pixels that can remain set
86 // are inside the current clip rect since the extensions
87 // to infinity of both clips cancel out and whatever
88 // is outside of the current clip is removed
89 fFiniteBoundType = kNormal_BoundsType;
90 break;
91 case kInvPrev_Cur_FillCombo:
92 // In this case the current op is finite so the only pixels
93 // that aren't set are whatever isn't set in the previous
94 // clip and whatever this clip carves out
95 fFiniteBound.join(prevFinite);
96 fFiniteBoundType = kInsideOut_BoundsType;
97 break;
98 case kPrev_InvCur_FillCombo:
99 // In this case everything outside of this clip's bound
100 // is erased, so the only pixels that can remain set
101 // occur w/in the intersection of the two finite bounds
102 if (!fFiniteBound.intersect(prevFinite)) {
103 fFiniteBound.setEmpty();
104 fGenID = kEmptyGenID;
105 }
106 fFiniteBoundType = kNormal_BoundsType;
107 break;
108 case kPrev_Cur_FillCombo:
109 // The most conservative result bound is that of the
110 // prior clip. This could be wildly incorrect if the
111 // second clip either exactly matches the first clip
112 // (which should yield the empty set) or reduces the
113 // size of the prior bound (e.g., if the second clip
114 // exactly matched the bottom half of the prior clip).
115 // We ignore these two possibilities.
116 fFiniteBound = prevFinite;
117 break;
118 default:
119 SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination");
120 break;
121 }
122 }
123
combineBoundsXOR(int combination,const SkRect & prevFinite)124 void SkClipStack::Element::combineBoundsXOR(int combination, const SkRect& prevFinite) {
125
126 switch (combination) {
127 case kInvPrev_Cur_FillCombo: // fall through
128 case kPrev_InvCur_FillCombo:
129 // With only one of the clips inverted the result will always
130 // extend to infinity. The only pixels that may be un-writeable
131 // lie within the union of the two finite bounds
132 fFiniteBound.join(prevFinite);
133 fFiniteBoundType = kInsideOut_BoundsType;
134 break;
135 case kInvPrev_InvCur_FillCombo:
136 // The only pixels that can survive are within the
137 // union of the two bounding boxes since the extensions
138 // to infinity of both clips cancel out
139 // fall through!
140 case kPrev_Cur_FillCombo:
141 // The most conservative bound for xor is the
142 // union of the two bounds. If the two clips exactly overlapped
143 // the xor could yield the empty set. Similarly the xor
144 // could reduce the size of the original clip's bound (e.g.,
145 // if the second clip exactly matched the bottom half of the
146 // first clip). We ignore these two cases.
147 fFiniteBound.join(prevFinite);
148 fFiniteBoundType = kNormal_BoundsType;
149 break;
150 default:
151 SkDEBUGFAIL("SkClipStack::Element::combineBoundsXOR Invalid fill combination");
152 break;
153 }
154 }
155
156 // a mirror of combineBoundsIntersection
combineBoundsUnion(int combination,const SkRect & prevFinite)157 void SkClipStack::Element::combineBoundsUnion(int combination, const SkRect& prevFinite) {
158
159 switch (combination) {
160 case kInvPrev_InvCur_FillCombo:
161 if (!fFiniteBound.intersect(prevFinite)) {
162 fFiniteBound.setEmpty();
163 fGenID = kWideOpenGenID;
164 }
165 fFiniteBoundType = kInsideOut_BoundsType;
166 break;
167 case kInvPrev_Cur_FillCombo:
168 // The only pixels that won't be drawable are inside
169 // the prior clip's finite bound
170 fFiniteBound = prevFinite;
171 fFiniteBoundType = kInsideOut_BoundsType;
172 break;
173 case kPrev_InvCur_FillCombo:
174 // The only pixels that won't be drawable are inside
175 // this clip's finite bound
176 break;
177 case kPrev_Cur_FillCombo:
178 fFiniteBound.join(prevFinite);
179 break;
180 default:
181 SkDEBUGFAIL("SkClipStack::Element::combineBoundsUnion Invalid fill combination");
182 break;
183 }
184 }
185
186 // a mirror of combineBoundsUnion
combineBoundsIntersection(int combination,const SkRect & prevFinite)187 void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
188
189 switch (combination) {
190 case kInvPrev_InvCur_FillCombo:
191 // The only pixels that aren't writable in this case
192 // occur in the union of the two finite bounds
193 fFiniteBound.join(prevFinite);
194 fFiniteBoundType = kInsideOut_BoundsType;
195 break;
196 case kInvPrev_Cur_FillCombo:
197 // In this case the only pixels that will remain writeable
198 // are within the current clip
199 break;
200 case kPrev_InvCur_FillCombo:
201 // In this case the only pixels that will remain writeable
202 // are with the previous clip
203 fFiniteBound = prevFinite;
204 fFiniteBoundType = kNormal_BoundsType;
205 break;
206 case kPrev_Cur_FillCombo:
207 if (!fFiniteBound.intersect(prevFinite)) {
208 fFiniteBound.setEmpty();
209 fGenID = kEmptyGenID;
210 }
211 break;
212 default:
213 SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
214 break;
215 }
216 }
217
218 // a mirror of combineBoundsDiff
combineBoundsRevDiff(int combination,const SkRect & prevFinite)219 void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& prevFinite) {
220
221 switch (combination) {
222 case kInvPrev_InvCur_FillCombo:
223 // The only pixels that can survive are in the
224 // previous bound since the extensions to infinity in
225 // both clips cancel out
226 fFiniteBound = prevFinite;
227 fFiniteBoundType = kNormal_BoundsType;
228 break;
229 case kInvPrev_Cur_FillCombo:
230 if (!fFiniteBound.intersect(prevFinite)) {
231 fFiniteBound.setEmpty();
232 fGenID = kEmptyGenID;
233 }
234 fFiniteBoundType = kNormal_BoundsType;
235 break;
236 case kPrev_InvCur_FillCombo:
237 fFiniteBound.join(prevFinite);
238 fFiniteBoundType = kInsideOut_BoundsType;
239 break;
240 case kPrev_Cur_FillCombo:
241 // Fall through - as with the kDifference_Op case, the
242 // most conservative result bound is the bound of the
243 // current clip. The prior clip could reduce the size of this
244 // bound (as in the kDifference_Op case) but we are ignoring
245 // those cases.
246 break;
247 default:
248 SkDEBUGFAIL("SkClipStack::Element::combineBoundsRevDiff Invalid fill combination");
249 break;
250 }
251 }
252
updateBoundAndGenID(const Element * prior)253 void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
254 // We set this first here but we may overwrite it later if we determine that the clip is
255 // either wide-open or empty.
256 fGenID = GetNextGenID();
257
258 // First, optimistically update the current Element's bound information
259 // with the current clip's bound
260 fIsIntersectionOfRects = false;
261 if (kRect_Type == fType) {
262 fFiniteBound = fRect;
263 fFiniteBoundType = kNormal_BoundsType;
264
265 if (SkRegion::kReplace_Op == fOp ||
266 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
267 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
268 prior->rectRectIntersectAllowed(fRect, fDoAA))) {
269 fIsIntersectionOfRects = true;
270 }
271
272 } else {
273 SkASSERT(kPath_Type == fType);
274
275 fFiniteBound = fPath.getBounds();
276
277 if (fPath.isInverseFillType()) {
278 fFiniteBoundType = kInsideOut_BoundsType;
279 } else {
280 fFiniteBoundType = kNormal_BoundsType;
281 }
282 }
283
284 if (!fDoAA) {
285 // Here we mimic a non-anti-aliased scanline system. If there is
286 // no anti-aliasing we can integerize the bounding box to exclude
287 // fractional parts that won't be rendered.
288 // Note: the left edge is handled slightly differently below. We
289 // are a bit more generous in the rounding since we don't want to
290 // risk missing the left pixels when fLeft is very close to .5
291 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
292 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
293 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
294 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
295 }
296
297 // Now determine the previous Element's bound information taking into
298 // account that there may be no previous clip
299 SkRect prevFinite;
300 SkClipStack::BoundsType prevType;
301
302 if (NULL == prior) {
303 // no prior clip means the entire plane is writable
304 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
305 prevType = kInsideOut_BoundsType;
306 } else {
307 prevFinite = prior->fFiniteBound;
308 prevType = prior->fFiniteBoundType;
309 }
310
311 FillCombo combination = kPrev_Cur_FillCombo;
312 if (kInsideOut_BoundsType == fFiniteBoundType) {
313 combination = (FillCombo) (combination | 0x01);
314 }
315 if (kInsideOut_BoundsType == prevType) {
316 combination = (FillCombo) (combination | 0x02);
317 }
318
319 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
320 kInvPrev_Cur_FillCombo == combination ||
321 kPrev_InvCur_FillCombo == combination ||
322 kPrev_Cur_FillCombo == combination);
323
324 // Now integrate with clip with the prior clips
325 switch (fOp) {
326 case SkRegion::kDifference_Op:
327 this->combineBoundsDiff(combination, prevFinite);
328 break;
329 case SkRegion::kXOR_Op:
330 this->combineBoundsXOR(combination, prevFinite);
331 break;
332 case SkRegion::kUnion_Op:
333 this->combineBoundsUnion(combination, prevFinite);
334 break;
335 case SkRegion::kIntersect_Op:
336 this->combineBoundsIntersection(combination, prevFinite);
337 break;
338 case SkRegion::kReverseDifference_Op:
339 this->combineBoundsRevDiff(combination, prevFinite);
340 break;
341 case SkRegion::kReplace_Op:
342 // Replace just ignores everything prior
343 // The current clip's bound information is already filled in
344 // so nothing to do
345 break;
346 default:
347 SkDebugf("SkRegion::Op error/n");
348 SkASSERT(0);
349 break;
350 }
351 }
352
353 // This constant determines how many Element's are allocated together as a block in
354 // the deque. As such it needs to balance allocating too much memory vs.
355 // incurring allocation/deallocation thrashing. It should roughly correspond to
356 // the deepest save/restore stack we expect to see.
357 static const int kDefaultElementAllocCnt = 8;
358
SkClipStack()359 SkClipStack::SkClipStack()
360 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
361 , fSaveCount(0) {
362 }
363
SkClipStack(const SkClipStack & b)364 SkClipStack::SkClipStack(const SkClipStack& b)
365 : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
366 *this = b;
367 }
368
SkClipStack(const SkRect & r)369 SkClipStack::SkClipStack(const SkRect& r)
370 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
371 , fSaveCount(0) {
372 if (!r.isEmpty()) {
373 this->clipDevRect(r, SkRegion::kReplace_Op, false);
374 }
375 }
376
SkClipStack(const SkIRect & r)377 SkClipStack::SkClipStack(const SkIRect& r)
378 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
379 , fSaveCount(0) {
380 if (!r.isEmpty()) {
381 SkRect temp;
382 temp.set(r);
383 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
384 }
385 }
386
~SkClipStack()387 SkClipStack::~SkClipStack() {
388 reset();
389 }
390
operator =(const SkClipStack & b)391 SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
392 if (this == &b) {
393 return *this;
394 }
395 reset();
396
397 fSaveCount = b.fSaveCount;
398 SkDeque::F2BIter recIter(b.fDeque);
399 for (const Element* element = (const Element*)recIter.next();
400 element != NULL;
401 element = (const Element*)recIter.next()) {
402 new (fDeque.push_back()) Element(*element);
403 }
404
405 return *this;
406 }
407
operator ==(const SkClipStack & b) const408 bool SkClipStack::operator==(const SkClipStack& b) const {
409 if (fSaveCount != b.fSaveCount ||
410 fDeque.count() != b.fDeque.count()) {
411 return false;
412 }
413 SkDeque::F2BIter myIter(fDeque);
414 SkDeque::F2BIter bIter(b.fDeque);
415 const Element* myElement = (const Element*)myIter.next();
416 const Element* bElement = (const Element*)bIter.next();
417
418 while (myElement != NULL && bElement != NULL) {
419 if (*myElement != *bElement) {
420 return false;
421 }
422 myElement = (const Element*)myIter.next();
423 bElement = (const Element*)bIter.next();
424 }
425 return myElement == NULL && bElement == NULL;
426 }
427
reset()428 void SkClipStack::reset() {
429 // We used a placement new for each object in fDeque, so we're responsible
430 // for calling the destructor on each of them as well.
431 while (!fDeque.empty()) {
432 Element* element = (Element*)fDeque.back();
433 element->~Element();
434 fDeque.pop_back();
435 }
436
437 fSaveCount = 0;
438 }
439
save()440 void SkClipStack::save() {
441 fSaveCount += 1;
442 }
443
restore()444 void SkClipStack::restore() {
445 fSaveCount -= 1;
446 while (!fDeque.empty()) {
447 Element* element = (Element*)fDeque.back();
448 if (element->fSaveCount <= fSaveCount) {
449 break;
450 }
451 this->purgeClip(element);
452 element->~Element();
453 fDeque.pop_back();
454 }
455 }
456
getBounds(SkRect * canvFiniteBound,BoundsType * boundType,bool * isIntersectionOfRects) const457 void SkClipStack::getBounds(SkRect* canvFiniteBound,
458 BoundsType* boundType,
459 bool* isIntersectionOfRects) const {
460 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
461
462 Element* element = (Element*)fDeque.back();
463
464 if (NULL == element) {
465 // the clip is wide open - the infinite plane w/ no pixels un-writeable
466 canvFiniteBound->setEmpty();
467 *boundType = kInsideOut_BoundsType;
468 if (NULL != isIntersectionOfRects) {
469 *isIntersectionOfRects = false;
470 }
471 return;
472 }
473
474 *canvFiniteBound = element->fFiniteBound;
475 *boundType = element->fFiniteBoundType;
476 if (NULL != isIntersectionOfRects) {
477 *isIntersectionOfRects = element->fIsIntersectionOfRects;
478 }
479 }
480
intersectRectWithClip(SkRect * rect) const481 bool SkClipStack::intersectRectWithClip(SkRect* rect) const {
482 SkASSERT(NULL != rect);
483
484 SkRect bounds;
485 SkClipStack::BoundsType bt;
486 this->getBounds(&bounds, &bt);
487 if (bt == SkClipStack::kInsideOut_BoundsType) {
488 if (bounds.contains(*rect)) {
489 return false;
490 } else {
491 // If rect's x values are both within bound's x range we
492 // could clip here. Same for y. But we don't bother to check.
493 return true;
494 }
495 } else {
496 return rect->intersect(bounds);
497 }
498 }
499
quickContains(const SkRect & rect) const500 bool SkClipStack::quickContains(const SkRect& rect) const {
501
502 Iter iter(*this, Iter::kTop_IterStart);
503 const Element* element = iter.prev();
504 while (element != NULL) {
505 if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp())
506 return false;
507 if (element->isInverseFilled()) {
508 // Part of 'rect' could be trimmed off by the inverse-filled clip element
509 if (SkRect::Intersects(element->getBounds(), rect)) {
510 return false;
511 }
512 } else {
513 if (!element->contains(rect)) {
514 return false;
515 }
516 }
517 if (SkRegion::kReplace_Op == element->getOp()) {
518 break;
519 }
520 element = iter.prev();
521 }
522 return true;
523 }
524
clipDevRect(const SkRect & rect,SkRegion::Op op,bool doAA)525 void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
526
527 // Use reverse iterator instead of back because Rect path may need previous
528 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
529 Element* element = (Element*) iter.prev();
530
531 if (element && element->canBeIntersectedInPlace(fSaveCount, op)) {
532 switch (element->fType) {
533 case Element::kEmpty_Type:
534 element->checkEmpty();
535 return;
536 case Element::kRect_Type:
537 if (element->rectRectIntersectAllowed(rect, doAA)) {
538 this->purgeClip(element);
539 if (!element->fRect.intersect(rect)) {
540 element->setEmpty();
541 return;
542 }
543
544 element->fDoAA = doAA;
545 Element* prev = (Element*) iter.prev();
546 element->updateBoundAndGenID(prev);
547 return;
548 }
549 break;
550 case Element::kPath_Type:
551 if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
552 this->purgeClip(element);
553 element->setEmpty();
554 return;
555 }
556 break;
557 }
558 }
559 new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA);
560 ((Element*) fDeque.back())->updateBoundAndGenID(element);
561
562 if (element && element->fSaveCount == fSaveCount) {
563 this->purgeClip(element);
564 }
565 }
566
clipDevPath(const SkPath & path,SkRegion::Op op,bool doAA)567 void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
568 SkRect alt;
569 if (path.isRect(&alt) && !path.isInverseFillType()) {
570 return this->clipDevRect(alt, op, doAA);
571 }
572
573 Element* element = (Element*)fDeque.back();
574 if (element && element->canBeIntersectedInPlace(fSaveCount, op)) {
575 const SkRect& pathBounds = path.getBounds();
576 switch (element->fType) {
577 case Element::kEmpty_Type:
578 element->checkEmpty();
579 return;
580 case Element::kRect_Type:
581 if (!SkRect::Intersects(element->fRect, pathBounds)) {
582 this->purgeClip(element);
583 element->setEmpty();
584 return;
585 }
586 break;
587 case Element::kPath_Type:
588 if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
589 this->purgeClip(element);
590 element->setEmpty();
591 return;
592 }
593 break;
594 }
595 }
596 new (fDeque.push_back()) Element(fSaveCount, path, op, doAA);
597 ((Element*) fDeque.back())->updateBoundAndGenID(element);
598
599 if (element && element->fSaveCount == fSaveCount) {
600 this->purgeClip(element);
601 }
602 }
603
clipEmpty()604 void SkClipStack::clipEmpty() {
605
606 Element* element = (Element*) fDeque.back();
607
608 if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
609 switch (element->fType) {
610 case Element::kEmpty_Type:
611 element->checkEmpty();
612 return;
613 case Element::kRect_Type:
614 case Element::kPath_Type:
615 this->purgeClip(element);
616 element->setEmpty();
617 return;
618 }
619 }
620 new (fDeque.push_back()) Element(fSaveCount);
621
622 if (element && element->fSaveCount == fSaveCount) {
623 this->purgeClip(element);
624 }
625 ((Element*)fDeque.back())->fGenID = kEmptyGenID;
626 }
627
isWideOpen() const628 bool SkClipStack::isWideOpen() const {
629 if (0 == fDeque.count()) {
630 return true;
631 }
632
633 const Element* back = (const Element*) fDeque.back();
634 return kWideOpenGenID == back->fGenID ||
635 (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty());
636 }
637
638 ///////////////////////////////////////////////////////////////////////////////
639
Iter()640 SkClipStack::Iter::Iter() : fStack(NULL) {
641 }
642
Iter(const SkClipStack & stack,IterStart startLoc)643 SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
644 : fStack(&stack) {
645 this->reset(stack, startLoc);
646 }
647
next()648 const SkClipStack::Element* SkClipStack::Iter::next() {
649 return (const SkClipStack::Element*)fIter.next();
650 }
651
prev()652 const SkClipStack::Element* SkClipStack::Iter::prev() {
653 return (const SkClipStack::Element*)fIter.prev();
654 }
655
skipToTopmost(SkRegion::Op op)656 const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
657
658 if (NULL == fStack) {
659 return NULL;
660 }
661
662 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
663
664 const SkClipStack::Element* element = NULL;
665
666 for (element = (const SkClipStack::Element*) fIter.prev();
667 NULL != element;
668 element = (const SkClipStack::Element*) fIter.prev()) {
669
670 if (op == element->fOp) {
671 // The Deque's iterator is actually one pace ahead of the
672 // returned value. So while "element" is the element we want to
673 // return, the iterator is actually pointing at (and will
674 // return on the next "next" or "prev" call) the element
675 // in front of it in the deque. Bump the iterator forward a
676 // step so we get the expected result.
677 if (NULL == fIter.next()) {
678 // The reverse iterator has run off the front of the deque
679 // (i.e., the "op" clip is the first clip) and can't
680 // recover. Reset the iterator to start at the front.
681 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
682 }
683 break;
684 }
685 }
686
687 if (NULL == element) {
688 // There were no "op" clips
689 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
690 }
691
692 return this->next();
693 }
694
reset(const SkClipStack & stack,IterStart startLoc)695 void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
696 fStack = &stack;
697 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
698 }
699
700 // helper method
getConservativeBounds(int offsetX,int offsetY,int maxWidth,int maxHeight,SkRect * devBounds,bool * isIntersectionOfRects) const701 void SkClipStack::getConservativeBounds(int offsetX,
702 int offsetY,
703 int maxWidth,
704 int maxHeight,
705 SkRect* devBounds,
706 bool* isIntersectionOfRects) const {
707 SkASSERT(NULL != devBounds);
708
709 devBounds->setLTRB(0, 0,
710 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
711
712 SkRect temp;
713 SkClipStack::BoundsType boundType;
714
715 // temp starts off in canvas space here
716 this->getBounds(&temp, &boundType, isIntersectionOfRects);
717 if (SkClipStack::kInsideOut_BoundsType == boundType) {
718 return;
719 }
720
721 // but is converted to device space here
722 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
723
724 if (!devBounds->intersect(temp)) {
725 devBounds->setEmpty();
726 }
727 }
728
addPurgeClipCallback(PFPurgeClipCB callback,void * data) const729 void SkClipStack::addPurgeClipCallback(PFPurgeClipCB callback, void* data) const {
730 ClipCallbackData temp = { callback, data };
731 fCallbackData.append(1, &temp);
732 }
733
removePurgeClipCallback(PFPurgeClipCB callback,void * data) const734 void SkClipStack::removePurgeClipCallback(PFPurgeClipCB callback, void* data) const {
735 ClipCallbackData temp = { callback, data };
736 int index = fCallbackData.find(temp);
737 if (index >= 0) {
738 fCallbackData.removeShuffle(index);
739 }
740 }
741
742 // The clip state represented by 'element' will never be used again. Purge it.
purgeClip(Element * element)743 void SkClipStack::purgeClip(Element* element) {
744 SkASSERT(NULL != element);
745 if (element->fGenID >= 0 && element->fGenID < kFirstUnreservedGenID) {
746 return;
747 }
748
749 for (int i = 0; i < fCallbackData.count(); ++i) {
750 (*fCallbackData[i].fCallback)(element->fGenID, fCallbackData[i].fData);
751 }
752
753 // Invalidate element's gen ID so handlers can detect already handled records
754 element->fGenID = kInvalidGenID;
755 }
756
GetNextGenID()757 int32_t SkClipStack::GetNextGenID() {
758 // TODO: handle overflow.
759 return sk_atomic_inc(&gGenID);
760 }
761
getTopmostGenID() const762 int32_t SkClipStack::getTopmostGenID() const {
763
764 if (fDeque.empty()) {
765 return kInvalidGenID;
766 }
767
768 Element* element = (Element*)fDeque.back();
769 return element->fGenID;
770 }
771