1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkDeviceLooper.h"
9 
SkDeviceLooper(const SkPixmap & base,const SkRasterClip & rc,const SkIRect & bounds,bool aa)10 SkDeviceLooper::SkDeviceLooper(const SkPixmap& base, const SkRasterClip& rc, const SkIRect& bounds,
11                                bool aa)
12     : fBaseDst(base)
13     , fBaseRC(rc)
14     , fSubsetRC(rc.isForceConservativeRects())
15     , fDelta(aa ? kAA_Delta : kBW_Delta)
16 {
17     // sentinels that next() has not yet been called, and so our mapper functions
18     // should not be called either.
19     fCurrDst = nullptr;
20     fCurrRC = nullptr;
21 
22     if (!rc.isEmpty()) {
23         // clip must be contained by the bitmap
24         SkASSERT(SkIRect::MakeWH(base.width(), base.height()).contains(rc.getBounds()));
25     }
26 
27     if (rc.isEmpty() || !fClippedBounds.intersect(bounds, rc.getBounds())) {
28         fState = kDone_State;
29     } else if (this->fitsInDelta(fClippedBounds)) {
30         fState = kSimple_State;
31     } else {
32         // back up by 1 DX, so that next() will put us in a correct starting
33         // position.
34         fCurrOffset.set(fClippedBounds.left() - fDelta,
35                         fClippedBounds.top());
36         fState = kComplex_State;
37     }
38 }
39 
~SkDeviceLooper()40 SkDeviceLooper::~SkDeviceLooper() {}
41 
mapRect(SkRect * dst,const SkRect & src) const42 void SkDeviceLooper::mapRect(SkRect* dst, const SkRect& src) const {
43     SkASSERT(kDone_State != fState);
44     SkASSERT(fCurrDst);
45     SkASSERT(fCurrRC);
46 
47     *dst = src;
48     dst->offset(SkIntToScalar(-fCurrOffset.fX),
49                 SkIntToScalar(-fCurrOffset.fY));
50 }
51 
mapMatrix(SkMatrix * dst,const SkMatrix & src) const52 void SkDeviceLooper::mapMatrix(SkMatrix* dst, const SkMatrix& src) const {
53     SkASSERT(kDone_State != fState);
54     SkASSERT(fCurrDst);
55     SkASSERT(fCurrRC);
56 
57     *dst = src;
58     dst->postTranslate(SkIntToScalar(-fCurrOffset.fX), SkIntToScalar(-fCurrOffset.fY));
59 }
60 
computeCurrBitmapAndClip()61 bool SkDeviceLooper::computeCurrBitmapAndClip() {
62     SkASSERT(kComplex_State == fState);
63 
64     SkIRect r = SkIRect::MakeXYWH(fCurrOffset.x(), fCurrOffset.y(),
65                                   fDelta, fDelta);
66     if (!fBaseDst.extractSubset(&fSubsetDst, r)) {
67         fSubsetRC.setEmpty();
68     } else {
69         fBaseRC.translate(-r.left(), -r.top(), &fSubsetRC);
70         (void)fSubsetRC.op(SkIRect::MakeWH(fDelta, fDelta), SkRegion::kIntersect_Op);
71     }
72 
73     fCurrDst = &fSubsetDst;
74     fCurrRC = &fSubsetRC;
75     return !fCurrRC->isEmpty();
76 }
77 
next_tile(const SkIRect & boundary,int delta,SkIPoint * offset)78 static bool next_tile(const SkIRect& boundary, int delta, SkIPoint* offset) {
79     // can we move to the right?
80     if (offset->x() + delta < boundary.right()) {
81         offset->fX += delta;
82         return true;
83     }
84 
85     // reset to the left, but move down a row
86     offset->fX = boundary.left();
87     if (offset->y() + delta < boundary.bottom()) {
88         offset->fY += delta;
89         return true;
90     }
91 
92     // offset is now outside of boundary, so we're done
93     return false;
94 }
95 
next()96 bool SkDeviceLooper::next() {
97     switch (fState) {
98         case kDone_State:
99             // in theory, we should not get called here, since we must have
100             // previously returned false, but we check anyway.
101             break;
102 
103         case kSimple_State:
104             // first time for simple
105             if (nullptr == fCurrDst) {
106                 fCurrDst = &fBaseDst;
107                 fCurrRC = &fBaseRC;
108                 fCurrOffset.set(0, 0);
109                 return true;
110             }
111             // 2nd time for simple, we are done
112             break;
113 
114         case kComplex_State:
115             // need to propogate fCurrOffset through clippedbounds
116             // left to right, until we wrap around and move down
117 
118             while (next_tile(fClippedBounds, fDelta, &fCurrOffset)) {
119                 if (this->computeCurrBitmapAndClip()) {
120                     return true;
121                 }
122             }
123             break;
124     }
125     fState = kDone_State;
126     return false;
127 }
128