Add SkImage_GpuYUVA
Bug: skia:7903
Change-Id: I06edc155b0a0a0697dc0d0aab74b6876d631ca0d
Reviewed-on: https://skia-review.googlesource.com/156942
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp
new file mode 100644
index 0000000..a89d54b
--- /dev/null
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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 <cstddef>
+#include <cstring>
+#include <type_traits>
+
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "GrTexture.h"
+#include "GrTextureAdjuster.h"
+#include "SkImage_Gpu.h"
+#include "SkImage_GpuYUVA.h"
+#include "SkReadPixelsRec.h"
+#include "effects/GrYUVtoRGBEffect.h"
+
+SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrContext> context, uint32_t uniqueID,
+ SkYUVColorSpace colorSpace, sk_sp<GrTextureProxy> proxies[],
+ SkYUVAIndex yuvaIndices[4], SkISize size, GrSurfaceOrigin origin,
+ sk_sp<SkColorSpace> imageColorSpace, SkBudgeted budgeted)
+ : INHERITED(size.width(), size.height(), uniqueID)
+ , fContext(std::move(context))
+ , fBudgeted(budgeted)
+ , fColorSpace(colorSpace)
+ , fOrigin(origin)
+ , fImageAlphaType(kOpaque_SkAlphaType)
+ , fImageColorSpace(std::move(imageColorSpace)) {
+ for (int i = 0; i < 4; ++i) {
+ fProxies[i] = std::move(proxies[i]);
+ }
+ memcpy(fYUVAIndices, yuvaIndices, 4*sizeof(SkYUVAIndex));
+ // If an alpha channel is present we always switch to kPremul. This is because, although the
+ // planar data is always un-premul, the final interleaved RGB image is/would-be premul.
+ if (-1 != yuvaIndices[3].fIndex) {
+ fImageAlphaType = kPremul_SkAlphaType;
+ }
+}
+
+SkImage_GpuYUVA::~SkImage_GpuYUVA() {}
+
+SkImageInfo SkImage_GpuYUVA::onImageInfo() const {
+ // Note: this is the imageInfo for the flattened image, not the YUV planes
+ return SkImageInfo::Make(this->width(), this->height(), kRGBA_8888_SkColorType,
+ fImageAlphaType, fImageColorSpace);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// need shared defs with SkImage_Gpu
+
+static bool validate_backend_texture(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);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef() const {
+ if (!fRGBProxy) {
+ sk_sp<GrTextureProxy> yProxy = fProxies[fYUVAIndices[0].fIndex];
+ sk_sp<GrTextureProxy> uProxy = fProxies[fYUVAIndices[1].fIndex];
+ sk_sp<GrTextureProxy> vProxy = fProxies[fYUVAIndices[2].fIndex];
+
+ if (!yProxy || !uProxy || !vProxy) {
+ return nullptr;
+ }
+
+ GrPaint paint;
+ paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+ // TODO: Modify the fragment processor to sample from different channel
+ // instead of taking nv12 bool.
+ bool nv12 = (fYUVAIndices[1].fIndex == fYUVAIndices[2].fIndex);
+ // TODO: modify the YUVtoRGBEffect to do premul if fImageAlphaType is kPremul_AlphaType
+ paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Make(std::move(yProxy), std::move(uProxy),
+ std::move(vProxy), fColorSpace,
+ nv12));
+
+ const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
+
+ // Needs to create a render target in order to draw to it for the yuv->rgb conversion.
+ sk_sp<GrRenderTargetContext> renderTargetContext(
+ fContext->contextPriv().makeDeferredRenderTargetContext(
+ SkBackingFit::kExact, this->width(), this->height(), kRGBA_8888_GrPixelConfig,
+ std::move(fImageColorSpace), 1, GrMipMapped::kNo, fOrigin));
+ if (!renderTargetContext) {
+ return nullptr;
+ }
+ renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
+
+ if (!renderTargetContext->asSurfaceProxy()) {
+ return nullptr;
+ }
+
+ // DDL TODO: in the promise image version we must not flush here
+ fContext->contextPriv().flushSurfaceWrites(renderTargetContext->asSurfaceProxy());
+
+ // cast to non-const
+ (sk_sp<GrTextureProxy>)(fRGBProxy) = renderTargetContext->asTextureProxyRef();
+ }
+
+ return fRGBProxy;
+}
+
+sk_sp<GrTextureProxy> SkImage_GpuYUVA::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(), this->alphaType(),
+ this->uniqueID(), this->fImageColorSpace.get());
+ return adjuster.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
+}
+
+bool SkImage_GpuYUVA::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;
+ }
+
+ // Flatten the YUVA planes to a single texture
+ sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef();
+
+ // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
+ // GrRenderTargetContext::onReadPixels
+ uint32_t flags = 0;
+ if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fImageAlphaType) {
+ // let the GPU perform this transformation for us
+ flags = GrContextPriv::kUnpremul_PixelOpsFlag;
+ }
+
+ sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
+ proxy, fImageColorSpace);
+ if (!sContext) {
+ return false;
+ }
+
+ if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
+ return false;
+ }
+
+ return true;
+}
+
+sk_sp<SkImage> SkImage_GpuYUVA::onMakeSubset(const SkIRect& subset) const {
+ // Flatten the YUVA planes to a single texture
+ 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,
+ fImageAlphaType, sContext->asTextureProxyRef(),
+ fImageColorSpace, fBudgeted);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+//*** bundle this into a helper function used by this and SkImage_Gpu?
+sk_sp<SkImage> SkImage_GpuYUVA::MakeFromYUVATextures(GrContext* ctx,
+ SkYUVColorSpace colorSpace,
+ const GrBackendTexture yuvaTextures[],
+ SkYUVAIndex yuvaIndices[4],
+ SkISize size,
+ GrSurfaceOrigin origin,
+ sk_sp<SkColorSpace> imageColorSpace) {
+ GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
+
+ // Right now this still only deals with YUV and NV12 formats. Assuming that YUV has different
+ // textures for U and V planes, while NV12 uses same texture for U and V planes.
+ bool nv12 = (yuvaIndices[1].fIndex == yuvaIndices[2].fIndex);
+ auto ct = nv12 ? kRGBA_8888_SkColorType : kAlpha_8_SkColorType;
+
+ // We need to make a copy of the input backend textures because we need to preserve the result
+ // of validate_backend_texture.
+ GrBackendTexture yuvaTexturesCopy[4];
+ for (int i = 0; i < 4; ++i) {
+ // Validate that the yuvaIndices refer to valid backend textures.
+ const SkYUVAIndex& yuvaIndex = yuvaIndices[i];
+ if (3 == i && yuvaIndex.fIndex == -1) {
+ // Meaning the A plane isn't passed in.
+ continue;
+ }
+ if (yuvaIndex.fIndex == -1 || yuvaIndex.fIndex > 3) {
+ // Y plane, U plane, and V plane must refer to image sources being passed in. There are
+ // at most 4 image sources being passed in, could not have a index more than 3.
+ return nullptr;
+ }
+ if (!yuvaTexturesCopy[yuvaIndex.fIndex].isValid()) {
+ yuvaTexturesCopy[yuvaIndex.fIndex] = yuvaTextures[yuvaIndex.fIndex];
+ // TODO: Instead of using assumption about whether it is NV12 format to guess colorType,
+ // actually use channel information here.
+ if (!validate_backend_texture(ctx, yuvaTexturesCopy[i], &yuvaTexturesCopy[i].fConfig,
+ ct, kUnpremul_SkAlphaType, nullptr)) {
+ return nullptr;
+ }
+ }
+
+ // TODO: Check that for each plane, the channel actually exist in the image source we are
+ // reading from.
+ }
+
+ sk_sp<GrTextureProxy> tempTextureProxies[4]; // build from yuvaTextures
+ for (int i = 0; i < 4; ++i) {
+ // Fill in tempTextureProxies to avoid duplicate texture proxies.
+ int textureIndex = yuvaIndices[i].fIndex;
+
+ // Safely ignore since this means we are missing the A plane.
+ if (textureIndex == -1) {
+ SkASSERT(3 == i);
+ continue;
+ }
+
+ if (!tempTextureProxies[textureIndex]) {
+ SkASSERT(yuvaTexturesCopy[textureIndex].isValid());
+ tempTextureProxies[textureIndex] =
+ proxyProvider->wrapBackendTexture(yuvaTexturesCopy[textureIndex], origin);
+ }
+ }
+ if (!tempTextureProxies[yuvaIndices[0].fIndex] || !tempTextureProxies[yuvaIndices[1].fIndex] ||
+ !tempTextureProxies[yuvaIndices[2].fIndex]) {
+ return nullptr;
+ }
+
+ return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), kNeedNewImageUniqueID, colorSpace,
+ tempTextureProxies, yuvaIndices, size, origin,
+ imageColorSpace, SkBudgeted::kYes);
+}
+