From 760d67a2a865f769d0924e0dfc345ea8530ffc7b Mon Sep 17 00:00:00 2001 From: UjinT34 <41110182+UjinT34@users.noreply.github.com> Date: Sun, 6 Apr 2025 18:27:46 +0300 Subject: [PATCH] color: CM structs, constants & math (#14) --- include/hyprgraphics/color/Color.hpp | 58 +++++++++++++- src/color/Color.cpp | 108 +++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/include/hyprgraphics/color/Color.hpp b/include/hyprgraphics/color/Color.hpp index 315fa71..e4f4ee5 100644 --- a/include/hyprgraphics/color/Color.hpp +++ b/include/hyprgraphics/color/Color.hpp @@ -1,5 +1,6 @@ #pragma once +#include namespace Hyprgraphics { class CColor { public: @@ -18,6 +19,25 @@ namespace Hyprgraphics { double l = 0, a = 0, b = 0; }; + // xy 0.0 - 1.0 + struct xy { + double x = 0, y = 0; + + bool operator==(const xy& p2) const { + return x == p2.x && y == p2.y; + } + }; + + // XYZ 0.0 - 1.0 + struct XYZ { + double x = 0, y = 0, z = 0; + + // per-component division + XYZ operator/(const XYZ& other) const { + return {x / other.x, y / other.y, z / other.z}; + } + }; + CColor(); // black CColor(const SSRGB& rgb); CColor(const SHSL& hsl); @@ -27,7 +47,7 @@ namespace Hyprgraphics { SHSL asHSL() const; SOkLab asOkLab() const; - bool operator==(const CColor& other) const { + bool operator==(const CColor& other) const { return other.r == r && other.g == g && other.b == b; } @@ -35,4 +55,40 @@ namespace Hyprgraphics { // SRGB space for internal color storage double r = 0, g = 0, b = 0; }; + + // 3x3 matrix for CM transformations + class CMatrix3 { + public: + CMatrix3() = default; + CMatrix3(const std::array, 3>& values); + + CMatrix3 invert() const; + CColor::XYZ operator*(const CColor::XYZ& xyz) const; + CMatrix3 operator*(const CMatrix3& other) const; + + const std::array, 3>& mat(); + + static const CMatrix3& identity(); + + private: + std::array, 3> m = { + 0, 0, 0, // + 0, 0, 0, // + 0, 0, 0, // + }; + }; + + CColor::XYZ xy2xyz(const CColor::xy& xy); + CMatrix3 adaptWhite(const CColor::xy& src, const CColor::xy& dst); + + struct SPCPRimaries { + CColor::xy red, green, blue, white; + + bool operator==(const SPCPRimaries& p2) const { + return red == p2.red && green == p2.green && blue == p2.blue && white == p2.white; + } + + CMatrix3 toXYZ() const; // toXYZ() * rgb -> xyz + CMatrix3 convertMatrix(const SPCPRimaries& dst) const; // convertMatrix(dst) * rgb with "this" primaries -> rgb with dst primaries + }; }; diff --git a/src/color/Color.cpp b/src/color/Color.cpp index 1a796aa..d123431 100644 --- a/src/color/Color.cpp +++ b/src/color/Color.cpp @@ -26,6 +26,114 @@ static double hueToRgb(double p, double q, double t) { return p; } +Hyprgraphics::CMatrix3::CMatrix3(const std::array, 3>& values) : m(values) {} + +CMatrix3 Hyprgraphics::CMatrix3::invert() const { + double invDet = 1 / + (0 // + + m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) // + - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) // + + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]) // + ); + + return CMatrix3(std::array, 3>{ + (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * invDet, (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * invDet, (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * invDet, // + (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * invDet, (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * invDet, (m[1][0] * m[0][2] - m[0][0] * m[1][2]) * invDet, // + (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * invDet, (m[2][0] * m[0][1] - m[0][0] * m[2][1]) * invDet, (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * invDet, // + }); +} + +CColor::XYZ Hyprgraphics::CMatrix3::operator*(const CColor::XYZ& value) const { + return CColor::XYZ{ + m[0][0] * value.x + m[0][1] * value.y + m[0][2] * value.z, // + m[1][0] * value.x + m[1][1] * value.y + m[1][2] * value.z, // + m[2][0] * value.x + m[2][1] * value.y + m[2][2] * value.z, // + }; +} + +CMatrix3 Hyprgraphics::CMatrix3::operator*(const CMatrix3& other) const { + std::array, 3> res = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + res[i][j] += m[i][k] * other.m[k][j]; + } + } + } + return CMatrix3(res); +} + +const std::array, 3>& Hyprgraphics::CMatrix3::mat() { + return m; +}; + +const CMatrix3& CMatrix3::identity() { + static const CMatrix3 Identity3 = CMatrix3(std::array, 3>{ + 1, 0, 0, // + 0, 1, 0, // + 0, 0, 1, // + }); + return Identity3; +} + +CColor::XYZ Hyprgraphics::xy2xyz(const CColor::xy& xy) { + if (xy.y == 0.0) + return {0.0, 0.0, 0.0}; + + return {xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y}; +} + +CMatrix3 Bradford = CMatrix3(std::array, 3>{ + 0.8951, 0.2664, -0.1614, // + -0.7502, 1.7135, 0.0367, // + 0.0389, -0.0685, 1.0296, // +}); + +CMatrix3 BradfordInv = Bradford.invert(); + +CMatrix3 Hyprgraphics::adaptWhite(const CColor::xy& src, const CColor::xy& dst) { + if (src == dst) + return CMatrix3::identity(); + + const auto srcXYZ = xy2xyz(src); + const auto dstXYZ = xy2xyz(dst); + const auto factors = (Bradford * dstXYZ) / (Bradford * srcXYZ); + + return BradfordInv * + CMatrix3(std::array, 3>{ + factors.x, 0.0, 0.0, // + 0.0, factors.y, 0.0, // + 0.0, 0.0, factors.z, // + }) * + Bradford; +} + +CMatrix3 Hyprgraphics::SPCPRimaries::toXYZ() const { + const auto r = xy2xyz(red); + const auto g = xy2xyz(green); + const auto b = xy2xyz(blue); + const auto w = xy2xyz(white); + + const auto invMat = CMatrix3(std::array, 3>{ + r.x, g.x, b.x, // + r.y, g.y, b.y, // + r.z, g.z, b.z, // + }) + .invert(); + + const auto s = invMat * w; + + return std::array, 3>{ + s.x * r.x, s.y * g.x, s.z * b.x, // + s.x * r.y, s.y * g.y, s.z * b.y, // + s.x * r.z, s.y * g.z, s.z * b.z, // + }; +} + +CMatrix3 Hyprgraphics::SPCPRimaries::convertMatrix(const SPCPRimaries& dst) const { + return dst.toXYZ().invert() * adaptWhite(white, dst.white) * toXYZ(); +} + Hyprgraphics::CColor::CColor() { ; }