Separate SkArithmeticImageFilter from SkXfermodeImageFilter.
authorBrian Salomon <bsalomon@google.com>
Mon, 9 Jan 2017 15:48:23 +0000 (10:48 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 9 Jan 2017 18:31:29 +0000 (18:31 +0000)
Change-Id: I145eed7276456b546ca5c66bc1a0f0f74a84f138
Reviewed-on: https://skia-review.googlesource.com/6728
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>

13 files changed:
gm/arithmode.cpp
gm/imagefiltersgraph.cpp
gm/xfermodeimagefilter.cpp
gn/effects.gni
include/effects/SkArithmeticImageFilter.h [new file with mode: 0644]
include/effects/SkXfermodeImageFilter.h
src/effects/SkArithmeticImageFilter.cpp [new file with mode: 0644]
src/effects/SkArithmeticMode.cpp
src/effects/SkArithmeticModePriv.h
src/effects/SkArithmeticMode_gpu.cpp [deleted file]
src/effects/SkArithmeticMode_gpu.h [deleted file]
src/effects/SkXfermodeImageFilter.cpp
src/ports/SkGlobalInitialization_default.cpp

index caa3e28..aec079c 100644 (file)
@@ -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<SkImageFilter> 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);
index fd4d5cf..30c987a 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "gm.h"
 
+#include "SkArithmeticImageFilter.h"
 #include "SkBlurImageFilter.h"
 #include "SkColorFilter.h"
 #include "SkColorFilterImageFilter.h"
 #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);
index 9190859..77c3502 100644 (file)
@@ -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) {
index 10a2576..30e36b2 100644 (file)
@@ -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 (file)
index 0000000..ef59603
--- /dev/null
@@ -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<SkImageFilter> Make(float k1, float k2, float k3, float k4, bool enforcePMColor,
+                                     sk_sp<SkImageFilter> background,
+                                     sk_sp<SkImageFilter> foreground,
+                                     const SkImageFilter::CropRect* cropRect);
+    static sk_sp<SkImageFilter> Make(float k1, float k2, float k3, float k4, bool enforcePMColor,
+                                     sk_sp<SkImageFilter> background) {
+        return Make(k1, k2, k3, k4, enforcePMColor, std::move(background), nullptr, nullptr);
+    }
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP();
+
+private:
+    SkArithmeticImageFilter();  // can't instantiate
+};
+
+#endif
index 5fbd1af..5e8b587 100644 (file)
@@ -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<SkImageFilter> MakeArithmetic(float k1, float k2, float k3, float k4,
-                                               bool enforcePMColor,
-                                               sk_sp<SkImageFilter> background,
+                                               bool enforcePMColor, sk_sp<SkImageFilter> background,
                                                sk_sp<SkImageFilter> 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<SkImageFilter> MakeArithmetic(float k1, float k2, float k3, float k4,
                                                bool enforcePMColor,
                                                sk_sp<SkImageFilter> 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 (file)
index 0000000..984aedc
--- /dev/null
@@ -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<SkImageFilter> 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<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
+                                        SkIPoint* offset) const override;
+
+#if SK_SUPPORT_GPU
+    sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
+                                         sk_sp<SkSpecialImage> background,
+                                         const SkIPoint& backgroundOffset,
+                                         sk_sp<SkSpecialImage> 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<SkFlattenable> 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 <bool EnforcePMColor>
+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<float>(Sk4b::Load(src+i)),
+             d = SkNx_cast<float>(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<uint8_t>(r).store(dst+i);
+    }
+}
+
+// apply mode to src==transparent (0)
+template<bool EnforcePMColor> 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<float>(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<uint8_t>(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<SkSpecialImage> ArithmeticImageFilterImpl::onFilterImage(SkSpecialImage* source,
+                                                               const Context& ctx,
+                                                               SkIPoint* offset) const {
+    SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
+    sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset));
+
+    SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
+    sk_sp<SkSpecialImage> 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<SkSpecialSurface> 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<GrFragmentProcessor> Make(float k1, float k2, float k3, float k4,
+                                           bool enforcePMColor, sk_sp<GrFragmentProcessor> dst) {
+        return sk_sp<GrFragmentProcessor>(
+                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<ArithmeticFP>();
+
+                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<ArithmeticFP>();
+                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<ArithmeticFP>();
+        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<GrFragmentProcessor> dst)
+            : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) {
+        this->initClassID<ArithmeticFP>();
+        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<GrFragmentProcessor> 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<GrFragmentProcessor> dst(GrProcessorUnitTest::MakeChildFP(d));
+    return ArithmeticFP::Make(k1, k2, k3, k4, enforcePMColor, std::move(dst));
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ArithmeticFP);
+
+sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::filterImageGPU(
+        SkSpecialImage* source,
+        sk_sp<SkSpecialImage>
+                background,
+        const SkIPoint& backgroundOffset,
+        sk_sp<SkSpecialImage>
+                foreground,
+        const SkIPoint& foregroundOffset,
+        const SkIRect& bounds,
+        const OutputProperties& outputProperties) const {
+    SkASSERT(source->isTextureBacked());
+
+    GrContext* context = source->getContext();
+
+    sk_sp<GrTexture> backgroundTex, foregroundTex;
+
+    if (background) {
+        backgroundTex = background->asTextureRef(context);
+    }
+
+    if (foreground) {
+        foregroundTex = foreground->asTextureRef(context);
+    }
+
+    GrPaint paint;
+    sk_sp<GrFragmentProcessor> bgFP;
+
+    if (backgroundTex) {
+        SkMatrix backgroundMatrix;
+        backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
+        backgroundMatrix.preTranslate(-SkIntToScalar(backgroundOffset.fX),
+                                      -SkIntToScalar(backgroundOffset.fY));
+        sk_sp<GrColorSpaceXform> 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<GrColorSpaceXform> fgXform =
+                GrColorSpaceXform::Make(foreground->getColorSpace(), outputProperties.colorSpace());
+        sk_sp<GrFragmentProcessor> 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<GrFragmentProcessor> 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<GrRenderTargetContext> 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<true> : arith_span<false>;
+        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<true> : arith_transparent<false>;
+    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<SkImageFilter> SkArithmeticImageFilter::Make(float k1, float k2, float k3, float k4,
+                                                   bool enforcePMColor,
+                                                   sk_sp<SkImageFilter> background,
+                                                   sk_sp<SkImageFilter> 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<SkImageFilter> inputs[2] = {std::move(background), std::move(foreground)};
+    return sk_sp<SkImageFilter>(
+            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
index c85d5c3..d604a12 100644 (file)
@@ -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<GrFragmentProcessor> makeFragmentProcessorForImageFilter(
-                                                sk_sp<GrFragmentProcessor> 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<SkFlattenable> 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<float>(Sk4b::Load(src+i)),
-             d = SkNx_cast<float>(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<uint8_t>(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<SkXfermode> SkArithmeticMode::Make(SkScalar k1, SkScalar k2, SkScalar k3,
     return sk_make_sp<SkArithmeticMode_scalar>(k1, k2, k3, k4, enforcePMColor);
 }
 
-
-//////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-sk_sp<GrFragmentProcessor> SkArithmeticMode_scalar::makeFragmentProcessorForImageFilter(
-                                                            sk_sp<GrFragmentProcessor> 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
index ff7f357..4c13a81 100644 (file)
@@ -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 (file)
index a6c7866..0000000
+++ /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<GrArithmeticFP>();
-
-        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<GrArithmeticFP>();
-        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<GrArithmeticFP>();
-        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<GrFragmentProcessor> dst)
-  : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) {
-    this->initClassID<GrArithmeticFP>();
-
-    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<GrArithmeticFP>();
-    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<GrFragmentProcessor> 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<GrFragmentProcessor> 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<ArithmeticXP>();
-        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 (file)
index f7ded9b..0000000
+++ /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<GrFragmentProcessor> Make(float k1, float k2, float k3, float k4,
-                                           bool enforcePMColor, sk_sp<GrFragmentProcessor> dst) {
-        return sk_sp<GrFragmentProcessor>(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<GrFragmentProcessor> dst);
-
-    float                       fK1, fK2, fK3, fK4;
-    bool                        fEnforcePMColor;
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-    typedef GrFragmentProcessor INHERITED;
-};
-
-#endif
-#endif
index d90cb91..2408619 100644 (file)
@@ -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<GrFragmentProcessor> makeFGFrag(sk_sp<GrFragmentProcessor> bgFP) const;
+    sk_sp<GrFragmentProcessor> makeFGFrag(sk_sp<GrFragmentProcessor> bgFP) const;
 #endif
 
 private:
+    static sk_sp<SkFlattenable> LegacyArithmeticCreateProc(SkReadBuffer& buffer);
+
     SkBlendMode fMode;
 
     friend class SkXfermodeImageFilter;
@@ -108,10 +109,9 @@ sk_sp<SkFlattenable> 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<GrFragmentProcessor> bgFP) const {
 }
 
 #endif
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-class SkArithmeticImageFilter : public SkXfermodeImageFilter_Base {
-public:
-    SkArithmeticImageFilter(float k1, float k2, float k3, float k4, bool enforcePMColor,
-                            sk_sp<SkImageFilter> 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<GrFragmentProcessor> makeFGFrag(sk_sp<GrFragmentProcessor> 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<SkFlattenable> SkArithmeticImageFilter::CreateProc(SkReadBuffer& buffer) {
+sk_sp<SkFlattenable> 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<bool EnforcePMColor> 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<float>(Sk4b::Load(src+i)),
-        d = SkNx_cast<float>(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<uint8_t>(r).store(dst+i);
-    }
-}
-
-// apply mode to src==transparent (0)
-template<bool EnforcePMColor> 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<float>(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<uint8_t>(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<true> : arith_span<false>;
-        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<true> : arith_transparent<false>;
-    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<SkImageFilter> SkXfermodeImageFilter::MakeArithmetic(float k1, float k2, float k3, float k4,
-                                                           bool enforcePMColor,
-                                                           sk_sp<SkImageFilter> background,
-                                                           sk_sp<SkImageFilter> 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<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
-    return sk_sp<SkImageFilter>(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
index 0a4e8f6..abf318a 100644 (file)
@@ -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)