Create an SkColorSpaceXform image generator
This should be immediately useful in the Skia-Android
rendering pipeline.
Possible future uses include creating a "renderable"
SkImage from a bitmap with a funny color space.
Inspired by:
https://skia-review.googlesource.com/c/13981/
Bug: b/62347704
Change-Id: I388c7af1fc43834b8ad22022d0caf3ac90b734c8
Reviewed-on: https://skia-review.googlesource.com/18598
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Matt Sarett <msarett@google.com>
diff --git a/gm/xform_image_gen.cpp b/gm/xform_image_gen.cpp
new file mode 100644
index 0000000..69a1ce1
--- /dev/null
+++ b/gm/xform_image_gen.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkColorSpaceXformImageGenerator.h"
+
+class ColorXformImageGenGM : public skiagm::GM {
+public:
+ ColorXformImageGenGM() {}
+
+protected:
+
+ SkString onShortName() override {
+ return SkString("color_xform_image_gen");
+ }
+
+ SkISize onISize() override {
+ return SkISize::Make(100, 100);
+ }
+
+ void onDraw(SkCanvas* canvas) override {
+ SkBitmap bitmap;
+ SkImageInfo info =
+ SkImageInfo::MakeN32(100, 100, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB());
+ bitmap.allocPixels(info);
+ bitmap.eraseColor(SK_ColorRED);
+ bitmap.eraseArea(SkIRect::MakeWH(25, 25), SK_ColorBLUE); // We should not see any blue.
+
+ std::unique_ptr<SkImageGenerator> gen = SkColorSpaceXformImageGenerator::Make(
+ bitmap,
+ SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+ SkColorSpace::kRec2020_Gamut),
+ kNever_SkCopyPixelsMode);
+
+ SkIRect subset = SkIRect::MakeXYWH(25, 25, 50, 50);
+ sk_sp<SkImage> image = SkImage::MakeFromGenerator(std::move(gen), &subset);
+ canvas->drawImage(image, 25, 25);
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+DEF_GM( return new ColorXformImageGenGM(); )
diff --git a/gn/core.gni b/gn/core.gni
index 21f3c20..0ffff84 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -84,6 +84,8 @@
"$_src/core/SkColorSpaceXformCanvas.cpp",
"$_src/core/SkColorSpaceXformer.cpp",
"$_src/core/SkColorSpaceXformer.h",
+ "$_src/core/SkColorSpaceXformImageGenerator.cpp",
+ "$_src/core/SkColorSpaceXformImageGenerator.h",
"$_src/core/SkColorSpaceXform_A2B.cpp",
"$_src/core/SkColorSpaceXform_A2B.h",
"$_src/core/SkColorTable.cpp",
diff --git a/gn/gm.gni b/gn/gm.gni
index cd7985a..c28504e 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -319,5 +319,6 @@
"$_gm/xfermodes.cpp",
"$_gm/xfermodes2.cpp",
"$_gm/xfermodes3.cpp",
+ "$_gm/xform_image_gen.cpp",
"$_gm/yuvtorgbeffect.cpp",
]
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index 764139b..8b8942f 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -571,7 +571,12 @@
* - If the src pixels are not available.
*/
bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
- int srcX, int srcY) const;
+ int srcX, int srcY, SkTransferFunctionBehavior behavior) const;
+ bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
+ int srcX, int srcY) const {
+ return this->readPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY,
+ SkTransferFunctionBehavior::kRespect);
+ }
bool readPixels(const SkPixmap& dst, int srcX, int srcY) const;
bool readPixels(const SkPixmap& dst) const {
return this->readPixels(dst, 0, 0);
@@ -589,6 +594,7 @@
bool writePixels(const SkPixmap& src) {
return this->writePixels(src, 0, 0);
}
+ bool writePixels(const SkPixmap& src, int x, int y, SkTransferFunctionBehavior behavior);
#ifdef SK_BUILD_FOR_ANDROID
bool hasHardwareMipMap() const {
@@ -684,9 +690,6 @@
uint32_t fRowBytes;
uint8_t fFlags;
- friend class SkImage_Raster;
- bool writePixels(const SkPixmap& src, int x, int y, SkTransferFunctionBehavior behavior);
-
/* Unreference any pixelrefs or colortables
*/
void freePixels();
diff --git a/include/core/SkPixmap.h b/include/core/SkPixmap.h
index af8618b..1e79bba 100644
--- a/include/core/SkPixmap.h
+++ b/include/core/SkPixmap.h
@@ -183,10 +183,15 @@
// copy methods
bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
- int srcX, int srcY) const;
+ int srcX, int srcY, SkTransferFunctionBehavior behavior) const;
bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes) const {
return this->readPixels(dstInfo, dstPixels, dstRowBytes, 0, 0);
}
+ bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, int srcX,
+ int srcY) const {
+ return this->readPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY,
+ SkTransferFunctionBehavior::kRespect);
+ }
bool readPixels(const SkPixmap& dst, int srcX, int srcY) const {
return this->readPixels(dst.info(), dst.writable_addr(), dst.rowBytes(), srcX, srcY);
}
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 72cac7c..e88fa5e 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -512,12 +512,12 @@
///////////////////////////////////////////////////////////////////////////////
bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
- int x, int y) const {
+ int x, int y, SkTransferFunctionBehavior behavior) const {
SkPixmap src;
if (!this->peekPixels(&src)) {
return false;
}
- return src.readPixels(requestedDstInfo, dstPixels, dstRB, x, y);
+ return src.readPixels(requestedDstInfo, dstPixels, dstRB, x, y, behavior);
}
bool SkBitmap::readPixels(const SkPixmap& dst, int srcX, int srcY) const {
diff --git a/src/core/SkColorSpaceXformImageGenerator.cpp b/src/core/SkColorSpaceXformImageGenerator.cpp
new file mode 100644
index 0000000..d9bec78
--- /dev/null
+++ b/src/core/SkColorSpaceXformImageGenerator.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorSpaceXformImageGenerator.h"
+
+std::unique_ptr<SkImageGenerator> SkColorSpaceXformImageGenerator::Make(
+ const SkBitmap& src, sk_sp<SkColorSpace> dst, SkCopyPixelsMode mode) {
+ if (!dst) {
+ return nullptr;
+ }
+
+ const SkBitmap* srcPtr = &src;
+ SkBitmap copy;
+ if (kAlways_SkCopyPixelsMode == mode ||
+ (kNever_SkCopyPixelsMode != mode && !src.isImmutable())) {
+ if (!copy.tryAllocPixels(src.info())) {
+ return nullptr;
+ }
+
+ SkAssertResult(src.readPixels(copy.info(), copy.getPixels(), copy.rowBytes(), 0, 0));
+ copy.setImmutable();
+ srcPtr = ©
+ }
+
+
+ return std::unique_ptr<SkImageGenerator>(
+ new SkColorSpaceXformImageGenerator(*srcPtr, std::move(dst)));
+}
+
+SkColorSpaceXformImageGenerator::SkColorSpaceXformImageGenerator(const SkBitmap& src,
+ sk_sp<SkColorSpace> dst)
+ : INHERITED(src.info().makeColorSpace(dst), kNeedNewImageUniqueID)
+ , fSrc(src)
+ , fDst(dst)
+{}
+
+bool SkColorSpaceXformImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels,
+ size_t rowBytes, const Options& opts) {
+ SkImageInfo dstInfo = info;
+ if (!info.colorSpace()) {
+ dstInfo = dstInfo.makeColorSpace(fDst);
+ }
+ return fSrc.readPixels(dstInfo, pixels, rowBytes, 0, 0, opts.fBehavior);
+}
+
+#if SK_SUPPORT_GPU
+
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrPaint.h"
+#include "GrRenderTargetContext.h"
+#include "GrTextureProxy.h"
+#include "SkGr.h"
+#include "effects/GrNonlinearColorSpaceXformEffect.h"
+
+sk_sp<GrTextureProxy> SkColorSpaceXformImageGenerator::onGenerateTexture(GrContext* ctx,
+ const SkImageInfo& info,
+ const SkIPoint& origin) {
+ // FIXME:
+ // This always operates as if SkTranferFunctionBehavior is kIgnore. Should we add
+ // options so that caller can also request kRespect?
+
+ SkASSERT(ctx);
+
+ sk_sp<GrTextureProxy> proxy =
+ GrUploadBitmapToTextureProxy(ctx->resourceProvider(), fSrc, nullptr);
+
+ sk_sp<SkColorSpace> srcSpace =
+ fSrc.colorSpace() ? sk_ref_sp(fSrc.colorSpace()) : SkColorSpace::MakeSRGB();
+ auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), fDst.get());
+ if (!xform) {
+ return nullptr;
+ }
+
+ sk_sp<GrRenderTargetContext> renderTargetContext = ctx->makeDeferredRenderTargetContext(
+ SkBackingFit::kExact, fSrc.width(), fSrc.height(), kRGBA_8888_GrPixelConfig, nullptr);
+ if (!renderTargetContext) {
+ return nullptr;
+ }
+
+ GrPaint paint;
+ paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+ paint.addColorTextureProcessor(ctx->resourceProvider(), proxy, nullptr,
+ SkMatrix::MakeTrans(origin.fX, origin.fY));
+ paint.addColorFragmentProcessor(std::move(xform));
+
+ const SkRect rect = SkRect::MakeWH(info.width(), info.height());
+ renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
+ return sk_ref_sp(renderTargetContext->asTextureProxy());
+}
+
+#endif
diff --git a/src/core/SkColorSpaceXformImageGenerator.h b/src/core/SkColorSpaceXformImageGenerator.h
new file mode 100644
index 0000000..29bf85b
--- /dev/null
+++ b/src/core/SkColorSpaceXformImageGenerator.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkColorSpaceXformImageGenerator_DEFINED
+#define SkColorSpaceXformImageGenerator_DEFINED
+
+#include "SkImageGenerator.h"
+#include "SkImagePriv.h"
+
+class SkColorSpaceXformImageGenerator : public SkImageGenerator {
+public:
+
+ static std::unique_ptr<SkImageGenerator> Make(
+ const SkBitmap& src, sk_sp<SkColorSpace> dst, SkCopyPixelsMode);
+
+protected:
+ bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+ const Options& opts) override;
+
+#if SK_SUPPORT_GPU
+ sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
+ const SkIPoint&) override;
+#endif
+
+private:
+ SkBitmap fSrc;
+ sk_sp<SkColorSpace> fDst;
+
+ SkColorSpaceXformImageGenerator(const SkBitmap& src, sk_sp<SkColorSpace> dst);
+
+ friend class SkImageGenerator;
+
+ typedef SkImageGenerator INHERITED;
+};
+
+#endif // SkColorSpaceXformImageGenerator_DEFINED
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 02090b7..6425b29 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -74,8 +74,8 @@
return true;
}
-bool SkPixmap::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, int x, int y)
-const {
+bool SkPixmap::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, int x, int y,
+ SkTransferFunctionBehavior behavior) const {
if (!SkImageInfoValidConversion(dstInfo, fInfo)) {
return false;
}
@@ -88,7 +88,7 @@
const void* srcPixels = this->addr(rec.fX, rec.fY);
const SkImageInfo srcInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
SkConvertPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, srcInfo, srcPixels, this->rowBytes(),
- this->ctable(), SkTransferFunctionBehavior::kRespect);
+ this->ctable(), behavior);
return true;
}