add filterImage() entry-point to SkDevice, to allow it to specialize on subclasses
of SkImageFilter. If that returns false, then the filter itself is invoked.



git-svn-id: http://skia.googlecode.com/svn/trunk@2977 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/testimagefilters.cpp b/gm/testimagefilters.cpp
index e484e1b..fc71d30 100644
--- a/gm/testimagefilters.cpp
+++ b/gm/testimagefilters.cpp
@@ -10,6 +10,8 @@
 #include "SkColorFilter.h"
 #include "SkColorPriv.h"
 #include "SkShader.h"
+
+#include "SkBlurImageFilter.h"
 #include "SkTestImageFilters.h"
 
 #define FILTER_WIDTH    SkIntToScalar(150)
@@ -24,13 +26,17 @@
     return new SkColorFilterImageFilter(cf);
 }
 static SkImageFilter* make3() {
+    return new SkBlurImageFilter(8, 0);
+}
+
+static SkImageFilter* make4() {
     SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
     SkImageFilter* inner = new SkDownSampleImageFilter(SK_Scalar1 / 5);
     SkAutoUnref aur0(outer);
     SkAutoUnref aur1(inner);
     return new SkComposeImageFilter(outer, inner);
 }
-static SkImageFilter* make4() {
+static SkImageFilter* make5() {
     SkImageFilter* first = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
     SkImageFilter* second = new SkDownSampleImageFilter(SK_Scalar1 / 5);
     SkAutoUnref aur0(first);
@@ -38,20 +44,37 @@
     return new SkMergeImageFilter(first, second);
 }
 
-static SkImageFilter* make5() {
+static SkImageFilter* make6() {
     SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
     SkImageFilter* inner = new SkDownSampleImageFilter(SK_Scalar1 / 5);
     SkAutoUnref aur0(outer);
     SkAutoUnref aur1(inner);
     SkImageFilter* compose = new SkComposeImageFilter(outer, inner);
     SkAutoUnref aur2(compose);
-
+    
     SkColorFilter* cf = SkColorFilter::CreateModeFilter(0x880000FF,
                                                         SkXfermode::kSrcIn_Mode);
     SkAutoUnref aur3(cf);
     SkImageFilter* blue = new SkColorFilterImageFilter(cf);
     SkAutoUnref aur4(blue);
+    
+    return new SkMergeImageFilter(compose, blue);
+}
 
+static SkImageFilter* make7() {
+    SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+    SkImageFilter* inner = make3();
+    SkAutoUnref aur0(outer);
+    SkAutoUnref aur1(inner);
+    SkImageFilter* compose = new SkComposeImageFilter(outer, inner);
+    SkAutoUnref aur2(compose);
+    
+    SkColorFilter* cf = SkColorFilter::CreateModeFilter(0x880000FF,
+                                                        SkXfermode::kSrcIn_Mode);
+    SkAutoUnref aur3(cf);
+    SkImageFilter* blue = new SkColorFilterImageFilter(cf);
+    SkAutoUnref aur4(blue);
+    
     return new SkMergeImageFilter(compose, blue);
 }
 
@@ -59,7 +82,7 @@
     SkPaint p;
     p.setAntiAlias(true);
     SkRect r = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
-    r.inset(SK_Scalar1, SK_Scalar1);
+    r.inset(SK_Scalar1 * 12, SK_Scalar1 * 12);
     p.setColor(SK_ColorRED);
     canvas->drawOval(r, p);
 }
@@ -74,11 +97,13 @@
         return SkString("testimagefilters");
     }
 
-    virtual SkISize onISize() { return SkISize::Make(640, 480); }
+    virtual SkISize onISize() { return SkISize::Make(700, 460); }
 
     virtual void onDraw(SkCanvas* canvas) {
+//        this->drawSizeBounds(canvas, 0xFFCCCCCC);
+
         static SkImageFilter* (*gFilterProc[])() = {
-            make0, make1, make2, make3, make4, make5
+            make0, make1, make2, make3, make4, make5, make6, make7
         };
         
         const SkRect bounds = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
@@ -86,20 +111,21 @@
         const SkScalar dx = bounds.width() * 8 / 7;
         const SkScalar dy = bounds.height() * 8 / 7;
 
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gFilterProc); ++i) {
-            int ix = i % 3;
-            int iy = i / 3;
+        canvas->translate(SkIntToScalar(8), SkIntToScalar(8));
 
-            SkPaint paint;
-            paint.setImageFilter(gFilterProc[i]())->unref();
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gFilterProc); ++i) {
+            int ix = i % 4;
+            int iy = i / 4;
 
             SkAutoCanvasRestore acr(canvas, true);
             canvas->translate(ix * dx, iy * dy);
 
             SkPaint p;
-            p.setColor(0xFFCCCCCC);
+            p.setStyle(SkPaint::kStroke_Style);
             canvas->drawRect(bounds, p);
             
+            SkPaint paint;
+            paint.setImageFilter(gFilterProc[i]())->unref();
             canvas->saveLayer(&bounds, &paint);
             draw0(canvas);
         }
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index f817c65..8c351f9 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -309,6 +309,15 @@
     virtual void lockPixels();
     virtual void unlockPixels();
 
+    /**
+     *  Override and return true for filters that the device handles
+     *  intrinsically. Returning false means call the filter.
+     *  Default impl returns false.
+     */
+    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
+                             const SkMatrix& ctm,
+                             SkBitmap* result, SkIPoint* offset);
+
     // This is equal kBGRA_Premul_Config8888 or kRGBA_Premul_Config8888 if 
     // either is identical to kNative_Premul_Config8888. Otherwise, -1.
     static const SkCanvas::Config8888 kPMColorAlias;
@@ -319,6 +328,7 @@
     friend class SkDraw;
     friend class SkDrawIter;
     friend class SkDeviceFilteredPaint;
+    friend class DeviceImageFilterProxy;
 
     // just called by SkCanvas when built as a layer
     void setOrigin(int x, int y) { fOrigin.set(x, y); }
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 1858791..1bb723f 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -11,6 +11,7 @@
 #include "SkFlattenable.h"
 
 class SkBitmap;
+class SkDevice;
 class SkMatrix;
 struct SkPoint;
 
@@ -37,6 +38,16 @@
  */
 class SK_API SkImageFilter : public SkFlattenable {
 public:
+    class Proxy {
+    public:
+        virtual SkDevice* 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(SkImageFilter*, const SkBitmap& src,
+                                 const SkMatrix& ctm,
+                                 SkBitmap* result, SkIPoint* offset) = 0;
+    };
 
     /**
      *  Request a new (result) image to be created from the src image.
@@ -51,7 +62,7 @@
      *  If the result image cannot be created, return false, in which case both
      *  the result and offset parameters will be ignored by the caller.
      */
-    bool filterImage(const SkBitmap& src, const SkMatrix& ctm,
+    bool filterImage(Proxy*, const SkBitmap& src, const SkMatrix& ctm,
                      SkBitmap* result, SkIPoint* offset);
 
     /**
@@ -73,7 +84,7 @@
     explicit SkImageFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
 
     // Default impl returns false
-    virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* offset);
     // Default impl copies src into dst and returns true
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*);
diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h
index 2139b2f..4c9798b 100644
--- a/include/effects/SkBlurImageFilter.h
+++ b/include/effects/SkBlurImageFilter.h
@@ -14,20 +14,23 @@
 class SK_API SkBlurImageFilter : public SkImageFilter {
 public:
     SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY);
-    virtual bool asABlur(SkSize* sigma) const;
+
+    virtual bool asABlur(SkSize* sigma) const SK_OVERRIDE;
+
     static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
         return SkNEW_ARGS(SkBlurImageFilter, (buffer));
     }
 
-    virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
-                               SkBitmap* result, SkIPoint* offset);
-
     SK_DECLARE_FLATTENABLE_REGISTRAR()
 
 protected:
     explicit SkBlurImageFilter(SkFlattenableReadBuffer& buffer);
-    virtual void flatten(SkFlattenableWriteBuffer& buffer);
-    virtual Factory getFactory() { return CreateProc; }
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE;
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+
 private:
     SkSize   fSigma;
     typedef SkImageFilter INHERITED;
diff --git a/include/effects/SkTestImageFilters.h b/include/effects/SkTestImageFilters.h
index 4ec697e..db020ad 100755
--- a/include/effects/SkTestImageFilters.h
+++ b/include/effects/SkTestImageFilters.h
@@ -17,7 +17,7 @@
 protected:
     SkOffsetImageFilter(SkFlattenableReadBuffer& buffer);
 
-    virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
     // overrides from SkFlattenable
@@ -47,7 +47,7 @@
 protected:
     SkComposeImageFilter(SkFlattenableReadBuffer& buffer);
     
-    virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
     // overrides from SkFlattenable
@@ -78,7 +78,7 @@
 protected:
     SkMergeImageFilter(SkFlattenableReadBuffer& buffer);
     
-    virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
     // overrides from SkFlattenable
@@ -116,7 +116,7 @@
 protected:
     SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer);
     
-    virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
     // overrides from SkFlattenable
     virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
@@ -142,7 +142,7 @@
 protected:
     SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer);
     
-    virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
     // overrides from SkFlattenable
     virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 6bafa94..6c4285e 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -110,6 +110,10 @@
      */
     virtual void makeRenderTargetCurrent();
 
+    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
+                             const SkMatrix& ctm,
+                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+    
 protected:
     typedef GrContext::TextureCacheEntry TexCache;
     enum TexType {
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 353c92c..e9fcb57 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -735,8 +735,14 @@
     bool isOpaque;
     SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
 
-    SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
-                                               isOpaque);
+    SkDevice* device;
+    if (paint && paint->getImageFilter()) {
+        device = this->createCompatibleDevice(config, ir.width(), ir.height(),
+                                              isOpaque);
+    } else {
+        device = this->createLayerDevice(config, ir.width(), ir.height(),
+                                         isOpaque);
+    }
     if (NULL == device) {
         SkDebugf("Unable to create device for layer.");
         return count;
@@ -855,6 +861,34 @@
     this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
 }
 
+#include "SkImageFilter.h"
+
+class DeviceImageFilterProxy : public SkImageFilter::Proxy {
+public:
+    DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
+    
+    virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
+    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
+                             const SkMatrix& ctm,
+                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+
+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* device, int x, int y,
                           const SkPaint* paint) {
     SkPaint tmp;
@@ -865,8 +899,21 @@
 
     LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
     while (iter.next()) {
-        iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
-                                 looper.paint());
+        paint = &looper.paint();
+        SkImageFilter* filter = paint->getImageFilter();
+        SkIPoint pos = { x - iter.getX(), y - iter.getY() };
+        if (filter) {
+            DeviceImageFilterProxy proxy(device);
+            SkBitmap dst;
+            const SkBitmap& src = device->accessBitmap(false);
+            if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
+                SkPaint tmp(*paint);
+                tmp.setImageFilter(NULL);
+                iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
+            }
+        } else {
+            iter.fDevice->drawDevice(iter, device, pos.x(), pos.y(), *paint);
+        }
     }
     LOOPER_END
 }
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 4cad466..578d0c8 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -101,6 +101,12 @@
                              const SkClipStack& clipStack) {
 }
 
+bool SkDevice::filterImage(SkImageFilter*, const SkBitmap& src,
+                           const SkMatrix& ctm,
+                           SkBitmap* result, SkIPoint* offset) {
+    return false;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y,
@@ -354,20 +360,8 @@
 
 void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device,
                               int x, int y, const SkPaint& paint) {
-    SkBitmap output;
-    const SkBitmap* src = &device->accessBitmap(false);
-    SkImageFilter* filter = paint.getImageFilter();
-
-    if (filter) {
-        SkIPoint loc;
-        loc.set(x, y);
-        if (filter->filterImage(*src, *draw.fMatrix, &output, &loc)) {
-            src = &output;
-            x = loc.fX;
-            y = loc.fY;
-        }
-    }
-    draw.drawSprite(*src, x, y, paint);
+    const SkBitmap& src = device->accessBitmap(false);
+    draw.drawSprite(src, x, y, paint);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index eeb8669..f7b3ae3 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -1982,11 +1982,18 @@
 
 //////////// Move these to their own file soon.
 
-bool SkImageFilter::filterImage(const SkBitmap& src, const SkMatrix& matrix,
+bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
+                                const SkMatrix& ctm,
                                 SkBitmap* result, SkIPoint* loc) {
+    SkASSERT(proxy);
     SkASSERT(result);
     SkASSERT(loc);
-    return this->onFilterImage(src, matrix, result, loc);
+    /*
+     *  Give the proxy first shot at the filter. If it returns false, ask
+     *  the filter to do it.
+     */
+    return proxy->filterImage(this, src, ctm, result, loc) ||
+           this->onFilterImage(proxy, src, ctm, result, loc);
 }
 
 bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
@@ -1996,7 +2003,7 @@
     return this->onFilterBounds(src, ctm, dst);
 }
 
-bool SkImageFilter::onFilterImage(const SkBitmap& src, const SkMatrix&,
+bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
                                   SkBitmap*, SkIPoint*) {
     return false;
 }
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index 490a5b6..1446e92 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -133,12 +133,18 @@
     }
 }
 
-bool SkBlurImageFilter::onFilterImage(const SkBitmap& src, const SkMatrix&,
+bool SkBlurImageFilter::onFilterImage(Proxy*,
+                                      const SkBitmap& src, const SkMatrix&,
                                       SkBitmap* dst, SkIPoint*) {
     if (src.config() != SkBitmap::kARGB_8888_Config) {
         return false;
     }
 
+    SkAutoLockPixels alp(src);
+    if (!src.getPixels()) {
+        return false;
+    }
+
     dst->setConfig(src.config(), src.width(), src.height());
     dst->allocPixels();
     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp
index c974365..a772f64 100755
--- a/src/effects/SkTestImageFilters.cpp
+++ b/src/effects/SkTestImageFilters.cpp
@@ -1,7 +1,15 @@
 #include "SkTestImageFilters.h"
 #include "SkCanvas.h"
+#include "SkDevice.h"
 
-bool SkOffsetImageFilter::onFilterImage(const SkBitmap& src,
+class OwnDeviceCanvas : public SkCanvas {
+public:
+    OwnDeviceCanvas(SkDevice* device) : SkCanvas(device) {
+        SkSafeUnref(device);
+    }
+};
+
+bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                         const SkMatrix& matrix,
                                         SkBitmap* result,
                                         SkIPoint* loc) {
@@ -46,7 +54,8 @@
     SkSafeUnref(fOuter);
 }
 
-bool SkComposeImageFilter::onFilterImage(const SkBitmap& src,
+bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
+                                         const SkBitmap& src,
                                          const SkMatrix& ctm,
                                          SkBitmap* result,
                                          SkIPoint* loc) {
@@ -55,12 +64,12 @@
     }
     
     if (!fOuter || !fInner) {
-        return (fOuter ? fOuter : fInner)->filterImage(src, ctm, result, loc);
+        return (fOuter ? fOuter : fInner)->filterImage(proxy, src, ctm, result, loc);
     }
     
     SkBitmap tmp;
-    return fInner->filterImage(src, ctm, &tmp, loc) &&
-    fOuter->filterImage(tmp, ctm, result, loc);
+    return fInner->filterImage(proxy, src, ctm, &tmp, loc) &&
+           fOuter->filterImage(proxy, tmp, ctm, result, loc);
 }
 
 bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
@@ -186,7 +195,8 @@
     return true;
 }
 
-bool SkMergeImageFilter::onFilterImage(const SkBitmap& src, const SkMatrix& ctm,
+bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
+                                       const SkMatrix& ctm,
                                        SkBitmap* result, SkIPoint* loc) {
     if (fCount < 1) {
         return false;
@@ -202,12 +212,11 @@
     const int x0 = bounds.left();
     const int y0 = bounds.top();
 
-    SkBitmap dst;
-    dst.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
-    dst.allocPixels();
-    dst.eraseColor(0);
-
-    SkCanvas canvas(dst);
+    SkDevice* dst = proxy->createDevice(bounds.width(), bounds.height());
+    if (NULL == dst) {
+        return false;
+    }
+    OwnDeviceCanvas canvas(dst);
     SkPaint paint;
 
     for (int i = 0; i < fCount; ++i) {
@@ -216,7 +225,7 @@
         SkIPoint pos = *loc;
         SkImageFilter* filter = fFilters[i];
         if (filter) {
-            if (!filter->filterImage(src, ctm, &tmp, &pos)) {
+            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
                 return false;
             }
             srcPtr = &tmp;
@@ -233,7 +242,7 @@
     }
 
     loc->set(bounds.left(), bounds.top());
-    result->swap(dst);
+    *result = dst->accessBitmap(false);
     return true;
 }
 
@@ -285,36 +294,28 @@
     SkSafeUnref(fColorFilter);
 }
 
-bool SkColorFilterImageFilter::onFilterImage(const SkBitmap& src,
+bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                              const SkMatrix& matrix,
                                              SkBitmap* result,
                                              SkIPoint* loc) {
-    if (SkBitmap::kARGB_8888_Config != src.config()) {
-        return false;
-    }
-
     SkColorFilter* cf = fColorFilter;
     if (NULL == cf) {
         *result = src;
         return true;
     }
 
-    SkAutoLockPixels alpsrc(src);
-    if (!src.readyToDraw()) {
+    SkDevice* dev = proxy->createDevice(src.width(), src.height());
+    if (NULL == dev) {
         return false;
     }
+    OwnDeviceCanvas canvas(dev);
+    SkPaint paint;
 
-    SkBitmap dst(src);
-    dst.allocPixels();
-    if (!dst.readyToDraw()) {
-        return false;
-    }
-    
-    for (int y = 0; y < src.height(); ++y) {
-        cf->filterSpan(src.getAddr32(0, y), src.width(), dst.getAddr32(0, y));
-    }
-    
-    result->swap(dst);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    paint.setColorFilter(fColorFilter);
+    canvas.drawSprite(src, 0, 0, &paint);
+
+    *result = dev->accessBitmap(false);
     return true;
 }
 
@@ -334,7 +335,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkDownSampleImageFilter::onFilterImage(const SkBitmap& src,
+bool SkDownSampleImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                             const SkMatrix& matrix,
                                             SkBitmap* result, SkIPoint*) {
     SkScalar scale = fScale;
@@ -350,32 +351,36 @@
     if (dstH < 1) {
         dstH = 1;
     }
-    
-    SkBitmap dst;
-    dst.setConfig(SkBitmap::kARGB_8888_Config, dstW, dstH);
-    dst.allocPixels();
-    dst.eraseColor(0);
-    
+
+    SkBitmap tmp;
+
     // downsample
     {
+        SkDevice* dev = proxy->createDevice(dstW, dstH);
+        if (NULL == dev) {
+            return false;
+        }
+        OwnDeviceCanvas canvas(dev);
         SkPaint paint;
+
         paint.setFilterBitmap(true);
-        
-        SkCanvas canvas(dst);
         canvas.scale(scale, scale);
         canvas.drawBitmap(src, 0, 0, &paint);
+        tmp = dev->accessBitmap(false);
     }
-    
-    result->setConfig(SkBitmap::kARGB_8888_Config, src.width(), src.height());
-    result->allocPixels();
-    result->eraseColor(0);
-    
+
     // upscale
     {
-        SkRect r = SkRect::MakeWH(SkIntToScalar(result->width()),
-                                  SkIntToScalar(result->height()));
-        SkCanvas canvas(*result);
-        canvas.drawBitmapRect(dst, NULL, r, NULL);
+        SkDevice* dev = proxy->createDevice(src.width(), src.height());
+        if (NULL == dev) {
+            return false;
+        }
+        OwnDeviceCanvas canvas(dev);
+
+        SkRect r = SkRect::MakeWH(SkIntToScalar(src.width()),
+                                  SkIntToScalar(src.height()));
+        canvas.drawBitmapRect(tmp, NULL, r, NULL);
+        *result = dev->accessBitmap(false);
     }
     return true;
 }
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 709aaba..42a76e4 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1461,6 +1461,9 @@
         return;
     }
 
+    int w = bitmap.width();
+    int h = bitmap.height();
+
     GrPaint grPaint;
     if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
         return;
@@ -1474,14 +1477,29 @@
     sampler->reset();
     SkAutoCachedTexture act(this, bitmap, sampler, &texture);
 
-    grPaint.setTexture(kBitmapTextureIdx, texture);
+    SkImageFilter* imageFilter = paint.getImageFilter();
+    SkSize blurSize;
+    if (NULL != imageFilter && imageFilter->asABlur(&blurSize)) {
+        GrAutoScratchTexture temp1, temp2;
+        GrTexture* blurTexture = gaussianBlur(fContext,
+                                              texture, &temp1, &temp2,
+                                              GrRect::MakeWH(w, h),
+                                              blurSize.width(),
+                                              blurSize.height());
+        texture = blurTexture;
+        grPaint.setTexture(kBitmapTextureIdx, texture);
+        GrPrintf("%d %d | %d %d\n", w, h, texture->width(), texture->height());
+    } else {
+        grPaint.setTexture(kBitmapTextureIdx, texture);
+    }
 
     fContext->drawRectToRect(grPaint,
-                             GrRect::MakeXYWH(GrIntToScalar(left),
-                                              GrIntToScalar(top),
-                                              GrIntToScalar(bitmap.width()),
-                                              GrIntToScalar(bitmap.height())),
-                             GrRect::MakeWH(GR_Scalar1, GR_Scalar1));
+                            GrRect::MakeXYWH(GrIntToScalar(left),
+                                            GrIntToScalar(top),
+                                            GrIntToScalar(w),
+                                            GrIntToScalar(h)),
+                            GrRect::MakeWH(GR_Scalar1 * w / texture->width(),
+                                        GR_Scalar1 * h / texture->height()));
 }
 
 void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
@@ -1509,18 +1527,7 @@
                                       GrIntToScalar(y),
                                       GrIntToScalar(w),
                                       GrIntToScalar(h));
-    SkImageFilter* imageFilter = paint.getImageFilter();
-    SkSize size;
-    if (NULL != imageFilter && imageFilter->asABlur(&size)) {
-        GrAutoScratchTexture temp1, temp2;
-        GrTexture* blurTexture = gaussianBlur(fContext,
-                                              devTex, &temp1, &temp2,
-                                              GrRect::MakeWH(w, h),
-                                              size.width(),
-                                              size.height());
-        grPaint.setTexture(kBitmapTextureIdx, blurTexture);
-        devTex = blurTexture;
-    }
+
     // The device being drawn may not fill up its texture (saveLayer uses
     // the approximate ).
     GrRect srcRect = GrRect::MakeWH(GR_Scalar1 * w / devTex->width(),
@@ -1529,6 +1536,29 @@
     fContext->drawRectToRect(grPaint, dstRect, srcRect);
 }
 
+bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
+                              const SkMatrix& ctm,
+                              SkBitmap* result, SkIPoint* offset) {
+    SkSize size;
+    if (!filter->asABlur(&size)) {
+        return false;
+    }
+    SkDevice* dev = this->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
+                                                 src.width(),
+                                                 src.height(),
+                                                 false);
+    if (NULL == dev) {
+        return false;
+    }
+    SkAutoUnref aur(dev);
+    SkCanvas canvas(dev);
+    SkPaint paint;
+    paint.setImageFilter(filter);
+    canvas.drawSprite(src, 0, 0, &paint);
+    *result = dev->accessBitmap(false);
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 // must be in SkCanvas::VertexMode order