Provide a GPU implementation of SkArithmeticMode, using a custom GrEffect exposed...
authorsenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 29 May 2013 18:50:46 +0000 (18:50 +0000)
committersenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 29 May 2013 18:50:46 +0000 (18:50 +0000)
Doing it this way required modifying the arithmode GM to use saveLayer()/restore() rather than creating an offscreen SkBitmap, since otherwise the compositing is always done in raster mode. Fixing that in turn exposed that SkArithmeticMode did not work in Picture mode, since it wasn't flattenable. Made it so.

Note: this will require rebaselining the arithmode GM (again).

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

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

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

gm/arithmode.cpp
include/effects/SkArithmeticMode.h
src/effects/SkArithmeticMode.cpp
src/ports/SkGlobalInitialization_default.cpp

index 2a97eca..63be6c3 100644 (file)
@@ -54,18 +54,6 @@ static SkBitmap make_dst() {
     return bm;
 }
 
-static SkBitmap make_arith(const SkBitmap& src, const SkBitmap& dst,
-                           const SkScalar k[]) {
-    SkBitmap bm = make_bm();
-    SkCanvas canvas(bm);
-    SkPaint paint;
-    canvas.drawBitmap(dst, 0, 0, NULL);
-    SkXfermode* xfer = SkArithmeticMode::Create(k[0], k[1], k[2], k[3]);
-    paint.setXfermode(xfer)->unref();
-    canvas.drawBitmap(src, 0, 0, &paint);
-    return bm;
-}
-
 static void show_k_text(SkCanvas* canvas, SkScalar x, SkScalar y, const SkScalar k[]) {
     SkPaint paint;
     paint.setTextSize(SkIntToScalar(24));
@@ -116,12 +104,18 @@ protected:
         SkScalar gap = SkIntToScalar(src.width() + 20);
         while (k < stop) {
             SkScalar x = 0;
-            SkBitmap res = make_arith(src, dst, k);
             canvas->drawBitmap(src, x, y, NULL);
             x += gap;
             canvas->drawBitmap(dst, x, y, NULL);
             x += gap;
-            canvas->drawBitmap(res, x, y, NULL);
+            SkRect rect = SkRect::MakeXYWH(x, y, SkIntToScalar(WW), SkIntToScalar(HH));
+            canvas->saveLayer(&rect, NULL);
+            canvas->drawBitmap(dst, x, y, NULL);
+            SkXfermode* xfer = SkArithmeticMode::Create(k[0], k[1], k[2], k[3]);
+            SkPaint paint;
+            paint.setXfermode(xfer)->unref();
+            canvas->drawBitmap(src, x, y, &paint);
+            canvas->restore();
             x += gap;
             show_k_text(canvas, x, y, k);
             k += 4;
index dc5493f..9de332c 100644 (file)
@@ -25,6 +25,8 @@ public:
     static SkXfermode* Create(SkScalar k1, SkScalar k2,
                               SkScalar k3, SkScalar k4);
 
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP();
+
 private:
     typedef SkXfermode INHERITED;
 };
index 5bc48f1..5913346 100644 (file)
@@ -7,8 +7,15 @@
 
 #include "SkArithmeticMode.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
 #include "SkString.h"
 #include "SkUnPreMultiply.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "gl/GrGLEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "SkImageFilterUtils.h"
+#endif
 
 class SkArithmeticMode_scalar : public SkXfermode {
 public:
@@ -23,9 +30,27 @@ public:
                         const SkAlpha aa[]) const SK_OVERRIDE;
 
     SK_DEVELOPER_TO_STRING()
-    SK_DECLARE_UNFLATTENABLE_OBJECT()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticMode_scalar)
+
+#if SK_SUPPORT_GPU
+    virtual bool asNewEffectOrCoeff(GrContext*, GrEffectRef** effect, Coeff*, Coeff*) const SK_OVERRIDE;
+#endif
 
 private:
+    SkArithmeticMode_scalar(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fK[0] = buffer.readScalar();
+        fK[1] = buffer.readScalar();
+        fK[2] = buffer.readScalar();
+        fK[3] = buffer.readScalar();
+    }
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+        INHERITED::flatten(buffer);
+        buffer.writeScalar(fK[0]);
+        buffer.writeScalar(fK[1]);
+        buffer.writeScalar(fK[2]);
+        buffer.writeScalar(fK[3]);
+    }
     SkScalar fK[4];
 
     typedef SkXfermode INHERITED;
@@ -191,3 +216,171 @@ SkXfermode* SkArithmeticMode::Create(SkScalar k1, SkScalar k2,
     }
     return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4));
 }
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+class GrGLArithmeticEffect : public GrGLEffect {
+public:
+    GrGLArithmeticEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
+    virtual ~GrGLArithmeticEffect();
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrDrawEffect&,
+                          EffectKey,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+private:
+    static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType;
+
+    GrGLUniformManager::UniformHandle fKUni;
+
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrArithmeticEffect();
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    typedef GrGLArithmeticEffect GLEffect;
+    static const char* Name() { return "Arithmetic"; }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    float k1() const { return fK1; }
+    float k2() const { return fK2; }
+    float k3() const { return fK3; }
+    float k4() const { return fK4; }
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrArithmeticEffect(float k1, float k2, float k3, float k4);
+    float                       fK1, fK2, fK3, fK4;
+
+    GR_DECLARE_EFFECT_TEST;
+    typedef GrEffect INHERITED;
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrArithmeticEffect::GrArithmeticEffect(float k1, float k2, float k3, float k4)
+  : fK1(k1), fK2(k2), fK3(k3), fK4(k4) {
+    this->setWillReadDstColor();
+}
+
+GrArithmeticEffect::~GrArithmeticEffect() {
+}
+
+bool GrArithmeticEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrArithmeticEffect& s = CastEffect<GrArithmeticEffect>(sBase);
+    return fK1 == s.fK1 &&
+           fK2 == s.fK2 &&
+           fK3 == s.fK3 &&
+           fK4 == s.fK4;
+}
+
+const GrBackendEffectFactory& GrArithmeticEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrArithmeticEffect>::getInstance();
+}
+
+void GrArithmeticEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // TODO: optimize this
+    *validFlags = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLArithmeticEffect::GrGLArithmeticEffect(const GrBackendEffectFactory& factory,
+                                           const GrDrawEffect& drawEffect) : INHERITED(factory) {
+}
+
+GrGLArithmeticEffect::~GrGLArithmeticEffect() {
+}
+
+void GrGLArithmeticEffect::emitCode(GrGLShaderBuilder* builder,
+                                    const GrDrawEffect&,
+                                    EffectKey key,
+                                    const char* outputColor,
+                                    const char* inputColor,
+                                    const TextureSamplerArray& samplers) {
+    const char* dstColor = builder->dstColor();
+    GrAssert(NULL != dstColor);
+
+    // We don't try to optimize for this case at all
+    if (NULL == inputColor) {
+        builder->fsCodeAppendf("\t\tconst vec4 ones = %s;\n", GrGLSLOnesVecf(4));
+        inputColor = "ones";
+    }
+    fKUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                kVec4f_GrSLType, "k");
+    const char* kUni = builder->getUniformCStr(fKUni);
+
+    builder->fsCodeAppendf("\t\t%s.rgb = clamp(%s.rgb / %s.a, 0.0, 1.0);\n", inputColor, inputColor, inputColor);
+    builder->fsCodeAppendf("\t\t%s.rgb = clamp(%s.rgb / %s.a, 0.0, 1.0);\n", dstColor, dstColor, dstColor);
+
+    builder->fsCodeAppendf("\t\t%s = %s.x * %s * %s + %s.y * %s + %s.z * %s + %s.w;\n", outputColor, kUni, inputColor, dstColor, kUni, inputColor, kUni, dstColor, kUni);
+    builder->fsCodeAppendf("\t\t%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor);
+    builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
+}
+
+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());
+}
+
+GrGLEffect::EffectKey GrGLArithmeticEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
+    return 0;
+}
+
+GrEffectRef* GrArithmeticEffect::TestCreate(SkMWCRandom* rand,
+                                            GrContext*,
+                                            const GrDrawTargetCaps&,
+                                            GrTexture*[]) {
+    float k1 = rand->nextF();
+    float k2 = rand->nextF();
+    float k3 = rand->nextF();
+    float k4 = rand->nextF();
+
+    static AutoEffectUnref gEffect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4)));
+    return CreateEffectRef(gEffect);
+}
+
+GR_DEFINE_EFFECT_TEST(GrArithmeticEffect);
+
+bool SkArithmeticMode_scalar::asNewEffectOrCoeff(GrContext*,
+                                                 GrEffectRef** effect,
+                                                 Coeff*,
+                                                 Coeff*) const {
+    if (effect) {
+        *effect = GrArithmeticEffect::Create(SkScalarToFloat(fK[0]),
+                                             SkScalarToFloat(fK[1]),
+                                             SkScalarToFloat(fK[2]),
+                                             SkScalarToFloat(fK[3]));
+    }
+    return true;
+}
+
+#endif
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkArithmeticMode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArithmeticMode_scalar)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
index 297f138..f6ed222 100644 (file)
@@ -16,6 +16,7 @@
 #include "Sk1DPathEffect.h"
 #include "Sk2DPathEffect.h"
 #include "SkAnnotation.h"
+#include "SkArithmeticMode.h"
 #include "SkAvoidXfermode.h"
 #include "SkBicubicImageFilter.h"
 #include "SkBitmapSource.h"
@@ -101,6 +102,7 @@ void SkFlattenable::InitializeFlattenables() {
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDownSampleImageFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMallocPixelRef)
 
+    SkArithmeticMode::InitializeFlattenables();
     SkBlurMaskFilter::InitializeFlattenables();
     SkColorFilter::InitializeFlattenables();
     SkGradientShader::InitializeFlattenables();