Loading include/ui/ColorSpace.h +14 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,11 @@ public: return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper()); } constexpr float3 transformLinear(const float3& v) const noexcept { float3 linear = apply(v, mSource.getClamper()); return apply(mTransform * linear, mDestination.getClamper()); } private: const ColorSpace& mSource; const ColorSpace& mDestination; Loading @@ -190,6 +195,15 @@ public: return Connector(src, dst); } // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256) // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B // The generated 3D LUT is meant to be used as a 3D texture and its Y // axis is thus already flipped // The source color space must define its values in the domain [0..1] // The generated LUT transforms from gamma space to gamma space static std::unique_ptr<float3> createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst); private: static constexpr mat3 computeXYZMatrix( const std::array<float2, 3>& primaries, const float2& whitePoint); Loading include/ui/TVecHelpers.h +6 −6 Original line number Diff line number Diff line Loading @@ -324,7 +324,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] == rv[i]; Loading @@ -334,7 +334,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] != rv[i]; Loading @@ -344,7 +344,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] < rv[i]; Loading @@ -354,7 +354,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] <= rv[i]; Loading @@ -364,7 +364,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] > rv[i]; Loading @@ -374,7 +374,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] >= rv[i]; Loading libs/ui/ColorSpace.cpp +64 −41 Original line number Diff line number Diff line Loading @@ -96,47 +96,6 @@ constexpr mat3 ColorSpace::computeXYZMatrix( }; } 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; } Loading Loading @@ -289,4 +248,68 @@ const ColorSpace ColorSpace::ACEScg() { }; } 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 BRADFORD = 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(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); } if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ()); } mTransform = xyzToRGB * rgbToXYZ; } } std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); std::unique_ptr<float3> lut(new float3[size * size * size]); float3* data = lut.get(); Connector connector(src, dst); for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { *data++ = connector.transform({x * m, y * m, z * m}); } } } return lut; } }; // namespace android libs/ui/tests/colorspace_test.cpp +18 −0 Original line number Diff line number Diff line Loading @@ -162,4 +162,22 @@ TEST_F(ColorSpaceTest, Connect) { EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); } TEST_F(ColorSpaceTest, LUT) { auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB()); EXPECT_TRUE(lut != nullptr); // {1.0f, 0.5f, 0.0f} auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16]; EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); // {1.0f, 1.0f, 0.5f} r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f}))); // {1.0f, 1.0f, 1.0f} r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f}))); } }; // namespace android Loading
include/ui/ColorSpace.h +14 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,11 @@ public: return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper()); } constexpr float3 transformLinear(const float3& v) const noexcept { float3 linear = apply(v, mSource.getClamper()); return apply(mTransform * linear, mDestination.getClamper()); } private: const ColorSpace& mSource; const ColorSpace& mDestination; Loading @@ -190,6 +195,15 @@ public: return Connector(src, dst); } // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256) // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B // The generated 3D LUT is meant to be used as a 3D texture and its Y // axis is thus already flipped // The source color space must define its values in the domain [0..1] // The generated LUT transforms from gamma space to gamma space static std::unique_ptr<float3> createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst); private: static constexpr mat3 computeXYZMatrix( const std::array<float2, 3>& primaries, const float2& whitePoint); Loading
include/ui/TVecHelpers.h +6 −6 Original line number Diff line number Diff line Loading @@ -324,7 +324,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] == rv[i]; Loading @@ -334,7 +334,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] != rv[i]; Loading @@ -344,7 +344,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] < rv[i]; Loading @@ -354,7 +354,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] <= rv[i]; Loading @@ -364,7 +364,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] > rv[i]; Loading @@ -374,7 +374,7 @@ public: template<typename RT> friend inline constexpr VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) { VECTOR<bool> r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] >= rv[i]; Loading
libs/ui/ColorSpace.cpp +64 −41 Original line number Diff line number Diff line Loading @@ -96,47 +96,6 @@ constexpr mat3 ColorSpace::computeXYZMatrix( }; } 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; } Loading Loading @@ -289,4 +248,68 @@ const ColorSpace ColorSpace::ACEScg() { }; } 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 BRADFORD = 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(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); } if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ()); } mTransform = xyzToRGB * rgbToXYZ; } } std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); std::unique_ptr<float3> lut(new float3[size * size * size]); float3* data = lut.get(); Connector connector(src, dst); for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { *data++ = connector.transform({x * m, y * m, z * m}); } } } return lut; } }; // namespace android
libs/ui/tests/colorspace_test.cpp +18 −0 Original line number Diff line number Diff line Loading @@ -162,4 +162,22 @@ TEST_F(ColorSpaceTest, Connect) { EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); } TEST_F(ColorSpaceTest, LUT) { auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB()); EXPECT_TRUE(lut != nullptr); // {1.0f, 0.5f, 0.0f} auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16]; EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); // {1.0f, 1.0f, 0.5f} r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f}))); // {1.0f, 1.0f, 1.0f} r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f}))); } }; // namespace android