• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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