[graphite] Add Image::Copy to handle both blit and draw copies

Bug: b/333909822
Change-Id: I5dbc33aa9b72b6509d9cd503eaf4f7a8dcde5669
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/838236
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
index 21e0b4a..34ae40c 100644
--- a/src/gpu/graphite/Device.cpp
+++ b/src/gpu/graphite/Device.cpp
@@ -40,7 +40,6 @@
 #include "src/gpu/graphite/geom/IntersectionTree.h"
 #include "src/gpu/graphite/geom/Shape.h"
 #include "src/gpu/graphite/geom/Transform_graphite.h"
-#include "src/gpu/graphite/task/CopyTask.h"
 #include "src/gpu/graphite/text/TextAtlasManager.h"
 
 #include "include/core/SkColorSpace.h"
@@ -423,73 +422,7 @@
                 colorInfo.colorType(), this->target()->textureInfo());
         srcView = {sk_ref_sp(this->target()), readSwizzle};
     }
-    TextureProxyView copiedView = TextureProxyView::Copy(fRecorder,
-                                                         colorInfo,
-                                                         srcView,
-                                                         subset,
-                                                         budgeted,
-                                                         mipmapped,
-                                                         backingFit);
-    if (!copiedView) {
-        // The surface didn't support read pixels, so copy-as-draw using the image view
-        sk_sp<Image> sampleableSurface = Image::WrapDevice(sk_ref_sp(this));
-        if (sampleableSurface) {
-            return sampleableSurface->copyImage(fRecorder, subset, budgeted, mipmapped, backingFit);
-        }
-        // Cannot be copied by blit nor by a draw, so cannot be done.
-        return nullptr;
-    }
-
-    return sk_make_sp<Image>(std::move(copiedView), colorInfo);
-}
-
-TextureProxyView TextureProxyView::Copy(Recorder* recorder,
-                                        const SkColorInfo& srcColorInfo,
-                                        const TextureProxyView& srcView,
-                                        SkIRect srcRect,
-                                        Budgeted budgeted,
-                                        Mipmapped mipmapped,
-                                        SkBackingFit backingFit) {
-    SkASSERT(!(mipmapped == Mipmapped::kYes && backingFit == SkBackingFit::kApprox));
-
-    SkASSERT(srcView.proxy()->isFullyLazy() ||
-             SkIRect::MakeSize(srcView.proxy()->dimensions()).contains(srcRect));
-
-    if (!recorder->priv().caps()->supportsReadPixels(srcView.proxy()->textureInfo())) {
-        // Caller is responsible for copy-as-draw fallbacks
-        return {};
-    }
-
-    skgpu::graphite::TextureInfo textureInfo =
-            recorder->priv().caps()->getTextureInfoForSampledCopy(srcView.proxy()->textureInfo(),
-                                                                  mipmapped);
-
-    sk_sp<TextureProxy> dest = TextureProxy::Make(
-            recorder->priv().caps(),
-            backingFit == SkBackingFit::kApprox ? GetApproxSize(srcRect.size()) : srcRect.size(),
-            textureInfo,
-            budgeted);
-    if (!dest) {
-        return {};
-    }
-
-    sk_sp<CopyTextureToTextureTask> copyTask = CopyTextureToTextureTask::Make(srcView.refProxy(),
-                                                                              srcRect,
-                                                                              dest,
-                                                                              {0, 0});
-    if (!copyTask) {
-        return {};
-    }
-
-    recorder->priv().add(std::move(copyTask));
-
-    if (mipmapped == Mipmapped::kYes) {
-        if (!GenerateMipmaps(recorder, dest, srcColorInfo)) {
-            SKGPU_LOG_W("TextureProxyView::Copy: Failed to generate mipmaps");
-        }
-    }
-
-    return { std::move(dest), srcView.swizzle() };
+    return Image::Copy(fRecorder, srcView, colorInfo, subset, budgeted, mipmapped, backingFit);
 }
 
 bool Device::onReadPixels(const SkPixmap& pm, int srcX, int srcY) {
diff --git a/src/gpu/graphite/Image_Graphite.cpp b/src/gpu/graphite/Image_Graphite.cpp
index 6437a89..4576e22 100644
--- a/src/gpu/graphite/Image_Graphite.cpp
+++ b/src/gpu/graphite/Image_Graphite.cpp
@@ -23,6 +23,8 @@
 #include "src/gpu/graphite/RecorderPriv.h"
 #include "src/gpu/graphite/ResourceProvider.h"
 #include "src/gpu/graphite/Texture.h"
+#include "src/gpu/graphite/TextureUtils.h"
+#include "src/gpu/graphite/task/CopyTask.h"
 
 #if defined(GRAPHITE_TEST_UTILS)
 #include "include/gpu/graphite/Context.h"
@@ -53,6 +55,62 @@
     return image;
 }
 
+sk_sp<Image> Image::Copy(Recorder* recorder,
+                         const TextureProxyView& srcView,
+                         const SkColorInfo& srcColorInfo,
+                         const SkIRect& subset,
+                         Budgeted budgeted,
+                         Mipmapped mipmapped,
+                         SkBackingFit backingFit) {
+    SkASSERT(!(mipmapped == Mipmapped::kYes && backingFit == SkBackingFit::kApprox));
+    if (!srcView) {
+        return nullptr;
+    }
+
+    SkASSERT(srcView.proxy()->isFullyLazy() ||
+             SkIRect::MakeSize(srcView.proxy()->dimensions()).contains(subset));
+
+    if (!recorder->priv().caps()->supportsReadPixels(srcView.proxy()->textureInfo())) {
+        if (!recorder->priv().caps()->isTexturable(srcView.proxy()->textureInfo())) {
+            // The texture is not blittable nor texturable so copying cannot be done.
+            return nullptr;
+        }
+        // Copy-as-draw
+        sk_sp<Image> srcImage(new Image(srcView, srcColorInfo));
+        return CopyAsDraw(recorder, srcImage.get(), subset, budgeted, mipmapped, backingFit);
+    }
+
+
+    skgpu::graphite::TextureInfo textureInfo =
+            recorder->priv().caps()->getTextureInfoForSampledCopy(srcView.proxy()->textureInfo(),
+                                                                  mipmapped);
+
+    sk_sp<TextureProxy> dst = TextureProxy::Make(
+            recorder->priv().caps(),
+            backingFit == SkBackingFit::kApprox ? GetApproxSize(subset.size()) : subset.size(),
+            textureInfo,
+            budgeted);
+    if (!dst) {
+        return nullptr;
+    }
+
+    auto copyTask = CopyTextureToTextureTask::Make(srcView.refProxy(), subset, dst, {0, 0});
+    if (!copyTask) {
+        return nullptr;
+    }
+
+    recorder->priv().add(std::move(copyTask));
+
+    if (mipmapped == Mipmapped::kYes) {
+        if (!GenerateMipmaps(recorder, dst, srcColorInfo)) {
+            SKGPU_LOG_W("Image::Copy failed to generate mipmaps");
+            return nullptr;
+        }
+    }
+
+    return sk_sp<Image>(new Image({std::move(dst), srcView.swizzle()}, srcColorInfo));
+}
+
 size_t Image::textureSize() const {
     if (!fTextureProxyView.proxy()) {
         return 0;
@@ -104,30 +162,9 @@
                               Budgeted budgeted,
                               Mipmapped mipmapped,
                               SkBackingFit backingFit) const {
-    SkASSERT(!(mipmapped == Mipmapped::kYes && backingFit == SkBackingFit::kApprox));
-    const TextureProxyView& srcView = this->textureProxyView();
-    if (!srcView) {
-        return nullptr;
-    }
-
-    // Attempt copying as a blit first.
-    const SkColorInfo& colorInfo = this->imageInfo().colorInfo();
     this->notifyInUse(recorder);
-    TextureProxyView copiedView = TextureProxyView::Copy(recorder,
-                                                         colorInfo,
-                                                         srcView,
-                                                         subset,
-                                                         budgeted,
-                                                         mipmapped,
-                                                         backingFit);
-    if (copiedView) {
-        return sk_make_sp<Image>(std::move(copiedView), colorInfo);
-    }
-
-    // Perform the copy as a draw; since the proxy has been wrapped in an Image, it should be
-    // texturable.
-    SkASSERT(recorder->priv().caps()->isTexturable(srcView.proxy()->textureInfo()));
-    return CopyAsDraw(recorder, this, subset, budgeted, mipmapped, backingFit);
+    return Image::Copy(recorder, fTextureProxyView, this->imageInfo().colorInfo(),
+                       subset, budgeted, mipmapped, backingFit);
 }
 
 sk_sp<SkImage> Image::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
diff --git a/src/gpu/graphite/Image_Graphite.h b/src/gpu/graphite/Image_Graphite.h
index 66da222..7f7e075 100644
--- a/src/gpu/graphite/Image_Graphite.h
+++ b/src/gpu/graphite/Image_Graphite.h
@@ -32,6 +32,16 @@
     // pending tasks when the Image is used in a draw to another canvas.
     static sk_sp<Image> WrapDevice(sk_sp<Device> device);
 
+    // Create an Image by copying the provided texture proxy view into a new texturable proxy.
+    // The source texture does not have to be texturable if it is blittable.
+    static sk_sp<Image> Copy(Recorder*,
+                             const TextureProxyView& srcView,
+                             const SkColorInfo&,
+                             const SkIRect& subset,
+                             Budgeted,
+                             Mipmapped,
+                             SkBackingFit);
+
     const TextureProxyView& textureProxyView() const { return fTextureProxyView; }
 
     // Always copy this image, even if 'subset' and mipmapping match this image exactly.
diff --git a/src/gpu/graphite/TextureProxyView.h b/src/gpu/graphite/TextureProxyView.h
index 96f28a3..5addb14 100644
--- a/src/gpu/graphite/TextureProxyView.h
+++ b/src/gpu/graphite/TextureProxyView.h
@@ -89,17 +89,6 @@
         return std::move(fProxy);
     }
 
-    // This Copy does not perform any copy-as-draw fallbacks; if 'srcView' does not support reading
-    // pixels, then this will return an empty proxy view. On success, the proxy view will be
-    // sampleable.
-    static TextureProxyView Copy(Recorder*,
-                                 const SkColorInfo& srcColorInfo,
-                                 const TextureProxyView& srcView,
-                                 SkIRect srcRect,
-                                 Budgeted,
-                                 Mipmapped,
-                                 SkBackingFit);
-
 private:
     sk_sp<TextureProxy> fProxy;
     Swizzle fSwizzle;