From 2232b9ed3998110ed613ae01f3637d2ce7317f05 Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Thu, 10 Jan 2019 17:03:25 -0500 Subject: [PATCH] Add target SkColorSpace to SkImage_GpuYUVA. Bug: skia:8868 Change-Id: I91f58bf88aec14c17ea904adca792e20099c36eb Reviewed-on: https://skia-review.googlesource.com/c/182816 Commit-Queue: Jim Van Verth Reviewed-by: Brian Osman --- gm/wacky_yuv_formats.cpp | 26 ++++++++++++++++---- src/gpu/GrImageTextureMaker.cpp | 3 +++ src/gpu/GrImageTextureMaker.h | 1 + src/gpu/GrTextureProducer.h | 1 + src/gpu/SkGpuDevice_drawTexture.cpp | 5 +++- src/image/SkImage_Gpu.cpp | 36 +++++++++++++++++++++++++++ src/image/SkImage_Gpu.h | 2 ++ src/image/SkImage_GpuBase.cpp | 36 --------------------------- src/image/SkImage_GpuBase.h | 2 -- src/image/SkImage_GpuYUVA.cpp | 38 +++++++++++++++++++++++++++++ src/image/SkImage_GpuYUVA.h | 12 +++++++++ 11 files changed, 118 insertions(+), 44 deletions(-) diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp index 0e4abc7bdf..155ec6d62b 100644 --- a/gm/wacky_yuv_formats.cpp +++ b/gm/wacky_yuv_formats.cpp @@ -719,14 +719,18 @@ namespace skiagm { // YV12 class WackyYUVFormatsGM : public GM { public: - WackyYUVFormatsGM() { + WackyYUVFormatsGM(bool useTargetColorSpace) : fUseTargetColorSpace(useTargetColorSpace) { this->setBGColor(0xFFCCCCCC); } protected: SkString onShortName() override { - return SkString("wacky_yuv_formats"); + SkString name("wacky_yuv_formats"); + if (fUseTargetColorSpace) { + name += "_cs"; + } + return name; } SkISize onISize() override { @@ -754,6 +758,10 @@ protected: SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles); fOriginalBMs[1] = make_bitmap(path, circles, true); } + + if (fUseTargetColorSpace) { + fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin(); + } } void createImages(GrContext* context) { @@ -850,8 +858,13 @@ protected: for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) { draw_row_label(canvas, y, format); - canvas->drawImage(fImages[opaque][cs][format], x, y); - + if (fUseTargetColorSpace) { + sk_sp csImage = + fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace); + canvas->drawImage(csImage, x, y); + } else { + canvas->drawImage(fImages[opaque][cs][format], x, y); + } y += kTileWidthHeight + kPad; } @@ -863,11 +876,14 @@ protected: private: SkBitmap fOriginalBMs[2]; sk_sp fImages[2][kLastEnum_SkYUVColorSpace+1][kLast_YUVFormat+1]; + bool fUseTargetColorSpace; + sk_sp fTargetColorSpace; typedef GM INHERITED; }; ////////////////////////////////////////////////////////////////////////////// -DEF_GM(return new WackyYUVFormatsGM;) +DEF_GM(return new WackyYUVFormatsGM(false);) +DEF_GM(return new WackyYUVFormatsGM(true);) } diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp index 567e91e463..fa87037d2a 100644 --- a/src/gpu/GrImageTextureMaker.cpp +++ b/src/gpu/GrImageTextureMaker.cpp @@ -81,6 +81,9 @@ SkAlphaType GrYUVAImageTextureMaker::alphaType() const { SkColorSpace* GrYUVAImageTextureMaker::colorSpace() const { return fImage->colorSpace(); } +SkColorSpace* GrYUVAImageTextureMaker::targetColorSpace() const { + return fImage->targetColorSpace(); +} std::unique_ptr GrYUVAImageTextureMaker::createFragmentProcessor( const SkMatrix& textureMatrix, diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h index 544705fff1..8024579505 100644 --- a/src/gpu/GrImageTextureMaker.h +++ b/src/gpu/GrImageTextureMaker.h @@ -65,6 +65,7 @@ protected: SkAlphaType alphaType() const override; SkColorSpace* colorSpace() const override; + SkColorSpace* targetColorSpace() const override; private: const SkImage_GpuYUVA* fImage; diff --git a/src/gpu/GrTextureProducer.h b/src/gpu/GrTextureProducer.h index 1994f8b3e1..68c1b47fb6 100644 --- a/src/gpu/GrTextureProducer.h +++ b/src/gpu/GrTextureProducer.h @@ -109,6 +109,7 @@ public: bool isAlphaOnly() const { return fIsAlphaOnly; } virtual SkAlphaType alphaType() const = 0; virtual SkColorSpace* colorSpace() const = 0; + virtual SkColorSpace* targetColorSpace() const { return nullptr; } protected: friend class GrTextureProducer_TestAccess; diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp index 3867465d46..a891c03d4a 100644 --- a/src/gpu/SkGpuDevice_drawTexture.cpp +++ b/src/gpu/SkGpuDevice_drawTexture.cpp @@ -289,8 +289,11 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, } auto fp = producer->createFragmentProcessor(*textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode); + SkColorSpace* rtColorSpace = fRenderTargetContext->colorSpaceInfo().colorSpace(); + SkColorSpace* targetColorSpace = producer->targetColorSpace(); + SkColorSpace* dstColorSpace = SkToBool(rtColorSpace) ? rtColorSpace : targetColorSpace; fp = GrColorSpaceXformEffect::Make(std::move(fp), producer->colorSpace(), producer->alphaType(), - fRenderTargetContext->colorSpaceInfo().colorSpace()); + dstColorSpace); if (!fp) { return; } diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 5de8aa95b6..9033780900 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -60,6 +60,42 @@ SkImageInfo SkImage_Gpu::onImageInfo() const { return SkImageInfo::Make(fProxy->width(), fProxy->height(), colorType, fAlphaType, fColorSpace); } +sk_sp SkImage_Gpu::onMakeColorSpace(sk_sp target) const { + auto xform = GrColorSpaceXformEffect::Make(fColorSpace.get(), fAlphaType, + target.get(), fAlphaType); + SkASSERT(xform); + + sk_sp proxy = this->asTextureProxyRef(); + + GrBackendFormat format = proxy->backendFormat().makeTexture2D(); + if (!format.isValid()) { + return nullptr; + } + + sk_sp renderTargetContext( + fContext->contextPriv().makeDeferredRenderTargetContextWithFallback( + format, SkBackingFit::kExact, this->width(), this->height(), + proxy->config(), nullptr)); + if (!renderTargetContext) { + return nullptr; + } + + GrPaint paint; + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + paint.addColorTextureProcessor(std::move(proxy), SkMatrix::I()); + paint.addColorFragmentProcessor(std::move(xform)); + + renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), + SkRect::MakeIWH(this->width(), this->height())); + if (!renderTargetContext->asTextureProxy()) { + return nullptr; + } + + // MDB: this call is okay bc we know 'renderTargetContext' was exact + return sk_make_sp(fContext, kNeedNewImageUniqueID, fAlphaType, + renderTargetContext->asTextureProxyRef(), std::move(target)); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// static sk_sp new_wrapped_texture_common(GrContext* ctx, diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h index 1f825391d3..42ba0a57a1 100644 --- a/src/image/SkImage_Gpu.h +++ b/src/image/SkImage_Gpu.h @@ -37,6 +37,8 @@ public: virtual bool onIsTextureBacked() const override { return SkToBool(fProxy.get()); } + sk_sp onMakeColorSpace(sk_sp) const final; + /** Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The main difference is that the client doesn't have the backend texture on the gpu yet but they know diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp index 73aecf4414..047b1a22e1 100644 --- a/src/image/SkImage_GpuBase.cpp +++ b/src/image/SkImage_GpuBase.cpp @@ -256,42 +256,6 @@ GrTexture* SkImage_GpuBase::onGetTexture() const { return proxy->peekTexture(); } -sk_sp SkImage_GpuBase::onMakeColorSpace(sk_sp target) const { - auto xform = GrColorSpaceXformEffect::Make(fColorSpace.get(), fAlphaType, - target.get(), fAlphaType); - SkASSERT(xform); - - sk_sp proxy = this->asTextureProxyRef(); - - GrBackendFormat format = proxy->backendFormat().makeTexture2D(); - if (!format.isValid()) { - return nullptr; - } - - sk_sp renderTargetContext( - fContext->contextPriv().makeDeferredRenderTargetContextWithFallback( - format, SkBackingFit::kExact, this->width(), this->height(), - proxy->config(), nullptr)); - if (!renderTargetContext) { - return nullptr; - } - - GrPaint paint; - paint.setPorterDuffXPFactory(SkBlendMode::kSrc); - paint.addColorTextureProcessor(std::move(proxy), SkMatrix::I()); - paint.addColorFragmentProcessor(std::move(xform)); - - renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), - SkRect::MakeIWH(this->width(), this->height())); - if (!renderTargetContext->asTextureProxy()) { - return nullptr; - } - - // MDB: this call is okay bc we know 'renderTargetContext' was exact - return sk_make_sp(fContext, kNeedNewImageUniqueID, fAlphaType, - renderTargetContext->asTextureProxyRef(), std::move(target)); -} - 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()) { diff --git a/src/image/SkImage_GpuBase.h b/src/image/SkImage_GpuBase.h index 5be97c8d91..59fce5eaea 100644 --- a/src/image/SkImage_GpuBase.h +++ b/src/image/SkImage_GpuBase.h @@ -49,8 +49,6 @@ public: GrTexture* onGetTexture() const final; - sk_sp onMakeColorSpace(sk_sp) const final; - bool onIsValid(GrContext*) const final; #if GR_TEST_UTILS diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp index 340f1ef695..55411cc935 100644 --- a/src/image/SkImage_GpuYUVA.cpp +++ b/src/image/SkImage_GpuYUVA.cpp @@ -48,6 +48,28 @@ SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp context, int width, int height memcpy(fYUVAIndices, yuvaIndices, 4*sizeof(SkYUVAIndex)); } +// For onMakeColorSpace() +SkImage_GpuYUVA::SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp targetCS) + : INHERITED(image->fContext, image->width(), image->height(), kNeedNewImageUniqueID, + // 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. + GetAlphaTypeFromYUVAIndices(image->fYUVAIndices), image->fColorSpace) + , fNumProxies(image->fNumProxies) + , fYUVColorSpace(image->fYUVColorSpace) + , fOrigin(image->fOrigin) + , fTargetColorSpace(targetCS) { + // The caller should have done this work, just verifying + SkDEBUGCODE(int textureCount;) + SkASSERT(SkYUVAIndex::AreValidIndices(image->fYUVAIndices, &textureCount)); + SkASSERT(textureCount == fNumProxies); + + for (int i = 0; i < fNumProxies; ++i) { + fProxies[i] = image->fProxies[i]; // we ref in this case, not move + } + memcpy(fYUVAIndices, image->fYUVAIndices, 4 * sizeof(SkYUVAIndex)); +} + SkImage_GpuYUVA::~SkImage_GpuYUVA() {} SkImageInfo SkImage_GpuYUVA::onImageInfo() const { @@ -121,6 +143,22 @@ sk_sp SkImage_GpuYUVA::asMippedTextureProxyRef() const { ////////////////////////////////////////////////////////////////////////////////////////////////// +sk_sp SkImage_GpuYUVA::onMakeColorSpace(sk_sp target) const { + // we may need a mutex here but for now we expect usage to be in a single thread + if (fOnMakeColorSpaceTarget && + SkColorSpace::Equals(target.get(), fOnMakeColorSpaceTarget.get())) { + return fOnMakeColorSpaceResult; + } + sk_sp result = sk_sp(new SkImage_GpuYUVA(this, target)); + if (result) { + fOnMakeColorSpaceTarget = target; + fOnMakeColorSpaceResult = result; + } + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + sk_sp SkImage::MakeFromYUVATextures(GrContext* ctx, SkYUVColorSpace colorSpace, const GrBackendTexture yuvaTextures[], diff --git a/src/image/SkImage_GpuYUVA.h b/src/image/SkImage_GpuYUVA.h index 1a6ab93e1a..c8b961613c 100644 --- a/src/image/SkImage_GpuYUVA.h +++ b/src/image/SkImage_GpuYUVA.h @@ -36,6 +36,8 @@ public: virtual bool onIsTextureBacked() const override { return SkToBool(fProxies[0].get()); } + sk_sp onMakeColorSpace(sk_sp) const final; + virtual bool isYUVA() const override { return true; } virtual bool asYUVATextureProxiesRef(sk_sp proxies[4], SkYUVAIndex yuvaIndices[4], @@ -53,6 +55,8 @@ public: // Returns a ref-ed texture proxy with miplevels sk_sp asMippedTextureProxyRef() const; + SkColorSpace* targetColorSpace() const { return fTargetColorSpace.get(); } + /** Create a new SkImage_GpuYUVA that's very similar to SkImage created by MakeFromYUVATextures. The main difference is that the client doesn't have the backend textures on the gpu yet but @@ -109,6 +113,8 @@ public: PromiseImageTextureContext textureContexts[]); private: + SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp); + // This array will usually only be sparsely populated. // The actual non-null fields are dictated by the 'fYUVAIndices' indices mutable sk_sp fProxies[4]; @@ -116,6 +122,12 @@ private: SkYUVAIndex fYUVAIndices[4]; const SkYUVColorSpace fYUVColorSpace; GrSurfaceOrigin fOrigin; + const sk_sp fTargetColorSpace; + + // Repeated calls to onMakeColorSpace will result in a proliferation of unique IDs and + // SkImage_GpuYUVA instances. Cache the result of the last successful onMakeColorSpace call. + mutable sk_sp fOnMakeColorSpaceTarget; + mutable sk_sp fOnMakeColorSpaceResult; // This is only allocated when the image needs to be flattened rather than // using the separate YUVA planes. From thence forth we will only use the