File indexing completed on 2024-10-20 03:38:30
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 KColorSpaces::KHCY 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; 0103 } 0104 0105 static qreal tintHelperLuma(const QColor &base, qreal baseLuma, const QColor &color, qreal amount) 0106 { 0107 qreal result(KColorUtils::luma(KColorUtils::mix(base, color, pow(amount, 0.3)))); 0108 result = mixQreal(baseLuma, result, amount); 0109 0110 return result; 0111 } 0112 0113 QColor KColorUtils::tint(const QColor &base, const QColor &color, qreal amount) 0114 { 0115 if (amount <= 0.0) { 0116 return base; 0117 } 0118 if (amount >= 1.0) { 0119 return color; 0120 } 0121 if (qIsNaN(amount)) { 0122 return base; 0123 } 0124 0125 qreal baseLuma = luma(base); // cache value because luma call is expensive 0126 double ri = contrastRatioForLuma(baseLuma, luma(color)); 0127 double rg = 1.0 + ((ri + 1.0) * amount * amount * amount); 0128 double u = 1.0; 0129 double l = 0.0; 0130 double a = 0.5; 0131 for (int i = 12; i; --i) { 0132 a = 0.5 * (l + u); 0133 qreal resultLuma = tintHelperLuma(base, baseLuma, color, a); 0134 double ra = contrastRatioForLuma(baseLuma, resultLuma); 0135 if (ra > rg) { 0136 u = a; 0137 } else { 0138 l = a; 0139 } 0140 } 0141 return tintHelper(base, baseLuma, color, a).qColor(); 0142 } 0143 0144 QColor KColorUtils::mix(const QColor &c1, const QColor &c2, qreal bias) 0145 { 0146 if (bias <= 0.0) { 0147 return c1; 0148 } 0149 if (bias >= 1.0) { 0150 return c2; 0151 } 0152 if (qIsNaN(bias)) { 0153 return c1; 0154 } 0155 0156 qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias); 0157 if (a <= 0.0) { 0158 return Qt::transparent; 0159 } 0160 0161 qreal r = qBound(0.0, mixQreal(c1.redF() * c1.alphaF(), c2.redF() * c2.alphaF(), bias), 1.0) / a; 0162 qreal g = qBound(0.0, mixQreal(c1.greenF() * c1.alphaF(), c2.greenF() * c2.alphaF(), bias), 1.0) / a; 0163 qreal b = qBound(0.0, mixQreal(c1.blueF() * c1.alphaF(), c2.blueF() * c2.alphaF(), bias), 1.0) / a; 0164 0165 return QColor::fromRgbF(r, g, b, a); 0166 } 0167 0168 QColor KColorUtils::overlayColors(const QColor &base, const QColor &paint, QPainter::CompositionMode comp) 0169 { 0170 // This isn't the fastest way, but should be "fast enough". 0171 // It's also the only safe way to use QPainter::CompositionMode 0172 QImage img(1, 1, QImage::Format_ARGB32_Premultiplied); 0173 QPainter p(&img); 0174 QColor start = base; 0175 start.setAlpha(255); // opaque 0176 p.fillRect(0, 0, 1, 1, start); 0177 p.setCompositionMode(comp); 0178 p.fillRect(0, 0, 1, 1, paint); 0179 p.end(); 0180 return img.pixel(0, 0); 0181 }