From 89cb821e9e9033281a33394709a1feed7eb0523a Mon Sep 17 00:00:00 2001 From: Brian Salomon Date: Mon, 9 Jan 2017 10:48:23 -0500 Subject: [PATCH] Separate SkArithmeticImageFilter from SkXfermodeImageFilter. Change-Id: I145eed7276456b546ca5c66bc1a0f0f74a84f138 Reviewed-on: https://skia-review.googlesource.com/6728 Reviewed-by: Mike Reed Commit-Queue: Brian Salomon --- gm/arithmode.cpp | 14 +- gm/imagefiltersgraph.cpp | 12 +- gm/xfermodeimagefilter.cpp | 3 +- gn/effects.gni | 4 +- include/effects/SkArithmeticImageFilter.h | 30 ++ include/effects/SkXfermodeImageFilter.h | 19 +- src/effects/SkArithmeticImageFilter.cpp | 514 +++++++++++++++++++++++++++ src/effects/SkArithmeticMode.cpp | 92 +---- src/effects/SkArithmeticModePriv.h | 2 + src/effects/SkArithmeticMode_gpu.cpp | 180 ---------- src/effects/SkArithmeticMode_gpu.h | 75 ---- src/effects/SkXfermodeImageFilter.cpp | 203 +---------- src/ports/SkGlobalInitialization_default.cpp | 1 + 13 files changed, 600 insertions(+), 549 deletions(-) create mode 100644 include/effects/SkArithmeticImageFilter.h create mode 100644 src/effects/SkArithmeticImageFilter.cpp delete mode 100644 src/effects/SkArithmeticMode_gpu.cpp delete mode 100644 src/effects/SkArithmeticMode_gpu.h diff --git a/gm/arithmode.cpp b/gm/arithmode.cpp index caa3e28..aec079c 100644 --- a/gm/arithmode.cpp +++ b/gm/arithmode.cpp @@ -6,6 +6,7 @@ */ #include "gm.h" +#include "SkArithmeticImageFilter.h" #include "SkCanvas.h" #include "SkColorPriv.h" #include "SkGradientShader.h" @@ -13,7 +14,6 @@ #include "SkImageSource.h" #include "SkShader.h" #include "SkSurface.h" -#include "SkXfermodeImageFilter.h" #define WW 100 #define HH 32 @@ -109,8 +109,8 @@ protected: canvas->drawImage(dst, 0, 0); canvas->translate(gap, 0); SkPaint paint; - paint.setImageFilter(SkXfermodeImageFilter::MakeArithmetic(k[0], k[1], k[2], k[3], - true, dstFilter, srcFilter, nullptr)); + paint.setImageFilter(SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], true, + dstFilter, srcFilter, nullptr)); canvas->saveLayer(&rect, &paint); canvas->restore(); @@ -137,12 +137,10 @@ protected: canvas->translate(gap, 0); sk_sp bg = - SkXfermodeImageFilter::MakeArithmetic(0, 0, -one / 2, 1, enforcePMColor, - dstFilter); + SkArithmeticImageFilter::Make(0, 0, -one / 2, 1, enforcePMColor, dstFilter); SkPaint p; - p.setImageFilter(SkXfermodeImageFilter::MakeArithmetic(0, one / 2, -one, 1, true, - std::move(bg), dstFilter, - nullptr)); + p.setImageFilter(SkArithmeticImageFilter::Make(0, one / 2, -one, 1, true, + std::move(bg), dstFilter, nullptr)); canvas->saveLayer(&rect, &p); canvas->restore(); canvas->translate(gap, 0); diff --git a/gm/imagefiltersgraph.cpp b/gm/imagefiltersgraph.cpp index fd4d5cf..30c987a 100644 --- a/gm/imagefiltersgraph.cpp +++ b/gm/imagefiltersgraph.cpp @@ -7,6 +7,7 @@ #include "gm.h" +#include "SkArithmeticImageFilter.h" #include "SkBlurImageFilter.h" #include "SkColorFilter.h" #include "SkColorFilterImageFilter.h" @@ -14,13 +15,13 @@ #include "SkImage.h" #include "SkImageSource.h" #include "SkMatrixConvolutionImageFilter.h" +#include "SkMergeImageFilter.h" +#include "SkMorphologyImageFilter.h" #include "SkOffsetImageFilter.h" #include "SkReadBuffer.h" #include "SkSpecialImage.h" #include "SkSpecialSurface.h" #include "SkWriteBuffer.h" -#include "SkMergeImageFilter.h" -#include "SkMorphologyImageFilter.h" #include "SkXfermodeImageFilter.h" class ImageFiltersGraphGM : public skiagm::GM { @@ -88,11 +89,8 @@ protected: matrixFilter)); SkPaint paint; - paint.setImageFilter( - SkXfermodeImageFilter::MakeArithmetic(0, 1, 1, 0, true, - std::move(matrixFilter), - std::move(offsetFilter), - nullptr)); + paint.setImageFilter(SkArithmeticImageFilter::Make( + 0, 1, 1, 0, true, std::move(matrixFilter), std::move(offsetFilter), nullptr)); DrawClippedImage(canvas, fImage.get(), paint); canvas->translate(SkIntToScalar(100), 0); diff --git a/gm/xfermodeimagefilter.cpp b/gm/xfermodeimagefilter.cpp index 9190859..77c3502 100644 --- a/gm/xfermodeimagefilter.cpp +++ b/gm/xfermodeimagefilter.cpp @@ -7,6 +7,7 @@ #include "gm.h" #include "sk_tool_utils.h" +#include "SkArithmeticImageFilter.h" #include "SkImage.h" #include "SkImageSource.h" #include "SkOffsetImageFilter.h" @@ -95,7 +96,7 @@ protected: } } // Test arithmetic mode as image filter - paint.setImageFilter(SkXfermodeImageFilter::MakeArithmetic(0, 1, 1, 0, true, background)); + paint.setImageFilter(SkArithmeticImageFilter::Make(0, 1, 1, 0, true, background)); DrawClippedBitmap(canvas, fBitmap, paint, x, y); x += fBitmap.width() + MARGIN; if (x + fBitmap.width() > WIDTH) { diff --git a/gn/effects.gni b/gn/effects.gni index 10a2576..30e36b2 100644 --- a/gn/effects.gni +++ b/gn/effects.gni @@ -17,9 +17,8 @@ skia_effects_sources = [ "$_src/effects/Sk2DPathEffect.cpp", "$_src/effects/SkAlphaThresholdFilter.cpp", "$_src/effects/SkArcToPathEffect.cpp", + "$_src/effects/SkArithmeticImageFilter.cpp", "$_src/effects/SkArithmeticMode.cpp", - "$_src/effects/SkArithmeticMode_gpu.cpp", - "$_src/effects/SkArithmeticMode_gpu.h", "$_src/effects/SkBlurDrawLooper.cpp", "$_src/effects/SkBlurMask.cpp", "$_src/effects/SkBlurMask.h", @@ -91,6 +90,7 @@ skia_effects_sources = [ "$_include/effects/Sk2DPathEffect.h", "$_include/effects/SkAlphaThresholdFilter.h", "$_include/effects/SkArithmeticMode.h", + "$_include/effects/SkArithmeticImageFilter.h", "$_include/effects/SkBlurDrawLooper.h", "$_include/effects/SkBlurImageFilter.h", "$_include/effects/SkBlurMaskFilter.h", diff --git a/include/effects/SkArithmeticImageFilter.h b/include/effects/SkArithmeticImageFilter.h new file mode 100644 index 0000000..ef59603 --- /dev/null +++ b/include/effects/SkArithmeticImageFilter.h @@ -0,0 +1,30 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkArithmeticImageFilter_DEFINED +#define SkArithmeticImageFilter_DEFINED + +#include "SkImageFilter.h" + +class SK_API SkArithmeticImageFilter { +public: + static sk_sp Make(float k1, float k2, float k3, float k4, bool enforcePMColor, + sk_sp background, + sk_sp foreground, + const SkImageFilter::CropRect* cropRect); + static sk_sp Make(float k1, float k2, float k3, float k4, bool enforcePMColor, + sk_sp background) { + return Make(k1, k2, k3, k4, enforcePMColor, std::move(background), nullptr, nullptr); + } + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP(); + +private: + SkArithmeticImageFilter(); // can't instantiate +}; + +#endif diff --git a/include/effects/SkXfermodeImageFilter.h b/include/effects/SkXfermodeImageFilter.h index 5fbd1af..5e8b587 100644 --- a/include/effects/SkXfermodeImageFilter.h +++ b/include/effects/SkXfermodeImageFilter.h @@ -8,13 +8,13 @@ #ifndef SkXfermodeImageFilter_DEFINED #define SkXfermodeImageFilter_DEFINED +#include "SkArithmeticImageFilter.h" #include "SkBlendMode.h" #include "SkImageFilter.h" /** - * This filter takes an xfermode, and uses it to composite the foreground - * over the background. If foreground or background is NULL, the input - * bitmap (src) is used instead. + * This filter takes a SkBlendMode, and uses it to composite the foreground over the background. + * If foreground or background is NULL, the input bitmap (src) is used instead. */ class SK_API SkXfermodeImageFilter { public: @@ -25,16 +25,19 @@ public: return Make(mode, std::move(background), nullptr, nullptr); } + // Arithmetic image filtering used to be implemented using SkXfermode. Some clients still rely + // on these factories existing in this class. static sk_sp MakeArithmetic(float k1, float k2, float k3, float k4, - bool enforcePMColor, - sk_sp background, + bool enforcePMColor, sk_sp background, sk_sp foreground, - const SkImageFilter::CropRect* cropRect); + const SkImageFilter::CropRect* cropRect) { + return SkArithmeticImageFilter::Make(k1, k2, k3, k4, enforcePMColor, std::move(background), + std::move(foreground), cropRect); + } static sk_sp MakeArithmetic(float k1, float k2, float k3, float k4, bool enforcePMColor, sk_sp background) { - return MakeArithmetic(k1, k2, k3, k4, enforcePMColor, std::move(background), - nullptr, nullptr); + return SkArithmeticImageFilter::Make(k1, k2, k3, k4, enforcePMColor, std::move(background)); } SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP(); diff --git a/src/effects/SkArithmeticImageFilter.cpp b/src/effects/SkArithmeticImageFilter.cpp new file mode 100644 index 0000000..984aedc --- /dev/null +++ b/src/effects/SkArithmeticImageFilter.cpp @@ -0,0 +1,514 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkArithmeticImageFilter.h" +#include "SkArithmeticModePriv.h" +#include "SkCanvas.h" +#include "SkNx.h" +#include "SkReadBuffer.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" +#include "SkWriteBuffer.h" +#include "SkXfermodeImageFilter.h" +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrRenderTargetContext.h" +#include "GrTextureProxy.h" +#include "SkGr.h" +#include "SkGrPriv.h" +#include "effects/GrConstColorProcessor.h" +#include "effects/GrTextureDomain.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" +#endif + +namespace { +class ArithmeticImageFilterImpl : public SkImageFilter { +public: + ArithmeticImageFilterImpl(float k1, float k2, float k3, float k4, bool enforcePMColor, + sk_sp inputs[2], const CropRect* cropRect) + : INHERITED(inputs, 2, cropRect), fK{k1, k2, k3, k4}, fEnforcePMColor(enforcePMColor) {} + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(ArithmeticImageFilterImpl) + +protected: + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; + +#if SK_SUPPORT_GPU + sk_sp filterImageGPU(SkSpecialImage* source, + sk_sp background, + const SkIPoint& backgroundOffset, + sk_sp foreground, + const SkIPoint& foregroundOffset, + const SkIRect& bounds, + const OutputProperties& outputProperties) const; +#endif + + void flatten(SkWriteBuffer& buffer) const override { + this->INHERITED::flatten(buffer); + for (int i = 0; i < 4; ++i) { + buffer.writeScalar(fK[i]); + } + buffer.writeBool(fEnforcePMColor); + } + + void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const; + +private: + const float fK[4]; + const bool fEnforcePMColor; + + friend class ::SkArithmeticImageFilter; + + typedef SkImageFilter INHERITED; +}; +} + +sk_sp ArithmeticImageFilterImpl::CreateProc(SkReadBuffer& buffer) { + SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2); + float k[4]; + for (int i = 0; i < 4; ++i) { + k[i] = buffer.readScalar(); + } + const bool enforcePMColor = buffer.readBool(); + return SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], enforcePMColor, common.getInput(0), + common.getInput(1), &common.cropRect()); +} + +static Sk4f pin(float min, const Sk4f& val, float max) { + return Sk4f::Max(min, Sk4f::Min(val, max)); +} + +template +void arith_span(const float k[], SkPMColor dst[], const SkPMColor src[], int count) { + const Sk4f k1 = k[0] * (1/255.0f), + k2 = k[1], + k3 = k[2], + k4 = k[3] * 255.0f + 0.5f; + + for (int i = 0; i < count; i++) { + Sk4f s = SkNx_cast(Sk4b::Load(src+i)), + d = SkNx_cast(Sk4b::Load(dst+i)), + r = pin(0, k1*s*d + k2*s + k3*d + k4, 255); + if (EnforcePMColor) { + Sk4f a = SkNx_shuffle<3,3,3,3>(r); + r = Sk4f::Min(a, r); + } + SkNx_cast(r).store(dst+i); + } +} + +// apply mode to src==transparent (0) +template void arith_transparent(const float k[], SkPMColor dst[], int count) { + const Sk4f k3 = k[2], + k4 = k[3] * 255.0f + 0.5f; + + for (int i = 0; i < count; i++) { + Sk4f d = SkNx_cast(Sk4b::Load(dst+i)), + r = pin(0, k3*d + k4, 255); + if (EnforcePMColor) { + Sk4f a = SkNx_shuffle<3,3,3,3>(r); + r = Sk4f::Min(a, r); + } + SkNx_cast(r).store(dst+i); + } +} + +static bool intersect(SkPixmap* dst, SkPixmap* src, int srcDx, int srcDy) { + SkIRect dstR = SkIRect::MakeWH(dst->width(), dst->height()); + SkIRect srcR = SkIRect::MakeXYWH(srcDx, srcDy, src->width(), src->height()); + SkIRect sect; + if (!sect.intersect(dstR, srcR)) { + return false; + } + *dst = SkPixmap(dst->info().makeWH(sect.width(), sect.height()), + dst->addr(sect.fLeft, sect.fTop), + dst->rowBytes()); + *src = SkPixmap(src->info().makeWH(sect.width(), sect.height()), + src->addr(SkTMax(0, -srcDx), SkTMax(0, -srcDy)), + src->rowBytes()); + return true; +} + +sk_sp ArithmeticImageFilterImpl::onFilterImage(SkSpecialImage* source, + const Context& ctx, + SkIPoint* offset) const { + SkIPoint backgroundOffset = SkIPoint::Make(0, 0); + sk_sp background(this->filterInput(0, source, ctx, &backgroundOffset)); + + SkIPoint foregroundOffset = SkIPoint::Make(0, 0); + sk_sp foreground(this->filterInput(1, source, ctx, &foregroundOffset)); + + SkIRect foregroundBounds = SkIRect::EmptyIRect(); + if (foreground) { + foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(), + foreground->width(), foreground->height()); + } + + SkIRect srcBounds = SkIRect::EmptyIRect(); + if (background) { + srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(), + background->width(), background->height()); + } + + srcBounds.join(foregroundBounds); + if (srcBounds.isEmpty()) { + return nullptr; + } + + SkIRect bounds; + if (!this->applyCropRect(ctx, srcBounds, &bounds)) { + return nullptr; + } + + offset->fX = bounds.left(); + offset->fY = bounds.top(); + +#if SK_SUPPORT_GPU + if (source->isTextureBacked()) { + return this->filterImageGPU(source, background, backgroundOffset, foreground, + foregroundOffset, bounds, ctx.outputProperties()); + } +#endif + + sk_sp surf(source->makeSurface(ctx.outputProperties(), bounds.size())); + if (!surf) { + return nullptr; + } + + SkCanvas* canvas = surf->getCanvas(); + SkASSERT(canvas); + + canvas->clear(0x0); // can't count on background to fully clear the background + canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); + + if (background) { + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + background->draw(canvas, SkIntToScalar(backgroundOffset.fX), + SkIntToScalar(backgroundOffset.fY), &paint); + } + + this->drawForeground(canvas, foreground.get(), foregroundBounds); + + return surf->makeImageSnapshot(); +} + +#if SK_SUPPORT_GPU + +namespace { +class ArithmeticFP : public GrFragmentProcessor { +public: + static sk_sp Make(float k1, float k2, float k3, float k4, + bool enforcePMColor, sk_sp dst) { + return sk_sp( + new ArithmeticFP(k1, k2, k3, k4, enforcePMColor, std::move(dst))); + } + + ~ArithmeticFP() override {} + + const char* name() const override { return "Arithmetic"; } + + SkString dumpInfo() const override { + SkString str; + str.appendf("K1: %.2f K2: %.2f K3: %.2f K4: %.2f", fK1, fK2, fK3, fK4); + return str; + } + + float k1() const { return fK1; } + float k2() const { return fK2; } + float k3() const { return fK3; } + float k4() const { return fK4; } + bool enforcePMColor() const { return fEnforcePMColor; } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + class GLSLFP : public GrGLSLFragmentProcessor { + public: + void emitCode(EmitArgs& args) override { + const ArithmeticFP& arith = args.fFp.cast(); + + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + SkString dstColor("dstColor"); + this->emitChild(0, nullptr, &dstColor, args); + + fKUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, + kDefault_GrSLPrecision, "k"); + const char* kUni = args.fUniformHandler->getUniformCStr(fKUni); + + // We don't try to optimize for this case at all + if (!args.fInputColor) { + fragBuilder->codeAppend("const vec4 src = vec4(1);"); + } else { + fragBuilder->codeAppendf("vec4 src = %s;", args.fInputColor); + } + + fragBuilder->codeAppendf("vec4 dst = %s;", dstColor.c_str()); + fragBuilder->codeAppendf("%s = %s.x * src * dst + %s.y * src + %s.z * dst + %s.w;", + args.fOutputColor, kUni, kUni, kUni, kUni); + fragBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);\n", args.fOutputColor, + args.fOutputColor); + if (arith.fEnforcePMColor) { + fragBuilder->codeAppendf("%s.rgb = min(%s.rgb, %s.a);", args.fOutputColor, + args.fOutputColor, args.fOutputColor); + } + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, + const GrProcessor& proc) override { + const ArithmeticFP& arith = proc.cast(); + pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4()); + } + + private: + GrGLSLProgramDataManager::UniformHandle fKUni; + }; + return new GLSLFP; + } + + void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { + b->add32(fEnforcePMColor ? 1 : 0); + } + + bool onIsEqual(const GrFragmentProcessor& fpBase) const override { + const ArithmeticFP& fp = fpBase.cast(); + return fK1 == fp.fK1 && fK2 == fp.fK2 && fK3 == fp.fK3 && fK4 == fp.fK4 && + fEnforcePMColor == fp.fEnforcePMColor; + } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + // TODO: optimize this + inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); + } + + ArithmeticFP(float k1, float k2, float k3, float k4, bool enforcePMColor, + sk_sp dst) + : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) { + this->initClassID(); + SkASSERT(dst); + SkDEBUGCODE(int dstIndex =) this->registerChildProcessor(std::move(dst)); + SkASSERT(0 == dstIndex); + } + + float fK1, fK2, fK3, fK4; + bool fEnforcePMColor; + + GR_DECLARE_FRAGMENT_PROCESSOR_TEST; + typedef GrFragmentProcessor INHERITED; +}; +} + +sk_sp ArithmeticFP::TestCreate(GrProcessorTestData* d) { + float k1 = d->fRandom->nextF(); + float k2 = d->fRandom->nextF(); + float k3 = d->fRandom->nextF(); + float k4 = d->fRandom->nextF(); + bool enforcePMColor = d->fRandom->nextBool(); + + sk_sp dst(GrProcessorUnitTest::MakeChildFP(d)); + return ArithmeticFP::Make(k1, k2, k3, k4, enforcePMColor, std::move(dst)); +} + +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ArithmeticFP); + +sk_sp ArithmeticImageFilterImpl::filterImageGPU( + SkSpecialImage* source, + sk_sp + background, + const SkIPoint& backgroundOffset, + sk_sp + foreground, + const SkIPoint& foregroundOffset, + const SkIRect& bounds, + const OutputProperties& outputProperties) const { + SkASSERT(source->isTextureBacked()); + + GrContext* context = source->getContext(); + + sk_sp backgroundTex, foregroundTex; + + if (background) { + backgroundTex = background->asTextureRef(context); + } + + if (foreground) { + foregroundTex = foreground->asTextureRef(context); + } + + GrPaint paint; + sk_sp bgFP; + + if (backgroundTex) { + SkMatrix backgroundMatrix; + backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); + backgroundMatrix.preTranslate(-SkIntToScalar(backgroundOffset.fX), + -SkIntToScalar(backgroundOffset.fY)); + sk_sp bgXform = + GrColorSpaceXform::Make(background->getColorSpace(), outputProperties.colorSpace()); + bgFP = GrTextureDomainEffect::Make( + backgroundTex.get(), std::move(bgXform), backgroundMatrix, + GrTextureDomain::MakeTexelDomain(backgroundTex.get(), background->subset()), + GrTextureDomain::kDecal_Mode, GrSamplerParams::kNone_FilterMode); + } else { + bgFP = GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), + GrConstColorProcessor::kIgnore_InputMode); + } + + if (foregroundTex) { + SkMatrix foregroundMatrix; + foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); + foregroundMatrix.preTranslate(-SkIntToScalar(foregroundOffset.fX), + -SkIntToScalar(foregroundOffset.fY)); + sk_sp fgXform = + GrColorSpaceXform::Make(foreground->getColorSpace(), outputProperties.colorSpace()); + sk_sp foregroundFP; + + foregroundFP = GrTextureDomainEffect::Make( + foregroundTex.get(), std::move(fgXform), foregroundMatrix, + GrTextureDomain::MakeTexelDomain(foregroundTex.get(), foreground->subset()), + GrTextureDomain::kDecal_Mode, GrSamplerParams::kNone_FilterMode); + + paint.addColorFragmentProcessor(std::move(foregroundFP)); + + sk_sp xferFP = + ArithmeticFP::Make(fK[0], fK[1], fK[2], fK[3], fEnforcePMColor, std::move(bgFP)); + + // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed + if (xferFP) { + paint.addColorFragmentProcessor(std::move(xferFP)); + } + } else { + paint.addColorFragmentProcessor(std::move(bgFP)); + } + + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + + sk_sp renderTargetContext(context->makeDeferredRenderTargetContext( + SkBackingFit::kApprox, bounds.width(), bounds.height(), + GrRenderableConfigForColorSpace(outputProperties.colorSpace()), + sk_ref_sp(outputProperties.colorSpace()))); + if (!renderTargetContext) { + return nullptr; + } + paint.setGammaCorrect(renderTargetContext->isGammaCorrect()); + + SkMatrix matrix; + matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); + renderTargetContext->drawRect(GrNoClip(), paint, GrAA::kNo, matrix, SkRect::Make(bounds)); + + return SkSpecialImage::MakeDeferredFromGpu(context, + SkIRect::MakeWH(bounds.width(), bounds.height()), + kNeedNewImageUniqueID_SpecialImage, + sk_ref_sp(renderTargetContext->asDeferredTexture()), + sk_ref_sp(renderTargetContext->getColorSpace())); +} +#endif + +void ArithmeticImageFilterImpl::drawForeground(SkCanvas* canvas, SkSpecialImage* img, + const SkIRect& fgBounds) const { + SkPixmap dst; + if (!canvas->peekPixels(&dst)) { + return; + } + + const SkMatrix& ctm = canvas->getTotalMatrix(); + SkASSERT(ctm.getType() <= SkMatrix::kTranslate_Mask); + const int dx = SkScalarRoundToInt(ctm.getTranslateX()); + const int dy = SkScalarRoundToInt(ctm.getTranslateY()); + + if (img) { + SkBitmap srcBM; + SkPixmap src; + if (!img->getROPixels(&srcBM)) { + return; + } + srcBM.lockPixels(); + if (!srcBM.peekPixels(&src)) { + return; + } + + auto proc = fEnforcePMColor ? arith_span : arith_span; + SkPixmap tmpDst = dst; + if (intersect(&tmpDst, &src, fgBounds.fLeft + dx, fgBounds.fTop + dy)) { + for (int y = 0; y < tmpDst.height(); ++y) { + proc(fK, tmpDst.writable_addr32(0, y), src.addr32(0, y), tmpDst.width()); + } + } + } + + // Now apply the mode with transparent-color to the outside of the fg image + SkRegion outside(SkIRect::MakeWH(dst.width(), dst.height())); + outside.op(fgBounds.makeOffset(dx, dy), SkRegion::kDifference_Op); + auto proc = fEnforcePMColor ? arith_transparent : arith_transparent; + for (SkRegion::Iterator iter(outside); !iter.done(); iter.next()) { + const SkIRect r = iter.rect(); + for (int y = r.fTop; y < r.fBottom; ++y) { + proc(fK, dst.writable_addr32(r.fLeft, y), r.width()); + } + } +} + +#ifndef SK_IGNORE_TO_STRING +void ArithmeticImageFilterImpl::toString(SkString* str) const { + str->appendf("SkArithmeticImageFilter: ("); + str->appendf("K[]: (%f %f %f %f)", fK[0], fK[1], fK[2], fK[3]); + if (this->getInput(0)) { + str->appendf("foreground: ("); + this->getInput(0)->toString(str); + str->appendf(")"); + } + if (this->getInput(1)) { + str->appendf("background: ("); + this->getInput(1)->toString(str); + str->appendf(")"); + } + str->append(")"); +} +#endif + +sk_sp SkArithmeticImageFilter::Make(float k1, float k2, float k3, float k4, + bool enforcePMColor, + sk_sp background, + sk_sp foreground, + const SkImageFilter::CropRect* crop) { + if (!SkScalarIsFinite(k1) || !SkScalarIsFinite(k2) || !SkScalarIsFinite(k3) || + !SkScalarIsFinite(k4)) { + return nullptr; + } + + // are we nearly some other "std" mode? + int mode = -1; // illegal mode + if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) && SkScalarNearlyZero(k3) && + SkScalarNearlyZero(k4)) { + mode = (int)SkBlendMode::kSrc; + } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && + SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) { + mode = (int)SkBlendMode::kDst; + } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && SkScalarNearlyZero(k3) && + SkScalarNearlyZero(k4)) { + mode = (int)SkBlendMode::kClear; + } + if (mode >= 0) { + return SkXfermodeImageFilter::Make((SkBlendMode)mode, std::move(background), + std::move(foreground), crop); + } + + sk_sp inputs[2] = {std::move(background), std::move(foreground)}; + return sk_sp( + new ArithmeticImageFilterImpl(k1, k2, k3, k4, enforcePMColor, inputs, crop)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkArithmeticImageFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(ArithmeticImageFilterImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp index c85d5c3..d604a12 100644 --- a/src/effects/SkArithmeticMode.cpp +++ b/src/effects/SkArithmeticMode.cpp @@ -6,17 +6,11 @@ */ #include "SkArithmeticModePriv.h" -#include "SkColorPriv.h" -#include "SkNx.h" -#include "SkRasterPipeline.h" #include "SkReadBuffer.h" -#include "SkString.h" -#include "SkUnPreMultiply.h" -#include "SkWriteBuffer.h" -#if SK_SUPPORT_GPU -#include "SkArithmeticMode_gpu.h" -#endif +// This class only exists to unflatten instances that were serialized into old pictures as part of +// SkXfermodeImageFilter before the advent of SkBlendMode. Those image filters will now be +// transformed to SkArithmeticImageFilter which does not use this class in its implementation. class SkArithmeticMode_scalar : public SkXfermode { public: SkArithmeticMode_scalar(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, @@ -28,20 +22,15 @@ public: fEnforcePMColor = enforcePMColor; } - void xfer32(SkPMColor[], const SkPMColor[], int count, const SkAlpha[]) const override; + void xfer32(SkPMColor[], const SkPMColor[], int count, const SkAlpha[]) const override { + SkFAIL("This should never be called."); + } SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticMode_scalar) -#if SK_SUPPORT_GPU - sk_sp makeFragmentProcessorForImageFilter( - sk_sp dst) const override; - const GrXPFactory* asXPFactory() const override { - SkFAIL("This should only be used as a FP."); - return nullptr; - } -#endif - + // This is used to extract the arithmetic params into an SkArithmeticImageFilter. Afterwards, + // this object is destroyed and arithemtic blending is implemented directly in the image filter. bool isArithmetic(SkArithmeticParams* params) const override { if (params) { memcpy(params->fK, fK, 4 * sizeof(float)); @@ -51,13 +40,7 @@ public: } private: - void flatten(SkWriteBuffer& buffer) const override { - buffer.writeScalar(fK[0]); - buffer.writeScalar(fK[1]); - buffer.writeScalar(fK[2]); - buffer.writeScalar(fK[3]); - buffer.writeBool(fEnforcePMColor); - } + void flatten(SkWriteBuffer& buffer) const override { SkFAIL("This shouild never be called."); } SkScalar fK[4]; bool fEnforcePMColor; @@ -76,48 +59,9 @@ sk_sp SkArithmeticMode_scalar::CreateProc(SkReadBuffer& buffer) { return SkArithmeticMode::Make(k1, k2, k3, k4, enforcePMColor); } -void SkArithmeticMode_scalar::xfer32(SkPMColor dst[], const SkPMColor src[], - int count, const SkAlpha aaCoverage[]) const { - const Sk4f k1 = fK[0] * (1/255.0f), - k2 = fK[1], - k3 = fK[2], - k4 = fK[3] * 255.0f + 0.5f; - - auto pin = [](float min, const Sk4f& val, float max) { - return Sk4f::Max(min, Sk4f::Min(val, max)); - }; - - for (int i = 0; i < count; i++) { - if (aaCoverage && aaCoverage[i] == 0) { - continue; - } - - Sk4f s = SkNx_cast(Sk4b::Load(src+i)), - d = SkNx_cast(Sk4b::Load(dst+i)), - r = pin(0, k1*s*d + k2*s + k3*d + k4, 255); - - if (fEnforcePMColor) { - Sk4f a = SkNx_shuffle<3,3,3,3>(r); - r = Sk4f::Min(a, r); - } - - if (aaCoverage && aaCoverage[i] != 255) { - Sk4f c = aaCoverage[i] * (1/255.0f); - r = d + (r-d)*c; - } - - SkNx_cast(r).store(dst+i); - } -} - #ifndef SK_IGNORE_TO_STRING void SkArithmeticMode_scalar::toString(SkString* str) const { - str->append("SkArithmeticMode_scalar: "); - for (int i = 0; i < 4; ++i) { - str->appendScalar(fK[i]); - str->append(" "); - } - str->appendS32(fEnforcePMColor ? 1 : 0); + SkFAIL("This should never be called."); } #endif @@ -135,22 +79,6 @@ sk_sp SkArithmeticMode::Make(SkScalar k1, SkScalar k2, SkScalar k3, return sk_make_sp(k1, k2, k3, k4, enforcePMColor); } - -////////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU -sk_sp SkArithmeticMode_scalar::makeFragmentProcessorForImageFilter( - sk_sp dst) const { - return GrArithmeticFP::Make(SkScalarToFloat(fK[0]), - SkScalarToFloat(fK[1]), - SkScalarToFloat(fK[2]), - SkScalarToFloat(fK[3]), - fEnforcePMColor, - std::move(dst)); -} - -#endif - SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkArithmeticMode) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArithmeticMode_scalar) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/src/effects/SkArithmeticModePriv.h b/src/effects/SkArithmeticModePriv.h index ff7f357..4c13a81 100644 --- a/src/effects/SkArithmeticModePriv.h +++ b/src/effects/SkArithmeticModePriv.h @@ -18,6 +18,8 @@ struct SkArithmeticParams { bool fEnforcePMColor; }; +// This only exists to unflatten instances that were serialized into old pictures as part of +// SkXfermodeImageFilter before the advent of SkBlendMode. class SK_API SkArithmeticMode { public: /** diff --git a/src/effects/SkArithmeticMode_gpu.cpp b/src/effects/SkArithmeticMode_gpu.cpp deleted file mode 100644 index a6c7866..0000000 --- a/src/effects/SkArithmeticMode_gpu.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkArithmeticMode_gpu.h" - -#if SK_SUPPORT_GPU -#include "GrContext.h" -#include "GrFragmentProcessor.h" -#include "GrInvariantOutput.h" -#include "GrProcessor.h" -#include "GrTexture.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#include "glsl/GrGLSLXferProcessor.h" - -static void add_arithmetic_code(GrGLSLFragmentBuilder* fragBuilder, - const char* srcColor, - const char* dstColor, - const char* outputColor, - const char* kUni, - bool enforcePMColor) { - // We don't try to optimize for this case at all - if (nullptr == srcColor) { - fragBuilder->codeAppend("const vec4 src = vec4(1);"); - } else { - fragBuilder->codeAppendf("vec4 src = %s;", srcColor); - } - - fragBuilder->codeAppendf("vec4 dst = %s;", dstColor); - fragBuilder->codeAppendf("%s = %s.x * src * dst + %s.y * src + %s.z * dst + %s.w;", - outputColor, kUni, kUni, kUni, kUni); - fragBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor); - if (enforcePMColor) { - fragBuilder->codeAppendf("%s.rgb = min(%s.rgb, %s.a);", - outputColor, outputColor, outputColor); - } -} - -class GLArithmeticFP : public GrGLSLFragmentProcessor { -public: - void emitCode(EmitArgs& args) override { - const GrArithmeticFP& arith = args.fFp.cast(); - - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString dstColor("dstColor"); - this->emitChild(0, nullptr, &dstColor, args); - - fKUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "k"); - const char* kUni = args.fUniformHandler->getUniformCStr(fKUni); - - add_arithmetic_code(fragBuilder, - args.fInputColor, - dstColor.c_str(), - args.fOutputColor, - kUni, - arith.enforcePMColor()); - } - - static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - const GrArithmeticFP& arith = proc.cast(); - uint32_t key = arith.enforcePMColor() ? 1 : 0; - b->add32(key); - } - -protected: - void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { - const GrArithmeticFP& arith = proc.cast(); - pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4()); - } - -private: - GrGLSLProgramDataManager::UniformHandle fKUni; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////// - -GrArithmeticFP::GrArithmeticFP(float k1, float k2, float k3, float k4, bool enforcePMColor, - sk_sp dst) - : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) { - this->initClassID(); - - SkASSERT(dst); - SkDEBUGCODE(int dstIndex = )this->registerChildProcessor(std::move(dst)); - SkASSERT(0 == dstIndex); -} - -void GrArithmeticFP::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GLArithmeticFP::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* GrArithmeticFP::onCreateGLSLInstance() const { - return new GLArithmeticFP; -} - -bool GrArithmeticFP::onIsEqual(const GrFragmentProcessor& fpBase) const { - const GrArithmeticFP& fp = fpBase.cast(); - return fK1 == fp.fK1 && - fK2 == fp.fK2 && - fK3 == fp.fK3 && - fK4 == fp.fK4 && - fEnforcePMColor == fp.fEnforcePMColor; -} - -void GrArithmeticFP::onComputeInvariantOutput(GrInvariantOutput* inout) const { - // TODO: optimize this - inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); -} - -/////////////////////////////////////////////////////////////////////////////// - -sk_sp GrArithmeticFP::TestCreate(GrProcessorTestData* d) { - float k1 = d->fRandom->nextF(); - float k2 = d->fRandom->nextF(); - float k3 = d->fRandom->nextF(); - float k4 = d->fRandom->nextF(); - bool enforcePMColor = d->fRandom->nextBool(); - - sk_sp dst(GrProcessorUnitTest::MakeChildFP(d)); - return GrArithmeticFP::Make(k1, k2, k3, k4, enforcePMColor, std::move(dst)); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrArithmeticFP); - -/////////////////////////////////////////////////////////////////////////////// -// Xfer Processor -/////////////////////////////////////////////////////////////////////////////// - -class ArithmeticXP : public GrXferProcessor { -public: - ArithmeticXP(const DstTexture*, bool hasMixedSamples, - float k1, float k2, float k3, float k4, bool enforcePMColor); - - const char* name() const override { return "Arithmetic"; } - - GrGLSLXferProcessor* createGLSLInstance() const override; - - float k1() const { return fK1; } - float k2() const { return fK2; } - float k3() const { return fK3; } - float k4() const { return fK4; } - bool enforcePMColor() const { return fEnforcePMColor; } - -private: - GrXferProcessor::OptFlags onGetOptimizations(const GrPipelineAnalysis&, - bool doesStencilWrite, - GrColor* overrideColor, - const GrCaps& caps) const override; - - void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; - - bool onIsEqual(const GrXferProcessor& xpBase) const override { - const ArithmeticXP& xp = xpBase.cast(); - if (fK1 != xp.fK1 || - fK2 != xp.fK2 || - fK3 != xp.fK3 || - fK4 != xp.fK4 || - fEnforcePMColor != xp.fEnforcePMColor) { - return false; - } - return true; - } - - float fK1, fK2, fK3, fK4; - bool fEnforcePMColor; - - typedef GrXferProcessor INHERITED; -}; - -#endif diff --git a/src/effects/SkArithmeticMode_gpu.h b/src/effects/SkArithmeticMode_gpu.h deleted file mode 100644 index f7ded9b..0000000 --- a/src/effects/SkArithmeticMode_gpu.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkArithmeticMode_gpu_DEFINED -#define SkArithmeticMode_gpu_DEFINED - -#include "SkTypes.h" - -#if SK_SUPPORT_GPU - -#include "GrCaps.h" -#include "GrCoordTransform.h" -#include "GrFragmentProcessor.h" -#include "GrTypes.h" -#include "GrXferProcessor.h" - -class GrInvariantOutput; -class GrProcOptInfo; -class GrTexture; - -/////////////////////////////////////////////////////////////////////////////// -// Fragment Processor -/////////////////////////////////////////////////////////////////////////////// - -class GrGLArtithmeticFP; - -class GrArithmeticFP : public GrFragmentProcessor { -public: - static sk_sp Make(float k1, float k2, float k3, float k4, - bool enforcePMColor, sk_sp dst) { - return sk_sp(new GrArithmeticFP(k1, k2, k3, k4, enforcePMColor, - std::move(dst))); - } - - ~GrArithmeticFP() override {} - - const char* name() const override { return "Arithmetic"; } - - SkString dumpInfo() const override { - SkString str; - str.appendf("K1: %.2f K2: %.2f K3: %.2f K4: %.2f", fK1, fK2, fK3, fK4); - return str; - } - - float k1() const { return fK1; } - float k2() const { return fK2; } - float k3() const { return fK3; } - float k4() const { return fK4; } - bool enforcePMColor() const { return fEnforcePMColor; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; - - bool onIsEqual(const GrFragmentProcessor&) const override; - - void onComputeInvariantOutput(GrInvariantOutput* inout) const override; - - GrArithmeticFP(float k1, float k2, float k3, float k4, bool enforcePMColor, - sk_sp dst); - - float fK1, fK2, fK3, fK4; - bool fEnforcePMColor; - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - typedef GrFragmentProcessor INHERITED; -}; - -#endif -#endif diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp index d90cb91..2408619 100644 --- a/src/effects/SkXfermodeImageFilter.cpp +++ b/src/effects/SkXfermodeImageFilter.cpp @@ -6,8 +6,8 @@ */ #include "SkXfermodeImageFilter.h" +#include "SkArithmeticImageFilter.h" #include "SkArithmeticModePriv.h" - #include "SkCanvas.h" #include "SkColorPriv.h" #include "SkReadBuffer.h" @@ -22,7 +22,6 @@ #include "effects/GrConstColorProcessor.h" #include "effects/GrTextureDomain.h" #include "effects/GrSimpleTextureEffect.h" -#include "SkArithmeticMode_gpu.h" #include "SkGr.h" #include "SkGrPriv.h" #endif @@ -52,12 +51,14 @@ protected: void flatten(SkWriteBuffer&) const override; - virtual void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const; + void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const; #if SK_SUPPORT_GPU - virtual sk_sp makeFGFrag(sk_sp bgFP) const; + sk_sp makeFGFrag(sk_sp bgFP) const; #endif private: + static sk_sp LegacyArithmeticCreateProc(SkReadBuffer& buffer); + SkBlendMode fMode; friend class SkXfermodeImageFilter; @@ -108,10 +109,9 @@ sk_sp SkXfermodeImageFilter_Base::CreateProc(SkReadBuffer& buffer return SkXfermodeImageFilter::Make((SkBlendMode)mode, common.getInput(0), common.getInput(1), &common.cropRect()); } else { - return SkXfermodeImageFilter::MakeArithmetic(arith.fK[0], arith.fK[1], arith.fK[2], - arith.fK[3], arith.fEnforcePMColor, - common.getInput(0), - common.getInput(1), &common.cropRect()); + return SkArithmeticImageFilter::Make(arith.fK[0], arith.fK[1], arith.fK[2], arith.fK[3], + arith.fEnforcePMColor, common.getInput(0), + common.getInput(1), &common.cropRect()); } } @@ -337,196 +337,23 @@ SkXfermodeImageFilter_Base::makeFGFrag(sk_sp bgFP) const { } #endif - /////////////////////////////////////////////////////////////////////////////////////////////////// -class SkArithmeticImageFilter : public SkXfermodeImageFilter_Base { -public: - SkArithmeticImageFilter(float k1, float k2, float k3, float k4, bool enforcePMColor, - sk_sp inputs[2], const CropRect* cropRect) - // need to pass a blendmode to our inherited constructor, but we ignore it - : SkXfermodeImageFilter_Base(SkBlendMode::kSrcOver, inputs, cropRect) - , fK{ k1, k2, k3, k4 } - , fEnforcePMColor(enforcePMColor) - {} - - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticImageFilter) - -protected: - void flatten(SkWriteBuffer& buffer) const override { - this->INHERITED::flatten(buffer); - for (int i = 0; i < 4; ++i) { - buffer.writeScalar(fK[i]); - } - buffer.writeBool(fEnforcePMColor); - } - void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const override; -#if SK_SUPPORT_GPU - sk_sp makeFGFrag(sk_sp bgFP) const override { - return GrArithmeticFP::Make(fK[0], fK[1], fK[2], fK[3], fEnforcePMColor, std::move(bgFP)); - } -#endif - -private: - const float fK[4]; - const bool fEnforcePMColor; - - friend class SkXfermodeImageFilter; - - typedef SkXfermodeImageFilter_Base INHERITED; -}; - -sk_sp SkArithmeticImageFilter::CreateProc(SkReadBuffer& buffer) { +sk_sp SkXfermodeImageFilter_Base::LegacyArithmeticCreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2); - - // skip the mode (srcover) our parent-class wrote + // skip the unused mode (srcover) field SkDEBUGCODE(int mode =) unflatten_blendmode(buffer, nullptr); if (!buffer.isValid()) { return nullptr; } SkASSERT(SkBlendMode::kSrcOver == (SkBlendMode)mode); - float k[4]; for (int i = 0; i < 4; ++i) { k[i] = buffer.readScalar(); } const bool enforcePMColor = buffer.readBool(); - return SkXfermodeImageFilter::MakeArithmetic(k[0], k[1], k[2], k[3], enforcePMColor, - common.getInput(0), common.getInput(1), - &common.cropRect()); -} - -#include "SkNx.h" - -static Sk4f pin(float min, const Sk4f& val, float max) { - return Sk4f::Max(min, Sk4f::Min(val, max)); -} - -template void arith_span(const float k[], SkPMColor dst[], - const SkPMColor src[], int count) { - const Sk4f k1 = k[0] * (1/255.0f), - k2 = k[1], - k3 = k[2], - k4 = k[3] * 255.0f + 0.5f; - - for (int i = 0; i < count; i++) { - Sk4f s = SkNx_cast(Sk4b::Load(src+i)), - d = SkNx_cast(Sk4b::Load(dst+i)), - r = pin(0, k1*s*d + k2*s + k3*d + k4, 255); - if (EnforcePMColor) { - Sk4f a = SkNx_shuffle<3,3,3,3>(r); - r = Sk4f::Min(a, r); - } - SkNx_cast(r).store(dst+i); - } -} - -// apply mode to src==transparent (0) -template void arith_transparent(const float k[], SkPMColor dst[], int count) { - const Sk4f k3 = k[2], - k4 = k[3] * 255.0f + 0.5f; - - for (int i = 0; i < count; i++) { - Sk4f d = SkNx_cast(Sk4b::Load(dst+i)), - r = pin(0, k3*d + k4, 255); - if (EnforcePMColor) { - Sk4f a = SkNx_shuffle<3,3,3,3>(r); - r = Sk4f::Min(a, r); - } - SkNx_cast(r).store(dst+i); - } -} - -static bool intersect(SkPixmap* dst, SkPixmap* src, int srcDx, int srcDy) { - SkIRect dstR = SkIRect::MakeWH(dst->width(), dst->height()); - SkIRect srcR = SkIRect::MakeXYWH(srcDx, srcDy, src->width(), src->height()); - SkIRect sect; - if (!sect.intersect(dstR, srcR)) { - return false; - } - *dst = SkPixmap(dst->info().makeWH(sect.width(), sect.height()), - dst->addr(sect.fLeft, sect.fTop), - dst->rowBytes()); - *src = SkPixmap(src->info().makeWH(sect.width(), sect.height()), - src->addr(SkTMax(0, -srcDx), SkTMax(0, -srcDy)), - src->rowBytes()); - return true; -} - -void SkArithmeticImageFilter::drawForeground(SkCanvas* canvas, SkSpecialImage* img, - const SkIRect& fgBounds) const { - SkPixmap dst; - if (!canvas->peekPixels(&dst)) { - return; - } - - const SkMatrix& ctm = canvas->getTotalMatrix(); - SkASSERT(ctm.getType() <= SkMatrix::kTranslate_Mask); - const int dx = SkScalarRoundToInt(ctm.getTranslateX()); - const int dy = SkScalarRoundToInt(ctm.getTranslateY()); - - if (img) { - SkBitmap srcBM; - SkPixmap src; - if (!img->getROPixels(&srcBM)) { - return; - } - srcBM.lockPixels(); - if (!srcBM.peekPixels(&src)) { - return; - } - - auto proc = fEnforcePMColor ? arith_span : arith_span; - SkPixmap tmpDst = dst; - if (intersect(&tmpDst, &src, fgBounds.fLeft + dx, fgBounds.fTop + dy)) { - for (int y = 0; y < tmpDst.height(); ++y) { - proc(fK, tmpDst.writable_addr32(0, y), src.addr32(0, y), tmpDst.width()); - } - } - } - - // Now apply the mode with transparent-color to the outside of the fg image - SkRegion outside(SkIRect::MakeWH(dst.width(), dst.height())); - outside.op(fgBounds.makeOffset(dx, dy), SkRegion::kDifference_Op); - auto proc = fEnforcePMColor ? arith_transparent : arith_transparent; - for (SkRegion::Iterator iter(outside); !iter.done(); iter.next()) { - const SkIRect r = iter.rect(); - for (int y = r.fTop; y < r.fBottom; ++y) { - proc(fK, dst.writable_addr32(r.fLeft, y), r.width()); - } - } -} - -sk_sp SkXfermodeImageFilter::MakeArithmetic(float k1, float k2, float k3, float k4, - bool enforcePMColor, - sk_sp background, - sk_sp foreground, - const SkImageFilter::CropRect* crop) { - if (!SkScalarIsFinite(k1) || !SkScalarIsFinite(k2) || - !SkScalarIsFinite(k3) || !SkScalarIsFinite(k4)) { - return nullptr; - } - - // are we nearly some other "std" mode? - int mode = -1; // illegal mode - if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) && - SkScalarNearlyZero(k3) && SkScalarNearlyZero(k4)) { - mode = (int)SkBlendMode::kSrc; - } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && - SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) { - mode = (int)SkBlendMode::kDst; - } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && - SkScalarNearlyZero(k3) && SkScalarNearlyZero(k4)) { - mode = (int)SkBlendMode::kClear; - } - if (mode >= 0) { - return SkXfermodeImageFilter::Make((SkBlendMode)mode, - std::move(background), std::move(foreground), crop); - } - - sk_sp inputs[2] = { std::move(background), std::move(foreground) }; - return sk_sp(new SkArithmeticImageFilter(k1, k2, k3, k4, enforcePMColor, - inputs, crop)); + return SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], enforcePMColor, common.getInput(0), + common.getInput(1), &common.cropRect()); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -536,5 +363,9 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermodeImageFilter) // manually register the legacy serialized name "SkXfermodeImageFilter" SkFlattenable::Register("SkXfermodeImageFilter", SkXfermodeImageFilter_Base::CreateProc, SkFlattenable::kSkImageFilter_Type); - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArithmeticImageFilter) + // manually register the legacy serialized name "SkArithmeticImageFilter" from when that filter + // was implemented as a xfermode image filter. + SkFlattenable::Register("SkArithmeticImageFilter", + SkXfermodeImageFilter_Base::LegacyArithmeticCreateProc, + SkFlattenable::kSkImageFilter_Type); SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 0a4e8f6..abf318a 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -108,6 +108,7 @@ void SkFlattenable::PrivateInitializer::InitEffects() { // ImageFilter SkImageFilter::InitializeFlattenables(); + SkArithmeticImageFilter::InitializeFlattenables(); SkXfermodeImageFilter::InitializeFlattenables(); SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect) -- 2.7.4