Implement SkXfermode image filter. This required changing the signature of SkXfermode...
authorsenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 31 May 2013 17:49:12 +0000 (17:49 +0000)
committersenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 31 May 2013 17:49:12 +0000 (17:49 +0000)
For the raster path, we do a straightforward 2-pass method: draw background, then composite the foreground over it.

For the GPU path, if the xfermode can be expressed as an effect, we build an effect with the background texture incorporated, then do a single-pass draw fetching both foreground and background textures, and compositing to the result. If the xfermode is expressed as src/dst coefficients, we do a 2-pass draw as in the raster path and use fixed-function blending.

R=bsalomon@google.com, reed@google.com

Review URL: https://codereview.chromium.org/16125008

git-svn-id: http://skia.googlecode.com/svn/trunk@9373 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/xfermodeimagefilter.cpp [new file with mode: 0644]
gyp/effects.gypi
gyp/gmslides.gypi
include/core/SkXfermode.h
include/effects/SkXfermodeImageFilter.h [new file with mode: 0644]
src/core/SkXfermode.cpp
src/effects/SkArithmeticMode.cpp
src/effects/SkXfermodeImageFilter.cpp [new file with mode: 0644]
src/ports/SkGlobalInitialization_default.cpp

diff --git a/gm/xfermodeimagefilter.cpp b/gm/xfermodeimagefilter.cpp
new file mode 100644 (file)
index 0000000..a6d395e
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkArithmeticMode.h"
+#include "SkXfermodeImageFilter.h"
+#include "SkBitmapSource.h"
+
+#define WIDTH 600
+#define HEIGHT 600
+#define MARGIN 12
+
+namespace skiagm {
+
+class XfermodeImageFilterGM : public GM {
+public:
+    XfermodeImageFilterGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("xfermodeimagefilter");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xD000D000);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(65), paint);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fCheckerboard.allocPixels();
+        SkDevice device(fCheckerboard);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint darkPaint;
+        darkPaint.setColor(0xFF404040);
+        SkPaint lightPaint;
+        lightPaint.setColor(0xFFA0A0A0);
+        for (int y = 0; y < 80; y += 16) {
+          for (int x = 0; x < 80; x += 16) {
+            canvas.save();
+            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint);
+            canvas.restore();
+          }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    void drawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint, SkScalar x, SkScalar y) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(x, y,
+            SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())));
+        canvas->drawBitmap(bitmap, x, y, &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            make_checkerboard();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+
+        const struct {
+            SkXfermode::Mode  fMode;
+            const char*         fLabel;
+        } gModes[] = {
+            { SkXfermode::kClear_Mode,    "Clear"     },
+            { SkXfermode::kSrc_Mode,      "Src"       },
+            { SkXfermode::kDst_Mode,      "Dst"       },
+            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
+            { SkXfermode::kDstOver_Mode,  "DstOver"   },
+            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
+            { SkXfermode::kDstIn_Mode,    "DstIn"     },
+            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
+            { SkXfermode::kDstOut_Mode,   "DstOut"    },
+            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
+            { SkXfermode::kDstATop_Mode,  "DstATop"   },
+            { SkXfermode::kXor_Mode,      "Xor"       },
+
+            { SkXfermode::kPlus_Mode,         "Plus"          },
+            { SkXfermode::kModulate_Mode,     "Modulate"      },
+            { SkXfermode::kScreen_Mode,       "Screen"        },
+            { SkXfermode::kOverlay_Mode,      "Overlay"       },
+            { SkXfermode::kDarken_Mode,       "Darken"        },
+            { SkXfermode::kLighten_Mode,      "Lighten"       },
+            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
+            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
+            { SkXfermode::kHardLight_Mode,    "HardLight"     },
+            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
+            { SkXfermode::kDifference_Mode,   "Difference"    },
+            { SkXfermode::kExclusion_Mode,    "Exclusion"     },
+            { SkXfermode::kMultiply_Mode,     "Multiply"      },
+            { SkXfermode::kHue_Mode,          "Hue"           },
+            { SkXfermode::kSaturation_Mode,   "Saturation"    },
+            { SkXfermode::kColor_Mode,        "Color"         },
+            { SkXfermode::kLuminosity_Mode,   "Luminosity"    },
+        };
+
+        int x = 0, y = 0;
+        SkAutoTUnref<SkImageFilter> background(SkNEW_ARGS(SkBitmapSource, (fCheckerboard)));
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+            SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(gModes[i].fMode));
+            SkAutoTUnref<SkImageFilter> filter(SkNEW_ARGS(SkXfermodeImageFilter, (mode, background)));
+            paint.setImageFilter(filter);
+            drawClippedBitmap(canvas, fBitmap, paint, SkIntToScalar(x), SkIntToScalar(y));
+            x += fBitmap.width() + MARGIN;
+            if (x + fBitmap.width() > WIDTH) {
+                x = 0;
+                y += fBitmap.height() + MARGIN;
+            }
+        }
+        // Test arithmetic mode as image filter
+        SkAutoTUnref<SkXfermode> mode(SkArithmeticMode::Create(0, SK_Scalar1, SK_Scalar1, 0));
+        SkAutoTUnref<SkImageFilter> filter(SkNEW_ARGS(SkXfermodeImageFilter, (mode, background)));
+        paint.setImageFilter(filter);
+        drawClippedBitmap(canvas, fBitmap, paint, SkIntToScalar(x), SkIntToScalar(y));
+        x += fBitmap.width() + MARGIN;
+        if (x + fBitmap.width() > WIDTH) {
+            x = 0;
+            y += fBitmap.height() + MARGIN;
+        }
+        // Test NULL mode
+        filter.reset(SkNEW_ARGS(SkXfermodeImageFilter, (NULL, background)));
+        paint.setImageFilter(filter);
+        drawClippedBitmap(canvas, fBitmap, paint, SkIntToScalar(x), SkIntToScalar(y));
+    }
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new XfermodeImageFilterGM; }
+static GMRegistry reg(MyFactory);
+
+}
index 1730ffc..36638eb 100644 (file)
@@ -9,8 +9,8 @@
   'sources': [
     '<(skia_src_path)/effects/Sk1DPathEffect.cpp',
     '<(skia_src_path)/effects/Sk2DPathEffect.cpp',
-    '<(skia_src_path)/effects/SkAvoidXfermode.cpp',
     '<(skia_src_path)/effects/SkArithmeticMode.cpp',
+    '<(skia_src_path)/effects/SkAvoidXfermode.cpp',
     '<(skia_src_path)/effects/SkBicubicImageFilter.cpp',
     '<(skia_src_path)/effects/SkBitmapSource.cpp',
     '<(skia_src_path)/effects/SkBlendImageFilter.cpp',
@@ -36,6 +36,7 @@
     '<(skia_src_path)/effects/SkLayerRasterizer.cpp',
     '<(skia_src_path)/effects/SkLerpXfermode.cpp',
     '<(skia_src_path)/effects/SkLightingImageFilter.cpp',
+    '<(skia_src_path)/effects/SkMagnifierImageFilter.cpp',
     '<(skia_src_path)/effects/SkMatrixConvolutionImageFilter.cpp',
     '<(skia_src_path)/effects/SkMergeImageFilter.cpp',
     '<(skia_src_path)/effects/SkMorphologyImageFilter.cpp',
@@ -50,7 +51,7 @@
     '<(skia_src_path)/effects/SkTableMaskFilter.cpp',
     '<(skia_src_path)/effects/SkTestImageFilters.cpp',
     '<(skia_src_path)/effects/SkTransparentShader.cpp',
-    '<(skia_src_path)/effects/SkMagnifierImageFilter.cpp',
+    '<(skia_src_path)/effects/SkXfermodeImageFilter.cpp',
 
     '<(skia_src_path)/effects/gradients/SkBitmapCache.cpp',
     '<(skia_src_path)/effects/gradients/SkBitmapCache.h',
@@ -72,8 +73,9 @@
 
     '<(skia_include_path)/effects/Sk1DPathEffect.h',
     '<(skia_include_path)/effects/Sk2DPathEffect.h',
-    '<(skia_include_path)/effects/SkAvoidXfermode.h',
+    '<(skia_include_path)/effects/SkXfermodeImageFilter.h',
     '<(skia_include_path)/effects/SkArithmeticMode.h',
+    '<(skia_include_path)/effects/SkAvoidXfermode.h',
     '<(skia_include_path)/effects/SkBitmapSource.h',
     '<(skia_include_path)/effects/SkBlendImageFilter.h',
     '<(skia_include_path)/effects/SkBlurDrawLooper.h',
index ca833a5..e56d62b 100644 (file)
     '../gm/verttext.cpp',
     '../gm/verttext2.cpp',
     '../gm/verylargebitmap.cpp',
+    '../gm/xfermodeimagefilter.cpp',
     '../gm/xfermodes.cpp',
     '../gm/xfermodes2.cpp',
   ],
index f960e14..03ca101 100644 (file)
@@ -15,6 +15,7 @@
 
 class GrContext;
 class GrEffectRef;
+class GrTexture;
 class SkString;
 
 /** \class SkXfermode
@@ -197,12 +198,16 @@ public:
         it and own a ref to it. Since the xfermode may or may not assign *effect, the caller should
         set *effect to NULL beforehand. If the function returns true and *effect is NULL then the
         src and dst coeffs will be applied to the draw. When *effect is non-NULL the coeffs are
-        ignored.
+        ignored. background specifies the texture to use as the background for compositing, and
+        should be accessed in the effect's fragment shader. If NULL, the effect should request
+        access to destination color (setWillReadDstColor()), and use that in the fragment shader
+        (builder->dstColor()).
      */
     virtual bool asNewEffectOrCoeff(GrContext*,
                                     GrEffectRef** effect,
                                     Coeff* src,
-                                    Coeff* dst) const;
+                                    Coeff* dst,
+                                    GrTexture* background = NULL) const;
 
     /**
      *  The same as calling xfermode->asNewEffect(...), except that this also checks if the xfermode
@@ -212,7 +217,8 @@ public:
                                    GrContext*,
                                    GrEffectRef** effect,
                                    Coeff* src,
-                                   Coeff* dst);
+                                   Coeff* dst,
+                                   GrTexture* background = NULL);
 
     SkDEVCODE(virtual void toString(SkString* str) const = 0;)
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
diff --git a/include/effects/SkXfermodeImageFilter.h b/include/effects/SkXfermodeImageFilter.h
new file mode 100644 (file)
index 0000000..ec7ce87
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkXfermodeImageFilter_DEFINED
+#define SkXfermodeImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+
+class SkBitmap;
+class SkXfermode;
+
+class SK_API SkXfermodeImageFilter : public SkImageFilter {
+    /**
+     * 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.
+      */
+
+public:
+    SkXfermodeImageFilter(SkXfermode* mode, SkImageFilter* background, SkImageFilter* foreground = NULL);
+
+    virtual ~SkXfermodeImageFilter();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkXfermodeImageFilter)
+
+    virtual bool onFilterImage(Proxy* proxy,
+                               const SkBitmap& src,
+                               const SkMatrix& ctm,
+                               SkBitmap* dst,
+                               SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
+
+protected:
+    explicit SkXfermodeImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    SkXfermode* fMode;
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
index 7896061..2da500a 100644 (file)
@@ -680,7 +680,7 @@ bool SkXfermode::asMode(Mode* mode) const {
     return false;
 }
 
-bool SkXfermode::asNewEffectOrCoeff(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst) const {
+bool SkXfermode::asNewEffectOrCoeff(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst, GrTexture*) const {
     return this->asCoeff(src, dst);
 }
 
@@ -688,11 +688,12 @@ bool SkXfermode::AsNewEffectOrCoeff(SkXfermode* xfermode,
                                     GrContext* context,
                                     GrEffectRef** effect,
                                     Coeff* src,
-                                    Coeff* dst) {
+                                    Coeff* dst,
+                                    GrTexture* background) {
     if (NULL == xfermode) {
         return ModeAsCoeff(kSrcOver_Mode, src, dst);
     } else {
-        return xfermode->asNewEffectOrCoeff(context, effect, src, dst);
+        return xfermode->asNewEffectOrCoeff(context, effect, src, dst, background);
     }
 }
 
@@ -948,6 +949,7 @@ void SkProcXfermode::toString(SkString* str) const {
 #include "GrEffectUnitTest.h"
 #include "GrTBackendEffectFactory.h"
 #include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
 
 /**
  * GrEffect that implements the all the separable xfer modes that cannot be expressed as Coeffs.
@@ -958,11 +960,11 @@ public:
         return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
     }
 
-    static GrEffectRef* Create(SkXfermode::Mode mode) {
+    static GrEffectRef* Create(SkXfermode::Mode mode, GrTexture* background) {
         if (!IsSupportedMode(mode)) {
             return NULL;
         } else {
-            AutoEffectUnref effect(SkNEW_ARGS(XferEffect, (mode)));
+            AutoEffectUnref effect(SkNEW_ARGS(XferEffect, (mode, background)));
             return CreateEffectRef(effect);
         }
     }
@@ -979,11 +981,13 @@ public:
     static const char* Name() { return "XferEffect"; }
 
     SkXfermode::Mode mode() const { return fMode; }
+    const GrTextureAccess&  backgroundAccess() const { return fBackgroundAccess; }
 
     class GLEffect : public GrGLEffect {
     public:
         GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
-            : GrGLEffect(factory ) {
+            : GrGLEffect(factory )
+            , fBackgroundEffectMatrix(kCoordsType) {
         }
         virtual void emitCode(GrGLShaderBuilder* builder,
                               const GrDrawEffect& drawEffect,
@@ -991,7 +995,22 @@ public:
                               const char* outputColor,
                               const char* inputColor,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
-            const char* dstColor = builder->dstColor();
+            SkXfermode::Mode mode = drawEffect.castEffect<XferEffect>().mode();
+            const GrTexture* backgroundTex = drawEffect.castEffect<XferEffect>().backgroundAccess().getTexture();
+            const char* dstColor;
+            if (backgroundTex) {
+                const char* bgCoords;
+                GrSLType bgCoordsType = fBackgroundEffectMatrix.emitCode(builder, key, &bgCoords, NULL, "BG");
+                dstColor = "bgColor";
+                builder->fsCodeAppendf("\t\tvec4 %s = ", dstColor);
+                builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
+                                             samplers[0],
+                                             bgCoords,
+                                             bgCoordsType);
+                builder->fsCodeAppendf(";\n");
+            } else {
+                dstColor = builder->dstColor();
+            }
             GrAssert(NULL != dstColor);
 
             // We don't try to optimize for this case at all
@@ -999,8 +1018,6 @@ public:
                 builder->fsCodeAppendf("\t\tconst vec4 ones = %s;\n", GrGLSLOnesVecf(4));
                 inputColor = "ones";
             }
-
-            SkXfermode::Mode mode = drawEffect.castEffect<XferEffect>().mode();
             builder->fsCodeAppendf("\t\t// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode));
 
             // These all perform src-over on the alpha channel.
@@ -1125,10 +1142,29 @@ public:
         }
 
         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
-            return drawEffect.castEffect<XferEffect>().mode();
+            const XferEffect& xfer = drawEffect.castEffect<XferEffect>();
+            GrTexture* bgTex = xfer.backgroundAccess().getTexture();
+            EffectKey bgKey = 0;
+            if (bgTex) {
+                bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                                 drawEffect,
+                                                 GLEffect::kCoordsType,
+                                                 bgTex);
+            }
+            EffectKey modeKey = xfer.mode() << GrGLEffectMatrix::kKeyBits;
+            return modeKey | bgKey;
         }
 
-        virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
+        virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) SK_OVERRIDE {
+            const XferEffect& xfer = drawEffect.castEffect<XferEffect>();
+            GrTexture* bgTex = xfer.backgroundAccess().getTexture();
+            if (bgTex) {
+                fBackgroundEffectMatrix.setData(uman,
+                                                GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                                drawEffect,
+                                                bgTex);
+            }
+        }
 
     private:
         static void HardLight(GrGLShaderBuilder* builder,
@@ -1352,16 +1388,31 @@ public:
 
         }
 
+        static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType;
+        GrGLEffectMatrix   fBackgroundEffectMatrix;
         typedef GrGLEffect INHERITED;
     };
 
     GR_DECLARE_EFFECT_TEST;
 
 private:
-    XferEffect(SkXfermode::Mode mode) : fMode(mode) { this->setWillReadDstColor(); }
-    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { return true; }
+    XferEffect(SkXfermode::Mode mode, GrTexture* background)
+        : fMode(mode) {
+        if (background) {
+            fBackgroundAccess.reset(background);
+            this->addTextureAccess(&fBackgroundAccess);
+        } else {
+            this->setWillReadDstColor();
+        }
+    }
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        const XferEffect& s = CastEffect<XferEffect>(other);
+        return fMode == s.fMode &&
+               fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture();
+    }
 
     SkXfermode::Mode fMode;
+    GrTextureAccess  fBackgroundAccess;
 
     typedef GrEffect INHERITED;
 };
@@ -1373,7 +1424,7 @@ GrEffectRef* XferEffect::TestCreate(SkMWCRandom* rand,
                                     GrTexture*[]) {
     int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode);
 
-    static AutoEffectUnref gEffect(SkNEW_ARGS(XferEffect, (static_cast<SkXfermode::Mode>(mode))));
+    static AutoEffectUnref gEffect(SkNEW_ARGS(XferEffect, (static_cast<SkXfermode::Mode>(mode), NULL)));
     return CreateEffectRef(gEffect);
 }
 
@@ -1417,13 +1468,14 @@ public:
     virtual bool asNewEffectOrCoeff(GrContext*,
                                     GrEffectRef** effect,
                                     Coeff* src,
-                                    Coeff* dst) const SK_OVERRIDE {
+                                    Coeff* dst,
+                                    GrTexture* background) const SK_OVERRIDE {
         if (this->asCoeff(src, dst)) {
             return true;
         }
         if (XferEffect::IsSupportedMode(fMode)) {
             if (NULL != effect) {
-                *effect = XferEffect::Create(fMode);
+                *effect = XferEffect::Create(fMode, background);
                 SkASSERT(NULL != *effect);
             }
             return true;
index 1d06264..e9dc782 100644 (file)
@@ -13,6 +13,7 @@
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
 #include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
 #include "GrTBackendEffectFactory.h"
 #include "SkImageFilterUtils.h"
 #endif
@@ -33,7 +34,7 @@ public:
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticMode_scalar)
 
 #if SK_SUPPORT_GPU
-    virtual bool asNewEffectOrCoeff(GrContext*, GrEffectRef** effect, Coeff*, Coeff*) const SK_OVERRIDE;
+    virtual bool asNewEffectOrCoeff(GrContext*, GrEffectRef** effect, Coeff*, Coeff*, GrTexture* background) const SK_OVERRIDE;
 #endif
 
 private:
@@ -240,7 +241,7 @@ public:
 
 private:
     static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType;
-
+    GrGLEffectMatrix fBackgroundEffectMatrix;
     GrGLUniformManager::UniformHandle fKUni;
 
     typedef GrGLEffect INHERITED;
@@ -250,8 +251,8 @@ private:
 
 class GrArithmeticEffect : public GrEffect {
 public:
-    static GrEffectRef* Create(float k1, float k2, float k3, float k4) {
-        AutoEffectUnref effect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4)));
+    static GrEffectRef* Create(float k1, float k2, float k3, float k4, GrTexture* background) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, background)));
         return CreateEffectRef(effect);
     }
 
@@ -261,6 +262,7 @@ public:
 
     typedef GrGLArithmeticEffect GLEffect;
     static const char* Name() { return "Arithmetic"; }
+    GrTexture* backgroundTexture() const { return fBackgroundAccess.getTexture(); }
 
     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
@@ -272,8 +274,9 @@ public:
 private:
     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
 
-    GrArithmeticEffect(float k1, float k2, float k3, float k4);
+    GrArithmeticEffect(float k1, float k2, float k3, float k4, GrTexture* background);
     float                       fK1, fK2, fK3, fK4;
+    GrTextureAccess             fBackgroundAccess;
 
     GR_DECLARE_EFFECT_TEST;
     typedef GrEffect INHERITED;
@@ -282,9 +285,15 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrArithmeticEffect::GrArithmeticEffect(float k1, float k2, float k3, float k4)
+GrArithmeticEffect::GrArithmeticEffect(float k1, float k2, float k3, float k4,
+                                       GrTexture* background)
   : fK1(k1), fK2(k2), fK3(k3), fK4(k4) {
-    this->setWillReadDstColor();
+    if (background) {
+        fBackgroundAccess.reset(background);
+        this->addTextureAccess(&fBackgroundAccess);
+    } else {
+        this->setWillReadDstColor();
+    }
 }
 
 GrArithmeticEffect::~GrArithmeticEffect() {
@@ -295,7 +304,8 @@ bool GrArithmeticEffect::onIsEqual(const GrEffect& sBase) const {
     return fK1 == s.fK1 &&
            fK2 == s.fK2 &&
            fK3 == s.fK3 &&
-           fK4 == s.fK4;
+           fK4 == s.fK4 &&
+           backgroundTexture() == s.backgroundTexture();
 }
 
 const GrBackendEffectFactory& GrArithmeticEffect::getFactory() const {
@@ -310,19 +320,37 @@ void GrArithmeticEffect::getConstantColorComponents(GrColor* color, uint32_t* va
 ///////////////////////////////////////////////////////////////////////////////
 
 GrGLArithmeticEffect::GrGLArithmeticEffect(const GrBackendEffectFactory& factory,
-                                           const GrDrawEffect& drawEffect) : INHERITED(factory) {
+                                           const GrDrawEffect& drawEffect)
+   : INHERITED(factory)
+   , fBackgroundEffectMatrix(kCoordsType) {
 }
 
 GrGLArithmeticEffect::~GrGLArithmeticEffect() {
 }
 
 void GrGLArithmeticEffect::emitCode(GrGLShaderBuilder* builder,
-                                    const GrDrawEffect&,
+                                    const GrDrawEffect& drawEffect,
                                     EffectKey key,
                                     const char* outputColor,
                                     const char* inputColor,
                                     const TextureSamplerArray& samplers) {
-    const char* dstColor = builder->dstColor();
+
+    GrTexture* backgroundTex = drawEffect.castEffect<GrArithmeticEffect>().backgroundTexture();
+    const char* dstColor;
+    if (backgroundTex) {
+        const char* bgCoords;
+        GrSLType bgCoordsType = fBackgroundEffectMatrix.emitCode(builder, key, &bgCoords, NULL, "BG");
+        builder->fsCodeAppend("\t\tvec4 bgColor = ");
+        builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
+                                     samplers[0],
+                                     bgCoords,
+                                     bgCoordsType);
+        builder->fsCodeAppendf(";\n");
+        dstColor = "bgColor";
+    } else {
+        dstColor = builder->dstColor();
+    }
+
     GrAssert(NULL != dstColor);
     fKUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                 kVec4f_GrSLType, "k");
@@ -347,10 +375,26 @@ void GrGLArithmeticEffect::emitCode(GrGLShaderBuilder* builder,
 void GrGLArithmeticEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
     const GrArithmeticEffect& arith = drawEffect.castEffect<GrArithmeticEffect>();
     uman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
+    GrTexture* bgTex = arith.backgroundTexture();
+    if (bgTex) {
+        fBackgroundEffectMatrix.setData(uman,
+                                        GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                        drawEffect,
+                                        bgTex);
+    }
 }
 
 GrGLEffect::EffectKey GrGLArithmeticEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
-    return 0;
+    const GrArithmeticEffect& effect = drawEffect.castEffect<GrArithmeticEffect>();
+    GrTexture* bgTex = effect.backgroundTexture();
+    EffectKey bgKey = 0;
+    if (bgTex) {
+        bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                         drawEffect,
+                                         GrGLArithmeticEffect::kCoordsType,
+                                         bgTex);
+    }
+    return bgKey;
 }
 
 GrEffectRef* GrArithmeticEffect::TestCreate(SkMWCRandom* rand,
@@ -362,7 +406,7 @@ GrEffectRef* GrArithmeticEffect::TestCreate(SkMWCRandom* rand,
     float k3 = rand->nextF();
     float k4 = rand->nextF();
 
-    static AutoEffectUnref gEffect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4)));
+    static AutoEffectUnref gEffect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, NULL)));
     return CreateEffectRef(gEffect);
 }
 
@@ -371,12 +415,14 @@ GR_DEFINE_EFFECT_TEST(GrArithmeticEffect);
 bool SkArithmeticMode_scalar::asNewEffectOrCoeff(GrContext*,
                                                  GrEffectRef** effect,
                                                  Coeff*,
-                                                 Coeff*) const {
+                                                 Coeff*,
+                                                 GrTexture* background) const {
     if (effect) {
         *effect = GrArithmeticEffect::Create(SkScalarToFloat(fK[0]),
                                              SkScalarToFloat(fK[1]),
                                              SkScalarToFloat(fK[2]),
-                                             SkScalarToFloat(fK[3]));
+                                             SkScalarToFloat(fK[3]),
+                                             background);
     }
     return true;
 }
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
new file mode 100644 (file)
index 0000000..5f2d368
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkXfermodeImageFilter.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkXfermode.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "SkGr.h"
+#include "SkImageFilterUtils.h"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode, SkImageFilter* background, SkImageFilter* foreground)
+  : INHERITED(background, foreground), fMode(mode) {
+    SkSafeRef(fMode);
+}
+
+SkXfermodeImageFilter::~SkXfermodeImageFilter() {
+    SkSafeUnref(fMode);
+}
+
+SkXfermodeImageFilter::SkXfermodeImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    fMode = buffer.readFlattenableT<SkXfermode>();
+}
+
+void SkXfermodeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fMode);
+}
+
+bool SkXfermodeImageFilter::onFilterImage(Proxy* proxy,
+                                            const SkBitmap& src,
+                                            const SkMatrix& ctm,
+                                            SkBitmap* dst,
+                                            SkIPoint* offset) {
+    SkBitmap background = src, foreground = src;
+    SkImageFilter* backgroundInput = getInput(0);
+    SkImageFilter* foregroundInput = getInput(1);
+    if (backgroundInput && !backgroundInput->filterImage(proxy, src, ctm, &background, offset)) {
+        return false;
+    }
+    if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) {
+        return false;
+    }
+    dst->setConfig(background.config(), background.width(), background.height());
+    dst->allocPixels();
+    SkCanvas canvas(*dst);
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    canvas.drawBitmap(background, 0, 0, &paint);
+    paint.setXfermode(fMode);
+    canvas.drawBitmap(foreground, 0, 0, &paint);
+    return true;
+}
+
+#if SK_SUPPORT_GPU
+
+bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap background;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &background)) {
+        return false;
+    }
+    GrTexture* backgroundTex = (GrTexture*) background.getTexture();
+    SkBitmap foreground;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(1), proxy, src, &foreground)) {
+        return false;
+    }
+    GrTexture* foregroundTex = (GrTexture*) foreground.getTexture();
+    GrContext* context = foregroundTex->getContext();
+
+    GrEffectRef* xferEffect = NULL;
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = src.width();
+    desc.fHeight = src.height();
+    desc.fConfig = kSkia8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    SkAutoTUnref<GrTexture> dst(ast.detach());
+
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+
+    SkXfermode::Coeff sm, dm;
+    if (!SkXfermode::AsNewEffectOrCoeff(fMode, context, &xferEffect, &sm, &dm, backgroundTex)) {
+        return false;
+    }
+
+    GrPaint paint;
+    SkRect srcRect;
+    src.getBounds(&srcRect);
+    if (NULL != xferEffect) {
+        paint.colorStage(0)->setEffect(
+            GrSimpleTextureEffect::Create(foregroundTex, GrEffect::MakeDivByTextureWHMatrix(foregroundTex)))->unref();
+        paint.colorStage(1)->setEffect(xferEffect);
+        context->drawRect(paint, srcRect);
+    } else {
+        paint.colorStage(0)->setEffect(
+            GrSimpleTextureEffect::Create(backgroundTex, GrEffect::MakeDivByTextureWHMatrix(backgroundTex)))->unref();
+        context->drawRect(paint, srcRect);
+        paint.setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
+        paint.colorStage(0)->setEffect(
+            GrSimpleTextureEffect::Create(foregroundTex, GrEffect::MakeDivByTextureWHMatrix(foregroundTex)))->unref();
+        context->drawRect(paint, srcRect);
+    }
+    return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
+}
+
+#endif
index f6ed222..91c849c 100644 (file)
@@ -55,6 +55,7 @@
 #include "SkStippleMaskFilter.h"
 #include "SkTableColorFilter.h"
 #include "SkTestImageFilters.h"
+#include "SkXfermodeImageFilter.h"
 
 void SkFlattenable::InitializeFlattenables() {
 
@@ -92,6 +93,7 @@ void SkFlattenable::InitializeFlattenables() {
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStippleMaskFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixConvolutionImageFilter)