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);