/*
* 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)
, fSubsetRC(rc.isForceConservativeRects())
, 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;
}