1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
3 SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
4 SPDX-FileCopyrightText: 2007 Zack Rusin <zack@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8#include "kcolorspaces_p.h"
9#include "kguiaddons_colorhelpers_p.h"
10#include <kcolorutils.h>
11
12#include <QColor>
13#include <QImage>
14#include <QtNumeric> // qIsNaN
15
16#include <math.h>
17
18// BEGIN internal helper functions
19static inline qreal mixQreal(qreal a, qreal b, qreal bias)
20{
21 return a + (b - a) * bias;
22}
23// END internal helper functions
24
25qreal KColorUtils::hue(const QColor &color)
26{
27 return KColorSpaces::KHCY::hue(color);
28}
29
30qreal KColorUtils::chroma(const QColor &color)
31{
32 return KColorSpaces::KHCY::chroma(color);
33}
34
35qreal KColorUtils::luma(const QColor &color)
36{
37 return KColorSpaces::KHCY::luma(color);
38}
39
40void KColorUtils::getHcy(const QColor &color, qreal *h, qreal *c, qreal *y, qreal *a)
41{
42 if (!c || !h || !y) {
43 return;
44 }
45 KColorSpaces::KHCY khcy(color);
46 *c = khcy.c;
47 *h = khcy.h + (khcy.h < 0.0 ? 1.0 : 0.0);
48 *y = khcy.y;
49 if (a) {
50 *a = khcy.a;
51 }
52}
53
54QColor KColorUtils::hcyColor(qreal h, qreal c, qreal y, qreal a)
55{
56 return KColorSpaces::KHCY(h, c, y, a).qColor();
57}
58
59static qreal contrastRatioForLuma(qreal y1, qreal y2)
60{
61 if (y1 > y2) {
62 return (y1 + 0.05) / (y2 + 0.05);
63 } else {
64 return (y2 + 0.05) / (y1 + 0.05);
65 }
66}
67
68qreal KColorUtils::contrastRatio(const QColor &c1, const QColor &c2)
69{
70 return contrastRatioForLuma(y1: luma(color: c1), y2: luma(color: c2));
71}
72
73QColor KColorUtils::lighten(const QColor &color, qreal ky, qreal kc)
74{
75 KColorSpaces::KHCY c(color);
76 c.y = 1.0 - normalize(a: (1.0 - c.y) * (1.0 - ky));
77 c.c = 1.0 - normalize(a: (1.0 - c.c) * kc);
78 return c.qColor();
79}
80
81QColor KColorUtils::darken(const QColor &color, qreal ky, qreal kc)
82{
83 KColorSpaces::KHCY c(color);
84 c.y = normalize(a: c.y * (1.0 - ky));
85 c.c = normalize(a: c.c * kc);
86 return c.qColor();
87}
88
89QColor KColorUtils::shade(const QColor &color, qreal ky, qreal kc)
90{
91 KColorSpaces::KHCY c(color);
92 c.y = normalize(a: c.y + ky);
93 c.c = normalize(a: c.c + kc);
94 return c.qColor();
95}
96
97static KColorSpaces::KHCY tintHelper(const QColor &base, qreal baseLuma, const QColor &color, qreal amount)
98{
99 KColorSpaces::KHCY result(KColorUtils::mix(c1: base, c2: color, bias: pow(x: amount, y: 0.3)));
100 result.y = mixQreal(a: baseLuma, b: result.y, bias: amount);
101
102 return result;
103}
104
105static qreal tintHelperLuma(const QColor &base, qreal baseLuma, const QColor &color, qreal amount)
106{
107 qreal result(KColorUtils::luma(color: KColorUtils::mix(c1: base, c2: color, bias: pow(x: amount, y: 0.3))));
108 result = mixQreal(a: baseLuma, b: result, bias: amount);
109
110 return result;
111}
112
113QColor KColorUtils::tint(const QColor &base, const QColor &color, qreal amount)
114{
115 if (amount <= 0.0) {
116 return base;
117 }
118 if (amount >= 1.0) {
119 return color;
120 }
121 if (qIsNaN(d: amount)) {
122 return base;
123 }
124
125 qreal baseLuma = luma(color: base); // cache value because luma call is expensive
126 double ri = contrastRatioForLuma(y1: baseLuma, y2: luma(color));
127 double rg = 1.0 + ((ri + 1.0) * amount * amount * amount);
128 double u = 1.0;
129 double l = 0.0;
130 double a = 0.5;
131 for (int i = 12; i; --i) {
132 a = 0.5 * (l + u);
133 qreal resultLuma = tintHelperLuma(base, baseLuma, color, amount: a);
134 double ra = contrastRatioForLuma(y1: baseLuma, y2: resultLuma);
135 if (ra > rg) {
136 u = a;
137 } else {
138 l = a;
139 }
140 }
141 return tintHelper(base, baseLuma, color, amount: a).qColor();
142}
143
144QColor KColorUtils::mix(const QColor &c1, const QColor &c2, qreal bias)
145{
146 if (bias <= 0.0) {
147 return c1;
148 }
149 if (bias >= 1.0) {
150 return c2;
151 }
152 if (qIsNaN(d: bias)) {
153 return c1;
154 }
155
156 qreal a = mixQreal(a: c1.alphaF(), b: c2.alphaF(), bias);
157 if (a <= 0.0) {
158 return Qt::transparent;
159 }
160
161 qreal r = qBound(min: 0.0, val: mixQreal(a: c1.redF() * c1.alphaF(), b: c2.redF() * c2.alphaF(), bias), max: 1.0) / a;
162 qreal g = qBound(min: 0.0, val: mixQreal(a: c1.greenF() * c1.alphaF(), b: c2.greenF() * c2.alphaF(), bias), max: 1.0) / a;
163 qreal b = qBound(min: 0.0, val: mixQreal(a: c1.blueF() * c1.alphaF(), b: c2.blueF() * c2.alphaF(), bias), max: 1.0) / a;
164
165 return QColor::fromRgbF(r, g, b, a);
166}
167
168QColor KColorUtils::overlayColors(const QColor &base, const QColor &paint, QPainter::CompositionMode comp)
169{
170 // This isn't the fastest way, but should be "fast enough".
171 // It's also the only safe way to use QPainter::CompositionMode
172 QImage img(1, 1, QImage::Format_ARGB32_Premultiplied);
173 QPainter p(&img);
174 QColor start = base;
175 start.setAlpha(255); // opaque
176 p.fillRect(x: 0, y: 0, w: 1, h: 1, b: start);
177 p.setCompositionMode(comp);
178 p.fillRect(x: 0, y: 0, w: 1, h: 1, b: paint);
179 p.end();
180 return img.pixel(x: 0, y: 0);
181}
182

source code of kguiaddons/src/colors/kcolorutils.cpp