converted GrMagnifierEffect to SkSL

Bug: skia:
Change-Id: I6dc14ac66d5b911117e71fa23fef49a897082781
Reviewed-on: https://skia-review.googlesource.com/71342
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.cpp b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
index 4a872e9..be7aded 100644
--- a/src/effects/GrAlphaThresholdFragmentProcessor.cpp
+++ b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
@@ -23,6 +23,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLAlphaThresholdFragmentProcessor : public GrGLSLFragmentProcessor {
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp
index 962bf05..bb9bff2 100644
--- a/src/effects/GrCircleBlurFragmentProcessor.cpp
+++ b/src/effects/GrCircleBlurFragmentProcessor.cpp
@@ -252,6 +252,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor {
@@ -296,7 +297,8 @@
         (void)textureRadius;
         auto solidRadius = _outer.solidRadius();
         (void)solidRadius;
-        UniformHandle& blurProfileSampler = fBlurProfileSamplerVar;
+        GrSurfaceProxy& blurProfileSamplerProxy = *_outer.textureSampler(0).proxy();
+        GrTexture& blurProfileSampler = *blurProfileSamplerProxy.priv().peekTexture();
         (void)blurProfileSampler;
         UniformHandle& circleData = fCircleDataVar;
         (void)circleData;
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index c5b3396..ff5bf3c 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -23,6 +23,7 @@
 #include "GrContext.h"
 #include "GrCoordTransform.h"
 #include "GrTexture.h"
+#include "effects/GrMagnifierEffect.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramDataManager.h"
@@ -47,256 +48,6 @@
                                                            cropRect));
 }
 
-#if SK_SUPPORT_GPU
-class GrMagnifierEffect : public GrFragmentProcessor {
-public:
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     const SkIRect& bounds,
-                                                     const SkRect& srcRect,
-                                                     float xInvZoom,
-                                                     float yInvZoom,
-                                                     float xInvInset,
-                                                     float yInvInset) {
-        return std::unique_ptr<GrFragmentProcessor>(
-                new GrMagnifierEffect(std::move(proxy), bounds, srcRect, xInvZoom, yInvZoom,
-                                      xInvInset, yInvInset));
-    }
-
-    ~GrMagnifierEffect() override {}
-
-    const char* name() const override { return "Magnifier"; }
-
-    std::unique_ptr<GrFragmentProcessor> clone() const override {
-        return std::unique_ptr<GrFragmentProcessor>(new GrMagnifierEffect(*this));
-    }
-
-    SkString dumpInfo() const override {
-        SkString str;
-        str.appendf("Texture: %d", fTextureSampler.proxy()->uniqueID().asUInt());
-        return str;
-    }
-
-    const SkIRect& bounds() const { return fBounds; }    // Bounds of source image.
-    const SkRect& srcRect() const { return fSrcRect; }
-
-    // Scale to apply to zoomed pixels (srcRect size / bounds size).
-    float xInvZoom() const { return fXInvZoom; }
-    float yInvZoom() const { return fYInvZoom; }
-
-    // 1/radius over which to transition from unzoomed to zoomed pixels (bounds size / inset).
-    float xInvInset() const { return fXInvInset; }
-    float yInvInset() const { return fYInvInset; }
-
-private:
-    GrMagnifierEffect(sk_sp<GrTextureProxy> proxy,
-                      const SkIRect& bounds,
-                      const SkRect& srcRect,
-                      float xInvZoom,
-                      float yInvZoom,
-                      float xInvInset,
-                      float yInvInset)
-            : INHERITED{kGrMagnifierEffect_ClassID,
-                        ModulateByConfigOptimizationFlags(proxy->config())}
-            // TODO: no GrSamplerState::Filter::kBilerp?
-            , fCoordTransform(proxy.get())
-            , fTextureSampler(std::move(proxy))
-            , fBounds(bounds)
-            , fSrcRect(srcRect)
-            , fXInvZoom(xInvZoom)
-            , fYInvZoom(yInvZoom)
-            , fXInvInset(xInvInset)
-            , fYInvInset(yInvInset) {
-        this->addCoordTransform(&fCoordTransform);
-        this->addTextureSampler(&fTextureSampler);
-    }
-
-    explicit GrMagnifierEffect(const GrMagnifierEffect& that)
-            : INHERITED(kGrMagnifierEffect_ClassID, that.optimizationFlags())
-            , fCoordTransform(that.fCoordTransform)
-            , fTextureSampler(that.fTextureSampler)
-            , fBounds(that.fBounds)
-            , fSrcRect(that.fSrcRect)
-            , fXInvZoom(that.fXInvZoom)
-            , fYInvZoom(that.fYInvZoom)
-            , fXInvInset(that.fXInvInset)
-            , fYInvInset(that.fYInvInset) {
-        this->addCoordTransform(&fCoordTransform);
-        this->addTextureSampler(&fTextureSampler);
-    }
-
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
-    bool onIsEqual(const GrFragmentProcessor&) const override;
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
-    GrCoordTransform fCoordTransform;
-    TextureSampler fTextureSampler;
-    SkIRect fBounds;
-    SkRect  fSrcRect;
-    float fXInvZoom;
-    float fYInvZoom;
-    float fXInvInset;
-    float fYInvInset;
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-// For brevity
-typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
-
-class GrGLMagnifierEffect : public GrGLSLFragmentProcessor {
-public:
-    void emitCode(EmitArgs&) override;
-
-    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
-
-private:
-    UniformHandle       fOffsetVar;
-    UniformHandle       fInvZoomVar;
-    UniformHandle       fInvInsetVar;
-    UniformHandle       fBoundsVar;
-
-    typedef GrGLSLFragmentProcessor INHERITED;
-};
-
-void GrGLMagnifierEffect::emitCode(EmitArgs& args) {
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    fOffsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                            kHalf2_GrSLType, "Offset");
-    fInvZoomVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                             kHalf2_GrSLType, "InvZoom");
-    fInvInsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                              kHalf2_GrSLType, "InvInset");
-    fBoundsVar = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                            kHalf4_GrSLType, "Bounds");
-
-    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-    SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-    fragBuilder->codeAppendf("\t\tfloat2 coord = %s;\n", coords2D.c_str());
-    fragBuilder->codeAppendf("\t\tfloat2 zoom_coord = %s + %s * %s;\n",
-                             uniformHandler->getUniformCStr(fOffsetVar),
-                             coords2D.c_str(),
-                             uniformHandler->getUniformCStr(fInvZoomVar));
-    const char* bounds = uniformHandler->getUniformCStr(fBoundsVar);
-    fragBuilder->codeAppendf("\t\tfloat2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds);
-    fragBuilder->codeAppendf("\t\tdelta = min(delta, half2(1.0, 1.0) - delta);\n");
-    fragBuilder->codeAppendf("\t\tdelta = delta * %s;\n",
-                             uniformHandler->getUniformCStr(fInvInsetVar));
-
-    fragBuilder->codeAppend("\t\thalf weight = 0.0;\n");
-    fragBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n");
-    fragBuilder->codeAppend("\t\t\tdelta = half2(2.0, 2.0) - delta;\n");
-    fragBuilder->codeAppend("\t\t\thalf dist = length(delta);\n");
-    fragBuilder->codeAppend("\t\t\tdist = max(2.0 - dist, 0.0);\n");
-    fragBuilder->codeAppend("\t\t\tweight = min(dist * dist, 1.0);\n");
-    fragBuilder->codeAppend("\t\t} else {\n");
-    fragBuilder->codeAppend("\t\t\tfloat2 delta_squared = delta * delta;\n");
-    fragBuilder->codeAppend("\t\t\tweight = min(min(delta_squared.x, delta_squared.y), 1.0);\n");
-    fragBuilder->codeAppend("\t\t}\n");
-
-    fragBuilder->codeAppend("\t\tfloat2 mix_coord = mix(coord, zoom_coord, weight);\n");
-    fragBuilder->codeAppend("\t\thalf4 output_color = ");
-    fragBuilder->appendTextureLookup(args.fTexSamplers[0], "mix_coord", kHalf2_GrSLType);
-    fragBuilder->codeAppend(";\n");
-
-    fragBuilder->codeAppendf("\t\t%s = output_color;\n", args.fOutputColor);
-    fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
-}
-
-void GrGLMagnifierEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                    const GrFragmentProcessor& effect) {
-    const GrMagnifierEffect& zoom = effect.cast<GrMagnifierEffect>();
-
-    GrSurfaceProxy* proxy = zoom.textureSampler(0).proxy();
-    GrTexture* tex = proxy->priv().peekTexture();
-
-    SkScalar invW = 1.0f / tex->width();
-    SkScalar invH = 1.0f / tex->height();
-
-    {
-        SkScalar y = zoom.srcRect().y() * invH;
-        if (proxy->origin() != kTopLeft_GrSurfaceOrigin) {
-            y = 1.0f - (zoom.srcRect().height() / zoom.bounds().height()) - y;
-        }
-
-        pdman.set2f(fOffsetVar, zoom.srcRect().x() * invW, y);
-    }
-
-    pdman.set2f(fInvZoomVar, zoom.xInvZoom(), zoom.yInvZoom());
-    pdman.set2f(fInvInsetVar, zoom.xInvInset(), zoom.yInvInset());
-
-    {
-        SkScalar y = zoom.bounds().y() * invH;
-        if (proxy->origin() != kTopLeft_GrSurfaceOrigin) {
-            y = 1.0f - zoom.bounds().height() * invH;
-        }
-
-        pdman.set4f(fBoundsVar,
-                    zoom.bounds().x() * invW,
-                    y,
-                    SkIntToScalar(tex->width()) / zoom.bounds().width(),
-                    SkIntToScalar(tex->height()) / zoom.bounds().height());
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-void GrMagnifierEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                              GrProcessorKeyBuilder* b) const {
-    GrGLMagnifierEffect::GenKey(*this, caps, b);
-}
-
-GrGLSLFragmentProcessor* GrMagnifierEffect::onCreateGLSLInstance() const {
-    return new GrGLMagnifierEffect;
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMagnifierEffect);
-
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrMagnifierEffect::TestCreate(GrProcessorTestData* d) {
-    sk_sp<GrTextureProxy> proxy = d->textureProxy(0);
-    const int kMaxWidth = 200;
-    const int kMaxHeight = 200;
-    const SkScalar kMaxInset = 20.0f;
-    uint32_t width = d->fRandom->nextULessThan(kMaxWidth);
-    uint32_t height = d->fRandom->nextULessThan(kMaxHeight);
-    SkScalar inset = d->fRandom->nextRangeScalar(1.0f, kMaxInset);
-
-    SkIRect bounds = SkIRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight));
-    SkRect srcRect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
-
-    auto effect = GrMagnifierEffect::Make(std::move(proxy),
-                                          bounds,
-                                          srcRect,
-                                          srcRect.width() / bounds.width(),
-                                          srcRect.height() / bounds.height(),
-                                          bounds.width() / inset,
-                                          bounds.height() / inset);
-    SkASSERT(effect);
-    return effect;
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-bool GrMagnifierEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
-    const GrMagnifierEffect& s = sBase.cast<GrMagnifierEffect>();
-    return (this->fBounds == s.fBounds &&
-            this->fSrcRect == s.fSrcRect &&
-            this->fXInvZoom == s.fXInvZoom &&
-            this->fYInvZoom == s.fYInvZoom &&
-            this->fXInvInset == s.fXInvInset &&
-            this->fYInvInset == s.fYInvInset);
-}
-
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
 
 SkMagnifierImageFilter::SkMagnifierImageFilter(const SkRect& srcRect,
diff --git a/src/gpu/effects/GrAARectEffect.cpp b/src/gpu/effects/GrAARectEffect.cpp
index 0e49e0f..48ed644 100644
--- a/src/gpu/effects/GrAARectEffect.cpp
+++ b/src/gpu/effects/GrAARectEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLAARectEffect : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrArithmeticFP.cpp b/src/gpu/effects/GrArithmeticFP.cpp
index 41de643..a905705 100644
--- a/src/gpu/effects/GrArithmeticFP.cpp
+++ b/src/gpu/effects/GrArithmeticFP.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLArithmeticFP : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
index 6dfb6c5..a531d42 100644
--- a/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLBlurredEdgeFragmentProcessor : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrCircleEffect.cpp b/src/gpu/effects/GrCircleEffect.cpp
index a5adf1c..ed3ad51 100644
--- a/src/gpu/effects/GrCircleEffect.cpp
+++ b/src/gpu/effects/GrCircleEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLCircleEffect : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 9887cac..b1e4aa7 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLConfigConversionEffect : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrConstColorProcessor.cpp b/src/gpu/effects/GrConstColorProcessor.cpp
index b2ac825..cfc910a 100644
--- a/src/gpu/effects/GrConstColorProcessor.cpp
+++ b/src/gpu/effects/GrConstColorProcessor.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLConstColorProcessor : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp
index d3fdc4f..a66a40d 100644
--- a/src/gpu/effects/GrDitherEffect.cpp
+++ b/src/gpu/effects/GrDitherEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLDitherEffect : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrEllipseEffect.cpp b/src/gpu/effects/GrEllipseEffect.cpp
index 88a07f9..7ea975f 100644
--- a/src/gpu/effects/GrEllipseEffect.cpp
+++ b/src/gpu/effects/GrEllipseEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLEllipseEffect : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrLumaColorFilterEffect.cpp b/src/gpu/effects/GrLumaColorFilterEffect.cpp
index e09751d..6cd1030 100644
--- a/src/gpu/effects/GrLumaColorFilterEffect.cpp
+++ b/src/gpu/effects/GrLumaColorFilterEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLLumaColorFilterEffect : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrMagnifierEffect.cpp b/src/gpu/effects/GrMagnifierEffect.cpp
new file mode 100644
index 0000000..ac00270
--- /dev/null
+++ b/src/gpu/effects/GrMagnifierEffect.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This file was autogenerated from GrMagnifierEffect.fp; do not modify.
+ */
+#include "GrMagnifierEffect.h"
+#if SK_SUPPORT_GPU
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
+#include "SkSLCPP.h"
+#include "SkSLUtil.h"
+class GrGLSLMagnifierEffect : public GrGLSLFragmentProcessor {
+public:
+    GrGLSLMagnifierEffect() {}
+    void emitCode(EmitArgs& args) override {
+        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+        const GrMagnifierEffect& _outer = args.fFp.cast<GrMagnifierEffect>();
+        (void)_outer;
+        auto bounds = _outer.bounds();
+        (void)bounds;
+        auto srcRect = _outer.srcRect();
+        (void)srcRect;
+        auto xInvZoom = _outer.xInvZoom();
+        (void)xInvZoom;
+        auto yInvZoom = _outer.yInvZoom();
+        (void)yInvZoom;
+        auto xInvInset = _outer.xInvInset();
+        (void)xInvInset;
+        auto yInvInset = _outer.yInvInset();
+        (void)yInvInset;
+        fBoundsUniformVar = args.fUniformHandler->addUniform(
+                kFragment_GrShaderFlag, kFloat4_GrSLType, kDefault_GrSLPrecision, "boundsUniform");
+        fXInvZoomVar = args.fUniformHandler->addUniform(
+                kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "xInvZoom");
+        fYInvZoomVar = args.fUniformHandler->addUniform(
+                kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "yInvZoom");
+        fXInvInsetVar = args.fUniformHandler->addUniform(
+                kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "xInvInset");
+        fYInvInsetVar = args.fUniformHandler->addUniform(
+                kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "yInvInset");
+        fOffsetVar = args.fUniformHandler->addUniform(
+                kFragment_GrShaderFlag, kHalf2_GrSLType, kDefault_GrSLPrecision, "offset");
+        SkString sk_TransformedCoords2D_0 = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+        fragBuilder->codeAppendf(
+                "float2 coord = %s;\nfloat2 zoom_coord = float2(%s + half2(coord * "
+                "float2(half2(half(%s), half(%s)))));\nfloat2 delta = (coord - %s.xy) * "
+                "%s.zw;\ndelta = min(delta, float2(half2(1.0, 1.0) - half2(delta)));\ndelta *= "
+                "float2(half2(half(%s), half(%s)));\nhalf weight = 0.0;\nif (delta.x < 2.0 && "
+                "delta.y < 2.0) {\n    delta = float2(half2(2.0, 2.0) - half2(delta));\n    half "
+                "dist = half(length(delta));\n    dist = half(max(2.0 - float(dist), 0.0));\n    "
+                "weight = half(min(float(dist * dist), 1.0));\n} else {\n    ",
+                sk_TransformedCoords2D_0.c_str(),
+                args.fUniformHandler->getUniformCStr(fOffsetVar),
+                args.fUniformHandler->getUniformCStr(fXInvZoomVar),
+                args.fUniformHandler->getUniformCStr(fYInvZoomVar),
+                args.fUniformHandler->getUniformCStr(fBoundsUniformVar),
+                args.fUniformHandler->getUniformCStr(fBoundsUniformVar),
+                args.fUniformHandler->getUniformCStr(fXInvInsetVar),
+                args.fUniformHandler->getUniformCStr(fYInvInsetVar));
+        fragBuilder->codeAppendf(
+                "float2 delta_squared = delta * delta;\n    weight = half(min(min(delta_squared.x, "
+                "delta_squared.y), 1.0));\n}\n%s = texture(%s, mix(coord, zoom_coord, "
+                "float(weight))).%s;\n",
+                args.fOutputColor,
+                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
+                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+    }
+
+private:
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& _proc) override {
+        const GrMagnifierEffect& _outer = _proc.cast<GrMagnifierEffect>();
+        {
+            pdman.set1f(fXInvZoomVar, _outer.xInvZoom());
+            pdman.set1f(fYInvZoomVar, _outer.yInvZoom());
+            pdman.set1f(fXInvInsetVar, _outer.xInvInset());
+            pdman.set1f(fYInvInsetVar, _outer.yInvInset());
+        }
+        GrSurfaceProxy& srcProxy = *_outer.textureSampler(0).proxy();
+        GrTexture& src = *srcProxy.priv().peekTexture();
+        (void)src;
+        auto bounds = _outer.bounds();
+        (void)bounds;
+        UniformHandle& boundsUniform = fBoundsUniformVar;
+        (void)boundsUniform;
+        auto srcRect = _outer.srcRect();
+        (void)srcRect;
+        UniformHandle& xInvZoom = fXInvZoomVar;
+        (void)xInvZoom;
+        UniformHandle& yInvZoom = fYInvZoomVar;
+        (void)yInvZoom;
+        UniformHandle& xInvInset = fXInvInsetVar;
+        (void)xInvInset;
+        UniformHandle& yInvInset = fYInvInsetVar;
+        (void)yInvInset;
+        UniformHandle& offset = fOffsetVar;
+        (void)offset;
+
+        SkScalar invW = 1.0f / src.width();
+        SkScalar invH = 1.0f / src.height();
+
+        {
+            SkScalar y = srcRect.y() * invH;
+            if (srcProxy.origin() != kTopLeft_GrSurfaceOrigin) {
+                y = 1.0f - (srcRect.height() / bounds.height()) - y;
+            }
+
+            pdman.set2f(offset, srcRect.x() * invW, y);
+        }
+
+        {
+            SkScalar y = bounds.y() * invH;
+            if (srcProxy.origin() != kTopLeft_GrSurfaceOrigin) {
+                y = 1.0f - bounds.height() * invH;
+            }
+
+            pdman.set4f(boundsUniform,
+                        bounds.x() * invW,
+                        y,
+                        SkIntToScalar(src.width()) / bounds.width(),
+                        SkIntToScalar(src.height()) / bounds.height());
+        }
+    }
+    UniformHandle fBoundsUniformVar;
+    UniformHandle fOffsetVar;
+    UniformHandle fSrcVar;
+    UniformHandle fXInvZoomVar;
+    UniformHandle fYInvZoomVar;
+    UniformHandle fXInvInsetVar;
+    UniformHandle fYInvInsetVar;
+};
+GrGLSLFragmentProcessor* GrMagnifierEffect::onCreateGLSLInstance() const {
+    return new GrGLSLMagnifierEffect();
+}
+void GrMagnifierEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                              GrProcessorKeyBuilder* b) const {}
+bool GrMagnifierEffect::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrMagnifierEffect& that = other.cast<GrMagnifierEffect>();
+    (void)that;
+    if (fSrc != that.fSrc) return false;
+    if (fBounds != that.fBounds) return false;
+    if (fSrcRect != that.fSrcRect) return false;
+    if (fXInvZoom != that.fXInvZoom) return false;
+    if (fYInvZoom != that.fYInvZoom) return false;
+    if (fXInvInset != that.fXInvInset) return false;
+    if (fYInvInset != that.fYInvInset) return false;
+    return true;
+}
+GrMagnifierEffect::GrMagnifierEffect(const GrMagnifierEffect& src)
+        : INHERITED(kGrMagnifierEffect_ClassID, src.optimizationFlags())
+        , fSrc(src.fSrc)
+        , fBounds(src.fBounds)
+        , fSrcRect(src.fSrcRect)
+        , fXInvZoom(src.fXInvZoom)
+        , fYInvZoom(src.fYInvZoom)
+        , fXInvInset(src.fXInvInset)
+        , fYInvInset(src.fYInvInset)
+        , fSrcCoordTransform(src.fSrcCoordTransform) {
+    this->addTextureSampler(&fSrc);
+    this->addCoordTransform(&fSrcCoordTransform);
+}
+std::unique_ptr<GrFragmentProcessor> GrMagnifierEffect::clone() const {
+    return std::unique_ptr<GrFragmentProcessor>(new GrMagnifierEffect(*this));
+}
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMagnifierEffect);
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> GrMagnifierEffect::TestCreate(GrProcessorTestData* d) {
+    sk_sp<GrTextureProxy> proxy = d->textureProxy(0);
+    const int kMaxWidth = 200;
+    const int kMaxHeight = 200;
+    const SkScalar kMaxInset = 20.0f;
+    uint32_t width = d->fRandom->nextULessThan(kMaxWidth);
+    uint32_t height = d->fRandom->nextULessThan(kMaxHeight);
+    SkScalar inset = d->fRandom->nextRangeScalar(1.0f, kMaxInset);
+
+    SkIRect bounds = SkIRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight));
+    SkRect srcRect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
+
+    auto effect = GrMagnifierEffect::Make(std::move(proxy),
+                                          bounds,
+                                          srcRect,
+                                          srcRect.width() / bounds.width(),
+                                          srcRect.height() / bounds.height(),
+                                          bounds.width() / inset,
+                                          bounds.height() / inset);
+    SkASSERT(effect);
+    return effect;
+}
+#endif
+#endif
diff --git a/src/gpu/effects/GrMagnifierEffect.fp b/src/gpu/effects/GrMagnifierEffect.fp
new file mode 100644
index 0000000..962c1a0
--- /dev/null
+++ b/src/gpu/effects/GrMagnifierEffect.fp
@@ -0,0 +1,85 @@
+in uniform sampler2D src;
+layout(ctype=SkIRect) in int4 bounds;
+uniform float4 boundsUniform;
+layout(ctype=SkRect) in float4 srcRect;
+in uniform float xInvZoom;
+in uniform float yInvZoom;
+in uniform float xInvInset;
+in uniform float yInvInset;
+
+uniform half2 offset;
+
+@coordTransform(src) {
+    SkMatrix::I()
+}
+
+void main() {
+    float2 coord = sk_TransformedCoords2D[0];
+    float2 zoom_coord = offset + coord * half2(xInvZoom, yInvZoom);
+    float2 delta = (coord - boundsUniform.xy) * boundsUniform.zw;
+    delta = min(delta, half2(1.0, 1.0) - delta);
+    delta *= half2(xInvInset, yInvInset);
+
+    half weight = 0.0;
+    if (delta.s < 2.0 && delta.t < 2.0) {
+        delta = half2(2.0, 2.0) - delta;
+        half dist = length(delta);
+        dist = max(2.0 - dist, 0.0);
+        weight = min(dist * dist, 1.0);
+    } else {
+        float2 delta_squared = delta * delta;
+        weight = min(min(delta_squared.x, delta_squared.y), 1.0);
+    }
+
+    sk_OutColor = texture(src, mix(coord, zoom_coord, weight));
+}
+
+@setData(pdman) {
+    SkScalar invW = 1.0f / src.width();
+    SkScalar invH = 1.0f / src.height();
+
+    {
+        SkScalar y = srcRect.y() * invH;
+        if (srcProxy.origin() != kTopLeft_GrSurfaceOrigin) {
+            y = 1.0f - (srcRect.height() / bounds.height()) - y;
+        }
+
+        pdman.set2f(offset, srcRect.x() * invW, y);
+    }
+
+    {
+        SkScalar y = bounds.y() * invH;
+        if (srcProxy.origin() != kTopLeft_GrSurfaceOrigin) {
+            y = 1.0f - bounds.height() * invH;
+        }
+
+        pdman.set4f(boundsUniform,
+                    bounds.x() * invW,
+                    y,
+                    SkIntToScalar(src.width()) / bounds.width(),
+                    SkIntToScalar(src.height()) / bounds.height());
+    }
+}
+
+@test(d) {
+    sk_sp<GrTextureProxy> proxy = d->textureProxy(0);
+    const int kMaxWidth = 200;
+    const int kMaxHeight = 200;
+    const SkScalar kMaxInset = 20.0f;
+    uint32_t width = d->fRandom->nextULessThan(kMaxWidth);
+    uint32_t height = d->fRandom->nextULessThan(kMaxHeight);
+    SkScalar inset = d->fRandom->nextRangeScalar(1.0f, kMaxInset);
+
+    SkIRect bounds = SkIRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight));
+    SkRect srcRect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
+
+    auto effect = GrMagnifierEffect::Make(std::move(proxy),
+                                          bounds,
+                                          srcRect,
+                                          srcRect.width() / bounds.width(),
+                                          srcRect.height() / bounds.height(),
+                                          bounds.width() / inset,
+                                          bounds.height() / inset);
+    SkASSERT(effect);
+    return effect;
+}
diff --git a/src/gpu/effects/GrMagnifierEffect.h b/src/gpu/effects/GrMagnifierEffect.h
new file mode 100644
index 0000000..13904f9
--- /dev/null
+++ b/src/gpu/effects/GrMagnifierEffect.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This file was autogenerated from GrMagnifierEffect.fp; do not modify.
+ */
+#ifndef GrMagnifierEffect_DEFINED
+#define GrMagnifierEffect_DEFINED
+#include "SkTypes.h"
+#if SK_SUPPORT_GPU
+#include "GrFragmentProcessor.h"
+#include "GrCoordTransform.h"
+class GrMagnifierEffect : public GrFragmentProcessor {
+public:
+    SkIRect bounds() const { return fBounds; }
+    SkRect srcRect() const { return fSrcRect; }
+    float xInvZoom() const { return fXInvZoom; }
+    float yInvZoom() const { return fYInvZoom; }
+    float xInvInset() const { return fXInvInset; }
+    float yInvInset() const { return fYInvInset; }
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> src, SkIRect bounds,
+                                                     SkRect srcRect, float xInvZoom, float yInvZoom,
+                                                     float xInvInset, float yInvInset) {
+        return std::unique_ptr<GrFragmentProcessor>(new GrMagnifierEffect(
+                src, bounds, srcRect, xInvZoom, yInvZoom, xInvInset, yInvInset));
+    }
+    GrMagnifierEffect(const GrMagnifierEffect& src);
+    std::unique_ptr<GrFragmentProcessor> clone() const override;
+    const char* name() const override { return "MagnifierEffect"; }
+
+private:
+    GrMagnifierEffect(sk_sp<GrTextureProxy> src, SkIRect bounds, SkRect srcRect, float xInvZoom,
+                      float yInvZoom, float xInvInset, float yInvInset)
+            : INHERITED(kGrMagnifierEffect_ClassID, kNone_OptimizationFlags)
+            , fSrc(std::move(src))
+            , fBounds(bounds)
+            , fSrcRect(srcRect)
+            , fXInvZoom(xInvZoom)
+            , fYInvZoom(yInvZoom)
+            , fXInvInset(xInvInset)
+            , fYInvInset(yInvInset)
+            , fSrcCoordTransform(SkMatrix::I(), fSrc.proxy()) {
+        this->addTextureSampler(&fSrc);
+        this->addCoordTransform(&fSrcCoordTransform);
+    }
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+    TextureSampler fSrc;
+    SkIRect fBounds;
+    SkRect fSrcRect;
+    float fXInvZoom;
+    float fYInvZoom;
+    float fXInvInset;
+    float fYInvInset;
+    GrCoordTransform fSrcCoordTransform;
+    typedef GrFragmentProcessor INHERITED;
+};
+#endif
+#endif
diff --git a/src/gpu/effects/GrOverdrawFragmentProcessor.cpp b/src/gpu/effects/GrOverdrawFragmentProcessor.cpp
index 31ab53d..9d9e4fe 100644
--- a/src/gpu/effects/GrOverdrawFragmentProcessor.cpp
+++ b/src/gpu/effects/GrOverdrawFragmentProcessor.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLOverdrawFragmentProcessor : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrPremulInputFragmentProcessor.cpp b/src/gpu/effects/GrPremulInputFragmentProcessor.cpp
index a90f529..2e3bece 100644
--- a/src/gpu/effects/GrPremulInputFragmentProcessor.cpp
+++ b/src/gpu/effects/GrPremulInputFragmentProcessor.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLPremulInputFragmentProcessor : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrRectBlurEffect.cpp b/src/gpu/effects/GrRectBlurEffect.cpp
index 573c131..e656d84 100644
--- a/src/gpu/effects/GrRectBlurEffect.cpp
+++ b/src/gpu/effects/GrRectBlurEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLRectBlurEffect : public GrGLSLFragmentProcessor {
@@ -113,7 +114,8 @@
         (void)rect;
         auto sigma = _outer.sigma();
         (void)sigma;
-        UniformHandle& blurProfile = fBlurProfileVar;
+        GrSurfaceProxy& blurProfileProxy = *_outer.textureSampler(0).proxy();
+        GrTexture& blurProfile = *blurProfileProxy.priv().peekTexture();
         (void)blurProfile;
         UniformHandle& proxyRectHalf = fProxyRectHalfVar;
         (void)proxyRectHalf;
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
index 387731e..d1efc85 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLSimpleTextureEffect : public GrGLSLFragmentProcessor {
diff --git a/src/gpu/effects/GrUnpremulInputFragmentProcessor.cpp b/src/gpu/effects/GrUnpremulInputFragmentProcessor.cpp
index 273643f..6e33763 100644
--- a/src/gpu/effects/GrUnpremulInputFragmentProcessor.cpp
+++ b/src/gpu/effects/GrUnpremulInputFragmentProcessor.cpp
@@ -13,6 +13,7 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
 #include "SkSLCPP.h"
 #include "SkSLUtil.h"
 class GrGLSLUnpremulInputFragmentProcessor : public GrGLSLFragmentProcessor {
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index 5b0b497..c5ddc7a 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -176,6 +176,12 @@
     } else if (type.kind() == Type::kEnum_Kind) {
         this->write("%d");
         fFormatArgs.push_back("(int) " + cppCode);
+    } else if (type == *fContext.fInt4_Type || type == *fContext.fShort4_Type) {
+        this->write(type.name() + "(%d, %d, %d, %d)");
+        fFormatArgs.push_back(cppCode + ".left()");
+        fFormatArgs.push_back(cppCode + ".top()");
+        fFormatArgs.push_back(cppCode + ".right()");
+        fFormatArgs.push_back(cppCode + ".bottom()");
     } else {
         printf("unsupported runtime value type '%s'\n", String(type.fName).c_str());
         ASSERT(false);
@@ -589,6 +595,7 @@
         this->writef("        }\n");
     }
     if (section) {
+        int samplerIndex = 0;
         for (const auto& p : fProgram.fElements) {
             if (ProgramElement::kVar_Kind == p->fKind) {
                 const VarDeclarations* decls = (const VarDeclarations*) p.get();
@@ -596,7 +603,15 @@
                     VarDeclaration& decl = (VarDeclaration&) *raw;
                     String nameString(decl.fVar->fName);
                     const char* name = nameString.c_str();
-                    if (needs_uniform_var(*decl.fVar)) {
+                    if (decl.fVar->fType.kind() == Type::kSampler_Kind) {
+                        this->writef("        GrSurfaceProxy& %sProxy = "
+                                     "*_outer.textureSampler(%d).proxy();\n",
+                                     name, samplerIndex);
+                        this->writef("        GrTexture& %s = *%sProxy.priv().peekTexture();\n",
+                                     name, name);
+                        this->writef("        (void) %s;\n", name);
+                        ++samplerIndex;
+                    } else if (needs_uniform_var(*decl.fVar)) {
                         this->writef("        UniformHandle& %s = %sVar;\n"
                                      "        (void) %s;\n",
                                      name, HCodeGenerator::FieldName(name).c_str(), name);
@@ -755,6 +770,7 @@
     this->writef("#include \"glsl/GrGLSLFragmentProcessor.h\"\n"
                  "#include \"glsl/GrGLSLFragmentShaderBuilder.h\"\n"
                  "#include \"glsl/GrGLSLProgramBuilder.h\"\n"
+                 "#include \"GrTexture.h\"\n"
                  "#include \"SkSLCPP.h\"\n"
                  "#include \"SkSLUtil.h\"\n"
                  "class GrGLSL%s : public GrGLSLFragmentProcessor {\n"