14#include "kirigamiplatform_logging.h"
16ColorUtils::ColorUtils(
QObject *parent)
24 return (0.299 * color.
red() + 0.587 * color.
green() + 0.114 * color.
blue()) / 255;
27 return luma(color) > 0.5 ? ColorUtils::Brightness::Light : ColorUtils::Brightness::Dark;
32 return (0.299 * color.
red() + 0.587 * color.
green() + 0.114 * color.
blue()) / 255;
37 const auto foregroundAlpha = foreground.
alpha();
38 const auto inverseForegroundAlpha = 0xff - foregroundAlpha;
39 const auto backgroundAlpha = background.
alpha();
41 if (foregroundAlpha == 0x00) {
45 if (backgroundAlpha == 0xff) {
46 return QColor::fromRgb((foregroundAlpha * foreground.
red()) + (inverseForegroundAlpha * background.
red()),
47 (foregroundAlpha * foreground.
green()) + (inverseForegroundAlpha * background.
green()),
48 (foregroundAlpha * foreground.
blue()) + (inverseForegroundAlpha * background.
blue()),
51 const auto inverseBackgroundAlpha = (backgroundAlpha * inverseForegroundAlpha) / 255;
52 const auto finalAlpha = foregroundAlpha + inverseBackgroundAlpha;
53 Q_ASSERT(finalAlpha != 0x00);
54 return QColor::fromRgb((foregroundAlpha * foreground.
red()) + (inverseBackgroundAlpha * background.
red()),
55 (foregroundAlpha * foreground.
green()) + (inverseBackgroundAlpha * background.
green()),
56 (foregroundAlpha * foreground.
blue()) + (inverseBackgroundAlpha * background.
blue()),
63 auto linearlyInterpolateDouble = [](
double one,
double two,
double factor) {
64 return one + (two - one) * factor;
71 auto sourceHue = std::max(one.
hueF() > 0.0 ? one.
hueF() : two.
hueF(), 0.0f);
72 auto targetHue = std::max(two.
hueF() > 0.0 ? two.
hueF() : one.
hueF(), 0.0f);
74 auto hue = std::fmod(linearlyInterpolateDouble(sourceHue, targetHue, balance), 1.0);
75 auto saturation = std::clamp(linearlyInterpolateDouble(one.
saturationF(), two.
saturationF(), balance), 0.0, 1.0);
76 auto value = std::clamp(linearlyInterpolateDouble(one.
valueF(), two.
valueF(), balance), 0.0, 1.0);
77 auto alpha = std::clamp(linearlyInterpolateDouble(one.
alphaF(), two.
alphaF(), balance), 0.0, 1.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},
117 {QStringLiteral(
"alpha"), parsed.alpha}};
119 for (
const auto &item : items) {
120 auto val = checkProperty(value, item.first);
122 item.second = val.toDouble();
126 if ((parsed.red || parsed.green || parsed.blue) && (parsed.hue || parsed.saturation || parsed.value)) {
127 qCCritical(KirigamiPlatform) <<
"It is an error to have both RGB and HSV values in an adjustment.";
135 auto adjusts = parseAdjustments(adjustments);
137 if (qBound(-360.0, adjusts.hue, 360.0) != adjusts.hue) {
138 qCCritical(KirigamiPlatform) <<
"Hue is out of bounds";
140 if (qBound(-255.0, adjusts.red, 255.0) != adjusts.red) {
141 qCCritical(KirigamiPlatform) <<
"Red is out of bounds";
143 if (qBound(-255.0, adjusts.green, 255.0) != adjusts.green) {
144 qCCritical(KirigamiPlatform) <<
"Green is out of bounds";
146 if (qBound(-255.0, adjusts.blue, 255.0) != adjusts.blue) {
147 qCCritical(KirigamiPlatform) <<
"Green is out of bounds";
149 if (qBound(-255.0, adjusts.saturation, 255.0) != adjusts.saturation) {
150 qCCritical(KirigamiPlatform) <<
"Saturation is out of bounds";
152 if (qBound(-255.0, adjusts.value, 255.0) != adjusts.value) {
153 qCCritical(KirigamiPlatform) <<
"Value is out of bounds";
155 if (qBound(-255.0, adjusts.alpha, 255.0) != adjusts.alpha) {
156 qCCritical(KirigamiPlatform) <<
"Alpha is out of bounds";
162 copy.setAlpha(qBound(0.0,
copy.alpha() + adjusts.alpha, 255.0));
165 if (adjusts.red || adjusts.green || adjusts.blue) {
166 copy.setRed(qBound(0.0,
copy.red() + adjusts.red, 255.0));
167 copy.setGreen(qBound(0.0,
copy.green() + adjusts.green, 255.0));
168 copy.setBlue(qBound(0.0,
copy.blue() + adjusts.blue, 255.0));
169 }
else if (adjusts.hue || adjusts.saturation || adjusts.value) {
170 copy.setHsv(std::fmod(
copy.hue() + adjusts.hue, 360.0),
171 qBound(0.0,
copy.saturation() + adjusts.saturation, 255.0),
172 qBound(0.0,
copy.value() + adjusts.value, 255.0),
181 auto adjusts = parseAdjustments(adjustments);
184 if (qBound(-100.0, adjusts.red, 100.00) != adjusts.red) {
185 qCCritical(KirigamiPlatform) <<
"Red is out of bounds";
187 if (qBound(-100.0, adjusts.green, 100.00) != adjusts.green) {
188 qCCritical(KirigamiPlatform) <<
"Green is out of bounds";
190 if (qBound(-100.0, adjusts.blue, 100.00) != adjusts.blue) {
191 qCCritical(KirigamiPlatform) <<
"Blue is out of bounds";
193 if (qBound(-100.0, adjusts.saturation, 100.00) != adjusts.saturation) {
194 qCCritical(KirigamiPlatform) <<
"Saturation is out of bounds";
196 if (qBound(-100.0, adjusts.value, 100.00) != adjusts.value) {
197 qCCritical(KirigamiPlatform) <<
"Value is out of bounds";
199 if (qBound(-100.0, adjusts.alpha, 100.00) != adjusts.alpha) {
200 qCCritical(KirigamiPlatform) <<
"Alpha is out of bounds";
203 if (adjusts.hue != 0) {
204 qCCritical(KirigamiPlatform) <<
"Hue cannot be scaled";
207 auto shiftToAverage = [](
double current,
double factor) {
208 auto scale = qBound(-100.0, factor, 100.0) / 100;
209 return current + (scale > 0 ? 255 - current : current) * scale;
213 copy.setAlpha(qBound(0.0, shiftToAverage(
copy.alpha(), adjusts.alpha), 255.0));
216 if (adjusts.red || adjusts.green || adjusts.blue) {
217 copy.setRed(qBound(0.0, shiftToAverage(
copy.red(), adjusts.red), 255.0));
218 copy.setGreen(qBound(0.0, shiftToAverage(
copy.green(), adjusts.green), 255.0));
219 copy.setBlue(qBound(0.0, shiftToAverage(
copy.blue(), adjusts.blue), 255.0));
222 qBound(0.0, shiftToAverage(
copy.saturation(), adjusts.saturation), 255.0),
223 qBound(0.0, shiftToAverage(
copy.value(), adjusts.value), 255.0),
232 qreal tintAlpha = tintColor.
alphaF() * alpha;
233 qreal inverseAlpha = 1.0 - tintAlpha;
235 if (qFuzzyCompare(tintAlpha, 1.0)) {
237 }
else if (qFuzzyIsNull(tintAlpha)) {
242 tintColor.
greenF() * tintAlpha + targetColor.
greenF() * inverseAlpha,
243 tintColor.
blueF() * tintAlpha + targetColor.
blueF() * inverseAlpha,
244 tintAlpha + inverseAlpha * targetColor.
alphaF());
247ColorUtils::XYZColor ColorUtils::colorToXYZ(
const QColor &color)
250 qreal r = color.
redF();
252 qreal b = color.
blueF();
254 auto correct = [](qreal &v) {
256 v = std::pow((v + 0.055) / 1.055, 2.4);
267 const qreal x = r * 0.4124 + g * 0.3576 + b * 0.1805;
268 const qreal y = r * 0.2126 + g * 0.7152 + b * 0.0722;
269 const qreal z = r * 0.0193 + g * 0.1192 + b * 0.9505;
271 return XYZColor{x, y, z};
274ColorUtils::LabColor ColorUtils::colorToLab(
const QColor &color)
277 const auto xyz = colorToXYZ(color);
280 qreal x = xyz.x / 0.95047;
281 qreal y = xyz.y / 1.0;
282 qreal z = xyz.z / 1.08883;
284 auto pivot = [](qreal &v) {
286 v = std::pow(v, 1.0 / 3.0);
288 v = (7.787 * v) + (16.0 / 116.0);
297 labColor.l = std::max(0.0, (116 * y) - 16);
298 labColor.a = 500 * (x - y);
299 labColor.b = 200 * (y - z);
306 LabColor labColor = colorToLab(color);
309 return sqrt(pow(labColor.a, 2) + pow(labColor.b, 2));
312qreal ColorUtils::luminance(
const QColor &color)
314 const auto &xyz = colorToXYZ(color);
319#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)