Revert "Move all YUVA image creation in GMs into sk_gpu_test::LazyYUVImage."

This reverts commit db0288d747ae84e8cfb577f70953c1d56eb78110.

Reason for revert: undeclared tuple size

Original change's description:
> Move all YUVA image creation in GMs into sk_gpu_test::LazyYUVImage.
>
> LazyYUVImage now supports making images from a generator and from
> textures. It uses ManagedBackendTexture to manage texture plane
> lifetime via ref-counting.
>
> Adds some supporting utility functions to SkYUVAInfo and
> SkYUVAPixmaps.
>
> Eases transition of forthcoming MakeFromYUVATextures API change.
>
> Bug: skia:10632
>
> Change-Id: I8cfd747c27076d1627da6ea8a169e554a96049e0
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/326720
> Reviewed-by: Robert Phillips <robertphillips@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

TBR=bsalomon@google.com,robertphillips@google.com

Change-Id: Icdfb70f7dadd97eace8f88d5a886d31534102f5f
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:10632
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/327622
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/asyncrescaleandread.cpp b/gm/asyncrescaleandread.cpp
index b4d55c9..d6f151b 100644
--- a/gm/asyncrescaleandread.cpp
+++ b/gm/asyncrescaleandread.cpp
@@ -11,15 +11,14 @@
 #include "include/core/SkPaint.h"
 #include "include/core/SkRect.h"
 #include "include/core/SkSurface.h"
-#include "include/core/SkYUVAInfo.h"
-#include "include/core/SkYUVAPixmaps.h"
+#include "include/core/SkYUVAIndex.h"
 #include "include/gpu/GrDirectContext.h"
 #include "include/gpu/GrRecordingContext.h"
 #include "src/core/SkAutoPixmapStorage.h"
+#include "src/core/SkConvertPixels.h"
 #include "src/core/SkScopeExit.h"
 #include "tools/Resources.h"
 #include "tools/ToolUtils.h"
-#include "tools/gpu/YUVUtils.h"
 
 namespace {
 struct AsyncContext {
@@ -94,17 +93,33 @@
     if (!asyncContext.fResult) {
         return nullptr;
     }
-    SkYUVAInfo yuvaInfo(size, SkYUVAInfo::PlanarConfig::kY_U_V_420, yuvCS);
-    SkPixmap yuvPMs[] = {
-            {yII,  asyncContext.fResult->data(0), asyncContext.fResult->rowBytes(0)},
-            {uvII, asyncContext.fResult->data(1), asyncContext.fResult->rowBytes(1)},
-            {uvII, asyncContext.fResult->data(2), asyncContext.fResult->rowBytes(2)}
+    GrBackendTexture backendTextures[3];
+
+    SkPixmap yPM(yII, asyncContext.fResult->data(0), asyncContext.fResult->rowBytes(0));
+    SkPixmap uPM(uvII, asyncContext.fResult->data(1), asyncContext.fResult->rowBytes(1));
+    SkPixmap vPM(uvII, asyncContext.fResult->data(2), asyncContext.fResult->rowBytes(2));
+
+    backendTextures[0] = direct->createBackendTexture(yPM, GrRenderable::kNo, GrProtected::kNo);
+    backendTextures[1] = direct->createBackendTexture(uPM, GrRenderable::kNo, GrProtected::kNo);
+    backendTextures[2] = direct->createBackendTexture(vPM, GrRenderable::kNo, GrProtected::kNo);
+
+    SkYUVAIndex indices[4] = {
+        { 0, SkColorChannel::kR},
+        { 1, SkColorChannel::kR},
+        { 2, SkColorChannel::kR},
+        {-1, SkColorChannel::kR}
     };
-    auto pixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, yuvPMs);
-    SkASSERT(pixmaps.isValid());
-    auto lazyYUVImage = sk_gpu_test::LazyYUVImage::Make(pixmaps);
-    SkASSERT(lazyYUVImage);
-    return lazyYUVImage->refImage(direct, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
+
+    *cleanup = {[direct, backendTextures] {
+        direct->flush();
+        direct->submit(true);
+        direct->deleteBackendTexture(backendTextures[0]);
+        direct->deleteBackendTexture(backendTextures[1]);
+        direct->deleteBackendTexture(backendTextures[2]);
+    }};
+
+    return SkImage::MakeFromYUVATextures(direct, yuvCS, backendTextures, indices, size,
+                                         kTopLeft_GrSurfaceOrigin, SkColorSpace::MakeSRGB());
 }
 
 // Draws a grid of rescales. The columns are none, low, and high filter quality. The rows are
diff --git a/gm/compositor_quads.cpp b/gm/compositor_quads.cpp
index f3a684d..934afba 100644
--- a/gm/compositor_quads.cpp
+++ b/gm/compositor_quads.cpp
@@ -897,8 +897,7 @@
     int drawTiles(SkCanvas* canvas) override {
         // Refresh the SkImage at the start, so that it's not attempted for every set entry
         if (fYUVData) {
-            fImage = fYUVData->refImage(canvas->recordingContext(),
-                                        sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
+            fImage = fYUVData->refImage(canvas->recordingContext());
             if (!fImage) {
                 return 0;
             }
diff --git a/gm/ducky_yuv_blend.cpp b/gm/ducky_yuv_blend.cpp
index bd39353..af45b89 100644
--- a/gm/ducky_yuv_blend.cpp
+++ b/gm/ducky_yuv_blend.cpp
@@ -33,7 +33,7 @@
         auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(GetResourceAsData("images/ducky.jpg"),
                                                        GrMipmapped::kYes);
         if (lazyYUV) {
-            duckyFG[1] = lazyYUV->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
+            duckyFG[1] = lazyYUV->refImage(rContext);
         }
         if (!duckyFG[1]) {
             return skiagm::DrawResult::kFail;
diff --git a/gm/imagefromyuvtextures.cpp b/gm/imagefromyuvtextures.cpp
index 2a50973..ede9586 100644
--- a/gm/imagefromyuvtextures.cpp
+++ b/gm/imagefromyuvtextures.cpp
@@ -32,6 +32,8 @@
 #include "tools/Resources.h"
 #include "tools/gpu/YUVUtils.h"
 
+using sk_gpu_test::YUVABackendReleaseContext;
+
 class GrRenderTargetContext;
 
 namespace skiagm {
@@ -48,90 +50,128 @@
 
     SkISize onISize() override { return {1420, 610}; }
 
-    static std::unique_ptr<sk_gpu_test::LazyYUVImage> CreatePlanes(const char* name) {
+    static SkBitmap CreateBmpAndPlanes(const char* name, SkBitmap yuvaBmps[4]) {
         SkBitmap bmp;
         if (!GetResourceAsBitmap(name, &bmp)) {
             return {};
         }
-        if (bmp.colorType() != kRGBA_8888_SkColorType) {
-            auto info = bmp.info().makeColorType(kRGBA_8888_SkColorType);
-            SkBitmap copy;
-            copy.allocPixels(info);
-            SkAssertResult(bmp.readPixels(copy.pixmap()));
-            bmp = copy;
-        }
-        SkYUVAPixmapInfo pixmapInfo({bmp.dimensions(),
-                                     SkYUVAInfo::PlanarConfig::kY_U_V_A_4204,
-                                     kJPEG_Full_SkYUVColorSpace},
-                                    SkYUVAPixmapInfo::DataType::kUnorm8,
-                                    nullptr);
-        auto pixmaps = SkYUVAPixmaps::Allocate(pixmapInfo);
+        auto ii = SkImageInfo::Make(bmp.dimensions(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+        SkBitmap rgbaBmp;
+        rgbaBmp.allocPixels(ii);
+        bmp.readPixels(rgbaBmp.pixmap(), 0, 0);
+
+        SkImageInfo yaInfo = SkImageInfo::Make(rgbaBmp.dimensions(), kAlpha_8_SkColorType,
+                                               kUnpremul_SkAlphaType);
+        yuvaBmps[0].allocPixels(yaInfo);
+        SkISize uvSize = {rgbaBmp.width()/2, rgbaBmp.height()/2};
+        SkImageInfo uvInfo = SkImageInfo::Make(uvSize, kAlpha_8_SkColorType, kUnpremul_SkAlphaType);
+        yuvaBmps[1].allocPixels(uvInfo);
+        yuvaBmps[2].allocPixels(uvInfo);
+        yuvaBmps[3].allocPixels(yaInfo);
 
         unsigned char* yuvPixels[] = {
-                static_cast<unsigned char*>(pixmaps.planes()[0].writable_addr()),
-                static_cast<unsigned char*>(pixmaps.planes()[1].writable_addr()),
-                static_cast<unsigned char*>(pixmaps.planes()[2].writable_addr()),
-                static_cast<unsigned char*>(pixmaps.planes()[3].writable_addr()),
+                static_cast<unsigned char*>(yuvaBmps[0].getPixels()),
+                static_cast<unsigned char*>(yuvaBmps[1].getPixels()),
+                static_cast<unsigned char*>(yuvaBmps[2].getPixels()),
+                static_cast<unsigned char*>(yuvaBmps[3].getPixels()),
         };
 
         float m[20];
-        SkColorMatrix_RGB2YUV(pixmaps.yuvaInfo().yuvColorSpace(), m);
+        SkColorMatrix_RGB2YUV(kJPEG_SkYUVColorSpace, m);
         // Here we encode using the kJPEG_SkYUVColorSpace (i.e., full-swing Rec 601) even though
         // we will draw it with all the supported yuv color spaces when converted back to RGB
-        for (int j = 0; j < pixmaps.planes()[0].height(); ++j) {
-            for (int i = 0; i < pixmaps.planes()[0].width(); ++i) {
-                auto rgba = *bmp.getAddr32(i, j);
+        for (int j = 0; j < yaInfo.height(); ++j) {
+            for (int i = 0; i < yaInfo.width(); ++i) {
+                auto rgba = *rgbaBmp.getAddr32(i, j);
                 auto r = (rgba & 0x000000ff) >>  0;
                 auto g = (rgba & 0x0000ff00) >>  8;
                 auto b = (rgba & 0x00ff0000) >> 16;
                 auto a = (rgba & 0xff000000) >> 24;
-                yuvPixels[0][j*pixmaps.planes()[0].width() + i] = SkToU8(
+                yuvPixels[0][j*yaInfo.width() + i] = SkToU8(
                         sk_float_round2int(m[0]*r + m[1]*g + m[2]*b + m[3]*a + 255*m[4]));
-                yuvPixels[3][j*pixmaps.planes()[0].width() + i] = SkToU8(sk_float_round2int(
+                yuvPixels[3][j*yaInfo.width() + i] = SkToU8(sk_float_round2int(
                         m[15]*r + m[16]*g + m[17]*b + m[18]*a + 255*m[19]));
             }
         }
-        for (int j = 0; j < pixmaps.planes()[1].height(); ++j) {
-            for (int i = 0; i < pixmaps.planes()[1].width(); ++i) {
+        for (int j = 0; j < uvInfo.height(); ++j) {
+            for (int i = 0; i < uvInfo.width(); ++i) {
                 // Average together 4 pixels of RGB.
                 int rgba[] = {0, 0, 0, 0};
-                int denom = 0;
-                int ylimit = std::min(2*j + 2, pixmaps.planes()[0].height());
-                int xlimit = std::min(2*i + 2, pixmaps.planes()[0].width());
-                for (int y = 2*j; y < ylimit; ++y) {
-                    for (int x = 2*i; x < xlimit; ++x) {
-                        auto src = *bmp.getAddr32(x, y);
+                for (int y = 0; y < 2; ++y) {
+                    for (int x = 0; x < 2; ++x) {
+                        auto src = *rgbaBmp.getAddr32(2 * i + x, 2 * j + y);
                         rgba[0] += (src & 0x000000ff) >> 0;
                         rgba[1] += (src & 0x0000ff00) >> 8;
                         rgba[2] += (src & 0x00ff0000) >> 16;
                         rgba[3] += (src & 0xff000000) >> 24;
-                        ++denom;
                     }
                 }
                 for (int c = 0; c < 4; ++c) {
-                    rgba[c] /= denom;
+                    rgba[c] /= 4;
                 }
-                int uvIndex = j*pixmaps.planes()[1].width() + i;
+                int uvIndex = j*uvInfo.width() + i;
                 yuvPixels[1][uvIndex] = SkToU8(sk_float_round2int(
                         m[5]*rgba[0] + m[6]*rgba[1] + m[7]*rgba[2] + m[8]*rgba[3] + 255*m[9]));
                 yuvPixels[2][uvIndex] = SkToU8(sk_float_round2int(
                         m[10]*rgba[0] + m[11]*rgba[1] + m[12]*rgba[2] + m[13]*rgba[3] + 255*m[14]));
             }
         }
-        return sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
+        return rgbaBmp;
+    }
+
+    static bool CreateYUVBackendTextures(GrDirectContext* context, SkBitmap bmps[4],
+                                         SkYUVAIndex indices[4],
+                                         YUVABackendReleaseContext* beContext) {
+        for (int i = 0; i < 4; ++i) {
+            GrBackendTexture tmp = context->createBackendTexture(
+                                        bmps[i].pixmap(), GrRenderable::kNo, GrProtected::kNo,
+                                        YUVABackendReleaseContext::CreationCompleteProc(i),
+                                        beContext);
+            if (!tmp.isValid()) {
+                return false;
+            }
+
+            beContext->set(i, tmp);
+        }
+
+        for (int i = 0; i < 4; ++i) {
+            auto chanMask = beContext->beTexture(i).getBackendFormat().channelMask();
+            // We expect the single channel bitmaps to produce single channel textures.
+            SkASSERT(chanMask && SkIsPow2(chanMask));
+            if (chanMask & kGray_SkColorChannelFlag) {
+                indices[i].fChannel = SkColorChannel::kR;
+            } else {
+                indices[i].fChannel = static_cast<SkColorChannel>(31 - SkCLZ(chanMask));
+            }
+            indices[i].fIndex = i;
+        }
+
+        return true;
     }
 
     sk_sp<SkImage> makeYUVAImage(GrDirectContext* context) {
-        return fLazyYUVImage->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
-    }
+        auto releaseContext = new YUVABackendReleaseContext(context);
+        SkYUVAIndex indices[4];
 
-    sk_sp<SkImage> createReferenceImage(GrDirectContext* dContext) {
-        auto planarImage = this->makeYUVAImage(dContext);
-        if (!planarImage) {
+        if (!CreateYUVBackendTextures(context, fYUVABmps, indices, releaseContext)) {
+            YUVABackendReleaseContext::Unwind(context, releaseContext, false);
             return nullptr;
         }
 
-        auto resultInfo = SkImageInfo::Make(fLazyYUVImage->dimensions(),
+        return SkImage::MakeFromYUVATextures(context,
+                                             kJPEG_SkYUVColorSpace,
+                                             releaseContext->beTextures(),
+                                             indices,
+                                             fRGBABmp.dimensions(),
+                                             kTopLeft_GrSurfaceOrigin,
+                                             nullptr,
+                                             YUVABackendReleaseContext::Release,
+                                             releaseContext);
+    }
+
+    sk_sp<SkImage> createReferenceImage(GrDirectContext* dContext) {
+        auto resultInfo = SkImageInfo::Make(fRGBABmp.dimensions(),
                                             kRGBA_8888_SkColorType,
                                             kPremul_SkAlphaType);
         auto resultSurface = SkSurface::MakeRenderTarget(dContext,
@@ -144,7 +184,27 @@
             return nullptr;
         }
 
-        resultSurface->getCanvas()->drawImage(std::move(planarImage), 0, 0);
+        auto planeReleaseContext = new YUVABackendReleaseContext(dContext);
+        SkYUVAIndex indices[4];
+
+        if (!CreateYUVBackendTextures(dContext, fYUVABmps, indices, planeReleaseContext)) {
+            YUVABackendReleaseContext::Unwind(dContext, planeReleaseContext, false);
+            return nullptr;
+        }
+
+        auto tmp = SkImage::MakeFromYUVATextures(dContext,
+                                                 kJPEG_SkYUVColorSpace,
+                                                 planeReleaseContext->beTextures(),
+                                                 indices,
+                                                 fRGBABmp.dimensions(),
+                                                 kTopLeft_GrSurfaceOrigin,
+                                                 nullptr);
+        if (!tmp) {
+            YUVABackendReleaseContext::Unwind(dContext, planeReleaseContext, false);
+            return nullptr;
+        }
+        resultSurface->getCanvas()->drawImage(std::move(tmp), 0, 0);
+        YUVABackendReleaseContext::Unwind(dContext, planeReleaseContext, true);
         return resultSurface->makeImageSnapshot();
     }
 
@@ -153,9 +213,7 @@
             return DrawResult::kSkip;
         }
 
-        if (!fLazyYUVImage) {
-            fLazyYUVImage = CreatePlanes("images/mandrill_32.png");
-        }
+        fRGBABmp = CreateBmpAndPlanes("images/mandrill_32.png", fYUVABmps);
 
         // We make a version of this image for each draw because, if any draw flattens it to
         // RGBA, then all subsequent draws would use the RGBA texture.
@@ -262,7 +320,8 @@
      }
 
 private:
-    std::unique_ptr<sk_gpu_test::LazyYUVImage> fLazyYUVImage;
+    SkBitmap fRGBABmp; // TODO: oddly, it looks like this could just be an SkISize
+    SkBitmap fYUVABmps[4];
 
     // 3 draws x 3 scales x 4 filter qualities
     static constexpr int kNumImages = 3 * 3 * 4;
diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp
index c198715..559bdc8 100644
--- a/gm/wacky_yuv_formats.cpp
+++ b/gm/wacky_yuv_formats.cpp
@@ -57,6 +57,7 @@
 #include <memory>
 #include <utility>
 
+using sk_gpu_test::YUVABackendReleaseContext;
 class GrRenderTargetContext;
 
 static const int kTileWidthHeight = 128;
@@ -115,51 +116,77 @@
 
 class YUVAPlanarConfig {
 public:
+    enum class YUVAChannel { kY, kU, kV, kA };
+
     YUVAPlanarConfig(YUVFormat format, bool opaque) {
         switch (format) {
             case kP016_YUVFormat:
             case kP010_YUVFormat:
             case kP016F_YUVFormat:
             case kNV12_YUVFormat:
+                fLocations[0] = {0, 0};
+                fLocations[1] = {1, 0};
+                fLocations[2] = {1, 1};
                 if (opaque) {
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_UV_420;
                 } else {
+                    fLocations[3] = {2, 0};
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_UV_A_4204;
                 }
                 break;
             case kY416_YUVFormat:
             case kY410_YUVFormat:
+                fLocations[0] = {0, 1};
+                fLocations[1] = {0, 0};
+                fLocations[2] = {0, 2};
                 if (opaque) {
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kUYV_444;
                 } else {
+                    fLocations[3] = {0, 3};
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kUYVA_4444;
                 }
                 break;
             case kAYUV_YUVFormat:
+                fLocations[0] = {0, 0};
+                fLocations[1] = {0, 1};
+                fLocations[2] = {0, 2};
                 if (opaque) {
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kYUV_444;
                 } else {
+                    fLocations[3] = {0, 3};
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kYUVA_4444;
                 }
                 break;
             case kNV21_YUVFormat:
+                fLocations[0] = {0, 0};
+                fLocations[1] = {1, 1};
+                fLocations[2] = {1, 0};
                 if (opaque) {
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_VU_420;
                 } else {
+                    fLocations[3] = {2, 0};
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_VU_A_4204;
                 }
                 break;
             case kI420_YUVFormat:
+                fLocations[0] = {0, 0};
+                fLocations[1] = {1, 0};
+                fLocations[2] = {2, 0};
                 if (opaque) {
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_U_V_420;
                 } else {
+                    fLocations[3] = {3, 0};
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_U_V_A_4204;
                 }
                 break;
             case kYV12_YUVFormat:
+                fLocations[0] = {0, 0};
+                fLocations[1] = {2, 0};
+                fLocations[2] = {1, 0};
                 if (opaque) {
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_V_U_420;
                 } else {
+                    fLocations[3] = {3, 0};
                     fPlanarConfig = SkYUVAInfo::PlanarConfig::kY_V_U_A_4204;
                 }
                 break;
@@ -168,20 +195,129 @@
 
     int numPlanes() const { return SkYUVAInfo::NumPlanes(fPlanarConfig); }
 
+    int planeIndex(YUVAChannel c) const { return fLocations[static_cast<int>(c)].fPlaneIdx; }
+
+    int channelIndex(YUVAChannel c) const { return fLocations[static_cast<int>(c)].fChannelIdx; }
+
+    bool hasAlpha() const { return SkYUVAInfo::HasAlpha(fPlanarConfig); }
+
+    /**
+     * Given a mask of SkColorChannelFlags choose a channel by index. Legal 'channelMask' values
+     * are:
+     *      kAlpha, kGray, kRed, kRG, kRGB, kRGBA.
+     * The channel index must be less than the number of bits set in the mask. The index order is
+     * the order listed above (e.g. if 'channelMask' is kRGB and 'channelIdx' is 1 then
+     * SkColorChannel::kG is returned as 'channel'). The function fails if 'channelMask' is not one
+     * of the listed allowed values or 'channelIdx' is invalid for the mask.
+     */
+    static bool ChannelIndexToChannel(uint32_t channelMask,
+                                      int channelIdx,
+                                      SkColorChannel* channel);
+
+    /**
+     * Goes from channel indices to actual channels given texture formats. Also supports adding
+     * on an external alpha plane if this format doesn't already have alpha. The extra alpha texture
+     * must be the last texture and the channel index is assumed to be 0.
+     */
+    bool getYUVAIndices(const GrBackendTexture textures[],
+                        int numTextures,
+                        SkYUVAIndex indices[4]) const;
+
+    SkYUVAInfo getYUVAInfo(SkISize dimensions, SkYUVColorSpace yuvColorSpace) const;
+
     SkYUVAPixmaps makeYUVAPixmaps(SkISize dimensions,
                                   SkYUVColorSpace yuvColorSpace,
                                   const SkBitmap bitmaps[],
                                   int numBitmaps) const;
 
 private:
+    struct YUVALocation {
+        int fPlaneIdx = -1;
+        int fChannelIdx = -1;
+    };
+
     SkYUVAInfo::PlanarConfig fPlanarConfig;
+    YUVALocation fLocations[4] = {};
 };
 
+bool YUVAPlanarConfig::ChannelIndexToChannel(uint32_t channelFlags,
+                                             int channelIdx,
+                                             SkColorChannel* channel) {
+    switch (channelFlags) {
+        case kGray_SkColorChannelFlag:  // For gray returning any of R, G, or B for index 0 is ok.
+        case kRed_SkColorChannelFlag:
+            if (channelIdx == 0) {
+                *channel = SkColorChannel::kR;
+                return true;
+            }
+            return false;
+        case kAlpha_SkColorChannelFlag:
+            if (channelIdx == 0) {
+                *channel = SkColorChannel::kA;
+                return true;
+            }
+            return false;
+        case kRG_SkColorChannelFlags:
+            if (channelIdx == 0 || channelIdx == 1) {
+                *channel = static_cast<SkColorChannel>(channelIdx);
+                return true;
+            }
+            return false;
+        case kRGB_SkColorChannelFlags:
+            if (channelIdx >= 0 && channelIdx <= 2) {
+                *channel = static_cast<SkColorChannel>(channelIdx);
+                return true;
+            }
+            return false;
+        case kRGBA_SkColorChannelFlags:
+            if (channelIdx >= 0 && channelIdx <= 3) {
+                *channel = static_cast<SkColorChannel>(channelIdx);
+                return true;
+            }
+            return false;
+        default:
+            return false;
+    }
+}
+
+bool YUVAPlanarConfig::getYUVAIndices(const GrBackendTexture textures[],
+                                      int numTextures,
+                                      SkYUVAIndex indices[4]) const {
+    if (numTextures != this->numPlanes()) {
+        return false;
+    }
+    uint32_t channelMasks[4] = {};
+    for (int i = 0; i < numTextures; ++i) {
+        channelMasks[i] = textures[i].getBackendFormat().channelMask();
+    }
+    for (int i = 0; i < 4; ++i) {
+        int plane = fLocations[i].fPlaneIdx;
+        if (plane < 0) {
+            indices[i].fIndex = -1;
+            indices[i].fChannel = SkColorChannel::kR;
+        } else {
+            indices[i].fIndex = plane;
+            if (!ChannelIndexToChannel(channelMasks[plane], fLocations[i].fChannelIdx,
+                                       &indices[i].fChannel)) {
+                return false;
+            }
+        }
+    }
+    SkDEBUGCODE(int checkNumPlanes;)
+    SkASSERT(SkYUVAIndex::AreValidIndices(indices, &checkNumPlanes));
+    SkASSERT(checkNumPlanes == this->numPlanes());
+    return true;
+}
+
+SkYUVAInfo YUVAPlanarConfig::getYUVAInfo(SkISize dimensions, SkYUVColorSpace yuvColorSpace) const {
+    return SkYUVAInfo(dimensions, fPlanarConfig, yuvColorSpace);
+}
+
 SkYUVAPixmaps YUVAPlanarConfig::makeYUVAPixmaps(SkISize dimensions,
                                                 SkYUVColorSpace yuvColorSpace,
                                                 const SkBitmap bitmaps[],
                                                 int numBitmaps) const {
-    SkYUVAInfo info(dimensions, fPlanarConfig, yuvColorSpace);
+    SkYUVAInfo info = this->getYUVAInfo(dimensions, yuvColorSpace);
     SkPixmap pmaps[SkYUVAInfo::kMaxPlanes];
     int n = info.numPlanes();
     if (numBitmaps < n) {
@@ -193,6 +329,38 @@
     return SkYUVAPixmaps::FromExternalPixmaps(info, pmaps);
 }
 
+static bool is_colorType_texturable(const GrCaps* caps, GrColorType ct) {
+    GrBackendFormat format = caps->getDefaultBackendFormat(ct, GrRenderable::kNo);
+    if (!format.isValid()) {
+        return false;
+    }
+
+    return caps->isFormatTexturable(format);
+}
+
+static bool is_format_natively_supported(GrRecordingContext* context, YUVFormat yuvFormat) {
+
+    const GrCaps* caps = context->priv().caps();
+
+    switch (yuvFormat) {
+        case kP016_YUVFormat:  // fall through
+        case kP010_YUVFormat:  return is_colorType_texturable(caps, GrColorType::kAlpha_16) &&
+                                      is_colorType_texturable(caps, GrColorType::kRG_1616);
+        case kP016F_YUVFormat: return is_colorType_texturable(caps, GrColorType::kAlpha_F16) &&
+                                      is_colorType_texturable(caps, GrColorType::kRG_F16);
+        case kY416_YUVFormat:  return is_colorType_texturable(caps, GrColorType::kRGBA_16161616);
+        case kAYUV_YUVFormat:  return is_colorType_texturable(caps, GrColorType::kRGBA_8888);
+        case kY410_YUVFormat:  return is_colorType_texturable(caps, GrColorType::kRGBA_1010102);
+        case kNV12_YUVFormat:  // fall through
+        case kNV21_YUVFormat:  return is_colorType_texturable(caps, GrColorType::kGray_8) &&
+                                      is_colorType_texturable(caps, GrColorType::kRG_88);
+        case kI420_YUVFormat: // fall through
+        case kYV12_YUVFormat: return is_colorType_texturable(caps, GrColorType::kGray_8);
+    }
+
+    SkUNREACHABLE;
+}
+
 // All the planes we need to construct the various YUV formats
 struct PlaneData {
    SkBitmap fYFull;
@@ -359,6 +527,19 @@
     yuv[3] = SkColorGetA(col);
 }
 
+static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
+    uint8_t y = yuva[0];
+    uint8_t u = yuva[1];
+    uint8_t v = yuva[2];
+    uint8_t a = yuva[3];
+
+    uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
+    uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
+    uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
+
+    return SkPremultiplyARGBInline(a, r, g, b);
+}
+
 static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
     if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
         // To test the identity color space we use JPEG YUV planes
@@ -648,6 +829,118 @@
     return nextLayer;
 }
 
+static uint8_t look_up(float x1, float y1, const SkBitmap& bm, int channelIdx) {
+    SkASSERT(x1 > 0 && x1 < 1.0f);
+    SkASSERT(y1 > 0 && y1 < 1.0f);
+    int x = SkScalarFloorToInt(x1 * bm.width());
+    int y = SkScalarFloorToInt(y1 * bm.height());
+
+    auto channelMask = SkColorTypeChannelFlags(bm.colorType());
+    SkColorChannel channel;
+    SkAssertResult(YUVAPlanarConfig::ChannelIndexToChannel(channelMask, channelIdx, &channel));
+    auto ii = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, bm.alphaType(), bm.refColorSpace());
+    uint32_t pixel;
+    SkAssertResult(bm.readPixels(ii, &pixel, sizeof(pixel), x, y));
+    int shift = static_cast<int>(channel) * 8;
+    return static_cast<uint8_t>((pixel >> shift) & 0xff);
+}
+
+class YUVGenerator : public SkImageGenerator {
+public:
+    YUVGenerator(const SkImageInfo& ii,
+                 YUVAPlanarConfig planarConfig,
+                 SkYUVColorSpace yuvColorSpace,
+                 SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
+            : SkImageGenerator(ii), fPlanarConfig(planarConfig), fYUVColorSpace(yuvColorSpace) {
+        int numPlanes = fPlanarConfig.numPlanes();
+        for (int i = 0; i < numPlanes; ++i) {
+            fYUVBitmaps[i] = bitmaps[i];
+            SkASSERT(!bitmaps[i].drawsNothing());
+        }
+    }
+
+protected:
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+                     const Options&) override {
+
+        if (kUnknown_SkColorType == fFlattened.colorType()) {
+            fFlattened.allocPixels(info);
+            SkASSERT(kN32_SkColorType == info.colorType());
+            SkASSERT(kPremul_SkAlphaType == info.alphaType());
+
+            float mtx[20];
+            SkColorMatrix_YUV2RGB(fYUVColorSpace, mtx);
+
+            for (int y = 0; y < info.height(); ++y) {
+                for (int x = 0; x < info.width(); ++x) {
+
+                    float x1 = (x + 0.5f) / info.width();
+                    float y1 = (y + 0.5f) / info.height();
+
+                    uint8_t yuva[4] = {0, 0, 0, 255};
+
+                    using YUVAChannel = YUVAPlanarConfig::YUVAChannel;
+                    for (auto c : {YUVAChannel::kY, YUVAChannel::kU, YUVAChannel::kV}) {
+                        const auto& bmp = fYUVBitmaps[fPlanarConfig.planeIndex(c)];
+                        int channelIdx = fPlanarConfig.channelIndex(c);
+                        yuva[static_cast<int>(c)] = look_up(x1, y1, bmp, channelIdx);
+                    }
+                    if (fPlanarConfig.hasAlpha()) {
+                        const auto& bmp = fYUVBitmaps[fPlanarConfig.planeIndex(YUVAChannel::kA)];
+                        int channelIdx = fPlanarConfig.channelIndex(YUVAChannel::kA);
+                        yuva[3] = look_up(x1, y1, bmp, channelIdx);
+                    }
+
+                    // Making premul here.
+                    *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
+                }
+            }
+        }
+
+        return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
+    }
+
+    bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
+                         SkYUVAPixmapInfo* info) const override {
+        SkYUVAInfo yuvaInfo =
+                fPlanarConfig.getYUVAInfo(this->getInfo().dimensions(), fYUVColorSpace);
+        SkColorType colorTypes[SkYUVAInfo::kMaxPlanes] = {};
+        for (int i = 0; i < yuvaInfo.numPlanes(); ++i) {
+            colorTypes[i] = fYUVBitmaps[i].colorType();
+        }
+        *info = SkYUVAPixmapInfo(yuvaInfo, colorTypes, /* row bytes */ nullptr);
+        SkASSERT(info->isValid());
+        return true;
+    }
+
+    bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
+        int n = pixmaps.numPlanes();
+        for (int i = 0; i < n; ++i) {
+            SkASSERT(pixmaps.plane(i).dimensions() == fYUVBitmaps[i].dimensions());
+            SkASSERT(pixmaps.plane(i).colorType() == fYUVBitmaps[i].colorType());
+            SkRectMemcpy(pixmaps.plane(i).writable_addr(), pixmaps.plane(i).rowBytes(),
+                         fYUVBitmaps[i].getPixels(), fYUVBitmaps[i].rowBytes(),
+                         fYUVBitmaps[i].info().minRowBytes(), fYUVBitmaps[i].height());
+        }
+        return true;
+    }
+
+private:
+    YUVAPlanarConfig fPlanarConfig;
+    SkYUVColorSpace  fYUVColorSpace;
+    SkBitmap         fYUVBitmaps[SkYUVAInfo::kMaxPlanes];
+    SkBitmap         fFlattened;
+};
+
+static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
+                                         YUVAPlanarConfig planarConfig,
+                                         SkYUVColorSpace yuvColorSpace,
+                                         SkBitmap bitmaps[]) {
+    auto gen = std::make_unique<YUVGenerator>(ii, planarConfig, yuvColorSpace, bitmaps);
+
+    return SkImage::MakeFromGenerator(std::move(gen));
+}
+
 static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
     static const char* kYUVColorSpaceNames[] = {"JPEG",     "601",      "709F",     "709L",
                                                 "2020_8F",  "2020_8L",  "2020_10F", "2020_10L",
@@ -695,6 +988,13 @@
     canvas->drawString(rowLabel, 0, y, font, paint);
 }
 
+static GrBackendTexture create_yuva_texture(GrDirectContext* context, const SkBitmap& bm, int index,
+                                            YUVABackendReleaseContext* releaseContext) {
+    return context->createBackendTexture(bm.pixmap(), GrRenderable::kNo, GrProtected::kNo,
+                                         YUVABackendReleaseContext::CreationCompleteProc(index),
+                                         releaseContext);
+}
+
 static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
     static const float kJPEGConversionMatrix[20] = {
         1.0f,  0.0f,       1.402f,    0.0f, -180.0f/255,
@@ -726,10 +1026,18 @@
 // YV12
 class WackyYUVFormatsGM : public GM {
 public:
-    using Type = sk_gpu_test::LazyYUVImage::Type;
+    // This GM has a variety of ways in which the test images can be constructed.
+    enum class ImageType {
+        kYUVAPixmaps,      // SkImage::MakeFromYUVAPixmaps.
+        kYUVATextures,     // SkImage::MakeFromYUVATextures.
+        kGenerator,        // SkImage_Lazy backed by generator that supports YUVA. This is the only
+                           // mode that runs on CPU but CPU uses the flattening onGetPixels.
+    };
 
-    WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, Type type)
-            : fUseTargetColorSpace(useTargetColorSpace), fUseSubset(useSubset), fImageType(type) {
+    WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, ImageType imageType)
+            : fUseTargetColorSpace(useTargetColorSpace)
+            , fUseSubset(useSubset)
+            , fImageType(imageType) {
         this->setBGColor(0xFFCCCCCC);
     }
 
@@ -743,12 +1051,12 @@
             name += "_domain";
         }
         switch (fImageType) {
-            case Type::kFromPixmaps:
+            case ImageType::kYUVAPixmaps:
                 name += "_frompixmaps";
                 break;
-            case Type::kFromTextures:
+            case ImageType::kYUVATextures:
                 break;
-            case Type::kFromGenerator:
+            case ImageType::kGenerator:
                 name += "_imggen";
                 break;
         }
@@ -800,14 +1108,65 @@
 
                     int numPlanes = create_YUV(planes, format, resultBMs, opaque);
                     const YUVAPlanarConfig planarConfig(format, opaque);
-                    SkYUVAPixmaps pixmaps =
-                            planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
-                                                         static_cast<SkYUVColorSpace>(cs),
-                                                         resultBMs,
-                                                         numPlanes);
-                    auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
+                    SkASSERT(numPlanes == planarConfig.numPlanes());
 
-                    fImages[opaque][cs][format] = lazyYUV->refImage(dContext, fImageType);
+                    if (fImageType == ImageType::kYUVATextures) {
+                        SkASSERT(dContext);
+
+                        if (dContext->abandoned()) {
+                            return false;
+                        }
+
+                        if (!is_format_natively_supported(dContext, format)) {
+                            continue;
+                        }
+
+                        auto releaseCtx = new YUVABackendReleaseContext(dContext);
+
+                        for (int i = 0; i < numPlanes; ++i) {
+                            GrBackendTexture tmp = create_yuva_texture(dContext, resultBMs[i], i,
+                                                                       releaseCtx);
+                            if (!tmp.isValid()) {
+                                YUVABackendReleaseContext::Unwind(dContext, releaseCtx, false);
+                                return false;
+                            }
+                            releaseCtx->set(i, tmp);
+                        }
+
+                        SkYUVAIndex yuvaIndices[4];
+                        if (!planarConfig.getYUVAIndices(releaseCtx->beTextures(),
+                                                         numPlanes,
+                                                         yuvaIndices)) {
+                            YUVABackendReleaseContext::Unwind(dContext, releaseCtx, false);
+                            continue;
+                        }
+
+                        fImages[opaque][cs][format] =
+                                SkImage::MakeFromYUVATextures(dContext,
+                                                              (SkYUVColorSpace)cs,
+                                                              releaseCtx->beTextures(),
+                                                              yuvaIndices,
+                                                              fOriginalBMs[opaque].dimensions(),
+                                                              kTopLeft_GrSurfaceOrigin,
+                                                              nullptr,
+                                                              YUVABackendReleaseContext::Release,
+                                                              releaseCtx);
+                    } else if (fImageType == ImageType::kGenerator) {
+                        SkImageInfo ii = SkImageInfo::MakeN32(fOriginalBMs[opaque].width(),
+                                                              fOriginalBMs[opaque].height(),
+                                                              kPremul_SkAlphaType);
+                        fImages[opaque][cs][format] = make_yuv_gen_image(
+                                ii, planarConfig, (SkYUVColorSpace)cs, resultBMs);
+                    } else if (fImageType == ImageType::kYUVAPixmaps) {
+                        SkASSERT(dContext);
+                        SkYUVAPixmaps pixmaps =
+                                planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
+                                                             static_cast<SkYUVColorSpace>(cs),
+                                                             resultBMs,
+                                                             numPlanes);
+                        fImages[opaque][cs][format] =
+                                SkImage::MakeFromYUVAPixmaps(dContext, pixmaps);
+                    }
                 }
             }
         }
@@ -833,7 +1192,7 @@
         }
 
         // Only the generator is expected to work with the CPU backend.
-        if (fImageType != Type::kFromGenerator && !dContext) {
+        if (fImageType != ImageType::kGenerator && !dContext) {
             return DrawResult::kSkip;
         }
 
@@ -919,7 +1278,7 @@
     sk_sp<SkImage>             fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
     bool                       fUseTargetColorSpace;
     bool                       fUseSubset;
-    Type                       fImageType;
+    ImageType                  fImageType;
     sk_sp<SkColorSpace>        fTargetColorSpace;
 
     using INHERITED = GM;
@@ -929,19 +1288,19 @@
 
 DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
                                     /* subset */ false,
-                                    WackyYUVFormatsGM::Type::kFromTextures);)
+                                    WackyYUVFormatsGM::ImageType::kYUVATextures);)
 DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
                                     /* subset */ true,
-                                    WackyYUVFormatsGM::Type::kFromTextures);)
+                                    WackyYUVFormatsGM::ImageType::kYUVATextures);)
 DEF_GM(return new WackyYUVFormatsGM(/* target cs */ true,
                                     /* subset */ false,
-                                    WackyYUVFormatsGM::Type::kFromTextures);)
+                                    WackyYUVFormatsGM::ImageType::kYUVATextures);)
 DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
                                     /* subset */ false,
-                                    WackyYUVFormatsGM::Type::kFromGenerator);)
+                                    WackyYUVFormatsGM::ImageType::kGenerator);)
 DEF_GM(return new WackyYUVFormatsGM(/* target cs */ false,
                                     /* subset */ false,
-                                    WackyYUVFormatsGM::Type::kFromPixmaps);)
+                                    WackyYUVFormatsGM::ImageType::kYUVAPixmaps);)
 
 class YUVMakeColorSpaceGM : public GpuGM {
 public:
@@ -993,21 +1352,52 @@
             create_YUV(planes, kAYUV_YUVFormat, resultBMs, opaque);
 
             YUVAPlanarConfig planarConfig(kAYUV_YUVFormat, opaque);
+            int numPlanes = planarConfig.numPlanes();
 
-            auto yuvaPixmaps = planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
-                                                            kJPEG_Full_SkYUVColorSpace,
-                                                            resultBMs,
-                                                            SK_ARRAY_COUNT(resultBMs));
+            auto releaseContext = new YUVABackendReleaseContext(context);
+            auto srgbReleaseContext = new YUVABackendReleaseContext(context);
 
-            int i = 0;
-            for (sk_sp<SkColorSpace> cs : {sk_sp<SkColorSpace>(nullptr),
-                                           SkColorSpace::MakeSRGB()}) {
-                auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(yuvaPixmaps,
-                                                               GrMipmapped::kNo,
-                                                               std::move(cs));
-                fImages[opaque][i++] =
-                        lazyYUV->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
+            for (int i = 0; i < numPlanes; ++i) {
+                GrBackendTexture tmp = create_yuva_texture(context, resultBMs[i], i,
+                                                           releaseContext);
+                if (!tmp.isValid()) {
+                    YUVABackendReleaseContext::Unwind(context, releaseContext, false);
+                    YUVABackendReleaseContext::Unwind(context, srgbReleaseContext, false);
+                    return false;
+                }
+
+                releaseContext->set(i, tmp);
+
+                tmp = create_yuva_texture(context, resultBMs[i], i, srgbReleaseContext);
+                if (!tmp.isValid()) {
+                    YUVABackendReleaseContext::Unwind(context, releaseContext, false);
+                    YUVABackendReleaseContext::Unwind(context, srgbReleaseContext, false);
+                    return false;
+                }
+
+                srgbReleaseContext->set(i, tmp);
             }
+
+            SkYUVAIndex yuvaIndices[4];
+            planarConfig.getYUVAIndices(releaseContext->beTextures(), numPlanes, yuvaIndices);
+
+            fImages[opaque][0] = SkImage::MakeFromYUVATextures(
+                    context,
+                    kJPEG_SkYUVColorSpace,
+                    releaseContext->beTextures(),
+                    yuvaIndices,
+                    { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
+                    kTopLeft_GrSurfaceOrigin, nullptr,
+                    YUVABackendReleaseContext::Release, releaseContext);
+            fImages[opaque][1] = SkImage::MakeFromYUVATextures(
+                    context,
+                    kJPEG_SkYUVColorSpace,
+                    srgbReleaseContext->beTextures(),
+                    yuvaIndices,
+                    { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
+                    kTopLeft_GrSurfaceOrigin,
+                    SkColorSpace::MakeSRGB(),
+                    YUVABackendReleaseContext::Release, srgbReleaseContext);
         }
 
         // Some backends (e.g., Vulkan) require all work be completed for backend textures before
diff --git a/gm/yuv420_odd_dim.cpp b/gm/yuv420_odd_dim.cpp
index 0277208..91647cf 100644
--- a/gm/yuv420_odd_dim.cpp
+++ b/gm/yuv420_odd_dim.cpp
@@ -49,7 +49,7 @@
     if (!imageHelper) {
         return nullptr;
     }
-    return imageHelper->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
+    return imageHelper->refImage(rContext);
 }
 
 // This GM tests that the YUVA image code path in the GPU backend handles odd sized images with
diff --git a/include/core/SkYUVAInfo.h b/include/core/SkYUVAInfo.h
index da94ef7..d2659c4 100644
--- a/include/core/SkYUVAInfo.h
+++ b/include/core/SkYUVAInfo.h
@@ -11,7 +11,6 @@
 #include "include/codec/SkEncodedOrigin.h"
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkSize.h"
-#include "include/core/SkYUVAIndex.h"
 
 /**
  * Specifies the structure of planes for a YUV image with optional alpha. The actual planar data
@@ -98,15 +97,6 @@
      */
     static constexpr int NumChannelsInPlane(PlanarConfig, int i);
 
-    /**
-     * Given a PlanarConfig and a set of channel flags for each plane, convert to SkYUVAIndex
-     * representation. Fails if channel flags aren't valid for the PlanarConfig (i.e. don't have
-     * enough channels in a plane).
-     */
-    static bool GetYUVAIndices(PlanarConfig,
-                               const uint32_t planeChannelFlags[kMaxPlanes],
-                               SkYUVAIndex indices[SkYUVAIndex::kIndexCount]);
-
     /** Does the PlanarConfig have alpha values? */
     static bool HasAlpha(PlanarConfig);
 
@@ -165,15 +155,6 @@
 
     int numChannelsInPlane(int i) const { return NumChannelsInPlane(fPlanarConfig, i); }
 
-    /**
-     * Given a set of channel flags for each plane, converts this->planarConfig() to SkYUVAIndex
-     * representation. Fails if the channel flags aren't valid for the PlanarConfig (i.e. don't have
-     * enough channels in a plane).
-     */
-    bool toYUVAIndices(const uint32_t channelFlags[4], SkYUVAIndex indices[4]) const {
-        return GetYUVAIndices(fPlanarConfig, channelFlags, indices);
-    }
-
     bool operator==(const SkYUVAInfo& that) const;
     bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); }
 
diff --git a/include/core/SkYUVAPixmaps.h b/include/core/SkYUVAPixmaps.h
index 69e1085..4a6601e 100644
--- a/include/core/SkYUVAPixmaps.h
+++ b/include/core/SkYUVAPixmaps.h
@@ -183,12 +183,6 @@
     static SkYUVAPixmaps FromData(const SkYUVAPixmapInfo&, sk_sp<SkData>);
 
     /**
-     * Makes a deep copy of the src SkYUVAPixmaps. The returned SkYUVAPixmaps owns its planes'
-     * backing stores.
-     */
-    static SkYUVAPixmaps MakeCopy(const SkYUVAPixmaps& src);
-
-    /**
      * Use passed in memory as backing store for pixmaps' pixels. Caller must ensure memory remains
      * allocated while pixmaps are in use. There must be at least
      * SkYUVAPixmapInfo::computeTotalBytes() allocated starting at memory.
@@ -217,8 +211,6 @@
 
     const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; }
 
-    SkYUVAPixmapInfo pixmapsInfo() const;
-
     /** Number of pixmap planes or 0 if this SkYUVAPixmaps is invalid. */
     int numPlanes() const { return this->isValid() ? fYUVAInfo.numPlanes() : 0; }
 
@@ -235,18 +227,9 @@
     const SkPixmap& plane(int i) const { return fPlanes[SkToSizeT(i)]; }
 
     /**
-     * Computes a SkYUVAIndex representation of the planar layout. Returns true on success and
-     * false on failure. Will succeed whenever this->isValid() is true.
-     */
-    bool toYUVAIndices(SkYUVAIndex[SkYUVAIndex::kIndexCount]) const;
-
-    /** Does this SkPixmaps own the backing store of the planes? */
-    bool ownsStorage() const { return SkToBool(fData); }
-
-    /**
      * Conversion to legacy SkYUVA data structures.
      */
-    bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[SkYUVAIndex::kIndexCount]) const;
+    bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[4]) const;
 
 private:
     SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp<SkData>);
diff --git a/src/core/SkYUVAInfo.cpp b/src/core/SkYUVAInfo.cpp
index 5c46e0f..a43b477 100644
--- a/src/core/SkYUVAInfo.cpp
+++ b/src/core/SkYUVAInfo.cpp
@@ -79,135 +79,6 @@
     SkUNREACHABLE;
 }
 
-static bool channel_index_to_channel(uint32_t channelFlags,
-                                     int channelIdx,
-                                     SkColorChannel* channel) {
-    switch (channelFlags) {
-        case kGray_SkColorChannelFlag:  // For gray returning any of R, G, or B for index 0 is ok.
-        case kRed_SkColorChannelFlag:
-            if (channelIdx == 0) {
-                *channel = SkColorChannel::kR;
-                return true;
-            }
-            return false;
-        case kAlpha_SkColorChannelFlag:
-            if (channelIdx == 0) {
-                *channel = SkColorChannel::kA;
-                return true;
-            }
-            return false;
-        case kRG_SkColorChannelFlags:
-            if (channelIdx == 0 || channelIdx == 1) {
-                *channel = static_cast<SkColorChannel>(channelIdx);
-                return true;
-            }
-            return false;
-        case kRGB_SkColorChannelFlags:
-            if (channelIdx >= 0 && channelIdx <= 2) {
-                *channel = static_cast<SkColorChannel>(channelIdx);
-                return true;
-            }
-            return false;
-        case kRGBA_SkColorChannelFlags:
-            if (channelIdx >= 0 && channelIdx <= 3) {
-                *channel = static_cast<SkColorChannel>(channelIdx);
-                return true;
-            }
-            return false;
-        default:
-            return false;
-    }
-}
-
-bool SkYUVAInfo::GetYUVAIndices(PlanarConfig config,
-                                const uint32_t planeChannelFlags[kMaxPlanes],
-                                SkYUVAIndex indices[SkYUVAIndex::kIndexCount]) {
-    struct Location {int plane, chanIdx;};
-    const Location* locations = nullptr;
-    switch (config) {
-        case PlanarConfig::kY_U_V_444:
-        case PlanarConfig::kY_U_V_422:
-        case PlanarConfig::kY_U_V_420:
-        case PlanarConfig::kY_U_V_440:
-        case PlanarConfig::kY_U_V_411:
-        case PlanarConfig::kY_U_V_410: {
-            static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {2, 0}, {-1, -1}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kY_V_U_420: {
-            static constexpr Location kLocations[] = {{0, 0}, {2, 0}, {1, 0}, {-1, -1}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kY_U_V_A_4204: {
-            static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kY_V_U_A_4204: {
-            static constexpr Location kLocations[] = {{0, 0}, {2, 0}, {1, 0}, {3, 0}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kY_UV_420: {
-            static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {1, 1}, {-1, -1}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kY_VU_420: {
-            static constexpr Location kLocations[] = {{0, 0}, {1, 1}, {1, 0}, {-1, -1}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kY_UV_A_4204: {
-            static constexpr Location kLocations[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kY_VU_A_4204: {
-            static constexpr Location kLocations[] = {{0, 0}, {1, 1}, {1, 0}, {2, 0}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kYUV_444: {
-            static constexpr Location kLocations[] = {{0, 0}, {0, 1}, {0, 2}, {-1, -1}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kUYV_444: {
-            static constexpr Location kLocations[] = {{0, 1}, {0, 0}, {0, 2}, {-1, -1}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kYUVA_4444: {
-            static constexpr Location kLocations[] = {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
-            locations = kLocations;
-            break;
-        }
-        case PlanarConfig::kUYVA_4444: {
-            static constexpr Location kLocations[] = {{0, 1}, {0, 0}, {0, 2}, {0, 3}};
-            locations = kLocations;
-            break;
-        }
-    }
-    SkASSERT(locations);
-    for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
-        const auto& [plane, chanIdx] = locations[i];
-        SkColorChannel channel;
-        if (plane >= 0) {
-            if (!channel_index_to_channel(planeChannelFlags[plane], chanIdx, &channel)) {
-                return false;
-            }
-            indices[i] = {plane, channel};
-        } else {
-            SkASSERT(i == 3);
-            indices[i] = {-1, SkColorChannel::kR};
-        }
-    }
-    return true;
-}
-
 bool SkYUVAInfo::HasAlpha(PlanarConfig planarConfig) {
     switch (planarConfig) {
         case PlanarConfig::kY_U_V_444:    return false;
diff --git a/src/core/SkYUVAPixmaps.cpp b/src/core/SkYUVAPixmaps.cpp
index e840fc5..d7bdef9 100644
--- a/src/core/SkYUVAPixmaps.cpp
+++ b/src/core/SkYUVAPixmaps.cpp
@@ -10,7 +10,6 @@
 #include "include/core/SkYUVAIndex.h"
 #include "include/core/SkYUVASizeInfo.h"
 #include "include/private/SkImageInfoPriv.h"
-#include "src/core/SkConvertPixels.h"
 
 #if SK_SUPPORT_GPU
 #include "include/private/GrImageContext.h"
@@ -97,10 +96,7 @@
     bool ok = true;
     for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
         fRowBytes[i] = rowBytes[i];
-        // Use kUnpremul so that we never multiply alpha when copying data in.
-        fPlaneInfos[i] = SkImageInfo::Make(planeDimensions[i],
-                                           colorTypes[i],
-                                           kUnpremul_SkAlphaType);
+        fPlaneInfos[i] = SkImageInfo::Make(planeDimensions[i], colorTypes[i], kPremul_SkAlphaType);
         int numRequiredChannels = yuvaInfo.numChannelsInPlane(i);
         SkASSERT(numRequiredChannels > 0);
         auto [numColorTypeChannels, colorTypeDataType] = NumChannelsAndDataType(colorTypes[i]);
@@ -195,27 +191,6 @@
     return SkYUVAPixmaps(yuvaPixmapInfo, std::move(data));
 }
 
-SkYUVAPixmaps SkYUVAPixmaps::MakeCopy(const SkYUVAPixmaps& src) {
-    if (!src.isValid()) {
-        return {};
-    }
-    SkYUVAPixmaps result = Allocate(src.pixmapsInfo());
-    int n = result.numPlanes();
-    for (int i = 0; i < n; ++i) {
-        // We use SkRectMemCpy rather than readPixels to ensure that we don't do any alpha type
-        // conversion.
-        const SkPixmap& s = src.plane(i);
-        const SkPixmap& d = result.plane(i);
-        SkRectMemcpy(d.writable_addr(),
-                     d.rowBytes(),
-                     s.addr(),
-                     s.rowBytes(),
-                     s.info().minRowBytes(),
-                     s.height());
-    }
-    return result;
-}
-
 SkYUVAPixmaps SkYUVAPixmaps::FromExternalMemory(const SkYUVAPixmapInfo& yuvaPixmapInfo,
                                                 void* memory) {
     if (!yuvaPixmapInfo.isValid()) {
@@ -255,43 +230,168 @@
     std::copy_n(pixmaps, yuvaInfo.numPlanes(), fPlanes.data());
 }
 
-SkYUVAPixmapInfo SkYUVAPixmaps::pixmapsInfo() const {
-    if (!this->isValid()) {
-        return {};
-    }
-    SkColorType colorTypes[kMaxPlanes] = {};
-    size_t rowBytes[kMaxPlanes] = {};
-    int numPlanes = this->numPlanes();
-    for (int i = 0; i < numPlanes; ++i) {
-        colorTypes[i] = fPlanes[i].colorType();
-        rowBytes[i] = fPlanes[i].rowBytes();
-    }
-    return {fYUVAInfo, colorTypes, rowBytes};
-}
-
-bool SkYUVAPixmaps::toYUVAIndices(SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount]) const {
-    SkASSERT(yuvaIndices);
-    uint32_t channelFlags[] = {SkColorTypeChannelFlags(fPlanes[0].colorType()),
-                               SkColorTypeChannelFlags(fPlanes[1].colorType()),
-                               SkColorTypeChannelFlags(fPlanes[2].colorType()),
-                               SkColorTypeChannelFlags(fPlanes[3].colorType())};
-    bool result = fYUVAInfo.toYUVAIndices(channelFlags, yuvaIndices);
-    SkASSERT(result == this->isValid());
-    return result;
-}
-
 bool SkYUVAPixmaps::toLegacy(SkYUVASizeInfo* yuvaSizeInfo, SkYUVAIndex yuvaIndices[4]) const {
     if (!this->isValid()) {
         return false;
     }
-    SkYUVAIndex tempIndices[4];
-    if (!yuvaIndices) {
-        yuvaIndices = tempIndices;
+    bool ok = true;
+    auto getIthChannel = [&ok](SkColorType ct, int idx) -> SkColorChannel {
+      switch (SkColorTypeChannelFlags(ct)) {
+          case kAlpha_SkColorChannelFlag:
+              ok &= idx == 0;
+              return SkColorChannel::kA;
+          case kGray_SkColorChannelFlag:
+          case kRed_SkColorChannelFlag:
+              ok &= idx == 0;
+              return SkColorChannel::kR;
+          case kRG_SkColorChannelFlags:
+              ok &= idx < 2;
+              return static_cast<SkColorChannel>(idx);
+          case kRGB_SkColorChannelFlags:
+              ok &= idx < 3;
+              return static_cast<SkColorChannel>(idx);
+          case kRGBA_SkColorChannelFlags:
+              ok &= idx < 4;
+              return static_cast<SkColorChannel>(idx);
+          default:
+              ok = false;
+              return SkColorChannel::kR;
+      }
+    };
+    SkColorType cts[] = {fPlanes[0].colorType(),
+                         fPlanes[1].colorType(),
+                         fPlanes[2].colorType(),
+                         fPlanes[3].colorType()};
+    switch (fYUVAInfo.planarConfig()) {
+        case SkYUVAInfo::PlanarConfig::kY_U_V_444:
+        case SkYUVAInfo::PlanarConfig::kY_U_V_422:
+        case SkYUVAInfo::PlanarConfig::kY_U_V_420:
+        case SkYUVAInfo::PlanarConfig::kY_U_V_440:
+        case SkYUVAInfo::PlanarConfig::kY_U_V_411:
+        case SkYUVAInfo::PlanarConfig::kY_U_V_410:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex =  1;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex =  2;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[2], 0);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;  // arbitrary
+            break;
+        case SkYUVAInfo::PlanarConfig::kY_V_U_420:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex =  2;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex =  1;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[2], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;  // arbitrary
+            break;
+        case SkYUVAInfo::PlanarConfig::kY_U_V_A_4204:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 2;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 3;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[2], 0);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[3], 0);
+            break;
+        case SkYUVAInfo::PlanarConfig::kY_V_U_A_4204:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 2;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 3;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[2], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[3], 0);
+            break;
+        case SkYUVAInfo::PlanarConfig::kY_UV_420:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex =  1;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex =  1;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 1);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;  // arbitrary
+            break;
+        case SkYUVAInfo::PlanarConfig::kY_VU_420:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex =  1;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex =  1;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 1);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;  // arbitrary
+            break;
+        case SkYUVAInfo::PlanarConfig::kY_UV_A_4204:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 2;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 1);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[2], 0);
+            break;
+        case SkYUVAInfo::PlanarConfig::kY_VU_A_4204:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 1;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 1;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 2;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[1], 1);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[1], 0);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[2], 0);
+            break;
+        case SkYUVAInfo::PlanarConfig::kYUV_444:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 1);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;  // arbitrary
+            break;
+        case SkYUVAInfo::PlanarConfig::kUYV_444:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex =  0;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = -1;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 1);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR;  // arbitrary
+            break;
+        case SkYUVAInfo::PlanarConfig::kYUVA_4444:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 1);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[0], 3);
+            break;
+        case SkYUVAInfo::PlanarConfig::kUYVA_4444:
+            yuvaIndices[SkYUVAIndex::kY_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kU_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kV_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kA_Index].fIndex = 0;
+            yuvaIndices[SkYUVAIndex::kY_Index].fChannel = getIthChannel(cts[0], 1);
+            yuvaIndices[SkYUVAIndex::kU_Index].fChannel = getIthChannel(cts[0], 0);
+            yuvaIndices[SkYUVAIndex::kV_Index].fChannel = getIthChannel(cts[0], 2);
+            yuvaIndices[SkYUVAIndex::kA_Index].fChannel = getIthChannel(cts[0], 3);
+            break;
     }
-    if (!this->toYUVAIndices(yuvaIndices)) {
+    if (!ok) {
         return false;
     }
-
     if (yuvaSizeInfo) {
         yuvaSizeInfo->fOrigin = fYUVAInfo.origin();
         int n = fYUVAInfo.numPlanes();
diff --git a/tools/gpu/ManagedBackendTexture.cpp b/tools/gpu/ManagedBackendTexture.cpp
index a1a7449..192a57c 100644
--- a/tools/gpu/ManagedBackendTexture.cpp
+++ b/tools/gpu/ManagedBackendTexture.cpp
@@ -14,9 +14,9 @@
 namespace {
 
 struct Context {
+    sk_sp<sk_gpu_test::ManagedBackendTexture> fMBET;
     GrGpuFinishedProc fWrappedProc = nullptr;
     GrGpuFinishedContext fWrappedContext = nullptr;
-    sk_sp<sk_gpu_test::ManagedBackendTexture> fMBETs[SkYUVAInfo::kMaxPlanes];
 };
 
 }  // anonymous namespace
@@ -40,16 +40,7 @@
                                             GrGpuFinishedContext wrappedCtx) const {
     // Make sure we don't get a wrapped ctx without a wrapped proc
     SkASSERT(!wrappedCtx || wrappedProc);
-    return new Context{wrappedProc, wrappedCtx, {sk_ref_sp(this)}};
-}
-
-void* ManagedBackendTexture::MakeYUVAReleaseContext(
-        const sk_sp<ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes]) {
-    auto context = new Context;
-    for (int i = 0; i < SkYUVAInfo::kMaxPlanes; ++i) {
-        context->fMBETs[i] = mbets[i];
-    }
-    return context;
+    return new Context{sk_ref_sp(this), wrappedProc, wrappedCtx};
 }
 
 sk_sp<GrRefCntedCallback> ManagedBackendTexture::refCountedCallback() const {
diff --git a/tools/gpu/ManagedBackendTexture.h b/tools/gpu/ManagedBackendTexture.h
index 7c65d54..931e71b 100644
--- a/tools/gpu/ManagedBackendTexture.h
+++ b/tools/gpu/ManagedBackendTexture.h
@@ -9,7 +9,6 @@
 #define ManagedBackendTexture_DEFINED
 
 #include "include/core/SkRefCnt.h"
-#include "include/core/SkYUVAInfo.h"
 #include "include/gpu/GrDirectContext.h"
 
 class GrRefCntedCallback;
@@ -71,14 +70,6 @@
      */
     void wasAdopted();
 
-    /**
-     * SkImage::MakeFromYUVATextures takes a single release proc that is called once for all the
-     * textures. This makes a single release context for the group of textures. It's used with the
-     * standard ReleaseProc. Like releaseContext(), it must be balanced by a ReleaseProc call for
-     * proper ref counting.
-     */
-    static void* MakeYUVAReleaseContext(const sk_sp<ManagedBackendTexture>[SkYUVAInfo::kMaxPlanes]);
-
     const GrBackendTexture& texture() { return fTexture; }
 
 private:
diff --git a/tools/gpu/YUVUtils.cpp b/tools/gpu/YUVUtils.cpp
index 93c011b..4e8ad24 100644
--- a/tools/gpu/YUVUtils.cpp
+++ b/tools/gpu/YUVUtils.cpp
@@ -7,154 +7,40 @@
 
 #include "tools/gpu/YUVUtils.h"
 
-#include "include/core/SkColorPriv.h"
 #include "include/core/SkData.h"
 #include "include/gpu/GrRecordingContext.h"
 #include "src/codec/SkCodecImageGenerator.h"
-#include "src/core/SkYUVMath.h"
 #include "src/gpu/GrDirectContextPriv.h"
 #include "src/gpu/GrRecordingContextPriv.h"
-#include "tools/gpu/ManagedBackendTexture.h"
-
-namespace {
-
-static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
-    uint8_t y = yuva[0];
-    uint8_t u = yuva[1];
-    uint8_t v = yuva[2];
-    uint8_t a = yuva[3];
-
-    uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
-    uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
-    uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
-
-    return SkPremultiplyARGBInline(a, r, g, b);
-}
-
-static uint8_t look_up(float x1, float y1, const SkPixmap& pmap, SkColorChannel channel) {
-    SkASSERT(x1 > 0 && x1 < 1.0f);
-    SkASSERT(y1 > 0 && y1 < 1.0f);
-    int x = SkScalarFloorToInt(x1 * pmap.width());
-    int y = SkScalarFloorToInt(y1 * pmap.height());
-
-    auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1);
-    uint32_t pixel;
-    SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y));
-    int shift = static_cast<int>(channel) * 8;
-    return static_cast<uint8_t>((pixel >> shift) & 0xff);
-}
-
-class Generator : public SkImageGenerator {
-public:
-    Generator(SkYUVAPixmaps pixmaps, sk_sp<SkColorSpace> cs)
-            : SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(),
-                                                 kN32_SkColorType,
-                                                 kPremul_SkAlphaType,
-                                                 std::move(cs)))
-            , fPixmaps(std::move(pixmaps)) {}
-
-protected:
-    bool onGetPixels(const SkImageInfo& info,
-                     void* pixels,
-                     size_t rowBytes,
-                     const Options&) override {
-        if (kUnknown_SkColorType == fFlattened.colorType()) {
-            fFlattened.allocPixels(info);
-            SkASSERT(info == this->getInfo());
-
-            float mtx[20];
-            SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx);
-            SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
-            SkAssertResult(fPixmaps.toYUVAIndices(yuvaIndices));
-
-            for (int y = 0; y < info.height(); ++y) {
-                for (int x = 0; x < info.width(); ++x) {
-                    float x1 = (x + 0.5f) / info.width();
-                    float y1 = (y + 0.5f) / info.height();
-
-                    uint8_t yuva[4] = {0, 0, 0, 255};
-
-                    for (auto c : {SkYUVAIndex::kY_Index,
-                                   SkYUVAIndex::kU_Index,
-                                   SkYUVAIndex::kV_Index}) {
-                        const auto& pmap = fPixmaps.plane(yuvaIndices[c].fIndex);
-                        yuva[c] = look_up(x1, y1, pmap, yuvaIndices[c].fChannel);
-                    }
-                    if (yuvaIndices[SkYUVAIndex::kA_Index].fIndex >= 0) {
-                        const auto& pmap =
-                                fPixmaps.plane(yuvaIndices[SkYUVAIndex::kA_Index].fIndex);
-                        yuva[3] =
-                                look_up(x1, y1, pmap, yuvaIndices[SkYUVAIndex::kA_Index].fChannel);
-                    }
-
-                    // Making premul here.
-                    *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
-                }
-            }
-        }
-
-        return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
-    }
-
-    bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
-                         SkYUVAPixmapInfo* info) const override {
-        *info = fPixmaps.pixmapsInfo();
-        return info->isValid();
-    }
-
-    bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
-        SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo());
-        for (int i = 0; i < pixmaps.numPlanes(); ++i) {
-            SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType());
-            SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions());
-            SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes());
-            fPixmaps.plane(i).readPixels(pixmaps.plane(i));
-        }
-        return true;
-    }
-
-private:
-    SkYUVAPixmaps fPixmaps;
-    SkBitmap      fFlattened;
-};
-
-}  // anonymous namespace
 
 namespace sk_gpu_test {
 
-std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
-                                                 GrMipmapped mipmapped,
-                                                 sk_sp<SkColorSpace> cs) {
+std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data, GrMipmapped mipmapped) {
     std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
-    if (image->reset(std::move(data), mipmapped, std::move(cs))) {
+    if (image->reset(std::move(data), mipmapped)) {
         return image;
     } else {
         return nullptr;
     }
 }
 
-std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps,
-                                                 GrMipmapped mipmapped,
-                                                 sk_sp<SkColorSpace> cs) {
-    std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
-    if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) {
-        return image;
+sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext) {
+    if (this->ensureYUVImage(rContext)) {
+        return fYUVImage;
     } else {
         return nullptr;
     }
 }
 
-sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext, Type type) {
-    if (this->ensureYUVImage(rContext, type)) {
-        size_t idx = static_cast<size_t>(type);
-        SkASSERT(idx >= 0 && idx < SK_ARRAY_COUNT(fYUVImage));
-        return fYUVImage[idx];
+const SkImage* LazyYUVImage::getImage(GrRecordingContext* rContext) {
+    if (this->ensureYUVImage(rContext)) {
+        return fYUVImage.get();
     } else {
         return nullptr;
     }
 }
 
-bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
+bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped) {
     fMipmapped = mipmapped;
     auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data);
     if (!codec) {
@@ -174,87 +60,78 @@
         return false;
     }
 
-    fColorSpace = std::move(cs);
-
-    // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
-    return true;
-}
-
-bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
-    if (!pixmaps.isValid()) {
+    if (!fPixmaps.toLegacy(&fSizeInfo, fComponents)) {
         return false;
     }
-    fMipmapped = mipmapped;
-    if (pixmaps.ownsStorage()) {
-        fPixmaps = std::move(pixmaps);
-    } else {
-        fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps));
-    }
-    fColorSpace = std::move(cs);
     // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
     return true;
 }
 
-bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) {
-    size_t idx = static_cast<size_t>(type);
-    SkASSERT(idx >= 0 && idx < SK_ARRAY_COUNT(fYUVImage));
-    if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) {
-        return true;  // Have already made a YUV image valid for this context.
+bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext) {
+    if (!rContext) {
+        return false; // Cannot make a YUV image from planes
+    }
+    if (fYUVImage && fYUVImage->isValid(rContext)) {
+        return true; // Have already made a YUV image valid for this context.
     }
     // Try to make a new YUV image for this context.
-    switch (type) {
-        case Type::kFromPixmaps:
-            if (!rContext || rContext->abandoned()) {
-                return false;
-            }
-            fYUVImage[idx] = SkImage::MakeFromYUVAPixmaps(rContext,
-                                                          fPixmaps,
-                                                          fMipmapped,
-                                                          /*limit to max tex size*/ false,
-                                                          fColorSpace);
-            break;
-        case Type::kFromGenerator: {
-            // Make sure the generator has ownership of its backing planes.
-            auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
-            fYUVImage[idx] = SkImage::MakeFromGenerator(std::move(generator));
-            break;
-        }
-        case Type::kFromTextures:
-            if (!rContext || rContext->abandoned()) {
-                return false;
-            }
-            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)) {
-                    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,
-                        fColorSpace,
-                        sk_gpu_test::ManagedBackendTexture::ReleaseProc,
-                        relContext);
-            }
-    }
-    return fYUVImage[idx] != nullptr;
+    fYUVImage = SkImage::MakeFromYUVAPixmaps(rContext, fPixmaps, fMipmapped, false, nullptr);
+    return fYUVImage != nullptr;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+void YUVABackendReleaseContext::Unwind(GrDirectContext* dContext,
+                                       YUVABackendReleaseContext* beContext,
+                                       bool fullFlush) {
+
+    // Some backends (e.g., Vulkan) require that all work associated w/ texture
+    // creation be completed before deleting the textures.
+    if (fullFlush) {
+        // If the release context client performed some operations other than backend texture
+        // creation then we may require a full flush to ensure that all the work is completed.
+        dContext->flush();
+        dContext->submit(true);
+    } else {
+        dContext->submit();
+
+        while (!beContext->creationCompleted()) {
+            dContext->checkAsyncWorkCompletion();
+        }
+    }
+
+    delete beContext;
+}
+
+YUVABackendReleaseContext::YUVABackendReleaseContext(GrDirectContext* dContext)
+        : fDContext(dContext) {
+}
+
+YUVABackendReleaseContext::~YUVABackendReleaseContext() {
+    for (int i = 0; i < 4; ++i) {
+        if (fBETextures[i].isValid()) {
+            SkASSERT(fCreationComplete[i]);
+            fDContext->deleteBackendTexture(fBETextures[i]);
+        }
+    }
+}
+
+template<int I> static void CreationComplete(void* releaseContext) {
+    auto beContext = reinterpret_cast<YUVABackendReleaseContext*>(releaseContext);
+    beContext->setCreationComplete(I);
+}
+
+GrGpuFinishedProc YUVABackendReleaseContext::CreationCompleteProc(int index) {
+    SkASSERT(index >= 0 && index < 4);
+
+    switch (index) {
+        case 0: return CreationComplete<0>;
+        case 1: return CreationComplete<1>;
+        case 2: return CreationComplete<2>;
+        case 3: return CreationComplete<3>;
+    }
+
+    SK_ABORT("Invalid YUVA Index.");
+    return nullptr;
+}
+
 } // namespace sk_gpu_test
diff --git a/tools/gpu/YUVUtils.h b/tools/gpu/YUVUtils.h
index e9dad4f..8363032 100644
--- a/tools/gpu/YUVUtils.h
+++ b/tools/gpu/YUVUtils.h
@@ -25,43 +25,93 @@
 // the image if the context has changed, as in Viewer)
 class LazyYUVImage {
 public:
-    // Returns null if the data could not be extracted into YUVA planes
-    static std::unique_ptr<LazyYUVImage> Make(sk_sp<SkData> data,
-                                              GrMipmapped = GrMipmapped::kNo,
-                                              sk_sp<SkColorSpace> = nullptr);
-    static std::unique_ptr<LazyYUVImage> Make(SkYUVAPixmaps,
-                                              GrMipmapped = GrMipmapped::kNo,
-                                              sk_sp<SkColorSpace> = nullptr);
+    // Returns null if the data could not be extracted into YUVA8 planes
+    static std::unique_ptr<LazyYUVImage> Make(sk_sp<SkData> data, GrMipmapped = GrMipmapped::kNo);
 
-    enum class Type {
-        kFromPixmaps,
-        kFromGenerator,
-        kFromTextures,
-    };
+    sk_sp<SkImage> refImage(GrRecordingContext* rContext);
 
-    SkISize dimensions() const { return fPixmaps.yuvaInfo().dimensions(); }
-
-    sk_sp<SkImage> refImage(GrRecordingContext* rContext, Type);
+    const SkImage* getImage(GrRecordingContext* rContext);
 
 private:
     // Decoded YUV data
     SkYUVAPixmaps fPixmaps;
 
+    // Legacy representation used to import to SkImage.
+    SkYUVASizeInfo fSizeInfo;
+    SkYUVAIndex fComponents[SkYUVAIndex::kIndexCount];
+
     GrMipmapped fMipmapped;
 
-    sk_sp<SkColorSpace> fColorSpace;
-
-    // Memoized SkImages formed with planes, one for each Type.
-    sk_sp<SkImage> fYUVImage[3];
+    // Memoized SkImage formed with planes
+    sk_sp<SkImage> fYUVImage;
 
     LazyYUVImage() = default;
 
-    bool reset(sk_sp<SkData> data, GrMipmapped, sk_sp<SkColorSpace>);
-    bool reset(SkYUVAPixmaps pixmaps, GrMipmapped, sk_sp<SkColorSpace>);
+    bool reset(sk_sp<SkData> data, GrMipmapped);
 
-    bool ensureYUVImage(GrRecordingContext* rContext, Type type);
+    bool ensureYUVImage(GrRecordingContext* rContext);
 };
 
+// A helper for managing the lifetime of backend textures for YUVA images.
+class YUVABackendReleaseContext {
+public:
+    static GrGpuFinishedProc CreationCompleteProc(int index);
+
+    // A stock 'TextureReleaseProc' to use with this class
+    static void Release(void* releaseContext) {
+        auto beContext = reinterpret_cast<YUVABackendReleaseContext*>(releaseContext);
+
+        delete beContext;
+    }
+
+    // Given how and when backend textures are created, just deleting this object often
+    // isn't enough. This helper encapsulates the extra work needed.
+    static void Unwind(GrDirectContext*, YUVABackendReleaseContext* beContext, bool fullFlush);
+
+    YUVABackendReleaseContext(GrDirectContext*);
+    ~YUVABackendReleaseContext();
+
+    void set(int index, const GrBackendTexture& beTex) {
+        SkASSERT(index >= 0 && index < 4);
+        SkASSERT(!fBETextures[index].isValid());
+        SkASSERT(beTex.isValid());
+
+        fBETextures[index] = beTex;
+    }
+
+    void setCreationComplete(int index) {
+        SkASSERT(index >= 0 && index < 4);
+        // In GL, the finished proc can fire before the backend texture is returned to the client
+        // SkASSERT(fBETextures[index].isValid());
+
+        fCreationComplete[index] = true;
+    }
+
+    bool creationCompleted() const {
+        for (int i = 0; i < 4; ++i) {
+            if (fBETextures[i].isValid() && !fCreationComplete[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    const GrBackendTexture* beTextures() const { return fBETextures; }
+
+    const GrBackendTexture& beTexture(int index) {
+        SkASSERT(index >= 0 && index < 4);
+        SkASSERT(fBETextures[index].isValid());
+        return fBETextures[index];
+    }
+
+private:
+    GrDirectContext* fDContext;
+    GrBackendTexture fBETextures[4];
+    bool             fCreationComplete[4] = { false };
+};
+
+
 } // namespace sk_gpu_test
 
 #endif // YUVUtils_DEFINED