File indexing completed on 2023-12-03 07:42:26
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org> 0004 SPDX-FileCopyrightText: 2007 Zack Rusin <zack@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 #include "kcolorspaces_p.h" 0009 #include "kguiaddons_colorhelpers_p.h" 0010 #include <kcolorutils.h> 0011 0012 #include <QColor> 0013 #include <QImage> 0014 #include <QtNumeric> // qIsNaN 0015 0016 #include <math.h> 0017 0018 // BEGIN internal helper functions 0019 static inline qreal mixQreal(qreal a, qreal b, qreal bias) 0020 { 0021 return a + (b - a) * bias; 0022 } 0023 // END internal helper functions 0024 0025 qreal KColorUtils::hue(const QColor &color) 0026 { 0027 return KColorSpaces::KHCY::hue(color); 0028 } 0029 0030 qreal KColorUtils::chroma(const QColor &color) 0031 { 0032 return KColorSpaces::KHCY::chroma(color); 0033 } 0034 0035 qreal KColorUtils::luma(const QColor &color) 0036 { 0037 return KColorSpaces::KHCY::luma(color); 0038 } 0039 0040 void KColorUtils::getHcy(const QColor &color, qreal *h, qreal *c, qreal *y, qreal *a) 0041 { 0042 if (!c || !h || !y) { 0043 return; 0044 } 0045 KColorSpaces::KHCY khcy(color); 0046 *c = khcy.c; 0047 *h = khcy.h + (khcy.h < 0.0 ? 1.0 : 0.0); 0048 *y = khcy.y; 0049 if (a) { 0050 *a = khcy.a; 0051 } 0052 } 0053 0054 QColor KColorUtils::hcyColor(qreal h, qreal c, qreal y, qreal a) 0055 { 0056 return KColorSpaces::KHCY(h, c, y, a).qColor(); 0057 } 0058 0059 static qreal contrastRatioForLuma(qreal y1, qreal y2) 0060 { 0061 if (y1 > y2) { 0062 return (y1 + 0.05) / (y2 + 0.05); 0063 } else { 0064 return (y2 + 0.05) / (y1 + 0.05); 0065 } 0066 } 0067 0068 qreal KColorUtils::contrastRatio(const QColor &c1, const QColor &c2) 0069 { 0070 return contrastRatioForLuma(luma(c1), luma(c2)); 0071 } 0072 0073 QColor KColorUtils::lighten(const QColor &color, qreal ky, qreal kc) 0074 { 0075 KColorSpaces::KHCY c(color); 0076 c.y = 1.0 - normalize((1.0 - c.y) * (1.0 - ky)); 0077 c.c = 1.0 - normalize((1.0 - c.c) * kc); 0078 return c.qColor(); 0079 } 0080 0081 QColor KColorUtils::darken(const QColor &color, qreal ky, qreal kc) 0082 { 0083 KColorSpaces::KHCY c(color); 0084 c.y = normalize(c.y * (1.0 - ky)); 0085 c.c = normalize(c.c * kc); 0086 return c.qColor(); 0087 } 0088 0089 QColor KColorUtils::shade(const QColor &color, qreal ky, qreal kc) 0090 { 0091 KColorSpaces::KHCY c(color); 0092 c.y = normalize(c.y + ky); 0093 c.c = normalize(c.c + kc); 0094 return c.qColor(); 0095 } 0096 0097 static QColor tintHelper(const QColor &base, qreal baseLuma, const QColor &color, qreal amount) 0098 { 0099 KColorSpaces::KHCY result(KColorUtils::mix(base, color, pow(amount, 0.3))); 0100 result.y = mixQreal(baseLuma, result.y, amount); 0101 0102 return result.qColor(); 0103 } 0104 0105 QColor KColorUtils::tint(const QColor &base, const QColor &color, qreal amount) 0106 { 0107 if (amount <= 0.0) { 0108 return base; 0109 } 0110 if (amount >= 1.0) { 0111 return color; 0112 } 0113 if (qIsNaN(amount)) { 0114 return base; 0115 } 0116 0117 qreal baseLuma = luma(base); // cache value because luma call is expensive 0118 double ri = contrastRatioForLuma(baseLuma, luma(color)); 0119 double rg = 1.0 + ((ri + 1.0) * amount * amount * amount); 0120 double u = 1.0; 0121 double l = 0.0; 0122 QColor result; 0123 for (int i = 12; i; --i) { 0124 double a = 0.5 * (l + u); 0125 result = tintHelper(base, baseLuma, color, a); 0126 double ra = contrastRatioForLuma(baseLuma, luma(result)); 0127 if (ra > rg) { 0128 u = a; 0129 } else { 0130 l = a; 0131 } 0132 } 0133 return result; 0134 } 0135 0136 QColor KColorUtils::mix(const QColor &c1, const QColor &c2, qreal bias) 0137 { 0138 if (bias <= 0.0) { 0139 return c1; 0140 } 0141 if (bias >= 1.0) { 0142 return c2; 0143 } 0144 if (qIsNaN(bias)) { 0145 return c1; 0146 } 0147 0148 qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias); 0149 if (a <= 0.0) { 0150 return Qt::transparent; 0151 } 0152 0153 qreal r = qBound(0.0, mixQreal(c1.redF() * c1.alphaF(), c2.redF() * c2.alphaF(), bias), 1.0) / a; 0154 qreal g = qBound(0.0, mixQreal(c1.greenF() * c1.alphaF(), c2.greenF() * c2.alphaF(), bias), 1.0) / a; 0155 qreal b = qBound(0.0, mixQreal(c1.blueF() * c1.alphaF(), c2.blueF() * c2.alphaF(), bias), 1.0) / a; 0156 0157 return QColor::fromRgbF(r, g, b, a); 0158 } 0159 0160 QColor KColorUtils::overlayColors(const QColor &base, const QColor &paint, QPainter::CompositionMode comp) 0161 { 0162 // This isn't the fastest way, but should be "fast enough". 0163 // It's also the only safe way to use QPainter::CompositionMode 0164 QImage img(1, 1, QImage::Format_ARGB32_Premultiplied); 0165 QPainter p(&img); 0166 QColor start = base; 0167 start.setAlpha(255); // opaque 0168 p.fillRect(0, 0, 1, 1, start); 0169 p.setCompositionMode(comp); 0170 p.fillRect(0, 0, 1, 1, paint); 0171 p.end(); 0172 return img.pixel(0, 0); 0173 }