More SkImage_GpuYUV updates

Bug: skia:7901
Change-Id: If5f747ff95c65ac95cfed8c1282cc08019d8006a
Reviewed-on: https://skia-review.googlesource.com/c/160024
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 63d65a4..b5105ab 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -15,6 +15,7 @@
 #include "SkBitmapCache.h"
 #include "SkImage_Gpu.h"
 #include "SkImage_GpuBase.h"
+#include "SkReadPixelsRec.h"
 
 SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
                                  SkAlphaType at, SkBudgeted budgeted, sk_sp<SkColorSpace> cs)
@@ -121,6 +122,76 @@
                                    fColorSpace, fBudgeted);
 }
 
+static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
+    switch (info.colorType()) {
+    case kRGBA_8888_SkColorType:
+    case kBGRA_8888_SkColorType:
+        break;
+    default:
+        return; // nothing to do
+    }
+
+    // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
+    // and in either case, the alpha-byte is always in the same place, so we can safely call
+    // SkPreMultiplyColor()
+    //
+    SkColor* row = (SkColor*)pixels;
+    for (int y = 0; y < info.height(); ++y) {
+        for (int x = 0; x < info.width(); ++x) {
+            row[x] = SkPreMultiplyColor(row[x]);
+        }
+        row = (SkColor*)((char*)(row)+rowBytes);
+    }
+}
+
+bool SkImage_GpuBase::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
+                                   int srcX, int srcY, CachingHint) const {
+    if (!fContext->contextPriv().resourceProvider()) {
+        // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
+        return false;
+    }
+
+    if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
+        return false;
+    }
+
+    SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
+    if (!rec.trim(this->width(), this->height())) {
+        return false;
+    }
+
+    // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
+    // GrRenderTargetContext::onReadPixels
+    uint32_t flags = 0;
+    if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
+        // let the GPU perform this transformation for us
+        flags = GrContextPriv::kUnpremul_PixelOpsFlag;
+    }
+
+    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
+        this->asTextureProxyRef(), fColorSpace);
+    if (!sContext) {
+        return false;
+    }
+
+    if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
+        return false;
+    }
+
+    // do we have to manually fix-up the alpha channel?
+    //      src         dst
+    //      unpremul    premul      fix manually
+    //      premul      unpremul    done by kUnpremul_PixelOpsFlag
+    // all other combos need to change.
+    //
+    // Should this be handled by Ganesh? todo:?
+    //
+    if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
+        apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
+    }
+    return true;
+}
+
 sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrContext* context,
                                                          const GrSamplerState& params,
                                                          SkColorSpace* dstColorSpace,
@@ -235,3 +306,52 @@
 
     return true;
 }
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+sk_sp<GrTexture> SkPromiseImageHelper::getTexture(GrResourceProvider* resourceProvider,
+                                                  GrPixelConfig config) {
+    // Releases the promise helper if there are no outstanding hard refs. This means that we
+    // don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
+    if (fReleaseHelper && fReleaseHelper->weak_expired()) {
+        this->resetReleaseHelper();
+    }
+
+    sk_sp<GrTexture> tex;
+    if (!fReleaseHelper) {
+        fFulfillProc(fContext, &fBackendTex);
+        fBackendTex.fConfig = config;
+        if (!fBackendTex.isValid()) {
+            // Even though the GrBackendTexture is not valid, we must call the release
+            // proc to keep our contract of always calling Fulfill and Release in pairs.
+            fReleaseProc(fContext);
+            return sk_sp<GrTexture>();
+        }
+
+        tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
+        if (!tex) {
+            // Even though the GrBackendTexture is not valid, we must call the release
+            // proc to keep our contract of always calling Fulfill and Release in pairs.
+            fReleaseProc(fContext);
+            return sk_sp<GrTexture>();
+        }
+        fReleaseHelper = new SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
+        // Take a weak ref
+        fReleaseHelper->weak_ref();
+    } else {
+        SkASSERT(fBackendTex.isValid());
+        tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
+        if (!tex) {
+            // We weren't able to make a texture here, but since we are in this branch
+            // of the calls (promiseHelper.fReleaseHelper is valid) there is already a
+            // texture out there which will call the release proc so we don't need to
+            // call it here.
+            return sk_sp<GrTexture>();
+        }
+
+        SkAssertResult(fReleaseHelper->try_ref());
+    }
+    SkASSERT(tex);
+    // Pass the hard ref off to the texture
+    tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
+    return tex;
+}