/* * Copyright 2014 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkMatrixImageFilter.h" #include "SkCanvas.h" #include "SkColorSpaceXformer.h" #include "SkImageFilterPriv.h" #include "SkReadBuffer.h" #include "SkSpecialImage.h" #include "SkSpecialSurface.h" #include "SkWriteBuffer.h" #include "SkRect.h" SkMatrixImageFilter::SkMatrixImageFilter(const SkMatrix& transform, SkFilterQuality filterQuality, sk_sp input) : INHERITED(&input, 1, nullptr) , fTransform(transform) , fFilterQuality(filterQuality) { } sk_sp SkMatrixImageFilter::Make(const SkMatrix& transform, SkFilterQuality filterQuality, sk_sp input) { return sk_sp(new SkMatrixImageFilter(transform, filterQuality, std::move(input))); } sk_sp SkMatrixImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); SkMatrix matrix; buffer.readMatrix(&matrix); return Make(matrix, buffer.read32LE(kLast_SkFilterQuality), common.getInput(0)); } void SkMatrixImageFilter::flatten(SkWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); buffer.writeMatrix(fTransform); buffer.writeInt(fFilterQuality); } sk_sp SkMatrixImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } SkMatrix matrix; if (!ctx.ctm().invert(&matrix)) { return nullptr; } matrix.postConcat(fTransform); matrix.postConcat(ctx.ctm()); const SkIRect srcBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(), input->width(), input->height()); const SkRect srcRect = SkRect::Make(srcBounds); SkRect dstRect; matrix.mapRect(&dstRect, srcRect); SkIRect dstBounds; dstRect.roundOut(&dstBounds); sk_sp surf(input->makeSurface(ctx.outputProperties(), dstBounds.size())); if (!surf) { return nullptr; } SkCanvas* canvas = surf->getCanvas(); SkASSERT(canvas); canvas->clear(0x0); canvas->translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y())); canvas->concat(matrix); SkPaint paint; paint.setAntiAlias(true); paint.setBlendMode(SkBlendMode::kSrc); paint.setFilterQuality(fFilterQuality); input->draw(canvas, srcRect.x(), srcRect.y(), &paint); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; return surf->makeImageSnapshot(); } sk_sp SkMatrixImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const { SkASSERT(1 == this->countInputs()); auto input = xformer->apply(this->getInput(0)); if (input.get() != this->getInput(0)) { return SkMatrixImageFilter::Make(fTransform, fFilterQuality, std::move(input)); } return this->refMe(); } SkRect SkMatrixImageFilter::computeFastBounds(const SkRect& src) const { SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; SkRect dst; fTransform.mapRect(&dst, bounds); return dst; } SkIRect SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, MapDirection dir, const SkIRect* inputRect) const { SkMatrix matrix; if (!ctm.invert(&matrix)) { return src; } if (kForward_MapDirection == dir) { matrix.postConcat(fTransform); } else { SkMatrix transformInverse; if (!fTransform.invert(&transformInverse)) { return src; } matrix.postConcat(transformInverse); } matrix.postConcat(ctm); SkRect floatBounds; matrix.mapRect(&floatBounds, SkRect::Make(src)); SkIRect result = floatBounds.roundOut(); #ifndef SK_IGNORE_MATRIX_IMAGE_FILTER_FIX if (kReverse_MapDirection == dir && kNone_SkFilterQuality != fFilterQuality) { // When filtering we might need some pixels in the source that might be otherwise // clipped off. result.outset(1, 1); } #endif return result; }