diff --git a/gm/shadermaskfilter.cpp b/gm/shadermaskfilter.cpp index c1bee02e6d..83c95f41b4 100644 --- a/gm/shadermaskfilter.cpp +++ b/gm/shadermaskfilter.cpp @@ -11,6 +11,7 @@ #include "SkCanvas.h" #include "SkImage.h" #include "SkMaskFilter.h" +#include "SkPictureRecorder.h" #include "SkShaderMaskFilter.h" static void draw_masked_image(SkCanvas* canvas, const SkImage* image, SkScalar x, SkScalar y, @@ -220,3 +221,92 @@ DEF_SIMPLE_GM(savelayer_maskfilter, canvas, 450, 675) { } } +static void draw_mask(SkCanvas* canvas) { + SkPaint p; + p.setAntiAlias(true); + canvas->drawOval(SkRect::Make(canvas->imageInfo().bounds()), p); +} + +DEF_SIMPLE_GM(shadermaskfilter_localmatrix, canvas, 1500, 1000) { + static constexpr SkScalar kSize = 100; + + using ShaderMakerT = sk_sp(*)(SkCanvas*, const SkMatrix& lm); + static const ShaderMakerT gShaderMakers[] = { + [](SkCanvas* canvas, const SkMatrix& lm) -> sk_sp { + auto surface = sk_tool_utils::makeSurface(canvas, + SkImageInfo::MakeN32Premul(kSize, kSize)); + draw_mask(surface->getCanvas()); + return surface->makeImageSnapshot()->makeShader(SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, &lm); + }, + [](SkCanvas*, const SkMatrix& lm) -> sk_sp { + SkPictureRecorder recorder; + draw_mask(recorder.beginRecording(kSize, kSize)); + return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(), + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, + &lm, nullptr); + }, + }; + + struct Config { + SkMatrix fCanvasMatrix, + fMaskMatrix, + fShaderMatrix; + } gConfigs[] = { + { SkMatrix::I(), SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10) }, + { SkMatrix::MakeScale(2, 2), SkMatrix::I(), SkMatrix::MakeTrans(10, 10) }, + { SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10), SkMatrix::I() }, + { SkMatrix::Concat(SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10)), + SkMatrix::I(), SkMatrix::I() }, + { SkMatrix::I(), + SkMatrix::Concat(SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10)), + SkMatrix::I() }, + { SkMatrix::I(), SkMatrix::I(), + SkMatrix::Concat(SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10)) }, + }; + + using DrawerT = void(*)(SkCanvas*, const SkRect&, const SkPaint&); + static const DrawerT gDrawers[] = { + [](SkCanvas* canvas, const SkRect& dest, const SkPaint& mask) { + canvas->drawRect(dest, mask); + }, + [](SkCanvas* canvas, const SkRect& dest, const SkPaint& mask) { + canvas->saveLayer(&dest, &mask); + SkPaint p = mask; + p.setMaskFilter(nullptr); + canvas->drawPaint(p); + canvas->restore(); + }, + }; + + SkPaint paint, rectPaint; + paint.setColor(0xff00ff00); + rectPaint.setStyle(SkPaint::kStroke_Style); + rectPaint.setColor(0xffff0000); + + for (const auto& sm : gShaderMakers) { + for (const auto& drawer : gDrawers) { + { + SkAutoCanvasRestore acr(canvas, true); + for (const auto& cfg : gConfigs) { + paint.setMaskFilter(SkShaderMaskFilter::Make(sm(canvas, cfg.fShaderMatrix)) + ->makeWithMatrix(cfg.fMaskMatrix)); + auto dest = SkRect::MakeWH(kSize, kSize); + SkMatrix::Concat(cfg.fMaskMatrix, cfg.fShaderMatrix).mapRect(&dest); + + { + SkAutoCanvasRestore acr(canvas, true); + canvas->concat(cfg.fCanvasMatrix); + drawer(canvas, dest, paint); + canvas->drawRect(dest, rectPaint); + } + + canvas->translate(kSize * 2.5f, 0); + } + } + canvas->translate(0, kSize * 2.5f); + } + + } +} diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h index 1ea774a027..bd2a20b825 100644 --- a/include/core/SkMaskFilter.h +++ b/include/core/SkMaskFilter.h @@ -50,7 +50,14 @@ public: static sk_sp MakeCombine(sk_sp filterA, sk_sp filterB, SkCoverageMode mode); - sk_sp makeWithLocalMatrix(const SkMatrix&) const; + /** + * Construct a maskfilter with an additional transform. + * + * Note: unlike shader local matrices, this transform composes next to the CTM. + * + * TotalMatrix = CTM x MaskFilterMatrix x (optional/downstream) ShaderLocalMatrix + */ + sk_sp makeWithMatrix(const SkMatrix&) const; virtual void toString(SkString* str) const = 0; SK_DEFINE_FLATTENABLE_TYPE(SkMaskFilter) diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp index f3299ad28c..0e456e771c 100644 --- a/src/core/SkBitmapDevice.cpp +++ b/src/core/SkBitmapDevice.cpp @@ -500,7 +500,7 @@ void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPain // todo: can we unify with similar adjustment in SkGpuDevice? SkTCopyOnFirstWrite paint(origPaint); if (paint->getMaskFilter()) { - paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm())); + paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithMatrix(this->ctm())); } this->drawSprite(static_cast(device)->fBitmap, x, y, *paint); } @@ -559,7 +559,7 @@ void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPain } if (paint->getMaskFilter()) { - paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm())); + paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithMatrix(this->ctm())); } if (!clipImage) { diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp index e1805637a9..7c90634e12 100644 --- a/src/core/SkMaskFilter.cpp +++ b/src/core/SkMaskFilter.cpp @@ -635,9 +635,9 @@ void SkCombineMF::toString(SkString* str) const { /////////////////////////////////////////////////////////////////////////////////////////////////// -class SkLocalMatrixMF : public SkMaskFilterBase { +class SkMatrixMF : public SkMaskFilterBase { public: - SkLocalMatrixMF(sk_sp filter, const SkMatrix& lm) + SkMatrixMF(sk_sp filter, const SkMatrix& lm) : fFilter(std::move(filter)) , fLM(lm) {} @@ -657,7 +657,7 @@ public: SkMask::Format getFormat() const override { return as_MFB(fFilter)->getFormat(); } void toString(SkString* str) const override { - str->set("SkLocalMatrixMF:"); + str->set("SkMatrixMF:"); } SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLocalMatrixMF) @@ -665,16 +665,7 @@ public: protected: #if SK_SUPPORT_GPU std::unique_ptr onAsFragmentProcessor(const GrFPArgs& args) const override{ - GrFPArgs newArgs = args; - - SkMatrix storage; - if (args.fLocalMatrix) { - storage.setConcat(*args.fLocalMatrix, fLM); - newArgs.fLocalMatrix = &storage; - } else { - newArgs.fLocalMatrix = &fLM; - } - return as_MFB(fFilter)->asFragmentProcessor(newArgs); + return as_MFB(fFilter)->asFragmentProcessor(GrFPArgs::WithPostLocalMatrix(args, fLM)); } bool onHasFragmentProcessor() const override { @@ -695,11 +686,11 @@ private: typedef SkMaskFilterBase INHERITED; }; -sk_sp SkLocalMatrixMF::CreateProc(SkReadBuffer& buffer) { - SkMatrix lm; - buffer.readMatrix(&lm); +sk_sp SkMatrixMF::CreateProc(SkReadBuffer& buffer) { + SkMatrix m; + buffer.readMatrix(&m); auto filter = buffer.readMaskFilter(); - return filter ? filter->makeWithLocalMatrix(lm) : nullptr; + return filter ? filter->makeWithMatrix(m) : nullptr; } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -735,16 +726,16 @@ sk_sp SkMaskFilter::MakeCombine(sk_sp dst, sk_sp(new SkCombineMF(std::move(dst), std::move(src), mode)); } -sk_sp SkMaskFilter::makeWithLocalMatrix(const SkMatrix& lm) const { +sk_sp SkMaskFilter::makeWithMatrix(const SkMatrix& lm) const { sk_sp me = sk_ref_sp(const_cast(this)); if (lm.isIdentity()) { return me; } - return sk_sp(new SkLocalMatrixMF(std::move(me), lm)); + return sk_sp(new SkMatrixMF(std::move(me), lm)); } void SkMaskFilter::InitializeFlattenables() { - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLocalMatrixMF) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixMF) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeMF) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCombineMF) sk_register_blur_maskfilter_createproc(); diff --git a/src/gpu/GrFPArgs.h b/src/gpu/GrFPArgs.h index cdb2e5de50..e67c638226 100644 --- a/src/gpu/GrFPArgs.h +++ b/src/gpu/GrFPArgs.h @@ -9,39 +9,86 @@ #define GrFPArgs_DEFINED #include "SkFilterQuality.h" +#include "SkMatrix.h" -class SkMatrix; class GrContext; class GrColorSpaceInfo; struct GrFPArgs { - GrFPArgs(GrContext* context, - const SkMatrix* viewMatrix, - const SkMatrix* localMatrix, - SkFilterQuality filterQuality, - const GrColorSpaceInfo* dstColorSpaceInfo) - : fContext(context) - , fViewMatrix(viewMatrix) - , fLocalMatrix(localMatrix) - , fFilterQuality(filterQuality) - , fDstColorSpaceInfo(dstColorSpaceInfo) {} - GrFPArgs(GrContext* context, const SkMatrix* viewMatrix, SkFilterQuality filterQuality, const GrColorSpaceInfo* dstColorSpaceInfo) : fContext(context) , fViewMatrix(viewMatrix) - , fLocalMatrix(nullptr) , fFilterQuality(filterQuality) - , fDstColorSpaceInfo(dstColorSpaceInfo) {} + , fDstColorSpaceInfo(dstColorSpaceInfo) { + SkASSERT(fContext); + SkASSERT(fViewMatrix); + } + + class WithPreLocalMatrix; + class WithPostLocalMatrix; GrContext* fContext; const SkMatrix* fViewMatrix; - const SkMatrix* fLocalMatrix; + + // We track both pre and post local matrix adjustments. For a given FP: + // + // total_local_matrix = postLocalMatrix x FP_localMatrix x preLocalMatrix + // + // Use the helpers above to create pre/post GrFPArgs wrappers. + // + const SkMatrix* fPreLocalMatrix = nullptr; + const SkMatrix* fPostLocalMatrix = nullptr; + SkFilterQuality fFilterQuality; const GrColorSpaceInfo* fDstColorSpaceInfo; }; +class GrFPArgs::WithPreLocalMatrix final : public GrFPArgs { +public: + WithPreLocalMatrix(const GrFPArgs& args, const SkMatrix& lm) : INHERITED(args) { + if (!lm.isIdentity()) { + if (fPreLocalMatrix) { + fStorage.setConcat(lm, *fPreLocalMatrix); + fPreLocalMatrix = fStorage.isIdentity() ? nullptr : &fStorage; + } else { + fPreLocalMatrix = &lm; + } + } + } + +private: + WithPreLocalMatrix(const WithPreLocalMatrix&) = delete; + WithPreLocalMatrix& operator=(const WithPreLocalMatrix&) = delete; + + SkMatrix fStorage; + + using INHERITED = GrFPArgs; +}; + +class GrFPArgs::WithPostLocalMatrix final : public GrFPArgs { +public: + WithPostLocalMatrix(const GrFPArgs& args, const SkMatrix& lm) : INHERITED(args) { + if (!lm.isIdentity()) { + if (fPostLocalMatrix) { + fStorage.setConcat(*fPostLocalMatrix, lm); + fPostLocalMatrix = fStorage.isIdentity() ? nullptr : &fStorage; + } else { + fPostLocalMatrix = &lm; + } + } + } + +private: + WithPostLocalMatrix(const WithPostLocalMatrix&) = delete; + WithPostLocalMatrix& operator=(const WithPostLocalMatrix&) = delete; + + SkMatrix fStorage; + + using INHERITED = GrFPArgs; +}; + #endif diff --git a/src/gpu/GrTestUtils.cpp b/src/gpu/GrTestUtils.cpp index 8958f84704..2aff35dad2 100644 --- a/src/gpu/GrTestUtils.cpp +++ b/src/gpu/GrTestUtils.cpp @@ -337,8 +337,7 @@ TestAsFPArgs::TestAsFPArgs(GrProcessorTestData* d) : fViewMatrixStorage(TestMatrix(d->fRandom)) , fColorSpaceInfoStorage(skstd::make_unique(TestColorSpace(d->fRandom), kRGBA_8888_GrPixelConfig)) - , fArgs(d->context(), &fViewMatrixStorage, nullptr, kNone_SkFilterQuality, - fColorSpaceInfoStorage.get()) + , fArgs(d->context(), &fViewMatrixStorage, kNone_SkFilterQuality, fColorSpaceInfoStorage.get()) {} TestAsFPArgs::~TestAsFPArgs() {} diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index eb26ba2f9b..fca372eabd 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1092,7 +1092,7 @@ void SkGpuDevice::drawSpecial(SkSpecialImage* special1, int left, int top, const if (tmpUnfiltered.getMaskFilter()) { SkMatrix ctm = this->ctm(); ctm.postTranslate(-SkIntToScalar(left + offset.fX), -SkIntToScalar(top + offset.fY)); - tmpUnfiltered.setMaskFilter(tmpUnfiltered.getMaskFilter()->makeWithLocalMatrix(ctm)); + tmpUnfiltered.setMaskFilter(tmpUnfiltered.getMaskFilter()->makeWithMatrix(ctm)); } tmpUnfiltered.setImageFilter(nullptr); diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index b4cb22bd10..99465f620b 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -514,7 +514,7 @@ bool SkPaintToGrPaintWithTexture(GrContext* context, if (textureIsAlphaOnly) { if (const auto* shader = as_SB(paint.getShader())) { shaderFP = shader->asFragmentProcessor(GrFPArgs( - context, &viewM, nullptr, paint.getFilterQuality(), &colorSpaceInfo)); + context, &viewM, paint.getFilterQuality(), &colorSpaceInfo)); if (!shaderFP) { return false; } diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp index 265f7e6013..1838e9e498 100644 --- a/src/shaders/SkImageShader.cpp +++ b/src/shaders/SkImageShader.cpp @@ -213,19 +213,11 @@ static GrSamplerState::WrapMode tile_mode_to_wrap_mode(const SkShader::TileMode std::unique_ptr SkImageShader::asFragmentProcessor( const GrFPArgs& args) const { - SkMatrix lm = this->getLocalMatrix(); + const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix); SkMatrix lmInverse; - if (!lm.invert(&lmInverse)) { + if (!lm->invert(&lmInverse)) { return nullptr; } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - lmInverse.postConcat(inv); - lm.preConcat(*args.fLocalMatrix); - } GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX), tile_mode_to_wrap_mode(fTileModeY)}; @@ -236,7 +228,7 @@ std::unique_ptr SkImageShader::asFragmentProcessor( // are provided by the caller. bool doBicubic; GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode( - args.fFilterQuality, *args.fViewMatrix, lm, + args.fFilterQuality, *args.fViewMatrix, *lm, args.fContext->contextPriv().sharpenMipmappedTextures(), &doBicubic); GrSamplerState samplerState(wrapModes, textureFilterMode); sk_sp texColorSpace; diff --git a/src/shaders/SkLocalMatrixShader.cpp b/src/shaders/SkLocalMatrixShader.cpp index ca99af1bef..509c66d43e 100644 --- a/src/shaders/SkLocalMatrixShader.cpp +++ b/src/shaders/SkLocalMatrixShader.cpp @@ -15,13 +15,8 @@ #if SK_SUPPORT_GPU std::unique_ptr SkLocalMatrixShader::asFragmentProcessor( const GrFPArgs& args) const { - SkMatrix tmp = this->getLocalMatrix(); - if (args.fLocalMatrix) { - tmp.preConcat(*args.fLocalMatrix); - } - return as_SB(fProxyShader) - ->asFragmentProcessor(GrFPArgs(args.fContext, args.fViewMatrix, &tmp, - args.fFilterQuality, args.fDstColorSpaceInfo)); + return as_SB(fProxyShader)->asFragmentProcessor( + GrFPArgs::WithPreLocalMatrix(args, this->getLocalMatrix())); } #endif diff --git a/src/shaders/SkPerlinNoiseShader.cpp b/src/shaders/SkPerlinNoiseShader.cpp index 6179da3d32..8f6c85a494 100644 --- a/src/shaders/SkPerlinNoiseShader.cpp +++ b/src/shaders/SkPerlinNoiseShader.cpp @@ -1394,13 +1394,8 @@ std::unique_ptr SkPerlinNoiseShaderImpl::asFragmentProcesso const GrFPArgs& args) const { SkASSERT(args.fContext); - SkMatrix localMatrix = this->getLocalMatrix(); - if (args.fLocalMatrix) { - localMatrix.preConcat(*args.fLocalMatrix); - } - - SkMatrix matrix = *args.fViewMatrix; - matrix.preConcat(localMatrix); + const auto localMatrix = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix); + const auto matrix = SkMatrix::Concat(*args.fViewMatrix, *localMatrix); // Either we don't stitch tiles, either we have a valid tile size SkASSERT(!fStitchTiles || !fTileSize.isEmpty()); @@ -1413,8 +1408,8 @@ std::unique_ptr SkPerlinNoiseShaderImpl::asFragmentProcesso matrix); SkMatrix m = *args.fViewMatrix; - m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1); - m.setTranslateY(-localMatrix.getTranslateY() + SK_Scalar1); + m.setTranslateX(-localMatrix->getTranslateX() + SK_Scalar1); + m.setTranslateY(-localMatrix->getTranslateY() + SK_Scalar1); auto proxyProvider = args.fContext->contextPriv().proxyProvider(); if (fType == kImprovedNoise_Type) { diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp index 1d19b04721..bbed6d695a 100644 --- a/src/shaders/SkPictureShader.cpp +++ b/src/shaders/SkPictureShader.cpp @@ -175,24 +175,20 @@ void SkPictureShader::flatten(SkWriteBuffer& buffer) const { // // 1) a cached image shader, which wraps a single picture tile at the given CTM/local matrix // -// 2) a "composite" local matrix, to be passed down when dispatching createContext(), +// 2) a tile scale adjustment, to be applied downstream when dispatching createContext(), // appendStages() and asFragmentProcessor() in callers // // The composite local matrix includes the actual local matrix, any inherited/outer local matrix // and a scale component (to mape the actual tile bitmap size -> fTile size). // sk_sp SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, - const SkMatrix* outerLocalMatrix, + const SkMatrix& localMatrix, SkColorSpace* dstColorSpace, - SkMatrix* compositeLocalMatrix, + SkVector* scaleAdjust, const int maxTextureSize) const { SkASSERT(fPicture && !fPicture->cullRect().isEmpty()); - *compositeLocalMatrix = this->getLocalMatrix(); - if (outerLocalMatrix) { - compositeLocalMatrix->preConcat(*outerLocalMatrix); - } - const SkMatrix m = SkMatrix::Concat(viewMatrix, *compositeLocalMatrix); + const SkMatrix m = SkMatrix::Concat(viewMatrix, localMatrix); // Use a rotation-invariant scale SkPoint scale; @@ -274,37 +270,52 @@ sk_sp SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, fAddedToCache.store(true); } - compositeLocalMatrix->preScale(1 / tileScale.width(), 1 / tileScale.height()); + scaleAdjust->set(1 / tileScale.width(), 1 / tileScale.height()); return tileShader; } bool SkPictureShader::onAppendStages(const StageRec& rec) const { + auto lm = this->totalLocalMatrix(rec.fLocalM); + SkVector scaleAdjust; + // Keep bitmapShader alive by using alloc instead of stack memory auto& bitmapShader = *rec.fAlloc->make>(); - SkMatrix compositeLocalMatrix; - bitmapShader = this->refBitmapShader(rec.fCTM, rec.fLocalM, rec.fDstCS, &compositeLocalMatrix); + bitmapShader = this->refBitmapShader(rec.fCTM, *lm, rec.fDstCS, &scaleAdjust); + + if (!bitmapShader) { + return false; + } + + if (scaleAdjust != SkVector::Make(1, 1)) { + lm.writable()->preScale(scaleAdjust.fX, scaleAdjust.fY); + } StageRec localRec = rec; - localRec.fLocalM = compositeLocalMatrix.isIdentity() ? nullptr : &compositeLocalMatrix; + localRec.fLocalM = lm->isIdentity() ? nullptr : lm.get(); - return bitmapShader && as_SB(bitmapShader)->appendStages(localRec); + return as_SB(bitmapShader)->appendStages(localRec); } ///////////////////////////////////////////////////////////////////////////////////////// SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const { - SkMatrix compositeLocalMatrix; + auto lm = this->totalLocalMatrix(rec.fLocalMatrix); + SkVector scaleAdjust; sk_sp bitmapShader = this->refBitmapShader(*rec.fMatrix, - rec.fLocalMatrix, + *lm, rec.fDstColorSpace, - &compositeLocalMatrix); + &scaleAdjust); if (!bitmapShader) { return nullptr; } + if (scaleAdjust != SkVector::Make(1, 1)) { + lm.writable()->preScale(scaleAdjust.fX, scaleAdjust.fY); + } + ContextRec localRec = rec; - localRec.fLocalMatrix = compositeLocalMatrix.isIdentity() ? nullptr : &compositeLocalMatrix; + localRec.fLocalMatrix = lm->isIdentity() ? nullptr : lm.get(); PictureShaderContext* ctx = alloc->make(*this, localRec, std::move(bitmapShader), alloc); @@ -369,20 +380,24 @@ std::unique_ptr SkPictureShader::asFragmentProcessor( if (args.fContext) { maxTextureSize = args.fContext->caps()->maxTextureSize(); } - SkMatrix compositeLocalMatrix; - sk_sp bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix, + + auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix); + SkVector scaleAdjust; + sk_sp bitmapShader(this->refBitmapShader(*args.fViewMatrix,*lm, args.fDstColorSpaceInfo->colorSpace(), - &compositeLocalMatrix, - maxTextureSize)); + &scaleAdjust, maxTextureSize)); if (!bitmapShader) { return nullptr; } - return as_SB(bitmapShader)->asFragmentProcessor( - GrFPArgs(args.fContext, - args.fViewMatrix, - compositeLocalMatrix.isIdentity() ? nullptr : &compositeLocalMatrix, - args.fFilterQuality, - args.fDstColorSpaceInfo)); + if (scaleAdjust != SkVector::Make(1, 1)) { + lm.writable()->preScale(scaleAdjust.fX, scaleAdjust.fY); + } + + // We want to *reset* args.fPreLocalMatrix, not compose it. + GrFPArgs newArgs(args.fContext, args.fViewMatrix, args.fFilterQuality, args.fDstColorSpaceInfo); + newArgs.fPreLocalMatrix = lm.get(); + + return as_SB(bitmapShader)->asFragmentProcessor(newArgs); } #endif diff --git a/src/shaders/SkPictureShader.h b/src/shaders/SkPictureShader.h index 4970dac81b..5f28b6074b 100644 --- a/src/shaders/SkPictureShader.h +++ b/src/shaders/SkPictureShader.h @@ -46,9 +46,9 @@ private: SkPictureShader(sk_sp, TileMode, TileMode, const SkMatrix*, const SkRect*, sk_sp); - sk_sp refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix, + sk_sp refBitmapShader(const SkMatrix&, const SkMatrix& localMatrix, SkColorSpace* dstColorSpace, - SkMatrix* compositeLocalMatrix, + SkVector* scaleAdjust, const int maxTextureSize = 0) const; class PictureShaderContext : public Context { diff --git a/src/shaders/SkShader.cpp b/src/shaders/SkShader.cpp index 265f22beaa..f3ffd26ef4 100644 --- a/src/shaders/SkShader.cpp +++ b/src/shaders/SkShader.cpp @@ -67,15 +67,26 @@ void SkShaderBase::flatten(SkWriteBuffer& buffer) const { } } +SkTCopyOnFirstWrite +SkShaderBase::totalLocalMatrix(const SkMatrix* preLocalMatrix, + const SkMatrix* postLocalMatrix) const { + SkTCopyOnFirstWrite m(fLocalMatrix); + + if (preLocalMatrix) { + m.writable()->preConcat(*preLocalMatrix); + } + + if (postLocalMatrix) { + m.writable()->postConcat(*postLocalMatrix); + } + + return m; +} + bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm, const SkMatrix* outerLocalMatrix, SkMatrix* totalInverse) const { - SkMatrix total = SkMatrix::Concat(ctm, fLocalMatrix); - if (outerLocalMatrix) { - total.preConcat(*outerLocalMatrix); - } - - return total.invert(totalInverse); + return SkMatrix::Concat(ctm, *this->totalLocalMatrix(outerLocalMatrix)).invert(totalInverse); } bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const { diff --git a/src/shaders/SkShaderBase.h b/src/shaders/SkShaderBase.h index 89674a6ebb..cddc5540ee 100644 --- a/src/shaders/SkShaderBase.h +++ b/src/shaders/SkShaderBase.h @@ -12,6 +12,7 @@ #include "SkMask.h" #include "SkMatrix.h" #include "SkShader.h" +#include "SkTLazy.h" #if SK_SUPPORT_GPU #include "GrFPArgs.h" @@ -185,9 +186,16 @@ public: // If this returns false, then we draw nothing (do not fall back to shader context) bool appendStages(const StageRec&) const; - bool computeTotalInverse(const SkMatrix& ctm, - const SkMatrix* outerLocalMatrix, - SkMatrix* totalInverse) const; + bool SK_WARN_UNUSED_RESULT computeTotalInverse(const SkMatrix& ctm, + const SkMatrix* outerLocalMatrix, + SkMatrix* totalInverse) const; + + // Returns the total local matrix for this shader: + // + // M = postLocalMatrix x shaderLocalMatrix x preLocalMatrix + // + SkTCopyOnFirstWrite totalLocalMatrix(const SkMatrix* preLocalMatrix, + const SkMatrix* postLocalMatrix = nullptr) const; #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP virtual bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode[2]) const { diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp index b6436ca801..a377442dc4 100644 --- a/src/shaders/gradients/SkLinearGradient.cpp +++ b/src/shaders/gradients/SkLinearGradient.cpp @@ -195,16 +195,9 @@ std::unique_ptr SkLinearGradient::asFragmentProcessor( SkASSERT(args.fContext); SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { + if (!this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) { return nullptr; } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } matrix.postConcat(fPtsToUnit); return GrLinearGradient::Make(GrGradientEffect::CreateArgs( diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp index 3f71f38c1f..6d193615a4 100644 --- a/src/shaders/gradients/SkRadialGradient.cpp +++ b/src/shaders/gradients/SkRadialGradient.cpp @@ -162,19 +162,10 @@ void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) { std::unique_ptr SkRadialGradient::asFragmentProcessor( const GrFPArgs& args) const { - SkASSERT(args.fContext); - SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { + if (!this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) { return nullptr; } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } matrix.postConcat(fPtsToUnit); return GrRadialGradient::Make(GrGradientEffect::CreateArgs( diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp index e9c4450b00..3f605d7761 100644 --- a/src/shaders/gradients/SkSweepGradient.cpp +++ b/src/shaders/gradients/SkSweepGradient.cpp @@ -216,16 +216,9 @@ void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) { std::unique_ptr SkSweepGradient::asFragmentProcessor( const GrFPArgs& args) const { SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { + if (!this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) { return nullptr; } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } matrix.postConcat(fPtsToUnit); return GrSweepGradient::Make( diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp index cce25d00b7..88f0f82245 100644 --- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp +++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp @@ -175,9 +175,13 @@ void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const { std::unique_ptr SkTwoPointConicalGradient::asFragmentProcessor( const GrFPArgs& args) const { - SkASSERT(args.fContext); + SkMatrix matrix; + if (!this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) { + return nullptr; + } + return Gr2PtConicalGradientEffect::Make( - GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode, + GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode, args.fDstColorSpaceInfo->colorSpace())); } diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp index c242895f1c..9d447785ed 100644 --- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp +++ b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp @@ -388,18 +388,7 @@ std::unique_ptr Gr2PtConicalGradientEffect::Make( const SkTwoPointConicalGradient& shader = *static_cast(args.fShader); - SkMatrix matrix; - if (!shader.getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fMatrix) { - SkMatrix inv; - if (!args.fMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - + SkMatrix matrix = *args.fMatrix; GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fWrapMode, args.fDstColorSpace); // Data and matrix has to be prepared before constructing TwoPointConicalEffect so its parent diff --git a/tests/TessellatingPathRendererTests.cpp b/tests/TessellatingPathRendererTests.cpp index 8fefc9b56e..0ff0af71e0 100644 --- a/tests/TessellatingPathRendererTests.cpp +++ b/tests/TessellatingPathRendererTests.cpp @@ -479,8 +479,7 @@ static std::unique_ptr create_linear_gradient_processor(GrC sk_sp shader = SkGradientShader::MakeLinear( pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); GrColorSpaceInfo colorSpaceInfo(nullptr, kRGBA_8888_GrPixelConfig); - GrFPArgs args(ctx, &SkMatrix::I(), &SkMatrix::I(), SkFilterQuality::kLow_SkFilterQuality, - &colorSpaceInfo); + GrFPArgs args(ctx, &SkMatrix::I(), SkFilterQuality::kLow_SkFilterQuality, &colorSpaceInfo); return as_SB(shader)->asFragmentProcessor(args); }