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/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index 6c92071..e8f1793 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -26,6 +26,8 @@
 #include "GrTexture.h"
 #include "GrStencil.h"
 
+#include "SkXfermode.h"
+
 class GrTexture;
 class GrClipIterator;
 class GrVertexBuffer;
@@ -131,6 +133,9 @@
             // all DrState members should default to something
             // valid by the memset
             memset(this, 0, sizeof(DrState));
+            // This is an exception to our memset, since it will
+            // result in no change.
+            fColorFilterXfermode = SkXfermode::kDstIn_Mode;
             GrAssert((intptr_t)(void*)NULL == 0LL);
             GrAssert(fStencilSettings.isDisabled());
         }
@@ -144,6 +149,8 @@
         GrRenderTarget*         fRenderTarget;
         GrColor                 fColor;
         DrawFace                fDrawFace;
+        GrColor                 fColorFilterColor;
+        SkXfermode::Mode        fColorFilterXfermode;
 
         GrStencilSettings       fStencilSettings;
         GrMatrix                fViewMatrix;
@@ -312,6 +319,11 @@
     void setColor(GrColor);
 
     /**
+     * Add a color filter that can be represented by a color and a mode.
+     */
+    void setColorFilter(GrColor, SkXfermode::Mode);
+
+    /**
      *  Sets the color to be used for the next draw to be
      *  (r,g,b,a) = (alpha, alpha, alpha, alpha).
      *
diff --git a/gpu/include/GrPaint.h b/gpu/include/GrPaint.h
index 9402209..3035ca1 100644
--- a/gpu/include/GrPaint.h
+++ b/gpu/include/GrPaint.h
@@ -21,6 +21,8 @@
 #include "GrColor.h"
 #include "GrSamplerState.h"
 
+#include "SkXfermode.h"
+
 /**
  * The paint describes how pixels are colored when the context draws to
  * them.
@@ -38,6 +40,9 @@
 
     GrSamplerState              fSampler;
 
+    GrColor                     fColorFilterColor;
+    SkXfermode::Mode            fColorFilterXfermode;
+
     void setTexture(GrTexture* texture) {
         GrSafeRef(texture);
         GrSafeUnref(fTexture);
@@ -59,6 +64,9 @@
 
         fColor = paint.fColor;
 
+        fColorFilterColor = paint.fColorFilterColor;
+        fColorFilterXfermode = paint.fColorFilterXfermode;
+
         fSampler = paint.fSampler;
         fTexture = paint.fTexture;
         GrSafeRef(fTexture);
@@ -74,6 +82,12 @@
         resetOptions();
         resetColor();
         resetTexture();
+        resetColorFilter();
+    }
+
+    void resetColorFilter() {
+        fColorFilterXfermode = SkXfermode::kDst_Mode;
+        fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
     }
 
 private:
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;
 }
 
 
diff --git a/gyp/skia.gyp b/gyp/skia.gyp
index 8f228c9..c37d567 100644
--- a/gyp/skia.gyp
+++ b/gyp/skia.gyp
@@ -1424,6 +1424,7 @@
         '../samplecode/SampleCamera.cpp',
         '../samplecode/SampleCircle.cpp',
         '../samplecode/SampleCode.h',
+        '../samplecode/SampleColorFilter.cpp',
         '../samplecode/SampleComplexClip.cpp',
         '../samplecode/SampleCull.cpp',
         '../samplecode/SampleDecode.cpp',
diff --git a/samplecode/SampleColorFilter.cpp b/samplecode/SampleColorFilter.cpp
index 8b1c317..77b64a8 100644
--- a/samplecode/SampleColorFilter.cpp
+++ b/samplecode/SampleColorFilter.cpp
@@ -91,14 +91,20 @@
         }
         
         static const SkXfermode::Mode gModes[] = {
+            SkXfermode::kClear_Mode,
             SkXfermode::kSrc_Mode,
             SkXfermode::kDst_Mode,
+            SkXfermode::kSrcOver_Mode,
+            SkXfermode::kDstOver_Mode,
             SkXfermode::kSrcIn_Mode,
             SkXfermode::kDstIn_Mode,
             SkXfermode::kSrcOut_Mode,
             SkXfermode::kDstOut_Mode,
             SkXfermode::kSrcATop_Mode,
             SkXfermode::kDstATop_Mode,
+            SkXfermode::kXor_Mode,
+            SkXfermode::kPlus_Mode,
+            SkXfermode::kMultiply_Mode,
         };
     
         static const SkColor gColors[] = {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 8818dc0..563dde7 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -22,6 +22,7 @@
 #include "SkGpuDeviceFactory.h"
 #include "SkGrTexturePixelRef.h"
 
+#include "SkColorFilter.h"
 #include "SkDrawProcs.h"
 #include "SkGlyphCache.h"
 #include "SkUtils.h"
@@ -368,6 +369,15 @@
         grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor());
         grPaint->setTexture(NULL);
     }
+    SkColorFilter* colorFilter = skPaint.getColorFilter();
+    SkColor color;
+    SkXfermode::Mode filterMode;
+    if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
+        grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
+        grPaint->fColorFilterXfermode = filterMode;
+    } else {
+        grPaint->resetColorFilter();
+    }
     return true;
 }