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
10SkDeviceLooper::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
41SkDeviceLooper::~SkDeviceLooper() {
42}
43
44void 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
54void 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
64bool 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
83static 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
101bool 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