aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gn/core.gni1
-rw-r--r--gn/tests.gni1
-rw-r--r--src/core/SkColorSpaceXformSteps.cpp74
-rw-r--r--src/core/SkColorSpaceXformSteps.h42
-rw-r--r--tests/SkColorSpaceXformStepsTest.cpp150
5 files changed, 268 insertions, 0 deletions
diff --git a/gn/core.gni b/gn/core.gni
index 7d1762f44e..5aca52cc98 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -83,6 +83,7 @@ skia_core_sources = [
"$_src/core/SkColorSpace_ICC.cpp",
"$_src/core/SkColorSpaceXform.cpp",
"$_src/core/SkColorSpaceXformCanvas.cpp",
+ "$_src/core/SkColorSpaceXformSteps.cpp",
"$_src/core/SkColorSpaceXformer.cpp",
"$_src/core/SkColorSpaceXformer.h",
"$_src/core/SkColorTable.cpp",
diff --git a/gn/tests.gni b/gn/tests.gni
index 968b80ebdc..bbab21e76a 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -216,6 +216,7 @@ tests_sources = [
"$_tests/skbug6389.cpp",
"$_tests/skbug6653.cpp",
"$_tests/SkColor4fTest.cpp",
+ "$_tests/SkColorSpaceXformStepsTest.cpp",
"$_tests/SkDOMTest.cpp",
"$_tests/SkFixed15Test.cpp",
"$_tests/SkGaussFilterTest.cpp",
diff --git a/src/core/SkColorSpaceXformSteps.cpp b/src/core/SkColorSpaceXformSteps.cpp
new file mode 100644
index 0000000000..0067a70934
--- /dev/null
+++ b/src/core/SkColorSpaceXformSteps.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "SkColorSpaceXformSteps.h"
+
+// Our logical flow modulo any optimization is:
+// For source colors:
+// 1) get colors into linear, unpremul format
+// 2) transform to dst gamut
+// 3) encode with dst transfer function if dst has non-linear blending
+// 4) premul so we can blend
+//
+// For destination colors:
+// a) Linearize if dst has linear blending
+// b) (Presumably all destinations are already premul, but logically, premul here)
+//
+// Now the source and destination pipelines unify:
+// I) blend the src and dst colors, which are encoded the same way in the same gamut
+// II) if dst has linear blending, encode using dst transfer function
+//
+// Step 1) is represented by three bits:
+// - early_unpremul, converting f(s)*a,a to f(s),a if src has non-linear blending
+// - linearize_src, converting f(s),a to s,a _or_ f(s*a),a to s*a,a
+// - late_unpremul, converting s*a,a to s,a if src has linear blending
+// So we'll either early_unpremul and linearize_src, or linearize_src and late_unpremul.
+//
+
+SkColorSpaceXformSteps::SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType srcAT,
+ SkColorSpace* dst) {
+ // Set all bools to false, all floats to 0.0f.
+ memset(this, 0, sizeof(*this));
+
+ // We have some options about what to do with null src or dst here.
+#if 1
+ SkASSERT(src && dst);
+#else
+ // If either the source or destination color space is unspecified,
+ // treat it as non-linearly-blended sRGB ("legacy").
+ sk_sp<SkColorSpace> sRGB_NL;
+ if (!src || !dst) {
+ sRGB_NL = SkColorSpace::MakeSRGB()->makeNonlinearBlending();
+ if (!src) { src = sRGB_NL.get(); }
+ if (!dst) { dst = sRGB_NL.get(); }
+ }
+#endif
+
+ bool srcNL = src->nonlinearBlending(),
+ srcPM = srcAT == kPremul_SkAlphaType,
+ dstNL = dst->nonlinearBlending();
+
+ // Step 1) get source colors into linear, unpremul format
+ this->early_unpremul = srcNL && srcPM;
+ this->linearize_src = true;
+ this->late_unpremul = !srcNL && srcPM;
+
+ // Step 2) transform source colors into destination gamut
+ this->gamut_transform = true;
+
+ // Step 3) encode with dst transfer function if dst has non-linear blending
+ this->early_encode = dstNL;
+
+ // Step 4) premul so we can blend
+ this->premul = true || !srcPM;
+
+ // Step a) linearize if dst has linear blending
+ this->linearize_dst = !dstNL;
+
+ // Step II) if dst has linear blending, encode back using dst transfer function before storing
+ this->late_encode = !dstNL;
+}
diff --git a/src/core/SkColorSpaceXformSteps.h b/src/core/SkColorSpaceXformSteps.h
new file mode 100644
index 0000000000..03702d813a
--- /dev/null
+++ b/src/core/SkColorSpaceXformSteps.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkColorSpaceXformSteps_DEFINED
+#define SkColorSpaceXformSteps_DEFINED
+
+#include "SkColorSpace.h"
+#include "SkImageInfo.h"
+
+struct SkColorSpaceXformSteps {
+ SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType srcAT,
+ SkColorSpace* dst);
+
+ // Source pipeline steps, pre-blend.
+ bool early_unpremul;
+ bool linearize_src;
+ bool late_unpremul;
+ bool gamut_transform;
+ bool early_encode;
+ bool premul;
+
+ // Destination pipeline steps, pre-blend.
+ bool linearize_dst;
+
+ // Post-blend steps.
+ bool late_encode;
+
+/* TODO
+ SkColorSpaceTransferFn srcTFInv, // Apply for linearize_src.
+ dstTFInv, // Apply for linearize_dst.
+ dstTF; // Apply for early_encode or late_encode.
+ float src_to_dst_matrix[9]; // Apply this 3x3 row-major matrix for gamut_transform.
+*/
+};
+
+
+
+#endif//SkColorSpaceXformSteps_DEFINED
diff --git a/tests/SkColorSpaceXformStepsTest.cpp b/tests/SkColorSpaceXformStepsTest.cpp
new file mode 100644
index 0000000000..47e5e1b90b
--- /dev/null
+++ b/tests/SkColorSpaceXformStepsTest.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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 "SkColorSpacePriv.h"
+#include "SkColorSpaceXformSteps.h"
+#include "Test.h"
+
+template <typename T>
+static void check_eq(skiatest::Reporter* r, SkColorSpaceXformSteps steps, T baseline) {
+ REPORTER_ASSERT(r, steps.early_unpremul == baseline.early_unpremul);
+ REPORTER_ASSERT(r, steps.linearize_src == baseline.linearize_src);
+ REPORTER_ASSERT(r, steps.late_unpremul == baseline.late_unpremul);
+ REPORTER_ASSERT(r, steps.gamut_transform == baseline.gamut_transform);
+ REPORTER_ASSERT(r, steps.early_encode == baseline.early_encode);
+ REPORTER_ASSERT(r, steps.premul == baseline.premul);
+ REPORTER_ASSERT(r, steps.linearize_dst == baseline.linearize_dst);
+ REPORTER_ASSERT(r, steps.late_encode == baseline.late_encode);
+
+ // A couple (redundant) sanity checks that cover impossible states.
+ // At most one of the early/late options should happen, possibly neither.
+ REPORTER_ASSERT(r, !(steps.early_unpremul && steps.late_unpremul));
+ REPORTER_ASSERT(r, !(steps.early_encode && steps.late_encode));
+}
+
+DEF_TEST(SkColorSpaceXformSteps, r) {
+ auto srgb_L = SkColorSpace::MakeSRGB(),
+ adobe_L = SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace::kAdobeRGB_Gamut),
+ srgb22_L = SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace:: kSRGB_Gamut),
+ srgb_N = srgb_L->makeNonlinearBlending(),
+ adobe_N = adobe_L->makeNonlinearBlending(),
+ srgb22_N = srgb22_L->makeNonlinearBlending();
+
+ struct {
+ sk_sp<SkColorSpace> src, dst;
+
+ bool early_unpremul;
+ bool linearize_src;
+ bool late_unpremul;
+
+ bool gamut_transform;
+ bool early_encode;
+ bool premul;
+
+ bool linearize_dst;
+ bool late_encode;
+ } tests[] = {
+ // The first eight cases we test are back and forth between two color spaces with
+ // different gamuts and transfer functions. There's not much optimization possible here.
+
+ { adobe_N, srgb_N,
+ true, // src is encoded as f(s)*a,a, so we unpremul to f(s),a before linearizing.
+ true, // Linearize to s,a.
+ false,
+
+ true, // Gamut transform.
+ true, // Non-linear blending, so we encode to sRGB g(s),a early.
+ true, // Premul to g(s)*a,a
+
+ false, // Non-linear blending, so no need to linearize dst.
+ false, // Non-linear blending, so the output of our blend function is what we want.
+ },
+ { srgb_N, adobe_N, true,true,false, true,true,true, false,false },
+
+ { adobe_L, srgb_L,
+ false, // src is encoded as f(s*a),a, so we linearize before unpremul.
+ true, // Linearize,
+ true, // then unpremul.
+
+ true, // Gamut transform.
+ false, // We're doing linear blending, so we don't encode to sRGB yet.
+ true, // Premul so we can blend.
+
+ true, // We're doing linear blending, so we need to linearize dst.
+ true, // Once blending is done, finally encode to sRGB.
+ },
+ { srgb_L, adobe_L, false,true,true, true,false,true, true,true },
+
+ { adobe_L, srgb_N,
+ false, // src is encoded as f(s*a),a, so we linearize before unpremul.
+ true, // Linearize,
+ true, // then unpremul.
+
+ true, // Gamut transform
+ true, // We're doing non-linear blending, so encode to sRGB now.
+ true, // (non-linear) premul
+
+ false, // We're doing non-linear blending, so dst is already ready to blend.
+ false, // The output of the blend is just what we want.
+ },
+ { srgb_L, adobe_N, false,true,true, true,true,true, false,false },
+
+ { adobe_N, srgb_L,
+ true, // src is encoded as f(s)*a,a, so we unpremul to f(s),a before linearizing.
+ true, // Linearize to s,a.
+ false,
+
+ true, // Gamut transform
+ false, // We're doing linear blending, so we don't encode to sRGB yet.
+ true, // (linear) premul
+
+ true, // We're doing linear blending, so we need to linearize dst.
+ true, // Once blending is done, finally encode to sRGB.
+ },
+ { srgb_N, adobe_L, true,true,false, true,false,true, true,true },
+
+ // These eight cases transform between color spaces with different
+ // transfer functions and the same gamut. Optimization here is limited
+ // to skipping the gamut_transform step: |
+ // v This column of true can all become false.
+ { srgb_N, srgb22_N, true,true,false, true,true,true, false,false },
+ { srgb22_N, srgb_N, true,true,false, true,true,true, false,false },
+
+ { srgb_L, srgb22_L, false,true,true, true,false,true, true,true },
+ { srgb22_L, srgb_L, false,true,true, true,false,true, true,true },
+
+ { srgb_N, srgb22_L, true,true,false, true,false,true, true,true },
+ { srgb22_N, srgb_L, true,true,false, true,false,true, true,true },
+
+ { srgb_L, srgb22_N, false,true,true, true,true,true, false,false },
+ { srgb22_L, srgb_N, false,true,true, true,true,true, false,false },
+
+ // These four test cases test drawing in the same color space.
+ // There is lots of room for optimization here, but none implemented yet.
+ { srgb_N, srgb_N, true,true,false, true,true,true, false,false }, // a.k.a legacy 8888
+ { srgb_L, srgb_L, false,true,true, true,false,true, true,true }, // <canvas> use case
+ { srgb_N, srgb_L, true,true,false, true,false,true, true,true },
+ { srgb_L, srgb_N, false,true,true, true,true,true, false,false },
+
+ // TODO: versions of above crossing in linear transfer functions
+ };
+
+ for (auto t : tests) {
+ // Our expectations are written for premul source alpha types.
+ check_eq(r, SkColorSpaceXformSteps(t.src.get(), kPremul_SkAlphaType, t.dst.get()), t);
+
+ // Opaque and unpremul sources should always go through the same steps,
+ // and they should be the same as premul's steps, with these fixed premul/unpremul steps.
+ auto upm = t;
+ upm.early_unpremul = false;
+ upm.late_unpremul = false;
+ upm.premul = true;
+
+ check_eq(r, SkColorSpaceXformSteps(t.src.get(), kUnpremul_SkAlphaType, t.dst.get()), upm);
+ check_eq(r, SkColorSpaceXformSteps(t.src.get(), kOpaque_SkAlphaType, t.dst.get()), upm);
+ }
+}