Implement SkColorFilter as a GrGLEffect

Adds GrEffect::willUseInputColor() which indicates whether or not the
input color affects the output of the effect. This is needed for
certain Xfermodes, such as kSrc_Mode. For these modes the color filter
will not use the input color.

An effect with GrEffect::willUseInputColor() true will cause all color
or coverage effects before it to be discarded, as their computations
cannot affect the output. In these cases program is marked as having
white input color.

This fixes an assert when Skia is compiled in a mode that prefers
using uniforms instead of attributes for constants. (Flags
GR_GL_USE_NV_PATH_RENDERING or GR_GL_NO_CONSTANT_ATTRIBUTES). Using
attributes hides the problem where the fragment shader does not need
input color for color filters that ignore DST part of the filter. The
assert would be hit when uniform manager tries to bind an uniform which
has been optimized away by the shader compiler.

Adds specific GrGLSLExpr4 and GrGLSLExpr1 classes. This way the GLSL
expressions like "(v - src.a)" can remain somewhat readable in form of
"(v - src.a())". The GrGLSLExpr<typename> template implements the
generic functionality, GrGLSLExprX is the specialization that exposes
the type-safe interface to this functionality.

Also adds operators so that GLSL binary operators of the form
"(float * vecX)" can be expressed in C++. Before only the equivalent
"(vecX * float)" was possible. This reverts the common blending
calculations to more conventional order, such as "(1-a) * c" instead of
"c * (1-a)".

Changes GrGLSLExpr1::OnesStr from 1 to 1.0 in order to preserve the
color filter blending formula string the same (with the exception of
variable name change).

Shaders change in case of input color being needed:
 -   vec4 filteredColor;
 -   filteredColor = (((1.0 - uFilterColor.a) * output_Stage0) + uFilterColor);
 -   fsColorOut = filteredColor;
 +   vec4 output_Stage1;
 +   { // Stage 1: ModeColorFilterEffect
 +   output_Stage1 = (((1.0 - uFilterColor_Stage1.a) * output_Stage0) + uFilterColor_Stage1);
 +   }
 +   fsColorOut = output_Stage1;

Shaders change in case of input color being not needed:
 -uniform vec4 uFilterColor;
 -in vec4 vColor;
 +uniform vec4 uFilterColor_Stage0;
  out vec4 fsColorOut;
  void main() {
 -   vec4 filteredColor;
 -   filteredColor = uFilterColor;
 -   fsColorOut = filteredColor;
 +   vec4 output_Stage0;
 +   { // Stage 0: ModeColorFilterEffect
 +   output_Stage0 = uFilterColor_Stage0;
 +   }
 +   fsColorOut = output_Stage0;
  }

R=bsalomon@google.com, robertphillips@google.com, jvanverth@google.com

Author: kkinnunen@nvidia.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@11912 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkBitmapAlphaThresholdShader.cpp b/src/effects/SkBitmapAlphaThresholdShader.cpp
index 6f1c94a..7fb09fd 100644
--- a/src/effects/SkBitmapAlphaThresholdShader.cpp
+++ b/src/effects/SkBitmapAlphaThresholdShader.cpp
@@ -150,7 +150,7 @@
                                     "\t\t}\n");
 
             builder->fsCodeAppendf("color = %s = %s;\n", outputColor,
-                                   (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<4>("color")).c_str());
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr4("color")).c_str());
         }
 
         virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& e) SK_OVERRIDE {
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
index a821129..66ddb02 100644
--- a/src/effects/SkColorFilters.cpp
+++ b/src/effects/SkColorFilters.cpp
@@ -85,6 +85,9 @@
     }
 #endif
 
+#if SK_SUPPORT_GPU
+    virtual GrEffectRef* asNewEffect(GrContext*) const SK_OVERRIDE;
+#endif
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkModeColorFilter)
 
 protected:
@@ -117,6 +120,293 @@
     typedef SkColorFilter INHERITED;
 };
 
+///////////////////////////////////////////////////////////////////////////////
+#if SK_SUPPORT_GPU
+#include "GrBlend.h"
+#include "GrEffect.h"
+#include "GrEffectUnitTest.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+#include "SkGr.h"
+
+namespace {
+/**
+ * A definition of blend equation for one coefficient. Generates a
+ * blend_coeff * value "expression".
+ */
+template<typename ColorExpr>
+static inline ColorExpr blend_term(SkXfermode::Coeff coeff,
+                                   const ColorExpr& src,
+                                   const ColorExpr& dst,
+                                   const ColorExpr& value) {
+    switch (coeff) {
+    default:
+        GrCrash("Unexpected xfer coeff.");
+    case SkXfermode::kZero_Coeff:    /** 0 */
+        return ColorExpr(0);
+    case SkXfermode::kOne_Coeff:     /** 1 */
+        return value;
+    case SkXfermode::kSC_Coeff:
+        return src * value;
+    case SkXfermode::kISC_Coeff:
+        return (ColorExpr(1) - src) * dst;
+    case SkXfermode::kDC_Coeff:
+        return dst * value;
+    case SkXfermode::kIDC_Coeff:
+        return (ColorExpr(1) - dst) * value;
+    case SkXfermode::kSA_Coeff:      /** src alpha */
+        return src.a() * value;
+    case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
+        return (typename ColorExpr::AExpr(1) - src.a()) * value;
+    case SkXfermode::kDA_Coeff:      /** dst alpha */
+        return dst.a() * value;
+    case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
+        return (typename ColorExpr::AExpr(1) - dst.a()) *  value;
+    }
+}
+/**
+ * Creates a color filter expression which modifies the color by
+ * the specified color filter.
+ */
+template <typename ColorExpr>
+static inline ColorExpr color_filter_expression(const SkXfermode::Mode& mode,
+                                                const ColorExpr& filterColor,
+                                                const ColorExpr& inColor) {
+    SkXfermode::Coeff colorCoeff;
+    SkXfermode::Coeff filterColorCoeff;
+    SkAssertResult(SkXfermode::ModeAsCoeff(mode, &filterColorCoeff, &colorCoeff));
+    return blend_term(colorCoeff, filterColor, inColor, inColor) +
+        blend_term(filterColorCoeff, filterColor, inColor, filterColor);
+}
+
+}
+
+class ModeColorFilterEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(const GrColor& c, SkXfermode::Mode mode) {
+        AutoEffectUnref effect(SkNEW_ARGS(ModeColorFilterEffect, (c, mode)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    bool willUseFilterColor() const {
+        SkXfermode::Coeff dstCoeff;
+        SkXfermode::Coeff srcCoeff;
+        SkAssertResult(SkXfermode::ModeAsCoeff(fMode, &srcCoeff, &dstCoeff));
+        if (SkXfermode::kZero_Coeff == srcCoeff) {
+            return GrBlendCoeffRefsSrc(sk_blend_to_grblend(dstCoeff));
+        }
+        return true;
+    }
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<ModeColorFilterEffect>::getInstance();
+    }
+
+    static const char* Name() { return "ModeColorFilterEffect"; }
+
+    SkXfermode::Mode mode() const { return fMode; }
+    GrColor color() const { return fColor; }
+
+    class GLEffect : public GrGLEffect {
+    public:
+        GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+            : INHERITED(factory) {
+        }
+
+        virtual void emitCode(GrGLShaderBuilder* builder,
+                              const GrDrawEffect& drawEffect,
+                              EffectKey key,
+                              const char* outputColor,
+                              const char* inputColor,
+                              const TransformedCoordsArray& coords,
+                              const TextureSamplerArray& samplers) SK_OVERRIDE {
+            SkXfermode::Mode mode = drawEffect.castEffect<ModeColorFilterEffect>().mode();
+
+            SkASSERT(SkXfermode::kDst_Mode != mode);
+            const char* colorFilterColorUniName = NULL;
+            if (drawEffect.castEffect<ModeColorFilterEffect>().willUseFilterColor()) {
+                fFilterColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                                      kVec4f_GrSLType, "FilterColor",
+                                                      &colorFilterColorUniName);
+            }
+
+            GrGLSLExpr4 filter =
+                color_filter_expression(mode, GrGLSLExpr4(colorFilterColorUniName), GrGLSLExpr4(inputColor));
+
+            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, filter.c_str());
+        }
+
+        static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
+            const ModeColorFilterEffect& colorModeFilter = drawEffect.castEffect<ModeColorFilterEffect>();
+            // The SL code does not depend on filter color at the moment, so no need to represent it
+            // in the key.
+            EffectKey modeKey = colorModeFilter.mode();
+            return modeKey;
+        }
+
+        virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) SK_OVERRIDE {
+            if (fFilterColorUni.isValid()) {
+                const ModeColorFilterEffect& colorModeFilter = drawEffect.castEffect<ModeColorFilterEffect>();
+                GrGLfloat c[4];
+                GrColorToRGBAFloat(colorModeFilter.color(), c);
+                uman.set4fv(fFilterColorUni, 0, 1, c);
+            }
+        }
+
+    private:
+
+        GrGLUniformManager::UniformHandle fFilterColorUni;
+        typedef GrGLEffect INHERITED;
+    };
+
+    GR_DECLARE_EFFECT_TEST;
+
+private:
+    ModeColorFilterEffect(GrColor color, SkXfermode::Mode mode)
+        : fMode(mode),
+          fColor(color) {
+
+        SkXfermode::Coeff dstCoeff;
+        SkXfermode::Coeff srcCoeff;
+        SkAssertResult(SkXfermode::ModeAsCoeff(fMode, &srcCoeff, &dstCoeff));
+        // These could be calculated from the blend equation with template trickery..
+        if (SkXfermode::kZero_Coeff == dstCoeff && !GrBlendCoeffRefsDst(sk_blend_to_grblend(srcCoeff))) {
+            this->setWillNotUseInputColor();
+        }
+    }
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        const ModeColorFilterEffect& s = CastEffect<ModeColorFilterEffect>(other);
+        return fMode == s.fMode && fColor == s.fColor;
+    }
+
+    SkXfermode::Mode fMode;
+    GrColor fColor;
+
+    typedef GrEffect INHERITED;
+};
+
+namespace {
+
+/** Function color_component_to_int tries to reproduce the GLSL rounding. The spec doesn't specify
+ * to which direction the 0.5 goes.
+ */
+static inline int color_component_to_int(float value) {
+    return sk_float_round2int(GrMax(0.f, GrMin(1.f, value)) * 255.f);
+}
+
+/** MaskedColorExpr is used to evaluate the color and valid color component flags through the
+ * blending equation. It has members similar to GrGLSLExpr so that it can be used with the
+ * templated helpers above.
+ */
+class MaskedColorExpr {
+public:
+    MaskedColorExpr(const float color[], uint32_t flags)
+        : fFlags(flags) {
+        fColor[0] = color[0];
+        fColor[1] = color[1];
+        fColor[2] = color[2];
+        fColor[3] = color[3];
+    }
+
+    MaskedColorExpr(float v, uint32_t flags = kRGBA_GrColorComponentFlags)
+        : fFlags(flags) {
+        fColor[0] = v;
+        fColor[1] = v;
+        fColor[2] = v;
+        fColor[3] = v;
+    }
+
+    MaskedColorExpr operator*(const MaskedColorExpr& other) const {
+        float tmp[4];
+        tmp[0] = fColor[0] * other.fColor[0];
+        tmp[1] = fColor[1] * other.fColor[1];
+        tmp[2] = fColor[2] * other.fColor[2];
+        tmp[3] = fColor[3] * other.fColor[3];
+
+        return MaskedColorExpr(tmp, fFlags & other.fFlags);
+    }
+
+    MaskedColorExpr operator+(const MaskedColorExpr& other) const {
+        float tmp[4];
+        tmp[0] = fColor[0] + other.fColor[0];
+        tmp[1] = fColor[1] + other.fColor[1];
+        tmp[2] = fColor[2] + other.fColor[2];
+        tmp[3] = fColor[3] + other.fColor[3];
+
+        return MaskedColorExpr(tmp, fFlags & other.fFlags);
+    }
+
+    MaskedColorExpr operator-(const MaskedColorExpr& other) const {
+        float tmp[4];
+        tmp[0] = fColor[0] - other.fColor[0];
+        tmp[1] = fColor[1] - other.fColor[1];
+        tmp[2] = fColor[2] - other.fColor[2];
+        tmp[3] = fColor[3] - other.fColor[3];
+
+        return MaskedColorExpr(tmp, fFlags & other.fFlags);
+    }
+
+    MaskedColorExpr a() const {
+        uint32_t flags = (fFlags & kA_GrColorComponentFlag) ? kRGBA_GrColorComponentFlags : 0;
+        return MaskedColorExpr(fColor[3], flags);
+    }
+
+    GrColor getColor() const {
+        return GrColorPackRGBA(color_component_to_int(fColor[0]),
+                               color_component_to_int(fColor[1]),
+                               color_component_to_int(fColor[2]),
+                               color_component_to_int(fColor[3]));
+    }
+
+    uint32_t getValidComponents() const  { return fFlags; }
+
+    typedef MaskedColorExpr AExpr;
+private:
+    float fColor[4];
+    uint32_t fFlags;
+};
+
+}
+
+void ModeColorFilterEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    float inputColor[4];
+    GrColorToRGBAFloat(*color, inputColor);
+    float filterColor[4];
+    GrColorToRGBAFloat(fColor, filterColor);
+    MaskedColorExpr result =
+        color_filter_expression(fMode,
+                                MaskedColorExpr(filterColor, kRGBA_GrColorComponentFlags),
+                                MaskedColorExpr(inputColor, *validFlags));
+
+    *color = result.getColor();
+    *validFlags = result.getValidComponents();
+}
+
+GR_DEFINE_EFFECT_TEST(ModeColorFilterEffect);
+GrEffectRef* ModeColorFilterEffect::TestCreate(SkRandom* rand,
+                                    GrContext*,
+                                    const GrDrawTargetCaps&,
+                                    GrTexture*[]) {
+    int mode = rand->nextRangeU(0, SkXfermode::kLastCoeffMode);
+    GrColor color = rand->nextU();
+    static AutoEffectUnref gEffect(SkNEW_ARGS(ModeColorFilterEffect, (color, static_cast<SkXfermode::Mode>(mode))));
+    return CreateEffectRef(gEffect);
+}
+
+GrEffectRef* SkModeColorFilter::asNewEffect(GrContext*) const {
+    if (SkXfermode::kDst_Mode != fMode) {
+        return ModeColorFilterEffect::Create(SkColor2GrColor(fColor), fMode);
+    }
+    return NULL;
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
 class Src_SkModeColorFilter : public SkModeColorFilter {
 public:
     Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {}
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 3722178..ff2e837 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -348,6 +348,7 @@
     this->addTextureAccess(&fDisplacementAccess);
     this->addCoordTransform(&fColorTransform);
     this->addTextureAccess(&fColorAccess);
+    this->setWillNotUseInputColor();
 }
 
 GrDisplacementMapEffect::~GrDisplacementMapEffect() {
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
index a9dfecc..c89b9dc 100644
--- a/src/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -423,6 +423,7 @@
                                            const char* inputColor,
                                            const TransformedCoordsArray& coords,
                                            const TextureSamplerArray& samplers) {
+    sk_ignore_unused_variable(inputColor);
     SkString coords2D = builder->ensureFSCoords2D(coords, 0);
     fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                      kVec4f_GrSLType, "Bounds");
@@ -548,6 +549,7 @@
     }
     fTarget[0] = static_cast<float>(target.x());
     fTarget[1] = static_cast<float>(target.y());
+    this->setWillNotUseInputColor();
 }
 
 GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp
index df6b78e..af51afc 100644
--- a/src/effects/SkPerlinNoiseShader.cpp
+++ b/src/effects/SkPerlinNoiseShader.cpp
@@ -610,6 +610,7 @@
         m.postTranslate(SK_Scalar1, SK_Scalar1);
         fCoordTransform.reset(kLocal_GrCoordSet, m);
         this->addCoordTransform(&fCoordTransform);
+        this->setWillNotUseInputColor();
     }
 
     SkPerlinNoiseShader::Type       fType;
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index fb8bbac..c90adfe 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -951,7 +951,7 @@
         }
 
         builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                               (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<4>("colorTemp")).c_str());
+                               (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
     } else if (GrGradientEffect::kThree_ColorType == ColorTypeFromKey(key)){
         builder->fsCodeAppendf("\tfloat oneMinus2t = 1.0 - (2.0 * (%s));\n",
                                gradientTValue);
@@ -975,7 +975,7 @@
         }
 
         builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
-                               (GrGLSLExpr<4>(inputColor) * GrGLSLExpr<4>("colorTemp")).c_str());
+                               (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
     } else {
         builder->fsCodeAppendf("\tvec2 coord = vec2(%s, %s);\n",
                                gradientTValue,