| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkDeviceLooper.h" |
| |
| SkDeviceLooper::SkDeviceLooper(const SkPixmap& base, const SkRasterClip& rc, const SkIRect& bounds, |
| bool aa) |
| : fBaseDst(base) |
| , fBaseRC(rc) |
| , fDelta(aa ? kAA_Delta : kBW_Delta) |
| { |
| // sentinels that next() has not yet been called, and so our mapper functions |
| // should not be called either. |
| fCurrDst = nullptr; |
| fCurrRC = nullptr; |
| |
| if (!rc.isEmpty()) { |
| // clip must be contained by the bitmap |
| SkASSERT(SkIRect::MakeWH(base.width(), base.height()).contains(rc.getBounds())); |
| } |
| |
| if (rc.isEmpty() || !fClippedBounds.intersect(bounds, rc.getBounds())) { |
| fState = kDone_State; |
| } else if (this->fitsInDelta(fClippedBounds)) { |
| fState = kSimple_State; |
| } else { |
| // back up by 1 DX, so that next() will put us in a correct starting |
| // position. |
| fCurrOffset.set(fClippedBounds.left() - fDelta, |
| fClippedBounds.top()); |
| fState = kComplex_State; |
| } |
| } |
| |
| SkDeviceLooper::~SkDeviceLooper() {} |
| |
| void SkDeviceLooper::mapRect(SkRect* dst, const SkRect& src) const { |
| SkASSERT(kDone_State != fState); |
| SkASSERT(fCurrDst); |
| SkASSERT(fCurrRC); |
| |
| *dst = src; |
| dst->offset(SkIntToScalar(-fCurrOffset.fX), |
| SkIntToScalar(-fCurrOffset.fY)); |
| } |
| |
| void SkDeviceLooper::mapMatrix(SkMatrix* dst, const SkMatrix& src) const { |
| SkASSERT(kDone_State != fState); |
| SkASSERT(fCurrDst); |
| SkASSERT(fCurrRC); |
| |
| *dst = src; |
| dst->postTranslate(SkIntToScalar(-fCurrOffset.fX), SkIntToScalar(-fCurrOffset.fY)); |
| } |
| |
| bool SkDeviceLooper::computeCurrBitmapAndClip() { |
| SkASSERT(kComplex_State == fState); |
| |
| SkIRect r = SkIRect::MakeXYWH(fCurrOffset.x(), fCurrOffset.y(), |
| fDelta, fDelta); |
| if (!fBaseDst.extractSubset(&fSubsetDst, r)) { |
| fSubsetRC.setEmpty(); |
| } else { |
| fBaseRC.translate(-r.left(), -r.top(), &fSubsetRC); |
| (void)fSubsetRC.op(SkIRect::MakeWH(fDelta, fDelta), SkRegion::kIntersect_Op); |
| } |
| |
| fCurrDst = &fSubsetDst; |
| fCurrRC = &fSubsetRC; |
| return !fCurrRC->isEmpty(); |
| } |
| |
| static bool next_tile(const SkIRect& boundary, int delta, SkIPoint* offset) { |
| // can we move to the right? |
| if (offset->x() + delta < boundary.right()) { |
| offset->fX += delta; |
| return true; |
| } |
| |
| // reset to the left, but move down a row |
| offset->fX = boundary.left(); |
| if (offset->y() + delta < boundary.bottom()) { |
| offset->fY += delta; |
| return true; |
| } |
| |
| // offset is now outside of boundary, so we're done |
| return false; |
| } |
| |
| bool SkDeviceLooper::next() { |
| switch (fState) { |
| case kDone_State: |
| // in theory, we should not get called here, since we must have |
| // previously returned false, but we check anyway. |
| break; |
| |
| case kSimple_State: |
| // first time for simple |
| if (nullptr == fCurrDst) { |
| fCurrDst = &fBaseDst; |
| fCurrRC = &fBaseRC; |
| fCurrOffset.set(0, 0); |
| return true; |
| } |
| // 2nd time for simple, we are done |
| break; |
| |
| case kComplex_State: |
| // need to propogate fCurrOffset through clippedbounds |
| // left to right, until we wrap around and move down |
| |
| while (next_tile(fClippedBounds, fDelta, &fCurrOffset)) { |
| if (this->computeCurrBitmapAndClip()) { |
| return true; |
| } |
| } |
| break; |
| } |
| fState = kDone_State; |
| return false; |
| } |