1 /*****************************************************************************
2  *   Copyright 2009 - 2010 Craig Drummond <craig.p.drummond@gmail.com>       *
3  *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
4  *                                                                           *
5  *   This program is free software; you can redistribute it and/or modify    *
6  *   it under the terms of the GNU Lesser General Public License as          *
7  *   published by the Free Software Foundation; either version 2.1 of the    *
8  *   License, or (at your option) version 3, or any later version accepted   *
9  *   by the membership of KDE e.V. (or its successor approved by the         *
10  *   membership of KDE e.V.), which shall act as a proxy defined in          *
11  *   Section 6 of version 3 of the license.                                  *
12  *                                                                           *
13  *   This program is distributed in the hope that it will be useful,         *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
16  *   Lesser General Public License for more details.                         *
17  *                                                                           *
18  *   You should have received a copy of the GNU Lesser General Public        *
19  *   License along with this library. If not,                                *
20  *   see <http://www.gnu.org/licenses/>.                                     *
21  *****************************************************************************/
22 
23 #ifndef QTC_UTILS_COLOR_H
24 #define QTC_UTILS_COLOR_H
25 
26 #include "utils.h"
27 #include "options.h"
28 
29 // Using c99 function in c++ mode seems to cause trouble on some OSX versions.
30 #include <cmath>
31 
32 typedef struct {
33     double red;
34     double green;
35     double blue;
36 } QtcColor;
37 
38 typedef struct {
39     double h;
40     double c;
41     double y;
42 } QtcColorHCY;
43 
44 extern double qtc_ring_alpha[3];
45 
46 // use 709 for now
47 #define HCY_REC 709
48 #if HCY_REC == 601
49 static const double _qtc_yc[3] = {0.299, 0.587, 0.114};
50 #elif HCY_REC == 709
51 static const double _qtc_yc[3] = {0.2126, 0.7152, 0.0722};
52 #else // use Qt values
53 static const double _qtc_yc[3] = {0.34375, 0.5, 0.15625};
54 #endif
55 #undef HCY_REC
56 
57 QTC_ALWAYS_INLINE static inline double
qtcColorWrap(double a,double d)58 qtcColorWrap(double a, double d)
59 {
60     double r = fmod(a, d);
61     return (r < 0.0 ? d + r : (r > 0.0 ? r : 0.0));
62 }
63 
64 QTC_ALWAYS_INLINE static inline double
qtcColorMixF(double a,double b,double bias)65 qtcColorMixF(double a, double b, double bias)
66 {
67     return a + (b - a) * bias;
68 }
69 
70 QTC_ALWAYS_INLINE static inline double
qtcColorHCYGamma(double n)71 qtcColorHCYGamma(double n)
72 {
73     return pow(qtcBound(0, n, 1), 2.2);
74 }
75 
76 QTC_ALWAYS_INLINE static inline double
qtcColorHCYIGamma(double n)77 qtcColorHCYIGamma(double n)
78 {
79     return pow(qtcBound(0, n, 1), 1.0 / 2.2);
80 }
81 
82 QTC_ALWAYS_INLINE static inline double
qtcColorHCYLumag(double r,double g,double b)83 qtcColorHCYLumag(double r, double g, double b)
84 {
85     return r * _qtc_yc[0] + g * _qtc_yc[1] + b * _qtc_yc[2];
86 }
87 
88 QTC_ALWAYS_INLINE static inline void
qtcColorFill(QtcColor * color,double r,double g,double b)89 qtcColorFill(QtcColor *color, double r, double g, double b)
90 {
91     color->red = r;
92     color->green = g;
93     color->blue = b;
94 }
95 
96 QTC_ALWAYS_INLINE static inline double
qtcColorHCYLuma(const QtcColor * color)97 qtcColorHCYLuma(const QtcColor *color)
98 {
99     return qtcColorHCYLumag(qtcColorHCYGamma(color->red),
100                             qtcColorHCYGamma(color->green),
101                             qtcColorHCYGamma(color->blue));
102 }
103 
104 static inline void
qtcHsvToRgb(double * r,double * g,double * b,double h,double s,double v)105 qtcHsvToRgb(double *r, double *g, double *b, double h, double s, double v)
106 {
107     if (0 == s) {
108         *r = *g = *b = v;
109     } else {
110         int i;
111         double f;
112         double p;
113 
114         h /= 60; /* sector 0 to 5 */
115         i = (int)floor(h);
116         f = h - i; /* factorial part of h */
117         p = v * (1 - s);
118         switch (i) {
119         case 0:
120             *r = v;
121             *g = v * (1 - s * (1 - f));
122             *b = p;
123             break;
124         case 1:
125             *r = v * (1 - s * f);
126             *g = v;
127             *b = p;
128             break;
129         case 2:
130             *r = p;
131             *g = v;
132             *b = v * (1 - s * (1 - f));
133             break;
134         case 3:
135             *r = p;
136             *g = v * (1 - s * f);
137             *b = v;
138             break;
139         case 4:
140             *r = v * (1 - s * (1 - f));
141             *g = p;
142             *b = v;
143             break;
144         case 5:
145         default:
146             *r = v;
147             *g = p;
148             *b = v * (1 - s * f);
149             break;
150         }
151     }
152 }
153 
154 static inline void
qtcRgbToHsv(double r,double g,double b,double * h,double * s,double * v)155 qtcRgbToHsv(double r, double g, double b, double *h, double *s, double *v)
156 {
157     double min = qtcMin(qtcMin(r, g), b);
158     double max = qtcMax(qtcMax(r, g), b);
159     double delta = max - min;
160 
161     *v = max;
162     if (max != 0) {
163         *s = delta / max;
164     } else {
165         *s = 0;
166     }
167 
168     if (*s == 0.0) {
169         *h = 0.0;
170     } else {
171         if (r == max) {
172             *h = (g - b) / delta; /* between yellow & magenta */
173         } else if (g == max) {
174             *h = 2 + (b - r) / delta; /* between cyan & yellow */
175         } else {
176             *h = 4 + (r - g) / delta; /* between magenta & cyan */
177         }
178         *h *= 60; /* degrees */
179         if (*h < 0) {
180             *h += 360;
181         }
182     }
183 }
184 
185 void _qtcColorLighten(QtcColor *color, double ky, double kc);
186 void _qtcColorDarken(QtcColor *color, double ky, double kc);
187 void _qtcColorShade(QtcColor *color, double ky, double kc);
188 void _qtcColorTint(const QtcColor *base, const QtcColor *col,
189                    double amount, QtcColor *out);
190 void _qtcColorMix(const QtcColor *c1, const QtcColor *c2,
191                   double bias, QtcColor *out);
192 void _qtcShade(const QtcColor *ca, QtcColor *cb, double k, Shading shading);
193 double _qtcShineAlpha(const QtcColor *bgnd);
194 void _qtcCalcRingAlphas(const QtcColor *bgnd);
195 void qtcColorFromStr(QtcColor *color, const char *str);
196 void qtcColorToStr(const QtcColor *color, char *str);
197 
198 typedef enum {
199     QTC_PIXEL_ARGB,
200     QTC_PIXEL_BGRA,
201     QTC_PIXEL_RGBA,
202     QTC_PIXEL_GDK = QTC_PIXEL_RGBA,
203 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
204     QTC_PIXEL_XCB = QTC_PIXEL_ARGB,
205     QTC_PIXEL_QT = QTC_PIXEL_ARGB,
206 #else
207     QTC_PIXEL_XCB = QTC_PIXEL_BGRA,
208     QTC_PIXEL_QT = QTC_PIXEL_BGRA,
209 #endif
210 } QtcPixelByteOrder;
211 void qtcAdjustPix(unsigned char *data, int numChannels, int w, int h,
212                   int stride, int ro, int go, int bo, double shade,
213                   QtcPixelByteOrder byte_order);
214 
215 #ifndef QTC_UTILS_INTERNAL
216 
217 #ifdef QTC_UTILS_QT
218 #include <QColor>
219 
220 QTC_ALWAYS_INLINE static inline QColor
qtcColorLighten(const QColor * color,double ky,double kc)221 qtcColorLighten(const QColor *color, double ky, double kc)
222 {
223     QtcColor qtc_color = {color->redF(), color->greenF(), color->blueF()};
224     _qtcColorLighten(&qtc_color, ky, kc);
225     return QColor::fromRgbF(qtc_color.red, qtc_color.green, qtc_color.blue);
226 }
227 
228 QTC_ALWAYS_INLINE static inline QColor
qtcColorDarken(const QColor * color,double ky,double kc)229 qtcColorDarken(const QColor *color, double ky, double kc)
230 {
231     QtcColor qtc_color = {color->redF(), color->greenF(), color->blueF()};
232     _qtcColorDarken(&qtc_color, ky, kc);
233     return QColor::fromRgbF(qtc_color.red, qtc_color.green, qtc_color.blue);
234 }
235 
236 QTC_ALWAYS_INLINE static inline QColor
qtcColorShade(const QColor * color,double ky,double kc)237 qtcColorShade(const QColor *color, double ky, double kc)
238 {
239     QtcColor qtc_color = {color->redF(), color->greenF(), color->blueF()};
240     _qtcColorShade(&qtc_color, ky, kc);
241     return QColor::fromRgbF(qtc_color.red, qtc_color.green, qtc_color.blue);
242 }
243 
244 QTC_ALWAYS_INLINE static inline QColor
qtcColorTint(const QColor * base,const QColor * col,double amount)245 qtcColorTint(const QColor *base, const QColor *col, double amount)
246 {
247     if (amount <= 0.0) {
248         return *base;
249     } else if (amount >= 1.0) {
250         return *col;
251     } else if (std::isnan(amount)) {
252         return *base;
253     }
254     const QtcColor qtc_base = {base->redF(), base->greenF(), base->blueF()};
255     const QtcColor qtc_col = {col->redF(), col->greenF(), col->blueF()};
256     QtcColor out;
257     _qtcColorTint(&qtc_base, &qtc_col, amount, &out);
258     return QColor::fromRgbF(out.red, out.green, out.blue);
259 }
260 
261 QTC_ALWAYS_INLINE static inline QColor
qtcColorMix(const QColor * c1,const QColor * c2,double bias)262 qtcColorMix(const QColor *c1, const QColor *c2, double bias)
263 {
264     if (bias <= 0.0) {
265         return *c1;
266     } else if (bias >= 1.0) {
267         return *c2;
268     } else if (std::isnan(bias)) {
269         return *c1;
270     }
271     const QtcColor qtc_c1 = {c1->redF(), c1->greenF(), c1->blueF()};
272     const QtcColor qtc_c2 = {c2->redF(), c2->greenF(), c2->blueF()};
273     QtcColor out;
274     _qtcColorMix(&qtc_c1, &qtc_c2, bias, &out);
275     return QColor::fromRgbF(out.red, out.green, out.blue);
276 }
277 
278 QTC_ALWAYS_INLINE static inline double
qtcColorLuma(const QColor * color)279 qtcColorLuma(const QColor *color)
280 {
281     QtcColor qtc_color = {color->redF(), color->greenF(), color->blueF()};
282     return qtcColorHCYLuma(&qtc_color);
283 }
284 
285 QTC_ALWAYS_INLINE static inline void
qtcShade(const QColor * ca,QColor * cb,double k,Shading shading)286 qtcShade(const QColor *ca, QColor *cb, double k, Shading shading)
287 {
288     if (qtcEqual(k, 1.0)) {
289         *cb = *ca;
290         return;
291     }
292     const QtcColor qtc_ca = {ca->redF(), ca->greenF(), ca->blueF()};
293     QtcColor qtc_cb;
294     _qtcShade(&qtc_ca, &qtc_cb, k, shading);
295     cb->setRgbF(qtc_cb.red, qtc_cb.green, qtc_cb.blue, ca->alphaF());
296 }
297 
298 QTC_ALWAYS_INLINE static inline double
qtcShineAlpha(const QColor * bgnd)299 qtcShineAlpha(const QColor *bgnd)
300 {
301     const QtcColor qtc_bgnd = {bgnd->redF(), bgnd->greenF(), bgnd->blueF()};
302     return _qtcShineAlpha(&qtc_bgnd);
303 }
304 
305 QTC_ALWAYS_INLINE static inline void
qtcCalcRingAlphas(const QColor * bgnd)306 qtcCalcRingAlphas(const QColor *bgnd)
307 {
308     const QtcColor qtc_bgnd = {bgnd->redF(), bgnd->greenF(), bgnd->blueF()};
309     _qtcCalcRingAlphas(&qtc_bgnd);
310 }
311 #endif
312 
313 #ifdef QTC_UTILS_GTK
314 #include <gdk/gdk.h>
315 
316 QTC_ALWAYS_INLINE static inline GdkColor
_qtcColorToGdk(const QtcColor * qtc_color)317 _qtcColorToGdk(const QtcColor *qtc_color)
318 {
319     GdkColor color;
320     color.red = qtc_color->red * 65535;
321     color.green = qtc_color->green * 65535;
322     color.blue = qtc_color->blue * 65535;
323     return color;
324 }
325 
326 QTC_ALWAYS_INLINE static inline QtcColor
_qtc_color_from_gdk(const GdkColor * color)327 _qtc_color_from_gdk(const GdkColor *color)
328 {
329     QtcColor qtc_color;
330     qtc_color.red = color->red / 65535.0;
331     qtc_color.green = color->green / 65535.0;
332     qtc_color.blue = color->blue / 65535.0;
333     return qtc_color;
334 }
335 
336 QTC_ALWAYS_INLINE static inline GdkColor
qtcColorLighten(const GdkColor * color,double ky,double kc)337 qtcColorLighten(const GdkColor *color, double ky, double kc)
338 {
339     QtcColor qtc_color = _qtc_color_from_gdk(color);
340     _qtcColorLighten(&qtc_color, ky, kc);
341     return _qtcColorToGdk(&qtc_color);
342 }
343 
344 QTC_ALWAYS_INLINE static inline GdkColor
qtcColorDarken(const GdkColor * color,double ky,double kc)345 qtcColorDarken(const GdkColor *color, double ky, double kc)
346 {
347     QtcColor qtc_color = _qtc_color_from_gdk(color);
348     _qtcColorDarken(&qtc_color, ky, kc);
349     return _qtcColorToGdk(&qtc_color);
350 }
351 
352 QTC_ALWAYS_INLINE static inline GdkColor
qtcColorShade(const GdkColor * color,double ky,double kc)353 qtcColorShade(const GdkColor *color, double ky, double kc)
354 {
355     QtcColor qtc_color = _qtc_color_from_gdk(color);
356     _qtcColorShade(&qtc_color, ky, kc);
357     return _qtcColorToGdk(&qtc_color);
358 }
359 
360 QTC_ALWAYS_INLINE static inline GdkColor
qtcColorTint(const GdkColor * base,const GdkColor * col,double amount)361 qtcColorTint(const GdkColor *base, const GdkColor *col, double amount)
362 {
363     if (amount <= 0.0) {
364         return *base;
365     } else if (amount >= 1.0) {
366         return *col;
367     } else if (std::isnan(amount)) {
368         return *base;
369     }
370     QtcColor qtc_base = _qtc_color_from_gdk(base);
371     QtcColor qtc_col = _qtc_color_from_gdk(col);
372     QtcColor out;
373     _qtcColorTint(&qtc_base, &qtc_col, amount, &out);
374     return _qtcColorToGdk(&out);
375 }
376 
377 QTC_ALWAYS_INLINE static inline GdkColor
qtcColorMix(const GdkColor * c1,const GdkColor * c2,double bias)378 qtcColorMix(const GdkColor *c1, const GdkColor *c2, double bias)
379 {
380     if (bias <= 0.0) {
381         return *c1;
382     } else if (bias >= 1.0) {
383         return *c2;
384     } else if (std::isnan(bias)) {
385         return *c1;
386     }
387     QtcColor qtc_c1 = _qtc_color_from_gdk(c1);
388     QtcColor qtc_c2 = _qtc_color_from_gdk(c2);
389     QtcColor out;
390     _qtcColorMix(&qtc_c1, &qtc_c2, bias, &out);
391     return _qtcColorToGdk(&out);
392 }
393 
394 QTC_ALWAYS_INLINE static inline double
qtcColorLuma(const GdkColor * color)395 qtcColorLuma(const GdkColor *color)
396 {
397     QtcColor qtc_color = _qtc_color_from_gdk(color);
398     return qtcColorHCYLuma(&qtc_color);
399 }
400 
401 QTC_ALWAYS_INLINE static inline void
qtcShade(const GdkColor * ca,GdkColor * cb,double k,Shading shading)402 qtcShade(const GdkColor *ca, GdkColor *cb, double k, Shading shading)
403 {
404     if (qtcEqual(k, 1.0)) {
405         *cb = *ca;
406         return;
407     }
408     QtcColor qtc_ca = _qtc_color_from_gdk(ca);
409     QtcColor qtc_cb;
410     _qtcShade(&qtc_ca, &qtc_cb, k, shading);
411     *cb = _qtcColorToGdk(&qtc_cb);
412 }
413 
414 QTC_ALWAYS_INLINE static inline double
qtcShineAlpha(const GdkColor * bgnd)415 qtcShineAlpha(const GdkColor *bgnd)
416 {
417     QtcColor qtc_bgnd = _qtc_color_from_gdk(bgnd);
418     return _qtcShineAlpha(&qtc_bgnd);
419 }
420 
421 QTC_ALWAYS_INLINE static inline void
qtcCalcRingAlphas(const GdkColor * bgnd)422 qtcCalcRingAlphas(const GdkColor *bgnd)
423 {
424     QtcColor qtc_bgnd = _qtc_color_from_gdk(bgnd);
425     _qtcCalcRingAlphas(&qtc_bgnd);
426 }
427 
428 #endif
429 
430 namespace QtCurve {
431 
432 #ifdef QTC_UTILS_QT
433 
434 static inline bool
isBlack(const QColor & color)435 isBlack(const QColor &color)
436 {
437     return color.red() == 0 && color.green() == 0 && color.blue() == 0;
438 }
439 
440 #endif
441 
442 #ifdef QTC_UTILS_GTK
443 
444 static inline bool
isBlack(const GdkColor & color)445 isBlack(const GdkColor &color)
446 {
447     return color.red == 0 && color.green == 0 && color.blue == 0;
448 }
449 
450 #endif
451 
452 }
453 
454 #endif
455 
456 #endif
457