From 9ad0531a18f854e5a2c8034880140dd6cd3ea3c1 Mon Sep 17 00:00:00 2001 From: Matt Sarett Date: Wed, 3 May 2017 19:09:46 -0400 Subject: Add SkImage::makeColorSpace() with correct transfer fn behavior Completes implementation for lazy and raster images. gpu is still a TODO. Bug: skia:6553 Change-Id: I04eea5c4fb53c50c0406c2e6b6778b0e21fd85f8 Reviewed-on: https://skia-review.googlesource.com/14403 Commit-Queue: Matt Sarett Reviewed-by: Mike Reed --- src/image/SkImage.cpp | 12 ++++----- src/image/SkImage_Base.h | 3 ++- src/image/SkImage_Gpu.cpp | 8 +++++- src/image/SkImage_Gpu.h | 3 ++- src/image/SkImage_Lazy.cpp | 19 +++++--------- src/image/SkImage_Raster.cpp | 62 ++++++-------------------------------------- 6 files changed, 32 insertions(+), 75 deletions(-) (limited to 'src/image') diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 5debe48b00..cbb9a621db 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -288,11 +288,6 @@ bool SkImage::isAlphaOnly() const { sk_sp SkImage::makeColorSpace(sk_sp target, SkTransferFunctionBehavior premulBehavior) const { - if (SkTransferFunctionBehavior::kRespect == premulBehavior) { - // TODO (msarett, brianosman): Implement this. - return nullptr; - } - SkColorSpaceTransferFn fn; if (!target || !target->isNumericalTransferFn(&fn)) { return nullptr; @@ -307,8 +302,13 @@ sk_sp SkImage::makeColorSpace(sk_sp target, return sk_ref_sp(const_cast(this)); } + SkColorType targetColorType = kN32_SkColorType; + if (SkTransferFunctionBehavior::kRespect == premulBehavior && target->gammaIsLinear()) { + targetColorType = kRGBA_F16_SkColorType; + } + // TODO: We might consider making this a deferred conversion? - return as_IB(this)->onMakeColorSpace(std::move(target)); + return as_IB(this)->onMakeColorSpace(std::move(target), targetColorType, premulBehavior); } ////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h index ce9c5cde52..34f194b61a 100644 --- a/src/image/SkImage_Base.h +++ b/src/image/SkImage_Base.h @@ -85,7 +85,8 @@ public: virtual bool onPinAsTexture(GrContext*) const { return false; } virtual void onUnpinAsTexture(GrContext*) const {} - virtual sk_sp onMakeColorSpace(sk_sp) const = 0; + virtual sk_sp onMakeColorSpace(sk_sp, SkColorType, + SkTransferFunctionBehavior) const = 0; private: // Set true by caches when they cache content that's derived from the current pixels. diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 2c7a194444..581841b1d5 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -905,7 +905,13 @@ sk_sp SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& info.refColorSpace(), budgeted); } -sk_sp SkImage_Gpu::onMakeColorSpace(sk_sp colorSpace) const { +sk_sp SkImage_Gpu::onMakeColorSpace(sk_sp colorSpace, SkColorType, + SkTransferFunctionBehavior premulBehavior) const { + if (SkTransferFunctionBehavior::kRespect == premulBehavior) { + // TODO: Implement this. + return nullptr; + } + sk_sp srcSpace = fColorSpace ? fColorSpace : SkColorSpace::MakeSRGB(); auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), colorSpace.get()); if (!xform) { diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h index b477acc7b5..a644ee25be 100644 --- a/src/image/SkImage_Gpu.h +++ b/src/image/SkImage_Gpu.h @@ -59,7 +59,8 @@ public: GrContext* context() { return fContext; } sk_sp refColorSpace() { return fColorSpace; } - sk_sp onMakeColorSpace(sk_sp) const override; + sk_sp onMakeColorSpace(sk_sp, SkColorType, + SkTransferFunctionBehavior) const override; private: GrContext* fContext; diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp index 215f4c284c..3ee18de9db 100644 --- a/src/image/SkImage_Lazy.cpp +++ b/src/image/SkImage_Lazy.cpp @@ -82,7 +82,8 @@ public: sk_sp onMakeSubset(const SkIRect&) const override; bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override; bool onIsLazyGenerated() const override { return true; } - sk_sp onMakeColorSpace(sk_sp) const override; + sk_sp onMakeColorSpace(sk_sp, SkColorType, + SkTransferFunctionBehavior) const override; SkImageCacherator* peekCacherator() const override { return const_cast(this); @@ -574,20 +575,14 @@ sk_sp SkImage_Lazy::onMakeSubset(const SkIRect& subset) const { return validator ? sk_sp(new SkImage_Lazy(&validator)) : nullptr; } -sk_sp SkImage_Lazy::onMakeColorSpace(sk_sp target) const { +sk_sp SkImage_Lazy::onMakeColorSpace(sk_sp target, + SkColorType targetColorType, + SkTransferFunctionBehavior premulBehavior) const { SkBitmap dst; - SkImageInfo dstInfo = fInfo.makeColorSpace(target); - if (kIndex_8_SkColorType == dstInfo.colorType() || - kGray_8_SkColorType == dstInfo.colorType() || - kRGB_565_SkColorType == dstInfo.colorType()) { - dstInfo = dstInfo.makeColorType(kN32_SkColorType); - } + SkImageInfo dstInfo = fInfo.makeColorType(targetColorType).makeColorSpace(target); dst.allocPixels(dstInfo); - - // Use kIgnore for transfer function behavior. This is used by the SkColorSpaceXformCanvas, - // which wants to pre-xform the inputs but ignore the transfer function on blends. if (!this->directGeneratePixels(dstInfo, dst.getPixels(), dst.rowBytes(), 0, 0, - SkTransferFunctionBehavior::kIgnore)) { + premulBehavior)) { return nullptr; } diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp index 88847551d1..1c3c515a60 100644 --- a/src/image/SkImage_Raster.cpp +++ b/src/image/SkImage_Raster.cpp @@ -112,7 +112,8 @@ public: SkASSERT(bitmapMayBeMutable || fBitmap.isImmutable()); } - sk_sp onMakeColorSpace(sk_sp) const override; + sk_sp onMakeColorSpace(sk_sp, SkColorType, + SkTransferFunctionBehavior) const override; #if SK_SUPPORT_GPU sk_sp refPinnedTextureProxy(uint32_t* uniqueID) const override; @@ -342,69 +343,22 @@ bool SkImage_Raster::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) c /////////////////////////////////////////////////////////////////////////////// -static inline void do_color_xform_non_linear_blending(SkBitmap* dst, const SkPixmap& src) { - SkDEBUGCODE(SkColorSpaceTransferFn fn;); - SkASSERT(dst->colorSpace()->isNumericalTransferFn(&fn) && - src.colorSpace()->isNumericalTransferFn(&fn)); - - void* dstPixels = dst->getPixels(); - const void* srcPixels = src.addr(); - size_t dstRowBytes = dst->rowBytes(); - size_t srcRowBytes = src.rowBytes(); - if (kN32_SkColorType != src.colorType()) { - SkAssertResult(src.readPixels(src.info().makeColorType(kN32_SkColorType), dstPixels, - dstRowBytes, 0, 0)); - - srcPixels = dstPixels; - srcRowBytes = dstRowBytes; - } - - std::unique_ptr xform = SkColorSpaceXform_Base::New( - src.colorSpace(), dst->colorSpace(), SkTransferFunctionBehavior::kIgnore); - - void* dstRow = dstPixels; - const void* srcRow = srcPixels; - for (int y = 0; y < dst->height(); y++) { - // This function assumes non-linear blending. Which means that we must start by - // unpremultiplying in the gamma encoded space. - const void* tmpRow = srcRow; - if (kPremul_SkAlphaType == src.alphaType()) { - SkUnpremultiplyRow((uint32_t*) dstRow, (const uint32_t*) srcRow, dst->width()); - tmpRow = dstRow; - } - - SkColorSpaceXform::ColorFormat fmt = select_xform_format(kN32_SkColorType); - SkAssertResult(xform->apply(fmt, dstRow, fmt, tmpRow, dst->width(), dst->alphaType())); - - dstRow = SkTAddOffset(dstRow, dstRowBytes); - srcRow = SkTAddOffset(srcRow, srcRowBytes); - } -} - -sk_sp SkImage_Raster::onMakeColorSpace(sk_sp target) const { - // Force the color type of the new image to be kN32_SkColorType. - // (1) This means we lose precision on F16 images. This is necessary while this function is - // used to pre-transform inputs to a legacy canvas. Legacy canvases do not handle F16. - // (2) kIndex8 and kGray8 must be expanded in order perform a color space transformation. - // (3) Seems reasonable to expand k565 and k4444. It's nice to avoid these color types for - // clients who opt into color space support. - SkImageInfo dstInfo = fBitmap.info().makeColorType(kN32_SkColorType).makeColorSpace(target); +sk_sp SkImage_Raster::onMakeColorSpace(sk_sp target, + SkColorType targetColorType, + SkTransferFunctionBehavior premulBehavior) const { + SkImageInfo dstInfo = fBitmap.info().makeColorType(targetColorType).makeColorSpace(target); SkBitmap dst; dst.allocPixels(dstInfo); SkPixmap src; - SkTLazy tmp; - if (!fBitmap.peekPixels(&src)) { - tmp.init(fBitmap); - SkAssertResult(tmp.get()->peekPixels(&src)); - } + SkAssertResult(fBitmap.peekPixels(&src)); // Treat nullptr srcs as sRGB. if (!src.colorSpace()) { src.setColorSpace(SkColorSpace::MakeSRGB()); } - do_color_xform_non_linear_blending(&dst, src); + SkAssertResult(dst.writePixels(src)); dst.setImmutable(); return SkImage::MakeFromBitmap(dst); } -- cgit v1.2.3