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