Revert "Revert "New variant of SkImage::MakeFromYUVATextures.""

This reverts commit be8004d2fb6c8575d7da7c580811639654a9d255.

Bug: skia:10632
Change-Id: I52dd36ae167623563c7554c40092442a3822ee3c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/327760
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index f3f13e2..e2d4147 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -9,6 +9,11 @@
 
   * <insert new release notes here>
 
+  * New variant of SkImage::MakeFromYUVATextures. Takes a new type GrYUVATextures
+    which wraps an SkYUVAInfo and compatible set of GrBackendTextures. The provides
+    a more complete and structured specification of the planar configuration. Previous
+    version is deprecated.
+
   * Add field to GrContextOptions to disable mipmap support even if the backend
     supports it.
 
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 63acc5f..6c494a2 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -19,6 +19,7 @@
   "$_include/gpu/GrDriverBugWorkarounds.h",
   "$_include/gpu/GrRecordingContext.h",
   "$_include/gpu/GrTypes.h",
+  "$_include/gpu/GrYUVABackendTextures.h",
 
   # Private includes
   "$_include/private/GrContext.h",
@@ -269,6 +270,7 @@
   "$_src/gpu/GrWindowRectsState.h",
   "$_src/gpu/GrXferProcessor.cpp",
   "$_src/gpu/GrXferProcessor.h",
+  "$_src/gpu/GrYUVABackendTextures.cpp",
 
   # Ops
   "$_src/gpu/effects/GrBezierEffect.cpp",
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 990d37c..3f63a1b 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -38,6 +38,7 @@
 class GrDirectContext;
 class GrRecordingContext;
 class GrContextThreadSafeProxy;
+class GrYUVABackendTextures;
 
 struct SkYUVAIndex;
 
@@ -408,7 +409,30 @@
                                                  SkAlphaType alphaType = kPremul_SkAlphaType,
                                                  sk_sp<SkColorSpace> colorSpace = nullptr);
 
-    /** Creates an SkImage by storing the specified YUVA planes into an image, to be rendered
+    /** Creates an SkImage from YUV[A] planar textures. This requires that the textures stay valid
+        for the lifetime of the image. The ReleaseContext can be used to know when it is safe to
+        either delete or overwrite the textures. If ReleaseProc is provided it is also called before
+        return on failure.
+
+        @param context            GPU context
+        @param yuvaTextures       A set of textures containing YUVA data and a description of the
+                                  data and transformation to RGBA.
+        @param imageColorSpace    range of colors of the resulting image after conversion to RGB;
+                                  may be nullptr
+        @param textureReleaseProc called when the backend textures can be released
+        @param releaseContext     state passed to textureReleaseProc
+        @return                   created SkImage, or nullptr
+    */
+    static sk_sp<SkImage> MakeFromYUVATextures(GrRecordingContext* context,
+                                               const GrYUVABackendTextures& yuvaTextures,
+                                               sk_sp<SkColorSpace> imageColorSpace = nullptr,
+                                               TextureReleaseProc textureReleaseProc = nullptr,
+                                               ReleaseContext releaseContext = nullptr);
+
+    /**
+        Deprecated. Use version that takes GrYUVABackendTextures.
+
+        Creates an SkImage by storing the specified YUVA planes into an image, to be rendered
         via multitexturing.
 
         When all the provided backend textures can be released 'textureReleaseProc' will be called
diff --git a/include/core/SkYUVAInfo.h b/include/core/SkYUVAInfo.h
index da94ef7..0c0cb51 100644
--- a/include/core/SkYUVAInfo.h
+++ b/include/core/SkYUVAInfo.h
@@ -39,6 +39,8 @@
      * this expands.
      */
     enum class PlanarConfig {
+        kUnknown,
+
         kY_U_V_444,    ///< Plane 0: Y, Plane 1: U,  Plane 2: V
         kY_U_V_422,    ///< Plane 0: Y, Plane 1: U,  Plane 2: V
         kY_U_V_420,    ///< Plane 0: Y, Plane 1: U,  Plane 2: V
@@ -177,10 +179,12 @@
     bool operator==(const SkYUVAInfo& that) const;
     bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); }
 
+    bool isValid() const { return fPlanarConfig != PlanarConfig::kUnknown; }
+
 private:
     SkISize fDimensions = {0, 0};
 
-    PlanarConfig fPlanarConfig = PlanarConfig::kY_U_V_444;
+    PlanarConfig fPlanarConfig = PlanarConfig::kUnknown;
 
     SkYUVColorSpace fYUVColorSpace = SkYUVColorSpace::kIdentity_SkYUVColorSpace;
 
@@ -196,6 +200,8 @@
 
 constexpr int SkYUVAInfo::NumPlanes(PlanarConfig planarConfig) {
     switch (planarConfig) {
+        case PlanarConfig::kUnknown:      return 0;
+
         case PlanarConfig::kY_U_V_444:    return 3;
         case PlanarConfig::kY_U_V_422:    return 3;
         case PlanarConfig::kY_U_V_420:    return 3;
@@ -223,6 +229,9 @@
 
 constexpr int SkYUVAInfo::NumChannelsInPlane(PlanarConfig config, int i) {
     switch (config) {
+        case PlanarConfig::kUnknown:
+            return 0;
+
         case SkYUVAInfo::PlanarConfig::kY_U_V_444:
         case SkYUVAInfo::PlanarConfig::kY_U_V_422:
         case SkYUVAInfo::PlanarConfig::kY_U_V_420:
diff --git a/include/gpu/GrYUVABackendTextures.h b/include/gpu/GrYUVABackendTextures.h
new file mode 100644
index 0000000..347d00d
--- /dev/null
+++ b/include/gpu/GrYUVABackendTextures.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrYUVABackendTextures_DEFINED
+#define GrYUVABackendTextures_DEFINED
+
+#include "include/core/SkYUVAInfo.h"
+#include "include/gpu/GrBackendSurface.h"
+
+#include <tuple>
+
+struct SkYUVASizeInfo;
+struct SkYUVAIndex;
+
+/**
+ * A set of GrBackendTextures that hold the planar data for a SkYUVAInfo.
+ */
+class SK_API GrYUVABackendTextures {
+public:
+    GrYUVABackendTextures() = default;
+    GrYUVABackendTextures(const GrYUVABackendTextures&) = delete;
+    GrYUVABackendTextures(GrYUVABackendTextures&&) = default;
+
+    GrYUVABackendTextures& operator=(const GrYUVABackendTextures&) = delete;
+    GrYUVABackendTextures& operator=(GrYUVABackendTextures&&) = default;
+
+    GrYUVABackendTextures(const SkYUVAInfo&,
+                          const GrBackendTexture[SkYUVAInfo::kMaxPlanes],
+                          GrSurfaceOrigin textureOrigin);
+
+    const std::array<GrBackendTexture, SkYUVAInfo::kMaxPlanes>& textures() const {
+        return fTextures;
+    }
+
+    GrBackendTexture texture(int i) const {
+        SkASSERT(i >= 0 && i < SkYUVAInfo::kMaxPlanes);
+        return fTextures[static_cast<size_t>(i)];
+    }
+
+    const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; }
+
+    int numPlanes() const { return fYUVAInfo.numPlanes(); }
+
+    GrSurfaceOrigin textureOrigin() const { return fTextureOrigin; }
+
+    bool isValid() const { return fYUVAInfo.isValid(); }
+
+    bool toYUVAIndices(SkYUVAIndex[SkYUVAIndex::kIndexCount]) const;
+
+private:
+    SkYUVAInfo fYUVAInfo;
+    std::array<GrBackendTexture, SkYUVAInfo::kMaxPlanes> fTextures;
+    GrSurfaceOrigin fTextureOrigin = kTopLeft_GrSurfaceOrigin;
+};
+
+#endif
diff --git a/src/core/SkYUVAInfo.cpp b/src/core/SkYUVAInfo.cpp
index 07b7c82..c504f69 100644
--- a/src/core/SkYUVAInfo.cpp
+++ b/src/core/SkYUVAInfo.cpp
@@ -11,7 +11,7 @@
 int SkYUVAInfo::PlaneDimensions(SkISize imageDimensions,
                                 PlanarConfig planarConfig,
                                 SkEncodedOrigin origin,
-                                SkISize* planeDimensions) {
+                                SkISize planeDimensions[SkYUVAInfo::kMaxPlanes]) {
     int w = imageDimensions.width();
     int h = imageDimensions.height();
     if (origin >= kLeftTop_SkEncodedOrigin) {
@@ -21,6 +21,12 @@
     auto down2 = [](int x) { return (x + 1)/2; };
     auto down4 = [](int x) { return (x + 3)/4; };
     switch (planarConfig) {
+        case PlanarConfig::kUnknown:
+            planeDimensions[0] =
+            planeDimensions[1] =
+            planeDimensions[2] =
+            planeDimensions[3] = {0, 0};
+            return 0;
         case PlanarConfig::kY_U_V_444:
             planeDimensions[0] = planeDimensions[1] = planeDimensions[2] = {w, h};
             planeDimensions[3] = {0, 0};
@@ -125,6 +131,9 @@
     struct Location {int plane, chanIdx;};
     const Location* locations = nullptr;
     switch (config) {
+        case PlanarConfig::kUnknown:
+            return false;
+
         case PlanarConfig::kY_U_V_444:
         case PlanarConfig::kY_U_V_422:
         case PlanarConfig::kY_U_V_420:
@@ -210,6 +219,8 @@
 
 bool SkYUVAInfo::HasAlpha(PlanarConfig planarConfig) {
     switch (planarConfig) {
+        case PlanarConfig::kUnknown:      return false;
+
         case PlanarConfig::kY_U_V_444:    return false;
         case PlanarConfig::kY_U_V_422:    return false;
         case PlanarConfig::kY_U_V_420:    return false;
@@ -247,11 +258,20 @@
     , fYUVColorSpace(yuvColorSpace)
     , fOrigin(origin)
     , fSitingX(sitingX)
-    , fSitingY(sitingY) {}
+    , fSitingY(sitingY) {
+    if (fDimensions.width() <= 0 ||
+        fDimensions.height() <= 0 ||
+        planarConfig == PlanarConfig::kUnknown) {
+        *this = {};
+        SkASSERT(!this->isValid());
+        return;
+    }
+    SkASSERT(this->isValid());
+}
 
 size_t SkYUVAInfo::computeTotalBytes(const size_t rowBytes[kMaxPlanes],
                                      size_t planeSizes[kMaxPlanes]) const {
-    if (fDimensions.isEmpty()) {
+    if (!this->isValid()) {
         return 0;
     }
     SkSafeMath safe;
diff --git a/src/core/SkYUVAPixmaps.cpp b/src/core/SkYUVAPixmaps.cpp
index e840fc5..2ce4ba5 100644
--- a/src/core/SkYUVAPixmaps.cpp
+++ b/src/core/SkYUVAPixmaps.cpp
@@ -80,7 +80,7 @@
                                    const SkColorType colorTypes[kMaxPlanes],
                                    const size_t rowBytes[kMaxPlanes])
         : fYUVAInfo(yuvaInfo) {
-    if (yuvaInfo.dimensions().isEmpty()) {
+    if (!yuvaInfo.isValid()) {
         *this = {};
         SkASSERT(!this->isValid());
         return;
diff --git a/src/gpu/GrYUVABackendTextures.cpp b/src/gpu/GrYUVABackendTextures.cpp
new file mode 100644
index 0000000..945a372
--- /dev/null
+++ b/src/gpu/GrYUVABackendTextures.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/gpu/GrYUVABackendTextures.h"
+
+static int num_channels(const GrBackendFormat& format) {
+    switch (format.channelMask()) {
+        case kRed_SkColorChannelFlag  : return 1;
+        case kAlpha_SkColorChannelFlag: return 1;
+        case kGray_SkColorChannelFlag : return 1;
+        case kRG_SkColorChannelFlags  : return 2;
+        case kRGB_SkColorChannelFlags : return 3;
+        case kRGBA_SkColorChannelFlags: return 4;
+        default                       : return 0;
+    }
+}
+
+GrYUVABackendTextures::GrYUVABackendTextures(
+        const SkYUVAInfo& yuvaInfo,
+        const GrBackendTexture textures[SkYUVAInfo::kMaxPlanes],
+        GrSurfaceOrigin textureOrigin)
+        : fYUVAInfo(yuvaInfo), fTextureOrigin(textureOrigin) {
+    if (!fYUVAInfo.isValid()) {
+        return;
+    }
+    SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
+    int numPlanes = yuvaInfo.planeDimensions(planeDimensions);
+    for (int i = 0; i < numPlanes; ++i) {
+        int numRequiredChannels = fYUVAInfo.numChannelsInPlane(i);
+        if (!textures[i].isValid() ||
+            textures[i].dimensions() != planeDimensions[i] ||
+            textures[i].backend() != textures[0].backend() ||
+            num_channels(textures[i].getBackendFormat()) < numRequiredChannels) {
+            *this = {};
+            return;
+        }
+        fTextures[i] = textures[i];
+    }
+}
+
+bool GrYUVABackendTextures::toYUVAIndices(SkYUVAIndex indices[SkYUVAIndex::kIndexCount]) const {
+    SkASSERT(indices);
+    uint32_t channelFlags[] = {fTextures[0].getBackendFormat().channelMask(),
+                               fTextures[1].getBackendFormat().channelMask(),
+                               fTextures[2].getBackendFormat().channelMask(),
+                               fTextures[3].getBackendFormat().channelMask()};
+    return fYUVAInfo.toYUVAIndices(channelFlags, indices);
+}
diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp
index 0c38e98..b990f4d 100644
--- a/src/image/SkImage_GpuYUVA.cpp
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -13,6 +13,7 @@
 #include "include/core/SkYUVASizeInfo.h"
 #include "include/gpu/GrDirectContext.h"
 #include "include/gpu/GrRecordingContext.h"
+#include "include/gpu/GrYUVABackendTextures.h"
 #include "src/core/SkAutoPixmapStorage.h"
 #include "src/core/SkMipmap.h"
 #include "src/core/SkScopeExit.h"
@@ -234,6 +235,45 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
+sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrRecordingContext* context,
+                                             const GrYUVABackendTextures& yuvaTextures,
+                                             sk_sp<SkColorSpace> imageColorSpace,
+                                             TextureReleaseProc textureReleaseProc,
+                                             ReleaseContext releaseContext) {
+    sk_sp<GrRefCntedCallback> releaseHelper;
+    if (textureReleaseProc) {
+        releaseHelper.reset(new GrRefCntedCallback(textureReleaseProc, releaseContext));
+    }
+
+    SkYUVAIndex yuvaIndices[4];
+    int numTextures;
+    if (!yuvaTextures.toYUVAIndices(yuvaIndices) ||
+        !SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
+        return nullptr;
+    }
+    SkASSERT(numTextures == yuvaTextures.numPlanes());
+
+    GrSurfaceProxyView tempViews[4];
+    if (!SkImage_GpuBase::MakeTempTextureProxies(context,
+                                                 yuvaTextures.textures().data(),
+                                                 numTextures,
+                                                 yuvaIndices,
+                                                 yuvaTextures.textureOrigin(),
+                                                 tempViews,
+                                                 std::move(releaseHelper))) {
+        return nullptr;
+    }
+
+    return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context),
+                                       yuvaTextures.yuvaInfo().dimensions(),
+                                       kNeedNewImageUniqueID,
+                                       yuvaTextures.yuvaInfo().yuvColorSpace(),
+                                       tempViews,
+                                       numTextures,
+                                       yuvaIndices,
+                                       imageColorSpace);
+}
+
 sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrContext* ctx,
                                              SkYUVColorSpace colorSpace,
                                              const GrBackendTexture yuvaTextures[],
diff --git a/tools/gpu/YUVUtils.cpp b/tools/gpu/YUVUtils.cpp
index d5f7ac3..5d42ef7 100644
--- a/tools/gpu/YUVUtils.cpp
+++ b/tools/gpu/YUVUtils.cpp
@@ -10,6 +10,7 @@
 #include "include/core/SkColorPriv.h"
 #include "include/core/SkData.h"
 #include "include/gpu/GrRecordingContext.h"
+#include "include/gpu/GrYUVABackendTextures.h"
 #include "src/codec/SkCodecImageGenerator.h"
 #include "src/core/SkYUVMath.h"
 #include "src/gpu/GrDirectContextPriv.h"
@@ -226,30 +227,26 @@
             if (auto direct = rContext->asDirectContext()) {
                 sk_sp<sk_gpu_test::ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes];
                 GrBackendTexture textures[SkYUVAInfo::kMaxPlanes];
-                uint32_t componentFlags[SkYUVAInfo::kMaxPlanes] = {};
                 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
                     mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeWithData(
                             direct, fPixmaps.plane(i), GrRenderable::kNo, GrProtected::kNo);
                     if (mbets[i]) {
                         textures[i] = mbets[i]->texture();
-                        componentFlags[i] = textures[i].getBackendFormat().channelMask();
                     } else {
                         return false;
                     }
                 }
-                SkYUVAIndex indices[SkYUVAIndex::kIndexCount];
-                if (!fPixmaps.yuvaInfo().toYUVAIndices(componentFlags, indices)) {
+                GrYUVABackendTextures yuvaTextures(fPixmaps.yuvaInfo(),
+                                                   textures,
+                                                   kTopLeft_GrSurfaceOrigin);
+                if (!yuvaTextures.isValid()) {
                     return false;
                 }
                 void* relContext =
                         sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets);
                 fYUVImage[idx] = SkImage::MakeFromYUVATextures(
                         direct,
-                        fPixmaps.yuvaInfo().yuvColorSpace(),
-                        textures,
-                        indices,
-                        fPixmaps.yuvaInfo().dimensions(),
-                        kTopLeft_GrSurfaceOrigin,
+                        yuvaTextures,
                         fColorSpace,
                         sk_gpu_test::ManagedBackendTexture::ReleaseProc,
                         relContext);