2 Copyright (C) 2014 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "canvas/colors.h"
25 #include "canvas/colorspace.h"
28 using namespace ArdourCanvas;
34 ArdourCanvas::color_to_hsv (Color color, double& h, double& s, double& v)
37 color_to_hsva (color, h, s, v, a);
41 ArdourCanvas::color_to_hsva (Color color, double& h, double& s, double& v, double& a)
48 color_to_rgba (color, r, g, b, a);
67 // r = g = b == 0 ... v is undefined, s = 0
75 h = fmod ((g - b)/delta, 6.0);
76 } else if (cmax == g) {
77 h = ((b - r)/delta) + 2;
79 h = ((r - g)/delta) + 4;
85 /* negative values are legal but confusing, because
86 they alias positive values.
92 if (delta == 0 || cmax == 0) {
100 ArdourCanvas::hsva_to_color (double h, double s, double v, double a)
102 s = min (1.0, max (0.0, s));
103 v = min (1.0, max (0.0, v));
106 return rgba_to_color (v, v, v, a);
109 h = fmod (h + 360.0, 360.0);
112 double x = c * (1.0 - fabs(fmod(h / 60.0, 2) - 1.0));
115 if (h >= 0.0 && h < 60.0) {
116 return rgba_to_color (c + m, x + m, m, a);
117 } else if (h >= 60.0 && h < 120.0) {
118 return rgba_to_color (x + m, c + m, m, a);
119 } else if (h >= 120.0 && h < 180.0) {
120 return rgba_to_color (m, c + m, x + m, a);
121 } else if (h >= 180.0 && h < 240.0) {
122 return rgba_to_color (m, x + m, c + m, a);
123 } else if (h >= 240.0 && h < 300.0) {
124 return rgba_to_color (x + m, m, c + m, a);
125 } else if (h >= 300.0 && h < 360.0) {
126 return rgba_to_color (c + m, m, x + m, a);
128 return rgba_to_color (m, m, m, a);
132 ArdourCanvas::color_to_rgba (Color color, double& r, double& g, double& b, double& a)
134 r = ((color >> 24) & 0xff) / 255.0;
135 g = ((color >> 16) & 0xff) / 255.0;
136 b = ((color >> 8) & 0xff) / 255.0;
137 a = ((color >> 0) & 0xff) / 255.0;
141 ArdourCanvas::rgba_to_color (double r, double g, double b, double a)
143 /* clamp to [0 .. 1] range */
145 r = min (1.0, max (0.0, r));
146 g = min (1.0, max (0.0, g));
147 b = min (1.0, max (0.0, b));
148 a = min (1.0, max (0.0, a));
150 /* convert to [0..255] range */
152 unsigned int rc, gc, bc, ac;
153 rc = rint (r * 255.0);
154 gc = rint (g * 255.0);
155 bc = rint (b * 255.0);
156 ac = rint (a * 255.0);
158 /* build-an-integer */
160 return (rc << 24) | (gc << 16) | (bc << 8) | ac;
163 // Inverse of sRGB "gamma" function.
165 inv_gam_sRGB (double c)
170 return pow(((c+0.055)/(1.055)),2.4);
174 // sRGB "gamma" function
178 if (v <= 0.0031308) {
181 v = 1.055 * pow (v, 1.0 / 2.4) - 0.055;
183 return int (v*255+.5);
187 luminance (uint32_t c)
189 // sRGB luminance(Y) values
190 const double rY = 0.212655;
191 const double gY = 0.715158;
192 const double bY = 0.072187;
196 ArdourCanvas::color_to_rgba (c, r, g, b, a);
198 return (gam_sRGB (rY*inv_gam_sRGB(r) + gY*inv_gam_sRGB(g) + bY*inv_gam_sRGB(b))) / 255.0;
202 ArdourCanvas::contrasting_text_color (uint32_t c)
204 static const uint32_t white = ArdourCanvas::rgba_to_color (1.0, 1.0, 1.0, 1.0);
205 static const uint32_t black = ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0);
207 return (luminance (c) < 0.50) ? white : black;
220 HSV::HSV (double hh, double ss, double vv, double aa)
227 /* normalize negative hue values into positive range */
234 color_to_hsva (c, h, s, v, a);
238 HSV::is_gray () const
248 /* normalize negative hue values into positive range */
257 HSV::operator+ (const HSV& operand) const
260 hsv.h = h + operand.h;
261 hsv.s = s + operand.s;
262 hsv.v = v + operand.v;
263 hsv.a = a + operand.a;
269 HSV::operator- (const HSV& operand) const
272 hsv.h = h - operand.h;
273 hsv.s = s - operand.s;
274 hsv.v = s - operand.v;
275 hsv.a = a - operand.a;
281 HSV::operator=(Color c)
283 color_to_hsva (c, h, s, v, a);
289 HSV::operator=(const std::string& str)
292 c = strtol (str.c_str(), 0, 16);
293 color_to_hsva (c, h, s, v, a);
299 HSV::operator== (const HSV& other)
301 return h == other.h &&
308 HSV::shade (double factor) const
312 /* algorithm derived from a google palette website
313 and analysis of their color palettes.
315 basic rule: to make a color darker, increase its saturation
316 until it reaches 88%, but then additionally reduce value/lightness
319 invert rule to make a color lighter.
324 hsv.v += (hsv.v * (factor * 10.0));
329 hsv.v -= (hsv.v * (factor * 10.0));
340 HSV::outline () const
342 if (luminance (color()) < 0.50) {
343 /* light color, darker outline: black with 15% opacity */
344 return HSV (0.0, 0.0, 0.0, 0.15);
346 /* dark color, lighter outline: white with 15% opacity */
347 return HSV (0.0, 0.0, 1.0, 0.15);
352 HSV::mix (const HSV& other, double amount) const
356 hsv.h = h + (amount * (other.h - h));
357 hsv.v = v + (amount * (other.s - s));
358 hsv.s = s + (amount * (other.v - v));
366 HSV::delta (const HSV& other) const
370 if (is_gray() && other.is_gray()) {
379 /* do not clamp - we are returning a delta */
384 HSV::distance (const HSV& other) const
386 if (is_gray() && other.is_gray()) {
387 /* human color perception of achromatics generates about 450
388 distinct colors. By contrast, CIE94 could give a maximal
389 perceptual distance of sqrt ((360^2) + 1 + 1) = 360. The 450
390 are not evenly spread (Webers Law), so lets use 360 as an
391 approximation of the number of distinct achromatics.
393 So, scale up the achromatic difference to give about
394 a maximal distance between v = 1.0 and v = 0.0 of 360.
396 A difference of about 0.0055 will generate a return value of
397 2, which is roughly the limit of human perceptual
398 discrimination for chromatics.
400 return fabs (360.0 * (v - other.v));
403 if (is_gray() != other.is_gray()) {
404 /* no comparison possible */
408 /* Use CIE94 definition for now */
412 double r, g, b, alpha; // Careful, "a" is a field of this
415 c = hsva_to_color (h, s, v, a);
416 color_to_rgba (c, r, g, b, alpha);
417 Rgb2Lab (&sL, &sA, &sB, r, g, b);
419 c = hsva_to_color (other.h, other.s, other.v, other.a);
420 color_to_rgba (c, r, g, b, alpha);
421 Rgb2Lab (&oL, &oA, &oB, r, g, b);
423 // Weighting factors depending on the application (1 = default)
425 const double whtL = 1.0;
426 const double whtC = 1.0;
427 const double whtH = 1.0;
429 const double xC1 = sqrt ((sA * sA) + (sB * oB));
430 const double xC2 = sqrt ((oA * oA) + (oB * oB));
431 double xDL = oL - sL;
432 double xDC = xC2 - xC1;
433 const double xDE = sqrt (((sL - oL) * (sL - oL))
434 + ((sA - oA) * (sA - oA))
435 + ((sB - oB) * (sB - oB)));
439 if (sqrt (xDE) > (sqrt (abs (xDL)) + sqrt (abs (xDC)))) {
440 xDH = sqrt ((xDE * xDE) - (xDL * xDL) - (xDC * xDC));
445 const double xSC = 1 + (0.045 * xC1);
446 const double xSH = 1 + (0.015 * xC1);
452 return sqrt ((xDL * xDL) + (xDC * xDC) + (xDH * xDH));
456 HSV::opposite () const
459 hsv.h = fmod (h + 180.0, 360.0);
464 HSV::bw_text () const
466 return HSV (contrasting_text_color (color()));
476 HSV::selected () const
479 return HSV (Color (0xff0000));
484 HSV::print (std::ostream& o) const
487 o << '(' << h << ',' << s << ',' << v << ',' << a << ')';
489 o << "gray(" << v << ')';
494 std::ostream& operator<<(std::ostream& o, const ArdourCanvas::HSV& hsv) { hsv.print (o); return o; }