aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2017-03-14 12:07:12 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-03-15 12:52:03 +0000
commit63954c9944a5eed4527e0ed368f4e501faf2c625 (patch)
treee37b8575d61b68931c365e3e56c5122e95fecacb /src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
parente8b508556cdd1b18b7461301b35e8a20d3fe35e2 (diff)
GPU version of onMakeColorSpace
New fragment processor that implements end-to-end color space conversion, with nonlinear blending. BUG=skia:6242 Change-Id: Ied86170fc28537a2bc209d57530d3ded48b467a9 Reviewed-on: https://skia-review.googlesource.com/9543 Reviewed-by: Brian Salomon <bsalomon@google.com> Reviewed-by: Matt Sarett <msarett@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
Diffstat (limited to 'src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp')
-rw-r--r--src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp b/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
new file mode 100644
index 0000000000..d4c0a392d6
--- /dev/null
+++ b/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
@@ -0,0 +1,244 @@
+/*
+ * 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 "GrNonlinearColorSpaceXformEffect.h"
+
+#include "GrProcessor.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+#include "SkColorSpace_Base.h"
+
+class GrGLNonlinearColorSpaceXformEffect : public GrGLSLFragmentProcessor {
+public:
+ void emitCode(EmitArgs& args) override {
+ const GrNonlinearColorSpaceXformEffect& csxe =
+ args.fFp.cast<GrNonlinearColorSpaceXformEffect>();
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+
+ const char* srcCoeffsName = nullptr;
+ if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
+ fSrcTransferFnUni = uniformHandler->addUniformArray(
+ kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
+ "SrcTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
+ &srcCoeffsName);
+ }
+
+ const char* dstCoeffsName = nullptr;
+ if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
+ fDstTransferFnUni = uniformHandler->addUniformArray(
+ kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
+ "DstTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
+ &dstCoeffsName);
+ }
+
+ const char* gamutXformName = nullptr;
+ if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
+ fGamutXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType,
+ kDefault_GrSLPrecision, "GamutXform",
+ &gamutXformName);
+ }
+
+ // Helper function to apply a transfer function to a single value
+ SkString tfFuncNameString;
+ static const GrShaderVar gTransferFnFuncArgs[] = {
+ GrShaderVar("x", kFloat_GrSLType),
+ GrShaderVar("coeffs", kFloat_GrSLType,
+ GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs),
+ };
+ SkString transferFnBody;
+ // Temporaries to make evaluation line readable
+ transferFnBody.printf("float A = coeffs[0];");
+ transferFnBody.append("float B = coeffs[1];");
+ transferFnBody.append("float C = coeffs[2];");
+ transferFnBody.append("float D = coeffs[3];");
+ transferFnBody.append("float E = coeffs[4];");
+ transferFnBody.append("float F = coeffs[5];");
+ transferFnBody.append("float G = coeffs[6];");
+ transferFnBody.appendf("return (x < D) ? (C * x) + F : pow(A * x + B, G) + E;");
+ fragBuilder->emitFunction(kFloat_GrSLType, "transfer_fn",
+ SK_ARRAY_COUNT(gTransferFnFuncArgs), gTransferFnFuncArgs,
+ transferFnBody.c_str(), &tfFuncNameString);
+ const char* tfFuncName = tfFuncNameString.c_str();
+
+ if (nullptr == args.fInputColor) {
+ args.fInputColor = "vec4(1)";
+ }
+ fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
+
+ // 1: Un-premultiply the input color (if necessary)
+ fragBuilder->codeAppendf("float nonZeroAlpha = max(color.a, 0.00001);");
+ fragBuilder->codeAppendf("color = vec4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
+
+ // 2: Apply src transfer function (to get to linear RGB)
+ if (srcCoeffsName) {
+ fragBuilder->codeAppendf("color.r = %s(color.r, %s);", tfFuncName, srcCoeffsName);
+ fragBuilder->codeAppendf("color.g = %s(color.g, %s);", tfFuncName, srcCoeffsName);
+ fragBuilder->codeAppendf("color.b = %s(color.b, %s);", tfFuncName, srcCoeffsName);
+ }
+
+ // 3: Apply gamut matrix
+ if (gamutXformName) {
+ // Color is unpremultiplied at this point, so clamp to [0, 1]
+ fragBuilder->codeAppendf(
+ "color.rgb = clamp((%s * vec4(color.rgb, 1.0)).rgb, 0.0, 1.0);", gamutXformName);
+ }
+
+ // 4: Apply dst transfer fn
+ if (dstCoeffsName) {
+ fragBuilder->codeAppendf("color.r = %s(color.r, %s);", tfFuncName, dstCoeffsName);
+ fragBuilder->codeAppendf("color.g = %s(color.g, %s);", tfFuncName, dstCoeffsName);
+ fragBuilder->codeAppendf("color.b = %s(color.b, %s);", tfFuncName, dstCoeffsName);
+ }
+
+ // 5: Premultiply again
+ fragBuilder->codeAppendf("%s = vec4(color.rgb * color.a, color.a);", args.fOutputColor);
+ }
+
+ static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
+ GrProcessorKeyBuilder* b) {
+ const GrNonlinearColorSpaceXformEffect& csxe =
+ processor.cast<GrNonlinearColorSpaceXformEffect>();
+ b->add32(csxe.ops());
+ }
+
+protected:
+ void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& processor) override {
+ const GrNonlinearColorSpaceXformEffect& csxe =
+ processor.cast<GrNonlinearColorSpaceXformEffect>();
+ if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
+ pdman.set1fv(fSrcTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
+ csxe.srcTransferFnCoeffs());
+ }
+ if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
+ pdman.set1fv(fDstTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
+ csxe.dstTransferFnCoeffs());
+ }
+ if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
+ pdman.setSkMatrix44(fGamutXformUni, csxe.gamutXform());
+ }
+ }
+
+private:
+ GrGLSLProgramDataManager::UniformHandle fSrcTransferFnUni;
+ GrGLSLProgramDataManager::UniformHandle fDstTransferFnUni;
+ GrGLSLProgramDataManager::UniformHandle fGamutXformUni;
+
+ typedef GrGLSLFragmentProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect(
+ uint32_t ops, const SkColorSpaceTransferFn& srcTransferFn,
+ const SkColorSpaceTransferFn& dstTransferFn, const SkMatrix44& gamutXform)
+ : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
+ , fGamutXform(gamutXform)
+ , fOps(ops) {
+ this->initClassID<GrNonlinearColorSpaceXformEffect>();
+
+ fSrcTransferFnCoeffs[0] = srcTransferFn.fA;
+ fSrcTransferFnCoeffs[1] = srcTransferFn.fB;
+ fSrcTransferFnCoeffs[2] = srcTransferFn.fC;
+ fSrcTransferFnCoeffs[3] = srcTransferFn.fD;
+ fSrcTransferFnCoeffs[4] = srcTransferFn.fE;
+ fSrcTransferFnCoeffs[5] = srcTransferFn.fF;
+ fSrcTransferFnCoeffs[6] = srcTransferFn.fG;
+
+ fDstTransferFnCoeffs[0] = dstTransferFn.fA;
+ fDstTransferFnCoeffs[1] = dstTransferFn.fB;
+ fDstTransferFnCoeffs[2] = dstTransferFn.fC;
+ fDstTransferFnCoeffs[3] = dstTransferFn.fD;
+ fDstTransferFnCoeffs[4] = dstTransferFn.fE;
+ fDstTransferFnCoeffs[5] = dstTransferFn.fF;
+ fDstTransferFnCoeffs[6] = dstTransferFn.fG;
+}
+
+bool GrNonlinearColorSpaceXformEffect::onIsEqual(const GrFragmentProcessor& s) const {
+ const GrNonlinearColorSpaceXformEffect& other = s.cast<GrNonlinearColorSpaceXformEffect>();
+ if (other.fOps != fOps) {
+ return false;
+ }
+ if (SkToBool(fOps & kSrcTransfer_Op) &&
+ memcmp(&other.fSrcTransferFnCoeffs, &fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs))) {
+ return false;
+ }
+ if (SkToBool(fOps & kDstTransfer_Op) &&
+ memcmp(&other.fDstTransferFnCoeffs, &fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs))) {
+ return false;
+ }
+ if (SkToBool(fOps & kGamutXform_Op) && other.fGamutXform != fGamutXform) {
+ return false;
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrNonlinearColorSpaceXformEffect);
+
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::TestCreate(GrProcessorTestData* d) {
+ // TODO: Generate a random variety of color spaces for this effect (it can handle wacky
+ // transfer functions, etc...)
+ sk_sp<SkColorSpace> srcSpace = SkColorSpace::MakeSRGBLinear();
+ sk_sp<SkColorSpace> dstSpace = SkColorSpace::MakeSRGB();
+ return GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), dstSpace.get());
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrNonlinearColorSpaceXformEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+ GrProcessorKeyBuilder* b) const {
+ GrGLNonlinearColorSpaceXformEffect::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* GrNonlinearColorSpaceXformEffect::onCreateGLSLInstance() const {
+ return new GrGLNonlinearColorSpaceXformEffect();
+}
+
+sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::Make(const SkColorSpace* src,
+ const SkColorSpace* dst) {
+ if (!src || !dst || SkColorSpace::Equals(src, dst)) {
+ // No conversion possible (or necessary)
+ return nullptr;
+ }
+
+ uint32_t ops = 0;
+
+ // We rely on GrColorSpaceXform to build the gamut xform matrix for us (to get caching)
+ auto gamutXform = GrColorSpaceXform::Make(src, dst);
+ SkMatrix44 srcToDstMtx(SkMatrix44::kUninitialized_Constructor);
+ if (gamutXform) {
+ ops |= kGamutXform_Op;
+ srcToDstMtx = gamutXform->srcToDst();
+ }
+
+ SkColorSpaceTransferFn srcTransferFn;
+ if (!src->gammaIsLinear()) {
+ if (src->isNumericalTransferFn(&srcTransferFn)) {
+ ops |= kSrcTransfer_Op;
+ } else {
+ return nullptr;
+ }
+ }
+
+ SkColorSpaceTransferFn dstTransferFn;
+ if (!dst->gammaIsLinear()) {
+ if (dst->isNumericalTransferFn(&dstTransferFn)) {
+ dstTransferFn = dstTransferFn.invert();
+ ops |= kDstTransfer_Op;
+ } else {
+ return nullptr;
+ }
+ }
+
+ return sk_sp<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(
+ ops, srcTransferFn, dstTransferFn, srcToDstMtx));
+}