diff options
-rw-r--r-- | gn/tests.gni | 1 | ||||
-rw-r--r-- | include/core/SkColorSpace.h | 2 | ||||
-rw-r--r-- | src/core/SkColorSpaceXformSteps.cpp | 68 | ||||
-rw-r--r-- | src/core/SkColorSpaceXformSteps.h | 28 | ||||
-rw-r--r-- | tests/NonlinearBlendingTest.cpp | 69 | ||||
-rw-r--r-- | tests/SkColorSpaceXformStepsTest.cpp | 10 |
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 |