Reland "SkSurface asynchronous read APIs allow client to extend pixel lifetime"

This is a reland of ce240cc6fd8ec95bd051c7df2173dad2ae8f6ad6

Original change's description:
> SkSurface asynchronous read APIs allow client to extend pixel lifetime
> 
> Previously the pixel data passed to the client was only valid during
> the client's callback. This meant if the client wanted to defer
> processing of the data a copy was necessary.
> 
> Now we pass an object to the callback and the pixel lifetime is tied
> to the lifetime of that object.
> 
> The object may be holding a GPU transfer buffer mapped. We don't assume
> that the object will be released on the direct GrContext thread. So
> when the object is destroyed it posts a message to a new type,
> GrClientMappedBufferManager, hanging off the direct context. The direct
> context will periodically check for messages and unmap and then unref
> buffers so that they can be reused. Currently this is done in
> GrContext::performDeferredCleanup() and GrDrawingManager::flush().
> 
> The old API is kept around for backwards compatibility but it is
> reimplemented as a bridge on top of the new mechanism.
> 
> Also a utility function to SkImageInfo is added to directly make a new
> info with a specified dimensions rather than passing the width and
> height separately to makeWH().
> 
> Bug: chromium:973403
> Bug: skia:8962
> 
> Change-Id: Id5cf04235376170142a48e90d3ecd13fd021a2a6
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/245457
> Reviewed-by: Brian Osman <brianosman@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

Bug: chromium:973403, skia:8962
Change-Id: I5cecd36276c8b6dc942cf549c7095db2df88530c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/245678
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/android/SkAnimatedImage.cpp b/src/android/SkAnimatedImage.cpp
index e15a862..6db6afd 100644
--- a/src/android/SkAnimatedImage.cpp
+++ b/src/android/SkAnimatedImage.cpp
@@ -23,7 +23,7 @@
     if (!codec) {
         return nullptr;
     }
-    auto info = codec->getInfo().makeWH(scaledSize.width(), scaledSize.height());
+    auto info = codec->getInfo().makeDimensions(scaledSize);
     return Make(std::move(codec), info, cropRect, std::move(postProcess));
 }
 
@@ -40,7 +40,7 @@
             || scaledSize.height() >= decodeInfo.height()) {
         // Only libwebp can decode to arbitrary smaller sizes.
         auto dims = codec->getInfo().dimensions();
-        decodeInfo = decodeInfo.makeWH(dims.width(), dims.height());
+        decodeInfo = decodeInfo.makeDimensions(dims);
     }
 
     auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 0fd776f..cc5b9bf 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -246,7 +246,7 @@
         }
     }
 
-    const SkImageInfo info = dstInfo.makeWH(prevRect.width(), prevRect.height());
+    const SkImageInfo info = dstInfo.makeDimensions(prevRect.size());
     const size_t bpp = dstInfo.bytesPerPixel();
     const size_t offset = prevRect.x() * bpp + prevRect.y() * rowBytes;
     void* eraseDst = SkTAddOffset<void>(pixels, offset);
diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp
index 8e1cf07..c0c8cf0 100644
--- a/src/codec/SkSampledCodec.cpp
+++ b/src/codec/SkSampledCodec.cpp
@@ -101,7 +101,7 @@
     int scaledSubsetWidth = info.width();
     int scaledSubsetHeight = info.height();
 
-    const SkImageInfo scaledInfo = info.makeWH(scaledSize.width(), scaledSize.height());
+    const SkImageInfo scaledInfo = info.makeDimensions(scaledSize);
 
     {
         // Although startScanlineDecode expects the bottom and top to match the
@@ -211,7 +211,7 @@
     const int startY = samplingOffsetY + subsetY;
     const int dstHeight = info.height();
 
-    const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height());
+    const SkImageInfo nativeInfo = info.makeDimensions(nativeSize);
 
     {
         // Although startScanlineDecode expects the bottom and top to match the
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 83ecc0c..f25991d 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -451,7 +451,7 @@
     SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
 
     SkBitmap dst;
-    dst.setInfo(this->info().makeWH(r.width(), r.height()), this->rowBytes());
+    dst.setInfo(this->info().makeDimensions(r.size()), this->rowBytes());
     dst.setIsVolatile(this->isVolatile());
 
     if (fPixelRef) {
@@ -492,7 +492,7 @@
     }
 
     void* dstPixels = this->getAddr(rec.fX, rec.fY);
-    const SkImageInfo dstInfo = this->info().makeWH(rec.fInfo.width(), rec.fInfo.height());
+    const SkImageInfo dstInfo = this->info().makeDimensions(rec.fInfo.dimensions());
     SkConvertPixels(dstInfo, dstPixels, this->rowBytes(), rec.fInfo, rec.fPixels, rec.fRowBytes);
     this->notifyPixelsChanged();
     return true;
diff --git a/src/core/SkImageInfo.cpp b/src/core/SkImageInfo.cpp
index f033bb1..e6e12bc 100644
--- a/src/core/SkImageInfo.cpp
+++ b/src/core/SkImageInfo.cpp
@@ -149,7 +149,7 @@
     // we negate and add them so UBSAN (pointer-overflow) doesn't get confused.
     fPixels = ((char*)fPixels + -y*fRowBytes + -x*fInfo.bytesPerPixel());
     // the intersect may have shrunk info's logical size
-    fInfo = fInfo.makeWH(srcR.width(), srcR.height());
+    fInfo = fInfo.makeDimensions(srcR.size());
     fX = srcR.x();
     fY = srcR.y();
 
@@ -186,7 +186,7 @@
     // we negate and add them so UBSAN (pointer-overflow) doesn't get confused.
     fPixels = ((const char*)fPixels + -y*fRowBytes + -x*fInfo.bytesPerPixel());
     // the intersect may have shrunk info's logical size
-    fInfo = fInfo.makeWH(dstR.width(), dstR.height());
+    fInfo = fInfo.makeDimensions(dstR.size());
     fX = dstR.x();
     fY = dstR.y();
 
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 3da7c1f..c8a09ff 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -76,7 +76,7 @@
         const size_t bpp = fInfo.bytesPerPixel();
         pixels = (const uint8_t*)fPixels + r.fTop * fRowBytes + r.fLeft * bpp;
     }
-    result->reset(fInfo.makeWH(r.width(), r.height()), pixels, fRowBytes);
+    result->reset(fInfo.makeDimensions(r.size()), pixels, fRowBytes);
     return true;
 }
 
@@ -163,7 +163,7 @@
     }
 
     const void* srcPixels = this->addr(rec.fX, rec.fY);
-    const SkImageInfo srcInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
+    const SkImageInfo srcInfo = fInfo.makeDimensions(rec.fInfo.dimensions());
     SkConvertPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, srcInfo, srcPixels, this->rowBytes());
     return true;
 }
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 109741a..bdd5874 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -346,7 +346,7 @@
     }
 
     SkBitmap tmp;
-    SkImageInfo info = bm.info().makeWH(subset.width(), subset.height());
+    SkImageInfo info = bm.info().makeDimensions(subset.size());
     // As in MakeFromRaster, must force src to N32 for ImageFilters
     if (!valid_for_imagefilters(bm.info())) {
         info = info.makeColorType(kN32_SkColorType);
diff --git a/src/effects/imagefilters/SkArithmeticImageFilter.cpp b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
index 5ec1909..1da9be1 100644
--- a/src/effects/imagefilters/SkArithmeticImageFilter.cpp
+++ b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
@@ -193,10 +193,10 @@
     if (!sect.intersect(dstR, srcR)) {
         return false;
     }
-    *dst = SkPixmap(dst->info().makeWH(sect.width(), sect.height()),
+    *dst = SkPixmap(dst->info().makeDimensions(sect.size()),
                     dst->addr(sect.fLeft, sect.fTop),
                     dst->rowBytes());
-    *src = SkPixmap(src->info().makeWH(sect.width(), sect.height()),
+    *src = SkPixmap(src->info().makeDimensions(sect.size()),
                     src->addr(SkTMax(0, -srcDx), SkTMax(0, -srcDy)),
                     src->rowBytes());
     return true;
diff --git a/src/gpu/GrClientMappedBufferManager.cpp b/src/gpu/GrClientMappedBufferManager.cpp
new file mode 100644
index 0000000..1d4d922
--- /dev/null
+++ b/src/gpu/GrClientMappedBufferManager.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/GrClientMappedBufferManager.h"
+
+#include <algorithm>
+
+GrClientMappedBufferManager::~GrClientMappedBufferManager() {
+    this->process();
+    if (!fAbandoned) {
+        // If we're going down before we got the messages we go ahead and unmap all the buffers.
+        // It's up to the client to ensure that they aren't being accessed on another thread while
+        // this is happening (or afterwards on any thread).
+        for (auto& b : fClientHeldBuffers) {
+            b->unmap();
+        }
+    }
+}
+
+void GrClientMappedBufferManager::insert(sk_sp<GrGpuBuffer> b) {
+    SkDEBUGCODE(auto end = fClientHeldBuffers.end());
+    SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) == end);
+    fClientHeldBuffers.emplace_front(std::move(b));
+}
+
+void GrClientMappedBufferManager::process() {
+    SkSTArray<4, BufferFinishedMessage> messages;
+    fFinishedBufferInbox.poll(&messages);
+    if (!fAbandoned) {
+        for (auto& m : messages) {
+            this->remove(m.fBuffer);
+            m.fBuffer->unmap();
+        }
+    }
+}
+
+void GrClientMappedBufferManager::abandon() {
+    fAbandoned = true;
+    fClientHeldBuffers.clear();
+}
+
+void GrClientMappedBufferManager::remove(const sk_sp<GrGpuBuffer>& b) {
+    // There is no convenient remove only the first element that equals a value functionality in
+    // std::forward_list.
+    auto prev = fClientHeldBuffers.before_begin();
+    auto end = fClientHeldBuffers.end();
+    SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) != end);
+    for (auto cur = fClientHeldBuffers.begin(); cur != end; prev = cur++) {
+        if (*cur == b) {
+            fClientHeldBuffers.erase_after(prev);
+            break;
+        }
+    }
+    SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) == end);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+DECLARE_SKMESSAGEBUS_MESSAGE(GrClientMappedBufferManager::BufferFinishedMessage)
+
+bool SkShouldPostMessageToBus(const GrClientMappedBufferManager::BufferFinishedMessage& m,
+                              uint32_t msgBusUniqueID) {
+    return m.fInboxID == msgBusUniqueID;
+}
diff --git a/src/gpu/GrClientMappedBufferManager.h b/src/gpu/GrClientMappedBufferManager.h
new file mode 100644
index 0000000..fa3243b
--- /dev/null
+++ b/src/gpu/GrClientMappedBufferManager.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrClientMappedBufferManager_DEFINED
+#define GrClientMappedBufferManager_DEFINED
+
+#include "include/private/SkTArray.h"
+#include "src/core/SkMessageBus.h"
+#include "src/gpu/GrGpuBuffer.h"
+#include <forward_list>
+
+/**
+ * We sometimes hand clients objects that contain mapped GrGpuBuffers. The client may consume
+ * the mapped buffer on another thread. This object manages receiving messages that buffers are
+ * ready to be unmapped (on the direct GrContext's thread). It also handles cleaning up mapped
+ * buffers if the GrContext is destroyed before the client has finished with the buffer.
+ *
+ * Buffers are first registered using insert() before being passed the client. process() should be
+ * called periodically on the direct GrContext thread to poll for messages and process them.
+ */
+class GrClientMappedBufferManager final {
+public:
+    /**
+     * The message type that internal users of this should post to unmap the buffer.
+     * Set fInboxID to inboxID(). fBuffer must have been previously passed to insert().
+     */
+    struct BufferFinishedMessage {
+        sk_sp<GrGpuBuffer> fBuffer;
+        uint32_t fInboxID;
+    };
+    using BufferFinishedMessageBus = SkMessageBus<BufferFinishedMessage>;
+
+    GrClientMappedBufferManager() = default;
+    GrClientMappedBufferManager(const GrClientMappedBufferManager&) = delete;
+    GrClientMappedBufferManager(GrClientMappedBufferManager&&) = delete;
+
+    ~GrClientMappedBufferManager();
+
+    GrClientMappedBufferManager& operator=(const GrClientMappedBufferManager&) = delete;
+    GrClientMappedBufferManager& operator=(GrClientMappedBufferManager&&) = delete;
+
+    /** Initialize BufferFinishedMessage::fInboxID to this value. */
+    uint32_t inboxID() const { return fFinishedBufferInbox.uniqueID(); }
+
+    /**
+     * Let the manager know to expect a message with buffer 'b'. It's illegal for a buffer to be
+     * inserted again before it is unmapped by process().
+     */
+    void insert(sk_sp<GrGpuBuffer> b);
+
+    /** Poll for messages and unmap any incoming buffers. */
+    void process();
+
+    /** Notifies the manager that the context has been abandoned. No more unmaps() will occur.*/
+    void abandon();
+
+private:
+    BufferFinishedMessageBus::Inbox fFinishedBufferInbox;
+    std::forward_list<sk_sp<GrGpuBuffer>> fClientHeldBuffers;
+    bool fAbandoned = false;
+
+    void remove(const sk_sp<GrGpuBuffer>& b);
+};
+
+bool SkShouldPostMessageToBus(const GrClientMappedBufferManager::BufferFinishedMessage&,
+                              uint32_t msgBusUniqueID);
+
+#endif
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index a41922d..7319ba5 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -5,13 +5,15 @@
  * found in the LICENSE file.
  */
 
+#include "include/gpu/GrContext.h"
+
 #include "include/core/SkTraceMemoryDump.h"
 #include "include/gpu/GrBackendSemaphore.h"
-#include "include/gpu/GrContext.h"
 #include "include/private/SkDeferredDisplayList.h"
 #include "include/private/SkImageInfoPriv.h"
 #include "src/core/SkMakeUnique.h"
 #include "src/core/SkTaskGroup.h"
+#include "src/gpu/GrClientMappedBufferManager.h"
 #include "src/gpu/GrDrawingManager.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrMemoryPool.h"
@@ -31,7 +33,6 @@
 #include "src/gpu/text/GrTextContext.h"
 #include "src/image/SkSurface_Gpu.h"
 #include <atomic>
-#include <unordered_map>
 
 #define ASSERT_OWNED_PROXY(P) \
     SkASSERT(!(P) || !((P)->peekTexture()) || (P)->peekTexture()->getContext() == this)
@@ -97,6 +98,8 @@
         fShaderErrorHandler = GrShaderUtils::DefaultShaderErrorHandler();
     }
 
+    fMappedBufferManager = skstd::make_unique<GrClientMappedBufferManager>();
+
     return true;
 }
 
@@ -113,6 +116,8 @@
 
     INHERITED::abandonContext();
 
+    fMappedBufferManager->abandon();
+
     fResourceProvider->abandon();
 
     // Need to cleanup the drawing manager first so all the render targets
@@ -124,6 +129,8 @@
     fResourceCache->abandonAll();
 
     fGpu->disconnect(GrGpu::DisconnectType::kAbandon);
+
+    fMappedBufferManager.reset();
 }
 
 void GrContext::releaseResourcesAndAbandonContext() {
@@ -133,6 +140,8 @@
 
     INHERITED::abandonContext();
 
+    fMappedBufferManager.reset();
+
     fResourceProvider->abandon();
 
     // Need to cleanup the drawing manager first so all the render targets
@@ -184,6 +193,11 @@
 
     ASSERT_SINGLE_OWNER
 
+    if (this->abandoned()) {
+        return;
+    }
+
+    fMappedBufferManager->process();
     auto purgeTime = GrStdSteadyClock::now() - msNotUsed;
 
     fResourceCache->purgeAsNeeded();
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 399ac57..64522b0 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -228,6 +228,10 @@
         return fContext->fShaderErrorHandler;
     }
 
+    GrClientMappedBufferManager* clientMappedBufferManager() {
+        return fContext->fMappedBufferManager.get();
+    }
+
 #if GR_TEST_UTILS
     /** Reset GPU stats */
     void resetGpuStats() const ;
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 3507f1b..65d8059 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -13,6 +13,7 @@
 #include "include/private/SkDeferredDisplayList.h"
 #include "src/core/SkTTopoSort.h"
 #include "src/gpu/GrAuditTrail.h"
+#include "src/gpu/GrClientMappedBufferManager.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrCopyRenderTask.h"
 #include "src/gpu/GrGpu.h"
@@ -248,6 +249,7 @@
         }
         return GrSemaphoresSubmitted::kNo; // Can't flush while DDL recording
     }
+    direct->priv().clientMappedBufferManager()->process();
 
     GrGpu* gpu = direct->priv().getGpu();
     if (!gpu) {
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index dcd108a..9f20a15 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -17,6 +17,7 @@
 #include "src/core/SkDrawShadowInfo.h"
 #include "src/core/SkGlyphRunPainter.h"
 #include "src/core/SkLatticeIter.h"
+#include "src/core/SkMakeUnique.h"
 #include "src/core/SkMatrixPriv.h"
 #include "src/core/SkRRectPriv.h"
 #include "src/core/SkSurfacePriv.h"
@@ -24,6 +25,7 @@
 #include "src/gpu/GrAuditTrail.h"
 #include "src/gpu/GrBlurUtils.h"
 #include "src/gpu/GrCaps.h"
+#include "src/gpu/GrClientMappedBufferManager.h"
 #include "src/gpu/GrColor.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrDataUtils.h"
@@ -1509,18 +1511,18 @@
         SkFilterQuality rescaleQuality, ReadPixelsCallback callback, ReadPixelsContext context) {
     auto direct = fContext->priv().asDirectContext();
     if (!direct) {
-        callback(context, nullptr, 0);
+        callback(context, nullptr);
         return;
     }
     if (fRenderTargetProxy->wrapsVkSecondaryCB()) {
-        callback(context, nullptr, 0);
+        callback(context, nullptr);
         return;
     }
     auto dstCT = SkColorTypeToGrColorType(info.colorType());
     // TODO: Support reading to gray.
     if (dstCT == GrColorType::kUnknown ||
         GrColorTypeComponentFlags(dstCT) & kGray_SkColorTypeComponentFlag) {
-        callback(context, nullptr, 0);
+        callback(context, nullptr);
         return;
     }
     bool needsRescale = srcRect.width() != info.width() || srcRect.height() != info.height();
@@ -1535,7 +1537,7 @@
                                                                backendFormatOfFinalContext, dstCT);
     // Fail if we can't read from the source surface's color type.
     if (readInfo.fColorType == GrColorType::kUnknown) {
-        callback(context, nullptr, 0);
+        callback(context, nullptr);
         return;
     }
     // Fail if read color type does not have all of dstCT's color channels and those missing color
@@ -1544,7 +1546,7 @@
     uint32_t legalReadComponents = GrColorTypeComponentFlags(readInfo.fColorType);
     uint32_t srcComponents = GrColorTypeComponentFlags(this->colorInfo().colorType());
     if ((~legalReadComponents & dstComponents) & srcComponents) {
-        callback(context, nullptr, 0);
+        callback(context, nullptr);
         return;
     }
 
@@ -1554,7 +1556,7 @@
     if (needsRescale) {
         tempRTC = this->rescale(info, srcRect, rescaleGamma, rescaleQuality);
         if (!tempRTC) {
-            callback(context, nullptr, 0);
+            callback(context, nullptr);
             return;
         }
         SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
@@ -1570,7 +1572,7 @@
             // We flip or color convert by drawing and we don't currently support drawing to
             // kPremul.
             if (info.alphaType() == kUnpremul_SkAlphaType) {
-                callback(context, nullptr, 0);
+                callback(context, nullptr);
                 return;
             }
             sk_sp<GrTextureProxy> texProxy = sk_ref_sp(fRenderTargetProxy->asTextureProxy());
@@ -1581,7 +1583,7 @@
                                                 GrMipMapped::kNo, srcRect, SkBackingFit::kApprox,
                                                 SkBudgeted::kNo);
                 if (!texProxy) {
-                    callback(context, nullptr, 0);
+                    callback(context, nullptr);
                     return;
                 }
                 srcRectToDraw = SkRect::MakeWH(srcRect.width(), srcRect.height());
@@ -1591,7 +1593,7 @@
                     this->colorInfo().colorType(), info.refColorSpace(), 1, GrMipMapped::kNo,
                     kTopLeft_GrSurfaceOrigin);
             if (!tempRTC) {
-                callback(context, nullptr, 0);
+                callback(context, nullptr);
                 return;
             }
             tempRTC->drawTexture(GrNoClip(), std::move(texProxy), GrSamplerState::Filter::kNearest,
@@ -1607,55 +1609,126 @@
                                 info.colorType(), callback, context);
 }
 
+class GrRenderTargetContext::AsyncReadResult : public SkSurface::AsyncReadResult {
+public:
+    AsyncReadResult(uint32_t inboxID) : fInboxID(inboxID) {}
+    ~AsyncReadResult() override {
+        for (int i = 0; i < fPlanes.count(); ++i) {
+            if (!fPlanes[i].fMappedBuffer) {
+                delete[] static_cast<const char*>(fPlanes[i].fData);
+            } else {
+                GrClientMappedBufferManager::BufferFinishedMessageBus::Post(
+                        {std::move(fPlanes[i].fMappedBuffer), fInboxID});
+            }
+        }
+    }
+
+    int count() const override { return fPlanes.count(); }
+    const void* data(int i) const override { return fPlanes[i].fData; }
+    size_t rowBytes(int i) const override { return fPlanes[i].fRowBytes; }
+
+    bool addTransferResult(const PixelTransferResult& result,
+                           SkISize size,
+                           size_t rowBytes,
+                           GrClientMappedBufferManager* manager) {
+        SkASSERT(!result.fTransferBuffer->isMapped());
+        const void* mappedData = result.fTransferBuffer->map();
+        if (!mappedData) {
+            return false;
+        }
+        if (result.fPixelConverter) {
+            std::unique_ptr<char[]> convertedData(new char[rowBytes * size.height()]);
+            result.fPixelConverter(convertedData.get(), mappedData);
+            this->addCpuPlane(std::move(convertedData), rowBytes);
+            result.fTransferBuffer->unmap();
+        } else {
+            manager->insert(result.fTransferBuffer);
+            this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer));
+        }
+        return true;
+    }
+
+    void addCpuPlane(std::unique_ptr<const char[]> data, size_t rowBytes) {
+        SkASSERT(data);
+        SkASSERT(rowBytes > 0);
+        fPlanes.emplace_back(data.release(), rowBytes, nullptr);
+    }
+
+private:
+    void addMappedPlane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> mappedBuffer) {
+        SkASSERT(data);
+        SkASSERT(rowBytes > 0);
+        SkASSERT(mappedBuffer);
+        SkASSERT(mappedBuffer->isMapped());
+        fPlanes.emplace_back(data, rowBytes, std::move(mappedBuffer));
+    }
+
+    struct Plane {
+        Plane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> buffer)
+                : fData(data), fRowBytes(rowBytes), fMappedBuffer(std::move(buffer)) {}
+        const void* fData;
+        size_t fRowBytes;
+        // If this is null then fData is heap alloc and must be delete[]ed as const char[].
+        sk_sp<GrGpuBuffer> fMappedBuffer;
+    };
+    SkSTArray<3, Plane> fPlanes;
+    uint32_t fInboxID;
+};
+
 void GrRenderTargetContext::asyncReadPixels(const SkIRect& rect, SkColorType colorType,
                                             ReadPixelsCallback callback,
                                             ReadPixelsContext context) {
     SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
     SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
 
+    auto directContext = fContext->priv().asDirectContext();
+    SkASSERT(directContext);
+    auto mappedBufferManager = directContext->priv().clientMappedBufferManager();
+
     auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect);
 
     if (!transferResult.fTransferBuffer) {
-        SkAutoPixmapStorage pm;
         auto ii = SkImageInfo::Make(rect.size(), colorType,
                                     this->colorInfo().alphaType(),
                                     this->colorInfo().refColorSpace());
-        pm.alloc(ii);
+        auto result = skstd::make_unique<AsyncReadResult>(0);
+        std::unique_ptr<char[]> data(new char[ii.computeMinByteSize()]);
+        SkPixmap pm(ii, data.get(), ii.minRowBytes());
+        result->addCpuPlane(std::move(data), pm.rowBytes());
+
         if (!this->readPixels(ii, pm.writable_addr(), pm.rowBytes(), {rect.fLeft, rect.fTop})) {
-            callback(context, nullptr, 0);
+            callback(context, nullptr);
         }
-        callback(context, pm.addr(), pm.rowBytes());
+        callback(context, std::move(result));
         return;
     }
 
     struct FinishContext {
         ReadPixelsCallback* fClientCallback;
         ReadPixelsContext fClientContext;
-        int fW, fH;
+        SkISize fSize;
         SkColorType fColorType;
+        GrClientMappedBufferManager* fMappedBufferManager;
         PixelTransferResult fTransferResult;
     };
     // 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
     // callback to GrGpu until after the next flush that flushes our op list, though.
-    auto* finishContext = new FinishContext{callback, context, rect.width(),
-                                            rect.height(), colorType, std::move(transferResult)};
+    auto* finishContext = new FinishContext{callback,
+                                            context,
+                                            rect.size(),
+                                            colorType,
+                                            mappedBufferManager,
+                                            std::move(transferResult)};
     auto finishCallback = [](GrGpuFinishedContext c) {
         const auto* context = reinterpret_cast<const FinishContext*>(c);
-        const void* data = context->fTransferResult.fTransferBuffer->map();
-        if (!data) {
-            (*context->fClientCallback)(context->fClientContext, nullptr, 0);
-            delete context;
-            return;
+        auto result = skstd::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
+        size_t rowBytes = context->fSize.width() * SkColorTypeBytesPerPixel(context->fColorType);
+        if (!result->addTransferResult(context->fTransferResult, context->fSize, rowBytes,
+                                       context->fMappedBufferManager)) {
+            result.reset();
         }
-        std::unique_ptr<char[]> tmp;
-        size_t rowBytes = context->fW * SkColorTypeBytesPerPixel(context->fColorType);
-        if (context->fTransferResult.fPixelConverter) {
-            tmp.reset(new char[rowBytes * context->fH]);
-            context->fTransferResult.fPixelConverter(tmp.get(), data);
-            data = tmp.get();
-        }
-        (*context->fClientCallback)(context->fClientContext, data, rowBytes);
+        (*context->fClientCallback)(context->fClientContext, std::move(result));
         delete context;
     };
     GrFlushInfo flushInfo;
@@ -1664,37 +1737,40 @@
     this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo);
 }
 
-void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
-        SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
-        int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
-        ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
+void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                            sk_sp<SkColorSpace> dstColorSpace,
+                                                            const SkIRect& srcRect,
+                                                            const SkISize& dstSize,
+                                                            RescaleGamma rescaleGamma,
+                                                            SkFilterQuality rescaleQuality,
+                                                            ReadPixelsCallback callback,
+                                                            ReadPixelsContext context) {
     SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width());
     SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height());
-    SkASSERT((dstW % 2 == 0) && (dstH % 2 == 0));
+    SkASSERT(!dstSize.isZero());
+    SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0));
+
     auto direct = fContext->priv().asDirectContext();
     if (!direct) {
-        callback(context, nullptr, nullptr);
+        callback(context, nullptr);
         return;
     }
     if (fRenderTargetProxy->wrapsVkSecondaryCB()) {
-        callback(context, nullptr, nullptr);
-        return;
-    }
-    if (dstW & 0x1) {
+        callback(context, nullptr);
         return;
     }
     int x = srcRect.fLeft;
     int y = srcRect.fTop;
     std::unique_ptr<GrRenderTargetContext> tempRTC;
-    bool needsRescale = srcRect.width() != dstW || srcRect.height() != dstH;
+    bool needsRescale = srcRect.size() != dstSize;
     if (needsRescale) {
         // We assume the caller wants kPremul. There is no way to indicate a preference.
-        auto info = SkImageInfo::Make(dstW, dstH, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
+        auto info = SkImageInfo::Make(dstSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
                                       dstColorSpace);
         // TODO: Incorporate the YUV conversion into last pass of rescaling.
         tempRTC = this->rescale(info, srcRect, rescaleGamma, rescaleQuality);
         if (!tempRTC) {
-            callback(context, nullptr, nullptr);
+            callback(context, nullptr);
             return;
         }
         SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
@@ -1709,15 +1785,16 @@
             sk_sp<GrTextureProxy> texProxy = this->asTextureProxyRef();
             // TODO: Do something if the input is not a texture already.
             if (!texProxy) {
-                callback(context, nullptr, nullptr);
+                callback(context, nullptr);
                 return;
             }
             SkRect srcRectToDraw = SkRect::Make(srcRect);
             tempRTC = direct->priv().makeDeferredRenderTargetContext(
-                    SkBackingFit::kApprox, dstW, dstH, this->colorInfo().colorType(), dstColorSpace,
-                    1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
+                    SkBackingFit::kApprox, dstSize.width(), dstSize.height(),
+                    this->colorInfo().colorType(), dstColorSpace, 1, GrMipMapped::kNo,
+                    kTopLeft_GrSurfaceOrigin);
             if (!tempRTC) {
-                callback(context, nullptr, nullptr);
+                callback(context, nullptr);
                 return;
             }
             tempRTC->drawTexture(GrNoClip(), std::move(texProxy), GrSamplerState::Filter::kNearest,
@@ -1731,20 +1808,22 @@
     auto srcProxy = tempRTC ? tempRTC->asTextureProxyRef() : this->asTextureProxyRef();
     // TODO: Do something if the input is not a texture already.
     if (!srcProxy) {
-        callback(context, nullptr, nullptr);
+        callback(context, nullptr);
         return;
     }
     auto yRTC = direct->priv().makeDeferredRenderTargetContextWithFallback(
-            SkBackingFit::kApprox, dstW, dstH, GrColorType::kAlpha_8, dstColorSpace, 1,
-            GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
+            SkBackingFit::kApprox, dstSize.width(), dstSize.height(), GrColorType::kAlpha_8,
+            dstColorSpace, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
+    int halfW = dstSize.width()/2;
+    int halfH = dstSize.height()/2;
     auto uRTC = direct->priv().makeDeferredRenderTargetContextWithFallback(
-            SkBackingFit::kApprox, dstW / 2, dstH / 2, GrColorType::kAlpha_8, dstColorSpace, 1,
+            SkBackingFit::kApprox, halfW, halfH, GrColorType::kAlpha_8, dstColorSpace, 1,
             GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
     auto vRTC = direct->priv().makeDeferredRenderTargetContextWithFallback(
-            SkBackingFit::kApprox, dstW / 2, dstH / 2, GrColorType::kAlpha_8, dstColorSpace, 1,
+            SkBackingFit::kApprox, halfW, halfH, GrColorType::kAlpha_8, dstColorSpace, 1,
             GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
     if (!yRTC || !uRTC || !vRTC) {
-        callback(context, nullptr, nullptr);
+        callback(context, nullptr);
         return;
     }
 
@@ -1787,8 +1866,8 @@
 
     auto texMatrix = SkMatrix::MakeTrans(x, y);
 
-    SkRect dstRectY = SkRect::MakeWH(dstW, dstH);
-    SkRect dstRectUV = SkRect::MakeWH(dstW / 2, dstH / 2);
+    SkRect dstRectY = SkRect::Make(dstSize);
+    SkRect dstRectUV = SkRect::MakeWH(halfW, halfH);
 
     // This matrix generates (r,g,b,a) = (0, 0, 0, y)
     float yM[20];
@@ -1804,7 +1883,7 @@
     auto yTransfer = yRTC->transferPixels(GrColorType::kAlpha_8,
                                           SkIRect::MakeWH(yRTC->width(), yRTC->height()));
     if (!yTransfer.fTransferBuffer) {
-        callback(context, nullptr, nullptr);
+        callback(context, nullptr);
         return;
     }
 
@@ -1823,7 +1902,7 @@
     auto uTransfer = uRTC->transferPixels(GrColorType::kAlpha_8,
                                           SkIRect::MakeWH(uRTC->width(), uRTC->height()));
     if (!uTransfer.fTransferBuffer) {
-        callback(context, nullptr, nullptr);
+        callback(context, nullptr);
         return;
     }
 
@@ -1841,14 +1920,15 @@
     auto vTransfer = vRTC->transferPixels(GrColorType::kAlpha_8,
                                           SkIRect::MakeWH(vRTC->width(), vRTC->height()));
     if (!vTransfer.fTransferBuffer) {
-        callback(context, nullptr, nullptr);
+        callback(context, nullptr);
         return;
     }
 
     struct FinishContext {
-        ReadPixelsCallbackYUV420* fClientCallback;
+        ReadPixelsCallback* fClientCallback;
         ReadPixelsContext fClientContext;
-        int fW, fH;
+        GrClientMappedBufferManager* fMappedBufferManager;
+        SkISize fSize;
         PixelTransferResult fYTransfer;
         PixelTransferResult fUTransfer;
         PixelTransferResult fVTransfer;
@@ -1858,56 +1938,34 @@
     // callback to GrGpu until after the next flush that flushes our op list, though.
     auto* finishContext = new FinishContext{callback,
                                             context,
-                                            dstW,
-                                            dstH,
+                                            direct->priv().clientMappedBufferManager(),
+                                            dstSize,
                                             std::move(yTransfer),
                                             std::move(uTransfer),
                                             std::move(vTransfer)};
     auto finishCallback = [](GrGpuFinishedContext c) {
         const auto* context = reinterpret_cast<const FinishContext*>(c);
-        const void* y = context->fYTransfer.fTransferBuffer->map();
-        const void* u = context->fUTransfer.fTransferBuffer->map();
-        const void* v = context->fVTransfer.fTransferBuffer->map();
-        if (!y || !u || !v) {
-            if (y) {
-                context->fYTransfer.fTransferBuffer->unmap();
-            }
-            if (u) {
-                context->fUTransfer.fTransferBuffer->unmap();
-            }
-            if (v) {
-                context->fVTransfer.fTransferBuffer->unmap();
-            }
-            (*context->fClientCallback)(context->fClientContext, nullptr, 0);
+        auto result = skstd::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
+        auto manager = context->fMappedBufferManager;
+        size_t rowBytes = SkToSizeT(context->fSize.width());
+        if (!result->addTransferResult(context->fYTransfer, context->fSize, rowBytes, manager)) {
+            (*context->fClientCallback)(context->fClientContext, nullptr);
             delete context;
             return;
         }
-        size_t w = SkToSizeT(context->fW);
-        size_t h = SkToSizeT(context->fH);
-        std::unique_ptr<uint8_t[]> yTemp;
-        if (context->fYTransfer.fPixelConverter) {
-            yTemp.reset(new uint8_t[w * h]);
-            context->fYTransfer.fPixelConverter(yTemp.get(), y);
-            y = yTemp.get();
+        rowBytes /= 2;
+        SkISize uvSize = {context->fSize.width()/2, context->fSize.height()/2};
+        if (!result->addTransferResult(context->fUTransfer, uvSize, rowBytes, manager)) {
+            (*context->fClientCallback)(context->fClientContext, nullptr);
+            delete context;
+            return;
         }
-        std::unique_ptr<uint8_t[]> uTemp;
-        if (context->fUTransfer.fPixelConverter) {
-            uTemp.reset(new uint8_t[w / 2 * h / 2]);
-            context->fUTransfer.fPixelConverter(uTemp.get(), u);
-            u = uTemp.get();
+        if (!result->addTransferResult(context->fVTransfer, uvSize, rowBytes, manager)) {
+            (*context->fClientCallback)(context->fClientContext, nullptr);
+            delete context;
+            return;
         }
-        std::unique_ptr<uint8_t[]> vTemp;
-        if (context->fVTransfer.fPixelConverter) {
-            vTemp.reset(new uint8_t[w / 2 * h / 2]);
-            context->fVTransfer.fPixelConverter(vTemp.get(), v);
-            v = vTemp.get();
-        }
-        const void* data[] = {y, u, v};
-        size_t rowBytes[] = {w, w / 2, w / 2};
-        (*context->fClientCallback)(context->fClientContext, data, rowBytes);
-        context->fYTransfer.fTransferBuffer->unmap();
-        context->fUTransfer.fTransferBuffer->unmap();
-        context->fVTransfer.fTransferBuffer->unmap();
+        (*context->fClientCallback)(context->fClientContext, std::move(result));
         delete context;
     };
     GrFlushInfo flushInfo;
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 1bc8165..0bb6525 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -441,7 +441,6 @@
     void drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>, const SkRect& bounds);
 
     using ReadPixelsCallback = SkSurface::ReadPixelsCallback;
-    using ReadPixelsCallbackYUV420 = SkSurface::ReadPixelsCallbackYUV420;
     using ReadPixelsContext = SkSurface::ReadPixelsContext;
     using RescaleGamma = SkSurface::RescaleGamma;
 
@@ -451,10 +450,12 @@
                                    ReadPixelsCallback callback, ReadPixelsContext context);
     // GPU implementation for SkSurface::asyncRescaleAndReadPixelsYUV420.
     void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
-                                         sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
-                                         int dstW, int dstH, RescaleGamma rescaleGamma,
+                                         sk_sp<SkColorSpace> dstColorSpace,
+                                         const SkIRect& srcRect,
+                                         const SkISize& dstSize,
+                                         RescaleGamma rescaleGamma,
                                          SkFilterQuality rescaleQuality,
-                                         ReadPixelsCallbackYUV420 callback,
+                                         ReadPixelsCallback callback,
                                          ReadPixelsContext context);
 
     /**
@@ -622,6 +623,8 @@
     bool SK_WARN_UNUSED_RESULT setupDstProxy(const GrClip&, const GrOp& op,
                                              GrXferProcessor::DstProxy* result);
 
+    class AsyncReadResult;
+
     // The async read step of asyncRescaleAndReadPixels()
     void asyncReadPixels(const SkIRect& rect, SkColorType colorType, ReadPixelsCallback callback,
                          ReadPixelsContext context);
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index 898ee91..c1ea2ca 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -83,7 +83,7 @@
         subset = &bounds;
     }
 
-    fInfo   = info.makeWH(subset->width(), subset->height());
+    fInfo   = info.makeDimensions(subset->size());
     fOrigin = SkIPoint::Make(subset->x(), subset->y());
     if (colorType || colorSpace) {
         if (colorType) {
@@ -321,7 +321,7 @@
         if (!SkIRect::MakeWH(info.width(), info.height()).contains(*subset)) {
             return nullptr;
         }
-        info = info.makeWH(subset->width(), subset->height());
+        info = info.makeDimensions(subset->size());
         origin = {subset->x(), subset->y()};
     }
 
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index f109234..5992af7 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -231,7 +231,7 @@
 #endif
 
 sk_sp<SkImage> SkImage_Raster::onMakeSubset(GrRecordingContext*, const SkIRect& subset) const {
-    SkImageInfo info = fBitmap.info().makeWH(subset.width(), subset.height());
+    SkImageInfo info = fBitmap.info().makeDimensions(subset.size());
     SkBitmap bitmap;
     if (!bitmap.tryAllocPixels(info)) {
         return nullptr;
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index c8b6e79..a04982d 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -12,6 +12,7 @@
 #include "include/gpu/GrBackendSurface.h"
 #include "src/core/SkAutoPixmapStorage.h"
 #include "src/core/SkImagePriv.h"
+#include "src/core/SkMakeUnique.h"
 #include "src/image/SkSurface_Base.h"
 
 static SkPixelGeometry compute_default_geometry() {
@@ -144,7 +145,7 @@
             ii = ii.makeColorType(this->getCanvas()->imageInfo().colorType());
             linearSurf = this->makeSurface(ii);
             if (!linearSurf) {
-                callback(context, nullptr, 0);
+                callback(context, nullptr);
                 return;
             }
         }
@@ -182,7 +183,7 @@
         }
         auto next = this->makeSurface(ii);
         if (!next) {
-            callback(context, nullptr, 0);
+            callback(context, nullptr);
             return;
         }
         next->getCanvas()->drawImageRect(
@@ -195,22 +196,35 @@
         constraint = SkCanvas::kFast_SrcRectConstraint;
     }
 
-    SkAutoPixmapStorage pm;
-    pm.alloc(info);
+    size_t rowBytes = info.minRowBytes();
+    std::unique_ptr<char[]> data(new char[info.height() * rowBytes]);
+    SkPixmap pm(info, data.get(), rowBytes);
     if (src->readPixels(pm, srcX, srcY)) {
-        callback(context, pm.addr(), pm.rowBytes());
+        class Result : public AsyncReadResult {
+        public:
+            Result(std::unique_ptr<const char[]> data, size_t rowBytes)
+                    : fData(std::move(data)), fRowBytes(rowBytes) {}
+            int count() const override { return 1; }
+            const void* data(int i) const override { return fData.get(); }
+            size_t rowBytes(int i) const override { return fRowBytes; }
+
+        private:
+            std::unique_ptr<const char[]> fData;
+            size_t fRowBytes;
+        };
+        callback(context, skstd::make_unique<Result>(std::move(data), rowBytes));
     } else {
-        callback(context, nullptr, 0);
+        callback(context, nullptr);
     }
 }
 
 void SkSurface_Base::onAsyncRescaleAndReadPixelsYUV420(
         SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
-        int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
-        ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
+        const SkISize& dstSize, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
+        ReadPixelsCallback callback, ReadPixelsContext context) {
     // TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
     // call client's callback.
-    callback(context, nullptr, nullptr);
+    callback(context, nullptr);
 }
 
 bool SkSurface_Base::outstandingImageSnapshot() const {
@@ -347,30 +361,119 @@
     return bitmap.peekPixels(&pm) && this->readPixels(pm, srcX, srcY);
 }
 
-void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
-                                          RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
-                                          ReadPixelsCallback callback, ReadPixelsContext context) {
+// Stuff to keep the legacy async readback APIs working on top of the new implementation.
+namespace {
+struct BridgeContext {
+    SkSurface::ReadPixelsContext fClientContext;
+    SkSurface::LegacyReadPixelsCallback* fClientCallback;
+};
+struct BridgeContextYUV420 {
+    SkSurface::ReadPixelsContext fClientContext;
+    SkSurface::LegacyReadPixelsCallbackYUV420* fClientCallback;
+};
+}  // anonymous namespace
+
+static void bridge_callback(SkSurface::ReadPixelsContext context,
+                            std::unique_ptr<const SkSurface::AsyncReadResult> result) {
+    auto bridgeContext = static_cast<const BridgeContext*>(context);
+    if (!result || result->count() != 1) {
+        bridgeContext->fClientCallback(bridgeContext->fClientContext, nullptr, 0);
+    } else {
+        bridgeContext->fClientCallback(bridgeContext->fClientContext, result->data(0),
+                                       result->rowBytes(0));
+    }
+    delete bridgeContext;
+}
+
+static void bridge_callback_yuv420(SkSurface::ReadPixelsContext context,
+                                   std::unique_ptr<const SkSurface::AsyncReadResult> result) {
+    auto bridgeContext = static_cast<const BridgeContextYUV420*>(context);
+    if (!result || result->count() != 3) {
+        bridgeContext->fClientCallback(bridgeContext->fClientContext, nullptr, 0);
+    } else {
+        const void* data[] = {result->data(0),    result->data(1),     result->data(2)};
+        size_t rowBytes[] = {result->rowBytes(0), result->rowBytes(1), result->rowBytes(2)};
+        bridgeContext->fClientCallback(bridgeContext->fClientContext, data, rowBytes);
+    }
+    delete bridgeContext;
+}
+
+void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info,
+                                          const SkIRect& srcRect,
+                                          RescaleGamma rescaleGamma,
+                                          SkFilterQuality rescaleQuality,
+                                          LegacyReadPixelsCallback callback,
+                                          ReadPixelsContext context) {
     if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
         !SkImageInfoIsValid(info)) {
         callback(context, nullptr, 0);
         return;
     }
-    asSB(this)->onAsyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality, callback,
-                                            context);
+
+    auto bridgeContext = new BridgeContext{context, callback};
+    asSB(this)->onAsyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality,
+                                            bridge_callback, bridgeContext);
 }
 
-void SkSurface::asyncRescaleAndReadPixelsYUV420(
-        SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
-        int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
-        ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
+void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info,
+                                          const SkIRect& srcRect,
+                                          RescaleGamma rescaleGamma,
+                                          SkFilterQuality rescaleQuality,
+                                          ReadPixelsCallback callback,
+                                          ReadPixelsContext context) {
+    if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
+        !SkImageInfoIsValid(info)) {
+        callback(context, nullptr);
+        return;
+    }
+    asSB(this)->onAsyncRescaleAndReadPixels(
+            info, srcRect, rescaleGamma, rescaleQuality, callback, context);
+}
+
+void SkSurface::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                sk_sp<SkColorSpace> dstColorSpace,
+                                                const SkIRect& srcRect,
+                                                int dstW, int dstH,
+                                                RescaleGamma rescaleGamma,
+                                                SkFilterQuality rescaleQuality,
+                                                LegacyReadPixelsCallbackYUV420 callback,
+                                                ReadPixelsContext context) {
     if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || (dstW & 0b1) ||
         (dstH & 0b1)) {
         callback(context, nullptr, nullptr);
         return;
     }
-    asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace, std::move(dstColorSpace), srcRect,
-                                                  dstW, dstH, rescaleGamma, rescaleQuality,
-                                                  callback, context);
+    auto bridgeContext = new BridgeContextYUV420{context, callback};
+    asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
+                                                  std::move(dstColorSpace), srcRect,
+                                                  {dstW, dstH},
+                                                  rescaleGamma,
+                                                  rescaleQuality,
+                                                  bridge_callback_yuv420,
+                                                  bridgeContext);
+}
+
+void SkSurface::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                sk_sp<SkColorSpace> dstColorSpace,
+                                                const SkIRect& srcRect,
+                                                const SkISize& dstSize,
+                                                RescaleGamma rescaleGamma,
+                                                SkFilterQuality rescaleQuality,
+                                                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,
+                                                  std::move(dstColorSpace),
+                                                  srcRect,
+                                                  dstSize,
+                                                  rescaleGamma,
+                                                  rescaleQuality,
+                                                  callback,
+                                                  context);
 }
 
 void SkSurface::writePixels(const SkPixmap& pmap, int x, int y) {
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index 460f5a8..7bff9a8 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -51,21 +51,23 @@
     /**
      * Default implementation does a rescale/read and then calls the callback.
      */
-    virtual void onAsyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
-                                             RescaleGamma rescaleGamma,
-                                             SkFilterQuality rescaleQuality,
-                                             ReadPixelsCallback callback,
-                                             ReadPixelsContext context);
+    virtual void onAsyncRescaleAndReadPixels(const SkImageInfo&,
+                                             const SkIRect& srcRect,
+                                             RescaleGamma,
+                                             SkFilterQuality,
+                                             ReadPixelsCallback,
+                                             ReadPixelsContext);
     /**
      * Default implementation does a rescale/read/yuv conversion and then calls the callback.
      */
-    virtual void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+    virtual void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
                                                    sk_sp<SkColorSpace> dstColorSpace,
-                                                   const SkIRect& srcRect, int dstW, int dstH,
-                                                   RescaleGamma rescaleGamma,
-                                                   SkFilterQuality rescaleQuality,
-                                                   ReadPixelsCallbackYUV420 callback,
-                                                   ReadPixelsContext context);
+                                                   const SkIRect& srcRect,
+                                                   const SkISize& dstSize,
+                                                   RescaleGamma,
+                                                   SkFilterQuality,
+                                                   ReadPixelsCallback,
+                                                   ReadPixelsContext);
 
     /**
      *  Default implementation:
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 56ce90a..6eb3fda 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -132,7 +132,8 @@
     fDevice->writePixels(src, x, y);
 }
 
-void SkSurface_Gpu::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
+void SkSurface_Gpu::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
+                                                const SkIRect& srcRect,
                                                 RescaleGamma rescaleGamma,
                                                 SkFilterQuality rescaleQuality,
                                                 ReadPixelsCallback callback,
@@ -141,13 +142,23 @@
     rtc->asyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality, callback, context);
 }
 
-void SkSurface_Gpu::onAsyncRescaleAndReadPixelsYUV420(
-        SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
-        int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
-        ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
+void SkSurface_Gpu::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
+                                                      sk_sp<SkColorSpace> dstColorSpace,
+                                                      const SkIRect& srcRect,
+                                                      const SkISize& dstSize,
+                                                      RescaleGamma rescaleGamma,
+                                                      SkFilterQuality rescaleQuality,
+                                                      ReadPixelsCallback callback,
+                                                      ReadPixelsContext context) {
     auto* rtc = this->fDevice->accessRenderTargetContext();
-    rtc->asyncRescaleAndReadPixelsYUV420(yuvColorSpace, std::move(dstColorSpace), srcRect, dstW,
-                                         dstH, rescaleGamma, rescaleQuality, callback, context);
+    rtc->asyncRescaleAndReadPixelsYUV420(yuvColorSpace,
+                                         std::move(dstColorSpace),
+                                         srcRect,
+                                         dstSize,
+                                         rescaleGamma,
+                                         rescaleQuality,
+                                         callback,
+                                         context);
 }
 
 // Create a new render target and, if necessary, copy the contents of the old
diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h
index c176811..e27e7e5 100644
--- a/src/image/SkSurface_Gpu.h
+++ b/src/image/SkSurface_Gpu.h
@@ -40,10 +40,11 @@
                                      ReadPixelsContext context) override;
     void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
                                            sk_sp<SkColorSpace> dstColorSpace,
-                                           const SkIRect& srcRect, int dstW, int dstH,
+                                           const SkIRect& srcRect,
+                                           const SkISize& dstSize,
                                            RescaleGamma rescaleGamma,
                                            SkFilterQuality rescaleQuality,
-                                           ReadPixelsCallbackYUV420 callback,
+                                           ReadPixelsCallback callback,
                                            ReadPixelsContext context) override;
 
     void onCopyOnWrite(ContentChangeMode) override;
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
index f6e4c4f..a086165 100644
--- a/src/image/SkSurface_Raster.cpp
+++ b/src/image/SkSurface_Raster.cpp
@@ -111,7 +111,7 @@
     if (subset) {
         SkASSERT(SkIRect::MakeWH(fBitmap.width(), fBitmap.height()).contains(*subset));
         SkBitmap dst;
-        dst.allocPixels(fBitmap.info().makeWH(subset->width(), subset->height()));
+        dst.allocPixels(fBitmap.info().makeDimensions(subset->size()));
         SkAssertResult(fBitmap.readPixels(dst.pixmap(), subset->left(), subset->top()));
         dst.setImmutable(); // key, so MakeFromBitmap doesn't make a copy of the buffer
         return SkImage::MakeFromBitmap(dst);