diff options
author | Mike Klein <mtklein@chromium.org> | 2018-05-24 12:33:23 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-05-24 18:24:48 +0000 |
commit | 6968f9ce54a627254ff91206df6c78de99930c89 (patch) | |
tree | 9c1608a40cec7b7148e6f1160c1a6fdf42e23d5a | |
parent | 7916c0ec353e059c4979102d015931a276103fd9 (diff) |
SkColorSpaceXformSteps
Lots TODO.
Change-Id: I95edb764b85a5140d432adb506c3b537869e6df4
Reviewed-on: https://skia-review.googlesource.com/129933
Commit-Queue: Mike Klein <mtklein@chromium.org>
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: Mike Klein <mtklein@chromium.org>
-rw-r--r-- | gn/core.gni | 1 | ||||
-rw-r--r-- | gn/tests.gni | 1 | ||||
-rw-r--r-- | src/core/SkColorSpaceXformSteps.cpp | 74 | ||||
-rw-r--r-- | src/core/SkColorSpaceXformSteps.h | 42 | ||||
-rw-r--r-- | tests/SkColorSpaceXformStepsTest.cpp | 150 |
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); + } +} |