Allow client to force an SkImage snapshot to be unique (and uniquely own its backing store).
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1686163002

Review URL: https://codereview.chromium.org/1686163002
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index fed13a2..711dfda 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -164,9 +164,13 @@
 }
 
 SkImage* SkSurface::newImageSnapshot(Budgeted budgeted) {
-    SkImage* image = asSB(this)->getCachedImage(budgeted);
-    SkSafeRef(image);   // the caller will call unref() to balance this
-    return image;
+    // the caller will call unref() to balance this
+    return asSB(this)->refCachedImage(budgeted, kNo_ForceUnique);
+}
+
+SkImage* SkSurface::newImageSnapshot(Budgeted budgeted, ForceUnique unique) {
+    // the caller will call unref() to balance this
+    return asSB(this)->refCachedImage(budgeted, unique);
 }
 
 SkSurface* SkSurface::newSurface(const SkImageInfo& info) {
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index 7658409..aaa19cf 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -9,6 +9,7 @@
 #define SkSurface_Base_DEFINED
 
 #include "SkCanvas.h"
+#include "SkImagePriv.h"
 #include "SkSurface.h"
 #include "SkSurfacePriv.h"
 
@@ -42,7 +43,7 @@
      *  must faithfully represent the current contents, even if the surface
      *  is changed after this called (e.g. it is drawn to via its canvas).
      */
-    virtual SkImage* onNewImageSnapshot(Budgeted) = 0;
+    virtual SkImage* onNewImageSnapshot(Budgeted, ForceCopyMode) = 0;
 
     /**
      *  Default implementation:
@@ -75,7 +76,7 @@
     virtual void onRestoreBackingMutability() {}
 
     inline SkCanvas* getCachedCanvas();
-    inline SkImage* getCachedImage(Budgeted);
+    inline SkImage* refCachedImage(Budgeted, ForceUnique);
 
     bool hasCachedImage() const { return fCachedImage != nullptr; }
 
@@ -108,12 +109,23 @@
     return fCachedCanvas;
 }
 
-SkImage* SkSurface_Base::getCachedImage(Budgeted budgeted) {
-    if (nullptr == fCachedImage) {
-        fCachedImage = this->onNewImageSnapshot(budgeted);
-        SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
+SkImage* SkSurface_Base::refCachedImage(Budgeted budgeted, ForceUnique unique) {
+    SkImage* snap = fCachedImage;
+    if (kYes_ForceUnique == unique && snap && !snap->unique()) {
+        snap = nullptr;
     }
-    return fCachedImage;
+    if (snap) {
+        return SkRef(snap);
+    }
+    ForceCopyMode fcm = (kYes_ForceUnique == unique) ? kYes_ForceCopyMode :
+                                                       kNo_ForceCopyMode;
+    snap = this->onNewImageSnapshot(budgeted, fcm);
+    if (kNo_ForceUnique == unique) {
+        SkASSERT(!fCachedImage);
+        fCachedImage = SkSafeRef(snap);
+    }
+    SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
+    return snap;
 }
 
 #endif
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 24ac6d3..03fdece 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -76,10 +76,27 @@
                                       &this->props());
 }
 
-SkImage* SkSurface_Gpu::onNewImageSnapshot(Budgeted budgeted) {
+SkImage* SkSurface_Gpu::onNewImageSnapshot(Budgeted budgeted, ForceCopyMode forceCopyMode) {
+    GrRenderTarget* rt = fDevice->accessRenderTarget();
+    SkASSERT(rt);
+    GrTexture* tex = rt->asTexture();
+    SkAutoTUnref<GrTexture> copy;
+    // TODO: Force a copy when the rt is an external resource.
+    if (kYes_ForceCopyMode == forceCopyMode || !tex) {
+        GrSurfaceDesc desc = fDevice->accessRenderTarget()->desc();
+        GrContext* ctx = fDevice->context();
+        desc.fFlags = desc.fFlags & !kRenderTarget_GrSurfaceFlag;
+        copy.reset(ctx->textureProvider()->createTexture(desc, kYes_Budgeted == budgeted));
+        if (!copy) {
+            return nullptr;
+        }
+        if (!ctx->copySurface(copy, rt)) {
+            return nullptr;
+        }
+        tex = copy;
+    }
     const SkImageInfo info = fDevice->imageInfo();
     SkImage* image = nullptr;
-    GrTexture* tex = fDevice->accessRenderTarget()->asTexture();
     if (tex) {
         image = new SkImage_Gpu(info.width(), info.height(), kNeedNewImageUniqueID,
                                 info.alphaType(), tex, budgeted);
@@ -94,7 +111,7 @@
     GrRenderTarget* rt = fDevice->accessRenderTarget();
     // are we sharing our render target with the image? Note this call should never create a new
     // image because onCopyOnWrite is only called when there is a cached image.
-    SkImage* image = this->getCachedImage(kNo_Budgeted);
+    SkAutoTUnref<SkImage> image(this->refCachedImage(kNo_Budgeted, kNo_ForceUnique));
     SkASSERT(image);
     if (rt->asTexture() == as_IB(image)->getTexture()) {
         this->fDevice->replaceRenderTarget(SkSurface::kRetain_ContentChangeMode == mode);
diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h
index 2324154..23aed2c 100644
--- a/src/image/SkSurface_Gpu.h
+++ b/src/image/SkSurface_Gpu.h
@@ -23,7 +23,7 @@
     bool onGetRenderTargetHandle(GrBackendObject*, BackendHandleAccess) override;
     SkCanvas* onNewCanvas() override;
     SkSurface* onNewSurface(const SkImageInfo&) override;
-    SkImage* onNewImageSnapshot(Budgeted) override;
+    SkImage* onNewImageSnapshot(Budgeted, ForceCopyMode) override;
     void onCopyOnWrite(ContentChangeMode) override;
     void onDiscard() override;
 
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
index d9763c0..37790a0 100644
--- a/src/image/SkSurface_Raster.cpp
+++ b/src/image/SkSurface_Raster.cpp
@@ -24,7 +24,7 @@
 
     SkCanvas* onNewCanvas() override;
     SkSurface* onNewSurface(const SkImageInfo&) override;
-    SkImage* onNewImageSnapshot(Budgeted) override;
+    SkImage* onNewImageSnapshot(Budgeted, ForceCopyMode) override;
     void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override;
     void onCopyOnWrite(ContentChangeMode) override;
     void onRestoreBackingMutability() override;
@@ -118,18 +118,20 @@
     canvas->drawBitmap(fBitmap, x, y, paint);
 }
 
-SkImage* SkSurface_Raster::onNewImageSnapshot(Budgeted) {
+SkImage* SkSurface_Raster::onNewImageSnapshot(Budgeted, ForceCopyMode forceCopyMode) {
     if (fWeOwnThePixels) {
         // SkImage_raster requires these pixels are immutable for its full lifetime.
         // We'll undo this via onRestoreBackingMutability() if we can avoid the COW.
         if (SkPixelRef* pr = fBitmap.pixelRef()) {
             pr->setTemporarilyImmutable();
         }
+    } else {
+        forceCopyMode = kYes_ForceCopyMode;
     }
+
     // Our pixels are in memory, so read access on the snapshot SkImage could be cheap.
     // Lock the shared pixel ref to ensure peekPixels() is usable.
-    return SkNewImageFromRasterBitmap(fBitmap,
-                                      fWeOwnThePixels ? kNo_ForceCopyMode : kYes_ForceCopyMode);
+    return SkNewImageFromRasterBitmap(fBitmap, forceCopyMode);
 }
 
 void SkSurface_Raster::onRestoreBackingMutability() {
@@ -141,8 +143,9 @@
 
 void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
     // are we sharing pixelrefs with the image?
-    SkASSERT(this->getCachedImage(kNo_Budgeted));
-    if (SkBitmapImageGetPixelRef(this->getCachedImage(kNo_Budgeted)) == fBitmap.pixelRef()) {
+    SkAutoTUnref<SkImage> cached(this->refCachedImage(kNo_Budgeted, kNo_ForceUnique));
+    SkASSERT(cached);
+    if (SkBitmapImageGetPixelRef(cached) == fBitmap.pixelRef()) {
         SkASSERT(fWeOwnThePixels);
         if (kDiscard_ContentChangeMode == mode) {
             fBitmap.allocPixels();