File indexing completed on 2024-04-28 05:46:49

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 }