This CL implements the Ganesh path for the SkTable_ColorFilter color transformation.

A new texture stage dedicated to color transforms has been added, along with the new custom stage implementing the LUT.
Review URL: https://codereview.appspot.com/6351081

git-svn-id: http://skia.googlecode.com/svn/trunk@4663 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index 523ba52..cff89d3 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -287,6 +287,8 @@
         '../src/gpu/gr_unittests.cpp',
 
         '../src/gpu/effects/Gr1DKernelEffect.h',
+        '../src/gpu/effects/GrColorTableEffect.cpp',
+        '../src/gpu/effects/GrColorTableEffect.h',
         '../src/gpu/effects/GrConvolutionEffect.cpp',
         '../src/gpu/effects/GrConvolutionEffect.h',
         '../src/gpu/effects/GrGradientEffects.cpp',
diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h
index 8b1bf2c..fc2fce5 100644
--- a/include/gpu/GrPaint.h
+++ b/include/gpu/GrPaint.h
@@ -24,7 +24,7 @@
 class GrPaint {
 public:
     enum {
-        kMaxTextures = 1,
+        kMaxTextures = 2,
         kMaxMasks    = 1,
     };
 
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index c709dd4..ab42031 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -182,7 +182,19 @@
             fBitmap = new SkBitmap;
             fBitmap->setConfig(SkBitmap::kA8_Config, 256, 4, 256);
             fBitmap->allocPixels();
-            memcpy(fBitmap->getAddr8(0, 0), fStorage, 256 * 4);
+            uint8_t* bitmapPixels = fBitmap->getAddr8(0, 0);
+            int offset = 0;
+            static const unsigned kFlags[] = { kA_Flag, kR_Flag, kG_Flag, kB_Flag };
+
+            for (int x = 0; x < 4; ++x) {
+                if (!(fFlags & kFlags[x])) {
+                    memcpy(bitmapPixels, gIdentityTable, sizeof(gIdentityTable));
+                } else {
+                    memcpy(bitmapPixels, fStorage + offset, 256);
+                    offset += 256;
+                }
+                bitmapPixels += 256;
+            }
         }
         *table = *fBitmap;
     }
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index b5c3482..de47182 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -44,7 +44,7 @@
      * GrPathRenderer-derived classes.
      */
     enum {
-        kNumStages = 4,
+        kNumStages = 5,
         kMaxTexCoords = kNumStages
     };
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index bd922fe..d3d4e45 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -67,19 +67,22 @@
 }
 
 /* These values were generated by the above function */
+
 const GrVertexLayout gStageTexCoordMasks[] = {
-    0x1111,
-    0x2222,
-    0x4444,
-    0x8888,
+    0x108421,
+    0x210842,
+    0x421084,
+    0x842108,
+    0x1084210,
 };
 GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));
 
 const GrVertexLayout gTexCoordMasks[] = {
-    0xf,
-    0xf0,
-    0xf00,
-    0xf000,
+    0x1f,
+    0x3e0,
+    0x7c00,
+    0xf8000,
+    0x1f00000,
 };
 GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));
 
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 5e4d8936..ed52848 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -8,6 +8,7 @@
 #include "SkGpuDevice.h"
 
 #include "effects/GrGradientEffects.h"
+#include "effects/GrColorTableEffect.h"
 #include "effects/GrTextureDomainEffect.h"
 
 #include "GrContext.h"
@@ -41,7 +42,8 @@
 // (since drawBitmap, drawSprite, and drawDevice ignore skia's shader)
 enum {
     kBitmapTextureIdx = 0,
-    kShaderTextureIdx = 0
+    kShaderTextureIdx = 0,
+    kColorFilterTextureIdx = 1
 };
 
 
@@ -448,10 +450,12 @@
 // Callers may subsequently modify the GrPaint. Setting constantColor indicates
 // that the final paint will draw the same color at every pixel. This allows
 // an optimization where the the color filter can be applied to the skPaint's
-// color once while converting to GrPain and then ignored.
-inline bool skPaint2GrPaintNoShader(const SkPaint& skPaint,
+// color once while converting to GrPaint and then ignored.
+inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
+                                    const SkPaint& skPaint,
                                     bool justAlpha,
                                     bool constantColor,
+                                    SkGpuDevice::SkAutoCachedTexture* act,
                                     GrPaint* grPaint) {
 
     grPaint->fDither    = skPaint.isDither();
@@ -487,6 +491,7 @@
     SkColor color;
     SkXfermode::Mode filterMode;
     SkScalar matrix[20];
+    SkBitmap colorTransformTable;
     if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
         grPaint->fColorMatrixEnabled = false;
         if (!constantColor) {
@@ -501,6 +506,17 @@
         grPaint->fColorMatrixEnabled = true;
         memcpy(grPaint->fColorMatrix, matrix, sizeof(matrix));
         grPaint->fColorFilterXfermode = SkXfermode::kDst_Mode;
+    } else if (colorFilter != NULL && colorFilter->asComponentTable(
+        &colorTransformTable)) {
+        grPaint->resetColorFilter();
+
+        GrSamplerState* colorSampler = grPaint->textureSampler(kColorFilterTextureIdx);
+        GrTexture* texture = act->set(dev, colorTransformTable, colorSampler);
+
+        colorSampler->setCustomStage(SkNEW_ARGS(GrColorTableEffect, (texture)))->unref();
+        colorSampler->setFilter(GrSamplerState::kNearest_Filter);
+        colorSampler->setWrapX(GrSamplerState::kClamp_WrapMode);
+        colorSampler->setWrapY(GrSamplerState::kClamp_WrapMode);
     } else {
         grPaint->resetColorFilter();
     }
@@ -514,18 +530,18 @@
 inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
                                   const SkPaint& skPaint,
                                   bool constantColor,
-                                  SkGpuDevice::SkAutoCachedTexture* act,
+                                  SkGpuDevice::SkAutoCachedTexture textures[GrPaint::kMaxTextures],
                                   GrPaint* grPaint) {
-
-    SkASSERT(NULL != act);
-
     SkShader* shader = skPaint.getShader();
     if (NULL == shader) {
-        return skPaint2GrPaintNoShader(skPaint,
+        return skPaint2GrPaintNoShader(dev,
+                                       skPaint,
                                        false,
                                        constantColor,
+                                       &textures[kColorFilterTextureIdx],
                                        grPaint);
-    } else if (!skPaint2GrPaintNoShader(skPaint, true, false, grPaint)) {
+    } else if (!skPaint2GrPaintNoShader(dev, skPaint, true, false, 
+                                        &textures[kColorFilterTextureIdx], grPaint)) {
         return false;
     }
 
@@ -549,16 +565,18 @@
             // modulate the paint alpha by the shader's solid color alpha
             U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
             copy.setColor(SkColorSetA(color, newA));
-            return skPaint2GrPaintNoShader(copy,
+            return skPaint2GrPaintNoShader(dev,
+                                           copy,
                                            false,
                                            constantColor,
+                                           &textures[kColorFilterTextureIdx],
                                            grPaint);
         }
         return false;
     }
 
     GrSamplerState* sampler = grPaint->textureSampler(kShaderTextureIdx);
-    GrTexture* texture = act->set(dev, bitmap, sampler);
+    GrTexture* texture = textures[kShaderTextureIdx].set(dev, bitmap, sampler);
     if (NULL == texture) {
         SkDebugf("Couldn't convert bitmap to texture.\n");
         return false;
@@ -634,11 +652,11 @@
     CHECK_SHOULD_DRAW(draw);
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
+    SkAutoCachedTexture textures[GrPaint::kMaxTextures];
     if (!skPaint2GrPaintShader(this,
                                paint,
                                true,
-                               &act,
+                               textures,
                                &grPaint)) {
         return;
     }
@@ -670,11 +688,11 @@
     }
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
+    SkAutoCachedTexture textures[GrPaint::kMaxTextures];
     if (!skPaint2GrPaintShader(this,
                                paint,
                                true,
-                               &act,
+                               textures,
                                &grPaint)) {
         return;
     }
@@ -732,11 +750,11 @@
     }
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
+    SkAutoCachedTexture textures[GrPaint::kMaxTextures];
     if (!skPaint2GrPaintShader(this,
                                paint,
                                true,
-                               &act,
+                               textures,
                                &grPaint)) {
         return;
     }
@@ -1011,11 +1029,11 @@
     bool             doFill = true;
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
+    SkAutoCachedTexture textures[GrPaint::kMaxTextures];
     if (!skPaint2GrPaintShader(this,
                                paint,
                                true,
-                               &act,
+                               textures,
                                &grPaint)) {
         return;
     }
@@ -1249,7 +1267,8 @@
     }
 
     GrPaint grPaint;
-    if (!skPaint2GrPaintNoShader(paint, true, false, &grPaint)) {
+    SkAutoCachedTexture colorLutTexture;
+    if (!skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
         return;
     }
     GrSamplerState* sampler = grPaint.textureSampler(kBitmapTextureIdx);
@@ -1531,7 +1550,8 @@
     int h = bitmap.height();
 
     GrPaint grPaint;
-    if(!skPaint2GrPaintNoShader(paint, true, false, &grPaint)) {
+    SkAutoCachedTexture colorLutTexture;
+    if(!skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
         return;
     }
 
@@ -1576,8 +1596,9 @@
     CHECK_SHOULD_DRAW(draw);
 
     GrPaint grPaint;
+    SkAutoCachedTexture colorLutTexture;
     if (!dev->bindDeviceAsTexture(&grPaint) ||
-        !skPaint2GrPaintNoShader(paint, true, false, &grPaint)) {
+        !skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
         return;
     }
 
@@ -1682,12 +1703,14 @@
     CHECK_SHOULD_DRAW(draw);
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
+    SkAutoCachedTexture textures[GrPaint::kMaxTextures];
     // we ignore the shader if texs is null.
     if (NULL == texs) {
-        if (!skPaint2GrPaintNoShader(paint,
+        if (!skPaint2GrPaintNoShader(this,
+                                     paint,
                                      false,
                                      NULL == colors,
+                                     &textures[kColorFilterTextureIdx],
                                      &grPaint)) {
             return;
         }
@@ -1695,7 +1718,7 @@
         if (!skPaint2GrPaintShader(this,
                                    paint,
                                    NULL == colors,
-                                   &act,
+                                   textures,
                                    &grPaint)) {
             return;
         }
@@ -1795,12 +1818,11 @@
         SkDraw myDraw(draw);
 
         GrPaint grPaint;
-        SkAutoCachedTexture act;
-
+        SkAutoCachedTexture textures[GrPaint::kMaxTextures];
         if (!skPaint2GrPaintShader(this,
                                    paint,
                                    true,
-                                   &act,
+                                   textures,
                                    &grPaint)) {
             return;
         }
@@ -1824,11 +1846,11 @@
         SkDraw myDraw(draw);
 
         GrPaint grPaint;
-        SkAutoCachedTexture act;
+        SkAutoCachedTexture textures[GrPaint::kMaxTextures];
         if (!skPaint2GrPaintShader(this,
                                    paint,
                                    true,
-                                   &act,
+                                   textures,
                                    &grPaint)) {
             return;
         }
diff --git a/src/gpu/effects/GrColorTableEffect.cpp b/src/gpu/effects/GrColorTableEffect.cpp
new file mode 100644
index 0000000..5901a80
--- /dev/null
+++ b/src/gpu/effects/GrColorTableEffect.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+ #include "GrColorTableEffect.h"
+ #include "gl/GrGLProgramStage.h"
+ #include "GrProgramStageFactory.h"
+ #include "SkString.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLColorTableEffect : public GrGLProgramStage {
+public:
+    GrGLColorTableEffect(const GrProgramStageFactory& factory,
+                         const GrCustomStage& stage);
+
+    virtual void setupVariables(GrGLShaderBuilder* state,
+                                int stage) SK_OVERRIDE {}
+    virtual void emitVS(GrGLShaderBuilder* state,
+                        const char* vertexCoords) SK_OVERRIDE {}
+    virtual void emitFS(GrGLShaderBuilder* state,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const char* samplerName) SK_OVERRIDE;
+
+    virtual void initUniforms(const GrGLShaderBuilder*,
+                              const GrGLInterface*,
+                              int programID) SK_OVERRIDE {}
+
+    virtual void setData(const GrGLInterface*,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE {}
+
+    static inline StageKey GenKey(const GrCustomStage&);
+
+private:
+    typedef GrGLProgramStage INHERITED;
+};
+
+GrGLColorTableEffect::GrGLColorTableEffect(
+    const GrProgramStageFactory& factory, const GrCustomStage& stage)
+    : INHERITED(factory) {
+ }
+
+void GrGLColorTableEffect::emitFS(GrGLShaderBuilder* state,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const char* samplerName) {
+    static const float kColorScaleFactor = 255.0f / 256.0f;
+    static const float kColorOffsetFactor = 1.0f / 512.0f;
+    SkString* code = &state->fFSCode; 
+    code->appendf("\t\tvec4 coord = vec4(%s.rgb / %s.a, %s.a);\n",
+                  inputColor, inputColor, inputColor);
+    code->appendf("\t\tcoord = coord * %f + vec4(%f, %f, %f, %f);\n",
+                  kColorScaleFactor,
+                  kColorOffsetFactor, kColorOffsetFactor, kColorOffsetFactor, kColorOffsetFactor);
+    code->appendf("\t\t%s.a = texture2D(%s, vec2(coord.a, 0.125)).a;\n",
+                  outputColor, samplerName);
+    code->appendf("\t\t%s.r = texture2D(%s, vec2(coord.r, 0.375)).a;\n",
+                  outputColor, samplerName);
+    code->appendf("\t\t%s.g = texture2D(%s, vec2(coord.g, 0.625)).a;\n",
+                  outputColor, samplerName);
+    code->appendf("\t\t%s.b = texture2D(%s, vec2(coord.b, 0.875)).a;\n",
+                  outputColor, samplerName);
+    code->appendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
+}
+
+GrGLProgramStage::StageKey GrGLColorTableEffect::GenKey(
+    const GrCustomStage& s) {
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrColorTableEffect::GrColorTableEffect(GrTexture* texture)
+    : INHERITED(texture) {
+}
+
+GrColorTableEffect::~GrColorTableEffect() {
+}
+
+const GrProgramStageFactory&  GrColorTableEffect::getFactory() const {
+    return GrTProgramStageFactory<GrColorTableEffect>::getInstance();
+}
+
+bool GrColorTableEffect::isEqual(const GrCustomStage& sBase) const {
+    return INHERITED::isEqual(sBase);
+}
\ No newline at end of file
diff --git a/src/gpu/effects/GrColorTableEffect.h b/src/gpu/effects/GrColorTableEffect.h
new file mode 100644
index 0000000..f371633
--- /dev/null
+++ b/src/gpu/effects/GrColorTableEffect.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrColorTableEffect_DEFINED
+#define GrColorTableEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+#include "GrTexture.h"
+
+class GrGLColorTableEffect;
+
+/**
+ * LUT-based color transformation effect. This class implements the Gr
+ * counterpart to the SkTable_ColorFilter effect. A 256 * 4 (single-channel)
+ * LUT is used to transform the input colors of the image. 
+ */
+class GrColorTableEffect : public GrSingleTextureEffect {
+
+public:
+
+    GrColorTableEffect(GrTexture* texture);
+    virtual ~GrColorTableEffect();
+
+    static const char* Name() { return "ColorTable"; }
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+    typedef GrGLColorTableEffect GLProgramStage;
+
+private:
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+#endif
\ No newline at end of file
diff --git a/src/gpu/gl/GrGpuGL_unittest.cpp b/src/gpu/gl/GrGpuGL_unittest.cpp
index e0aa5b5..d15e087 100644
--- a/src/gpu/gl/GrGpuGL_unittest.cpp
+++ b/src/gpu/gl/GrGpuGL_unittest.cpp
@@ -1,5 +1,6 @@
 #include "GrGpuGL.h"
 
+#include "effects/GrColorTableEffect.h"
 #include "effects/GrConvolutionEffect.h"
 #include "effects/GrGradientEffects.h"
 #include "effects/GrMorphologyEffect.h"
@@ -47,6 +48,7 @@
         kSpecularPoint_EffectType,
         kSpecularSpot_EffectType,
         kSweepGradient_EffectType,
+        kColorTable_EffectType,
 
         kEffectCount
     };
@@ -207,6 +209,9 @@
             SkASSERT(ok);
             return stage;
         }
+        case kColorTable_EffectType: {
+            return SkNEW_ARGS(GrColorTableEffect, (NULL));
+        }
         default:
             GrCrash("Unexpected custom effect type");
     }