apply imagefilter to all draw calls
Review URL: https://codereview.appspot.com/5856048

git-svn-id: http://skia.googlecode.com/svn/trunk@3476 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
new file mode 100644
index 0000000..bde2612
--- /dev/null
+++ b/gm/imagefiltersbase.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+#include "SkBlurImageFilter.h"
+#include "SkTestImageFilters.h"
+
+class FailImageFilter : public SkImageFilter {
+public:
+    FailImageFilter() {}
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; };
+    
+protected:
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) {
+        return false;
+    }
+    
+    FailImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return new FailImageFilter(buffer);
+    }
+    
+    typedef SkImageFilter INHERITED;
+};
+
+class IdentityImageFilter : public SkImageFilter {
+public:
+    IdentityImageFilter() {}
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; };
+
+protected:
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) {
+        *result = src;
+        return true;
+    }
+
+    IdentityImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return new IdentityImageFilter(buffer);
+    }
+    
+    typedef SkImageFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void draw_paint(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+    paint.setColor(SK_ColorGREEN);
+    canvas->save();
+    canvas->clipRect(r);
+    canvas->drawPaint(paint);
+    canvas->restore();
+}
+
+static void draw_line(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    paint.setImageFilter(imf);
+    paint.setStrokeWidth(r.width()/10);
+    canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+}
+
+static void draw_rect(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setColor(SK_ColorYELLOW);
+    paint.setImageFilter(imf);
+    SkRect rr(r);
+    rr.inset(r.width()/10, r.height()/10);
+    canvas->drawRect(rr, paint);
+}
+
+static void draw_path(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setColor(SK_ColorMAGENTA);
+    paint.setImageFilter(imf);
+    paint.setAntiAlias(true);
+    canvas->drawCircle(r.centerX(), r.centerY(), r.width()*2/5, paint);
+}
+
+static void draw_text(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+    paint.setColor(SK_ColorCYAN);
+    paint.setAntiAlias(true);
+    paint.setTextSize(r.height()/2);
+    paint.setTextAlign(SkPaint::kCenter_Align);
+    canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint);
+}
+
+static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+    
+    SkIRect bounds;
+    r.roundOut(&bounds);
+    
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
+    bm.allocPixels();
+    bm.eraseColor(0);
+    SkCanvas c(bm);
+    draw_path(&c, r, NULL);
+    
+    canvas->drawBitmap(bm, 0, 0, &paint);
+}
+
+static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+    
+    SkIRect bounds;
+    r.roundOut(&bounds);
+    
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
+    bm.allocPixels();
+    bm.eraseColor(0);
+    SkCanvas c(bm);
+    draw_path(&c, r, NULL);
+
+    SkPoint loc = { r.fLeft, r.fTop };
+    canvas->getTotalMatrix().mapPoints(&loc, 1);
+    canvas->drawSprite(bm,
+                       SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY),
+                       &paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ImageFiltersBaseGM : public skiagm::GM {
+public:
+    ImageFiltersBaseGM () {}
+
+protected:
+
+    virtual SkString onShortName() {
+        return SkString("imagefiltersbase");
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(700, 460); }
+
+    void draw_frame(SkCanvas* canvas, const SkRect& r) {
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorRED);
+        canvas->drawRect(r, paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = {
+            draw_paint,
+            draw_line, draw_rect, draw_path, draw_text,
+            draw_bitmap,
+            draw_sprite
+        };
+        
+        SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorRED,
+                                                     SkXfermode::kSrcIn_Mode);
+        SkImageFilter* filters[] = {
+#if 1
+            NULL,
+            new IdentityImageFilter,
+            new FailImageFilter,
+            new SkColorFilterImageFilter(cf),
+#endif
+            new SkBlurImageFilter(12.0f, 0.0f),
+        };
+        cf->unref();
+
+        SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64));
+        SkScalar MARGIN = SkIntToScalar(16);
+        SkScalar DX = r.width() + MARGIN;
+        SkScalar DY = r.height() + MARGIN;
+
+        canvas->translate(MARGIN, MARGIN);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(drawProc); ++i) {
+            canvas->save();
+            for (size_t j = 0; j < SK_ARRAY_COUNT(filters); ++j) {
+                drawProc[i](canvas, r, filters[j]);
+                
+                draw_frame(canvas, r);
+                canvas->translate(0, DY);
+            }
+            canvas->restore();
+            canvas->translate(DX, 0);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new ImageFiltersBaseGM; }
+static skiagm::GMRegistry reg(MyFactory);
+
+
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 5beac2b..491af68 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -26,6 +26,7 @@
     '../gm/gradtext.cpp',
     '../gm/hairmodes.cpp',
     '../gm/imageblur.cpp',
+    '../gm/imagefiltersbase.cpp',
     '../gm/lcdtext.cpp',
     '../gm/linepaths.cpp',
     '../gm/morphology.cpp',
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 7ad88f6..5224217 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -977,6 +977,7 @@
     void updateDeviceCMCache();
 
     friend class SkDrawIter;    // needs setupDrawForLayerDevice()
+    friend class AutoDrawLooper;
 
     SkDevice* createLayerDevice(SkBitmap::Config, int width, int height,
                                 bool isOpaque);
@@ -992,9 +993,10 @@
     void internalDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
                                 const SkRect& dst, const SkPaint* paint);
     void internalDrawPaint(const SkPaint& paint);
+    int internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags, bool justForImageFilter);
+    void internalDrawDevice(SkDevice*, int x, int y, const SkPaint*);
 
-
-    void drawDevice(SkDevice*, int x, int y, const SkPaint*);
     // shared by save() and saveLayer()
     int internalSave(SaveFlags flags);
     void internalRestore();
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 3303981..db9c542 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -321,13 +321,22 @@
     virtual bool allowImageFilter(SkImageFilter*);
 
     /**
-     *  Override and return true for filters that the device handles
-     *  intrinsically. Returning false means call the filter.
-     *  Default impl returns false. This will only be called if allowImageFilter()
-     *  returned true.
+     *  Override and return true for filters that the device can handle
+     *  intrinsically. Doing so means that SkCanvas will pass-through this
+     *  filter to drawSprite and drawDevice (and potentially filterImage).
+     *  Returning false means the SkCanvas will have apply the filter itself,
+     *  and just pass the resulting image to the device.
      */
-    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
-                             const SkMatrix& ctm,
+    virtual bool canHandleImageFilter(SkImageFilter*);
+
+    /**
+     *  Related (but not required) to canHandleImageFilter, this method returns
+     *  true if the device could apply the filter to the src bitmap and return
+     *  the result (and updates offset as needed).
+     *  If the device does not recognize or support this filter,
+     *  it just returns false and leaves result and offset unchanged.
+     */
+    virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&,
                              SkBitmap* result, SkIPoint* offset);
 
     // This is equal kBGRA_Premul_Config8888 or kRGBA_Premul_Config8888 if
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 7d7c140..1b282d2 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -40,14 +40,16 @@
 public:
     class Proxy {
     public:
+        virtual ~Proxy() {};
+
         virtual SkDevice* createDevice(int width, int height) = 0;
-        
+        // returns true if the proxy can handle this filter natively
+        virtual bool canHandleImageFilter(SkImageFilter*) = 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(SkImageFilter*, const SkBitmap& src,
                                  const SkMatrix& ctm,
                                  SkBitmap* result, SkIPoint* offset) = 0;
-        virtual ~Proxy() {};
     };
 
     /**
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 24e6e5a..409e3b3 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -110,9 +110,9 @@
      */
     virtual void makeRenderTargetCurrent();
 
-    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
-                             const SkMatrix& ctm,
-                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+    virtual bool canHandleImageFilter(SkImageFilter*) SK_OVERRIDE;
+    virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&,
+                             SkBitmap*, SkIPoint*) SK_OVERRIDE;
 
     class SkAutoCachedTexture; // used internally
 
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 1cd9d71..dde3570 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -277,20 +277,36 @@
 
 class AutoDrawLooper {
 public:
-    AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
+    AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
+                   bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
         fCanvas = canvas;
         fLooper = paint.getLooper();
         fFilter = canvas->getDrawFilter();
         fPaint = NULL;
         fSaveCount = canvas->getSaveCount();
+        fDoClearImageFilter = false;
         fDone = false;
 
+        if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
+            SkPaint tmp;
+            tmp.setImageFilter(fOrigPaint.getImageFilter());
+            // it would be nice if we had a guess at the bounds, instead of null
+            (void)canvas->internalSaveLayer(NULL, &tmp,
+                                    SkCanvas::kARGB_ClipLayer_SaveFlag, true);
+            // we'll clear the imageFilter for the actual draws in next(), so
+            // it will only be applied during the restore().
+            fDoClearImageFilter = true;
+        }
+
         if (fLooper) {
             fLooper->init(canvas);
         }
     }
 
     ~AutoDrawLooper() {
+        if (fDoClearImageFilter) {
+            fCanvas->internalRestore();
+        }
         SkASSERT(fCanvas->getSaveCount() == fSaveCount);
     }
 
@@ -309,6 +325,7 @@
     SkDrawFilter*   fFilter;
     const SkPaint*  fPaint;
     int             fSaveCount;
+    bool            fDoClearImageFilter;
     bool            fDone;
 };
 
@@ -318,8 +335,13 @@
         return false;
     }
 
-    if (fLooper || fFilter) {
+    if (fLooper || fFilter || fDoClearImageFilter) {
         SkPaint* paint = fLazyPaint.set(fOrigPaint);
+
+        if (fDoClearImageFilter) {
+            paint->setImageFilter(NULL);
+        }
+
         if (fLooper && !fLooper->next(fCanvas, paint)) {
             fDone = true;
             return false;
@@ -332,6 +354,11 @@
             }
         }
         fPaint = paint;
+
+        // if we only came in here for the imagefilter, mark us as done
+        if (!fLooper && !fFilter) {
+            fDone = true;
+        }
     } else {
         fDone = true;
         fPaint = &fOrigPaint;
@@ -386,6 +413,13 @@
 
 ////////// macros to place around the internal draw calls //////////////////
 
+#define LOOPER_BEGIN_DRAWDEVICE(paint, type)                        \
+/*    AutoValidator   validator(fMCRec->fTopLayer->fDevice); */     \
+    AutoDrawLooper  looper(this, paint, true);                      \
+    while (looper.next(type)) {                                     \
+        SkAutoBounderCommit ac(fBounder);                           \
+        SkDrawIter          iter(this);
+
 #define LOOPER_BEGIN(paint, type)                                   \
 /*    AutoValidator   validator(fMCRec->fTopLayer->fDevice); */     \
     AutoDrawLooper  looper(this, paint);                            \
@@ -749,6 +783,11 @@
 
 int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
                         SaveFlags flags) {
+    return this->internalSaveLayer(bounds, paint, flags, false);
+}
+
+int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
+                                SaveFlags flags, bool justForImageFilter) {
     // do this before we create the layer. We don't call the public save() since
     // that would invoke a possibly overridden virtual
     int count = this->internalSave(flags);
@@ -764,6 +803,10 @@
     SkLazyPaint lazyP;
     if (paint && paint->getImageFilter()) {
         if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
+            if (justForImageFilter) {
+                // early exit if the layer was just for the imageFilter
+                return count;
+            }
             SkPaint* p = lazyP.set(*paint);
             p->setImageFilter(NULL);
             paint = p;
@@ -841,9 +884,9 @@
     if (NULL != layer) {
         if (layer->fNext) {
             const SkIPoint& origin = layer->fDevice->getOrigin();
-            this->drawDevice(layer->fDevice, origin.x(), origin.y(),
-                             layer->fPaint);
-            // reset this, since drawDevice will have set it to true
+            this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
+                                     layer->fPaint);
+            // reset this, since internalDrawDevice will have set it to true
             fDeviceCMDirty = true;
 
             SkASSERT(fLayerCount > 0);
@@ -905,43 +948,38 @@
 public:
     DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
 
-    virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
-    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
+    virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
+        return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
+                                               w, h, false);
+    }
+    virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
+        return fDevice->canHandleImageFilter(filter);
+    }
+    virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
                              const SkMatrix& ctm,
-                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
-
+                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
+        return fDevice->filterImage(filter, src, ctm, result, offset);
+    }
+    
 private:
     SkDevice* fDevice;
 };
 
-SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
-    return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
-                                           w, h, false);
-}
-
-bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
-                                         const SkBitmap& src,
-                                         const SkMatrix& ctm,
-                                         SkBitmap* result,
-                                         SkIPoint* offset) {
-    return fDevice->filterImage(filter, src, ctm, result, offset);
-}
-
-void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y,
-                          const SkPaint* paint) {
+void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
+                                  const SkPaint* paint) {
     SkPaint tmp;
     if (NULL == paint) {
         tmp.setDither(true);
         paint = &tmp;
     }
 
-    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+    LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
     while (iter.next()) {
         SkDevice* dstDev = iter.fDevice;
         paint = &looper.paint();
         SkImageFilter* filter = paint->getImageFilter();
         SkIPoint pos = { x - iter.getX(), y - iter.getY() };
-        if (filter) {
+        if (filter && !dstDev->canHandleImageFilter(filter)) {
             DeviceImageFilterProxy proxy(dstDev);
             SkBitmap dst;
             const SkBitmap& src = srcDev->accessBitmap(false);
@@ -957,6 +995,40 @@
     LOOPER_END
 }
 
+void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                          const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    
+    if (reject_bitmap(bitmap)) {
+        return;
+    }
+    
+    SkPaint tmp;
+    if (NULL == paint) {
+        paint = &tmp;
+    }
+    
+    LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
+    
+    while (iter.next()) {
+        paint = &looper.paint();
+        SkImageFilter* filter = paint->getImageFilter();
+        SkIPoint pos = { x - iter.getX(), y - iter.getY() };
+        if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
+            DeviceImageFilterProxy proxy(iter.fDevice);
+            SkBitmap dst;
+            if (filter->filterImage(&proxy, bitmap, *iter.fMatrix, &dst, &pos)) {
+                SkPaint tmp(*paint);
+                tmp.setImageFilter(NULL);
+                iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
+            }
+        } else {
+            iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
+        }
+    }
+    LOOPER_END
+}
+
 /////////////////////////////////////////////////////////////////////////////
 
 bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
@@ -1575,28 +1647,6 @@
     this->internalDrawBitmapNine(bitmap, center, dst, paint);
 }
 
-void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
-                          const SkPaint* paint) {
-    SkDEBUGCODE(bitmap.validate();)
-
-    if (reject_bitmap(bitmap)) {
-        return;
-    }
-
-    SkPaint tmp;
-    if (NULL == paint) {
-        paint = &tmp;
-    }
-
-    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
-
-    while (iter.next()) {
-        iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
-                                 looper.paint());
-    }
-    LOOPER_END
-}
-
 class SkDeviceFilteredPaint {
 public:
     SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index f1da2ef..b92f4e1 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -101,9 +101,13 @@
                              const SkClipStack& clipStack) {
 }
 
-bool SkDevice::filterImage(SkImageFilter*, const SkBitmap& src,
-                           const SkMatrix& ctm,
-                           SkBitmap* result, SkIPoint* offset) {
+bool SkDevice::canHandleImageFilter(SkImageFilter*) {
+    return false;
+}
+
+bool SkDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
+                           const SkMatrix& ctm, SkBitmap* result,
+                           SkIPoint* offset) {
     return false;
 }
 
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 4917dcb..0ac4ed5 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1398,11 +1398,50 @@
     fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
 }
 
+static GrTexture* filter_texture(GrContext* context, GrTexture* texture,
+                                 SkImageFilter* filter, const GrRect& rect) {
+    GrAssert(filter);
+
+    SkSize blurSize;
+    SkISize radius;
+
+    const GrTextureDesc desc = {
+        kRenderTarget_GrTextureFlagBit,
+        rect.width(),
+        rect.height(),
+        kRGBA_8888_PM_GrPixelConfig,
+        {0} // samples
+    };
+
+    if (filter->asABlur(&blurSize)) {
+        GrAutoScratchTexture temp1, temp2;
+        texture = context->gaussianBlur(texture, &temp1, &temp2, rect,
+                                        blurSize.width(),
+                                        blurSize.height());
+        texture->ref();
+    } else if (filter->asADilate(&radius)) {
+        GrAutoScratchTexture temp1(context, desc), temp2(context, desc);
+        texture = context->applyMorphology(texture, rect,
+                                           temp1.texture(), temp2.texture(),
+                                           GrSamplerState::kDilate_Filter,
+                                           radius);
+        texture->ref();
+    } else if (filter->asAnErode(&radius)) {
+        GrAutoScratchTexture temp1(context, desc), temp2(context, desc);
+        texture = context->applyMorphology(texture, rect,
+                                           temp1.texture(), temp2.texture(),
+                                           GrSamplerState::kErode_Filter,
+                                           radius);
+        texture->ref();
+    }
+    return texture;
+}
+
 void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
                             int left, int top, const SkPaint& paint) {
     CHECK_SHOULD_DRAW(draw);
 
-    SkAutoLockPixels alp(bitmap);
+    SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
     if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
         return;
     }
@@ -1422,50 +1461,19 @@
     GrTexture* texture;
     sampler->reset();
     SkAutoCachedTexture act(this, bitmap, sampler, &texture);
+    grPaint.setTexture(kBitmapTextureIdx, texture);
 
-    SkImageFilter* imageFilter = paint.getImageFilter();
-    SkSize blurSize;
-    SkISize radius;
-    if (NULL != imageFilter && imageFilter->asABlur(&blurSize)) {
-        GrAutoScratchTexture temp1, temp2;
-        GrTexture* blurTexture = fContext->gaussianBlur(texture, &temp1, &temp2,
-                                                        GrRect::MakeWH(w, h),
-                                                        blurSize.width(),
-                                                        blurSize.height());
-        texture = blurTexture;
-        grPaint.setTexture(kBitmapTextureIdx, texture);
-    } else if (NULL != imageFilter && imageFilter->asADilate(&radius)) {
-        const GrTextureDesc desc = {
-            kRenderTarget_GrTextureFlagBit,
-            w,
-            h,
-            kRGBA_8888_PM_GrPixelConfig,
-            {0} // samples
-        };
-        GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc);
-        texture = fContext->applyMorphology(texture, GrRect::MakeWH(w, h),
-                                            temp1.texture(), temp2.texture(),
-                                            GrSamplerState::kDilate_Filter,
-                                            radius);
-        grPaint.setTexture(kBitmapTextureIdx, texture);
-    } else if (NULL != imageFilter && imageFilter->asAnErode(&radius)) {
-        const GrTextureDesc desc = {
-            kRenderTarget_GrTextureFlagBit,
-            w,
-            h,
-            kRGBA_8888_PM_GrPixelConfig,
-            {0} // samples
-        };
-        GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc);
-        texture = fContext->applyMorphology(texture, GrRect::MakeWH(w, h),
-                                            temp1.texture(), temp2.texture(),
-                                            GrSamplerState::kErode_Filter,
-                                            radius);
-        grPaint.setTexture(kBitmapTextureIdx, texture);
-    } else {
-        grPaint.setTexture(kBitmapTextureIdx, texture);
+    SkImageFilter* filter = paint.getImageFilter();
+    if (NULL != filter) {
+        GrTexture* filteredTexture = filter_texture(fContext, texture, filter,
+                                                    GrRect::MakeWH(w, h));
+        if (filteredTexture) {
+            grPaint.setTexture(kBitmapTextureIdx, filteredTexture);
+            texture = filteredTexture;
+            filteredTexture->unref();
+        }
     }
-
+    
     fContext->drawRectToRect(grPaint,
                             GrRect::MakeXYWH(GrIntToScalar(left),
                                             GrIntToScalar(top),
@@ -1488,6 +1496,18 @@
     GrTexture* devTex = grPaint.getTexture(0);
     SkASSERT(NULL != devTex);
 
+    SkImageFilter* filter = paint.getImageFilter();
+    if (NULL != filter) {
+        GrRect rect = GrRect::MakeWH(devTex->width(), devTex->height());
+        GrTexture* filteredTexture = filter_texture(fContext, devTex, filter,
+                                                    rect);
+        if (filteredTexture) {
+            grPaint.setTexture(kBitmapTextureIdx, filteredTexture);
+            devTex = filteredTexture;
+            filteredTexture->unref();
+        }
+    }
+    
     const SkBitmap& bm = dev->accessBitmap(false);
     int w = bm.width();
     int h = bm.height();
@@ -1509,27 +1529,43 @@
     fContext->drawRectToRect(grPaint, dstRect, srcRect);
 }
 
-bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
-                              const SkMatrix& ctm,
-                              SkBitmap* result, SkIPoint* offset) {
+bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {
     SkSize size;
     SkISize radius;
     if (!filter->asABlur(&size) && !filter->asADilate(&radius) && !filter->asAnErode(&radius)) {
         return false;
     }
-    SkDevice* dev = this->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
-                                                 src.width(),
-                                                 src.height(),
-                                                 false);
-    if (NULL == dev) {
+    return true;
+}
+
+bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
+                              const SkMatrix& ctm,
+                              SkBitmap* result, SkIPoint* offset) {
+    // want explicitly our impl, so guard against a subclass of us overriding it
+    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
         return false;
     }
-    SkAutoUnref aur(dev);
-    SkCanvas canvas(dev);
-    SkPaint paint;
-    paint.setImageFilter(filter);
-    canvas.drawSprite(src, 0, 0, &paint);
-    *result = dev->accessBitmap(false);
+
+    SkAutoLockPixels alp(src, !src.getTexture());
+    if (!src.getTexture() && !src.readyToDraw()) {
+        return false;
+    }
+
+    GrPaint paint;
+    paint.reset();
+
+    GrSamplerState* sampler = paint.textureSampler(kBitmapTextureIdx);
+
+    GrTexture* texture;
+    SkAutoCachedTexture act(this, src, sampler, &texture);
+
+    result->setConfig(src.config(), src.width(), src.height());
+    GrRect rect = GrRect::MakeWH(src.width(), src.height());
+    GrTexture* resultTexture = filter_texture(fContext, texture, filter, rect);
+    if (resultTexture) {
+        result->setPixelRef(new SkGrTexturePixelRef(resultTexture))->unref();
+        resultTexture->unref();
+    }
     return true;
 }