1 | /* This file is part of the KDE project |
2 | SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> |
3 | SPDX-FileCopyrightText: 2007 Olaf Schmidt <ojschmidt@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | #include "kcolorspaces_p.h" |
8 | #include "kguiaddons_colorhelpers_p.h" |
9 | |
10 | #include <QColor> |
11 | |
12 | #include <math.h> |
13 | |
14 | using namespace KColorSpaces; |
15 | |
16 | static inline qreal wrap(qreal a, qreal d = 1.0) |
17 | { |
18 | qreal r = fmod(x: a, y: d); |
19 | return (r < 0.0 ? d + r : (r > 0.0 ? r : 0.0)); |
20 | } |
21 | |
22 | /////////////////////////////////////////////////////////////////////////////// |
23 | // HCY color space |
24 | |
25 | #define HCY_REC 709 // use 709 for now |
26 | #if HCY_REC == 601 |
27 | static const qreal yc[3] = {0.299, 0.587, 0.114}; |
28 | #elif HCY_REC == 709 |
29 | static const qreal yc[3] = {0.2126, 0.7152, 0.0722}; |
30 | #else // use Qt values |
31 | static const qreal yc[3] = {0.34375, 0.5, 0.15625}; |
32 | #endif |
33 | |
34 | qreal KHCY::gamma(qreal n) |
35 | { |
36 | return pow(x: normalize(a: n), y: 2.2); |
37 | } |
38 | |
39 | qreal KHCY::igamma(qreal n) |
40 | { |
41 | return pow(x: normalize(a: n), y: 1.0 / 2.2); |
42 | } |
43 | |
44 | qreal KHCY::lumag(qreal r, qreal g, qreal b) |
45 | { |
46 | return r * yc[0] + g * yc[1] + b * yc[2]; |
47 | } |
48 | |
49 | KHCY::KHCY(qreal h_, qreal c_, qreal y_, qreal a_) |
50 | { |
51 | h = h_; |
52 | c = c_; |
53 | y = y_; |
54 | a = a_; |
55 | } |
56 | |
57 | KHCY::KHCY(const QColor &color) |
58 | { |
59 | qreal r = gamma(n: color.redF()); |
60 | qreal g = gamma(n: color.greenF()); |
61 | qreal b = gamma(n: color.blueF()); |
62 | a = color.alphaF(); |
63 | |
64 | // luma component |
65 | y = lumag(r, g, b); |
66 | |
67 | // hue component |
68 | qreal p = qMax(a: qMax(a: r, b: g), b); |
69 | qreal n = qMin(a: qMin(a: r, b: g), b); |
70 | qreal d = 6.0 * (p - n); |
71 | if (n == p) { |
72 | h = 0.0; |
73 | } else if (r == p) { |
74 | h = ((g - b) / d); |
75 | } else if (g == p) { |
76 | h = ((b - r) / d) + (1.0 / 3.0); |
77 | } else { |
78 | h = ((r - g) / d) + (2.0 / 3.0); |
79 | } |
80 | |
81 | // chroma component |
82 | if (r == g && g == b) { |
83 | c = 0.0; |
84 | } else { |
85 | c = qMax(a: (y - n) / y, b: (p - y) / (1 - y)); |
86 | } |
87 | } |
88 | |
89 | QColor KHCY::qColor() const |
90 | { |
91 | // start with sane component values |
92 | qreal _h = wrap(a: h); |
93 | qreal _c = normalize(a: c); |
94 | qreal _y = normalize(a: y); |
95 | |
96 | // calculate some needed variables |
97 | qreal _hs = _h * 6.0; |
98 | qreal th; |
99 | qreal tm; |
100 | if (_hs < 1.0) { |
101 | th = _hs; |
102 | tm = yc[0] + yc[1] * th; |
103 | } else if (_hs < 2.0) { |
104 | th = 2.0 - _hs; |
105 | tm = yc[1] + yc[0] * th; |
106 | } else if (_hs < 3.0) { |
107 | th = _hs - 2.0; |
108 | tm = yc[1] + yc[2] * th; |
109 | } else if (_hs < 4.0) { |
110 | th = 4.0 - _hs; |
111 | tm = yc[2] + yc[1] * th; |
112 | } else if (_hs < 5.0) { |
113 | th = _hs - 4.0; |
114 | tm = yc[2] + yc[0] * th; |
115 | } else { |
116 | th = 6.0 - _hs; |
117 | tm = yc[0] + yc[2] * th; |
118 | } |
119 | |
120 | // calculate RGB channels in sorted order |
121 | qreal tn; |
122 | qreal to; |
123 | qreal tp; |
124 | if (tm >= _y) { |
125 | tp = _y + _y * _c * (1.0 - tm) / tm; |
126 | to = _y + _y * _c * (th - tm) / tm; |
127 | tn = _y - (_y * _c); |
128 | } else { |
129 | tp = _y + (1.0 - _y) * _c; |
130 | to = _y + (1.0 - _y) * _c * (th - tm) / (1.0 - tm); |
131 | tn = _y - (1.0 - _y) * _c * tm / (1.0 - tm); |
132 | } |
133 | |
134 | // return RGB channels in appropriate order |
135 | if (_hs < 1.0) { |
136 | return QColor::fromRgbF(r: igamma(n: tp), g: igamma(n: to), b: igamma(n: tn), a); |
137 | } else if (_hs < 2.0) { |
138 | return QColor::fromRgbF(r: igamma(n: to), g: igamma(n: tp), b: igamma(n: tn), a); |
139 | } else if (_hs < 3.0) { |
140 | return QColor::fromRgbF(r: igamma(n: tn), g: igamma(n: tp), b: igamma(n: to), a); |
141 | } else if (_hs < 4.0) { |
142 | return QColor::fromRgbF(r: igamma(n: tn), g: igamma(n: to), b: igamma(n: tp), a); |
143 | } else if (_hs < 5.0) { |
144 | return QColor::fromRgbF(r: igamma(n: to), g: igamma(n: tn), b: igamma(n: tp), a); |
145 | } else { |
146 | return QColor::fromRgbF(r: igamma(n: tp), g: igamma(n: tn), b: igamma(n: to), a); |
147 | } |
148 | } |
149 | |
150 | qreal KHCY::hue(const QColor &color) |
151 | { |
152 | return wrap(a: KHCY(color).h); |
153 | } |
154 | |
155 | qreal KHCY::chroma(const QColor &color) |
156 | { |
157 | return KHCY(color).c; |
158 | } |
159 | |
160 | qreal KHCY::luma(const QColor &color) |
161 | { |
162 | return lumag(r: gamma(n: color.redF()), g: gamma(n: color.greenF()), b: gamma(n: color.blueF())); |
163 | } |
164 | |