Make texture domain a helper so that it can be incorporated into other effects.

R=robertphillips@google.com, jvanverth@google.com

Author: bsalomon@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@12569 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 3eb60d2..cddabe3 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -140,8 +140,8 @@
       '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.h',
       '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.cpp',
-      '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.h',
-      '<(skia_src_path)/gpu/effects/GrTextureDomainEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrSingleTexture.h',
+      '<(skia_src_path)/gpu/effects/GrTextureDomain.cpp',
       '<(skia_src_path)/gpu/effects/GrTextureDomainEffect.h',
       '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.cpp',
       '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.h',
diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp
index 02fdf48..6133db1 100644
--- a/src/effects/SkGpuBlurUtils.cpp
+++ b/src/effects/SkGpuBlurUtils.cpp
@@ -11,7 +11,7 @@
 
 #if SK_SUPPORT_GPU
 #include "effects/GrConvolutionEffect.h"
-#include "effects/GrTextureDomainEffect.h"
+#include "effects/GrTextureDomain.h"
 #include "GrContext.h"
 #endif
 
@@ -173,7 +173,7 @@
                 srcTexture,
                 matrix,
                 domain,
-                GrTextureDomainEffect::kDecal_WrapMode,
+                GrTextureDomain::kDecal_Mode,
                 GrTextureParams::kBilerp_FilterMode));
             paint.addColorEffect(effect);
         } else {
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 3aef3de..4fd746f 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -17,7 +17,7 @@
 #include "GrRenderTarget.h"
 #include "GrStencilBuffer.h"
 #include "GrSWMaskHelper.h"
-#include "effects/GrTextureDomainEffect.h"
+#include "effects/GrTextureDomain.h"
 #include "SkRasterClip.h"
 #include "SkStrokeRec.h"
 #include "SkTLazy.h"
@@ -52,8 +52,8 @@
     drawState->addCoverageEffect(
         GrTextureDomainEffect::Create(result,
                                       mat,
-                                      GrTextureDomainEffect::MakeTexelDomain(result, domainTexels),
-                                      GrTextureDomainEffect::kDecal_WrapMode,
+                                      GrTextureDomain::MakeTexelDomain(result, domainTexels),
+                                      GrTextureDomain::kDecal_Mode,
                                       GrTextureParams::kNone_FilterMode,
                                       kPosition_GrCoordSet))->unref();
 }
@@ -365,8 +365,8 @@
     drawState->addColorEffect(
         GrTextureDomainEffect::Create(srcMask,
                                       sampleM,
-                                      GrTextureDomainEffect::MakeTexelDomain(srcMask, srcBound),
-                                      GrTextureDomainEffect::kDecal_WrapMode,
+                                      GrTextureDomain::MakeTexelDomain(srcMask, srcBound),
+                                      GrTextureDomain::kDecal_Mode,
                                       GrTextureParams::kNone_FilterMode))->unref();
     fGpu->drawSimpleRect(SkRect::Make(dstBound), NULL);
 }
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 9580d22..2555b41 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -8,7 +8,7 @@
 #include "SkGpuDevice.h"
 
 #include "effects/GrBicubicEffect.h"
-#include "effects/GrTextureDomainEffect.h"
+#include "effects/GrTextureDomain.h"
 #include "effects/GrSimpleTextureEffect.h"
 
 #include "GrContext.h"
@@ -1420,7 +1420,7 @@
         effect.reset(GrTextureDomainEffect::Create(texture,
                                                    SkMatrix::I(),
                                                    textureDomain,
-                                                   GrTextureDomainEffect::kClamp_WrapMode,
+                                                   GrTextureDomain::kClamp_Mode,
                                                    params.filterMode()));
     } else if (bicubic) {
         effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), params));
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
new file mode 100644
index 0000000..517eb6d
--- /dev/null
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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 "GrTextureDomain.h"
+#include "GrSimpleTextureEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+#include "SkFloatingPoint.h"
+
+
+GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index)
+    : fIndex(index) {
+
+    static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
+    if (domain.contains(kFullRect)) {
+        fMode = kIgnore_Mode;
+    } else {
+        fMode = mode;
+    }
+
+    if (fMode != kIgnore_Mode) {
+        // We don't currently handle domains that are empty or don't intersect the texture.
+        // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
+        // handle rects that do not intersect the [0..1]x[0..1] rect.
+        SkASSERT(domain.fLeft <= domain.fRight);
+        SkASSERT(domain.fTop <= domain.fBottom);
+        fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft);
+        fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight);
+        fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop);
+        fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
+        SkASSERT(fDomain.fLeft <= fDomain.fRight);
+        SkASSERT(fDomain.fTop <= fDomain.fBottom);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void GrTextureDomain::GLDomain::sampleTexture(GrGLShaderBuilder* builder,
+                                              const GrTextureDomain& textureDomain,
+                                              const char* outColor,
+                                              const SkString& inCoords,
+                                              const GrGLEffect::TextureSampler sampler,
+                                              const char* inModulateColor) {
+    SkASSERT(-1 == fMode || textureDomain.mode() == fMode);
+    SkDEBUGCODE(fMode = textureDomain.mode();)
+
+    if (kIgnore_Mode == textureDomain.mode()) {
+        builder->fsCodeAppendf("\t%s = ", outColor);
+        builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler,
+                                                    inCoords.c_str());
+        builder->fsCodeAppend(";\n");
+        return;
+    }
+
+    if (!fDomainUni.isValid()) {
+        const char* name;
+        SkString uniName("TexDom");
+        if (textureDomain.fIndex >= 0) {
+            uniName.appendS32(textureDomain.fIndex);
+        }
+        fDomainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                            kVec4f_GrSLType, uniName.c_str(), &name);
+        fDomainName = name;
+    }
+    if (kClamp_Mode == textureDomain.mode()) {
+        SkString clampedCoords;
+        clampedCoords.appendf("\tclamp(%s, %s.xy, %s.zw)",
+                                inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str());
+
+        builder->fsCodeAppendf("\t%s = ", outColor);
+        builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str());
+        builder->fsCodeAppend(";\n");
+    } else {
+        SkASSERT(GrTextureDomain::kDecal_Mode == textureDomain.mode());
+        // Add a block since we're going to declare variables.
+        GrGLShaderBuilder::FSBlock block(builder);
+
+        const char* domain = fDomainName.c_str();
+        if (kImagination_GrGLVendor == builder->ctxInfo().vendor()) {
+            // On the NexusS and GalaxyNexus, the other path (with the 'any'
+            // call) causes the compilation error "Calls to any function that
+            // may require a gradient calculation inside a conditional block
+            // may return undefined results". This appears to be an issue with
+            // the 'any' call since even the simple "result=black; if (any())
+            // result=white;" code fails to compile.
+            builder->fsCodeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n");
+            builder->fsCodeAppend("\tvec4 inside = ");
+            builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str());
+            builder->fsCodeAppend(";\n");
+
+            builder->fsCodeAppendf("\tfloat x = abs(2.0*(%s.x - %s.x)/(%s.z - %s.x) - 1.0);\n",
+                                    inCoords.c_str(), domain, domain, domain);
+            builder->fsCodeAppendf("\tfloat y = abs(2.0*(%s.y - %s.y)/(%s.w - %s.y) - 1.0);\n",
+                                    inCoords.c_str(), domain, domain, domain);
+            builder->fsCodeAppend("\tfloat blend = step(1.0, max(x, y));\n");
+            builder->fsCodeAppendf("\t%s = mix(inside, outside, blend);\n", outColor);
+        } else {
+            builder->fsCodeAppend("\tbvec4 outside;\n");
+            builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", inCoords.c_str(),
+                                   domain);
+            builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", inCoords.c_str(),
+                                   domain);
+            builder->fsCodeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outColor);
+            builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str());
+            builder->fsCodeAppend(";\n");
+        }
+    }
+}
+
+void GrTextureDomain::GLDomain::setData(const GrGLUniformManager& uman,
+                                        const GrTextureDomain& textureDomain,
+                                        GrSurfaceOrigin textureOrigin) {
+    SkASSERT(textureDomain.mode() == fMode);
+    if (kIgnore_Mode != textureDomain.mode()) {
+        GrGLfloat values[4] = {
+            SkScalarToFloat(textureDomain.domain().left()),
+            SkScalarToFloat(textureDomain.domain().top()),
+            SkScalarToFloat(textureDomain.domain().right()),
+            SkScalarToFloat(textureDomain.domain().bottom())
+        };
+        // vertical flip if necessary
+        if (kBottomLeft_GrSurfaceOrigin == textureOrigin) {
+            values[1] = 1.0f - values[1];
+            values[3] = 1.0f - values[3];
+            // The top and bottom were just flipped, so correct the ordering
+            // of elements so that values = (l, t, r, b).
+            SkTSwap(values[1], values[3]);
+        }
+        if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) {
+            uman.set4fv(fDomainUni, 1, values);
+            memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat));
+        }
+    }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GrGLTextureDomainEffect : public GrGLEffect {
+public:
+    GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrDrawEffect&,
+                          EffectKey,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TransformedCoordsArray&,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
+
+private:
+    GrTextureDomain::GLDomain         fGLDomain;
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory,
+                                                 const GrDrawEffect&)
+    : INHERITED(factory) {
+}
+
+void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder,
+                                       const GrDrawEffect& drawEffect,
+                                       EffectKey key,
+                                       const char* outputColor,
+                                       const char* inputColor,
+                                       const TransformedCoordsArray& coords,
+                                       const TextureSamplerArray& samplers) {
+    const GrTextureDomainEffect& effect = drawEffect.castEffect<GrTextureDomainEffect>();
+    const GrTextureDomain& domain = effect.textureDomain();
+
+    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
+    fGLDomain.sampleTexture(builder, domain, outputColor, coords2D, samplers[0], inputColor);
+}
+
+void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman,
+                                      const GrDrawEffect& drawEffect) {
+    const GrTextureDomainEffect& effect = drawEffect.castEffect<GrTextureDomainEffect>();
+    const GrTextureDomain& domain = effect.textureDomain();
+    fGLDomain.setData(uman, domain, effect.texture(0)->origin());
+}
+
+GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                      const GrGLCaps&) {
+    const GrTextureDomain& domain = drawEffect.castEffect<GrTextureDomainEffect>().textureDomain();
+    return GrTextureDomain::GLDomain::DomainKey(domain);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture,
+                                           const SkMatrix& matrix,
+                                           const SkRect& domain,
+                                           GrTextureDomain::Mode mode,
+                                           GrTextureParams::FilterMode filterMode,
+                                           GrCoordSet coordSet) {
+    static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
+    if (GrTextureDomain::kIgnore_Mode == mode ||
+        (GrTextureDomain::kClamp_Mode == mode && domain.contains(kFullRect))) {
+        return GrSimpleTextureEffect::Create(texture, matrix, filterMode);
+    } else {
+
+        AutoEffectUnref effect(SkNEW_ARGS(GrTextureDomainEffect, (texture,
+                                                                  matrix,
+                                                                  domain,
+                                                                  mode,
+                                                                  filterMode,
+                                                                  coordSet)));
+        return CreateEffectRef(effect);
+
+    }
+}
+
+GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
+                                             const SkMatrix& matrix,
+                                             const SkRect& domain,
+                                             GrTextureDomain::Mode mode,
+                                             GrTextureParams::FilterMode filterMode,
+                                             GrCoordSet coordSet)
+    : GrSingleTextureEffect(texture, matrix, filterMode, coordSet)
+    , fTextureDomain(domain, mode) {
+}
+
+GrTextureDomainEffect::~GrTextureDomainEffect() {
+
+}
+
+const GrBackendEffectFactory& GrTextureDomainEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance();
+}
+
+bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrTextureDomainEffect& s = CastEffect<GrTextureDomainEffect>(sBase);
+    return this->hasSameTextureParamsMatrixAndSourceCoords(s) &&
+           this->fTextureDomain == s.fTextureDomain;
+}
+
+void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    if (GrTextureDomain::kDecal_Mode == fTextureDomain.mode()) { // TODO: helper
+        *validFlags = 0;
+    } else {
+        this->updateConstantColorComponentsForModulation(color, validFlags);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect);
+
+GrEffectRef* GrTextureDomainEffect::TestCreate(SkRandom* random,
+                                               GrContext*,
+                                               const GrDrawTargetCaps&,
+                                               GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    SkRect domain;
+    domain.fLeft = random->nextUScalar1();
+    domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1);
+    domain.fTop = random->nextUScalar1();
+    domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
+    GrTextureDomain::Mode mode =
+        (GrTextureDomain::Mode) random->nextULessThan(GrTextureDomain::kModeCount);
+    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
+    bool bilerp = random->nextBool();
+    GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet;
+    return GrTextureDomainEffect::Create(textures[texIdx],
+                                         matrix,
+                                         domain,
+                                         mode,
+                                         bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode,
+                                         coords);
+}
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
new file mode 100644
index 0000000..f64d5c3
--- /dev/null
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -0,0 +1,169 @@
+/*
+ * 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 GrTextureDomainEffect_DEFINED
+#define GrTextureDomainEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+#include "gl/GrGLEffect.h"
+
+class GrGLShaderBuilder;
+struct SkRect;
+
+/**
+ * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped
+ * the edge of the domain or result in a vec4 of zeros (decal mode). The domain is clipped to
+ * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the
+ * domain to affect the read value unless the caller considers this when calculating the domain.
+ */
+class GrTextureDomain {
+public:
+    enum Mode {
+        kIgnore_Mode,  // Ignore the texture domain rectangle.
+        kClamp_Mode,   // Clamp texture coords to the domain rectangle.
+        kDecal_Mode,   // Treat the area outside the domain rectangle as fully transparent.
+
+        kLastMode = kDecal_Mode
+    };
+    static const int kModeCount = kLastMode + 1;
+
+    /**
+     * @param index     Pass a value >= 0 if using multiple texture domains in the same effect.
+     *                  It is used to keep inserted variables from causing name collisions.
+     */
+    GrTextureDomain(const SkRect& domain, Mode, int index = -1);
+
+    const SkRect& domain() const { return fDomain; }
+    Mode mode() const { return fMode; }
+
+    /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
+       texels neighboring the domain may be read. */
+    static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) {
+        SkScalar wInv = SK_Scalar1 / texture->width();
+        SkScalar hInv = SK_Scalar1 / texture->height();
+        SkRect result = {
+            texelRect.fLeft * wInv,
+            texelRect.fTop * hInv,
+            texelRect.fRight * wInv,
+            texelRect.fBottom * hInv
+        };
+        return result;
+    }
+
+    bool operator== (const GrTextureDomain& that) const {
+        return fMode == that.fMode && fDomain == that.fDomain;
+    }
+
+    /**
+     * A GrGLEffect subclass that corresponds to a GrEffect subclass that uses GrTextureDomain
+     * should include this helper. It generates the texture domain GLSL, produces the part of the
+     * effect key that reflects the texture domain code, and performs the uniform uploads necessary
+     * for texture domains.
+     */
+    class GLDomain {
+    public:
+        GLDomain() {
+            fPrevDomain[0] = SK_FloatNaN;
+            SkDEBUGCODE(fMode = (Mode) -1;)
+        }
+
+        /**
+         * Call this from GrGLEffect::emitCode() to sample the texture W.R.T. the domain and mode.
+         *
+         * @param outcolor  name of vec4 variable to hold the sampled color.
+         * @param inCoords  name of vec2 variable containing the coords to be used with the domain.
+         *                  It is assumed that this is a variable and not an expression.
+         * @param inModulateColor   if non-NULL the sampled color will be modulated with this
+         *                          expression before being written to outColor.
+         */
+        void sampleTexture(GrGLShaderBuilder* builder,
+                           const GrTextureDomain& textureDomain,
+                           const char* outColor,
+                           const SkString& inCoords,
+                           const GrGLEffect::TextureSampler sampler,
+                           const char* inModulateColor = NULL);
+
+        /**
+         * Call this from GrGLEffect::setData() to upload uniforms necessary for the texture domain.
+         * The rectangle is automatically adjusted to account for the texture's origin.
+         */
+        void setData(const GrGLUniformManager& uman, const GrTextureDomain& textureDomain,
+                     GrSurfaceOrigin textureOrigin);
+
+        enum {
+            kDomainKeyBits = 2, // See DomainKey().
+        };
+
+        /**
+         * GrGLEffect::GenKey() must call this and include the returned value in it's computed key.
+         * The returned will be limited to the lower kDomainKeyBits bits.
+         */
+        static GrGLEffect::EffectKey DomainKey(const GrTextureDomain& domain) {
+            GR_STATIC_ASSERT(kModeCount <= 4);
+            return domain.mode();
+        }
+
+    private:
+        SkDEBUGCODE(Mode                  fMode;)
+        GrGLUniformManager::UniformHandle fDomainUni;
+        SkString                          fDomainName;
+        GrGLfloat                         fPrevDomain[4];
+    };
+
+protected:
+    Mode    fMode;
+    SkRect  fDomain;
+    int     fIndex;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+class GrGLTextureDomainEffect;
+
+/**
+ * A basic texture effect that uses GrTextureDomain.
+ */
+class GrTextureDomainEffect : public GrSingleTextureEffect {
+
+public:
+    static GrEffectRef* Create(GrTexture*,
+                               const SkMatrix&,
+                               const SkRect& domain,
+                               GrTextureDomain::Mode,
+                               GrTextureParams::FilterMode filterMode,
+                               GrCoordSet = kLocal_GrCoordSet);
+
+    virtual ~GrTextureDomainEffect();
+
+    static const char* Name() { return "TextureDomain"; }
+
+    typedef GrGLTextureDomainEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    const GrTextureDomain& textureDomain() const { return fTextureDomain; }
+
+protected:
+    GrTextureDomain fTextureDomain;
+
+private:
+    GrTextureDomainEffect(GrTexture*,
+                          const SkMatrix&,
+                          const SkRect& domain,
+                          GrTextureDomain::Mode,
+                          GrTextureParams::FilterMode,
+                          GrCoordSet);
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrTextureDomainEffect.cpp b/src/gpu/effects/GrTextureDomainEffect.cpp
deleted file mode 100644
index 699aa72..0000000
--- a/src/gpu/effects/GrTextureDomainEffect.cpp
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * 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 "GrTextureDomainEffect.h"
-#include "GrSimpleTextureEffect.h"
-#include "GrTBackendEffectFactory.h"
-#include "gl/GrGLEffect.h"
-#include "SkFloatingPoint.h"
-
-class GrGLTextureDomainEffect : public GrGLEffect {
-public:
-    GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
-
-    virtual void emitCode(GrGLShaderBuilder*,
-                          const GrDrawEffect&,
-                          EffectKey,
-                          const char* outputColor,
-                          const char* inputColor,
-                          const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
-
-    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
-
-    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
-
-private:
-    GrGLUniformManager::UniformHandle fNameUni;
-    GrGLfloat                         fPrevDomain[4];
-
-    typedef GrGLEffect INHERITED;
-};
-
-GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory,
-                                                 const GrDrawEffect&)
-    : INHERITED(factory) {
-    fPrevDomain[0] = SK_FloatNaN;
-}
-
-void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder,
-                                       const GrDrawEffect& drawEffect,
-                                       EffectKey key,
-                                       const char* outputColor,
-                                       const char* inputColor,
-                                       const TransformedCoordsArray& coords,
-                                       const TextureSamplerArray& samplers) {
-    const GrTextureDomainEffect& texDom = drawEffect.castEffect<GrTextureDomainEffect>();
-
-    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
-    const char* domain;
-    fNameUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
-                                    kVec4f_GrSLType, "TexDom", &domain);
-    if (GrTextureDomainEffect::kClamp_WrapMode == texDom.wrapMode()) {
-
-        builder->fsCodeAppendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n",
-                                coords2D.c_str(), domain, domain);
-
-        builder->fsCodeAppendf("\t%s = ", outputColor);
-        builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], "clampCoord");
-        builder->fsCodeAppend(";\n");
-    } else {
-        SkASSERT(GrTextureDomainEffect::kDecal_WrapMode == texDom.wrapMode());
-
-        if (kImagination_GrGLVendor == builder->ctxInfo().vendor()) {
-            // On the NexusS and GalaxyNexus, the other path (with the 'any'
-            // call) causes the compilation error "Calls to any function that
-            // may require a gradient calculation inside a conditional block
-            // may return undefined results". This appears to be an issue with
-            // the 'any' call since even the simple "result=black; if (any())
-            // result=white;" code fails to compile.
-            builder->fsCodeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n");
-            builder->fsCodeAppend("\tvec4 inside = ");
-            builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords2D.c_str());
-            builder->fsCodeAppend(";\n");
-
-            builder->fsCodeAppendf("\tfloat x = abs(2.0*(%s.x - %s.x)/(%s.z - %s.x) - 1.0);\n",
-                                   coords2D.c_str(), domain, domain, domain);
-            builder->fsCodeAppendf("\tfloat y = abs(2.0*(%s.y - %s.y)/(%s.w - %s.y) - 1.0);\n",
-                                   coords2D.c_str(), domain, domain, domain);
-            builder->fsCodeAppend("\tfloat blend = step(1.0, max(x, y));\n");
-            builder->fsCodeAppendf("\t%s = mix(inside, outside, blend);\n", outputColor);
-        } else {
-            builder->fsCodeAppend("\tbvec4 outside;\n");
-            builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", coords2D.c_str(), domain);
-            builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", coords2D.c_str(), domain);
-            builder->fsCodeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outputColor);
-            builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords2D.c_str());
-            builder->fsCodeAppend(";\n");
-        }
-    }
-}
-
-void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman,
-                                      const GrDrawEffect& drawEffect) {
-    const GrTextureDomainEffect& texDom = drawEffect.castEffect<GrTextureDomainEffect>();
-    const SkRect& domain = texDom.domain();
-
-    float values[4] = {
-        SkScalarToFloat(domain.left()),
-        SkScalarToFloat(domain.top()),
-        SkScalarToFloat(domain.right()),
-        SkScalarToFloat(domain.bottom())
-    };
-    // vertical flip if necessary
-    if (kBottomLeft_GrSurfaceOrigin == texDom.texture(0)->origin()) {
-        values[1] = 1.0f - values[1];
-        values[3] = 1.0f - values[3];
-        // The top and bottom were just flipped, so correct the ordering
-        // of elements so that values = (l, t, r, b).
-        SkTSwap(values[1], values[3]);
-    }
-    if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) {
-        uman.set4fv(fNameUni, 1, values);
-        memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat));
-    }
-}
-
-GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrDrawEffect& drawEffect,
-                                                      const GrGLCaps&) {
-    return drawEffect.castEffect<GrTextureDomainEffect>().wrapMode();
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture,
-                                           const SkMatrix& matrix,
-                                           const SkRect& domain,
-                                           WrapMode wrapMode,
-                                           GrTextureParams::FilterMode filterMode,
-                                           GrCoordSet coordSet) {
-    static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
-    if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) {
-        return GrSimpleTextureEffect::Create(texture, matrix, filterMode);
-    } else {
-        SkRect clippedDomain;
-        // We don't currently handle domains that are empty or don't intersect the texture.
-        // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
-        // handle rects that do not intersect the [0..1]x[0..1] rect.
-        SkASSERT(domain.fLeft <= domain.fRight);
-        SkASSERT(domain.fTop <= domain.fBottom);
-        clippedDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft);
-        clippedDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight);
-        clippedDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop);
-        clippedDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
-        SkASSERT(clippedDomain.fLeft <= clippedDomain.fRight);
-        SkASSERT(clippedDomain.fTop <= clippedDomain.fBottom);
-
-        AutoEffectUnref effect(SkNEW_ARGS(GrTextureDomainEffect, (texture,
-                                                                  matrix,
-                                                                  clippedDomain,
-                                                                  wrapMode,
-                                                                  filterMode,
-                                                                  coordSet)));
-        return CreateEffectRef(effect);
-
-    }
-}
-
-GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
-                                             const SkMatrix& matrix,
-                                             const SkRect& domain,
-                                             WrapMode wrapMode,
-                                             GrTextureParams::FilterMode filterMode,
-                                             GrCoordSet coordSet)
-    : GrSingleTextureEffect(texture, matrix, filterMode, coordSet)
-    , fWrapMode(wrapMode)
-    , fTextureDomain(domain) {
-}
-
-GrTextureDomainEffect::~GrTextureDomainEffect() {
-
-}
-
-const GrBackendEffectFactory& GrTextureDomainEffect::getFactory() const {
-    return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance();
-}
-
-bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const {
-    const GrTextureDomainEffect& s = CastEffect<GrTextureDomainEffect>(sBase);
-    return this->hasSameTextureParamsMatrixAndSourceCoords(s) &&
-           this->fTextureDomain == s.fTextureDomain;
-}
-
-void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
-    if (kDecal_WrapMode == fWrapMode) {
-        *validFlags = 0;
-    } else {
-        this->updateConstantColorComponentsForModulation(color, validFlags);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect);
-
-GrEffectRef* GrTextureDomainEffect::TestCreate(SkRandom* random,
-                                               GrContext*,
-                                               const GrDrawTargetCaps&,
-                                               GrTexture* textures[]) {
-    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
-                                      GrEffectUnitTest::kAlphaTextureIdx;
-    SkRect domain;
-    domain.fLeft = random->nextUScalar1();
-    domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1);
-    domain.fTop = random->nextUScalar1();
-    domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
-    WrapMode wrapMode = random->nextBool() ? kClamp_WrapMode : kDecal_WrapMode;
-    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
-    bool bilerp = random->nextBool();
-    GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet;
-    return GrTextureDomainEffect::Create(textures[texIdx],
-                                         matrix,
-                                         domain,
-                                         wrapMode,
-                                         bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode,
-                                         coords);
-}
diff --git a/src/gpu/effects/GrTextureDomainEffect.h b/src/gpu/effects/GrTextureDomainEffect.h
deleted file mode 100644
index 46ee2a6..0000000
--- a/src/gpu/effects/GrTextureDomainEffect.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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 GrTextureDomainEffect_DEFINED
-#define GrTextureDomainEffect_DEFINED
-
-#include "GrSingleTextureEffect.h"
-
-class GrGLTextureDomainEffect;
-struct SkRect;
-
-/**
- * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped
- * the edge of the domain or result in a vec4 of zeros. The domain is clipped to normalized texture
- * coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the domain to affect the
- * read value unless the caller considers this when calculating the domain. TODO: This should be a
- * helper that can assist an effect rather than effect unto itself.
- */
-class GrTextureDomainEffect : public GrSingleTextureEffect {
-
-public:
-    /**
-     * If SkShader::kDecal_TileMode sticks then this enum could be replaced by SkShader::TileMode.
-     * We could also consider replacing/augmenting Decal mode with Border mode where the color
-     * outside of the domain is user-specifiable. Decal mode currently has a hard (non-lerped)
-     * transition between the border and the interior.
-     */
-    enum WrapMode {
-        kClamp_WrapMode,
-        kDecal_WrapMode,
-    };
-
-    static GrEffectRef* Create(GrTexture*,
-                               const SkMatrix&,
-                               const SkRect& domain,
-                               WrapMode,
-                               GrTextureParams::FilterMode filterMode,
-                               GrCoordSet = kLocal_GrCoordSet);
-
-    virtual ~GrTextureDomainEffect();
-
-    static const char* Name() { return "TextureDomain"; }
-
-    typedef GrGLTextureDomainEffect GLEffect;
-
-    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
-    const SkRect& domain() const { return fTextureDomain; }
-    WrapMode wrapMode() const { return fWrapMode; }
-
-    /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
-       texels neighboring the domain may be read. */
-    static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) {
-        SkScalar wInv = SK_Scalar1 / texture->width();
-        SkScalar hInv = SK_Scalar1 / texture->height();
-        SkRect result = {
-            texelRect.fLeft * wInv,
-            texelRect.fTop * hInv,
-            texelRect.fRight * wInv,
-            texelRect.fBottom * hInv
-        };
-        return result;
-    }
-
-protected:
-    WrapMode fWrapMode;
-    SkRect   fTextureDomain;
-
-private:
-    GrTextureDomainEffect(GrTexture*,
-                          const SkMatrix&,
-                          const SkRect& domain,
-                          WrapMode,
-                          GrTextureParams::FilterMode filterMode,
-                          GrCoordSet);
-
-    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
-
-    GR_DECLARE_EFFECT_TEST;
-
-    typedef GrSingleTextureEffect INHERITED;
-};
-
-#endif
diff --git a/src/gpu/gl/GrGLEffect.h b/src/gpu/gl/GrGLEffect.h
index b680738..1cc3df2 100644
--- a/src/gpu/gl/GrGLEffect.h
+++ b/src/gpu/gl/GrGLEffect.h
@@ -40,6 +40,7 @@
 public:
     typedef GrBackendEffectFactory::EffectKey EffectKey;
     typedef GrGLProgramEffects::TransformedCoordsArray TransformedCoordsArray;
+    typedef GrGLProgramEffects::TextureSampler TextureSampler;
     typedef GrGLProgramEffects::TextureSamplerArray TextureSamplerArray;
 
     enum {
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index 52c24ae..103efa5 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -208,6 +208,25 @@
 
     const GrGLContextInfo& ctxInfo() const;
 
+    /**
+     * Helper for begining and ending a block in the fragment code. TODO: Make GrGLShaderBuilder
+     * aware of all blocks and turn single \t's into the correct number of tabs (or spaces) so that
+     * our shaders print pretty without effect writers tracking indentation.
+     */
+    class FSBlock {
+    public:
+        FSBlock(GrGLShaderBuilder* builder) : fBuilder(builder) {
+            SkASSERT(NULL != builder);
+            fBuilder->fsCodeAppend("\t{\n");
+        }
+
+        ~FSBlock() {
+            fBuilder->fsCodeAppend("\t}\n");
+        }
+    private:
+        GrGLShaderBuilder* fBuilder;
+    };
+
 protected:
     GrGpuGL* gpu() const { return fGpu; }