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>
This commit is contained in:
parent
7916c0ec35
commit
6968f9ce54
@ -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",
|
||||
|
@ -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",
|
||||
|
74
src/core/SkColorSpaceXformSteps.cpp
Normal file
74
src/core/SkColorSpaceXformSteps.cpp
Normal file
@ -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;
|
||||
}
|
42
src/core/SkColorSpaceXformSteps.h
Normal file
42
src/core/SkColorSpaceXformSteps.h
Normal file
@ -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
|
150
tests/SkColorSpaceXformStepsTest.cpp
Normal file
150
tests/SkColorSpaceXformStepsTest.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user