New version of SkImage::MakeFromYUVAPixmaps()

Takes SkYUVAPixmaps. Still implemented in terms of SkYUVAIndex[4]
internally.

Replace all internal use cases except wacky_yuv_formats.

Takes GrRecordingContext rather than GrContext.

SkVideoDecoder updated to take GrRecordingContext.

Bug: skia:10632

Change-Id: I6e9b9b6a4f11333bce6f87c1ebff0acb297f6540
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/316837
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Adlai Holler <adlai@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index edf7e7c..c4c59e6 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -9,6 +9,12 @@
 
   * <insert new release notes here>
 
+  * Alternate SkImage::MakeFromYUVAPixmaps signature. Takes SkYUVAPixmaps, which specifies
+    planar configuration in a more structured manner. Currently limited to tri-planar
+    configurations without alpha but will be expanded. Older signature will become
+    deprecated when the new signature supports a broad range of planar configurations.
+    https://review.skia.org/316837
+
   * Switch to using Microsoft::WRL::ComPtr for wrapping D3D12 objects.
     https://review.skia.org/314957
 
diff --git a/experimental/ffmpeg/SkVideoDecoder.cpp b/experimental/ffmpeg/SkVideoDecoder.cpp
index 835a9b50..2d46f66 100644
--- a/experimental/ffmpeg/SkVideoDecoder.cpp
+++ b/experimental/ffmpeg/SkVideoDecoder.cpp
@@ -9,6 +9,7 @@
 #include "include/core/SkColorSpace.h"
 #include "include/core/SkImage.h"
 #include "include/core/SkYUVAIndex.h"
+#include "include/core/SkYUVAPixmaps.h"
 
 static SkYUVColorSpace get_yuvspace(AVColorSpace space) {
     // this is pretty incomplete -- TODO: look to convert more AVColorSpaces
@@ -159,29 +160,23 @@
     return stream->seek(SkToSizeT(pos)) ? pos : -1;
 }
 
-static sk_sp<SkImage> make_yuv_420(GrContext* gr, int w, int h,
-                                   uint8_t* const data[], int const strides[],
-                                   SkYUVColorSpace yuv_space,
+static sk_sp<SkImage> make_yuv_420(GrRecordingContext* rContext,
+                                   int w, int h,
+                                   uint8_t* const data[],
+                                   int const strides[],
+                                   SkYUVColorSpace yuvSpace,
                                    sk_sp<SkColorSpace> cs) {
-    SkImageInfo info[3];
-    info[0] = SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType);
-    info[1] = SkImageInfo::Make(w/2, h/2, kGray_8_SkColorType, kOpaque_SkAlphaType);
-    info[2] = SkImageInfo::Make(w/2, h/2, kGray_8_SkColorType, kOpaque_SkAlphaType);
+    SkYUVAInfo yuvaInfo({w, h}, SkYUVAInfo::PlanarConfig::kY_U_V_420, yuvSpace);
+    SkPixmap pixmaps[3];
+    pixmaps[0].reset(SkImageInfo::MakeA8(w, h), data[0], strides[0]);
+    w = (w + 1)/2;
+    h = (h + 1)/2;
+    pixmaps[1].reset(SkImageInfo::MakeA8(w, h), data[1], strides[1]);
+    pixmaps[2].reset(SkImageInfo::MakeA8(w, h), data[2], strides[2]);
+    auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, pixmaps);
 
-    SkPixmap pm[4];
-    for (int i = 0; i < 3; ++i) {
-        pm[i] = SkPixmap(info[i], data[i], strides[i]);
-    }
-    pm[3].reset();  // no alpha
-
-    SkYUVAIndex indices[4];
-    indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR};
-    indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR};
-    indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
-    indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
-
-    return SkImage::MakeFromYUVAPixmaps(gr, yuv_space, pm, indices, {w, h},
-                                        kTopLeft_GrSurfaceOrigin, false, false, cs);
+    return SkImage::MakeFromYUVAPixmaps(
+            rContext, yuvaPixmaps, GrMipMapped::kNo, false, std::move(cs));
 }
 
 // Init with illegal values, so our first compare will fail, forcing us to compute
@@ -222,8 +217,8 @@
 
     switch (frame->format) {
         case AV_PIX_FMT_YUV420P:
-            if (auto image = make_yuv_420(fGr, frame->width, frame->height, frame->data,
-                                          frame->linesize, yuv_space, fCSCache.fCS)) {
+            if (auto image = make_yuv_420(fRecordingContext, frame->width, frame->height,
+                                          frame->data, frame->linesize, yuv_space, fCSCache.fCS)) {
                 return image;
             }
             break;
@@ -311,7 +306,7 @@
     return nullptr;
 }
 
-SkVideoDecoder::SkVideoDecoder(GrContext* gr) : fGr(gr) {}
+SkVideoDecoder::SkVideoDecoder(GrRecordingContext* rContext) : fRecordingContext(rContext) {}
 
 SkVideoDecoder::~SkVideoDecoder() {
     this->reset();
diff --git a/experimental/ffmpeg/SkVideoDecoder.h b/experimental/ffmpeg/SkVideoDecoder.h
index 022009d..85c25a7 100644
--- a/experimental/ffmpeg/SkVideoDecoder.h
+++ b/experimental/ffmpeg/SkVideoDecoder.h
@@ -20,11 +20,11 @@
 
 class SkVideoDecoder {
 public:
-    SkVideoDecoder(GrContext* gr = nullptr);
+    SkVideoDecoder(GrRecordingContext* = nullptr);
     ~SkVideoDecoder();
 
     void reset();
-    void setGrContext(GrContext* gr) { fGr = gr; }
+    void setGrContext(GrRecordingContext* rContext) { fRecordingContext = rContext; }
 
     bool loadStream(std::unique_ptr<SkStream>);
     bool rewind();
@@ -52,7 +52,7 @@
         void update(AVColorPrimaries, AVColorTransferCharacteristic);
     };
 
-    GrContext*          fGr = nullptr;  // not owned by us
+    GrRecordingContext* fRecordingContext = nullptr;  // not owned by us
 
     std::unique_ptr<SkStream>   fStream;
 
diff --git a/gm/video_decoder.cpp b/gm/video_decoder.cpp
index c2480f0..cd4aa99 100644
--- a/gm/video_decoder.cpp
+++ b/gm/video_decoder.cpp
@@ -34,12 +34,12 @@
     }
 
     void onDraw(SkCanvas* canvas) override {
-        GrContext* gr = canvas->getGrContext();
-        if (!gr) {
+        GrContext* rContext = canvas->recordingContext();
+        if (!rContext) {
             return;
         }
 
-        fDecoder.setGrContext(gr); // gr can change over time in viewer
+        fDecoder.setGrContext(rContext);  // context can change over time in viewer
 
         double timeStamp;
         auto img = fDecoder.nextImage(&timeStamp);
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 7b12a82..b5dd49f 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -31,6 +31,7 @@
 class SkPaint;
 class SkPicture;
 class SkSurface;
+class SkYUVAPixmaps;
 class GrBackendTexture;
 class GrContext;
 class GrDirectContext;
@@ -505,6 +506,35 @@
             GrSurfaceOrigin imageOrigin, bool buildMips, bool limitToMaxTextureSize = false,
             sk_sp<SkColorSpace> imageColorSpace = nullptr);
 
+    /** Creates SkImage from SkYUVAPixmaps.
+
+        The image will remain planar with each plane converted to a texture using the passed
+        GrRecordingContext.
+
+        SkYUVAPixmaps has a SkYUVAInfo which specifies the transformation from YUV to RGB.
+        The SkColorSpace of the resulting RGB values is specified by imageColorSpace. This will
+        be the SkColorSpace reported by the image and when drawn the RGB values will be converted
+        from this space into the destination space (if the destination is tagged).
+
+        Currently, this is only supported using the GPU backend and will fail if context is nullptr.
+
+        SkYUVAPixmaps does not need to remain valid after this returns.
+
+        @param context                GPU context
+        @param pixmaps                The planes as pixmaps with supported SkYUVAInfo that
+                                      specifies conversion to RGB.
+        @param buildMips              create internal YUVA textures as mip map if kYes. This is
+                                      silently ignored if the context does not support mip maps.
+        @param limitToMaxTextureSize  downscale image to GPU maximum texture size, if necessary
+        @param imageColorSpace        range of colors of the resulting image; may be nullptr
+        @return                       created SkImage, or nullptr
+    */
+    static sk_sp<SkImage> MakeFromYUVAPixmaps(GrRecordingContext* context,
+                                              const SkYUVAPixmaps& pixmaps,
+                                              GrMipMapped buildMips = GrMipmapped::kNo,
+                                              bool limitToMaxTextureSize = false,
+                                              sk_sp<SkColorSpace> imageColorSpace = nullptr);
+
     /** To be deprecated.
     */
     static sk_sp<SkImage> MakeFromYUVTexturesCopyWithExternalBackend(
diff --git a/include/core/SkYUVAPixmaps.h b/include/core/SkYUVAPixmaps.h
index 51ed9e4..7d5d0ae 100644
--- a/include/core/SkYUVAPixmaps.h
+++ b/include/core/SkYUVAPixmaps.h
@@ -232,7 +232,7 @@
     /**
      * Conversion to legacy SkYUVA data structures.
      */
-    bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[4]);
+    bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[4]) const;
 
 private:
     SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp<SkData>);
diff --git a/src/core/SkYUVAPixmaps.cpp b/src/core/SkYUVAPixmaps.cpp
index 9dfa4a5..afd015a 100644
--- a/src/core/SkYUVAPixmaps.cpp
+++ b/src/core/SkYUVAPixmaps.cpp
@@ -129,7 +129,7 @@
 SkYUVAPixmapInfo::SkYUVAPixmapInfo(const SkYUVAInfo& yuvaInfo,
                                    DataType dataType,
                                    const size_t rowBytes[kMaxPlanes]) {
-    SkColorType colorTypes[kMaxPlanes];
+    SkColorType colorTypes[kMaxPlanes] = {};
     int n = yuvaInfo.numPlanes();
     for (int i = 0; i < n; ++i) {
         // Currently all PlanarConfigs have 1 channel per plane.
@@ -216,9 +216,10 @@
 
 SkYUVAPixmaps SkYUVAPixmaps::FromExternalPixmaps(const SkYUVAInfo& yuvaInfo,
                                                  const SkPixmap pixmaps[kMaxPlanes]) {
-    SkColorType colorTypes[kMaxPlanes];
-    size_t rowBytes[kMaxPlanes];
-    for (int i = 0; i < kMaxPlanes; ++i) {
+    SkColorType colorTypes[kMaxPlanes] = {};
+    size_t rowBytes[kMaxPlanes] = {};
+    int numPlanes = yuvaInfo.numPlanes();
+    for (int i = 0; i < numPlanes; ++i) {
         colorTypes[i] = pixmaps[i].colorType();
         rowBytes[i] = pixmaps[i].rowBytes();
     }
@@ -239,10 +240,10 @@
 
 SkYUVAPixmaps::SkYUVAPixmaps(const SkYUVAInfo& yuvaInfo, const SkPixmap pixmaps[kMaxPlanes])
         : fYUVAInfo(yuvaInfo) {
-    std::copy_n(pixmaps, kMaxPlanes, fPlanes.data());
+    std::copy_n(pixmaps, yuvaInfo.numPlanes(), fPlanes.data());
 }
 
-bool SkYUVAPixmaps::toLegacy(SkYUVASizeInfo* yuvaSizeInfo, SkYUVAIndex yuvaIndices[4]) {
+bool SkYUVAPixmaps::toLegacy(SkYUVASizeInfo* yuvaSizeInfo, SkYUVAIndex yuvaIndices[4]) const {
     if (!this->isValid()) {
         return false;
     }
@@ -293,14 +294,12 @@
     if (!ok) {
         return false;
     }
-    yuvaSizeInfo->fOrigin = fYUVAInfo.origin();
-    SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
-    int n = fYUVAInfo.planeDimensions(planeDimensions);
-    for (int i = 0; i < n; ++i) {
-        yuvaSizeInfo->fSizes[i] = fPlanes[i].dimensions();
-        yuvaSizeInfo->fWidthBytes[i] = fPlanes[i].rowBytes();
-        if (fPlanes[i].dimensions() != planeDimensions[i]) {
-            return false;
+    if (yuvaSizeInfo) {
+        yuvaSizeInfo->fOrigin = fYUVAInfo.origin();
+        int n = fYUVAInfo.numPlanes();
+        for (int i = 0; i < n; ++i) {
+            yuvaSizeInfo->fSizes[i] = fPlanes[i].dimensions();
+            yuvaSizeInfo->fWidthBytes[i] = fPlanes[i].rowBytes();
         }
     }
     return true;
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 37d3dd2..8674de4 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -593,6 +593,14 @@
     return nullptr;
 }
 
+sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(GrRecordingContext* context,
+                                            const SkYUVAPixmaps& pixmaps,
+                                            GrMipMapped buildMips,
+                                            bool limitToMaxTextureSize,
+                                            sk_sp<SkColorSpace> imageColorSpace) {
+    return nullptr;
+}
+
 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopyWithExternalBackend(
         GrContext*, SkYUVColorSpace, const GrBackendTexture[3], GrSurfaceOrigin,
         const GrBackendTexture&, sk_sp<SkColorSpace>) {
diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp
index 546c821..cbd78ec 100644
--- a/src/image/SkImage_GpuYUVA.cpp
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -9,6 +9,7 @@
 #include <cstring>
 #include <type_traits>
 
+#include "include/core/SkYUVAPixmaps.h"
 #include "include/core/SkYUVASizeInfo.h"
 #include "include/gpu/GrDirectContext.h"
 #include "include/gpu/GrRecordingContext.h"
@@ -290,10 +291,10 @@
 
     // Make proxies
     GrSurfaceProxyView tempViews[4];
+    int maxTextureSize = context->priv().caps()->maxTextureSize();
     for (int i = 0; i < numPixmaps; ++i) {
         const SkPixmap* pixmap = &yuvaPixmaps[i];
         SkAutoPixmapStorage resized;
-        int maxTextureSize = context->priv().caps()->maxTextureSize();
         int maxDim = std::max(yuvaPixmaps[i].width(), yuvaPixmaps[i].height());
         if (limitToMaxTextureSize && maxDim > maxTextureSize) {
             float scale = static_cast<float>(maxTextureSize) / maxDim;
@@ -312,7 +313,6 @@
         bmp.installPixels(*pixmap);
         GrBitmapTextureMaker bitmapMaker(context, bmp, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
         GrMipmapped mipMapped = buildMips ? GrMipmapped::kYes : GrMipmapped::kNo;
-        GrSurfaceProxyView view;
         tempViews[i] = bitmapMaker.view(mipMapped);
         if (!tempViews[i]) {
             return nullptr;
@@ -321,7 +321,76 @@
 
     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize, kNeedNewImageUniqueID,
                                        yuvColorSpace, tempViews, numPixmaps, yuvaIndices,
-                                       imageOrigin, imageColorSpace);
+                                       imageOrigin, std::move(imageColorSpace));
+}
+
+sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(GrRecordingContext* context,
+                                            const SkYUVAPixmaps& pixmaps,
+                                            GrMipMapped buildMips,
+                                            bool limitToMaxTextureSize,
+                                            sk_sp<SkColorSpace> imageColorSpace) {
+    if (!context) {
+        return nullptr;  // until we impl this for raster backend
+    }
+
+    if (!pixmaps.isValid()) {
+        return nullptr;
+    }
+
+    SkYUVAIndex yuvaIndices[4];
+    if (!pixmaps.toLegacy(nullptr, yuvaIndices)) {
+        return nullptr;
+    }
+
+    // SkImage_GpuYUVA doesn't yet support different encoded origins.
+    if (pixmaps.yuvaInfo().origin() != kTopLeft_SkEncodedOrigin) {
+        return nullptr;
+    }
+
+    if (!context->priv().caps()->mipmapSupport()) {
+        buildMips = GrMipMapped::kNo;
+    }
+
+    // Make proxies
+    GrSurfaceProxyView tempViews[4];
+    int numPlanes = pixmaps.numPlanes();
+    int maxTextureSize = context->priv().caps()->maxTextureSize();
+    for (int i = 0; i < numPlanes; ++i) {
+        const SkPixmap* pixmap = &pixmaps.plane(i);
+        SkAutoPixmapStorage resized;
+        int maxDim = std::max(pixmap->width(), pixmap->height());
+        if (maxDim > maxTextureSize) {
+            if (!limitToMaxTextureSize) {
+                return nullptr;
+            }
+            float scale = static_cast<float>(maxTextureSize)/maxDim;
+            int newWidth  = std::min(static_cast<int>(pixmap->width() *scale), maxTextureSize);
+            int newHeight = std::min(static_cast<int>(pixmap->height()*scale), maxTextureSize);
+            SkImageInfo info = pixmap->info().makeWH(newWidth, newHeight);
+            if (!resized.tryAlloc(info) || !pixmap->scalePixels(resized, kLow_SkFilterQuality)) {
+                return nullptr;
+            }
+            pixmap = &resized;
+        }
+        // Turn the pixmap into a GrTextureProxy
+        SkBitmap bmp;
+        bmp.installPixels(*pixmap);
+        GrBitmapTextureMaker bitmapMaker(context, bmp, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
+        tempViews[i] = bitmapMaker.view(buildMips);
+        if (!tempViews[i]) {
+            return nullptr;
+        }
+    }
+
+    return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context),
+                                       pixmaps.yuvaInfo().dimensions(),
+                                       kNeedNewImageUniqueID,
+                                       pixmaps.yuvaInfo().yuvColorSpace(),
+                                       tempViews,
+                                       numPlanes,
+                                       yuvaIndices,
+                                       kTopLeft_GrSurfaceOrigin,
+                                       std::move(imageColorSpace));
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 25c3543..b7dd5b9 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -1366,14 +1366,11 @@
 static sk_sp<SkImage> make_yuva_image(GrDirectContext* dContext) {
     SkAutoPixmapStorage pm;
     pm.alloc(SkImageInfo::Make(1, 1, kAlpha_8_SkColorType, kPremul_SkAlphaType));
-    const SkPixmap pmaps[] = {pm, pm, pm, pm};
-    SkYUVAIndex indices[] = {{0, SkColorChannel::kA},
-                             {1, SkColorChannel::kA},
-                             {2, SkColorChannel::kA},
-                             {3, SkColorChannel::kA}};
+    SkYUVAInfo yuvaInfo({1, 1}, SkYUVAInfo::PlanarConfig::kY_U_V_444, kJPEG_Full_SkYUVColorSpace);
+    const SkPixmap pmaps[] = {pm, pm, pm};
+    auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, pmaps);
 
-    return SkImage::MakeFromYUVAPixmaps(dContext, kJPEG_SkYUVColorSpace, pmaps, indices,
-                                        SkISize::Make(1, 1), kTopLeft_GrSurfaceOrigin, false);
+    return SkImage::MakeFromYUVAPixmaps(dContext, yuvaPixmaps);
 }
 
 DEF_GPUTEST_FOR_ALL_CONTEXTS(ImageFlush, reporter, ctxInfo) {
diff --git a/tools/gpu/YUVUtils.cpp b/tools/gpu/YUVUtils.cpp
index 87ce712..c2e2288 100644
--- a/tools/gpu/YUVUtils.cpp
+++ b/tools/gpu/YUVUtils.cpp
@@ -75,14 +75,7 @@
         return true; // Have already made a YUV image valid for this context.
     }
     // Try to make a new YUV image for this context.
-    fYUVImage = SkImage::MakeFromYUVAPixmaps(rContext,
-                                             fPixmaps.yuvaInfo().yuvColorSpace(),
-                                             fPixmaps.planes().data(),
-                                             fComponents,
-                                             fSizeInfo.fSizes[0],
-                                             kTopLeft_GrSurfaceOrigin,
-                                             static_cast<bool>(fMipmapped),
-                                             false);
+    fYUVImage = SkImage::MakeFromYUVAPixmaps(rContext, fPixmaps, fMipmapped, false, nullptr);
     return fYUVImage != nullptr;
 }