Arithmetic mode now has the option of not validating the output color, which will allow multiple arithmetic operations to be done sequentially, without intermediate clamping. This is required for mimicking blink's current behavior.

BUG=skia:
R=senorblanco@google.com, senorblanco@chromium.org, reed@google.com, bsalomon@google.com

Author: sugoi@chromium.org

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

git-svn-id: http://skia.googlecode.com/svn/trunk@14031 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/effects/SkArithmeticMode.h b/include/effects/SkArithmeticMode.h
index 46b7121..3b9585d 100644
--- a/include/effects/SkArithmeticMode.h
+++ b/include/effects/SkArithmeticMode.h
@@ -26,7 +26,8 @@
      *  k1=k2=k4=0, k3=1.0 results in returning the dst
      */
     static SkXfermode* Create(SkScalar k1, SkScalar k2,
-                              SkScalar k3, SkScalar k4);
+                              SkScalar k3, SkScalar k4,
+                              bool enforcePMColor = true);
 
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP();
 
diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp
index 034ea37..41cc266 100644
--- a/src/effects/SkArithmeticMode.cpp
+++ b/src/effects/SkArithmeticMode.cpp
@@ -22,8 +22,9 @@
 
 class SkArithmeticMode_scalar : public SkXfermode {
 public:
-    static SkArithmeticMode_scalar* Create(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4) {
-        return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4));
+    static SkArithmeticMode_scalar* Create(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4,
+                                           bool enforcePMColor) {
+        return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4, enforcePMColor));
     }
 
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
@@ -37,11 +38,12 @@
 #endif
 
 private:
-    SkArithmeticMode_scalar(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4) {
+    SkArithmeticMode_scalar(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, bool enforcePMColor) {
         fK[0] = k1;
         fK[1] = k2;
         fK[2] = k3;
         fK[3] = k4;
+        fEnforcePMColor = enforcePMColor;
     }
 
     SkArithmeticMode_scalar(SkReadBuffer& buffer) : INHERITED(buffer) {
@@ -49,6 +51,7 @@
         fK[1] = buffer.readScalar();
         fK[2] = buffer.readScalar();
         fK[3] = buffer.readScalar();
+        fEnforcePMColor = buffer.readBool();
     }
 
     virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
@@ -57,8 +60,10 @@
         buffer.writeScalar(fK[1]);
         buffer.writeScalar(fK[2]);
         buffer.writeScalar(fK[3]);
+        buffer.writeBool(fEnforcePMColor);
     }
     SkScalar fK[4];
+    bool fEnforcePMColor;
 
     typedef SkXfermode INHERITED;
 };
@@ -145,11 +150,13 @@
             } else {
                 a = arith(k1, k2, k3, k4, SkGetPackedA32(sc), SkGetPackedA32(dc));
                 r = arith(k1, k2, k3, k4, SkGetPackedR32(sc), SkGetPackedR32(dc));
-                r = SkMin32(r, a);
                 g = arith(k1, k2, k3, k4, SkGetPackedG32(sc), SkGetPackedG32(dc));
-                g = SkMin32(g, a);
                 b = arith(k1, k2, k3, k4, SkGetPackedB32(sc), SkGetPackedB32(dc));
-                b = SkMin32(b, a);
+                if (fEnforcePMColor) {
+                    r = SkMin32(r, a);
+                    g = SkMin32(g, a);
+                    b = SkMin32(b, a);
+                }
             }
 
             // apply antialias coverage if necessary
@@ -168,7 +175,7 @@
                 g = SkAlphaMul(g, scale);
                 b = SkAlphaMul(b, scale);
             }
-            dst[i] = SkPackARGB32(a, r, g, b);
+            dst[i] = fEnforcePMColor ? SkPackARGB32(a, r, g, b) : SkPackARGB32NoCheck(a, r, g, b);
         }
     }
 }
@@ -178,10 +185,9 @@
     str->append("SkArithmeticMode_scalar: ");
     for (int i = 0; i < 4; ++i) {
         str->appendScalar(fK[i]);
-        if (i < 3) {
-            str->append(" ");
-        }
+        str->append(" ");
     }
+    str->appendS32(fEnforcePMColor ? 1 : 0);
 }
 #endif
 
@@ -198,7 +204,8 @@
 #endif
 
 SkXfermode* SkArithmeticMode::Create(SkScalar k1, SkScalar k2,
-                                     SkScalar k3, SkScalar k4) {
+                                     SkScalar k3, SkScalar k4,
+                                     bool enforcePMColor) {
     if (fitsInBits(k1, 8) && fitsInBits(k2, 16) &&
         fitsInBits(k2, 16) && fitsInBits(k2, 24)) {
 
@@ -219,7 +226,7 @@
         return SkNEW_ARGS(SkArithmeticMode_linear, (i2, i3, i4));
 #endif
     }
-    return SkArithmeticMode_scalar::Create(k1, k2, k3, k4);
+    return SkArithmeticMode_scalar::Create(k1, k2, k3, k4, enforcePMColor);
 }
 
 
@@ -242,8 +249,11 @@
 
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
 
+    static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
+
 private:
     GrGLUniformManager::UniformHandle fKUni;
+    bool fEnforcePMColor;
 
     typedef GrGLEffect INHERITED;
 };
@@ -252,8 +262,10 @@
 
 class GrArithmeticEffect : public GrEffect {
 public:
-    static GrEffectRef* Create(float k1, float k2, float k3, float k4, GrTexture* background) {
-        AutoEffectUnref effect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, background)));
+    static GrEffectRef* Create(float k1, float k2, float k3, float k4, bool enforcePMColor,
+                               GrTexture* background) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, enforcePMColor,
+                                                               background)));
         return CreateEffectRef(effect);
     }
 
@@ -271,12 +283,15 @@
     float k2() const { return fK2; }
     float k3() const { return fK3; }
     float k4() const { return fK4; }
+    bool enforcePMColor() const { return fEnforcePMColor; }
 
 private:
     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
 
-    GrArithmeticEffect(float k1, float k2, float k3, float k4, GrTexture* background);
+    GrArithmeticEffect(float k1, float k2, float k3, float k4, bool enforcePMColor,
+                       GrTexture* background);
     float                       fK1, fK2, fK3, fK4;
+    bool                        fEnforcePMColor;
     GrCoordTransform            fBackgroundTransform;
     GrTextureAccess             fBackgroundAccess;
 
@@ -288,8 +303,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 GrArithmeticEffect::GrArithmeticEffect(float k1, float k2, float k3, float k4,
-                                       GrTexture* background)
-  : fK1(k1), fK2(k2), fK3(k3), fK4(k4) {
+                                       bool enforcePMColor, GrTexture* background)
+  : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) {
     if (background) {
         fBackgroundTransform.reset(kLocal_GrCoordSet, background);
         this->addCoordTransform(&fBackgroundTransform);
@@ -309,6 +324,7 @@
            fK2 == s.fK2 &&
            fK3 == s.fK3 &&
            fK4 == s.fK4 &&
+           fEnforcePMColor == s.fEnforcePMColor &&
            backgroundTexture() == s.backgroundTexture();
 }
 
@@ -325,7 +341,8 @@
 
 GrGLArithmeticEffect::GrGLArithmeticEffect(const GrBackendEffectFactory& factory,
                                            const GrDrawEffect& drawEffect)
-   : INHERITED(factory) {
+   : INHERITED(factory),
+     fEnforcePMColor(true) {
 }
 
 GrGLArithmeticEffect::~GrGLArithmeticEffect() {
@@ -374,7 +391,7 @@
     builder->fsCodeAppendf("\t\t%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor);
     if (gUseUnpremul) {
         builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
-    } else {
+    } else if (fEnforcePMColor) {
         builder->fsCodeAppendf("\t\t%s.rgb = min(%s.rgb, %s.a);\n", outputColor, outputColor, outputColor);
     }
 }
@@ -382,6 +399,17 @@
 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());
+    fEnforcePMColor = arith.enforcePMColor();
+}
+
+GrGLEffect::EffectKey GrGLArithmeticEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                   const GrGLCaps&) {
+    const GrArithmeticEffect& arith = drawEffect.castEffect<GrArithmeticEffect>();
+    EffectKey key = arith.enforcePMColor() ? 1 : 0;
+    if (arith.backgroundTexture()) {
+        key |= 2;
+    }
+    return key;
 }
 
 GrEffectRef* GrArithmeticEffect::TestCreate(SkRandom* rand,
@@ -392,8 +420,10 @@
     float k2 = rand->nextF();
     float k3 = rand->nextF();
     float k4 = rand->nextF();
+    bool enforcePMColor = rand->nextBool();
 
-    AutoEffectUnref gEffect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, NULL)));
+    AutoEffectUnref gEffect(SkNEW_ARGS(GrArithmeticEffect,
+                                       (k1, k2, k3, k4, enforcePMColor, NULL)));
     return CreateEffectRef(gEffect);
 }
 
@@ -405,6 +435,7 @@
                                              SkScalarToFloat(fK[1]),
                                              SkScalarToFloat(fK[2]),
                                              SkScalarToFloat(fK[3]),
+                                             fEnforcePMColor,
                                              background);
     }
     return true;