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;
+}