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