aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gn/tests.gni1
-rw-r--r--include/core/SkColorSpace.h2
-rw-r--r--src/core/SkColorSpaceXformSteps.cpp68
-rw-r--r--src/core/SkColorSpaceXformSteps.h28
-rw-r--r--tests/NonlinearBlendingTest.cpp69
-rw-r--r--tests/SkColorSpaceXformStepsTest.cpp10
6 files changed, 146 insertions, 32 deletions
diff --git a/gn/tests.gni b/gn/tests.gni
index 409d2317b4..5f028e8f45 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -139,6 +139,7 @@ tests_sources = [
"$_tests/MessageBusTest.cpp",
"$_tests/MetaDataTest.cpp",
"$_tests/MipMapTest.cpp",
+ "$_tests/NonlinearBlendingTest.cpp",
"$_tests/OnceTest.cpp",
"$_tests/OSPathTest.cpp",
"$_tests/OverAlignedTest.cpp",
diff --git a/include/core/SkColorSpace.h b/include/core/SkColorSpace.h
index 66b101a22d..4265fcb352 100644
--- a/include/core/SkColorSpace.h
+++ b/include/core/SkColorSpace.h
@@ -71,7 +71,7 @@ struct SK_API SkColorSpaceTransferFn {
* Transform a single float by this transfer function.
* For negative inputs, returns sign(x) * f(abs(x)).
*/
- float operator()(float x) {
+ float operator()(float x) const {
SkScalar s = SkScalarSignAsScalar(x);
x = sk_float_abs(x);
if (x >= fD) {
diff --git a/src/core/SkColorSpaceXformSteps.cpp b/src/core/SkColorSpaceXformSteps.cpp
index a084206aff..ae230ee0fc 100644
--- a/src/core/SkColorSpaceXformSteps.cpp
+++ b/src/core/SkColorSpaceXformSteps.cpp
@@ -17,13 +17,13 @@ SkColorSpaceXformSteps::SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType sr
// We have some options about what to do with null src or dst here.
SkASSERT(src && dst);
- this->unpremul = srcAT == kPremul_SkAlphaType;
- this->linearize = !src->gammaIsLinear();
- this->gamut_transform = src->toXYZD50Hash() != dst->toXYZD50Hash();
- this->encode = !dst->gammaIsLinear();
- this->premul = srcAT != kOpaque_SkAlphaType;
+ this->flags.unpremul = srcAT == kPremul_SkAlphaType;
+ this->flags.linearize = !src->gammaIsLinear();
+ this->flags.gamut_transform = src->toXYZD50Hash() != dst->toXYZD50Hash();
+ this->flags.encode = !dst->gammaIsLinear();
+ this->flags.premul = srcAT != kOpaque_SkAlphaType;
- if (this->gamut_transform && src->toXYZD50() && dst->fromXYZD50()) {
+ if (this->flags.gamut_transform && src->toXYZD50() && dst->fromXYZD50()) {
auto xform = SkMatrix44(*dst->fromXYZD50(), *src->toXYZD50());
if (xform.get(3,0) == 0 && xform.get(3,1) == 0 && xform.get(3,2) == 0 &&
xform.get(3,3) == 1 &&
@@ -31,7 +31,7 @@ SkColorSpaceXformSteps::SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType sr
for (int r = 0; r < 3; r++)
for (int c = 0; c < 3; c++) {
- this->src_to_dst_matrix[3*r+c] = xform.get(r,c);
+ this->src_to_dst_matrix[3*c+r] = xform.get(r,c);
}
}
}
@@ -44,22 +44,54 @@ SkColorSpaceXformSteps::SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType sr
this->dstTFInv = dstTF.invert();
// If we linearize then immediately reencode with the same transfer function, skip both.
- if ( this->linearize &&
- !this->gamut_transform &&
- this->encode &&
+ if ( this->flags.linearize &&
+ !this->flags.gamut_transform &&
+ this->flags.encode &&
0 == memcmp(&srcTF, &dstTF, sizeof(SkColorSpaceTransferFn)))
{
- this->linearize = false;
- this->encode = false;
+ this->flags.linearize = false;
+ this->flags.encode = false;
}
// Skip unpremul...premul if there are no non-linear operations between.
- if ( this->unpremul &&
- !this->linearize &&
- !this->encode &&
- this->premul)
+ if ( this->flags.unpremul &&
+ !this->flags.linearize &&
+ !this->flags.encode &&
+ this->flags.premul)
{
- this->unpremul = false;
- this->premul = false;
+ this->flags.unpremul = false;
+ this->flags.premul = false;
+ }
+}
+
+void SkColorSpaceXformSteps::apply(float* rgba) const {
+ if (flags.unpremul) {
+ float invA = isfinite(1.0f / rgba[3]) ? 1.0f / rgba[3] : 0;
+ rgba[0] *= invA;
+ rgba[1] *= invA;
+ rgba[2] *= invA;
+ }
+ if (flags.linearize) {
+ rgba[0] = srcTF(rgba[0]);
+ rgba[1] = srcTF(rgba[1]);
+ rgba[2] = srcTF(rgba[2]);
+ }
+ if (flags.gamut_transform) {
+ float temp[3] = { rgba[0], rgba[1], rgba[2] };
+ for (int i = 0; i < 3; ++i) {
+ rgba[i] = src_to_dst_matrix[ i] * temp[0] +
+ src_to_dst_matrix[3 + i] * temp[1] +
+ src_to_dst_matrix[6 + i] * temp[2];
+ }
+ }
+ if (flags.encode) {
+ rgba[0] = dstTFInv(rgba[0]);
+ rgba[1] = dstTFInv(rgba[1]);
+ rgba[2] = dstTFInv(rgba[2]);
+ }
+ if (flags.premul) {
+ rgba[0] *= rgba[3];
+ rgba[1] *= rgba[3];
+ rgba[2] *= rgba[3];
}
}
diff --git a/src/core/SkColorSpaceXformSteps.h b/src/core/SkColorSpaceXformSteps.h
index 6c38411692..3b2a88cddc 100644
--- a/src/core/SkColorSpaceXformSteps.h
+++ b/src/core/SkColorSpaceXformSteps.h
@@ -12,6 +12,22 @@
#include "SkImageInfo.h"
struct SkColorSpaceXformSteps {
+ struct Flags {
+ bool unpremul;
+ bool linearize;
+ bool gamut_transform;
+ bool encode;
+ bool premul;
+
+ uint32_t mask() const {
+ return (unpremul ? 1 : 0)
+ | (linearize ? 2 : 0)
+ | (gamut_transform ? 4 : 0)
+ | (encode ? 8 : 0)
+ | (premul ? 16 : 0);
+ }
+ };
+
SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType srcAT,
SkColorSpace* dst);
@@ -22,17 +38,13 @@ struct SkColorSpaceXformSteps {
return SkColorSpaceXformSteps(src, kOpaque_SkAlphaType, dst);
}
- bool unpremul;
- bool linearize;
- bool gamut_transform;
- bool encode;
- bool premul;
+ void apply(float rgba[4]) const;
+
+ Flags flags;
SkColorSpaceTransferFn srcTF, // Apply for linearize.
dstTFInv; // Apply for encode.
- float src_to_dst_matrix[9]; // Apply this 3x3 row-major matrix for gamut_transform.
+ float src_to_dst_matrix[9]; // Apply this 3x3 column-major matrix for gamut_transform.
};
-
-
#endif//SkColorSpaceXformSteps_DEFINED
diff --git a/tests/NonlinearBlendingTest.cpp b/tests/NonlinearBlendingTest.cpp
new file mode 100644
index 0000000000..0ea7d47dab
--- /dev/null
+++ b/tests/NonlinearBlendingTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 "../third_party/skcms/skcms.h"
+#include "SkColorSpace.h"
+#include "SkColorSpaceXformSteps.h"
+#include "Test.h"
+
+DEF_TEST(SkColorSpaceXformSteps_vs_skcms, r) {
+ auto srgb = SkColorSpace::MakeSRGB();
+ auto dp3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+ SkColorSpace::kDCIP3_D65_Gamut);
+
+ skcms_ICCProfile srgb_profile;
+ srgb->toProfile(&srgb_profile);
+ skcms_ICCProfile dp3_profile;
+ dp3->toProfile(&dp3_profile);
+
+ // These colors provide a good spread of interesting test cases.
+ SkColor colors[] = {
+ 0xffff0000, 0x7fff0000, 0x7f7f0000,
+ 0xff00ff00, 0x7f00ff00, 0x7f007f00,
+ };
+
+ for (auto color : colors) {
+ auto bgra = skcms_PixelFormat_BGRA_8888,
+ f32 = skcms_PixelFormat_RGBA_ffff;
+ auto unpremul = skcms_AlphaFormat_Unpremul,
+ premul = skcms_AlphaFormat_PremulAsEncoded;
+
+ float via_skcms[4];
+ skcms_Transform(&color, bgra, unpremul, &srgb_profile,
+ &via_skcms, f32, premul, &dp3_profile,
+ 1);
+
+ SkColorSpaceXformSteps steps(srgb.get(), kUnpremul_SkAlphaType, dp3.get());
+ float via_steps[4] = {
+ SkColorGetR(color) * (1 / 255.0f),
+ SkColorGetG(color) * (1 / 255.0f),
+ SkColorGetB(color) * (1 / 255.0f),
+ SkColorGetA(color) * (1 / 255.0f),
+ };
+ steps.apply(via_steps);
+
+ REPORTER_ASSERT(r, fabsf(via_skcms[0] - via_steps[0]) <= 0.005f);
+ REPORTER_ASSERT(r, fabsf(via_skcms[1] - via_steps[1]) <= 0.005f);
+ REPORTER_ASSERT(r, fabsf(via_skcms[2] - via_steps[2]) <= 0.005f);
+ REPORTER_ASSERT(r, fabsf(via_skcms[3] - via_steps[3]) <= 0.0f);
+
+ // Now go back using the other method's inverse transform
+ float steps_to_skcms[4];
+ skcms_Transform(via_steps, f32, premul, &dp3_profile,
+ steps_to_skcms, f32, premul, &srgb_profile,
+ 1);
+
+ float skcms_to_steps[4] = { via_skcms[0], via_skcms[1], via_skcms[2], via_skcms[3] };
+ SkColorSpaceXformSteps inv_steps(dp3.get(), kPremul_SkAlphaType, srgb.get());
+ inv_steps.apply(skcms_to_steps);
+
+ REPORTER_ASSERT(r, fabsf(skcms_to_steps[0] - steps_to_skcms[0]) <= 0.005f);
+ REPORTER_ASSERT(r, fabsf(skcms_to_steps[1] - steps_to_skcms[1]) <= 0.005f);
+ REPORTER_ASSERT(r, fabsf(skcms_to_steps[2] - steps_to_skcms[2]) <= 0.005f);
+ REPORTER_ASSERT(r, fabsf(skcms_to_steps[3] - steps_to_skcms[3]) <= 0.0f);
+ }
+}
diff --git a/tests/SkColorSpaceXformStepsTest.cpp b/tests/SkColorSpaceXformStepsTest.cpp
index 146c6f1502..05daa25b6b 100644
--- a/tests/SkColorSpaceXformStepsTest.cpp
+++ b/tests/SkColorSpaceXformStepsTest.cpp
@@ -98,11 +98,11 @@ DEF_TEST(SkColorSpaceXformSteps, r) {
uint32_t tested = 0x00000000;
for (auto t : tests) {
SkColorSpaceXformSteps steps{t.src.get(), t.srcAT, t.dst.get()};
- REPORTER_ASSERT(r, steps.unpremul == t.unpremul);
- REPORTER_ASSERT(r, steps.linearize == t.linearize);
- REPORTER_ASSERT(r, steps.gamut_transform == t.gamut_transform);
- REPORTER_ASSERT(r, steps.encode == t.encode);
- REPORTER_ASSERT(r, steps.premul == t.premul);
+ REPORTER_ASSERT(r, steps.flags.unpremul == t.unpremul);
+ REPORTER_ASSERT(r, steps.flags.linearize == t.linearize);
+ REPORTER_ASSERT(r, steps.flags.gamut_transform == t.gamut_transform);
+ REPORTER_ASSERT(r, steps.flags.encode == t.encode);
+ REPORTER_ASSERT(r, steps.flags.premul == t.premul);
uint32_t bits = (uint32_t)t.unpremul << 0
| (uint32_t)t.linearize << 1