Revert of Remove deprecated paths from image filter infrastructure. (patchset #2 id:20001 of https://codereview.chromium.org/1888243003/ )

Reason for revert:
Surprisingly (appallingly?), Chrome calls canFilterImageGPU

Original issue's description:
> Remove deprecated paths from image filter infrastructure.
>
> Now that there are no filterImageGPUDeprecated() implementations,
> we can being to rip out the deprecated infrastructure.
>
> BUG=skia:
> GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1888243003
>
> Committed: https://skia.googlesource.com/skia/+/6fb3cd7209849e665635ac17ef4eef4ad63e7f61

TBR=reed@google.com,senorblanco@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:

Review URL: https://codereview.chromium.org/1893993002
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 2377b57..c15aecc 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -280,6 +280,28 @@
     */
     virtual const SkBitmap& onAccessBitmap() = 0;
 
+    /**
+     *  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 canHandleImageFilter(const SkImageFilter*) { return false; }
+
+    /**
+     *  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(const SkImageFilter*, const SkBitmap&,
+                             const SkImageFilter::Context&,
+                             SkBitmap* /*result*/, SkIPoint* /*offset*/) {
+        return false;
+    }
+
 protected:
     virtual sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&);
     virtual bool onPeekPixels(SkPixmap*) { return false; }
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 554f9f8..dcd25bf 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -116,6 +116,12 @@
 
         virtual SkBaseDevice* createDevice(int width, int height,
                                            TileUsage usage = kNever_TileUsage) = 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 {
@@ -125,6 +131,11 @@
         SkBaseDevice* createDevice(int width, int height,
                                    TileUsage usage = kNever_TileUsage) 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) override;
+
     private:
         SkBaseDevice* fDevice;
     };
@@ -164,6 +175,31 @@
     SkIRect filterBounds(const SkIRect& src, const SkMatrix& ctm,
                          MapDirection = kReverse_MapDirection) const;
 
+    /**
+     *  Returns true if the filter can be processed on the GPU.  This is most
+     *  often used for multi-pass effects, where intermediate results must be
+     *  rendered to textures.  For single-pass effects, use asFragmentProcessor().
+     *  The default implementation returns asFragmentProcessor(NULL, NULL, SkMatrix::I(),
+     *  SkIRect()).
+     */
+    virtual bool canFilterImageGPU() const { return false; }
+
+    /**
+     *  Process this image filter on the GPU.  This is most often used for
+     *  multi-pass effects, where intermediate results must be rendered to
+     *  textures.  For single-pass effects, use asFragmentProcessor().  src is the
+     *  source image for processing, as a texture-backed bitmap.  result is
+     *  the destination bitmap, which should contain a texture-backed pixelref
+     *  on success.  offset is the amount to translate the resulting image
+     *  relative to the src when it is drawn. The default implementation does
+     *  single-pass processing using asFragmentProcessor().
+     */
+    virtual bool filterImageGPUDeprecated(Proxy*, const SkBitmap&, const Context&,
+                                          SkBitmap*, SkIPoint*) const {
+        SkASSERT(false);
+        return false;
+    }
+
 #if SK_SUPPORT_GPU
     static sk_sp<SkSpecialImage> DrawWithFP(GrContext* context, 
                                             sk_sp<GrFragmentProcessor> fp,
@@ -262,6 +298,18 @@
                                       const Context&, 
                                       SkIPoint* offset) const;
 
+#if SK_SUPPORT_GPU
+    // Helper function which invokes GPU filter processing on the
+    // input at the specified "index". If the input is null, it leaves
+    // "result" and "offset" untouched, and returns true. If the input
+    // has a GPU implementation, it will be invoked directly.
+    // Otherwise, the filter will be processed in software and
+    // uploaded to the GPU.
+    bool filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy,
+                                  const SkBitmap& src, const Context&,
+                                  SkBitmap* result, SkIPoint* offset) const;
+#endif
+
     SK_TO_STRING_PUREVIRT()
     SK_DEFINE_FLATTENABLE_TYPE(SkImageFilter)
 
@@ -392,6 +440,9 @@
      *  which are not capable of processing a smaller source bitmap into a
      *  larger destination.
      */
+    bool applyCropRectDeprecated(const Context&, Proxy* proxy, const SkBitmap& src,
+                                 SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* result) const;
+
     sk_sp<SkSpecialImage> applyCropRect(const Context&, SkSpecialImage* src, SkIPoint* srcOffset,
                                         SkIRect* bounds) const;
 
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 9d7a3d5..de69c36 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1408,7 +1408,7 @@
         paint = &looper.paint();
         SkImageFilter* filter = paint->getImageFilter();
         SkIPoint pos = { x - iter.getX(), y - iter.getY() };
-        if (filter) {
+        if (filter && !dstDev->canHandleImageFilter(filter)) {
             SkImageFilter::DeviceProxy proxy(dstDev);
             SkIPoint offset = SkIPoint::Make(0, 0);
             const SkBitmap& srcBM = srcDev->accessBitmap(false);
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 4c53ad4..1b22856 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -409,29 +409,33 @@
     SkImageFilter* filter = paint.getImageFilter();
     SkASSERT(filter);
 
-    SkImageFilter::DeviceProxy proxy(this);
-    SkIPoint offset = SkIPoint::Make(0, 0);
-    SkMatrix matrix = *draw.fMatrix;
-    matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
-    const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
-    SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache());
-    SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
+    if (!this->canHandleImageFilter(filter)) {
+        SkImageFilter::DeviceProxy proxy(this);
+        SkIPoint offset = SkIPoint::Make(0, 0);
+        SkMatrix matrix = *draw.fMatrix;
+        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+        const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
+        SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache());
+        SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
 
-    sk_sp<SkSpecialImage> srcImg(SkSpecialImage::internal_fromBM(&proxy, bitmap,
-                                                                 &this->surfaceProps()));
-    if (!srcImg) {
-        return; // something disastrous happened
-    }
-
-    sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
-    if (resultImg) {
-        SkPaint tmpUnfiltered(paint);
-        tmpUnfiltered.setImageFilter(nullptr);
-        SkBitmap resultBM;
-        if (resultImg->internal_getBM(&resultBM)) {
-            // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073)
-            this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
+        sk_sp<SkSpecialImage> srcImg(SkSpecialImage::internal_fromBM(&proxy, bitmap,
+                                                                     &this->surfaceProps()));
+        if (!srcImg) {
+            return; // something disastrous happened
         }
+
+        sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
+        if (resultImg) {
+            SkPaint tmpUnfiltered(paint);
+            tmpUnfiltered.setImageFilter(nullptr);
+            SkBitmap resultBM;
+            if (resultImg->internal_getBM(&resultBM)) {
+                // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073)
+                this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
+            }
+        }
+    } else {
+        this->drawSprite(draw, bitmap, x, y, paint);
     }
 }
 
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 22fdd67..fc59e64 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -256,7 +256,12 @@
             return true;
         }
     }
-    if (this->onFilterImageDeprecated(proxy, src, context, result, offset)) {
+    /*
+     *  Give the proxy first shot at the filter. If it returns false, ask
+     *  the filter to do it.
+     */
+    if ((proxy && proxy->filterImage(this, src, context, result, offset)) ||
+        this->onFilterImageDeprecated(proxy, src, context, result, offset)) {
         if (context.cache()) {
             context.cache()->set(key, *result, *offset);
             SkAutoMutexAcquire mutex(fMutex);
@@ -423,6 +428,35 @@
     return dstBounds->intersect(ctx.clipBounds());
 }
 
+bool SkImageFilter::applyCropRectDeprecated(const Context& ctx, Proxy* proxy, const SkBitmap& src,
+                                            SkIPoint* srcOffset, SkIRect* bounds,
+                                            SkBitmap* dst) const {
+    SkIRect srcBounds;
+    src.getBounds(&srcBounds);
+    srcBounds.offset(*srcOffset);
+    SkIRect dstBounds = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection);
+    fCropRect.applyTo(dstBounds, ctx.ctm(), this->affectsTransparentBlack(), bounds);
+    if (!bounds->intersect(ctx.clipBounds())) {
+        return false;
+    }
+
+    if (srcBounds.contains(*bounds)) {
+        *dst = src;
+        return true;
+    } else {
+        SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
+        if (!device) {
+            return false;
+        }
+        SkCanvas canvas(device);
+        canvas.clear(0x00000000);
+        canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
+        *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
+        *dst = device->accessBitmap(false);
+        return true;
+    }
+}
+
 // Return a larger (newWidth x newHeight) copy of 'src' with black padding
 // around it.
 static sk_sp<SkSpecialImage> pad_image(SkSpecialImage* src,
@@ -530,6 +564,52 @@
     return result;
 }
 
+#if SK_SUPPORT_GPU
+
+bool SkImageFilter::filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy,
+                                             const SkBitmap& src, const Context& ctx,
+                                             SkBitmap* result, SkIPoint* offset) const {
+    SkImageFilter* input = this->getInput(index);
+    if (!input) {
+        return true;
+    }
+
+    // SRGBTODO: Don't handle sRGB here, in anticipation of this code path being deleted.
+    sk_sp<SkSpecialImage> specialSrc(SkSpecialImage::internal_fromBM(proxy, src, nullptr));
+    if (!specialSrc) {
+        return false;
+    }
+
+    sk_sp<SkSpecialImage> tmp(input->onFilterImage(specialSrc.get(),
+                                                   this->mapContext(ctx),
+                                                   offset));
+    if (!tmp) {
+        return false;
+    }
+
+    if (!tmp->internal_getBM(result)) {
+        return false;
+    }
+
+    if (!result->getTexture()) {
+        GrContext* context = src.getTexture()->getContext();
+
+        const SkImageInfo info = result->info();
+        if (kUnknown_SkColorType == info.colorType()) {
+            return false;
+        }
+        SkAutoTUnref<GrTexture> resultTex(
+            GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter()));
+        if (!resultTex) {
+            return false;
+        }
+        result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref();
+    }
+
+    return true;
+}
+#endif
+
 namespace {
 
 class CacheImpl : public SkImageFilter::Cache {
@@ -697,3 +777,9 @@
     }
     return dev;
 }
+
+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/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 84e6a79..08eab55 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1159,6 +1159,25 @@
     }
 }
 
+bool SkGpuDevice::filterTexture(GrContext* context, GrTexture* texture,
+                                int width, int height,
+                                const SkImageFilter* filter,
+                                const SkImageFilter::Context& ctx,
+                                SkBitmap* result, SkIPoint* offset) {
+    ASSERT_SINGLE_OWNER
+    SkASSERT(filter);
+
+    SkImageFilter::DeviceProxy proxy(this);
+
+    if (filter->canFilterImageGPU()) {
+        SkBitmap bm;
+        GrWrapTextureInBitmap(texture, width, height, false, &bm);
+        return filter->filterImageGPUDeprecated(&proxy, bm, ctx, result, offset);
+    } else {
+        return false;
+    }
+}
+
 void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
                              int left, int top, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
@@ -1184,7 +1203,34 @@
 
     bool alphaOnly = kAlpha_8_SkColorType == bitmap.colorType();
 
-    SkASSERT(!paint.getImageFilter());
+    SkImageFilter* filter = paint.getImageFilter();
+    // This bitmap will own the filtered result as a texture.
+    SkBitmap filteredBitmap;
+
+    if (filter) {
+        SkIPoint offset = SkIPoint::Make(0, 0);
+        SkMatrix matrix(*draw.fMatrix);
+        matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
+        SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-left, -top);
+        SkAutoTUnref<SkImageFilter::Cache> cache(getImageFilterCache());
+        // This cache is transient, and is freed (along with all its contained
+        // textures) when it goes out of scope.
+        SkImageFilter::Context ctx(matrix, clipBounds, cache);
+        if (this->filterTexture(fContext, texture, w, h, filter, ctx, &filteredBitmap,
+                                &offset)) {
+            texture = (GrTexture*) filteredBitmap.getTexture();
+            offX = filteredBitmap.pixelRefOrigin().fX;
+            offY = filteredBitmap.pixelRefOrigin().fY;
+            w = filteredBitmap.width();
+            h = filteredBitmap.height();
+            left += offset.x();
+            top += offset.y();
+        } else {
+            return;
+        }
+        SkASSERT(!GrPixelConfigIsAlphaOnly(texture->config()));
+        alphaOnly = false;
+    }
 
     GrPaint grPaint;
     SkAutoTUnref<const GrFragmentProcessor> fp(
@@ -1320,7 +1366,30 @@
     int w = ii.width();
     int h = ii.height();
 
-    SkASSERT(!paint.getImageFilter());
+    SkImageFilter* filter = paint.getImageFilter();
+    // This bitmap will own the filtered result as a texture.
+    SkBitmap filteredBitmap;
+
+    if (filter) {
+        SkIPoint offset = SkIPoint::Make(0, 0);
+        SkMatrix matrix(*draw.fMatrix);
+        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+        SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
+        // This cache is transient, and is freed (along with all its contained
+        // textures) when it goes out of scope.
+        SkAutoTUnref<SkImageFilter::Cache> cache(getImageFilterCache());
+        SkImageFilter::Context ctx(matrix, clipBounds, cache);
+        if (this->filterTexture(fContext, devTex, device->width(), device->height(),
+                                filter, ctx, &filteredBitmap, &offset)) {
+            devTex = filteredBitmap.getTexture();
+            w = filteredBitmap.width();
+            h = filteredBitmap.height();
+            x += offset.fX;
+            y += offset.fY;
+        } else {
+            return;
+        }
+    }
 
     GrPaint grPaint;
     SkAutoTUnref<const GrFragmentProcessor> fp(
@@ -1350,6 +1419,37 @@
     fDrawContext->fillRectToRect(fClip, grPaint, SkMatrix::I(), dstRect, srcRect);
 }
 
+bool SkGpuDevice::canHandleImageFilter(const SkImageFilter* filter) {
+    ASSERT_SINGLE_OWNER
+    return filter->canFilterImageGPU();
+}
+
+bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src,
+                              const SkImageFilter::Context& ctx,
+                              SkBitmap* result, SkIPoint* offset) {
+    ASSERT_SINGLE_OWNER
+    // want explicitly our impl, so guard against a subclass of us overriding it
+    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(src, !src.getTexture());
+    if (!src.getTexture() && !src.readyToDraw()) {
+        return false;
+    }
+
+    GrTexture* texture;
+    // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
+    // must be pushed upstack.
+    AutoBitmapTexture abt(fContext, src, GrTextureParams::ClampNoFilter(), &texture);
+    if (!texture) {
+        return false;
+    }
+
+    return this->filterTexture(fContext, texture, src.width(), src.height(),
+                               filter, ctx, result, offset);
+}
+
 void SkGpuDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
                             const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
@@ -1836,11 +1936,15 @@
 #endif
 }
 
+SkImageFilter::Cache* SkGpuDevice::NewImageFilterCache() {
+    return SkImageFilter::Cache::Create(kDefaultImageFilterCacheSize);
+}
+
 SkImageFilter::Cache* SkGpuDevice::getImageFilterCache() {
     ASSERT_SINGLE_OWNER
     // We always return a transient cache, so it is freed after each
     // filter traversal.
-    return SkImageFilter::Cache::Create(kDefaultImageFilterCacheSize);
+    return SkGpuDevice::NewImageFilterCache();
 }
 
 #endif
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 1923525..4eaeab7 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -130,6 +130,17 @@
     const SkBitmap& onAccessBitmap() override;
     bool onAccessPixels(SkPixmap*) override;
 
+    bool canHandleImageFilter(const SkImageFilter*) override;
+    virtual bool filterImage(const SkImageFilter*, const SkBitmap&,
+                             const SkImageFilter::Context&,
+                             SkBitmap*, SkIPoint*) override;
+
+    bool filterTexture(GrContext*, GrTexture*, int width, int height, const SkImageFilter*,
+                       const SkImageFilter::Context&,
+                       SkBitmap* result, SkIPoint* offset);
+
+    static SkImageFilter::Cache* NewImageFilterCache();
+
     // for debugging purposes only
     void drawTexture(GrTexture*, const SkRect& dst, const SkPaint&);