Update SkSpecialImage to be able to create tight SkImages and SkSurfaces

This calved off of: https://codereview.chromium.org/1810693003/ (Switch SkTileImageFilter over to new onFilterImage interface) since the TileImageFilter needs a tight bitmap/texture/image to perform its draw.

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

Review URL: https://codereview.chromium.org/1816223002
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 426a44e..03223a2 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -35,9 +35,13 @@
     // Delete this entry point ASAP (see skbug.com/4965)
     virtual bool getBitmapDeprecated(SkBitmap* result) const = 0;
 
+    virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
+
     virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageInfo& info) const = 0;
 
-    virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
+    virtual sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const = 0;
+
+    virtual SkSurface* onMakeTightSurface(const SkImageInfo& info) const = 0;
 
 private:
     typedef SkSpecialImage INHERITED;
@@ -100,10 +104,19 @@
     return as_SIB(this)->onMakeSurface(info);
 }
 
+sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageInfo& info) const {
+    sk_sp<SkSurface> tmp(SkRef(as_SIB(this)->onMakeTightSurface(info)));
+    return tmp;
+}
+
 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
     return as_SIB(this)->onMakeSubset(subset);
 }
 
+sk_sp<SkImage> SkSpecialImage::makeTightSubset(const SkIRect& subset) const {
+    return as_SIB(this)->onMakeTightSubset(subset);
+}
+
 #if SK_SUPPORT_GPU
 #include "SkGr.h"
 #include "SkGrPixelRef.h"
@@ -227,6 +240,20 @@
                                              subsetImg);
     }
 
+    sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override {
+        return fImage->makeSubset(subset);
+    }
+
+    SkSurface* onMakeTightSurface(const SkImageInfo& info) const override {
+#if SK_SUPPORT_GPU
+        GrTexture* texture = as_IB(fImage.get())->peekTexture();
+        if (texture) {
+            return SkSurface::NewRenderTarget(texture->getContext(), SkBudgeted::kYes, info, 0);
+        }
+#endif
+        return SkSurface::NewRaster(info, nullptr);
+    }
+
 private:
     sk_sp<SkImage> fImage;
 
@@ -332,6 +359,20 @@
                                               subsetBM);
     }
 
+    sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override {
+        SkBitmap subsetBM;
+
+        if (!fBitmap.extractSubset(&subsetBM, subset)) {
+            return nullptr;
+        }
+
+        return SkImage::MakeFromBitmap(subsetBM);
+    }
+
+    SkSurface* onMakeTightSurface(const SkImageInfo& info) const override {
+        return SkSurface::NewRaster(info);
+    }
+
 private:
     SkBitmap fBitmap;
 
@@ -363,6 +404,7 @@
 #if SK_SUPPORT_GPU
 ///////////////////////////////////////////////////////////////////////////////
 #include "GrTexture.h"
+#include "SkImage_Gpu.h"
 
 class SkSpecialImage_Gpu : public SkSpecialImage_Base {
 public:                                       
@@ -445,6 +487,34 @@
                                            fAlphaType);
     }
 
+    sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override {
+        if (0 == subset.fLeft && 0 == subset.fTop &&
+            fTexture->width() == subset.width() &&
+            fTexture->height() == subset.height()) {
+            // The existing GrTexture is already tight so reuse it in the SkImage
+            return sk_make_sp<SkImage_Gpu>(fTexture->width(), fTexture->height(),
+                                           kNeedNewImageUniqueID,
+                                           fAlphaType, fTexture, SkBudgeted::kYes);
+        }
+
+        GrContext* ctx = fTexture->getContext();
+        GrSurfaceDesc desc = fTexture->desc();
+        desc.fWidth = subset.width();
+        desc.fHeight = subset.height();
+
+        GrTexture* subTx = ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes);
+        if (!subTx) {
+            return nullptr;
+        }
+        ctx->copySurface(subTx, fTexture, subset, SkIPoint::Make(0, 0));
+        return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID,
+                                       fAlphaType, subTx, SkBudgeted::kYes);
+    }
+
+    SkSurface* onMakeTightSurface(const SkImageInfo& info) const override {
+        return SkSurface::NewRenderTarget(fTexture->getContext(), SkBudgeted::kYes, info);
+    }
+
 private:
     SkAutoTUnref<GrTexture> fTexture;
     const SkAlphaType       fAlphaType;
diff --git a/src/core/SkSpecialImage.h b/src/core/SkSpecialImage.h
index 47ad6cc..778bf53 100644
--- a/src/core/SkSpecialImage.h
+++ b/src/core/SkSpecialImage.h
@@ -25,6 +25,7 @@
 class SkPaint;
 class SkPixmap;
 class SkSpecialSurface;
+class SkSurface;
 
 enum {
     kNeedNewImageUniqueID_SpecialImage = 0
@@ -84,16 +85,29 @@
                                                 ReleaseContext);
 
     /**
-     *  Create a new surface with a backend that is compatible with this image.
+     *  Create a new special surface with a backend that is compatible with this special image.
      */
     sk_sp<SkSpecialSurface> makeSurface(const SkImageInfo&) const;
 
     /**
+     * Create a new surface with a backend that is compatible with this special image.
+     * TODO: switch this to makeSurface once we resolved the naming issue
+     */
+    sk_sp<SkSurface> makeTightSurface(const SkImageInfo&) const;
+
+    /**
      * Extract a subset of this special image and return it as a special image.
      * It may or may not point to the same backing memory.
      */
     sk_sp<SkSpecialImage> makeSubset(const SkIRect& subset) const;
 
+    /**
+     * Extract a subset of this special image and return it as an SkImage.
+     * It may or may not point to the same backing memory.
+     * TODO: switch this to makeSurface once we resolved the naming issue
+     */
+    sk_sp<SkImage> makeTightSubset(const SkIRect& subset) const;
+
     // These three internal methods will go away (see skbug.com/4965)
     bool internal_getBM(SkBitmap* result);
     static sk_sp<SkSpecialImage> internal_fromBM(SkImageFilter::Proxy*, const SkBitmap&);
diff --git a/tests/SpecialImageTest.cpp b/tests/SpecialImageTest.cpp
index 3240fbd..432adc8 100644
--- a/tests/SpecialImageTest.cpp
+++ b/tests/SpecialImageTest.cpp
@@ -12,6 +12,7 @@
 #include "SkPixmap.h"
 #include "SkSpecialImage.h"
 #include "SkSpecialSurface.h"
+#include "SkSurface.h"
 #include "Test.h"
 #include "TestingSpecialImageAccess.h"
 
@@ -59,10 +60,12 @@
     REPORTER_ASSERT(reporter, kSmallerSize == subset.height());
 
     //--------------
+    // Test that peekTexture reports the correct backing type
     REPORTER_ASSERT(reporter, peekTextureSucceeds ==
                                         !!TestingSpecialImageAccess::PeekTexture(img.get()));
 
     //--------------
+    // Test that peekPixels reports the correct backing type
     SkPixmap pixmap;
     REPORTER_ASSERT(reporter, peekPixelsSucceeds ==
                               !!TestingSpecialImageAccess::PeekPixels(img.get(), &pixmap));
@@ -72,6 +75,7 @@
     }
 
     //--------------
+    // Test that draw restricts itself to the subset
     SkImageInfo info = SkImageInfo::MakeN32(kFullSize, kFullSize, kOpaque_SkAlphaType);
 
     sk_sp<SkSpecialSurface> surf(img->makeSurface(info));
@@ -94,6 +98,32 @@
                                                           kSmallerSize+kPad-1));
     REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kSmallerSize+kPad,
                                                           kSmallerSize+kPad));
+
+    //--------------
+    // Test that makeTightSubset & makeTightSurface return appropriately sized objects
+    // of the correct backing type
+    SkIRect newSubset = SkIRect::MakeWH(subset.width(), subset.height());
+    {
+        sk_sp<SkImage> tightImg(img->makeTightSubset(newSubset));
+
+        REPORTER_ASSERT(reporter, tightImg->width() == subset.width());
+        REPORTER_ASSERT(reporter, tightImg->height() == subset.height());
+        REPORTER_ASSERT(reporter, peekTextureSucceeds == !!tightImg->getTexture());
+        SkPixmap tmpPixmap;
+        REPORTER_ASSERT(reporter, peekPixelsSucceeds == !!tightImg->peekPixels(&tmpPixmap));
+    }
+    {
+        SkImageInfo info = SkImageInfo::MakeN32(subset.width(), subset.height(),
+                                                kPremul_SkAlphaType);
+        sk_sp<SkSurface> tightSurf(img->makeTightSurface(info));
+
+        REPORTER_ASSERT(reporter, tightSurf->width() == subset.width());
+        REPORTER_ASSERT(reporter, tightSurf->height() == subset.height());
+        REPORTER_ASSERT(reporter, peekTextureSucceeds ==
+                     !!tightSurf->getTextureHandle(SkSurface::kDiscardWrite_BackendHandleAccess));
+        SkPixmap tmpPixmap;
+        REPORTER_ASSERT(reporter, peekPixelsSucceeds == !!tightSurf->peekPixels(&tmpPixmap));
+    }
 }
 
 DEF_TEST(SpecialImage_Raster, reporter) {