Add color filters to gpu path.

git-svn-id: http://skia.googlecode.com/svn/trunk@1297 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 2d93e4b..8cb932b 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -1335,6 +1335,7 @@
         target->disableState(GrDrawTarget::kAntialias_StateBit);
     }
     target->setBlendFunc(paint.fSrcBlendCoeff, paint.fDstBlendCoeff);
+    target->setColorFilter(paint.fColorFilterColor, paint.fColorFilterXfermode);
 }
 
 GrDrawTarget* GrContext::prepareToDraw(const GrPaint& paint,
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 3810043..695a2ab 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -380,6 +380,11 @@
     fCurrDrawState.fColor = c;
 }
 
+void GrDrawTarget::setColorFilter(GrColor c, SkXfermode::Mode mode) {
+    fCurrDrawState.fColorFilterColor = c;
+    fCurrDrawState.fColorFilterXfermode = mode;
+}
+
 void GrDrawTarget::setAlpha(uint8_t a) {
     this->setColor((a << 24) | (a << 16) | (a << 8) | a);
 }
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index abed043..f89de9b 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -21,6 +21,8 @@
 #include "GrGLEffect.h"
 #include "GrMemory.h"
 
+#include "SkXfermode.h"
+
 namespace {
 
 const char* GrPrecision() {
@@ -52,6 +54,7 @@
 #define POS_ATTR_NAME "aPosition"
 #define COL_ATTR_NAME "aColor"
 #define COL_UNI_NAME "uColor"
+#define COL_FILTER_UNI_NAME "uColorFilter"
 
 static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
     *s = "aTexCoord";
@@ -174,6 +177,57 @@
     }
 }
 
+/**
+ * Create a text coefficient to be used in fragment shader code.
+ */
+static void coefficientString(GrStringBuilder& str, SkXfermode::Coeff coeff,
+            const char* src, const char* dst) {
+    switch (coeff) {
+    case SkXfermode::kZero_Coeff:    /** 0 */
+        str = "0.0";
+        break;
+    case SkXfermode::kOne_Coeff:     /** 1 */
+        str = "1.0";
+        break;
+    case SkXfermode::kSA_Coeff:      /** src alpha */
+        str.appendf("%s.a", src);
+        break;
+    case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
+        str.appendf("(1.0 - %s.a)", src);
+        break;
+    case SkXfermode::kDA_Coeff:      /** dst alpha */
+        str.appendf("%s.a", dst);
+        break;
+    case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
+        str.appendf("(1.0 - %s.a)", dst);
+        break;
+    case SkXfermode::kSC_Coeff:
+        str.append(src);
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * Adds a line to the fragment shader code which modifies the color by
+ * the specified color filter.
+ */
+static void addColorFilter(GrStringBuilder& FSCode, const char * outputVar,
+            SkXfermode::Mode colorFilterXfermode, const char* dstColor) {
+    SkXfermode::Coeff srcCoeff, dstCoeff;
+    bool success = SkXfermode::ModeAsCoeff(colorFilterXfermode,
+            &srcCoeff, &dstCoeff);
+    // We currently do not handle modes that cannot be represented as
+    // coefficients.
+    GrAssert(success);
+    GrStringBuilder srcCoeffStr, dstCoeffStr;
+    coefficientString(srcCoeffStr, srcCoeff, COL_FILTER_UNI_NAME, dstColor);
+    coefficientString(dstCoeffStr, dstCoeff, COL_FILTER_UNI_NAME, dstColor);
+    FSCode.appendf("\t%s = %s*%s + %s*%s;\n", outputVar, srcCoeffStr.c_str(),
+            COL_FILTER_UNI_NAME, dstCoeffStr.c_str(), dstColor);
+}
+
 bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
 
     ShaderCodeSegments segments;
@@ -230,23 +284,39 @@
         }
     }
 
+    bool useColorFilter = 
+            // The rest of transfer mode color filters have not been implemented
+            fProgramDesc.fColorFilterXfermode <= SkXfermode::kMultiply_Mode
+            // This mode has no effect.
+            && fProgramDesc.fColorFilterXfermode != SkXfermode::kDst_Mode;
+    bool onlyUseColorFilter = useColorFilter
+            && (fProgramDesc.fColorFilterXfermode == SkXfermode::kClear_Mode
+            || fProgramDesc.fColorFilterXfermode == SkXfermode::kSrc_Mode);
+    if (useColorFilter) {
+        // Set up a uniform for the color
+        segments.fFSUnis.append(  "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
+        programData->fUniLocations.fColorFilterUni = kUseUniform;
+    }
+
     // for each enabled stage figure out what the input coordinates are
     // and count the number of stages in use.
     const char* stageInCoords[GrDrawTarget::kNumStages];
     int numActiveStages = 0;
 
-    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
-        if (fProgramDesc.fStages[s].fEnabled) {
-            if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
-                stageInCoords[s] = POS_ATTR_NAME;
-            } else {
-                int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
-                 // we better have input tex coordinates if stage is enabled.
-                GrAssert(tcIdx >= 0);
-                GrAssert(texCoordAttrs[tcIdx].size());
-                stageInCoords[s] = texCoordAttrs[tcIdx].c_str();
+    if (!onlyUseColorFilter) {
+        for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+            if (fProgramDesc.fStages[s].fEnabled) {
+                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
+                    stageInCoords[s] = POS_ATTR_NAME;
+                } else {
+                    int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                     // we better have input tex coordinates if stage is enabled.
+                    GrAssert(tcIdx >= 0);
+                    GrAssert(texCoordAttrs[tcIdx].size());
+                    stageInCoords[s] = texCoordAttrs[tcIdx].c_str();
+                }
+                ++numActiveStages;
             }
-            ++numActiveStages;
         }
     }
 
@@ -254,10 +324,10 @@
     // of each to the next and generating code for each stage.
     if (numActiveStages) {
         int currActiveStage = 0;
+        GrStringBuilder outColor;
         for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
             if (fProgramDesc.fStages[s].fEnabled) {
-                GrStringBuilder outColor;
-                if (currActiveStage < (numActiveStages - 1)) {
+                if (currActiveStage < (numActiveStages - 1) || useColorFilter) {
                     outColor = "color";
                     outColor.appendS32(currActiveStage);
                     segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
@@ -276,9 +346,21 @@
                 inColor = outColor;
             }
         }
+        if (useColorFilter) {
+            addColorFilter(segments.fFSCode, "gl_FragColor",
+                    fProgramDesc.fColorFilterXfermode, outColor.c_str());
+        }
+
     } else {
         // we may not have any incoming color
-        segments.fFSCode.appendf("\tgl_FragColor = %s;\n", (inColor.size() ? inColor.c_str() : "vec4(1,1,1,1);\n"));
+        const char * incomingColor = (inColor.size() ? inColor.c_str()
+                : "vec4(1,1,1,1)");
+        if (useColorFilter) {
+            addColorFilter(segments.fFSCode, "gl_FragColor",
+                    fProgramDesc.fColorFilterXfermode, incomingColor);
+        } else {
+            segments.fFSCode.appendf("\tgl_FragColor = %s;\n", incomingColor);
+        }
     }
     segments.fVSCode.append("}\n");
     segments.fFSCode.append("}\n");
@@ -495,6 +577,11 @@
                                 GR_GL(GetUniformLocation(progID, COL_UNI_NAME));
         GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
     }
+    if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
+        programData->fUniLocations.fColorFilterUni = 
+                        GR_GL(GetUniformLocation(progID, COL_FILTER_UNI_NAME));
+        GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
+    }
 
     for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
         StageUniLocations& locations = programData->fUniLocations.fStages[s];
@@ -550,6 +637,7 @@
     }
     programData->fViewMatrix = GrMatrix::InvalidMatrix();
     programData->fColor = GrColor_ILLEGAL;
+    programData->fColorFilterColor = GrColor_ILLEGAL;
 }
 
 //============================================================================
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 54818ec..3b926a7 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -21,6 +21,8 @@
 #include "GrStringBuilder.h"
 #include "GrDrawTarget.h"
 
+#include "SkXfermode.h"
+
 class GrBinHashKeyBuilder;
 class GrGLEffect;
 struct ShaderCodeSegments;
@@ -107,6 +109,9 @@
 
         bool fEmitsPointSize;
 
+        GrColor fColorFilterColor;
+        SkXfermode::Mode fColorFilterXfermode;
+
         struct StageDesc {
             enum OptFlagBits {
                 kNoPerspective_OptFlagBit  = 0x1,
@@ -159,10 +164,12 @@
     struct UniLocations {
         GrGLint fViewMatrixUni;
         GrGLint fColorUni;
+        GrGLint fColorFilterUni;
         StageUniLocations fStages[GrDrawTarget::kNumStages];
         void reset() {
             fViewMatrixUni = kUnusedUniform;
             fColorUni = kUnusedUniform;
+            fColorFilterUni = kUnusedUniform;
             for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
                 fStages[s].reset();
             }
@@ -217,6 +224,7 @@
         // these reflect the current values of uniforms
         // (GL uniform values travel with program)
         GrColor                     fColor;
+        GrColor                     fColorFilterColor;
         GrMatrix                    fTextureMatrices[GrDrawTarget::kNumStages];
         // width and height used for normalized texel size
         int                         fTextureWidth[GrDrawTarget::kNumStages];
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 3ce6e55..2487563 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -438,6 +438,19 @@
                 GrCrash("Unknown color type.");
         }
     }
+    if (fProgramData->fUniLocations.fColorFilterUni
+                != GrGLProgram::kUnusedUniform
+            && fProgramData->fColorFilterColor
+                != fCurrDrawState.fColorFilterColor) {
+        float c[] = {
+            GrColorUnpackR(fCurrDrawState.fColorFilterColor) / 255.f,
+            GrColorUnpackG(fCurrDrawState.fColorFilterColor) / 255.f,
+            GrColorUnpackB(fCurrDrawState.fColorFilterColor) / 255.f,
+            GrColorUnpackA(fCurrDrawState.fColorFilterColor) / 255.f
+        };
+        GR_GL(Uniform4fv(fProgramData->fUniLocations.fColorFilterUni, 1, c));
+        fProgramData->fColorFilterColor = fCurrDrawState.fColorFilterColor;
+    }
 }
 
 
@@ -693,6 +706,8 @@
             fCurrentProgram.fStageEffects[s] = NULL;
         }
     }
+    desc.fColorFilterColor = fCurrDrawState.fColorFilterColor;
+    desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
 }