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.
25 #include "pbd/convert.h"
26 #include "pbd/failed_constructor.h"
27 #include "pbd/locale_guard.h"
29 #include "canvas/colors.h"
30 #include "canvas/colorspace.h"
33 using namespace ArdourCanvas;
39 ArdourCanvas::color_to_hsv (Color color, double& h, double& s, double& v)
42 color_to_hsva (color, h, s, v, a);
46 ArdourCanvas::color_to_hsva (Color color, double& h, double& s, double& v, double& a)
53 color_to_rgba (color, r, g, b, a);
72 // r = g = b == 0 ... v is undefined, s = 0
80 h = fmod ((g - b)/delta, 6.0);
81 } else if (cmax == g) {
82 h = ((b - r)/delta) + 2;
84 h = ((r - g)/delta) + 4;
90 /* negative values are legal but confusing, because
91 they alias positive values.
97 if (delta == 0 || cmax == 0) {
105 ArdourCanvas::hsva_to_color (double h, double s, double v, double a)
107 s = min (1.0, max (0.0, s));
108 v = min (1.0, max (0.0, v));
111 return rgba_to_color (v, v, v, a);
114 h = fmod (h + 360.0, 360.0);
117 double x = c * (1.0 - fabs(fmod(h / 60.0, 2) - 1.0));
120 if (h >= 0.0 && h < 60.0) {
121 return rgba_to_color (c + m, x + m, m, a);
122 } else if (h >= 60.0 && h < 120.0) {
123 return rgba_to_color (x + m, c + m, m, a);
124 } else if (h >= 120.0 && h < 180.0) {
125 return rgba_to_color (m, c + m, x + m, a);
126 } else if (h >= 180.0 && h < 240.0) {
127 return rgba_to_color (m, x + m, c + m, a);
128 } else if (h >= 240.0 && h < 300.0) {
129 return rgba_to_color (x + m, m, c + m, a);
130 } else if (h >= 300.0 && h < 360.0) {
131 return rgba_to_color (c + m, m, x + m, a);
133 return rgba_to_color (m, m, m, a);
137 ArdourCanvas::color_to_rgba (Color color, double& r, double& g, double& b, double& a)
139 r = ((color >> 24) & 0xff) / 255.0;
140 g = ((color >> 16) & 0xff) / 255.0;
141 b = ((color >> 8) & 0xff) / 255.0;
142 a = ((color >> 0) & 0xff) / 255.0;
146 ArdourCanvas::rgba_to_color (double r, double g, double b, double a)
148 /* clamp to [0 .. 1] range */
150 r = min (1.0, max (0.0, r));
151 g = min (1.0, max (0.0, g));
152 b = min (1.0, max (0.0, b));
153 a = min (1.0, max (0.0, a));
155 /* convert to [0..255] range */
157 unsigned int rc, gc, bc, ac;
158 rc = rint (r * 255.0);
159 gc = rint (g * 255.0);
160 bc = rint (b * 255.0);
161 ac = rint (a * 255.0);
163 /* build-an-integer */
165 return (rc << 24) | (gc << 16) | (bc << 8) | ac;
168 // Inverse of sRGB "gamma" function.
170 inv_gam_sRGB (double c)
175 return pow(((c+0.055)/(1.055)),2.4);
179 // sRGB "gamma" function
183 if (v <= 0.0031308) {
186 v = 1.055 * pow (v, 1.0 / 2.4) - 0.055;
188 return int (v*255+.5);
192 luminance (uint32_t c)
194 // sRGB luminance(Y) values
195 const double rY = 0.212655;
196 const double gY = 0.715158;
197 const double bY = 0.072187;
201 ArdourCanvas::color_to_rgba (c, r, g, b, a);
203 return (gam_sRGB (rY*inv_gam_sRGB(r) + gY*inv_gam_sRGB(g) + bY*inv_gam_sRGB(b))) / 255.0;
207 ArdourCanvas::contrasting_text_color (uint32_t c)
209 /* use a slightly off-white... XXX should really look this up */
211 static const uint32_t white = ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1.0);
212 static const uint32_t black = ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0);
214 return (luminance (c) < 0.50) ? white : black;
227 HSV::HSV (double hh, double ss, double vv, double aa)
234 /* normalize negative hue values into positive range */
241 color_to_hsva (c, h, s, v, a);
244 HSV::HSV (const std::string& str)
246 stringstream ss (str);
254 HSV::to_string () const
265 HSV::is_gray () const
275 /* normalize negative hue values into positive range */
284 HSV::operator+ (const HSV& operand) const
287 hsv.h = h + operand.h;
288 hsv.s = s + operand.s;
289 hsv.v = v + operand.v;
290 hsv.a = a + operand.a;
296 HSV::operator- (const HSV& operand) const
299 hsv.h = h - operand.h;
300 hsv.s = s - operand.s;
301 hsv.v = s - operand.v;
302 hsv.a = a - operand.a;
308 HSV::operator=(Color c)
310 color_to_hsva (c, h, s, v, a);
316 HSV::operator=(const std::string& str)
319 c = strtol (str.c_str(), 0, 16);
320 color_to_hsva (c, h, s, v, a);
326 HSV::operator== (const HSV& other)
328 return h == other.h &&
335 HSV::shade (double factor) const
339 /* algorithm derived from a google palette website
340 and analysis of their color palettes.
342 basic rule: to make a color darker, increase its saturation
343 until it reaches 88%, but then additionally reduce value/lightness
346 invert rule to make a color lighter.
351 hsv.v += (hsv.v * (factor * 10.0));
356 hsv.v -= (hsv.v * (factor * 10.0));
367 HSV::outline () const
369 if (luminance (color()) < 0.50) {
370 /* light color, darker outline: black with 15% opacity */
371 return HSV (0.0, 0.0, 0.0, 0.15);
373 /* dark color, lighter outline: white with 15% opacity */
374 return HSV (0.0, 0.0, 1.0, 0.15);
379 HSV::mix (const HSV& other, double amount) const
383 hsv.h = h + (amount * (other.h - h));
384 hsv.v = v + (amount * (other.s - s));
385 hsv.s = s + (amount * (other.v - v));
393 HSV::delta (const HSV& other) const
397 if (is_gray() && other.is_gray()) {
407 /* do not clamp - we are returning a delta */
412 HSV::distance (const HSV& other) const
414 if (is_gray() && other.is_gray()) {
415 /* human color perception of achromatics generates about 450
416 distinct colors. By contrast, CIE94 could give a maximal
417 perceptual distance of sqrt ((360^2) + 1 + 1) = 360. The 450
418 are not evenly spread (Webers Law), so lets use 360 as an
419 approximation of the number of distinct achromatics.
421 So, scale up the achromatic difference to give about
422 a maximal distance between v = 1.0 and v = 0.0 of 360.
424 A difference of about 0.0055 will generate a return value of
425 2, which is roughly the limit of human perceptual
426 discrimination for chromatics.
428 return fabs (360.0 * (v - other.v));
431 if (is_gray() != other.is_gray()) {
432 /* no comparison possible */
436 /* Use CIE94 definition for now */
440 double r, g, b, alpha; // Careful, "a" is a field of this
443 c = hsva_to_color (h, s, v, a);
444 color_to_rgba (c, r, g, b, alpha);
445 Rgb2Lab (&sL, &sA, &sB, r, g, b);
447 c = hsva_to_color (other.h, other.s, other.v, other.a);
448 color_to_rgba (c, r, g, b, alpha);
449 Rgb2Lab (&oL, &oA, &oB, r, g, b);
451 // Weighting factors depending on the application (1 = default)
453 const double whtL = 1.0;
454 const double whtC = 1.0;
455 const double whtH = 1.0;
457 const double xC1 = sqrt ((sA * sA) + (sB * oB));
458 const double xC2 = sqrt ((oA * oA) + (oB * oB));
459 double xDL = oL - sL;
460 double xDC = xC2 - xC1;
461 const double xDE = sqrt (((sL - oL) * (sL - oL))
462 + ((sA - oA) * (sA - oA))
463 + ((sB - oB) * (sB - oB)));
467 if (sqrt (xDE) > (sqrt (abs (xDL)) + sqrt (abs (xDC)))) {
468 xDH = sqrt ((xDE * xDE) - (xDL * xDL) - (xDC * xDC));
473 const double xSC = 1 + (0.045 * xC1);
474 const double xSH = 1 + (0.015 * xC1);
480 return sqrt ((xDL * xDL) + (xDC * xDC) + (xDH * xDH));
484 HSV::opposite () const
487 hsv.h = fmod (h + 180.0, 360.0);
492 HSV::bw_text () const
494 return HSV (contrasting_text_color (color()));
504 HSV::selected () const
507 return HSV (Color (0xff0000));
512 HSV::print (std::ostream& o) const
515 o << '(' << h << ',' << s << ',' << v << ',' << a << ')';
517 o << "gray(" << v << ')';
522 std::ostream& operator<<(std::ostream& o, const ArdourCanvas::HSV& hsv) { hsv.print (o); return o; }
525 HSV::mod (SVAModifier const & svam)
530 SVAModifier::SVAModifier (string const &str)
540 SVAModifier::from_string (string const & str)
543 stringstream ss (str);
559 throw failed_constructor ();
562 string::size_type pos;
566 if ((pos = mod.find ("alpha:")) != string::npos) {
567 a = PBD::atoi (mod.substr (pos+6));
568 } else if ((pos = mod.find ("saturate:")) != string::npos) {
569 s = PBD::atoi (mod.substr (pos+9));
570 } else if ((pos = mod.find ("darkness:")) != string::npos) {
571 v = PBD::atoi (mod.substr (pos+9));
573 throw failed_constructor ();
579 SVAModifier::to_string () const
581 PBD::LocaleGuard lg ("POSIX");
597 ss << " saturate:" << s;
601 ss << " darker:" << v;
605 ss << " alpha:" << a;
612 SVAModifier::operator () (HSV& hsv) const