| 1 | /* This file is part of the KDE project |
| 2 | SPDX-FileCopyrightText: 2007 Matthew Woehlke <[email protected]> |
| 3 | SPDX-FileCopyrightText: 2007 Olaf Schmidt <[email protected]> |
| 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 | |