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(luma(c1), luma(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((1.0 - c.y) * (1.0 - ky));
77 c.c = 1.0 - normalize((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(c.y * (1.0 - ky));
85 c.c = normalize(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(c.y + ky);
93 c.c = normalize(c.c + kc);
94 return c.qColor();
95}
96
97static QColor tintHelper(const QColor &base, qreal baseLuma, const QColor &color, qreal amount)
98{
99 KColorSpaces::KHCY result(KColorUtils::mix(base, color, pow(amount, 0.3)));
100 result.y = mixQreal(baseLuma, result.y, amount);
101
102 return result.qColor();
103}
104
105QColor KColorUtils::tint(const QColor &base, const QColor &color, qreal amount)
106{
107 if (amount <= 0.0) {
108 return base;
109 }
110 if (amount >= 1.0) {
111 return color;
112 }
113 if (qIsNaN(amount)) {
114 return base;
115 }
116
117 qreal baseLuma = luma(base); // cache value because luma call is expensive
118 double ri = contrastRatioForLuma(baseLuma, luma(color));
119 double rg = 1.0 + ((ri + 1.0) * amount * amount * amount);
120 double u = 1.0;
121 double l = 0.0;
122 QColor result;
123 for (int i = 12; i; --i) {
124 double a = 0.5 * (l + u);
125 result = tintHelper(base, baseLuma, color, a);
126 double ra = contrastRatioForLuma(baseLuma, luma(result));
127 if (ra > rg) {
128 u = a;
129 } else {
130 l = a;
131 }
132 }
133 return result;
134}
135
136QColor KColorUtils::mix(const QColor &c1, const QColor &c2, qreal bias)
137{
138 if (bias <= 0.0) {
139 return c1;
140 }
141 if (bias >= 1.0) {
142 return c2;
143 }
144 if (qIsNaN(bias)) {
145 return c1;
146 }
147
148 qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias);
149 if (a <= 0.0) {
150 return Qt::transparent;
151 }
152
153 qreal r = qBound(0.0, mixQreal(c1.redF() * c1.alphaF(), c2.redF() * c2.alphaF(), bias), 1.0) / a;
154 qreal g = qBound(0.0, mixQreal(c1.greenF() * c1.alphaF(), c2.greenF() * c2.alphaF(), bias), 1.0) / a;
155 qreal b = qBound(0.0, mixQreal(c1.blueF() * c1.alphaF(), c2.blueF() * c2.alphaF(), bias), 1.0) / a;
156
157 return QColor::fromRgbF(r, g, b, a);
158}
159
160QColor KColorUtils::overlayColors(const QColor &base, const QColor &paint, QPainter::CompositionMode comp)
161{
162 // This isn't the fastest way, but should be "fast enough".
163 // It's also the only safe way to use QPainter::CompositionMode
164 QImage img(1, 1, QImage::Format_ARGB32_Premultiplied);
165 QPainter p(&img);
166 QColor start = base;
167 start.setAlpha(255); // opaque
168 p.fillRect(0, 0, 1, 1, start);
169 p.setCompositionMode(comp);
170 p.fillRect(0, 0, 1, 1, paint);
171 p.end();
172 return img.pixel(0, 0);
173}
174

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