Merge "Add color space connection"
diff --git a/include/ui/ColorSpace.h b/include/ui/ColorSpace.h
index c6a20c9..15e6628 100644
--- a/include/ui/ColorSpace.h
+++ b/include/ui/ColorSpace.h
@@ -20,6 +20,7 @@
#include <array>
#include <cmath>
#include <functional>
+#include <memory>
#include <string>
#include <ui/mat3.h>
@@ -165,6 +166,30 @@
static const ColorSpace ACES();
static const ColorSpace ACEScg();
+ class Connector {
+ public:
+ Connector(const ColorSpace& src, const ColorSpace& dst) noexcept;
+
+ constexpr const ColorSpace& getSource() const noexcept { return mSource; }
+ constexpr const ColorSpace& getDestination() const noexcept { return mDestination; }
+
+ constexpr const mat3& getTransform() const noexcept { return mTransform; }
+
+ constexpr float3 transform(const float3& v) const noexcept {
+ float3 linear = mSource.toLinear(apply(v, mSource.getClamper()));
+ return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper());
+ }
+
+ private:
+ const ColorSpace& mSource;
+ const ColorSpace& mDestination;
+ mat3 mTransform;
+ };
+
+ static const Connector connect(const ColorSpace& src, const ColorSpace& dst) {
+ return Connector(src, dst);
+ }
+
private:
static constexpr mat3 computeXYZMatrix(
const std::array<float2, 3>& primaries, const float2& whitePoint);
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
index 6296abe..6f005f4 100644
--- a/libs/ui/ColorSpace.cpp
+++ b/libs/ui/ColorSpace.cpp
@@ -96,6 +96,47 @@
};
}
+static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 VON_KRIES = mat3{
+ float3{ 0.8951f, -0.7502f, 0.0389f},
+ float3{ 0.2664f, 1.7135f, -0.0685f},
+ float3{-0.1614f, 0.0367f, 1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+ float3 srcLMS = matrix * srcWhitePoint;
+ float3 dstLMS = matrix * dstWhitePoint;
+ return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+ColorSpace::Connector::Connector(
+ const ColorSpace& src,
+ const ColorSpace& dst) noexcept
+ : mSource(src)
+ , mDestination(dst) {
+
+ if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) {
+ mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ();
+ } else {
+ mat3 rgbToXYZ(src.getRGBtoXYZ());
+ mat3 xyzToRGB(dst.getXYZtoRGB());
+
+ float3 srcXYZ = XYZ(float3{src.getWhitePoint(), 1});
+ float3 dstXYZ = XYZ(float3{dst.getWhitePoint(), 1});
+
+ if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+ rgbToXYZ = adaptation(VON_KRIES, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
+ }
+
+ if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+ xyzToRGB = inverse(adaptation(VON_KRIES, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ());
+ }
+
+ mTransform = xyzToRGB * rgbToXYZ;
+ }
+}
+
static constexpr float rcpResponse(float x, float g,float a, float b, float c, float d) {
return x >= d * c ? (std::pow(x, 1.0f / g) - b) / a : x / c;
}
@@ -112,6 +153,10 @@
return std::copysign(response(std::abs(x), g, a, b, c, d), x);
}
+static float safePow(float x, float e) {
+ return powf(x < 0.0f ? 0.0f : x, e);
+}
+
const ColorSpace ColorSpace::sRGB() {
return {
"sRGB IEC61966-2.1",
@@ -187,8 +232,8 @@
"Adobe RGB (1998)",
{{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
{0.3127f, 0.3290f},
- std::bind(powf, _1, 1.0f / 2.2f),
- std::bind(powf, _1, 2.2f)
+ std::bind(safePow, _1, 1.0f / 2.2f),
+ std::bind(safePow, _1, 2.2f)
};
}
@@ -196,7 +241,7 @@
return {
"ROMM RGB ISO 22028-2:2013",
{{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
- {0.3457f, 0.3585f},
+ {0.34567f, 0.35850f},
std::bind(rcpResponse, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f),
std::bind(response, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f)
};
@@ -217,8 +262,8 @@
"SMPTE RP 431-2-2007 DCI (P3)",
{{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
{0.314f, 0.351f},
- std::bind(powf, _1, 1.0f / 2.6f),
- std::bind(powf, _1, 2.6f)
+ std::bind(safePow, _1, 1.0f / 2.6f),
+ std::bind(safePow, _1, 2.6f)
};
}
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
index e5c2633..0bfa487 100644
--- a/libs/ui/tests/colorspace_test.cpp
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -150,4 +150,16 @@
EXPECT_TRUE(extendedSRGB.g > 1.0f);
}
+TEST_F(ColorSpaceTest, Connect) {
+ // No chromatic adaptation
+ auto r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::AdobeRGB())
+ .transform({1.0f, 0.5f, 0.0f});
+ EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
+
+ // Test with chromatic adaptation
+ r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB())
+ .transform({1.0f, 0.0f, 0.0f});
+ EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f})));
+}
+
}; // namespace android