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
16static const int32_t kFirstUnreservedGenID = 3;
17int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
18
19void 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
34void 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
42bool 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
54bool 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
82void 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
124void 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
157void 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
187void 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
219void 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
253void 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.
357static const int kDefaultElementAllocCnt = 8;
358
359SkClipStack::SkClipStack()
360    : fDeque(sizeof(Element), kDefaultElementAllocCnt)
361    , fSaveCount(0) {
362}
363
364SkClipStack::SkClipStack(const SkClipStack& b)
365    : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
366    *this = b;
367}
368
369SkClipStack::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
377SkClipStack::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
387SkClipStack::~SkClipStack() {
388    reset();
389}
390
391SkClipStack& 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
408bool 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
428void 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
440void SkClipStack::save() {
441    fSaveCount += 1;
442}
443
444void SkClipStack::restore() {
445    fSaveCount -= 1;
446    restoreTo(fSaveCount);
447}
448
449void SkClipStack::restoreTo(int saveCount) {
450    while (!fDeque.empty()) {
451        Element* element = (Element*)fDeque.back();
452        if (element->fSaveCount <= saveCount) {
453            break;
454        }
455        element->~Element();
456        fDeque.pop_back();
457    }
458}
459
460void SkClipStack::getBounds(SkRect* canvFiniteBound,
461                            BoundsType* boundType,
462                            bool* isIntersectionOfRects) const {
463    SkASSERT(NULL != canvFiniteBound && NULL != boundType);
464
465    Element* element = (Element*)fDeque.back();
466
467    if (NULL == element) {
468        // the clip is wide open - the infinite plane w/ no pixels un-writeable
469        canvFiniteBound->setEmpty();
470        *boundType = kInsideOut_BoundsType;
471        if (NULL != isIntersectionOfRects) {
472            *isIntersectionOfRects = false;
473        }
474        return;
475    }
476
477    *canvFiniteBound = element->fFiniteBound;
478    *boundType = element->fFiniteBoundType;
479    if (NULL != isIntersectionOfRects) {
480        *isIntersectionOfRects = element->fIsIntersectionOfRects;
481    }
482}
483
484bool SkClipStack::intersectRectWithClip(SkRect* rect) const {
485    SkASSERT(NULL != rect);
486
487    SkRect bounds;
488    SkClipStack::BoundsType bt;
489    this->getBounds(&bounds, &bt);
490    if (bt == SkClipStack::kInsideOut_BoundsType) {
491        if (bounds.contains(*rect)) {
492            return false;
493        } else {
494            // If rect's x values are both within bound's x range we
495            // could clip here. Same for y. But we don't bother to check.
496            return true;
497        }
498    } else {
499        return rect->intersect(bounds);
500    }
501}
502
503bool SkClipStack::quickContains(const SkRect& rect) const {
504
505    Iter iter(*this, Iter::kTop_IterStart);
506    const Element* element = iter.prev();
507    while (element != NULL) {
508        if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp())
509            return false;
510        if (element->isInverseFilled()) {
511            // Part of 'rect' could be trimmed off by the inverse-filled clip element
512            if (SkRect::Intersects(element->getBounds(), rect)) {
513                return false;
514            }
515        } else {
516            if (!element->contains(rect)) {
517                return false;
518            }
519        }
520        if (SkRegion::kReplace_Op == element->getOp()) {
521            break;
522        }
523        element = iter.prev();
524    }
525    return true;
526}
527
528void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
529
530    // Use reverse iterator instead of back because Rect path may need previous
531    SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
532    Element* element = (Element*) iter.prev();
533
534    if (NULL != element) {
535        if (element->canBeIntersectedInPlace(fSaveCount, op)) {
536            switch (element->fType) {
537                case Element::kEmpty_Type:
538                    element->checkEmpty();
539                    return;
540                case Element::kRect_Type:
541                    if (element->rectRectIntersectAllowed(rect, doAA)) {
542                        if (!element->fRect.intersect(rect)) {
543                            element->setEmpty();
544                            return;
545                        }
546
547                        element->fDoAA = doAA;
548                        Element* prev = (Element*) iter.prev();
549                        element->updateBoundAndGenID(prev);
550                        return;
551                    }
552                    break;
553                case Element::kPath_Type:
554                    if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
555                        element->setEmpty();
556                        return;
557                    }
558                    break;
559            }
560        } else if (SkRegion::kReplace_Op == op) {
561            this->restoreTo(fSaveCount - 1);
562            element = (Element*) fDeque.back();
563        }
564    }
565    new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA);
566    ((Element*) fDeque.back())->updateBoundAndGenID(element);
567}
568
569void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
570    SkRect alt;
571    if (path.isRect(&alt) && !path.isInverseFillType()) {
572        return this->clipDevRect(alt, op, doAA);
573    }
574
575    Element* element = (Element*)fDeque.back();
576    if (NULL != element) {
577        if (element->canBeIntersectedInPlace(fSaveCount, op)) {
578            const SkRect& pathBounds = path.getBounds();
579            switch (element->fType) {
580                case Element::kEmpty_Type:
581                    element->checkEmpty();
582                    return;
583                case Element::kRect_Type:
584                    if (!SkRect::Intersects(element->fRect, pathBounds)) {
585                        element->setEmpty();
586                        return;
587                    }
588                    break;
589                case Element::kPath_Type:
590                    if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
591                        element->setEmpty();
592                        return;
593                    }
594                    break;
595            }
596        } else if (SkRegion::kReplace_Op == op) {
597            this->restoreTo(fSaveCount - 1);
598            element = (Element*) fDeque.back();
599        }
600    }
601    new (fDeque.push_back()) Element(fSaveCount, path, op, doAA);
602    ((Element*) fDeque.back())->updateBoundAndGenID(element);
603}
604
605void SkClipStack::clipEmpty() {
606
607    Element* element = (Element*) fDeque.back();
608
609    if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
610        switch (element->fType) {
611            case Element::kEmpty_Type:
612                element->checkEmpty();
613                return;
614            case Element::kRect_Type:
615            case Element::kPath_Type:
616                element->setEmpty();
617                return;
618        }
619    }
620    new (fDeque.push_back()) Element(fSaveCount);
621
622    ((Element*)fDeque.back())->fGenID = kEmptyGenID;
623}
624
625bool SkClipStack::isWideOpen() const {
626    return this->getTopmostGenID() == kWideOpenGenID;
627}
628
629///////////////////////////////////////////////////////////////////////////////
630
631SkClipStack::Iter::Iter() : fStack(NULL) {
632}
633
634SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
635    : fStack(&stack) {
636    this->reset(stack, startLoc);
637}
638
639const SkClipStack::Element* SkClipStack::Iter::next() {
640    return (const SkClipStack::Element*)fIter.next();
641}
642
643const SkClipStack::Element* SkClipStack::Iter::prev() {
644    return (const SkClipStack::Element*)fIter.prev();
645}
646
647const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
648
649    if (NULL == fStack) {
650        return NULL;
651    }
652
653    fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
654
655    const SkClipStack::Element* element = NULL;
656
657    for (element = (const SkClipStack::Element*) fIter.prev();
658         NULL != element;
659         element = (const SkClipStack::Element*) fIter.prev()) {
660
661        if (op == element->fOp) {
662            // The Deque's iterator is actually one pace ahead of the
663            // returned value. So while "element" is the element we want to
664            // return, the iterator is actually pointing at (and will
665            // return on the next "next" or "prev" call) the element
666            // in front of it in the deque. Bump the iterator forward a
667            // step so we get the expected result.
668            if (NULL == fIter.next()) {
669                // The reverse iterator has run off the front of the deque
670                // (i.e., the "op" clip is the first clip) and can't
671                // recover. Reset the iterator to start at the front.
672                fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
673            }
674            break;
675        }
676    }
677
678    if (NULL == element) {
679        // There were no "op" clips
680        fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
681    }
682
683    return this->next();
684}
685
686void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
687    fStack = &stack;
688    fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
689}
690
691// helper method
692void SkClipStack::getConservativeBounds(int offsetX,
693                                        int offsetY,
694                                        int maxWidth,
695                                        int maxHeight,
696                                        SkRect* devBounds,
697                                        bool* isIntersectionOfRects) const {
698    SkASSERT(NULL != devBounds);
699
700    devBounds->setLTRB(0, 0,
701                       SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
702
703    SkRect temp;
704    SkClipStack::BoundsType boundType;
705
706    // temp starts off in canvas space here
707    this->getBounds(&temp, &boundType, isIntersectionOfRects);
708    if (SkClipStack::kInsideOut_BoundsType == boundType) {
709        return;
710    }
711
712    // but is converted to device space here
713    temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
714
715    if (!devBounds->intersect(temp)) {
716        devBounds->setEmpty();
717    }
718}
719
720int32_t SkClipStack::GetNextGenID() {
721    // TODO: handle overflow.
722    return sk_atomic_inc(&gGenID);
723}
724
725int32_t SkClipStack::getTopmostGenID() const {
726    if (fDeque.empty()) {
727        return kWideOpenGenID;
728    }
729
730    const Element* back = static_cast<const Element*>(fDeque.back());
731    if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty()) {
732        return kWideOpenGenID;
733    }
734
735    return back->getGenID();
736}
737