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; )
+