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