Implement more SkImage_GpuYUVA functionality
Bug: skia:7901
Change-Id: I78a947edb924e0a1240537a83aa0bc111239e567
Reviewed-on: https://skia-review.googlesource.com/c/159320
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
new file mode 100644
index 0000000..63d65a4
--- /dev/null
+++ b/src/image/SkImage_GpuBase.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrBackendSurface.h"
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "GrTexture.h"
+#include "GrTextureAdjuster.h"
+#include "SkBitmapCache.h"
+#include "SkImage_Gpu.h"
+#include "SkImage_GpuBase.h"
+
+SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
+ SkAlphaType at, SkBudgeted budgeted, sk_sp<SkColorSpace> cs)
+ : INHERITED(width, height, uniqueID)
+ , fContext(std::move(context))
+ , fAlphaType(at)
+ , fBudgeted(budgeted)
+ , fColorSpace(std::move(cs)) {}
+
+SkImage_GpuBase::~SkImage_GpuBase() {}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImage_GpuBase::ValidateBackendTexture(GrContext* ctx, const GrBackendTexture& tex,
+ GrPixelConfig* config, SkColorType ct, SkAlphaType at,
+ sk_sp<SkColorSpace> cs) {
+ if (!tex.isValid()) {
+ return false;
+ }
+ // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
+ // create a fake image info here.
+ SkImageInfo info = SkImageInfo::Make(1, 1, ct, at, cs);
+ if (!SkImageInfoIsValid(info)) {
+ return false;
+ }
+
+ return ctx->contextPriv().caps()->validateBackendTexture(tex, ct, config);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImage_GpuBase::getROPixels(SkBitmap* dst, SkColorSpace*, CachingHint chint) const {
+ if (!fContext->contextPriv().resourceProvider()) {
+ // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
+ return false;
+ }
+
+ // The SkColorSpace parameter "dstColorSpace" is really just a hint about how/where the bitmap
+ // will be used. The client doesn't expect that we convert to that color space, it's intended
+ // for codec-backed images, to drive our decoding heuristic. In theory we *could* read directly
+ // into that color space (to save the client some effort in whatever they're about to do), but
+ // that would make our use of the bitmap cache incorrect (or much less efficient, assuming we
+ // rolled the dstColorSpace into the key).
+ const auto desc = SkBitmapCacheDesc::Make(this);
+ if (SkBitmapCache::Find(desc, dst)) {
+ SkASSERT(dst->getGenerationID() == this->uniqueID());
+ SkASSERT(dst->isImmutable());
+ SkASSERT(dst->getPixels());
+ return true;
+ }
+
+ SkBitmapCache::RecPtr rec = nullptr;
+ SkPixmap pmap;
+ if (kAllow_CachingHint == chint) {
+ rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
+ if (!rec) {
+ return false;
+ }
+ } else {
+ if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
+ return false;
+ }
+ }
+
+ sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
+ this->asTextureProxyRef(),
+ fColorSpace);
+ if (!sContext) {
+ return false;
+ }
+
+ if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
+ return false;
+ }
+
+ if (rec) {
+ SkBitmapCache::Add(std::move(rec), dst);
+ this->notifyAddedToRasterCache();
+ }
+ return true;
+}
+
+sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset) const {
+ sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef();
+
+ GrSurfaceDesc desc;
+ desc.fWidth = subset.width();
+ desc.fHeight = subset.height();
+ desc.fConfig = proxy->config();
+
+ sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
+ desc, proxy->origin(), GrMipMapped::kNo, SkBackingFit::kExact, fBudgeted));
+ if (!sContext) {
+ return nullptr;
+ }
+
+ if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
+ return nullptr;
+ }
+
+ // MDB: this call is okay bc we know 'sContext' was kExact
+ return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
+ fAlphaType, sContext->asTextureProxyRef(),
+ fColorSpace, fBudgeted);
+}
+
+sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrContext* context,
+ const GrSamplerState& params,
+ SkColorSpace* dstColorSpace,
+ sk_sp<SkColorSpace>* texColorSpace,
+ SkScalar scaleAdjust[2]) const {
+ if (context->uniqueID() != fContext->uniqueID()) {
+ SkASSERT(0);
+ return nullptr;
+ }
+
+ GrTextureAdjuster adjuster(fContext.get(), this->asTextureProxyRef(), fAlphaType,
+ this->uniqueID(), fColorSpace.get());
+ return adjuster.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
+}
+
+GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
+ GrSurfaceOrigin* origin) const {
+ sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();
+ SkASSERT(proxy);
+
+ if (!fContext->contextPriv().resourceProvider() && !proxy->isInstantiated()) {
+ // This image was created with a DDL context and cannot be instantiated.
+ return GrBackendTexture();
+}
+
+ if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
+ return GrBackendTexture(); // invalid
+ }
+
+ GrTexture* texture = proxy->peekTexture();
+
+ if (texture) {
+ if (flushPendingGrContextIO) {
+ fContext->contextPriv().prepareSurfaceForExternalIO(proxy.get());
+ }
+ if (origin) {
+ *origin = proxy->origin();
+ }
+ return texture->getBackendTexture();
+ }
+ return GrBackendTexture(); // invalid
+}
+
+GrTexture* SkImage_GpuBase::onGetTexture() const {
+ GrTextureProxy* proxy = this->peekProxy();
+ if (!proxy) {
+ return nullptr;
+ }
+
+ sk_sp<GrTextureProxy> proxyRef = this->asTextureProxyRef();
+ if (!fContext->contextPriv().resourceProvider() && !proxyRef->isInstantiated()) {
+ // This image was created with a DDL context and cannot be instantiated.
+ return nullptr;
+ }
+
+ if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
+ return nullptr;
+ }
+
+ return proxy->peekTexture();
+}
+
+sk_sp<SkImage> SkImage_GpuBase::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
+ SkAlphaType newAlphaType = fAlphaType;
+#if defined(SK_LEGACY_MAKE_COLOR_SPACE_IMPL)
+ if (kUnpremul_SkAlphaType == fAlphaType) {
+ newAlphaType = kPremul_SkAlphaType;
+ }
+#endif
+ auto xform = GrColorSpaceXformEffect::Make(fColorSpace.get(), this->alphaType(),
+ target.get(), newAlphaType);
+ if (!xform) {
+ return sk_ref_sp(const_cast<SkImage_GpuBase*>(this));
+ }
+
+ sk_sp<GrRenderTargetContext> renderTargetContext(
+ fContext->contextPriv().makeDeferredRenderTargetContext(
+ SkBackingFit::kExact, this->width(), this->height(),
+ kRGBA_8888_GrPixelConfig, nullptr));
+ if (!renderTargetContext) {
+ return nullptr;
+ }
+
+ GrPaint paint;
+ paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+ paint.addColorTextureProcessor(this->asTextureProxyRef(), SkMatrix::I());
+ paint.addColorFragmentProcessor(std::move(xform));
+
+ const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
+
+ renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
+
+ if (!renderTargetContext->asTextureProxy()) {
+ return nullptr;
+ }
+
+ // MDB: this call is okay bc we know 'renderTargetContext' was exact
+ return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
+ newAlphaType, renderTargetContext->asTextureProxyRef(),
+ std::move(target), fBudgeted);
+}
+
+bool SkImage_GpuBase::onIsValid(GrContext* context) const {
+ // The base class has already checked that context isn't abandoned (if it's not nullptr)
+ if (fContext->abandoned()) {
+ return false;
+ }
+
+ if (context && context != fContext.get()) {
+ return false;
+ }
+
+ return true;
+}