9#include "loggingcategory.h"
15ColorUtils::ColorUtils(
QObject *parent)
23 return (0.299 * color.
red() + 0.587 * color.
green() + 0.114 * color.
blue()) / 255;
26 return luma(color) > 0.5 ? ColorUtils::Brightness::Light : ColorUtils::Brightness::Dark;
31 return (0.299 * color.
red() + 0.587 * color.
green() + 0.114 * color.
blue()) / 255;
36 const auto foregroundAlpha = foreground.
alpha();
37 const auto inverseForegroundAlpha = 0xff - foregroundAlpha;
38 const auto backgroundAlpha = background.
alpha();
40 if (foregroundAlpha == 0x00) {
44 if (backgroundAlpha == 0xff) {
45 return QColor::fromRgb((foregroundAlpha * foreground.
red()) + (inverseForegroundAlpha * background.
red()),
46 (foregroundAlpha * foreground.
green()) + (inverseForegroundAlpha * background.
green()),
47 (foregroundAlpha * foreground.
blue()) + (inverseForegroundAlpha * background.
blue()),
50 const auto inverseBackgroundAlpha = (backgroundAlpha * inverseForegroundAlpha) / 255;
51 const auto finalAlpha = foregroundAlpha + inverseBackgroundAlpha;
52 Q_ASSERT(finalAlpha != 0x00);
53 return QColor::fromRgb((foregroundAlpha * foreground.
red()) + (inverseBackgroundAlpha * background.
red()),
54 (foregroundAlpha * foreground.
green()) + (inverseBackgroundAlpha * background.
green()),
55 (foregroundAlpha * foreground.
blue()) + (inverseBackgroundAlpha * background.
blue()),
62 auto linearlyInterpolateDouble = [](
double one,
double two,
double factor) {
63 return one + (two - one) * factor;
70 auto sourceHue = std::max(one.
hueF() > 0.0 ? one.
hueF() : two.hueF(), 0.0f);
71 auto targetHue = std::max(two.
hueF() > 0.0 ? two.
hueF() : one.hueF(), 0.0f);
73 auto hue = std::fmod(linearlyInterpolateDouble(sourceHue, targetHue, balance), 1.0);
74 auto saturation = std::clamp(linearlyInterpolateDouble(one.
saturationF(), two.
saturationF(), balance), 0.0, 1.0);
75 auto value = std::clamp(linearlyInterpolateDouble(one.
valueF(), two.
valueF(), balance), 0.0, 1.0);
76 auto alpha = std::clamp(linearlyInterpolateDouble(one.
alphaF(), two.
alphaF(), balance), 0.0, 1.0);
82struct ParsedAdjustments {
88 double saturation = 0.0;
94ParsedAdjustments parseAdjustments(
const QJSValue &value)
96 ParsedAdjustments parsed;
98 auto checkProperty = [](
const QJSValue &value,
const QString &property) {
100 auto val = value.
property(property);
101 if (val.isNumber()) {
108 std::vector<std::pair<QString, double &>> items{{QStringLiteral(
"red"), parsed.red},
109 {QStringLiteral(
"green"), parsed.green},
110 {QStringLiteral(
"blue"), parsed.blue},
112 {QStringLiteral(
"hue"), parsed.hue},
113 {QStringLiteral(
"saturation"), parsed.saturation},
114 {QStringLiteral(
"value"), parsed.value},
116 {QStringLiteral(
"alpha"), parsed.alpha}};
118 for (
const auto &item : items) {
119 auto val = checkProperty(value, item.first);
121 item.second = val.toDouble();
125 if ((parsed.red || parsed.green || parsed.blue) && (parsed.hue || parsed.saturation || parsed.value)) {
126 qCCritical(KirigamiLog) <<
"It is an error to have both RGB and HSV values in an adjustment.";
134 auto adjusts = parseAdjustments(adjustments);
136 if (qBound(-360.0, adjusts.hue, 360.0) != adjusts.hue) {
137 qCCritical(KirigamiLog) <<
"Hue is out of bounds";
139 if (qBound(-255.0, adjusts.red, 255.0) != adjusts.red) {
140 qCCritical(KirigamiLog) <<
"Red is out of bounds";
142 if (qBound(-255.0, adjusts.green, 255.0) != adjusts.green) {
143 qCCritical(KirigamiLog) <<
"Green is out of bounds";
145 if (qBound(-255.0, adjusts.blue, 255.0) != adjusts.blue) {
146 qCCritical(KirigamiLog) <<
"Green is out of bounds";
148 if (qBound(-255.0, adjusts.saturation, 255.0) != adjusts.saturation) {
149 qCCritical(KirigamiLog) <<
"Saturation is out of bounds";
151 if (qBound(-255.0, adjusts.value, 255.0) != adjusts.value) {
152 qCCritical(KirigamiLog) <<
"Value is out of bounds";
154 if (qBound(-255.0, adjusts.alpha, 255.0) != adjusts.alpha) {
155 qCCritical(KirigamiLog) <<
"Alpha is out of bounds";
161 copy.setAlpha(qBound(0.0,
copy.alpha() + adjusts.alpha, 255.0));
164 if (adjusts.red || adjusts.green || adjusts.blue) {
165 copy.setRed(qBound(0.0,
copy.red() + adjusts.red, 255.0));
166 copy.setGreen(qBound(0.0,
copy.green() + adjusts.green, 255.0));
167 copy.setBlue(qBound(0.0,
copy.blue() + adjusts.blue, 255.0));
168 }
else if (adjusts.hue || adjusts.saturation || adjusts.value) {
169 copy.setHsv(std::fmod(
copy.hue() + adjusts.hue, 360.0),
170 qBound(0.0,
copy.saturation() + adjusts.saturation, 255.0),
171 qBound(0.0,
copy.value() + adjusts.value, 255.0),
180 auto adjusts = parseAdjustments(adjustments);
183 if (qBound(-100.0, adjusts.red, 100.00) != adjusts.red) {
184 qCCritical(KirigamiLog) <<
"Red is out of bounds";
186 if (qBound(-100.0, adjusts.green, 100.00) != adjusts.green) {
187 qCCritical(KirigamiLog) <<
"Green is out of bounds";
189 if (qBound(-100.0, adjusts.blue, 100.00) != adjusts.blue) {
190 qCCritical(KirigamiLog) <<
"Blue is out of bounds";
192 if (qBound(-100.0, adjusts.saturation, 100.00) != adjusts.saturation) {
193 qCCritical(KirigamiLog) <<
"Saturation is out of bounds";
195 if (qBound(-100.0, adjusts.value, 100.00) != adjusts.value) {
196 qCCritical(KirigamiLog) <<
"Value is out of bounds";
198 if (qBound(-100.0, adjusts.alpha, 100.00) != adjusts.alpha) {
199 qCCritical(KirigamiLog) <<
"Alpha is out of bounds";
202 if (adjusts.hue != 0) {
203 qCCritical(KirigamiLog) <<
"Hue cannot be scaled";
206 auto shiftToAverage = [](
double current,
double factor) {
207 auto scale = qBound(-100.0, factor, 100.0) / 100;
208 return current + (scale > 0 ? 255 - current : current) * scale;
212 copy.setAlpha(qBound(0.0, shiftToAverage(
copy.alpha(), adjusts.alpha), 255.0));
215 if (adjusts.red || adjusts.green || adjusts.blue) {
216 copy.setRed(qBound(0.0, shiftToAverage(
copy.red(), adjusts.red), 255.0));
217 copy.setGreen(qBound(0.0, shiftToAverage(
copy.green(), adjusts.green), 255.0));
218 copy.setBlue(qBound(0.0, shiftToAverage(
copy.blue(), adjusts.blue), 255.0));
221 qBound(0.0, shiftToAverage(
copy.saturation(), adjusts.saturation), 255.0),
222 qBound(0.0, shiftToAverage(
copy.value(), adjusts.value), 255.0),
231 qreal tintAlpha = tintColor.
alphaF() * alpha;
232 qreal inverseAlpha = 1.0 - tintAlpha;
234 if (qFuzzyCompare(tintAlpha, 1.0)) {
236 }
else if (qFuzzyIsNull(tintAlpha)) {
241 tintColor.
greenF() * tintAlpha + targetColor.
greenF() * inverseAlpha,
242 tintColor.
blueF() * tintAlpha + targetColor.
blueF() * inverseAlpha,
243 tintAlpha + inverseAlpha * targetColor.
alphaF());
246ColorUtils::XYZColor ColorUtils::colorToXYZ(
const QColor &color)
249 qreal r = color.
redF();
251 qreal b = color.
blueF();
253 auto correct = [](qreal &v) {
255 v = std::pow((v + 0.055) / 1.055, 2.4);
266 const qreal x = r * 0.4124 + g * 0.3576 + b * 0.1805;
267 const qreal y = r * 0.2126 + g * 0.7152 + b * 0.0722;
268 const qreal z = r * 0.0193 + g * 0.1192 + b * 0.9505;
270 return XYZColor{x, y, z};
273ColorUtils::LabColor ColorUtils::colorToLab(
const QColor &color)
276 const auto xyz = colorToXYZ(color);
279 qreal x = xyz.x / 0.95047;
280 qreal y = xyz.y / 1.0;
281 qreal z = xyz.z / 1.08883;
283 auto pivot = [](qreal &v) {
285 v = std::pow(v, 1.0 / 3.0);
287 v = (7.787 * v) + (16.0 / 116.0);
296 labColor.l = std::max(0.0, (116 * y) - 16);
297 labColor.a = 500 * (x - y);
298 labColor.b = 200 * (y - z);
305 LabColor labColor = colorToLab(color);
308 return sqrt(pow(labColor.a, 2) + pow(labColor.b, 2));
311qreal ColorUtils::luminance(
const QColor &color)
313 const auto &xyz = colorToXYZ(color);
318#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 &)
KGUIADDONS_EXPORT qreal hue(const QColor &)
const QList< QKeySequence > & copy()
float alphaF() const const
float blueF() const const
QColor fromHsvF(float h, float s, float v, float a)
QColor fromRgbF(float r, float g, float b, float a)
float greenF() const const
float saturationF() const const
float valueF() const const
bool hasProperty(const QString &name) const const
QJSValue property(const QString &name) const const
QVariant fromValue(T &&value)