14ColorUtils::ColorUtils(
QObject *parent)
22 return (0.299 * color.
red() + 0.587 * color.
green() + 0.114 * color.
blue()) / 255;
25 return luma(color) > 0.5 ? ColorUtils::Brightness::Light : ColorUtils::Brightness::Dark;
30 return (0.299 * color.
red() + 0.587 * color.
green() + 0.114 * color.
blue()) / 255;
35 const auto foregroundAlpha = foreground.
alpha();
36 const auto inverseForegroundAlpha = 0xff - foregroundAlpha;
37 const auto backgroundAlpha = background.
alpha();
39 if (foregroundAlpha == 0x00) {
43 if (backgroundAlpha == 0xff) {
44 return QColor::fromRgb((foregroundAlpha * foreground.
red()) + (inverseForegroundAlpha * background.
red()),
45 (foregroundAlpha * foreground.
green()) + (inverseForegroundAlpha * background.
green()),
46 (foregroundAlpha * foreground.
blue()) + (inverseForegroundAlpha * background.
blue()),
49 const auto inverseBackgroundAlpha = (backgroundAlpha * inverseForegroundAlpha) / 255;
50 const auto finalAlpha = foregroundAlpha + inverseBackgroundAlpha;
51 Q_ASSERT(finalAlpha != 0x00);
52 return QColor::fromRgb((foregroundAlpha * foreground.
red()) + (inverseBackgroundAlpha * background.
red()),
53 (foregroundAlpha * foreground.
green()) + (inverseBackgroundAlpha * background.
green()),
54 (foregroundAlpha * foreground.
blue()) + (inverseBackgroundAlpha * background.
blue()),
61 auto scaleAlpha = [](
const QColor &color,
double factor) {
64 auto linearlyInterpolateDouble = [](
double one,
double two,
double factor) {
65 return one + (two - one) * factor;
69 return scaleAlpha(two, balance);
72 return scaleAlpha(one, 1 - balance);
77 qBound(0.0, linearlyInterpolateDouble(one.
value(), two.
value(), balance), 255.0),
78 qBound(0.0, linearlyInterpolateDouble(one.
alpha(), two.
alpha(), balance), 255.0));
83struct ParsedAdjustments {
89 double saturation = 0.0;
95ParsedAdjustments parseAdjustments(
const QJSValue &value)
97 ParsedAdjustments parsed;
99 auto checkProperty = [](
const QJSValue &value,
const QString &property) {
101 auto val = value.
property(property);
102 if (val.isNumber()) {
109 std::vector<std::pair<QString, double &>> items{{QStringLiteral(
"red"), parsed.red},
110 {QStringLiteral(
"green"), parsed.green},
111 {QStringLiteral(
"blue"), parsed.blue},
113 {QStringLiteral(
"hue"), parsed.hue},
114 {QStringLiteral(
"saturation"), parsed.saturation},
115 {QStringLiteral(
"value"), parsed.value},
116 {QStringLiteral(
"lightness"), parsed.value},
118 {QStringLiteral(
"alpha"), parsed.alpha}};
120 for (
const auto &item : items) {
121 auto val = checkProperty(value, item.first);
123 item.second = val.toDouble();
127 if ((parsed.red || parsed.green || parsed.blue) && (parsed.hue || parsed.saturation || parsed.value)) {
128 qCritical() <<
"It is an error to have both RGB and HSL values in an adjustment.";
136 auto adjusts = parseAdjustments(adjustments);
138 if (qBound(-360.0, adjusts.hue, 360.0) != adjusts.hue) {
139 qCritical() <<
"Hue is out of bounds";
141 if (qBound(-255.0, adjusts.red, 255.0) != adjusts.red) {
142 qCritical() <<
"Red is out of bounds";
144 if (qBound(-255.0, adjusts.green, 255.0) != adjusts.green) {
145 qCritical() <<
"Green is out of bounds";
147 if (qBound(-255.0, adjusts.blue, 255.0) != adjusts.blue) {
148 qCritical() <<
"Green is out of bounds";
150 if (qBound(-255.0, adjusts.saturation, 255.0) != adjusts.saturation) {
151 qCritical() <<
"Saturation is out of bounds";
153 if (qBound(-255.0, adjusts.value, 255.0) != adjusts.value) {
154 qCritical() <<
"Value is out of bounds";
156 if (qBound(-255.0, adjusts.alpha, 255.0) != adjusts.alpha) {
157 qCritical() <<
"Alpha is out of bounds";
163 copy.setAlpha(adjusts.alpha);
166 if (adjusts.red || adjusts.green || adjusts.blue) {
167 copy.setRed(
copy.red() + adjusts.red);
168 copy.setGreen(
copy.green() + adjusts.green);
169 copy.setBlue(
copy.blue() + adjusts.blue);
170 }
else if (adjusts.hue || adjusts.saturation || adjusts.value) {
171 copy.setHsl(std::fmod(
copy.hue() + adjusts.hue, 360.0),
172 copy.saturation() + adjusts.saturation,
173 copy.value() + adjusts.value,
182 auto adjusts = parseAdjustments(adjustments);
185 if (qBound(-100.0, adjusts.red, 100.00) != adjusts.red) {
186 qCritical() <<
"Red is out of bounds";
188 if (qBound(-100.0, adjusts.green, 100.00) != adjusts.green) {
189 qCritical() <<
"Green is out of bounds";
191 if (qBound(-100.0, adjusts.blue, 100.00) != adjusts.blue) {
192 qCritical() <<
"Blue is out of bounds";
194 if (qBound(-100.0, adjusts.saturation, 100.00) != adjusts.saturation) {
195 qCritical() <<
"Saturation is out of bounds";
197 if (qBound(-100.0, adjusts.value, 100.00) != adjusts.value) {
198 qCritical() <<
"Value is out of bounds";
200 if (qBound(-100.0, adjusts.alpha, 100.00) != adjusts.alpha) {
201 qCritical() <<
"Alpha is out of bounds";
204 if (adjusts.hue != 0) {
205 qCritical() <<
"Hue cannot be scaled";
208 auto shiftToAverage = [](
double current,
double factor) {
209 auto scale = qBound(-100.0, factor, 100.0) / 100;
210 return current + (scale > 0 ? 255 - current : current) * scale;
213 if (adjusts.red || adjusts.green || adjusts.blue) {
214 copy.setRed(qBound(0.0, shiftToAverage(
copy.red(), adjusts.red), 255.0));
215 copy.setGreen(qBound(0.0, shiftToAverage(
copy.green(), adjusts.green), 255.0));
216 copy.setBlue(qBound(0.0, shiftToAverage(
copy.blue(), adjusts.blue), 255.0));
219 qBound(0.0, shiftToAverage(
copy.saturation(), adjusts.saturation), 255.0),
220 qBound(0.0, shiftToAverage(
copy.value(), adjusts.value), 255.0),
221 qBound(0.0, shiftToAverage(
copy.alpha(), adjusts.alpha), 255.0));
229 qreal tintAlpha = tintColor.
alphaF() * alpha;
230 qreal inverseAlpha = 1.0 - tintAlpha;
232 if (qFuzzyCompare(tintAlpha, 1.0)) {
234 }
else if (qFuzzyIsNull(tintAlpha)) {
239 tintColor.
greenF() * tintAlpha + targetColor.
greenF() * inverseAlpha,
240 tintColor.
blueF() * tintAlpha + targetColor.
blueF() * inverseAlpha,
241 tintAlpha + inverseAlpha * targetColor.
alphaF());
244ColorUtils::XYZColor ColorUtils::colorToXYZ(
const QColor &color)
247 qreal r = color.
redF();
249 qreal b = color.
blueF();
251 auto correct = [](qreal &v) {
253 v = std::pow((v + 0.055) / 1.055, 2.4);
264 const qreal x = r * 0.4124 + g * 0.3576 + b * 0.1805;
265 const qreal y = r * 0.2126 + g * 0.7152 + b * 0.0722;
266 const qreal z = r * 0.0193 + g * 0.1192 + b * 0.9505;
268 return XYZColor{x, y, z};
271ColorUtils::LabColor ColorUtils::colorToLab(
const QColor &color)
274 const auto xyz = colorToXYZ(color);
277 qreal x = xyz.x / 0.95047;
278 qreal y = xyz.y / 1.0;
279 qreal z = xyz.z / 1.08883;
281 auto pivot = [](qreal &v) {
283 v = std::pow(v, 1.0 / 3.0);
285 v = (7.787 * v) + (16.0 / 116.0);
294 labColor.l = std::max(0.0, (116 * y) - 16);
295 labColor.a = 500 * (x - y);
296 labColor.b = 200 * (y - z);
303 LabColor labColor = colorToLab(color);
306 return sqrt(pow(labColor.a, 2) + pow(labColor.b, 2));
309qreal ColorUtils::luminance(
const QColor &color)
311 const auto &xyz = colorToXYZ(color);
316#include "moc_colorutils.cpp"
Q_INVOKABLE qreal grayForColor(const QColor &color)
Q_INVOKABLE QColor adjustColor(const QColor &color, const QJSValue &adjustments)
Q_INVOKABLE QColor alphaBlend(const QColor &foreground, const QColor &background)
Q_INVOKABLE QColor scaleColor(const QColor &color, const QJSValue &adjustments)
static Q_INVOKABLE qreal chroma(const QColor &color)
Q_INVOKABLE QColor tintWithAlpha(const QColor &targetColor, const QColor &tintColor, double alpha)
Q_INVOKABLE QColor linearInterpolation(const QColor &one, const QColor &two, double balance)
Q_INVOKABLE ColorUtils::Brightness brightnessForColor(const QColor &color)
KGUIADDONS_EXPORT qreal luma(const QColor &)
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
float alphaF() const const
float blueF() const const
QColor fromHsv(int h, int s, int v, int a)
QColor fromRgbF(float r, float g, float b, float a)
float greenF() const const
int saturation() const const
bool hasProperty(const QString &name) const const
QJSValue property(const QString &name) const const
QVariant fromValue(T &&value)