File indexing completed on 2024-05-19 16:50:18
0001 /***************************************************************************** 0002 * Copyright 2009 - 2010 Craig Drummond <craig.p.drummond@gmail.com> * 0003 * Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU Lesser General Public License as * 0007 * published by the Free Software Foundation; either version 2.1 of the * 0008 * License, or (at your option) version 3, or any later version accepted * 0009 * by the membership of KDE e.V. (or its successor approved by the * 0010 * membership of KDE e.V.), which shall act as a proxy defined in * 0011 * Section 6 of version 3 of the license. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, * 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 0016 * Lesser General Public License for more details. * 0017 * * 0018 * You should have received a copy of the GNU Lesser General Public * 0019 * License along with this library. If not, * 0020 * see <http://www.gnu.org/licenses/>. * 0021 *****************************************************************************/ 0022 0023 #include "color.h" 0024 0025 static void 0026 qtcColorHCYFromColor(const QtcColor *color, QtcColorHCY *hcy) 0027 { 0028 double r = qtcColorHCYGamma(color->red); 0029 double g = qtcColorHCYGamma(color->green); 0030 double b = qtcColorHCYGamma(color->blue); 0031 0032 // luma component 0033 hcy->y = qtcColorHCYLumag(r, g, b); 0034 0035 // hue component 0036 double p = qtcMax(qtcMax(r, g), b); 0037 double n = qtcMin(qtcMin(r, g), b); 0038 double d = 6.0 * (p - n); 0039 if (n == p) { 0040 hcy->h = 0.0; 0041 } else if (r == p) { 0042 hcy->h = ((g - b) / d); 0043 } else if (g == p) { 0044 hcy->h = ((b - r) / d) + (1.0 / 3.0); 0045 } else { 0046 hcy->h = ((r - g) / d) + (2.0 / 3.0); 0047 } 0048 0049 // chroma component 0050 if (0.0 == hcy->y || 1.0 == hcy->y) { 0051 hcy->c = 0.0; 0052 } else { 0053 hcy->c = qtcMax((hcy->y - n) / hcy->y, (p - hcy->y) / (1 - hcy->y)); 0054 } 0055 } 0056 0057 static void 0058 qtcColorHCYToColor(const QtcColorHCY *hcy, QtcColor *color) 0059 { 0060 // start with sane component values 0061 double _h = qtcColorWrap(hcy->h, 1); 0062 double _c = qtcBound(0, hcy->c, 1); 0063 double _y = qtcBound(0, hcy->y, 1); 0064 0065 // calculate some needed variables 0066 double _hs = _h * 6.0, th, tm; 0067 if (_hs < 1.0) { 0068 th = _hs; 0069 tm = _qtc_yc[0] + _qtc_yc[1] * th; 0070 } else if (_hs < 2.0) { 0071 th = 2.0 - _hs; 0072 tm = _qtc_yc[1] + _qtc_yc[0] * th; 0073 } else if (_hs < 3.0) { 0074 th = _hs - 2.0; 0075 tm = _qtc_yc[1] + _qtc_yc[2] * th; 0076 } else if (_hs < 4.0) { 0077 th = 4.0 - _hs; 0078 tm = _qtc_yc[2] + _qtc_yc[1] * th; 0079 } else if (_hs < 5.0) { 0080 th = _hs - 4.0; 0081 tm = _qtc_yc[2] + _qtc_yc[0] * th; 0082 } else { 0083 th = 6.0 - _hs; 0084 tm = _qtc_yc[0] + _qtc_yc[2] * th; 0085 } 0086 0087 // calculate RGB channels in sorted order 0088 double tn, to, tp; 0089 if (tm >= _y) { 0090 tp = _y + _y * _c * (1.0 - tm) / tm; 0091 to = _y + _y * _c * (th - tm) / tm; 0092 tn = _y - (_y * _c); 0093 } else { 0094 tp = _y + (1.0 - _y) * _c; 0095 to = _y + (1.0 - _y) * _c * (th - tm) / (1.0 - tm); 0096 tn = _y - (1.0 - _y) * _c * tm / (1.0 - tm); 0097 } 0098 0099 tp = qtcColorHCYIGamma(tp); 0100 to = qtcColorHCYIGamma(to); 0101 tn = qtcColorHCYIGamma(tn); 0102 // return RGB channels in appropriate order 0103 if (_hs < 1.0) { 0104 qtcColorFill(color, tp, to, tn); 0105 } else if (_hs < 2.0) { 0106 qtcColorFill(color, to, tp, tn); 0107 } else if (_hs < 3.0) { 0108 qtcColorFill(color, tn, tp, to); 0109 } else if (_hs < 4.0) { 0110 qtcColorFill(color, tn, to, tp); 0111 } else if (_hs < 5.0) { 0112 qtcColorFill(color, to, tn, tp); 0113 } else { 0114 qtcColorFill(color, tp, tn, to); 0115 } 0116 } 0117 0118 static double 0119 qtcColorContrastRatio(const QtcColor *c1, const QtcColor *c2) 0120 { 0121 double y1 = qtcColorHCYLuma(c1); 0122 double y2 = qtcColorHCYLuma(c2); 0123 if (y1 > y2) { 0124 return (y1 + 0.05) / (y2 + 0.05); 0125 } else { 0126 return (y2 + 0.05) / (y1 + 0.05); 0127 } 0128 } 0129 0130 QTC_EXPORT void 0131 _qtcColorLighten(QtcColor *color, double ky, double kc) 0132 { 0133 QtcColorHCY hcy; 0134 qtcColorHCYFromColor(color, &hcy); 0135 0136 hcy.y = 1.0 - qtcBound(0, (1.0 - hcy.y) * (1.0 - ky), 1); 0137 hcy.c = 1.0 - qtcBound(0, (1.0 - hcy.c) * kc, 1); 0138 qtcColorHCYToColor(&hcy, color); 0139 } 0140 0141 QTC_EXPORT void 0142 _qtcColorDarken(QtcColor *color, double ky, double kc) 0143 { 0144 QtcColorHCY hcy; 0145 qtcColorHCYFromColor(color, &hcy); 0146 0147 hcy.y = qtcBound(0, hcy.y * (1.0 - ky), 1); 0148 hcy.c = qtcBound(0, hcy.c * kc, 1); 0149 qtcColorHCYToColor(&hcy, color); 0150 } 0151 0152 QTC_EXPORT void 0153 _qtcColorShade(QtcColor *color, double ky, double kc) 0154 { 0155 QtcColorHCY hcy; 0156 qtcColorHCYFromColor(color, &hcy); 0157 0158 hcy.y = qtcBound(0, hcy.y + ky, 1); 0159 hcy.c = qtcBound(0, hcy.c + kc, 1); 0160 return qtcColorHCYToColor(&hcy, color); 0161 } 0162 0163 static void 0164 qtcColorTintHelper(const QtcColor *base, const QtcColor *col, 0165 double amount, QtcColor *out) 0166 { 0167 QtcColor mixed; 0168 _qtcColorMix(base, col, pow(amount, 0.3), &mixed); 0169 QtcColorHCY hcy; 0170 qtcColorHCYFromColor(&mixed, &hcy); 0171 hcy.y = qtcColorMixF(qtcColorHCYLuma(base), hcy.y, amount); 0172 0173 qtcColorHCYToColor(&hcy, out); 0174 } 0175 0176 QTC_EXPORT void 0177 _qtcColorTint(const QtcColor *base, const QtcColor *col, 0178 double amount, QtcColor *out) 0179 { 0180 if (qtcUnlikely(amount <= 0.0 || std::isnan(amount))) { 0181 *out = *base; 0182 return; 0183 } 0184 if (qtcUnlikely(amount >= 1.0)) { 0185 *out = *col; 0186 return; 0187 } 0188 0189 double ri = qtcColorContrastRatio(base, col); 0190 double rg = 1.0 + ((ri + 1.0) * amount * amount * amount); 0191 double u = 1.0, l = 0.0; 0192 int i; 0193 for (i = 12;i;i--) { 0194 double a = 0.5 * (l + u); 0195 qtcColorTintHelper(base, col, a, out); 0196 double ra = qtcColorContrastRatio(base, out); 0197 if (ra > rg) { 0198 u = a; 0199 } else { 0200 l = a; 0201 } 0202 } 0203 } 0204 0205 QTC_EXPORT void 0206 _qtcColorMix(const QtcColor *c1, const QtcColor *c2, double bias, QtcColor *out) 0207 { 0208 if (qtcUnlikely(bias <= 0.0 || std::isnan(bias))) { 0209 *out = *c1; 0210 return; 0211 } 0212 if (bias >= 1.0) { 0213 *out = *c2; 0214 return; 0215 } 0216 0217 qtcColorFill(out, qtcColorMixF(c1->red, c2->red, bias), 0218 qtcColorMixF(c1->green, c2->green, bias), 0219 qtcColorMixF(c1->blue, c2->blue, bias)); 0220 } 0221 0222 static inline void 0223 rgbToHsl(double r, double g, double b, double *h, double *s, double *l) 0224 { 0225 double min = qtcMin(qtcMin(r, g), b); 0226 double max = qtcMax(qtcMax(r, g), b); 0227 0228 *l = 0.5 * (max + min); 0229 *s = 0.0; 0230 *h = 0.0; 0231 0232 if (max != min) { 0233 double delta = max - min; 0234 0235 if (*l <= 0.5) { 0236 *s = delta / (max + min); 0237 } else { 0238 *s = delta / (2.0 - max - min); 0239 } 0240 0241 if (r == max) { 0242 *h = (g - b) / delta; 0243 } else if (g == max) { 0244 *h = 2.0 + (b - r) / delta; 0245 } else if (b == max) { 0246 *h = 4.0 + (r - g) / delta; 0247 } 0248 0249 *h /= 6.0; 0250 if (*h < 0.0) { 0251 (*h) += 1.0; 0252 } 0253 } 0254 } 0255 0256 static inline double 0257 h2c(double h, double m1, double m2) 0258 { 0259 h = qtcColorWrap(h, 6.0); 0260 0261 if (h < 1.0) { 0262 return qtcColorMixF(m1, m2, h); 0263 } 0264 if (h < 3.0) { 0265 return m2; 0266 } 0267 if (h < 4.0) { 0268 return qtcColorMixF(m1, m2, 4.0 - h); 0269 } 0270 return m1; 0271 } 0272 0273 static inline void 0274 hslToRgb(double h, double s, double l, double *r, double *g, double *b) 0275 { 0276 double m1, m2; 0277 0278 // TODO h2rgb( h, r, g, b ); 0279 h *= 6.0; 0280 0281 if (l <= 0.5) { 0282 m2 = l * (1.0 + s); 0283 } else { 0284 m2 = l + s * (1.0 - l); 0285 } 0286 m1 = 2.0 * l - m2; 0287 0288 *r = h2c(h + 2.0, m1, m2); 0289 *g = h2c(h, m1, m2); 0290 *b = h2c(h - 2.0, m1, m2); 0291 } 0292 0293 QTC_EXPORT void 0294 _qtcShade(const QtcColor *ca, QtcColor *cb, double k, Shading shading) 0295 { 0296 // Should be handled already by the wrapper inline functions. 0297 if (qtcUnlikely(qtcEqual(k, 1.0))) { 0298 *cb = *ca; 0299 return; 0300 } 0301 switch (shading) { 0302 case Shading::Simple: { 0303 double v = k - 1; 0304 qtcColorFill(cb, qtcLimit(ca->red + v, 1.0), 0305 qtcLimit(ca->green + v, 1.0), 0306 qtcLimit(ca->blue + v, 1.0)); 0307 break; 0308 } 0309 case Shading::HSL: { 0310 double r = ca->red; 0311 double g = ca->green; 0312 double b = ca->blue; 0313 double h, s, l; 0314 0315 rgbToHsl(r, g, b, &h, &s, &l); 0316 l = qtcBound(0, l * k, 1); 0317 s = qtcBound(0, s * k, 1); 0318 hslToRgb(h, s, l, &r, &g, &b); 0319 qtcColorFill(cb, qtcLimit(r, 1.0), qtcLimit(g, 1.0), 0320 qtcLimit(b, 1.0)); 0321 break; 0322 } 0323 case Shading::HSV: { 0324 double r = ca->red; 0325 double g = ca->green; 0326 double b = ca->blue; 0327 double h, s, v; 0328 0329 qtcRgbToHsv(r, g, b, &h, &s, &v); 0330 v *= k; 0331 if (v > 1.0) { 0332 s -= v - 1.0; 0333 if (s < 0) { 0334 s = 0; 0335 } 0336 v = 1.0; 0337 } 0338 qtcHsvToRgb(&r, &g, &b, h, s, v); 0339 qtcColorFill(cb, qtcLimit(r, 1.0), qtcLimit(g, 1.0), 0340 qtcLimit(b, 1.0)); 0341 break; 0342 } 0343 case Shading::HCY: 0344 #define HCY_FACTOR 0.15 0345 *cb = *ca; 0346 if (k > 1) { 0347 _qtcColorLighten(cb, (k * (1 + HCY_FACTOR)) - 1.0, 1.0); 0348 } else { 0349 _qtcColorDarken(cb, 1.0 - (k * (1 - HCY_FACTOR)), 1.0); 0350 } 0351 } 0352 } 0353 0354 QTC_EXPORT double 0355 _qtcShineAlpha(const QtcColor *bgnd) 0356 { 0357 double h = 0, s = 0, v = 0; 0358 qtcRgbToHsv(bgnd->red, bgnd->green, bgnd->blue, &h, &s, &v); 0359 return v * 0.8; 0360 } 0361 0362 QTC_EXPORT double qtc_ring_alpha[3] = {0.125, 0.125, 0.5}; 0363 0364 QTC_EXPORT void 0365 _qtcCalcRingAlphas(const QtcColor *bgnd) 0366 { 0367 double r = bgnd->red; 0368 double g = bgnd->green; 0369 double b = bgnd->blue; 0370 double h = 0, s = 0, v = 0; 0371 qtcRgbToHsv(r, g, b, &h, &s, &v); 0372 qtc_ring_alpha[0] = v * 0.26; 0373 qtc_ring_alpha[1] = v * 0.14; 0374 qtc_ring_alpha[2] = v * 0.55; 0375 } 0376 0377 QTC_EXPORT void 0378 qtcAdjustPix(unsigned char *data, int numChannels, int w, int h, int stride, 0379 int ro, int go, int bo, double shade, 0380 QtcPixelByteOrder byte_order) 0381 { 0382 int width = w * numChannels; 0383 int offset = 0; 0384 int r = (int)(ro * shade + 0.5); 0385 int g = (int)(go * shade + 0.5); 0386 int b = (int)(bo * shade + 0.5); 0387 0388 for (int row = 0;row < h;row++) { 0389 for (int column = 0;column < width;column += numChannels) { 0390 unsigned char source = data[offset + column + 1]; 0391 int new_r = qtcBound(0, r - source, 255); 0392 int new_g = qtcBound(0, g - source, 255); 0393 int new_b = qtcBound(0, b - source, 255); 0394 switch (byte_order) { 0395 case QTC_PIXEL_ARGB: 0396 /* ARGB */ 0397 data[offset + column + 1] = new_r; 0398 data[offset + column + 2] = new_g; 0399 data[offset + column + 3] = new_b; 0400 break; 0401 case QTC_PIXEL_BGRA: 0402 /* BGRA */ 0403 data[offset + column] = new_b; 0404 data[offset + column + 1] = new_g; 0405 data[offset + column + 2] = new_r; 0406 break; 0407 default: 0408 case QTC_PIXEL_RGBA: 0409 /* GdkPixbuf is RGBA */ 0410 data[offset + column] = new_r; 0411 data[offset + column + 1] = new_g; 0412 data[offset + column + 2] = new_b; 0413 break; 0414 } 0415 } 0416 offset += stride; 0417 } 0418 } 0419 0420 QTC_ALWAYS_INLINE static inline int 0421 _c2h(char c) 0422 { 0423 if (c >= '0' && c <= '9') { 0424 return c - '0'; 0425 } else if (c >= 'a' && c <= 'f') { 0426 return c - 'a' + 10; 0427 } else if (c >= 'A' && c <= 'F') { 0428 return c - 'A' + 10; 0429 } else { 0430 return 0; 0431 } 0432 } 0433 0434 QTC_ALWAYS_INLINE static inline int 0435 _2c2h(const char *s) 0436 { 0437 return _c2h(s[0]) * 16 + _c2h(s[1]); 0438 } 0439 0440 QTC_EXPORT void 0441 qtcColorFromStr(QtcColor *color, const char *str) 0442 { 0443 color->red = 0; 0444 color->green = 0; 0445 color->blue = 0; 0446 QTC_RET_IF_FAIL(str); 0447 str += strspn(str, " \t\b\n\f\v"); 0448 if (str[0] == '#') { 0449 str++; 0450 size_t len = strlen(str); 0451 if (len >= 6) { 0452 color->red = _2c2h(str) / 255.0; 0453 color->green = _2c2h(str + 2) / 255.0; 0454 color->blue = _2c2h(str + 4) / 255.0; 0455 return; 0456 } else if (len >= 3) { 0457 color->red = _c2h(str[0]) / 15.0; 0458 color->green = _c2h(str[1]) / 15.0; 0459 color->blue = _c2h(str[2]) / 15.0; 0460 return; 0461 } 0462 } 0463 } 0464 0465 QTC_EXPORT void 0466 qtcColorToStr(const QtcColor *color, char *str) 0467 { 0468 sprintf(str, "#%02X%02X%02X", (unsigned)(qtcBound(0, color->red, 1) * 255), 0469 (unsigned)(qtcBound(0, color->green, 1) * 255), 0470 (unsigned)(qtcBound(0, color->blue, 1) * 255)); 0471 }