[skif] Add non-strict asShader() to SkSpecialImage

Moves the replicate definitions of onAsShader() for Graphite and
Ganesh to the base class. The raster special image overrides it so
that it can subset the SkBitmap instead of going through
SkImageShader::MakeSubset(), which does not support bitmaps yet.

Adds a non-strict flag variant of asShader(), the variant that
does not take a tilemode. Right now, the only uses of asShader() are
the full parameter list, so the very simple signature is removed.

Under the assumption that the coordinates will not be outside of the
subset coords, a tilemode is unnecessary, hence the different
signature. I could be easily convinced to give it a different name if
there are any suggestions.

Bug: b/315351386
Change-Id: I61ae7428a68fa3d76b23a4082fd65797102d03d5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/787918
Reviewed-by: James Godfrey-Kittle <jamesgk@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index bedb563..1a61e27 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -12,11 +12,13 @@
 #include "include/core/SkColorType.h"
 #include "include/core/SkImage.h"
 #include "include/core/SkMatrix.h"
+#include "include/core/SkPoint.h"
 #include "include/core/SkShader.h"
 #include "include/core/SkTileMode.h"
 #include "include/private/base/SkAssert.h"
 #include "src/core/SkNextID.h"
 #include "src/image/SkImage_Base.h"
+#include "src/shaders/SkImageShader.h"
 
 // Currently, the raster imagefilters can only handle certain imageinfos. Call this to know if
 // a given info is supported.
@@ -49,16 +51,40 @@
 sk_sp<SkShader> SkSpecialImage::asShader(SkTileMode tileMode,
                                          const SkSamplingOptions& sampling,
                                          const SkMatrix& lm) const {
-    return this->onAsShader(tileMode, sampling, lm);
+    return this->onAsShader(tileMode, sampling, lm, /*strict=*/true);
 }
 
-sk_sp<SkShader> SkSpecialImage::asShader(const SkSamplingOptions& sampling) const {
-    return this->asShader(sampling, SkMatrix::I());
+sk_sp<SkShader> SkSpecialImage::asShaderFast(const SkSamplingOptions& sampling,
+                                             const SkMatrix& lm) const {
+    // Since this should only be used when safely sampling within the subset, the tile mode is
+    // irrelevant, but kClamp is a good default.
+    return this->onAsShader(SkTileMode::kClamp, sampling, lm, /*strict=*/false);
 }
 
-sk_sp<SkShader> SkSpecialImage::asShader(const SkSamplingOptions& sampling,
-                                         const SkMatrix& lm) const {
-    return this->asShader(SkTileMode::kClamp, sampling, lm);
+// TODO(skbug.com/12784): Once bitmap images work with SkImageShader::MakeSubset(), this does not
+// need to be virtual anymore.
+sk_sp<SkShader> SkSpecialImage::onAsShader(SkTileMode tileMode,
+                                           const SkSamplingOptions& sampling,
+                                           const SkMatrix& lm,
+                                           bool strict) const {
+    // The special image's logical (0,0) is at its subset's topLeft() so we need to account for
+    // that in the local matrix used when sampling.
+    SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft());
+    subsetOrigin.postConcat(lm);
+
+    if (strict) {
+        // However, we don't need to modify the subset itself since that is defined with respect
+        // to the base image, and the local matrix is applied before any tiling/clamping.
+        const SkRect subset = SkRect::Make(this->subset());
+
+        // asImage() w/o a subset makes no copy; create the SkImageShader directly to remember
+        // the subset used to access the image.
+        return SkImageShader::MakeSubset(
+                this->asImage(), subset, tileMode, tileMode, sampling, &subsetOrigin);
+    } else {
+        // Ignore 'subset' other than its origin translation applied to the local matrix.
+        return this->asImage()->makeShader(tileMode, tileMode, sampling, subsetOrigin);
+    }
 }
 
 class SkSpecialImage_Raster final : public SkSpecialImage {
@@ -85,14 +111,24 @@
 
     sk_sp<SkShader> onAsShader(SkTileMode tileMode,
                                const SkSamplingOptions& sampling,
-                               const SkMatrix& lm) const override {
-        // TODO(skbug.com/12784): SkImage::makeShader() doesn't support a subset yet, but SkBitmap
-        // supports subset views so create the shader from the subset bitmap instead of fBitmap.
-        SkBitmap subsetBM;
-        if (!this->getROPixels(&subsetBM)) {
-            return nullptr;
+                               const SkMatrix& lm,
+                               bool strict) const override {
+        if (strict) {
+            // TODO(skbug.com/12784): SkImage::makeShader() doesn't support a subset yet, but
+            // SkBitmap supports subset views so create the shader from the subset bitmap instead of
+            // fBitmap.
+            SkBitmap subsetBM;
+            if (!this->getROPixels(&subsetBM)) {
+                return nullptr;
+            }
+            return subsetBM.makeShader(tileMode, tileMode, sampling, lm);
+        } else {
+            // The special image's logical (0,0) is at its subset's topLeft() so we need to
+            // account for that in the local matrix used when sampling.
+            SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft());
+            subsetOrigin.postConcat(lm);
+            return fBitmap.makeShader(tileMode, tileMode, sampling, subsetOrigin);
         }
-        return subsetBM.asImage()->makeShader(tileMode, tileMode, sampling, lm);
     }
 
 private:
diff --git a/src/core/SkSpecialImage.h b/src/core/SkSpecialImage.h
index 89c82bb..6a08f74 100644
--- a/src/core/SkSpecialImage.h
+++ b/src/core/SkSpecialImage.h
@@ -104,8 +104,12 @@
      * any sample that falls outside its internal subset.
      */
     sk_sp<SkShader> asShader(SkTileMode, const SkSamplingOptions&, const SkMatrix& lm) const;
-    sk_sp<SkShader> asShader(const SkSamplingOptions& sampling) const;
-    sk_sp<SkShader> asShader(const SkSamplingOptions& sampling, const SkMatrix& lm) const;
+    /**
+     * Create an SkShader that samples the contents of this special image, assuming that the
+     * coords it's evaluated at will not access pixels beyond its subset
+     * (i.e., non-strict sampling).
+     */
+    sk_sp<SkShader> asShaderFast(const SkSamplingOptions& sampling, const SkMatrix& lm) const;
 
     /**
      *  If the SpecialImage is backed by a gpu texture, return true.
@@ -128,9 +132,11 @@
     // from the content rect by the non-virtual makeSubset().
     virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
 
+    // The default implementation calls `asImage()` or `SkImageShader::MakeSubset` based on `strict`
     virtual sk_sp<SkShader> onAsShader(SkTileMode,
                                        const SkSamplingOptions&,
-                                       const SkMatrix&) const = 0;
+                                       const SkMatrix&,
+                                       bool strict) const;
 
 private:
     const SkIRect        fSubset;
diff --git a/src/gpu/ganesh/image/SkSpecialImage_Ganesh.cpp b/src/gpu/ganesh/image/SkSpecialImage_Ganesh.cpp
index 2b61fe2..bc7babe 100644
--- a/src/gpu/ganesh/image/SkSpecialImage_Ganesh.cpp
+++ b/src/gpu/ganesh/image/SkSpecialImage_Ganesh.cpp
@@ -10,12 +10,10 @@
 #include "include/core/SkColorSpace.h"  // IWYU pragma: keep
 #include "include/core/SkImage.h"
 #include "include/core/SkImageInfo.h"
-#include "include/core/SkMatrix.h"
 #include "include/core/SkRect.h"
 #include "include/gpu/GpuTypes.h"
 #include "include/gpu/GrRecordingContext.h"
 #include "include/private/base/SkAssert.h"
-#include "include/private/base/SkPoint_impl.h"
 #include "include/private/gpu/ganesh/GrTypesPriv.h"
 #include "src/core/SkSpecialImage.h"
 #include "src/gpu/ganesh/GrSurfaceProxy.h"
@@ -23,15 +21,11 @@
 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
 #include "src/gpu/ganesh/image/GrImageUtils.h"
 #include "src/gpu/ganesh/image/SkImage_Ganesh.h"
-#include "src/shaders/SkImageShader.h"
 
 #include <cstddef>
 #include <utility>
 
-class SkShader;
-struct SkSamplingOptions;
 enum SkColorType : int;
-enum class SkTileMode;
 
 class SkSpecialImage_Gpu final : public SkSpecialImage {
 public:
@@ -64,23 +58,6 @@
                 sk_ref_sp(fContext), this->uniqueID(), fView, this->colorInfo());
     }
 
-    sk_sp<SkShader> onAsShader(SkTileMode tileMode,
-                               const SkSamplingOptions& sampling,
-                               const SkMatrix& lm) const override {
-        // The special image's logical (0,0) is at its subset's topLeft() so we need to account for
-        // that in the local matrix used when sampling.
-        SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft());
-        subsetOrigin.postConcat(lm);
-        // However, we don't need to modify the subset itself since that is defined with respect to
-        // the base image, and the local matrix is applied before any tiling/clamping.
-        const SkRect subset = SkRect::Make(this->subset());
-
-        // asImage() w/o a subset makes no copy; create the SkImageShader directly to remember the
-        // subset used to access the image.
-        return SkImageShader::MakeSubset(
-                this->asImage(), subset, tileMode, tileMode, sampling, &subsetOrigin);
-    }
-
 private:
     GrRecordingContext* fContext;
     GrSurfaceProxyView fView;
diff --git a/src/gpu/graphite/SpecialImage_Graphite.cpp b/src/gpu/graphite/SpecialImage_Graphite.cpp
index c44438e..fe1c48b 100644
--- a/src/gpu/graphite/SpecialImage_Graphite.cpp
+++ b/src/gpu/graphite/SpecialImage_Graphite.cpp
@@ -14,7 +14,6 @@
 #include "src/gpu/graphite/Image_Graphite.h"
 #include "src/gpu/graphite/Surface_Graphite.h"
 #include "src/gpu/graphite/TextureUtils.h"
-#include "src/shaders/SkImageShader.h"
 
 namespace skgpu::graphite {
 
@@ -50,23 +49,6 @@
         return sk_make_sp<Image>(this->uniqueID(), fTextureProxyView, this->colorInfo());
     }
 
-    sk_sp<SkShader> onAsShader(SkTileMode tileMode,
-                               const SkSamplingOptions& sampling,
-                               const SkMatrix& lm) const override {
-        // The special image's logical (0,0) is at its subset's topLeft() so we need to account for
-        // that in the local matrix used when sampling.
-        SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft());
-        subsetOrigin.postConcat(lm);
-        // However, we don't need to modify the subset itself since that is defined with respect to
-        // the base image, and the local matrix is applied before any tiling/clamping.
-        const SkRect subset = SkRect::Make(this->subset());
-
-        // asImage() w/o a subset makes no copy; create the SkImageShader directly to remember the
-        // subset used to access the image.
-        return SkImageShader::MakeSubset(this->asImage(), subset, tileMode, tileMode,
-                                         sampling, &subsetOrigin);
-    }
-
 private:
     TextureProxyView fTextureProxyView;
 };