add applyFilter() to SkImage

Result:
- clients can get a filtered version of an image without having to setup a temp drawing environment
- for some cases, the process is more efficient even than (deprecated) drawSprite, since there is no need to draw/copy the result

Impl:
- made Proxy virtual so we don't need to have an existing device to use it

This, in conjunction with LocalMatrixImageFilter, should allow us to simplify and optimize ApplyImageFilter() in cc/output/gl_renderer.cc

BUG=skia:

Review URL: https://codereview.chromium.org/1390913005
diff --git a/gm/spritebitmap.cpp b/gm/spritebitmap.cpp
index ceed50a..0d467ea 100644
--- a/gm/spritebitmap.cpp
+++ b/gm/spritebitmap.cpp
@@ -97,3 +97,121 @@
 };
 DEF_GM( return new SpriteBitmapGM; )
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorFilterImageFilter.h"
+#include "SkModeColorFilter.h"
+#include "SkMorphologyImageFilter.h"
+#include "SkOffsetImageFilter.h"
+
+static SkImage* make_image(SkCanvas* rootCanvas) {
+    SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
+    SkAutoTUnref<SkSurface> surface(rootCanvas->newSurface(info));
+    if (!surface) {
+        surface.reset(SkSurface::NewRaster(info));
+    }
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setColor(SK_ColorRED);
+    surface->getCanvas()->drawCircle(50, 50, 50, paint);
+    return surface->newImageSnapshot();
+}
+
+static void show_image(SkCanvas* canvas, SkImage* image, const SkIPoint& offset) {
+    SkScalar x = SkIntToScalar(offset.x());
+    SkScalar y = SkIntToScalar(offset.y());
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    SkRect r = SkRect::MakeIWH(image->width(), image->height());
+    r.offset(x, y);
+    // get on pixel-centers to make the hairline land on a numerical stable boundary
+    r.outset(SK_ScalarHalf, SK_ScalarHalf);
+    canvas->drawRect(r, paint);
+
+    canvas->drawImage(image, x, y, nullptr);
+}
+
+typedef SkImageFilter* (*ImageFilterFactory)();
+
+// +[]{...} did not work on windows (VS)
+// (ImageFilterFactory)[]{...} did not work on linux (gcc)
+// hence this cast function
+template <typename T> ImageFilterFactory IFCCast(T arg) { return arg; }
+
+/**
+ *  Compare output of drawSprite and drawBitmap (esp. clipping and imagefilters)
+ */
+class ApplyFilterGM : public skiagm::GM {
+public:
+    ApplyFilterGM() {}
+
+protected:
+    SkString onShortName() override {
+        return SkString("apply-filter");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(640, 480);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkAutoTUnref<SkImage> image0(make_image(canvas));
+
+        const ImageFilterFactory factories[] = {
+            IFCCast([]{ return SkBlurImageFilter::Create(8, 8); }),
+            IFCCast([]{ SkAutoTUnref<SkColorFilter> cf(SkModeColorFilter::Create(SK_ColorBLUE,
+                                                                     SkXfermode::kSrcIn_Mode));
+                 return SkColorFilterImageFilter::Create(cf);
+            }),
+            IFCCast([]{ return SkDilateImageFilter::Create(8, 8); }),
+            IFCCast([]{ return SkErodeImageFilter::Create(8, 8); }),
+            IFCCast([]{ return SkOffsetImageFilter::Create(8, 8); }),
+        };
+
+        const SkScalar spacer = image0->width() * 3.0f / 2;
+
+        for (auto&& factory : factories) {
+            SkAutoTUnref<SkImageFilter> filter(factory());
+
+            SkIPoint offset1, offset2;
+            SkAutoTUnref<SkImage> image1(image0->applyFilter(filter, &offset1, true));
+            SkAutoTUnref<SkImage> image2(image0->applyFilter(filter, &offset2, false));
+
+            canvas->save();
+            canvas->translate(30, 30);
+            show_image(canvas, image0, SkIPoint::Make(0, 0));   // original
+            canvas->translate(spacer, 0);
+            show_image(canvas, image1, offset1);                // snug
+            canvas->translate(spacer, 0);
+            show_image(canvas, image2, offset2);                // not snug
+
+            // Try drawing the original w/ the filter, to see that it "draws" the same as
+            // when we have manually applied the filter (above).
+            {
+                SkPaint paint;
+                paint.setImageFilter(filter);
+
+                SkBitmap bm;
+                image0->asLegacyBitmap(&bm, SkImage::kRO_LegacyBitmapMode);
+                SkPoint loc = { 0, 0 };
+                canvas->translate(spacer, 0);
+                canvas->getTotalMatrix().mapPoints(&loc, 1);
+                canvas->drawSprite(bm, (int)loc.x(), (int)loc.y(), &paint); // like snug
+
+                canvas->translate(spacer, 0);
+                canvas->drawImage(image0, 0, 0, &paint);        // like not snug
+            }
+            canvas->restore();
+
+            canvas->translate(0, spacer);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+DEF_GM( return new ApplyFilterGM; )
+
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 9e01e62..c52c579 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -372,7 +372,7 @@
     friend class SkDraw;
     friend class SkDrawIter;
     friend class SkDeviceFilteredPaint;
-    friend class SkImageFilter::Proxy;
+    friend class SkImageFilter::DeviceProxy;
     friend class SkNoPixelsBitmapDevice;
 
     friend class SkSurface_Raster;
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 96de89e..9e3810c 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -304,6 +304,28 @@
      */
     bool isLazyGenerated() const;
 
+    /**
+     *  Apply the specified filter to this image, and return the result as a new image.
+     *
+     *  if forceResultToOriginalSize is true, then the resulting image will be the same size as the
+     *  src, regardless of the normal output of the filter.
+     *
+     *  If offset is non-null, it is set to the relative offset needed to draw the resulting image
+     *  in the same logical place as the original.
+     *
+     *  e.g.
+     *      If the filter makes the result larger by a margin of 4 the output would be:
+     *          result->width() = this->width + 8
+     *          result->height() = this->height + 8
+     *          offset.x() == -4
+     *          offset.y() == -4
+     *
+     *  If the filter fails to create a resulting image, null is returned, and the offset parameter
+     *  (if specified) will be undefined.
+     */
+    SkImage* applyFilter(SkImageFilter* filter, SkIPoint* offset,
+                         bool forceResultToOriginalSize) const;
+
 protected:
     SkImage(int width, int height, uint32_t uniqueID);
 
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 30265e4..326ae5b 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -95,20 +95,32 @@
 
     class Proxy {
     public:
-        Proxy(SkBaseDevice* device) : fDevice(device) { }
+        virtual ~Proxy() {}
 
-        SkBaseDevice* createDevice(int width, int height);
+        virtual SkBaseDevice* createDevice(int width, int height) = 0;
+
+        // Returns true if the proxy handled the filter itself. If this returns
+        // false then the filter's code will be called.
+        virtual bool filterImage(const SkImageFilter*, const SkBitmap& src,
+                                 const SkImageFilter::Context&,
+                                 SkBitmap* result, SkIPoint* offset) = 0;
+    };
+
+    class DeviceProxy : public Proxy {
+    public:
+        DeviceProxy(SkBaseDevice* device) : fDevice(device) {}
+
+        SkBaseDevice* createDevice(int width, int height) override;
 
         // Returns true if the proxy handled the filter itself. If this returns
         // false then the filter's code will be called.
         bool filterImage(const SkImageFilter*, const SkBitmap& src, const SkImageFilter::Context&,
-                         SkBitmap* result, SkIPoint* offset);
+                                 SkBitmap* result, SkIPoint* offset) override;
 
     private:
         SkBaseDevice* fDevice;
     };
 
-
     /**
      *  Request a new (result) image to be created from the src image.
      *  If the src has no pixels (isNull()) then the request just wants to
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 4952ae2..6914963 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1300,7 +1300,7 @@
         SkImageFilter* filter = paint->getImageFilter();
         SkIPoint pos = { x - iter.getX(), y - iter.getY() };
         if (filter && !dstDev->canHandleImageFilter(filter)) {
-            SkImageFilter::Proxy proxy(dstDev);
+            SkImageFilter::DeviceProxy proxy(dstDev);
             SkBitmap dst;
             SkIPoint offset = SkIPoint::Make(0, 0);
             const SkBitmap& src = srcDev->accessBitmap(false);
@@ -1352,7 +1352,7 @@
         SkImageFilter* filter = paint->getImageFilter();
         SkIPoint pos = { x - iter.getX(), y - iter.getY() };
         if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
-            SkImageFilter::Proxy proxy(iter.fDevice);
+            SkImageFilter::DeviceProxy proxy(iter.fDevice);
             SkBitmap dst;
             SkIPoint offset = SkIPoint::Make(0, 0);
             SkMatrix matrix = *iter.fMatrix;
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 4ad7f0e..08d46c7 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -578,7 +578,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-SkBaseDevice* SkImageFilter::Proxy::createDevice(int w, int h) {
+SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h) {
     SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h),
                                    SkBaseDevice::kNever_TileUsage,
                                    kUnknown_SkPixelGeometry,
@@ -592,7 +592,7 @@
     return dev;
 }
 
-bool SkImageFilter::Proxy::filterImage(const SkImageFilter* filter, const SkBitmap& src,
+bool SkImageFilter::DeviceProxy::filterImage(const SkImageFilter* filter, const SkBitmap& src,
                                        const SkImageFilter::Context& ctx,
                                        SkBitmap* result, SkIPoint* offset) {
     return fDevice->filterImage(filter, src, ctx, result, offset);
diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp
index baeb378..5bddc03 100644
--- a/src/gpu/GrLayerHoister.cpp
+++ b/src/gpu/GrLayerHoister.cpp
@@ -313,7 +313,7 @@
     SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(kDefaultCacheSize));
     SkImageFilter::Context filterContext(totMat, clipBounds, cache);
 
-    SkImageFilter::Proxy proxy(device);
+    SkImageFilter::DeviceProxy proxy(device);
     const SkBitmap src = wrap_texture(layer->texture());
 
     if (!layer->filter()->filterImage(&proxy, src, filterContext, &filteredBitmap, &offset)) {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 4d15059..ab91316 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1395,7 +1395,7 @@
                                 SkBitmap* result, SkIPoint* offset) {
     SkASSERT(filter);
 
-    SkImageFilter::Proxy proxy(this);
+    SkImageFilter::DeviceProxy proxy(this);
 
     if (filter->canFilterImageGPU()) {
         return filter->filterImageGPU(&proxy, wrap_texture(texture, width, height),
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index af9c275..3ad8004 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -67,6 +67,81 @@
     }
 }
 
+SkImage* SkImage::applyFilter(SkImageFilter* filter, SkIPoint* offset,
+                              bool forceResultToOriginalSize) const {
+    if (!filter) {
+        return nullptr;
+    }
+
+    SkIPoint offsetStorage;
+    if (!offset) {
+        offset = &offsetStorage;
+    }
+    return as_IB(this)->onApplyFilter(filter, offset, forceResultToOriginalSize);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageFilter.h"
+#include "SkBitmapDevice.h"
+
+static SkIRect compute_fast_ibounds(SkImageFilter* filter, const SkIRect& srcBounds) {
+    SkRect fastBounds;
+    fastBounds.set(srcBounds);
+    filter->computeFastBounds(fastBounds, &fastBounds);
+    return fastBounds.roundOut();
+}
+
+class SkRasterImageFilterProxy : public SkImageFilter::Proxy {
+public:
+    SkBaseDevice* createDevice(int width, int height) override {
+        return SkBitmapDevice::Create(SkImageInfo::MakeN32Premul(width, height));
+    }
+
+    bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
+                     SkBitmap*, SkIPoint*) override {
+        return false;
+    }
+};
+
+SkImage* SkImage_Base::onApplyFilter(SkImageFilter* filter, SkIPoint* offsetResult,
+                                     bool forceResultToOriginalSize) const {
+    SkBitmap src;
+    if (!this->getROPixels(&src)) {
+        return nullptr;
+    }
+
+    const SkIRect srcBounds = SkIRect::MakeWH(this->width(), this->height());
+
+    if (forceResultToOriginalSize) {
+        const SkIRect clipBounds = srcBounds;
+        SkRasterImageFilterProxy proxy;
+        SkImageFilter::Context ctx(SkMatrix::I(), clipBounds, SkImageFilter::Cache::Get());
+
+        SkBitmap dst;
+        if (filter->filterImage(&proxy, src, ctx, &dst, offsetResult)) {
+            dst.setImmutable();
+            return SkImage::NewFromBitmap(dst);
+        }
+    } else {
+        const SkIRect dstR = compute_fast_ibounds(filter, srcBounds);
+
+        SkImageInfo info = SkImageInfo::MakeN32Premul(dstR.width(), dstR.height());
+        SkAutoTUnref<SkSurface> surface(this->onNewSurface(info));
+
+        SkPaint paint;
+        paint.setImageFilter(filter);
+        surface->getCanvas()->drawImage(this, SkIntToScalar(-dstR.x()), SkIntToScalar(-dstR.y()),
+                                        &paint);
+
+        offsetResult->set(dstR.x(), dstR.y());
+        return surface->newImageSnapshot();
+    }
+    return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 SkShader* SkImage::newShader(SkShader::TileMode tileX,
                              SkShader::TileMode tileY,
                              const SkMatrix* localMatrix) const {
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 7819e9c..3fe3444 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -37,6 +37,13 @@
     // but only inspect them (or encode them).
     virtual bool getROPixels(SkBitmap*) const = 0;
 
+    virtual SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset,
+                                   bool forceResultToOriginalSize) const;
+
+    virtual SkSurface* onNewSurface(const SkImageInfo& info) const {
+        return SkSurface::NewRaster(info);
+    }
+
     // Caller must call unref when they are done.
     virtual GrTexture* asTextureRef(GrContext*, const GrTextureParams&) const = 0;
 
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index ccb27ee..529df3a 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -38,6 +38,10 @@
     }
 }
 
+static SkImageInfo make_info(int w, int h, bool isOpaque) {
+    return SkImageInfo::MakeN32(w, h, isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
+}
+
 bool SkImage_Gpu::getROPixels(SkBitmap* dst) const {
     if (SkBitmapCache::Find(this->uniqueID(), dst)) {
         SkASSERT(dst->getGenerationID() == this->uniqueID());
@@ -46,8 +50,7 @@
         return true;
     }
 
-    SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
-    if (!dst->tryAllocPixels(SkImageInfo::MakeN32(this->width(), this->height(), at))) {
+    if (!dst->tryAllocPixels(make_info(this->width(), this->height(), this->isOpaque()))) {
         return false;
     }
     if (!fTexture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
@@ -183,6 +186,69 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+#include "SkBitmapDevice.h"
+#include "SkGrPixelRef.h"
+#include "SkImageFilter.h"
+
+class SkGpuImageFilterProxy : public SkImageFilter::Proxy {
+    GrContext* fCtx;
+
+public:
+    SkGpuImageFilterProxy(GrContext* ctx) : fCtx(ctx) {}
+
+    SkBaseDevice* createDevice(int width, int height) override {
+        GrSurfaceDesc desc;
+        desc.fConfig = kSkia8888_GrPixelConfig;
+        desc.fFlags = kRenderTarget_GrSurfaceFlag;
+        desc.fWidth = width;
+        desc.fHeight = height;
+        desc.fSampleCnt = 0;
+
+        SkAutoTUnref<GrTexture> texture(fCtx->textureProvider()->createTexture(desc, true));
+
+        if (texture) {
+            SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+            return SkGpuDevice::Create(texture->asRenderTarget(), width, height, &props,
+                                       SkGpuDevice::kClear_InitContents);
+        } else {
+            return nullptr;
+        }
+    }
+
+    bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
+                     SkBitmap*, SkIPoint*) override {
+        return false;
+    }
+};
+
+SkImage* SkImage_Gpu::onApplyFilter(SkImageFilter* filter, SkIPoint* offsetResult,
+                                    bool forceResultToOriginalSize) const {
+    if (!forceResultToOriginalSize || !filter->canFilterImageGPU()) {
+        return this->INHERITED::onApplyFilter(filter, offsetResult, forceResultToOriginalSize);
+    }
+
+    const SkImageInfo info = make_info(this->width(), this->height(), this->isOpaque());
+    SkAutoTUnref<SkGrPixelRef> pr(new SkGrPixelRef(info, fTexture));
+    SkBitmap src;
+    src.setInfo(info);
+    src.setPixelRef(pr, 0, 0);
+
+    GrContext* context = fTexture->getContext();
+    SkGpuImageFilterProxy proxy(context);
+    SkImageFilter::Context ctx(SkMatrix::I(),
+                               SkIRect::MakeWH(this->width(), this->height()),
+                               SkImageFilter::Cache::Get());
+
+    SkBitmap dst;
+    if (filter->filterImageGPU(&proxy, src, ctx, &dst, offsetResult)) {
+        return new SkImage_Gpu(dst.width(), dst.height(), kNeedNewImageUniqueID, info.alphaType(),
+                               dst.getTexture(), SkSurface::kNo_Budgeted);
+    }
+    return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 static SkImage* new_wrapped_texture_common(GrContext* ctx, const GrBackendTextureDesc& desc,
                                            SkAlphaType at, GrWrapOwnership ownership,
                                            SkImage::TextureReleaseProc releaseProc,
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 530c044..d5080d7 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -43,6 +43,12 @@
     bool isOpaque() const override;
     bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes,
                       int srcX, int srcY) const override;
+    SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset,
+                           bool forceResultToOriginalSize) const override;
+
+    SkSurface* onNewSurface(const SkImageInfo& info) const override {
+        return SkSurface::NewRenderTarget(fTexture->getContext(), SkSurface::kNo_Budgeted, info);
+    }
 
 private:
     SkAutoTUnref<GrTexture>     fTexture;
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 6b9064e..b118296 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -400,7 +400,7 @@
     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
 
     SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
-    SkImageFilter::Proxy proxy(device);
+    SkImageFilter::DeviceProxy proxy(device);
 
     test_negative_blur_sigma(&proxy, reporter);
 }
@@ -773,7 +773,7 @@
     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
 
     SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
-    SkImageFilter::Proxy proxy(device);
+    SkImageFilter::DeviceProxy proxy(device);
 
     test_crop_rects(&proxy, reporter);
 }
@@ -887,7 +887,7 @@
     bitmap.allocN32Pixels(2, 2);
     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
     SkBitmapDevice device(bitmap, props);
-    SkImageFilter::Proxy proxy(&device);
+    SkImageFilter::DeviceProxy proxy(&device);
     REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
 }
 
@@ -1125,7 +1125,7 @@
     bitmap.eraseARGB(0, 0, 0, 0);
     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
     SkBitmapDevice device(bitmap, props);
-    SkImageFilter::Proxy proxy(&device);
+    SkImageFilter::DeviceProxy proxy(&device);
 
     SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
     SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect));
@@ -1144,7 +1144,7 @@
     bitmap.eraseARGB(0, 0, 0, 0);
     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
     SkBitmapDevice device(bitmap, props);
-    SkImageFilter::Proxy proxy(&device);
+    SkImageFilter::DeviceProxy proxy(&device);
 
     SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(100, 0, 20, 30),
         SkImageFilter::CropRect::kHasWidth_CropEdge | SkImageFilter::CropRect::kHasHeight_CropEdge);
@@ -1251,7 +1251,7 @@
                                                          0,
                                                          &props,
                                                          SkGpuDevice::kUninit_InitContents));
-    SkImageFilter::Proxy proxy(device);
+    SkImageFilter::DeviceProxy proxy(device);
 
     test_crop_rects(&proxy, reporter);
 }
@@ -1305,7 +1305,7 @@
                                                          0,
                                                          &props,
                                                          SkGpuDevice::kUninit_InitContents));
-    SkImageFilter::Proxy proxy(device);
+    SkImageFilter::DeviceProxy proxy(device);
 
     test_negative_blur_sigma(&proxy, reporter);
 }