2 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
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 along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "pbd/failed_constructor.h"
26 #include "pbd/string_convert.h"
28 #include "gtkmm2ext/colors.h"
29 #include "gtkmm2ext/colorspace.h"
32 using namespace Gtkmm2ext;
38 Gtkmm2ext::change_alpha (Color c, double a)
40 return ((c & ~0xff) | (lrintf (a*255.0) & 0xff));
44 Gtkmm2ext::color_to_hsv (Color color, double& h, double& s, double& v)
47 color_to_hsva (color, h, s, v, a);
51 Gtkmm2ext::color_to_hsva (Color color, double& h, double& s, double& v, double& a)
58 color_to_rgba (color, r, g, b, a);
77 // r = g = b == 0 ... v is undefined, s = 0
85 h = fmod ((g - b)/delta, 6.0);
86 } else if (cmax == g) {
87 h = ((b - r)/delta) + 2;
89 h = ((r - g)/delta) + 4;
95 /* negative values are legal but confusing, because
96 they alias positive values.
102 if (delta == 0 || cmax == 0) {
110 Gtkmm2ext::hsva_to_color (double h, double s, double v, double a)
112 s = min (1.0, max (0.0, s));
113 v = min (1.0, max (0.0, v));
116 return rgba_to_color (v, v, v, a);
119 h = fmod (h + 360.0, 360.0);
122 double x = c * (1.0 - fabs(fmod(h / 60.0, 2) - 1.0));
125 if (h >= 0.0 && h < 60.0) {
126 return rgba_to_color (c + m, x + m, m, a);
127 } else if (h >= 60.0 && h < 120.0) {
128 return rgba_to_color (x + m, c + m, m, a);
129 } else if (h >= 120.0 && h < 180.0) {
130 return rgba_to_color (m, c + m, x + m, a);
131 } else if (h >= 180.0 && h < 240.0) {
132 return rgba_to_color (m, x + m, c + m, a);
133 } else if (h >= 240.0 && h < 300.0) {
134 return rgba_to_color (x + m, m, c + m, a);
135 } else if (h >= 300.0 && h < 360.0) {
136 return rgba_to_color (c + m, m, x + m, a);
138 return rgba_to_color (m, m, m, a);
142 Gtkmm2ext::color_to_rgba (Color color, double& r, double& g, double& b, double& a)
144 r = ((color >> 24) & 0xff) / 255.0;
145 g = ((color >> 16) & 0xff) / 255.0;
146 b = ((color >> 8) & 0xff) / 255.0;
147 a = ((color >> 0) & 0xff) / 255.0;
151 Gtkmm2ext::rgba_to_color (double r, double g, double b, double a)
153 /* clamp to [0 .. 1] range */
155 r = min (1.0, max (0.0, r));
156 g = min (1.0, max (0.0, g));
157 b = min (1.0, max (0.0, b));
158 a = min (1.0, max (0.0, a));
160 /* convert to [0..255] range */
162 unsigned int rc, gc, bc, ac;
163 rc = rint (r * 255.0);
164 gc = rint (g * 255.0);
165 bc = rint (b * 255.0);
166 ac = rint (a * 255.0);
168 /* build-an-integer */
170 return (rc << 24) | (gc << 16) | (bc << 8) | ac;
173 // Inverse of sRGB "gamma" function.
175 inv_gam_sRGB (double c)
180 return pow(((c+0.055)/(1.055)),2.4);
184 // sRGB "gamma" function
188 if (v <= 0.0031308) {
191 v = 1.055 * pow (v, 1.0 / 2.4) - 0.055;
193 return int (v*255+.5);
197 luminance (uint32_t c)
199 // sRGB luminance(Y) values
200 const double rY = 0.212655;
201 const double gY = 0.715158;
202 const double bY = 0.072187;
206 Gtkmm2ext::color_to_rgba (c, r, g, b, a);
208 return (gam_sRGB (rY*inv_gam_sRGB(r) + gY*inv_gam_sRGB(g) + bY*inv_gam_sRGB(b))) / 255.0;
212 Gtkmm2ext::contrasting_text_color (uint32_t c)
214 /* use a slightly off-white... XXX should really look this up */
216 static const uint32_t white = Gtkmm2ext::rgba_to_color (0.98, 0.98, 0.98, 1.0);
217 static const uint32_t black = Gtkmm2ext::rgba_to_color (0.0, 0.0, 0.0, 1.0);
219 return (luminance (c) < 0.50) ? white : black;
232 HSV::HSV (double hh, double ss, double vv, double aa)
239 /* normalize negative hue values into positive range */
246 color_to_hsva (c, h, s, v, a);
250 HSV::to_string () const
253 ss << PBD::to_string(h) << ' ';
254 ss << PBD::to_string(s) << ' ';
255 ss << PBD::to_string(v) << ' ';
256 ss << PBD::to_string(a);
261 HSV::is_gray () const
271 /* normalize negative hue values into positive range */
280 HSV::operator+ (const HSV& operand) const
283 hsv.h = h + operand.h;
284 hsv.s = s + operand.s;
285 hsv.v = v + operand.v;
286 hsv.a = a + operand.a;
292 HSV::operator- (const HSV& operand) const
295 hsv.h = h - operand.h;
296 hsv.s = s - operand.s;
297 hsv.v = s - operand.v;
298 hsv.a = a - operand.a;
304 HSV::operator=(Color c)
306 color_to_hsva (c, h, s, v, a);
312 HSV::operator=(const std::string& str)
315 c = strtol (str.c_str(), 0, 16);
316 color_to_hsva (c, h, s, v, a);
322 HSV::operator== (const HSV& other)
324 return h == other.h &&
331 HSV::shade (double factor) const
335 /* algorithm derived from a google palette website
336 and analysis of their color palettes.
338 basic rule: to make a color darker, increase its saturation
339 until it reaches 88%, but then additionally reduce value/lightness
342 invert rule to make a color lighter.
347 hsv.v += (hsv.v * (factor * 10.0));
352 hsv.v -= (hsv.v * (factor * 10.0));
363 HSV::outline () const
365 if (luminance (color()) < 0.50) {
366 /* light color, darker outline: black with 15% opacity */
367 return HSV (0.0, 0.0, 0.0, 0.15);
369 /* dark color, lighter outline: white with 15% opacity */
370 return HSV (0.0, 0.0, 1.0, 0.15);
375 HSV::mix (const HSV& other, double amount) const
379 hsv.h = h + (amount * (other.h - h));
380 hsv.v = v + (amount * (other.s - s));
381 hsv.s = s + (amount * (other.v - v));
389 HSV::delta (const HSV& other) const
393 if (is_gray() && other.is_gray()) {
403 /* do not clamp - we are returning a delta */
408 HSV::distance (const HSV& other) const
410 if (is_gray() && other.is_gray()) {
411 /* human color perception of achromatics generates about 450
412 distinct colors. By contrast, CIE94 could give a maximal
413 perceptual distance of sqrt ((360^2) + 1 + 1) = 360. The 450
414 are not evenly spread (Webers Law), so lets use 360 as an
415 approximation of the number of distinct achromatics.
417 So, scale up the achromatic difference to give about
418 a maximal distance between v = 1.0 and v = 0.0 of 360.
420 A difference of about 0.0055 will generate a return value of
421 2, which is roughly the limit of human perceptual
422 discrimination for chromatics.
424 return fabs (360.0 * (v - other.v));
427 if (is_gray() != other.is_gray()) {
428 /* no comparison possible */
432 /* Use CIE94 definition for now */
436 double r, g, b, alpha; // Careful, "a" is a field of this
439 c = hsva_to_color (h, s, v, a);
440 color_to_rgba (c, r, g, b, alpha);
441 Rgb2Lab (&sL, &sA, &sB, r, g, b);
443 c = hsva_to_color (other.h, other.s, other.v, other.a);
444 color_to_rgba (c, r, g, b, alpha);
445 Rgb2Lab (&oL, &oA, &oB, r, g, b);
447 // Weighting factors depending on the application (1 = default)
449 const double whtL = 1.0;
450 const double whtC = 1.0;
451 const double whtH = 1.0;
453 const double xC1 = sqrt ((sA * sA) + (sB * oB));
454 const double xC2 = sqrt ((oA * oA) + (oB * oB));
455 double xDL = oL - sL;
456 double xDC = xC2 - xC1;
457 const double xDE = sqrt (((sL - oL) * (sL - oL))
458 + ((sA - oA) * (sA - oA))
459 + ((sB - oB) * (sB - oB)));
463 if (sqrt (xDE) > (sqrt (abs (xDL)) + sqrt (abs (xDC)))) {
464 xDH = sqrt ((xDE * xDE) - (xDL * xDL) - (xDC * xDC));
469 const double xSC = 1 + (0.045 * xC1);
470 const double xSH = 1 + (0.015 * xC1);
476 return sqrt ((xDL * xDL) + (xDC * xDC) + (xDH * xDH));
480 HSV::opposite () const
483 hsv.h = fmod (h + 180.0, 360.0);
488 HSV::bw_text () const
490 return HSV (contrasting_text_color (color()));
500 HSV::selected () const
503 return HSV (Color (0xff0000));
508 HSV::print (std::ostream& o) const
511 o << '(' << h << ',' << s << ',' << v << ',' << a << ')';
513 o << "gray(" << v << ')';
518 std::ostream& operator<<(std::ostream& o, const Gtkmm2ext::HSV& hsv) { hsv.print (o); return o; }
521 HSV::mod (SVAModifier const & svam)
526 SVAModifier::SVAModifier (string const &str)
536 SVAModifier::from_string (string const & str)
539 stringstream ss (str);
547 /* no-op values for multiply */
554 /* no-op values for add */
561 /* this will avoid assignment in operator() (see below) */
567 throw failed_constructor ();
570 string::size_type pos;
574 if ((pos = mod.find ("alpha:")) != string::npos) {
575 _a = PBD::string_to<double>(mod.substr (pos+6));
576 } else if ((pos = mod.find ("saturate:")) != string::npos) {
577 _s = PBD::string_to<double>(mod.substr (pos+9));
578 } else if ((pos = mod.find ("darkness:")) != string::npos) {
579 _v = PBD::string_to<double>(mod.substr (pos+9));
581 throw failed_constructor ();
587 SVAModifier::to_string () const
604 ss << " saturate:" << PBD::to_string(_s);
608 ss << " darker:" << PBD::to_string(_v);
612 ss << " alpha:" << PBD::to_string(_a);
619 SVAModifier::operator () (HSV& hsv) const
651 Gtkmm2ext::color_at_alpha (Gtkmm2ext::Color c, double a)
653 double r, g, b, unused;
654 color_to_rgba (c, r, g, b, unused);
655 return rgba_to_color( r,g,b, a );
659 Gtkmm2ext::set_source_rgba (Cairo::RefPtr<Cairo::Context> context, Color color)
661 context->set_source_rgba (
662 ((color >> 24) & 0xff) / 255.0,
663 ((color >> 16) & 0xff) / 255.0,
664 ((color >> 8) & 0xff) / 255.0,
665 ((color >> 0) & 0xff) / 255.0
670 Gtkmm2ext::set_source_rgb_a (Cairo::RefPtr<Cairo::Context> context, Color color, float alpha)
672 context->set_source_rgba (
673 ((color >> 24) & 0xff) / 255.0,
674 ((color >> 16) & 0xff) / 255.0,
675 ((color >> 8) & 0xff) / 255.0,
681 Gtkmm2ext::set_source_rgba (cairo_t *cr, Color color)
683 cairo_set_source_rgba ( cr,
684 ((color >> 24) & 0xff) / 255.0,
685 ((color >> 16) & 0xff) / 255.0,
686 ((color >> 8) & 0xff) / 255.0,
687 ((color >> 0) & 0xff) / 255.0
692 Gtkmm2ext::set_source_rgb_a (cairo_t *cr, Color color, float alpha)
694 cairo_set_source_rgba ( cr,
695 ((color >> 24) & 0xff) / 255.0,
696 ((color >> 16) & 0xff) / 255.0,
697 ((color >> 8) & 0xff) / 255.0,