Add asyncRescaleAndReadPixeksYUVA420 methods

New methods are added to SkImage, SkSurface`, and skgpu::graphite::context named asyncRescaleAndReadPixeksYUVA420. These function identically to the existing asyncRescaleAndReadPixelsYUV420 methods but return a fourth plane containing alpha at full resolution.

Change-Id: I96d02fa3b5e7827c990bd13648829db413d7be2d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/721976
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <briansalomon@gmail.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/AUTHORS b/AUTHORS
index db8daa4..78a79a7 100755
--- a/AUTHORS
+++ b/AUTHORS
@@ -21,6 +21,7 @@
 Andrew Kurushin <ajax16384@gmail.com>
 Bharat Ahuja <ahujabharat93@gmail.com>
 Biswapriyo Nath <nathbappai@gmail.com>
+Brian Salomon <briansalomon@gmail.com>
 Callum Moffat <smartercallum@gmail.com>
 Cary Clark <cclark2@gmail.com>
 Casey Banner <kcbanner@gmail.com>
diff --git a/gm/asyncrescaleandread.cpp b/gm/asyncrescaleandread.cpp
index 39cae6c..5414fcc 100644
--- a/gm/asyncrescaleandread.cpp
+++ b/gm/asyncrescaleandread.cpp
@@ -105,6 +105,7 @@
                                             GrDirectContext* direct,
                                             skgpu::graphite::Recorder* recorder,
                                             SkYUVColorSpace yuvCS,
+                                            bool readAlpha,
                                             const SkIRect& srcRect,
                                             SkISize size,
                                             SkImage::RescaleGamma rescaleGamma,
@@ -113,7 +114,7 @@
     SkASSERT(!(size.width() & 0b1) && !(size.height() & 0b1));
 
     SkISize uvSize = {size.width()/2, size.height()/2};
-    SkImageInfo yII  = SkImageInfo::Make(size,   kGray_8_SkColorType, kPremul_SkAlphaType);
+    SkImageInfo yaII = SkImageInfo::Make(size  , kGray_8_SkColorType, kPremul_SkAlphaType);
     SkImageInfo uvII = SkImageInfo::Make(uvSize, kGray_8_SkColorType, kPremul_SkAlphaType);
 
     AsyncContext asyncContext;
@@ -134,18 +135,32 @@
             return nullptr;
         }
 
-        graphiteContext->asyncRescaleAndReadPixelsYUV420(src, yuvCS, SkColorSpace::MakeSRGB(),
-                                                         srcRect, size, rescaleGamma, rescaleMode,
-                                                         async_callback, &asyncContext);
+        if (readAlpha) {
+            graphiteContext->asyncRescaleAndReadPixelsYUVA420(src, yuvCS, SkColorSpace::MakeSRGB(),
+                                                              srcRect, size, rescaleGamma,
+                                                              rescaleMode, async_callback,
+                                                              &asyncContext);
+        } else {
+            graphiteContext->asyncRescaleAndReadPixelsYUV420(src, yuvCS, SkColorSpace::MakeSRGB(),
+                                                             srcRect, size, rescaleGamma,
+                                                             rescaleMode, async_callback,
+                                                             &asyncContext);
+        }
         graphiteContext->submit();
         while (!asyncContext.fCalled) {
             graphiteContext->checkAsyncWorkCompletion();
         }
 #endif
     } else {
-        src->asyncRescaleAndReadPixelsYUV420(yuvCS, SkColorSpace::MakeSRGB(),
-                                             srcRect, size, rescaleGamma, rescaleMode,
-                                             async_callback, &asyncContext);
+        if (readAlpha) {
+            src->asyncRescaleAndReadPixelsYUVA420(yuvCS, SkColorSpace::MakeSRGB(),
+                                                  srcRect, size, rescaleGamma, rescaleMode,
+                                                  async_callback, &asyncContext);
+        } else {
+            src->asyncRescaleAndReadPixelsYUV420(yuvCS, SkColorSpace::MakeSRGB(),
+                                                 srcRect, size, rescaleGamma, rescaleMode,
+                                                 async_callback, &asyncContext);
+        }
         if (direct) {
             direct->submit();
         }
@@ -158,15 +173,21 @@
     if (!asyncContext.fResult) {
         return nullptr;
     }
+    auto planeConfig = readAlpha ? SkYUVAInfo::PlaneConfig::kY_U_V_A
+                                 : SkYUVAInfo::PlaneConfig::kY_U_V;
     SkYUVAInfo yuvaInfo(size,
-                        SkYUVAInfo::PlaneConfig::kY_U_V,
+                        planeConfig,
                         SkYUVAInfo::Subsampling::k420,
                         yuvCS);
-    SkPixmap yuvPMs[] = {
-            {yII,  asyncContext.fResult->data(0), asyncContext.fResult->rowBytes(0)},
+    SkPixmap yuvPMs[4] = {
+            {yaII, 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)}
+            {uvII, asyncContext.fResult->data(2), asyncContext.fResult->rowBytes(2)},
+            {},
     };
+    if (readAlpha) {
+        yuvPMs[3] = {yaII, asyncContext.fResult->data(3), asyncContext.fResult->rowBytes(3)};
+    }
     auto pixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, yuvPMs);
     SkASSERT(pixmaps.isValid());
     auto lazyYUVImage = sk_gpu_test::LazyYUVImage::Make(pixmaps);
@@ -181,6 +202,17 @@
     }
 }
 
+enum class ReadSource {
+    kImage,
+    kSurface,
+};
+
+enum class Type {
+    kRGBA,
+    kYUV,
+    kYUVA
+};
+
 // Draws a grid of rescales. The columns are none, low, and high filter quality. The rows are
 // rescale in src gamma and rescale in linear gamma.
 template <typename Src>
@@ -190,7 +222,7 @@
                                           skgpu::graphite::Recorder* recorder,
                                           const SkIRect& srcRect,
                                           SkISize newSize,
-                                          bool doYUV420,
+                                          Type type,
                                           SkString* errorMsg,
                                           int pad = 0) {
     if (canvas->imageInfo().colorType() == kUnknown_SkColorType) {
@@ -209,22 +241,27 @@
                 SkImage::RescaleMode::kRepeatedCubic}) {
             SkScopeExit cleanup;
             sk_sp<SkImage> result;
-            if (doYUV420) {
-                result = do_read_and_scale_yuv(src, direct, recorder,
-                                               yuvColorSpace, srcRect, newSize, gamma,
-                                               mode, &cleanup);
-                if (!result) {
-                    errorMsg->printf("YUV420 async call failed. Allowed for now.");
-                    return skiagm::DrawResult::kSkip;
-                }
-                int nextCS = static_cast<int>(yuvColorSpace + 1) % (kLastEnum_SkYUVColorSpace + 1);
-                yuvColorSpace = static_cast<SkYUVColorSpace>(nextCS);
-            } else {
-                result = do_read_and_scale(src, direct, recorder, srcRect, ii, gamma, mode);
-                if (!result) {
-                    errorMsg->printf("async read call failed.");
-                    return skiagm::DrawResult::kFail;
-                }
+            switch (type) {
+                case Type::kRGBA:
+                    result = do_read_and_scale(src, direct, recorder, srcRect, ii, gamma, mode);
+                    if (!result) {
+                        errorMsg->printf("async read call failed.");
+                        return skiagm::DrawResult::kFail;
+                    }
+                    break;
+                case Type::kYUV:
+                case Type::kYUVA:
+                    result = do_read_and_scale_yuv(src, direct, recorder, yuvColorSpace,
+                                                   /*readAlpha=*/type == Type::kYUVA, srcRect,
+                                                   newSize, gamma, mode, &cleanup);
+                    if (!result) {
+                        errorMsg->printf("YUV[A]420 async call failed. Allowed for now.");
+                        return skiagm::DrawResult::kSkip;
+                    }
+                    int nextCS =
+                            static_cast<int>(yuvColorSpace + 1) % (kLastEnum_SkYUVColorSpace + 1);
+                    yuvColorSpace = static_cast<SkYUVColorSpace>(nextCS);
+                    break;
             }
             canvas->drawImage(result, 0, 0);
             canvas->translate(newSize.width() + pad, 0);
@@ -240,8 +277,8 @@
                                                 const char* imageFile,
                                                 const SkIRect& srcRect,
                                                 SkISize newSize,
-                                                bool doSurface,
-                                                bool doYUV420,
+                                                ReadSource source,
+                                                Type type,
                                                 SkString* errorMsg) {
     auto image = GetResourceAsImage(imageFile);
     if (!image) {
@@ -260,97 +297,125 @@
     }
     auto recorder = canvas->recorder();
 
-    if (doSurface) {
-        // Turn the image into a surface in order to call the read and rescale API
-        auto surfInfo = image->imageInfo().makeDimensions(image->dimensions());
-        auto surface = canvas->makeSurface(surfInfo);
-        if (!surface && surfInfo.colorType() == kBGRA_8888_SkColorType) {
-            surfInfo = surfInfo.makeColorType(kRGBA_8888_SkColorType);
-            surface = canvas->makeSurface(surfInfo);
-        }
-        if (!surface) {
-            *errorMsg = "Could not create surface for image.";
-            // When testing abandoned GrContext we expect surface creation to fail.
-            if (canvas->recordingContext() && canvas->recordingContext()->abandoned()) {
-                return skiagm::DrawResult::kSkip;
-            }
-            return skiagm::DrawResult::kFail;
-        }
-        SkPaint paint;
-        paint.setBlendMode(SkBlendMode::kSrc);
-        surface->getCanvas()->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
-        return do_rescale_grid(canvas, surface.get(), dContext, recorder, srcRect, newSize,
-                               doYUV420, errorMsg);
+    switch (source) {
+        case ReadSource::kImage:
 #if defined(SK_GRAPHITE)
-    } else if (recorder) {
-        image = SkImages::TextureFromImage(recorder, image);
-        if (!image) {
-            *errorMsg = "Could not create image.";
-            return skiagm::DrawResult::kFail;
-        }
+            if (recorder) {
+                image = SkImages::TextureFromImage(recorder, image);
+                if (!image) {
+                    *errorMsg = "Could not create image.";
+                    return skiagm::DrawResult::kFail;
+                }
+            } else
 #endif
-    } else if (dContext) {
-        image = SkImages::TextureFromImage(dContext, image);
-        if (!image) {
-            *errorMsg = "Could not create image.";
-            // When testing abandoned GrContext we expect surface creation to fail.
-            if (canvas->recordingContext() && canvas->recordingContext()->abandoned()) {
-                return skiagm::DrawResult::kSkip;
+            if (dContext) {
+                image = SkImages::TextureFromImage(dContext, image);
+                if (!image) {
+                    *errorMsg = "Could not create image.";
+                    // When testing abandoned GrContext we expect surface creation to fail.
+                    if (canvas->recordingContext() && canvas->recordingContext()->abandoned()) {
+                        return skiagm::DrawResult::kSkip;
+                    }
+                    return skiagm::DrawResult::kFail;
+                }
             }
-            return skiagm::DrawResult::kFail;
-        }
+            return do_rescale_grid(canvas, image.get(), dContext, recorder, srcRect, newSize, type,
+                                   errorMsg);
+        case ReadSource::kSurface:
+            // Turn the image into a surface in order to call the read and rescale API
+            auto surfInfo = image->imageInfo().makeDimensions(image->dimensions());
+            auto surface = canvas->makeSurface(surfInfo);
+            if (!surface && surfInfo.colorType() == kBGRA_8888_SkColorType) {
+                surfInfo = surfInfo.makeColorType(kRGBA_8888_SkColorType);
+                surface = canvas->makeSurface(surfInfo);
+            }
+            if (!surface) {
+                *errorMsg = "Could not create surface for image.";
+                // When testing abandoned GrContext we expect surface creation to fail.
+                if (canvas->recordingContext() && canvas->recordingContext()->abandoned()) {
+                    return skiagm::DrawResult::kSkip;
+                }
+                return skiagm::DrawResult::kFail;
+            }
+            SkPaint paint;
+            paint.setBlendMode(SkBlendMode::kSrc);
+            surface->getCanvas()->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
+            return do_rescale_grid(canvas, surface.get(), dContext, recorder, srcRect, newSize,
+                                   type, errorMsg);
     }
-    return do_rescale_grid(canvas, image.get(), dContext, recorder, srcRect, newSize, doYUV420,
-                           errorMsg);
+    SkUNREACHABLE;
 }
 
-#define DEF_RESCALE_AND_READ_SURF_GM(IMAGE_FILE, TAG, SRC_RECT, W, H)                      \
+#define DEF_RESCALE_AND_READ_GM(IMAGE_FILE, TAG, SRC_RECT, W, H, SOURCE, TYPE)             \
     DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
         ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25);          \
-        return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, false,   \
-                                     errorMsg);                                            \
+        return do_rescale_image_grid(                                                      \
+                canvas, #IMAGE_FILE, SRC_RECT, {W, H}, SOURCE, TYPE, errorMsg);            \
     }
 
-#define DEF_RESCALE_AND_READ_YUV_SURF_GM(IMAGE_FILE, TAG, SRC_RECT, W, H)                          \
-    DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_yuv420_##TAG, canvas, errorMsg, 3 * W, 2 * H) {  \
-        ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25);                  \
-        return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, true, errorMsg); \
-    }
+DEF_RESCALE_AND_READ_GM(images/yellow_rose.webp,
+                        yuv420_rose,
+                        SkIRect::MakeXYWH(50, 5, 200, 150),
+                        410,
+                        376,
+                        ReadSource::kSurface,
+                        Type::kYUVA)
 
-#define DEF_RESCALE_AND_READ_IMG_GM(IMAGE_FILE, TAG, SRC_RECT, W, H)                       \
-    DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
-        ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25);          \
-        return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, false, false,  \
-                                     errorMsg);                                            \
-    }
+DEF_RESCALE_AND_READ_GM(images/yellow_rose.webp,
+                        yuv420_rose_down,
+                        SkIRect::MakeXYWH(50, 5, 200, 150),
+                        106,
+                        60,
+                        ReadSource::kImage,
+                        Type::kYUV)
 
-#define DEF_RESCALE_AND_READ_YUV_IMG_GM(IMAGE_FILE, TAG, SRC_RECT, W, H)                           \
-    DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_yuv420_##TAG, canvas, errorMsg, 3 * W, 2 * H) {  \
-        ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25);                  \
-        return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, true, errorMsg); \
-    }
+DEF_RESCALE_AND_READ_GM(images/yellow_rose.webp,
+                        rose,
+                        SkIRect::MakeXYWH(100, 20, 100, 100),
+                        410,
+                        410,
+                        ReadSource::kSurface,
+                        Type::kRGBA)
 
-DEF_RESCALE_AND_READ_YUV_SURF_GM(
-        images/yellow_rose.webp, rose, SkIRect::MakeXYWH(50, 5, 200, 150), 410, 376)
+DEF_RESCALE_AND_READ_GM(images/dog.jpg,
+                        dog_down,
+                        SkIRect::MakeXYWH(0, 10, 180, 150),
+                        45,
+                        45,
+                        ReadSource::kSurface,
+                        Type::kRGBA)
 
-DEF_RESCALE_AND_READ_YUV_IMG_GM(
-        images/yellow_rose.webp, rose_down, SkIRect::MakeXYWH(50, 5, 200, 150), 106, 60)
+DEF_RESCALE_AND_READ_GM(images/dog.jpg,
+                        dog_up,
+                        SkIRect::MakeWH(180, 180),
+                        800,
+                        400,
+                        ReadSource::kImage,
+                        Type::kRGBA)
 
-DEF_RESCALE_AND_READ_SURF_GM(
-        images/yellow_rose.webp, rose, SkIRect::MakeXYWH(100, 20, 100, 100), 410, 410)
+DEF_RESCALE_AND_READ_GM(images/text.png,
+                        text_down,
+                        SkIRect::MakeWH(637, 105),
+                        (int)(0.7 * 637),
+                        (int)(0.7 * 105),
+                        ReadSource::kImage,
+                        Type::kRGBA)
 
-DEF_RESCALE_AND_READ_SURF_GM(images/dog.jpg, dog_down, SkIRect::MakeXYWH(0, 10, 180, 150), 45, 45)
-DEF_RESCALE_AND_READ_IMG_GM(images/dog.jpg, dog_up, SkIRect::MakeWH(180, 180), 800, 400)
+DEF_RESCALE_AND_READ_GM(images/text.png,
+                        text_up,
+                        SkIRect::MakeWH(637, 105),
+                        (int)(1.2 * 637),
+                        (int)(1.2 * 105),
+                        ReadSource::kSurface,
+                        Type::kRGBA)
 
-DEF_RESCALE_AND_READ_IMG_GM(
-        images/text.png, text_down, SkIRect::MakeWH(637, 105), (int)(0.7 * 637), (int)(0.7 * 105))
-DEF_RESCALE_AND_READ_SURF_GM(
-        images/text.png, text_up, SkIRect::MakeWH(637, 105), (int)(1.2 * 637), (int)(1.2 * 105))
-DEF_RESCALE_AND_READ_IMG_GM(images/text.png,
-                            text_up_large,
-                            SkIRect::MakeXYWH(300, 0, 300, 105),
-                            (int)(2.4 * 300),
-                            (int)(2.4 * 105))
+DEF_RESCALE_AND_READ_GM(images/text.png,
+                        text_up_large,
+                        SkIRect::MakeXYWH(300, 0, 300, 105),
+                        (int)(2.4 * 300),
+                        (int)(2.4 * 105),
+                        ReadSource::kImage,
+                        Type::kRGBA)
 
 // Exercises non-scaling YUV420. Reads from the original canvas's surface in order to
 // exercise case where source surface is not a texture (in glbert config).
@@ -376,9 +441,10 @@
 
     skgpu::graphite::Recorder* recorder = canvas->recorder();
     SkScopeExit scopeExit;
-    auto yuvImage = do_read_and_scale_yuv(
-            surface, dContext, recorder, kRec601_SkYUVColorSpace, SkIRect::MakeWH(400, 300),
-            {400, 300}, SkImage::RescaleGamma::kSrc, SkImage::RescaleMode::kNearest, &scopeExit);
+    auto yuvImage = do_read_and_scale_yuv(surface, dContext, recorder, kRec601_SkYUVColorSpace,
+                                          /*readAlpha=*/false, SkIRect::MakeWH(400, 300),
+                                          {400, 300}, SkImage::RescaleGamma::kSrc,
+                                          SkImage::RescaleMode::kNearest, &scopeExit);
 
     canvas->clear(SK_ColorWHITE);
     canvas->drawImage(yuvImage.get(), 0, 0);
@@ -423,16 +489,16 @@
     canvas->translate(kPad, kPad);
     skiagm::DrawResult result;
     SkISize downSize = {static_cast<int>(kInner/2),  static_cast<int>(kInner / 2)};
-    result = do_rescale_grid(canvas, surface.get(), dContext, recorder, srcRect, downSize, false,
-                             errorMsg, kPad);
+    result = do_rescale_grid(canvas, surface.get(), dContext, recorder, srcRect, downSize,
+                             Type::kRGBA, errorMsg, kPad);
 
     if (result != skiagm::DrawResult::kOk) {
         return result;
     }
     canvas->translate(0, 4 * downSize.height());
     SkISize upSize = {static_cast<int>(kInner * 3.5), static_cast<int>(kInner * 4.6)};
-    result = do_rescale_grid(canvas, surface.get(), dContext, recorder, srcRect, upSize, false,
-                             errorMsg, kPad);
+    result = do_rescale_grid(canvas, surface.get(), dContext, recorder, srcRect, upSize,
+                             Type::kRGBA, errorMsg, kPad);
     if (result != skiagm::DrawResult::kOk) {
         return result;
     }
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 3045d5a..6c8d9a7 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -642,6 +642,20 @@
                                          ReadPixelsCallback callback,
                                          ReadPixelsContext context) const;
 
+    /**
+     * Identical to asyncRescaleAndReadPixelsYUV420 but a fourth plane is returned in the
+     * AsyncReadResult passed to 'callback'. The fourth plane contains the alpha chanel at the
+     * same full resolution as the Y plane.
+     */
+    void asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace,
+                                          sk_sp<SkColorSpace> dstColorSpace,
+                                          const SkIRect& srcRect,
+                                          const SkISize& dstSize,
+                                          RescaleGamma rescaleGamma,
+                                          RescaleMode rescaleMode,
+                                          ReadPixelsCallback callback,
+                                          ReadPixelsContext context) const;
+
     /** Copies SkImage to dst, scaling pixels to fit dst.width() and dst.height(), and
         converting pixels to match dst.colorType() and dst.alphaType(). Returns true if
         pixels are copied. Returns false if dst.addr() is nullptr, or dst.rowBytes() is
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 3c810fa..3a3da55 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -555,6 +555,20 @@
                                          ReadPixelsCallback callback,
                                          ReadPixelsContext context);
 
+    /**
+     * Identical to asyncRescaleAndReadPixelsYUV420 but a fourth plane is returned in the
+     * AsyncReadResult passed to 'callback'. The fourth plane contains the alpha chanel at the
+     * same full resolution as the Y plane.
+     */
+    void asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace,
+                                          sk_sp<SkColorSpace> dstColorSpace,
+                                          const SkIRect& srcRect,
+                                          const SkISize& dstSize,
+                                          RescaleGamma rescaleGamma,
+                                          RescaleMode rescaleMode,
+                                          ReadPixelsCallback callback,
+                                          ReadPixelsContext context);
+
     /** Copies SkRect of pixels from the src SkPixmap to the SkSurface.
 
         Source SkRect corners are (0, 0) and (src.width(), src.height()).
diff --git a/include/gpu/graphite/Context.h b/include/gpu/graphite/Context.h
index 25f51b7..ed5b39e 100644
--- a/include/gpu/graphite/Context.h
+++ b/include/gpu/graphite/Context.h
@@ -90,6 +90,26 @@
                                          SkImage::ReadPixelsCallback callback,
                                          SkImage::ReadPixelsContext context);
 
+    void asyncRescaleAndReadPixelsYUVA420(const SkImage*,
+                                          SkYUVColorSpace yuvColorSpace,
+                                          sk_sp<SkColorSpace> dstColorSpace,
+                                          const SkIRect& srcRect,
+                                          const SkISize& dstSize,
+                                          SkImage::RescaleGamma rescaleGamma,
+                                          SkImage::RescaleMode rescaleMode,
+                                          SkImage::ReadPixelsCallback callback,
+                                          SkImage::ReadPixelsContext context);
+
+    void asyncRescaleAndReadPixelsYUVA420(const SkSurface*,
+                                          SkYUVColorSpace yuvColorSpace,
+                                          sk_sp<SkColorSpace> dstColorSpace,
+                                          const SkIRect& srcRect,
+                                          const SkISize& dstSize,
+                                          SkImage::RescaleGamma rescaleGamma,
+                                          SkImage::RescaleMode rescaleMode,
+                                          SkImage::ReadPixelsCallback callback,
+                                          SkImage::ReadPixelsContext context);
+
     /**
      * Checks whether any asynchronous work is complete and if so calls related callbacks.
      */
@@ -142,6 +162,17 @@
     // require Context::Make() to return a nullptr.
     bool finishInitialization();
 
+    void asyncRescaleAndReadPixelsYUV420Impl(const SkImage*,
+                                             SkYUVColorSpace yuvColorSpace,
+                                             bool readAlpha,
+                                             sk_sp<SkColorSpace> dstColorSpace,
+                                             const SkIRect& srcRect,
+                                             const SkISize& dstSize,
+                                             SkImage::RescaleGamma rescaleGamma,
+                                             SkImage::RescaleMode rescaleMode,
+                                             SkImage::ReadPixelsCallback callback,
+                                             SkImage::ReadPixelsContext context);
+
     void asyncReadPixels(const TextureProxy* textureProxy,
                          const SkImageInfo& srcImageInfo,
                          const SkColorInfo& dstColorInfo,
@@ -152,6 +183,7 @@
     void asyncReadPixelsYUV420(Recorder*,
                                const SkImage*,
                                SkYUVColorSpace yuvColorSpace,
+                               bool readAlpha,
                                const SkIRect& srcRect,
                                SkImage::ReadPixelsCallback callback,
                                SkImage::ReadPixelsContext context);
diff --git a/relnotes/asyncyuva420.md b/relnotes/asyncyuva420.md
new file mode 100644
index 0000000..ff80cba
--- /dev/null
+++ b/relnotes/asyncyuva420.md
@@ -0,0 +1,4 @@
+New methods are added to `SkImage`, `SkSurface`, and `skgpu::graphite::context` named
+`asyncRescaleAndReadPixeksYUVA420`. These function identically to the existing
+`asyncRescaleAndReadPixelsYUV420` methods but return a fourth plane containing alpha at full
+resolution.
\ No newline at end of file
diff --git a/src/gpu/AsyncReadTypes.h b/src/gpu/AsyncReadTypes.h
index 8bf5732..87536be 100644
--- a/src/gpu/AsyncReadTypes.h
+++ b/src/gpu/AsyncReadTypes.h
@@ -216,7 +216,7 @@
         sk_sp<T> fMappedBuffer;
         size_t fRowBytes;
     };
-    skia_private::STArray<3, Plane> fPlanes;
+    skia_private::STArray<4, Plane> fPlanes;
     IDType fIntendedRecipient;
 };
 
diff --git a/src/gpu/ganesh/Device.cpp b/src/gpu/ganesh/Device.cpp
index dda0cdc..f44fa74 100644
--- a/src/gpu/ganesh/Device.cpp
+++ b/src/gpu/ganesh/Device.cpp
@@ -1321,6 +1321,7 @@
 }
 
 void Device::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                             bool readAlpha,
                                              sk_sp<SkColorSpace> dstColorSpace,
                                              const SkIRect& srcRect,
                                              SkISize dstSize,
@@ -1336,6 +1337,7 @@
     }
     sdc->asyncRescaleAndReadPixelsYUV420(dContext,
                                          yuvColorSpace,
+                                         readAlpha,
                                          std::move(dstColorSpace),
                                          srcRect,
                                          dstSize,
diff --git a/src/gpu/ganesh/Device.h b/src/gpu/ganesh/Device.h
index 07afd0a..af5eb2a 100644
--- a/src/gpu/ganesh/Device.h
+++ b/src/gpu/ganesh/Device.h
@@ -125,6 +125,7 @@
                                    ReadPixelsContext context);
 
     void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                         bool readAlpha,
                                          sk_sp<SkColorSpace> dstColorSpace,
                                          const SkIRect& srcRect,
                                          SkISize dstSize,
diff --git a/src/gpu/ganesh/SurfaceContext.cpp b/src/gpu/ganesh/SurfaceContext.cpp
index 325bc31..55e3b89 100644
--- a/src/gpu/ganesh/SurfaceContext.cpp
+++ b/src/gpu/ganesh/SurfaceContext.cpp
@@ -709,6 +709,7 @@
 
 void SurfaceContext::asyncRescaleAndReadPixelsYUV420(GrDirectContext* dContext,
                                                      SkYUVColorSpace yuvColorSpace,
+                                                     bool readAlpha,
                                                      sk_sp<SkColorSpace> dstColorSpace,
                                                      const SkIRect& srcRect,
                                                      SkISize dstSize,
@@ -783,14 +784,18 @@
         x = y = 0;
     }
 
-    auto yInfo = SkImageInfo::MakeA8(dstSize);
-    auto yFC = dContext->priv().makeSFCWithFallback(yInfo, SkBackingFit::kApprox);
+    auto yaInfo = SkImageInfo::MakeA8(dstSize);
+    auto yFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox);
+    std::unique_ptr<SurfaceFillContext> aFC;
+    if (readAlpha) {
+        aFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox);
+    }
 
-    auto uvInfo = yInfo.makeWH(yInfo.width()/2, yInfo.height()/2);
+    auto uvInfo = yaInfo.makeWH(yaInfo.width()/2, yaInfo.height()/2);
     auto uFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox);
     auto vFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox);
 
-    if (!yFC || !uFC || !vFC) {
+    if (!yFC || !uFC || !vFC || (readAlpha && !aFC)) {
         callback(callbackContext, nullptr);
         return;
     }
@@ -812,7 +817,7 @@
     }
     bool doSynchronousRead = !this->caps()->transferFromSurfaceToBufferSupport() ||
                              !offsetAlignment;
-    PixelTransferResult yTransfer, uTransfer, vTransfer;
+    PixelTransferResult yTransfer, aTransfer, uTransfer, vTransfer;
 
     // This matrix generates (r,g,b,a) = (0, 0, 0, y)
     float yM[20];
@@ -835,6 +840,24 @@
         }
     }
 
+    if (readAlpha) {
+        auto aFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
+        SkASSERT(baseM[15] == 0 &&
+                 baseM[16] == 0 &&
+                 baseM[17] == 0 &&
+                 baseM[18] == 1 &&
+                 baseM[19] == 0);
+        aFC->fillWithFP(std::move(aFP));
+        if (!doSynchronousRead) {
+            aTransfer = aFC->transferPixels(GrColorType::kAlpha_8,
+                                            SkIRect::MakeSize(aFC->dimensions()));
+            if (!aTransfer.fTransferBuffer) {
+                callback(callbackContext, nullptr);
+                return;
+            }
+        }
+    }
+
     texMatrix.preScale(2.f, 2.f);
     // This matrix generates (r,g,b,a) = (0, 0, 0, u)
     float uM[20];
@@ -885,12 +908,17 @@
     }
 
     if (doSynchronousRead) {
-        GrPixmap yPmp = GrPixmap::Allocate(yInfo);
+        GrPixmap yPmp = GrPixmap::Allocate(yaInfo);
         GrPixmap uPmp = GrPixmap::Allocate(uvInfo);
         GrPixmap vPmp = GrPixmap::Allocate(uvInfo);
+        GrPixmap aPmp;
+        if (readAlpha) {
+            aPmp = GrPixmap::Allocate(yaInfo);
+        }
         if (!yFC->readPixels(dContext, yPmp, {0, 0}) ||
             !uFC->readPixels(dContext, uPmp, {0, 0}) ||
-            !vFC->readPixels(dContext, vPmp, {0, 0})) {
+            !vFC->readPixels(dContext, vPmp, {0, 0}) ||
+            (readAlpha && !aFC->readPixels(dContext, aPmp, {0, 0}))) {
             callback(callbackContext, nullptr);
             return;
         }
@@ -898,6 +926,9 @@
         result->addCpuPlane(yPmp.pixelStorage(), yPmp.rowBytes());
         result->addCpuPlane(uPmp.pixelStorage(), uPmp.rowBytes());
         result->addCpuPlane(vPmp.pixelStorage(), vPmp.rowBytes());
+        if (readAlpha) {
+            result->addCpuPlane(aPmp.pixelStorage(), aPmp.rowBytes());
+        }
         callback(callbackContext, std::move(result));
         return;
     }
@@ -911,6 +942,7 @@
         PixelTransferResult fYTransfer;
         PixelTransferResult fUTransfer;
         PixelTransferResult fVTransfer;
+        PixelTransferResult fATransfer;
     };
     // Assumption is that the caller would like to flush. We could take a parameter or require an
     // explicit flush from the caller. We'd have to have a way to defer attaching the finish
@@ -922,27 +954,34 @@
                                             this->caps()->transferBufferRowBytesAlignment(),
                                             std::move(yTransfer),
                                             std::move(uTransfer),
-                                            std::move(vTransfer)};
+                                            std::move(vTransfer),
+                                            std::move(aTransfer)};
     auto finishCallback = [](GrGpuFinishedContext c) {
         const auto* context = reinterpret_cast<const FinishContext*>(c);
         auto manager = context->fMappedBufferManager;
         auto result = std::make_unique<AsyncReadResult>(manager->ownerID());
-        size_t rowBytes = SkToSizeT(context->fSize.width());
-        rowBytes = SkAlignTo(rowBytes, context->fBufferAlignment);
-        if (!result->addTransferResult(context->fYTransfer, context->fSize, rowBytes, manager)) {
+        size_t yaRowBytes = SkToSizeT(context->fSize.width());
+        yaRowBytes = SkAlignTo(yaRowBytes, context->fBufferAlignment);
+        if (!result->addTransferResult(context->fYTransfer, context->fSize, yaRowBytes, manager)) {
             (*context->fClientCallback)(context->fClientContext, nullptr);
             delete context;
             return;
         }
-        rowBytes = SkToSizeT(context->fSize.width()) / 2;
-        rowBytes = SkAlignTo(rowBytes, context->fBufferAlignment);
+        size_t uvRowBytes = SkToSizeT(context->fSize.width()) / 2;
+        uvRowBytes = SkAlignTo(uvRowBytes, context->fBufferAlignment);
         SkISize uvSize = {context->fSize.width() / 2, context->fSize.height() / 2};
-        if (!result->addTransferResult(context->fUTransfer, uvSize, rowBytes, manager)) {
+        if (!result->addTransferResult(context->fUTransfer, uvSize, uvRowBytes, manager)) {
             (*context->fClientCallback)(context->fClientContext, nullptr);
             delete context;
             return;
         }
-        if (!result->addTransferResult(context->fVTransfer, uvSize, rowBytes, manager)) {
+        if (!result->addTransferResult(context->fVTransfer, uvSize, uvRowBytes, manager)) {
+            (*context->fClientCallback)(context->fClientContext, nullptr);
+            delete context;
+            return;
+        }
+        if (context->fATransfer.fTransferBuffer &&
+            !result->addTransferResult(context->fATransfer, context->fSize, yaRowBytes, manager)) {
             (*context->fClientCallback)(context->fClientContext, nullptr);
             delete context;
             return;
diff --git a/src/gpu/ganesh/SurfaceContext.h b/src/gpu/ganesh/SurfaceContext.h
index c8e7f15..250b2c5 100644
--- a/src/gpu/ganesh/SurfaceContext.h
+++ b/src/gpu/ganesh/SurfaceContext.h
@@ -92,6 +92,7 @@
     // GPU implementation for SkImage:: and SkSurface::asyncRescaleAndReadPixelsYUV420.
     void asyncRescaleAndReadPixelsYUV420(GrDirectContext*,
                                          SkYUVColorSpace yuvColorSpace,
+                                         bool readAlpha,
                                          sk_sp<SkColorSpace> dstColorSpace,
                                          const SkIRect& srcRect,
                                          SkISize dstSize,
diff --git a/src/gpu/ganesh/image/SkImage_Ganesh.cpp b/src/gpu/ganesh/image/SkImage_Ganesh.cpp
index 5ca7973..d752d4c 100644
--- a/src/gpu/ganesh/image/SkImage_Ganesh.cpp
+++ b/src/gpu/ganesh/image/SkImage_Ganesh.cpp
@@ -353,6 +353,7 @@
 }
 
 void SkImage_Ganesh::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                       bool readAlpha,
                                                        sk_sp<SkColorSpace> dstColorSpace,
                                                        SkIRect srcRect,
                                                        SkISize dstSize,
@@ -373,6 +374,7 @@
     }
     ctx->asyncRescaleAndReadPixelsYUV420(dContext,
                                          yuvColorSpace,
+                                         readAlpha,
                                          std::move(dstColorSpace),
                                          srcRect,
                                          dstSize,
diff --git a/src/gpu/ganesh/image/SkImage_Ganesh.h b/src/gpu/ganesh/image/SkImage_Ganesh.h
index 0b693a8..3fb556c 100644
--- a/src/gpu/ganesh/image/SkImage_Ganesh.h
+++ b/src/gpu/ganesh/image/SkImage_Ganesh.h
@@ -87,6 +87,7 @@
                                      ReadPixelsContext) const override;
 
     void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
+                                           bool readAlpha,
                                            sk_sp<SkColorSpace>,
                                            SkIRect srcRect,
                                            SkISize dstSize,
diff --git a/src/gpu/ganesh/surface/SkSurface_Ganesh.cpp b/src/gpu/ganesh/surface/SkSurface_Ganesh.cpp
index 64492cf..150b977 100644
--- a/src/gpu/ganesh/surface/SkSurface_Ganesh.cpp
+++ b/src/gpu/ganesh/surface/SkSurface_Ganesh.cpp
@@ -194,6 +194,7 @@
 }
 
 void SkSurface_Ganesh::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                         bool readAlpha,
                                                          sk_sp<SkColorSpace> dstColorSpace,
                                                          SkIRect srcRect,
                                                          SkISize dstSize,
@@ -202,6 +203,7 @@
                                                          ReadPixelsCallback callback,
                                                          ReadPixelsContext context) {
     fDevice->asyncRescaleAndReadPixelsYUV420(yuvColorSpace,
+                                             readAlpha,
                                              std::move(dstColorSpace),
                                              srcRect,
                                              dstSize,
diff --git a/src/gpu/ganesh/surface/SkSurface_Ganesh.h b/src/gpu/ganesh/surface/SkSurface_Ganesh.h
index f2d8834..a4617b0 100644
--- a/src/gpu/ganesh/surface/SkSurface_Ganesh.h
+++ b/src/gpu/ganesh/surface/SkSurface_Ganesh.h
@@ -66,6 +66,7 @@
                                      ReadPixelsCallback callback,
                                      ReadPixelsContext context) override;
     void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                           bool readAlpha,
                                            sk_sp<SkColorSpace> dstColorSpace,
                                            SkIRect srcRect,
                                            SkISize dstSize,
diff --git a/src/gpu/graphite/Context.cpp b/src/gpu/graphite/Context.cpp
index 9853d3c..c51e781 100644
--- a/src/gpu/graphite/Context.cpp
+++ b/src/gpu/graphite/Context.cpp
@@ -328,6 +328,101 @@
                                               SkImage::RescaleMode rescaleMode,
                                               SkImage::ReadPixelsCallback callback,
                                               SkImage::ReadPixelsContext callbackContext) {
+    this->asyncRescaleAndReadPixelsYUV420Impl(image,
+                                              yuvColorSpace,
+                                              /*readAlpha=*/false,
+                                              dstColorSpace,
+                                              srcRect,
+                                              dstSize,
+                                              rescaleGamma,
+                                              rescaleMode,
+                                              callback,
+                                              callbackContext);
+}
+
+void Context::asyncRescaleAndReadPixelsYUV420(const SkSurface* surface,
+                                              SkYUVColorSpace yuvColorSpace,
+                                              sk_sp<SkColorSpace> dstColorSpace,
+                                              const SkIRect& srcRect,
+                                              const SkISize& dstSize,
+                                              SkImage::RescaleGamma rescaleGamma,
+                                              SkImage::RescaleMode rescaleMode,
+                                              SkImage::ReadPixelsCallback callback,
+                                              SkImage::ReadPixelsContext callbackContext) {
+    if (!static_cast<const SkSurface_Base*>(surface)->isGraphiteBacked()) {
+        callback(callbackContext, nullptr);
+        return;
+    }
+
+    sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface));
+    this->asyncRescaleAndReadPixelsYUV420(surfaceImage.get(),
+                                          yuvColorSpace,
+                                          dstColorSpace,
+                                          srcRect,
+                                          dstSize,
+                                          rescaleGamma,
+                                          rescaleMode,
+                                          callback,
+                                          callbackContext);
+}
+
+void Context::asyncRescaleAndReadPixelsYUVA420(const SkImage* image,
+                                               SkYUVColorSpace yuvColorSpace,
+                                               sk_sp<SkColorSpace> dstColorSpace,
+                                               const SkIRect& srcRect,
+                                               const SkISize& dstSize,
+                                               SkImage::RescaleGamma rescaleGamma,
+                                               SkImage::RescaleMode rescaleMode,
+                                               SkImage::ReadPixelsCallback callback,
+                                               SkImage::ReadPixelsContext callbackContext) {
+    this->asyncRescaleAndReadPixelsYUV420Impl(image,
+                                              yuvColorSpace,
+                                              /*readAlpha=*/true,
+                                              dstColorSpace,
+                                              srcRect,
+                                              dstSize,
+                                              rescaleGamma,
+                                              rescaleMode,
+                                              callback,
+                                              callbackContext);
+}
+
+void Context::asyncRescaleAndReadPixelsYUVA420(const SkSurface* surface,
+                                               SkYUVColorSpace yuvColorSpace,
+                                               sk_sp<SkColorSpace> dstColorSpace,
+                                               const SkIRect& srcRect,
+                                               const SkISize& dstSize,
+                                               SkImage::RescaleGamma rescaleGamma,
+                                               SkImage::RescaleMode rescaleMode,
+                                               SkImage::ReadPixelsCallback callback,
+                                               SkImage::ReadPixelsContext callbackContext) {
+    if (!static_cast<const SkSurface_Base*>(surface)->isGraphiteBacked()) {
+        callback(callbackContext, nullptr);
+        return;
+    }
+
+    sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface));
+    this->asyncRescaleAndReadPixelsYUVA420(surfaceImage.get(),
+                                           yuvColorSpace,
+                                           dstColorSpace,
+                                           srcRect,
+                                           dstSize,
+                                           rescaleGamma,
+                                           rescaleMode,
+                                           callback,
+                                           callbackContext);
+}
+
+void Context::asyncRescaleAndReadPixelsYUV420Impl(const SkImage* image,
+                                                  SkYUVColorSpace yuvColorSpace,
+                                                  bool readAlpha,
+                                                  sk_sp<SkColorSpace> dstColorSpace,
+                                                  const SkIRect& srcRect,
+                                                  const SkISize& dstSize,
+                                                  SkImage::RescaleGamma rescaleGamma,
+                                                  SkImage::RescaleMode rescaleMode,
+                                                  SkImage::ReadPixelsCallback callback,
+                                                  SkImage::ReadPixelsContext callbackContext) {
     if (!image || !as_IB(image)->isGraphiteBacked()) {
         callback(callbackContext, nullptr);
         return;
@@ -349,6 +444,7 @@
         return this->asyncReadPixelsYUV420(recorder.get(),
                                            image,
                                            yuvColorSpace,
+                                           readAlpha,
                                            srcRect,
                                            callback,
                                            callbackContext);
@@ -384,52 +480,32 @@
     this->asyncReadPixelsYUV420(recorder.get(),
                                 scaledImage.get(),
                                 yuvColorSpace,
+                                readAlpha,
                                 SkIRect::MakeSize(dstSize),
                                 callback,
                                 callbackContext);
 }
 
-void Context::asyncRescaleAndReadPixelsYUV420(const SkSurface* surface,
-                                              SkYUVColorSpace yuvColorSpace,
-                                              sk_sp<SkColorSpace> dstColorSpace,
-                                              const SkIRect& srcRect,
-                                              const SkISize& dstSize,
-                                              SkImage::RescaleGamma rescaleGamma,
-                                              SkImage::RescaleMode rescaleMode,
-                                              SkImage::ReadPixelsCallback callback,
-                                              SkImage::ReadPixelsContext callbackContext) {
-    if (!static_cast<const SkSurface_Base*>(surface)->isGraphiteBacked()) {
-        callback(callbackContext, nullptr);
-        return;
-    }
-
-    sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface));
-    this->asyncRescaleAndReadPixelsYUV420(surfaceImage.get(),
-                                          yuvColorSpace,
-                                          dstColorSpace,
-                                          srcRect,
-                                          dstSize,
-                                          rescaleGamma,
-                                          rescaleMode,
-                                          callback,
-                                          callbackContext);
-}
-
 void Context::asyncReadPixelsYUV420(Recorder* recorder,
                                     const SkImage* srcImage,
                                     SkYUVColorSpace yuvColorSpace,
+                                    bool readAlpha,
                                     const SkIRect& srcRect,
                                     SkImage::ReadPixelsCallback callback,
                                     SkImage::ReadPixelsContext callbackContext) {
-    // Make three Surfaces to draw the YUV planes into
-    SkImageInfo yInfo = SkImageInfo::MakeA8(srcRect.size());
-    sk_sp<SkSurface> ySurface = Surface::MakeGraphite(recorder, yInfo, Budgeted::kNo);
+    // Make three or four Surfaces to draw the YUV[A] planes into
+    SkImageInfo yaInfo = SkImageInfo::MakeA8(srcRect.size());
+    sk_sp<SkSurface> ySurface = Surface::MakeGraphite(recorder, yaInfo, Budgeted::kNo);
+    sk_sp<SkSurface> aSurface;
+    if (readAlpha) {
+        aSurface = Surface::MakeGraphite(recorder, yaInfo, Budgeted::kNo);
+    }
 
-    SkImageInfo uvInfo = yInfo.makeWH(yInfo.width()/2, yInfo.height()/2);
+    SkImageInfo uvInfo = yaInfo.makeWH(yaInfo.width()/2, yaInfo.height()/2);
     sk_sp<SkSurface> uSurface = Surface::MakeGraphite(recorder, uvInfo, Budgeted::kNo);
     sk_sp<SkSurface> vSurface = Surface::MakeGraphite(recorder, uvInfo, Budgeted::kNo);
 
-    if (!ySurface || !uSurface || !vSurface) {
+    if (!ySurface || !uSurface || !vSurface || (readAlpha && !aSurface)) {
         callback(callbackContext, nullptr);
         return;
     }
@@ -441,27 +517,32 @@
                         float rgb2yuv[20],
                         const SkMatrix& texMatrix) {
         // Render the plane defined by rgb2yuv from srcImage into dstSurface
-        SkCanvas* canvas = dstSurface->getCanvas();
+        SkPaint paint;
         const SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
         sk_sp<SkShader> imgShader = srcImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
                                                          sampling, texMatrix);
-        sk_sp<SkColorFilter> matrixFilter = SkColorFilters::Matrix(rgb2yuv);
-        SkPaint paint;
         paint.setShader(std::move(imgShader));
-        paint.setColorFilter(std::move(matrixFilter));
+
+        if (rgb2yuv) {
+            sk_sp<SkColorFilter> matrixFilter = SkColorFilters::Matrix(rgb2yuv);
+            paint.setColorFilter(std::move(matrixFilter));
+        }
+
+        SkCanvas* canvas = dstSurface->getCanvas();
         canvas->drawPaint(paint);
     };
 
-    auto copyPlane = [this](SkSurface* dstSurface,
-                            const SkImageInfo& surfaceInfo) {
+    auto copyPlane = [this](SkSurface* surface) {
         // Transfer result from dstSurface
-        auto graphiteSurface = reinterpret_cast<const skgpu::graphite::Surface*>(dstSurface);
+        auto graphiteSurface = reinterpret_cast<const skgpu::graphite::Surface*>(surface);
         TextureProxyView proxyView = graphiteSurface->readSurfaceView();
 
+        auto srcImageInfo = surface->imageInfo();
+        auto dstColorInfo = srcImageInfo.colorInfo().makeColorType(kAlpha_8_SkColorType);
         return this->transferPixels(proxyView.proxy(),
-                                    surfaceInfo,
-                                    surfaceInfo.colorInfo().makeColorType(kAlpha_8_SkColorType),
-                                    SkIRect::MakeWH(dstSurface->width(), dstSurface->height()));
+                                    srcImageInfo,
+                                    dstColorInfo,
+                                    SkIRect::MakeWH(surface->width(), surface->height()));
     };
 
     float baseM[20];
@@ -473,6 +554,15 @@
     std::fill_n(yM, 15, 0.f);
     std::copy_n(baseM + 0, 5, yM + 15);
     drawPlane(ySurface.get(), srcImage, yM, texMatrix);
+    if (readAlpha) {
+        // No matrix, straight copy of alpha channel
+        SkASSERT(baseM[15] == 0 &&
+                 baseM[16] == 0 &&
+                 baseM[17] == 0 &&
+                 baseM[18] == 1 &&
+                 baseM[19] == 0);
+        drawPlane(aSurface.get(), srcImage, nullptr, texMatrix);
+    }
 
     texMatrix.preScale(0.5f, 0.5f);
     // This matrix generates (r,g,b,a) = (0, 0, 0, u)
@@ -501,22 +591,29 @@
     }
 
     // Now set up transfers
-    PixelTransferResult yTransfer, uTransfer, vTransfer;
-    yTransfer = copyPlane(ySurface.get(), yInfo);
+    PixelTransferResult yTransfer, uTransfer, vTransfer, aTransfer;
+    yTransfer = copyPlane(ySurface.get());
     if (!yTransfer.fTransferBuffer) {
         callback(callbackContext, nullptr);
         return;
     }
-    uTransfer = copyPlane(uSurface.get(), uvInfo);
+    uTransfer = copyPlane(uSurface.get());
     if (!uTransfer.fTransferBuffer) {
         callback(callbackContext, nullptr);
         return;
     }
-    vTransfer = copyPlane(vSurface.get(), uvInfo);
+    vTransfer = copyPlane(vSurface.get());
     if (!vTransfer.fTransferBuffer) {
         callback(callbackContext, nullptr);
         return;
     }
+    if (readAlpha) {
+        aTransfer = copyPlane(aSurface.get());
+        if (!aTransfer.fTransferBuffer) {
+            callback(callbackContext, nullptr);
+            return;
+        }
+    }
 
     // Set up FinishContext and add transfer commands to queue
     using AsyncReadResult = skgpu::TAsyncReadResult<Buffer, ContextID, PixelTransferResult>;
@@ -528,6 +625,7 @@
         PixelTransferResult fYTransfer;
         PixelTransferResult fUTransfer;
         PixelTransferResult fVTransfer;
+        PixelTransferResult fATransfer;
     };
     auto* finishContext = new FinishContext{callback,
                                             callbackContext,
@@ -535,7 +633,8 @@
                                             fMappedBufferManager.get(),
                                             std::move(yTransfer),
                                             std::move(uTransfer),
-                                            std::move(vTransfer)};
+                                            std::move(vTransfer),
+                                            std::move(aTransfer)};
     GpuFinishedProc finishCallback = [](GpuFinishedContext c, CallbackResult status) {
         const auto* context = reinterpret_cast<const FinishContext*>(c);
         if (status == CallbackResult::kSuccess) {
@@ -554,6 +653,11 @@
                                                      context->fVTransfer.fRowBytes, manager)) {
                 result.reset();
             }
+            if (result && context->fATransfer.fTransferBuffer &&
+                !result->addTransferResult(context->fATransfer, context->fSize,
+                                           context->fATransfer.fRowBytes, manager)) {
+                result.reset();
+            }
             (*context->fClientCallback)(context->fClientContext, std::move(result));
         } else {
             (*context->fClientCallback)(context->fClientContext, nullptr);
diff --git a/src/gpu/graphite/Image_Base_Graphite.cpp b/src/gpu/graphite/Image_Base_Graphite.cpp
index a95bb3b..b12f532 100644
--- a/src/gpu/graphite/Image_Base_Graphite.cpp
+++ b/src/gpu/graphite/Image_Base_Graphite.cpp
@@ -36,6 +36,7 @@
 }
 
 void Image_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                   bool readAlpha,
                                                    sk_sp<SkColorSpace> dstColorSpace,
                                                    const SkIRect srcRect,
                                                    const SkISize dstSize,
diff --git a/src/gpu/graphite/Image_Base_Graphite.h b/src/gpu/graphite/Image_Base_Graphite.h
index f8844a8..7543cbf 100644
--- a/src/gpu/graphite/Image_Base_Graphite.h
+++ b/src/gpu/graphite/Image_Base_Graphite.h
@@ -53,6 +53,7 @@
                                      ReadPixelsContext) const override;
 
     void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
+                                           bool readAlpha,
                                            sk_sp<SkColorSpace>,
                                            SkIRect srcRect,
                                            SkISize dstSize,
diff --git a/src/gpu/graphite/Surface_Graphite.cpp b/src/gpu/graphite/Surface_Graphite.cpp
index 94e7a7e..5ed79e3 100644
--- a/src/gpu/graphite/Surface_Graphite.cpp
+++ b/src/gpu/graphite/Surface_Graphite.cpp
@@ -103,6 +103,7 @@
 }
 
 void Surface::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                bool readAlpha,
                                                 sk_sp<SkColorSpace> dstColorSpace,
                                                 SkIRect srcRect,
                                                 SkISize dstSize,
diff --git a/src/gpu/graphite/Surface_Graphite.h b/src/gpu/graphite/Surface_Graphite.h
index 6271dfa..aa5b21c 100644
--- a/src/gpu/graphite/Surface_Graphite.h
+++ b/src/gpu/graphite/Surface_Graphite.h
@@ -48,6 +48,7 @@
                                      ReadPixelsCallback callback,
                                      ReadPixelsContext context) override;
     void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                           bool readAlpha,
                                            sk_sp<SkColorSpace> dstColorSpace,
                                            SkIRect srcRect,
                                            SkISize dstSize,
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 47fac2f..c89c43c 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -87,6 +87,31 @@
         return;
     }
     as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
+                                                   /*readAlpha=*/false,
+                                                   std::move(dstColorSpace),
+                                                   srcRect,
+                                                   dstSize,
+                                                   rescaleGamma,
+                                                   rescaleMode,
+                                                   callback,
+                                                   context);
+}
+
+void SkImage::asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace,
+                                               sk_sp<SkColorSpace> dstColorSpace,
+                                               const SkIRect& srcRect,
+                                               const SkISize& dstSize,
+                                               RescaleGamma rescaleGamma,
+                                               RescaleMode rescaleMode,
+                                               ReadPixelsCallback callback,
+                                               ReadPixelsContext context) const {
+    if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
+        (dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
+        callback(context, nullptr);
+        return;
+    }
+    as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
+                                                   /*readAlpha=*/true,
                                                    std::move(dstColorSpace),
                                                    srcRect,
                                                    dstSize,
diff --git a/src/image/SkImage_Base.cpp b/src/image/SkImage_Base.cpp
index 0681fba..5a2c3f8 100644
--- a/src/image/SkImage_Base.cpp
+++ b/src/image/SkImage_Base.cpp
@@ -112,6 +112,7 @@
 }
 
 void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
+                                                     bool readAlpha,
                                                      sk_sp<SkColorSpace> dstColorSpace,
                                                      SkIRect srcRect,
                                                      SkISize dstSize,
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 7d4aba7..522f2e4 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -98,6 +98,7 @@
      * Default implementation does a rescale/read/yuv conversion and then calls the callback.
      */
     virtual void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
+                                                   bool readAlpha,
                                                    sk_sp<SkColorSpace> dstColorSpace,
                                                    SkIRect srcRect,
                                                    SkISize dstSize,
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 9ece09a..743cc05 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -156,6 +156,31 @@
         return;
     }
     asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
+                                                  /*readAlpha=*/false,
+                                                  std::move(dstColorSpace),
+                                                  srcRect,
+                                                  dstSize,
+                                                  rescaleGamma,
+                                                  rescaleMode,
+                                                  callback,
+                                                  context);
+}
+
+void SkSurface::asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace,
+                                                 sk_sp<SkColorSpace> dstColorSpace,
+                                                 const SkIRect& srcRect,
+                                                 const SkISize& dstSize,
+                                                 RescaleGamma rescaleGamma,
+                                                 RescaleMode rescaleMode,
+                                                 ReadPixelsCallback callback,
+                                                 ReadPixelsContext context) {
+    if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
+        (dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
+        callback(context, nullptr);
+        return;
+    }
+    asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
+                                                  /*readAlpha=*/true,
                                                   std::move(dstColorSpace),
                                                   srcRect,
                                                   dstSize,
diff --git a/src/image/SkSurface_Base.cpp b/src/image/SkSurface_Base.cpp
index d2d4943..67826be 100644
--- a/src/image/SkSurface_Base.cpp
+++ b/src/image/SkSurface_Base.cpp
@@ -80,8 +80,8 @@
 }
 
 void SkSurface_Base::onAsyncRescaleAndReadPixelsYUV420(
-        SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, SkIRect srcRect,
-        SkISize dstSize, RescaleGamma rescaleGamma, RescaleMode,
+        SkYUVColorSpace yuvColorSpace, bool readAlpha, sk_sp<SkColorSpace> dstColorSpace,
+        SkIRect srcRect, SkISize dstSize, RescaleGamma rescaleGamma, RescaleMode,
         ReadPixelsCallback callback, ReadPixelsContext context) {
     // TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
     // call client's callback.
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index 51bd4d0..9375518 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -108,6 +108,7 @@
      * Default implementation does a rescale/read/yuv conversion and then calls the callback.
      */
     virtual void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
+                                                   bool readAlpha,
                                                    sk_sp<SkColorSpace> dstColorSpace,
                                                    SkIRect srcRect,
                                                    SkISize dstSize,
diff --git a/tests/ReadWritePixelsGpuTest.cpp b/tests/ReadWritePixelsGpuTest.cpp
index b8ac093..4de710c 100644
--- a/tests/ReadWritePixelsGpuTest.cpp
+++ b/tests/ReadWritePixelsGpuTest.cpp
@@ -782,7 +782,12 @@
                  sequence == ShutdownSequence::kAbandon_DestroyContext_FreeResult)) {
                 continue;
             }
-            for (bool yuv : {false, true}) {
+            enum class ReadType {
+                kRGBA,
+                kYUV,
+                kYUVA
+            };
+            for (ReadType readType : {ReadType::kRGBA, ReadType::kYUV, ReadType::kYUVA}) {
                 sk_gpu_test::GrContextFactory factory(options);
                 auto direct = factory.get(type);
                 if (!direct) {
@@ -798,23 +803,40 @@
                     continue;
                 }
                 AsyncContext cbContext;
-                if (yuv) {
-                    surf->asyncRescaleAndReadPixelsYUV420(
-                            kIdentity_SkYUVColorSpace, SkColorSpace::MakeSRGB(), ii.bounds(),
-                            ii.dimensions(), SkImage::RescaleGamma::kSrc,
-                            SkImage::RescaleMode::kNearest, &async_callback, &cbContext);
-                } else {
-                    surf->asyncRescaleAndReadPixels(ii, ii.bounds(), SkImage::RescaleGamma::kSrc,
-                                                    SkImage::RescaleMode::kNearest, &async_callback,
-                                                    &cbContext);
+                switch (readType) {
+                    case ReadType::kRGBA:
+                        surf->asyncRescaleAndReadPixels(ii, ii.bounds(),
+                                                        SkImage::RescaleGamma::kSrc,
+                                                        SkImage::RescaleMode::kNearest,
+                                                        &async_callback, &cbContext);
+                        break;
+                    case ReadType::kYUV:
+                        surf->asyncRescaleAndReadPixelsYUV420(
+                                kIdentity_SkYUVColorSpace, SkColorSpace::MakeSRGB(), ii.bounds(),
+                                ii.dimensions(), SkImage::RescaleGamma::kSrc,
+                                SkImage::RescaleMode::kNearest, &async_callback, &cbContext);
+                        break;
+                    case ReadType::kYUVA:
+                        surf->asyncRescaleAndReadPixelsYUVA420(
+                                kIdentity_SkYUVColorSpace, SkColorSpace::MakeSRGB(), ii.bounds(),
+                                ii.dimensions(), SkImage::RescaleGamma::kSrc,
+                                SkImage::RescaleMode::kNearest, &async_callback, &cbContext);
+                        break;
                 }
+
                 direct->submit();
                 while (!cbContext.fCalled) {
                     direct->checkAsyncWorkCompletion();
                 }
                 if (!cbContext.fResult) {
-                    ERRORF(reporter, "Callback failed on %s. is YUV: %d",
-                           sk_gpu_test::GrContextFactory::ContextTypeName(type), yuv);
+                    const char* readTypeStr;
+                    switch (readType) {
+                        case ReadType::kRGBA: readTypeStr = "rgba"; break;
+                        case ReadType::kYUV:  readTypeStr = "yuv";  break;
+                        case ReadType::kYUVA: readTypeStr = "yuva"; break;
+                    }
+                    ERRORF(reporter, "Callback failed on %s. read type is: %s",
+                           sk_gpu_test::GrContextFactory::ContextTypeName(type), readTypeStr);
                     continue;
                 }
                 // For vulkan we need to release all refs to the GrDirectContext before trying to