Add SkSpecialImage::makeTextureImage entry point

This is calved off of: https://codereview.chromium.org/1785643003 (Switch SkBlurImageFilter over to new onFilterImage interface)

GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1813813002

Committed: https://skia.googlesource.com/skia/+/05849018c85403a34b88819db1c4bcf713b70a2b

Review URL: https://codereview.chromium.org/1813813002
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 1a4c876..4ae839c 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -596,7 +596,18 @@
         return SkRef(src);
     }
 
-    return input->filterImage(src, this->mapContext(ctx), offset);
+    SkAutoTUnref<SkSpecialImage> result(input->filterImage(src, this->mapContext(ctx), offset));
+
+#if SK_SUPPORT_GPU
+    if (src->peekTexture() && result && !result->peekTexture()) {
+        // Keep the result on the GPU - this is still required for some
+        // image filters that don't support GPU in all cases
+        GrContext* context = src->peekTexture()->getContext();
+        return result->makeTextureImage(src->internal_getProxy(), context).release();
+    }
+#endif
+
+    return result.release();
 }
 
 #if SK_SUPPORT_GPU
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index e90c655..991e163 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -4,10 +4,16 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file
  */
+#include "SkSpecialImage.h"
+
+#if SK_SUPPORT_GPU
+#include "GrTexture.h"
+#include "GrTextureParams.h"
+#include "SkGr.h"
+#endif
 
 #include "SkCanvas.h"
 #include "SkImage_Base.h"
-#include "SkSpecialImage.h"
 #include "SkSpecialSurface.h"
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -42,6 +48,38 @@
     return static_cast<const SkSpecialImage_Base*>(image);
 }
 
+sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(SkImageFilter::Proxy* proxy,
+                                                       GrContext* context) {
+#if SK_SUPPORT_GPU
+    if (!context) {
+        return nullptr;
+    }
+    if (GrTexture* peek = as_SIB(this)->peekTexture()) {
+        return peek->getContext() == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr;
+    }
+
+    SkBitmap bmp;
+    if (!this->internal_getBM(&bmp)) {
+        return nullptr;
+    }
+
+    SkAutoTUnref<GrTexture> resultTex(
+        GrRefCachedBitmapTexture(context, bmp, GrTextureParams::ClampNoFilter()));
+    if (!resultTex) {
+        return nullptr;
+    }
+
+    SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+
+    return SkSpecialImage::MakeFromGpu(proxy,
+                                       SkIRect::MakeWH(resultTex->width(), resultTex->height()),
+                                       this->uniqueID(),
+                                       resultTex, at);
+#else
+    return nullptr;
+#endif
+}
+
 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
     return as_SIB(this)->onDraw(canvas, x, y, paint);
 }
diff --git a/src/core/SkSpecialImage.h b/src/core/SkSpecialImage.h
index 1a01a5a..47ad6cc 100644
--- a/src/core/SkSpecialImage.h
+++ b/src/core/SkSpecialImage.h
@@ -14,12 +14,16 @@
 // remove this when internal_getProxy goes away (see skbug.com/4965)
 #include "SkImageFilter.h"
 
+#include "SkImageInfo.h" // for SkAlphaType
+
+class GrContext;
 class GrTexture;
 class SkBitmap;
 class SkCanvas;
 class SkImage;
 struct SkImageInfo;
 class SkPaint;
+class SkPixmap;
 class SkSpecialSurface;
 
 enum {
@@ -51,6 +55,13 @@
     virtual size_t getSize() const = 0;
 
     /**
+     *  Ensures that a special image is backed by a texture (when GrContext is non-null). If no
+     *  transformation is required, the returned image may be the same as this special image.
+     *  If this special image is from a different GrContext, this will fail.
+     */
+    sk_sp<SkSpecialImage> makeTextureImage(SkImageFilter::Proxy*, GrContext*);
+
+    /**
      *  Draw this SpecialImage into the canvas.
      */
     void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const;
diff --git a/tests/SpecialImageTest.cpp b/tests/SpecialImageTest.cpp
index 233ce2c..3240fbd 100644
--- a/tests/SpecialImageTest.cpp
+++ b/tests/SpecialImageTest.cpp
@@ -174,6 +174,81 @@
 
 
 #if SK_SUPPORT_GPU
+
+static void test_texture_backed(skiatest::Reporter* reporter,
+                                const sk_sp<SkSpecialImage>& orig,
+                                const sk_sp<SkSpecialImage>& gpuBacked) {
+    REPORTER_ASSERT(reporter, gpuBacked);
+    REPORTER_ASSERT(reporter, gpuBacked->peekTexture());
+    REPORTER_ASSERT(reporter, gpuBacked->uniqueID() == orig->uniqueID());
+    REPORTER_ASSERT(reporter, gpuBacked->subset().width() == orig->subset().width() &&
+                              gpuBacked->subset().height() == orig->subset().height());
+}
+
+// Test out the SkSpecialImage::makeTextureImage entry point
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture, reporter, context) {
+    SkBitmap bm = create_bm();
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    {
+        // raster
+        sk_sp<SkSpecialImage> rasterImage(SkSpecialImage::MakeFromRaster(
+                                                                        nullptr,
+                                                                        SkIRect::MakeWH(kFullSize,
+                                                                                        kFullSize),
+                                                                        bm));
+
+        {
+            sk_sp<SkSpecialImage> fromRaster(rasterImage->makeTextureImage(nullptr, context));
+            test_texture_backed(reporter, rasterImage, fromRaster);
+        }
+
+        {
+            sk_sp<SkSpecialImage> subRasterImage(rasterImage->makeSubset(subset));
+
+            sk_sp<SkSpecialImage> fromSubRaster(subRasterImage->makeTextureImage(nullptr, context));
+            test_texture_backed(reporter, subRasterImage, fromSubRaster);
+        }
+    }
+
+    {
+        // gpu
+        GrSurfaceDesc desc;
+        desc.fConfig = kSkia8888_GrPixelConfig;
+        desc.fFlags = kNone_GrSurfaceFlags;
+        desc.fWidth = kFullSize;
+        desc.fHeight = kFullSize;
+
+        SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc,
+                                                                                  SkBudgeted::kNo,
+                                                                                  bm.getPixels(),
+                                                                                  0));
+        if (!texture) {
+            return;
+        }
+
+        sk_sp<SkSpecialImage> gpuImage(SkSpecialImage::MakeFromGpu(
+                                                                nullptr,
+                                                                SkIRect::MakeWH(kFullSize,
+                                                                                kFullSize),
+                                                                kNeedNewImageUniqueID_SpecialImage,
+                                                                texture));
+
+        {
+            sk_sp<SkSpecialImage> fromGPU(gpuImage->makeTextureImage(nullptr, context));
+            test_texture_backed(reporter, gpuImage, fromGPU);
+        }
+
+        {
+            sk_sp<SkSpecialImage> subGPUImage(gpuImage->makeSubset(subset));
+
+            sk_sp<SkSpecialImage> fromSubGPU(subGPUImage->makeTextureImage(nullptr, context));
+            test_texture_backed(reporter, subGPUImage, fromSubGPU);
+        }
+    }
+}
+
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, context) {
     SkBitmap bm = create_bm();
 
@@ -183,7 +258,8 @@
     desc.fWidth  = kFullSize;
     desc.fHeight = kFullSize;
 
-    SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, SkBudgeted::kNo,
+    SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc,
+                                                                              SkBudgeted::kNo,
                                                                               bm.getPixels(), 0));
     if (!texture) {
         return;