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/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;