expose direct methods for decoding to an image

These are meant to contrast MakeFromEncoded(), and emphasize that it is deferred/cached,
while the new methods are not.

Change-Id: I83ac22394cb14cdc84ff8507a514bf708734b84f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/234476
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 98892fd..6a833c6 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -10,6 +10,8 @@
 
  * SkDrawLooper is no longer supported in SkPaint or SkCanvas.
 
+ * SkImage: new factories: DecodeToRaster, DecodeToTexture
+
  * SkImageFilter API refactor started:
    - Provide new factory API in include/effects/SkImageFilters
    - Consolidated enum types to use SkTileMode and SkColorChannel
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 96809fd..fef0021 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -154,19 +154,77 @@
     static sk_sp<SkImage> MakeFromGenerator(std::unique_ptr<SkImageGenerator> imageGenerator,
                                             const SkIRect* subset = nullptr);
 
-    /** Creates SkImage from encoded data.
-        subset allows selecting a portion of the full image. Pass nullptr to select the entire
-        image; otherwise, subset must be contained by image bounds.
-
-        SkImage is returned if format of the encoded data is recognized and supported.
-        Recognized formats vary by platform.
-
-        @param encoded  data of SkImage to decode
-        @param subset   bounds of returned SkImage; may be nullptr
-        @return         created SkImage, or nullptr
+    /**
+     *  Return an image backed by the encoded data, but attempt to defer decoding until the image
+     *  is actually used/drawn. This deferral allows the system to cache the result, either on the
+     *  CPU or on the GPU, depending on where the image is drawn. If memory is low, the cache may
+     *  be purged, causing the next draw of the image to have to re-decode.
+     *
+     *  The subset parameter specifies a area within the decoded image to create the image from.
+     *  If subset is null, then the entire image is returned.
+     *
+     *  This is similar to DecodeTo[Raster,Texture], but this method will attempt to defer the
+     *  actual decode, while the DecodeTo... method explicitly decode and allocate the backend
+     *  when the call is made.
+     *
+     *  If the encoded format is not supported, or subset is outside of the bounds of the decoded
+     *  image, nullptr is returned.
+     *
+     *  @param encoded  the encoded data
+     *  @param length   the number of bytes of encoded data
+     *  @param subset   the bounds of the pixels within the decoded image to return. may be null.
+     *  @return         created SkImage, or nullptr
     */
     static sk_sp<SkImage> MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset = nullptr);
 
+    /**
+     *  Decode the data in encoded/length into a raster image.
+     *
+     *  The subset parameter specifies a area within the decoded image to create the image from.
+     *  If subset is null, then the entire image is returned.
+     *
+     *  This is similar to MakeFromEncoded, but this method will always decode immediately, and
+     *  allocate the memory for the pixels for the lifetime of the returned image.
+     *
+     *  If the encoded format is not supported, or subset is outside of the bounds of the decoded
+     *  image, nullptr is returned.
+     *
+     *  @param encoded  the encoded data
+     *  @param length   the number of bytes of encoded data
+     *  @param subset   the bounds of the pixels within the decoded image to return. may be null.
+     *  @return         created SkImage, or nullptr
+     */
+    static sk_sp<SkImage> DecodeToRaster(const void* encoded, size_t length,
+                                         const SkIRect* subset = nullptr);
+    static sk_sp<SkImage> DecodeToRaster(const sk_sp<SkData>& data,
+                                         const SkIRect* subset = nullptr) {
+        return DecodeToRaster(data->data(), data->size(), subset);
+    }
+
+    /**
+     *  Decode the data in encoded/length into a texture-backed image.
+     *
+     *  The subset parameter specifies a area within the decoded image to create the image from.
+     *  If subset is null, then the entire image is returned.
+     *
+     *  This is similar to MakeFromEncoded, but this method will always decode immediately, and
+     *  allocate the texture for the pixels for the lifetime of the returned image.
+     *
+     *  If the encoded format is not supported, or subset is outside of the bounds of the decoded
+     *  image, nullptr is returned.
+     *
+     *  @param encoded  the encoded data
+     *  @param length   the number of bytes of encoded data
+     *  @param subset   the bounds of the pixels within the decoded image to return. may be null.
+     *  @return         created SkImage, or nullptr
+     */
+    static sk_sp<SkImage> DecodeToTexture(GrContext* ctx, const void* encoded, size_t length,
+                                          const SkIRect* subset = nullptr);
+    static sk_sp<SkImage> DecodeToTexture(GrContext* ctx, const sk_sp<SkData>& data,
+                                          const SkIRect* subset = nullptr) {
+        return DecodeToTexture(ctx, data->data(), data->size(), subset);
+    }
+
     // Experimental
     enum CompressionType {
         kETC1_CompressionType,
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 079992a..ae0998c 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -410,6 +410,10 @@
 
 #if !SK_SUPPORT_GPU
 
+sk_sp<SkImage> SkImage::DecodeToTexture(GrContext*, const void*, size_t, const SkIRect*) {
+    return nullptr;
+}
+
 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
                                         const GrBackendTexture& tex, GrSurfaceOrigin origin,
                                         SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index fbccdc2..5a9c9b2 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -287,6 +287,44 @@
     return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr;
 }
 
+sk_sp<SkImage> SkImage::DecodeToRaster(const void* encoded, size_t length, const SkIRect* subset) {
+    // The generator will not outlive this function, so we can wrap the encoded data without copy
+    auto gen = SkImageGenerator::MakeFromEncoded(SkData::MakeWithoutCopy(encoded, length));
+    if (!gen) {
+        return nullptr;
+    }
+    SkImageInfo info = gen->getInfo();
+    if (info.isEmpty()) {
+        return nullptr;
+    }
+
+    SkIPoint origin = {0, 0};
+    if (subset) {
+        if (!SkIRect::MakeWH(info.width(), info.height()).contains(*subset)) {
+            return nullptr;
+        }
+        info = info.makeWH(subset->width(), subset->height());
+        origin = {subset->x(), subset->y()};
+    }
+
+    size_t rb = info.minRowBytes();
+    if (rb == 0) {
+        return nullptr; // rb was too big
+    }
+    size_t size = info.computeByteSize(rb);
+    if (size == SIZE_MAX) {
+        return nullptr;
+    }
+    auto data = SkData::MakeUninitialized(size);
+
+    SkPixmap pmap(info, data->writable_data(), rb);
+    if (!generate_pixels(gen.get(), pmap, origin.x(), origin.y())) {
+        return nullptr;
+    }
+
+    return SkImage::MakeRasterData(info, data, rb);
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
 #if SK_SUPPORT_GPU
@@ -498,4 +536,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+sk_sp<SkImage> SkImage::DecodeToTexture(GrContext* ctx, const void* encoded, size_t length,
+                                        const SkIRect* subset) {
+    // img will not survive this function, so we don't need to copy/own the encoded data,
+    auto img = MakeFromEncoded(SkData::MakeWithoutCopy(encoded, length), subset);
+    if (!img) {
+        return nullptr;
+    }
+    return img->makeTextureImage(ctx, nullptr);
+}
+
 #endif