Implement morphology as a custom effect

Review URL: http://codereview.appspot.com/6250073/



git-svn-id: http://skia.googlecode.com/svn/trunk@4102 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 9a78a6e..a49aa61 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -95,18 +95,6 @@
     s->appendS32(stage);
 }
 
-inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) {
-    *k = "uKernel";
-    k->appendS32(stage);
-    *i = "uImageIncrement";
-    i->appendS32(stage);
-}
-
-inline void image_increment_param_name(int stage, GrStringBuilder* i) {
-    *i = "uImageIncrement";
-    i->appendS32(stage);
-}
-
 inline void tex_domain_name(int stage, GrStringBuilder* s) {
     *s = "uTexDom";
     s->appendS32(stage);
@@ -629,7 +617,7 @@
                     const GrProgramStageFactory& factory =
                         customStages[s]->getFactory();
                     programData->fCustomStage[s] =
-                        factory.createGLInstance(customStages[s]);
+                        factory.createGLInstance(*customStages[s]);
                 }
                 this->genStageCode(gl,
                                    s,
@@ -752,7 +740,7 @@
                         const GrProgramStageFactory& factory =
                             customStages[s]->getFactory();
                         programData->fCustomStage[s] =
-                            factory.createGLInstance(customStages[s]);
+                            factory.createGLInstance(*customStages[s]);
                     }
                     this->genStageCode(gl, s,
                         fProgramDesc.fStages[s],
@@ -1186,21 +1174,6 @@
                 GrAssert(kUnusedUniform != locations.fTexDomUni);
             }
 
-            GrStringBuilder kernelName, imageIncrementName;
-            convolve_param_names(s, &kernelName, &imageIncrementName);
-            if (kUseUniform == locations.fKernelUni) {
-                GL_CALL_RET(locations.fKernelUni,
-                            GetUniformLocation(progID, kernelName.c_str()));
-                GrAssert(kUnusedUniform != locations.fKernelUni);
-            }
-
-            if (kUseUniform == locations.fImageIncrementUni) {
-                GL_CALL_RET(locations.fImageIncrementUni, 
-                            GetUniformLocation(progID, 
-                                               imageIncrementName.c_str()));
-                GrAssert(kUnusedUniform != locations.fImageIncrementUni);
-            }
-
             if (NULL != programData->fCustomStage[s]) {
                 programData->fCustomStage[s]->
                     initUniforms(gl.interface(), progID);
@@ -1411,65 +1384,6 @@
 
 }
 
-void genMorphologyVS(int stageNum,
-                     const StageDesc& desc,
-                     GrGLShaderBuilder* segments,
-                     GrGLProgram::StageUniLocations* locations,
-                     const char** imageIncrementName,
-                     const char* varyingVSName) {
-
-    GrStringBuilder iiName;
-    image_increment_param_name(stageNum, &iiName);
-    const GrGLShaderVar* imgInc =
-        &segments->addUniform(
-            GrGLShaderBuilder::kBoth_VariableLifetime, kVec2f_GrSLType, 
-            iiName.c_str());
-    *imageIncrementName = imgInc->getName().c_str();
-
-    locations->fImageIncrementUni = kUseUniform;
-    segments->fVSCode.appendf("\t%s -= vec2(%d, %d) * %s;\n",
-                                  varyingVSName, desc.fKernelWidth,
-                                  desc.fKernelWidth, *imageIncrementName);
-}
- 
-void genMorphologyFS(int stageNum,
-                     const StageDesc& desc,
-                     GrGLShaderBuilder* segments,
-                     const char* samplerName,
-                     const char* imageIncrementName,
-                     const char* fsOutColor,
-                     GrStringBuilder& texFunc) {
-    GrStringBuilder valueVar("value");
-    valueVar.appendS32(stageNum);
-    GrStringBuilder coordVar("coord");
-    coordVar.appendS32(stageNum);
-    bool isDilate = StageDesc::kDilate_FetchMode == desc.fFetchMode;
-
-   if (isDilate) {
-        segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n",
-                                  valueVar.c_str());
-    } else {
-        segments->fFSCode.appendf("\tvec4 %s = vec4(1, 1, 1, 1);\n",
-                                  valueVar.c_str());
-    }
-    segments->fFSCode.appendf("\tvec2 %s = %s;\n", 
-                              coordVar.c_str(),
-                              segments->fSampleCoords.c_str());
-    segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n",
-                              desc.fKernelWidth * 2 + 1);
-    segments->fFSCode.appendf("\t\t%s = %s(%s, %s(%s, %s)%s);\n",
-                              valueVar.c_str(), isDilate ? "max" : "min",
-                              valueVar.c_str(), texFunc.c_str(),
-                              samplerName, coordVar.c_str(),
-                              segments->fSwizzle.c_str());
-    segments->fFSCode.appendf("\t\t%s += %s;\n",
-                              coordVar.c_str(),
-                              imageIncrementName);
-    segments->fFSCode.appendf("\t}\n");
-    segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
-                              valueVar.c_str(), segments->fModulate.c_str());
-}
-
 }
 
 void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
@@ -1562,12 +1476,6 @@
 
     GrGLShaderVar* kernel = NULL;
     const char* imageIncrementName = NULL;
-    if (StageDesc::kDilate_FetchMode == desc.fFetchMode ||
-               StageDesc::kErode_FetchMode == desc.fFetchMode) {
-        genMorphologyVS(stageNum, desc, segments, locations,
-                        &imageIncrementName, varyingVSName);
-    }
-
     if (NULL != customStage) {
         segments->fVSCode.appendf("\t{ // stage %d %s\n",
                                   stageNum, customStage->name());
@@ -1677,15 +1585,6 @@
             gen2x2FS(stageNum, segments, locations,
                 samplerName.c_str(), texelSizeName, fsOutColor, texFunc);
             break;
-        case StageDesc::kConvolution_FetchMode:
-            GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
-            break;
-        case StageDesc::kDilate_FetchMode:
-        case StageDesc::kErode_FetchMode:
-            GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
-            genMorphologyFS(stageNum, desc, segments,
-                samplerName.c_str(), imageIncrementName, fsOutColor, texFunc);
-            break;
         default:
             if (desc.fInConfigFlags & kMulByAlphaMask) {
                 // only one of the mul by alpha flags should be set
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 189e876..bfb1f1c 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -106,13 +106,10 @@
                 kCustomTextureDomain_OptFlagBit = 1 << 2,
                 kIsEnabled_OptFlagBit           = 1 << 7
             };
-            // Convolution is obsolete; left in for testing only
+
             enum FetchMode {
                 kSingle_FetchMode,
                 k2x2_FetchMode,
-                kConvolution_FetchMode,
-                kErode_FetchMode,
-                kDilate_FetchMode,
 
                 kFetchModeCnt,
             };
@@ -179,7 +176,6 @@
             uint8_t fInConfigFlags; // bitfield of InConfigFlags values
             uint8_t fFetchMode;     // casts to enum FetchMode
             uint8_t fCoordMapping;  // casts to enum CoordMapping
-            uint8_t fKernelWidth;
 
             /** Non-zero if user-supplied code will write the stage's
                 contribution to the fragment shader. */
@@ -268,16 +264,12 @@
         GrGLint fSamplerUni;
         GrGLint fRadial2Uni;
         GrGLint fTexDomUni;
-        GrGLint fKernelUni;
-        GrGLint fImageIncrementUni;
         void reset() {
             fTextureMatrixUni = kUnusedUniform;
             fNormalizedTexelSizeUni = kUnusedUniform;
             fSamplerUni = kUnusedUniform;
             fRadial2Uni = kUnusedUniform;
             fTexDomUni = kUnusedUniform;
-            fKernelUni = kUnusedUniform;
-            fImageIncrementUni = kUnusedUniform;
         }
     };
 
diff --git a/src/gpu/gl/GrGLProgramStage.cpp b/src/gpu/gl/GrGLProgramStage.cpp
index 70d1186..487b468 100644
--- a/src/gpu/gl/GrGLProgramStage.cpp
+++ b/src/gpu/gl/GrGLProgramStage.cpp
@@ -25,11 +25,9 @@
 
 }
 
-void GrGLProgramStage::setData(const GrGLInterface*, 
-                               const GrGLTexture&, 
-                               GrCustomStage*,
+void GrGLProgramStage::setData(const GrGLInterface*,
+                               const GrGLTexture&,
+                               const GrCustomStage&,
                                int stageNum) {
-
-
 }
 
diff --git a/src/gpu/gl/GrGLProgramStage.h b/src/gpu/gl/GrGLProgramStage.h
index 52d7200..e10d13b 100644
--- a/src/gpu/gl/GrGLProgramStage.h
+++ b/src/gpu/gl/GrGLProgramStage.h
@@ -32,7 +32,12 @@
 class GrGLProgramStage {
 
 public:
-    typedef GrCustomStage::StageKey StageKey ;
+    typedef GrCustomStage::StageKey StageKey;
+    enum {
+        // the number of bits in StageKey available to GenKey
+        kProgramStageKeyBits = GrProgramStageFactory::kProgramStageKeyBits,
+    };
+
     // TODO: redundant with GrGLProgram.cpp
     enum {
         kUnusedUniform = -1,
@@ -79,7 +84,7 @@
         created in emit*(). */
     virtual void setData(const GrGLInterface* gl,
                          const GrGLTexture& texture,
-                         GrCustomStage* stage,
+                         const GrCustomStage& stage,
                          int stageNum);
 
     const char* name() const { return fFactory.name(); }
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 8b28c9f..29c7900 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -2102,9 +2102,6 @@
         case GrSamplerState::k4x4Downsample_Filter:
             return GR_GL_LINEAR;
         case GrSamplerState::kNearest_Filter:
-        case GrSamplerState::kConvolution_Filter:
-        case GrSamplerState::kErode_Filter:
-        case GrSamplerState::kDilate_Filter:
             return GR_GL_NEAREST;
         default:
             GrAssert(!"Unknown filter type");
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 2ac26d5..2448fa4 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -7,8 +7,10 @@
 
 #include "GrGpuGL.h"
 
-#include "GrBinHashKey.h"
 #include "effects/GrConvolutionEffect.h"
+#include "effects/GrMorphologyEffect.h"
+
+#include "GrBinHashKey.h"
 #include "GrCustomStage.h"
 #include "GrGLProgramStage.h"
 #include "GrGLSL.h"
@@ -176,6 +178,75 @@
     return r->nextF() > .5f;
 }
 
+typedef GrGLProgram::StageDesc StageDesc;
+// TODO: Effects should be able to register themselves for inclusion in the
+// randomly generated shaders. They should be able to configure themselves
+// randomly.
+GrCustomStage* create_random_effect(StageDesc* stageDesc,
+                                    GrRandom* random) {
+    enum EffectType {
+        kConvolution_EffectType,
+        kErode_EffectType,
+        kDilate_EffectType,
+
+        kEffectCount
+    };
+
+    // TODO: Remove this when generator doesn't apply this non-custom-stage
+    // notion to custom stages automatically.
+    static const uint32_t kMulByAlphaMask =
+        StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
+        StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
+
+    static const Gr1DKernelEffect::Direction gKernelDirections[] = {
+        Gr1DKernelEffect::kX_Direction,
+        Gr1DKernelEffect::kY_Direction
+    };
+
+    // TODO: When matrices are property of the custom-stage then remove the
+    // no-persp flag code below.
+    int effect = random_int(random, kEffectCount);
+    switch (effect) {
+        case kConvolution_EffectType: {
+            int direction = random_int(random, 2);
+            int kernelRadius = random_int(random, 1, 4);
+            float kernel[GrConvolutionEffect::kMaxKernelWidth];
+            for (int i = 0; i < GrConvolutionEffect::kMaxKernelWidth; i++) {
+                kernel[i] = random->nextF();
+            }
+            // does not work with perspective or mul-by-alpha-mask
+            stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+            stageDesc->fInConfigFlags &= ~kMulByAlphaMask;
+            return new GrConvolutionEffect(gKernelDirections[direction],
+                                           kernelRadius,
+                                           kernel);
+            }
+        case kErode_EffectType: {
+            int direction = random_int(random, 2);
+            int kernelRadius = random_int(random, 1, 4);
+            // does not work with perspective or mul-by-alpha-mask
+            stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+            stageDesc->fInConfigFlags &= ~kMulByAlphaMask;
+            return new GrMorphologyEffect(gKernelDirections[direction],
+                                          kernelRadius,
+                                          GrContext::kErode_MorphologyType);
+            }
+        case kDilate_EffectType: {
+            int direction = random_int(random, 2);
+            int kernelRadius = random_int(random, 1, 4);
+            // does not work with perspective or mul-by-alpha-mask
+            stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+            stageDesc->fInConfigFlags &= ~kMulByAlphaMask;
+            return new GrMorphologyEffect(gKernelDirections[direction],
+                                          kernelRadius,
+                                          GrContext::kDilate_MorphologyType);
+            }
+        default:
+            GrCrash("Unexpected custom effect type");
+    }
+    return NULL;
+}
+
 }
 
 bool GrGpuGL::programUnitTest() {
@@ -250,7 +321,7 @@
             pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
         }
 
-        GrCustomStage* customStages[GrDrawState::kNumStages];
+        SkAutoTUnref<GrCustomStage> customStages[GrDrawState::kNumStages];
 
         for (int s = 0; s < GrDrawState::kNumStages; ++s) {
             // enable the stage?
@@ -270,56 +341,34 @@
             StageDesc& stage = pdesc.fStages[s];
 
             stage.fCustomStageKey = 0;
-            customStages[s] = NULL;
 
             stage.fOptFlags = STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))];
             stage.fInConfigFlags = IN_CONFIG_FLAGS[random_int(&random, GR_ARRAY_COUNT(IN_CONFIG_FLAGS))];
             stage.fCoordMapping =  random_int(&random, StageDesc::kCoordMappingCnt);
             stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt);
-            // convolution shaders don't work with persp tex matrix
-            if (stage.fFetchMode == StageDesc::kConvolution_FetchMode ||
-                stage.fFetchMode == StageDesc::kDilate_FetchMode ||
-                stage.fFetchMode == StageDesc::kErode_FetchMode) {
-                stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
-            }
             stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
             static const uint32_t kMulByAlphaMask =
                 StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
                 StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
 
-            switch (stage.fFetchMode) {
-                case StageDesc::kSingle_FetchMode:
-                    stage.fKernelWidth = 0;
-                    break;
-                case StageDesc::kConvolution_FetchMode:
-                case StageDesc::kDilate_FetchMode:
-                case StageDesc::kErode_FetchMode:
-                    stage.fKernelWidth = random_int(&random, 2, 8);
-                    stage.fInConfigFlags &= ~kMulByAlphaMask;
-                    break;
-                case StageDesc::k2x2_FetchMode:
-                    stage.fKernelWidth = 0;
-                    stage.fInConfigFlags &= ~kMulByAlphaMask;
-                    break;
+            if (StageDesc::k2x2_FetchMode == stage.fFetchMode) {
+                stage.fInConfigFlags &= ~kMulByAlphaMask;
             }
 
-            // TODO: is there a more elegant way to express this?
-            if (stage.fFetchMode == StageDesc::kConvolution_FetchMode) {
-                int direction = random_int(&random, 2);
-                float kernel[MAX_KERNEL_WIDTH];
-                for (int i = 0; i < stage.fKernelWidth; i++) {
-                    kernel[i] = random.nextF();
+            bool useCustomEffect = random_bool(&random);
+            if (useCustomEffect) {
+                customStages[s].reset(create_random_effect(&stage, &random));
+                if (NULL != customStages[s]) {
+                    stage.fCustomStageKey =
+                        customStages[s]->getFactory().glStageKey(*customStages[s]);
                 }
-                customStages[s] = new GrConvolutionEffect(
-                    (GrSamplerState::FilterDirection)direction,
-                    stage.fKernelWidth, kernel);
-                stage.fCustomStageKey =
-                    customStages[s]->getFactory().glStageKey(customStages[s]);
             }
         }
         CachedData cachedData;
-        if (!program.genProgram(this->glContextInfo(), customStages,
-                                &cachedData)) {
+        GR_STATIC_ASSERT(sizeof(customStages) ==
+                         GrDrawState::kNumStages * sizeof(GrCustomStage*));
+        GrCustomStage** stages = reinterpret_cast<GrCustomStage**>(&customStages);
+        if (!program.genProgram(this->glContextInfo(), stages, &cachedData)) {
             return false;
         }
         DeleteProgram(this->glInterface(), &cachedData);
@@ -469,32 +518,6 @@
     }
 }
 
-void GrGpuGL::flushConvolution(int s) {
-    const GrSamplerState& sampler = this->getDrawState().getSampler(s);
-    int kernelUni = fProgramData->fUniLocations.fStages[s].fKernelUni;
-    if (GrGLProgram::kUnusedUniform != kernelUni) {
-        GL_CALL(Uniform1fv(kernelUni, sampler.getKernelWidth(),
-                           sampler.getKernel()));
-    }
-    int imageIncrementUni = fProgramData->fUniLocations.fStages[s].fImageIncrementUni;
-    if (GrGLProgram::kUnusedUniform != imageIncrementUni) {
-        const GrGLTexture* texture =
-            static_cast<const GrGLTexture*>(this->getDrawState().getTexture(s));
-        float imageIncrement[2] = { 0 };
-        switch (sampler.getFilterDirection()) {
-            case GrSamplerState::kX_FilterDirection:
-                imageIncrement[0] = 1.0f / texture->width();
-                break;
-            case GrSamplerState::kY_FilterDirection:
-                imageIncrement[1] = 1.0f / texture->height();
-                break;
-            default:
-                GrCrash("Unknown filter direction.");
-        }
-        GL_CALL(Uniform2fv(imageIncrementUni, 1, imageIncrement));
-    }
-}
-
 void GrGpuGL::flushTexelSize(int s) {
     const int& uni = fProgramData->fUniLocations.fStages[s].fNormalizedTexelSizeUni;
     if (GrGLProgram::kUnusedUniform != uni) {
@@ -692,8 +715,6 @@
 
             this->flushRadial2(s);
 
-            this->flushConvolution(s);
-
             this->flushTexelSize(s);
 
             this->flushTextureDomain(s);
@@ -706,7 +727,7 @@
                         this->getDrawState().getTexture(s));
                 fProgramData->fCustomStage[s]->setData(
                     this->glInterface(), *texture,
-                    sampler.getCustomStage(), s);
+                    *sampler.getCustomStage(), s);
             }
         }
     }
@@ -875,7 +896,7 @@
     GrCustomStage* customStage = sampler.getCustomStage();
     if (customStage) {
         const GrProgramStageFactory& factory = customStage->getFactory();
-        stage->fCustomStageKey = factory.glStageKey(customStage);
+        stage->fCustomStageKey = factory.glStageKey(*customStage);
         customStages[index] = customStage;
     } else {
         stage->fCustomStageKey = 0;
@@ -1027,16 +1048,6 @@
                 case GrSamplerState::k4x4Downsample_Filter:
                     stage.fFetchMode = StageDesc::k2x2_FetchMode;
                     break;
-                // performs fKernelWidth texture2D()s
-                case GrSamplerState::kConvolution_Filter:
-                    stage.fFetchMode = StageDesc::kConvolution_FetchMode;
-                    break;
-                case GrSamplerState::kDilate_Filter:
-                    stage.fFetchMode = StageDesc::kDilate_FetchMode;
-                    break;
-                case GrSamplerState::kErode_Filter:
-                    stage.fFetchMode = StageDesc::kErode_FetchMode;
-                    break;
                 default:
                     GrCrash("Unexpected filter!");
                     break;
@@ -1083,13 +1094,6 @@
                 }
             }
 
-            if (sampler.getFilter() == GrSamplerState::kDilate_Filter ||
-                sampler.getFilter() == GrSamplerState::kErode_Filter) {
-                stage.fKernelWidth = sampler.getKernelWidth();
-            } else {
-                stage.fKernelWidth = 0;
-            }
-
             setup_custom_stage(&stage, sampler, customStages,
                                &fCurrentProgram, s);
 
@@ -1098,7 +1102,6 @@
             stage.fCoordMapping     = (StageDesc::CoordMapping) 0;
             stage.fInConfigFlags    = 0;
             stage.fFetchMode        = (StageDesc::FetchMode) 0;
-            stage.fKernelWidth      = 0;
             stage.fCustomStageKey   = 0;
             customStages[s] = NULL;
         }