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