From 601c317d70a03190257577bd867cefc2c70d3275 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 17 Jul 2017 18:34:35 +0200 Subject: Clean up library inheritance (colors.h, utils.h) make libwidget independent of libcanvas. Confine basics to pbd and gtkmm2ext. --- libs/gtkmm2ext/colors.cc | 700 +++++++++++++++++++++++++ libs/gtkmm2ext/colorspace.cc | 944 ++++++++++++++++++++++++++++++++++ libs/gtkmm2ext/gtkmm2ext/colors.h | 138 +++++ libs/gtkmm2ext/gtkmm2ext/colorspace.h | 59 +++ libs/gtkmm2ext/wscript | 2 + 5 files changed, 1843 insertions(+) create mode 100644 libs/gtkmm2ext/colors.cc create mode 100644 libs/gtkmm2ext/colorspace.cc create mode 100644 libs/gtkmm2ext/gtkmm2ext/colors.h create mode 100644 libs/gtkmm2ext/gtkmm2ext/colorspace.h (limited to 'libs/gtkmm2ext') diff --git a/libs/gtkmm2ext/colors.cc b/libs/gtkmm2ext/colors.cc new file mode 100644 index 0000000000..73cf061899 --- /dev/null +++ b/libs/gtkmm2ext/colors.cc @@ -0,0 +1,700 @@ +/* + Copyright (C) 2014 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include + +#include "pbd/failed_constructor.h" +#include "pbd/string_convert.h" + +#include "gtkmm2ext/colors.h" +#include "gtkmm2ext/colorspace.h" + +using namespace std; +using namespace Gtkmm2ext; + +using std::max; +using std::min; + +Gtkmm2ext::Color +Gtkmm2ext::change_alpha (Color c, double a) +{ + return ((c & ~0xff) | (lrintf (a*255.0) & 0xff)); +} + +void +Gtkmm2ext::color_to_hsv (Color color, double& h, double& s, double& v) +{ + double a; + color_to_hsva (color, h, s, v, a); +} + +void +Gtkmm2ext::color_to_hsva (Color color, double& h, double& s, double& v, double& a) +{ + double r, g, b; + double cmax; + double cmin; + double delta; + + color_to_rgba (color, r, g, b, a); + + if (r > g) { + cmax = max (r, b); + } else { + cmax = max (g, b); + } + + if (r < g) { + cmin = min (r, b); + } else { + cmin = min (g, b); + } + + v = cmax; + + delta = cmax - cmin; + + if (cmax == 0) { + // r = g = b == 0 ... v is undefined, s = 0 + s = 0.0; + h = 0.0; + return; + } + + if (delta != 0.0) { + if (cmax == r) { + h = fmod ((g - b)/delta, 6.0); + } else if (cmax == g) { + h = ((b - r)/delta) + 2; + } else { + h = ((r - g)/delta) + 4; + } + + h *= 60.0; + + if (h < 0.0) { + /* negative values are legal but confusing, because + they alias positive values. + */ + h = 360 + h; + } + } + + if (delta == 0 || cmax == 0) { + s = 0; + } else { + s = delta / cmax; + } +} + +Gtkmm2ext::Color +Gtkmm2ext::hsva_to_color (double h, double s, double v, double a) +{ + s = min (1.0, max (0.0, s)); + v = min (1.0, max (0.0, v)); + + if (s == 0) { + return rgba_to_color (v, v, v, a); + } + + h = fmod (h + 360.0, 360.0); + + double c = v * s; + double x = c * (1.0 - fabs(fmod(h / 60.0, 2) - 1.0)); + double m = v - c; + + if (h >= 0.0 && h < 60.0) { + return rgba_to_color (c + m, x + m, m, a); + } else if (h >= 60.0 && h < 120.0) { + return rgba_to_color (x + m, c + m, m, a); + } else if (h >= 120.0 && h < 180.0) { + return rgba_to_color (m, c + m, x + m, a); + } else if (h >= 180.0 && h < 240.0) { + return rgba_to_color (m, x + m, c + m, a); + } else if (h >= 240.0 && h < 300.0) { + return rgba_to_color (x + m, m, c + m, a); + } else if (h >= 300.0 && h < 360.0) { + return rgba_to_color (c + m, m, x + m, a); + } + return rgba_to_color (m, m, m, a); +} + +void +Gtkmm2ext::color_to_rgba (Color color, double& r, double& g, double& b, double& a) +{ + r = ((color >> 24) & 0xff) / 255.0; + g = ((color >> 16) & 0xff) / 255.0; + b = ((color >> 8) & 0xff) / 255.0; + a = ((color >> 0) & 0xff) / 255.0; +} + +Gtkmm2ext::Color +Gtkmm2ext::rgba_to_color (double r, double g, double b, double a) +{ + /* clamp to [0 .. 1] range */ + + r = min (1.0, max (0.0, r)); + g = min (1.0, max (0.0, g)); + b = min (1.0, max (0.0, b)); + a = min (1.0, max (0.0, a)); + + /* convert to [0..255] range */ + + unsigned int rc, gc, bc, ac; + rc = rint (r * 255.0); + gc = rint (g * 255.0); + bc = rint (b * 255.0); + ac = rint (a * 255.0); + + /* build-an-integer */ + + return (rc << 24) | (gc << 16) | (bc << 8) | ac; +} + +// Inverse of sRGB "gamma" function. +static inline double +inv_gam_sRGB (double c) +{ + if (c <= 0.04045) { + return c/12.92; + } else { + return pow(((c+0.055)/(1.055)),2.4); + } +} + +// sRGB "gamma" function +static inline int +gam_sRGB(double v) +{ + if (v <= 0.0031308) { + v *= 12.92; + } else { + v = 1.055 * pow (v, 1.0 / 2.4) - 0.055; + } + return int (v*255+.5); +} + +static double +luminance (uint32_t c) +{ + // sRGB luminance(Y) values + const double rY = 0.212655; + const double gY = 0.715158; + const double bY = 0.072187; + + double r, g, b, a; + + Gtkmm2ext::color_to_rgba (c, r, g, b, a); + + return (gam_sRGB (rY*inv_gam_sRGB(r) + gY*inv_gam_sRGB(g) + bY*inv_gam_sRGB(b))) / 255.0; +} + +uint32_t +Gtkmm2ext::contrasting_text_color (uint32_t c) +{ + /* use a slightly off-white... XXX should really look this up */ + + static const uint32_t white = Gtkmm2ext::rgba_to_color (0.98, 0.98, 0.98, 1.0); + static const uint32_t black = Gtkmm2ext::rgba_to_color (0.0, 0.0, 0.0, 1.0); + + return (luminance (c) < 0.50) ? white : black; +} + + + +HSV::HSV () + : h (0.0) + , s (1.0) + , v (1.0) + , a (1.0) +{ +} + +HSV::HSV (double hh, double ss, double vv, double aa) + : h (hh) + , s (ss) + , v (vv) + , a (aa) +{ + if (h < 0.0) { + /* normalize negative hue values into positive range */ + h = 360.0 + h; + } +} + +HSV::HSV (Color c) +{ + color_to_hsva (c, h, s, v, a); +} + +string +HSV::to_string () const +{ + stringstream ss; + ss << PBD::to_string(h) << ' '; + ss << PBD::to_string(s) << ' '; + ss << PBD::to_string(v) << ' '; + ss << PBD::to_string(a); + return ss.str(); +} + +bool +HSV::is_gray () const +{ + return s == 0; +} + +void +HSV::clamp () +{ + h = fmod (h, 360.0); + if (h < 0.0) { + /* normalize negative hue values into positive range */ + h = 360.0 + h; + } + s = min (1.0, s); + v = min (1.0, v); + a = min (1.0, a); +} + +HSV +HSV::operator+ (const HSV& operand) const +{ + HSV hsv; + hsv.h = h + operand.h; + hsv.s = s + operand.s; + hsv.v = v + operand.v; + hsv.a = a + operand.a; + hsv.clamp (); + return hsv; +} + +HSV +HSV::operator- (const HSV& operand) const +{ + HSV hsv; + hsv.h = h - operand.h; + hsv.s = s - operand.s; + hsv.v = s - operand.v; + hsv.a = a - operand.a; + hsv.clamp (); + return hsv; +} + +HSV& +HSV::operator=(Color c) +{ + color_to_hsva (c, h, s, v, a); + clamp (); + return *this; +} + +HSV& +HSV::operator=(const std::string& str) +{ + uint32_t c; + c = strtol (str.c_str(), 0, 16); + color_to_hsva (c, h, s, v, a); + clamp (); + return *this; +} + +bool +HSV::operator== (const HSV& other) +{ + return h == other.h && + s == other.s && + v == other.v && + a == other.a; +} + +HSV +HSV::shade (double factor) const +{ + HSV hsv (*this); + + /* algorithm derived from a google palette website + and analysis of their color palettes. + + basic rule: to make a color darker, increase its saturation + until it reaches 88%, but then additionally reduce value/lightness + by a larger amount. + + invert rule to make a color lighter. + */ + + if (factor > 1.0) { + if (s < 88) { + hsv.v += (hsv.v * (factor * 10.0)); + } + hsv.s *= factor; + } else { + if (s < 88) { + hsv.v -= (hsv.v * (factor * 10.0)); + } + hsv.s *= factor; + } + + hsv.clamp(); + + return hsv; +} + +HSV +HSV::outline () const +{ + if (luminance (color()) < 0.50) { + /* light color, darker outline: black with 15% opacity */ + return HSV (0.0, 0.0, 0.0, 0.15); + } else { + /* dark color, lighter outline: white with 15% opacity */ + return HSV (0.0, 0.0, 1.0, 0.15); + } +} + +HSV +HSV::mix (const HSV& other, double amount) const +{ + HSV hsv; + + hsv.h = h + (amount * (other.h - h)); + hsv.v = v + (amount * (other.s - s)); + hsv.s = s + (amount * (other.v - v)); + + hsv.clamp(); + + return hsv; +} + +HSV +HSV::delta (const HSV& other) const +{ + HSV d; + + if (is_gray() && other.is_gray()) { + d.h = 0.0; + d.s = 0.0; + d.v = v - other.v; + } else { + d.h = h - other.h; + d.s = s - other.s; + d.v = v - other.v; + } + d.a = a - other.a; + /* do not clamp - we are returning a delta */ + return d; +} + +double +HSV::distance (const HSV& other) const +{ + if (is_gray() && other.is_gray()) { + /* human color perception of achromatics generates about 450 + distinct colors. By contrast, CIE94 could give a maximal + perceptual distance of sqrt ((360^2) + 1 + 1) = 360. The 450 + are not evenly spread (Webers Law), so lets use 360 as an + approximation of the number of distinct achromatics. + + So, scale up the achromatic difference to give about + a maximal distance between v = 1.0 and v = 0.0 of 360. + + A difference of about 0.0055 will generate a return value of + 2, which is roughly the limit of human perceptual + discrimination for chromatics. + */ + return fabs (360.0 * (v - other.v)); + } + + if (is_gray() != other.is_gray()) { + /* no comparison possible */ + return DBL_MAX; + } + + /* Use CIE94 definition for now */ + + double sL, sA, sB; + double oL, oA, oB; + double r, g, b, alpha; // Careful, "a" is a field of this + Color c; + + c = hsva_to_color (h, s, v, a); + color_to_rgba (c, r, g, b, alpha); + Rgb2Lab (&sL, &sA, &sB, r, g, b); + + c = hsva_to_color (other.h, other.s, other.v, other.a); + color_to_rgba (c, r, g, b, alpha); + Rgb2Lab (&oL, &oA, &oB, r, g, b); + + // Weighting factors depending on the application (1 = default) + + const double whtL = 1.0; + const double whtC = 1.0; + const double whtH = 1.0; + + const double xC1 = sqrt ((sA * sA) + (sB * oB)); + const double xC2 = sqrt ((oA * oA) + (oB * oB)); + double xDL = oL - sL; + double xDC = xC2 - xC1; + const double xDE = sqrt (((sL - oL) * (sL - oL)) + + ((sA - oA) * (sA - oA)) + + ((sB - oB) * (sB - oB))); + + double xDH; + + if (sqrt (xDE) > (sqrt (abs (xDL)) + sqrt (abs (xDC)))) { + xDH = sqrt ((xDE * xDE) - (xDL * xDL) - (xDC * xDC)); + } else { + xDH = 0; + } + + const double xSC = 1 + (0.045 * xC1); + const double xSH = 1 + (0.015 * xC1); + + xDL /= whtL; + xDC /= whtC * xSC; + xDH /= whtH * xSH; + + return sqrt ((xDL * xDL) + (xDC * xDC) + (xDH * xDH)); +} + +HSV +HSV::opposite () const +{ + HSV hsv (*this); + hsv.h = fmod (h + 180.0, 360.0); + return hsv; +} + +HSV +HSV::bw_text () const +{ + return HSV (contrasting_text_color (color())); +} + +HSV +HSV::text () const +{ + return opposite (); +} + +HSV +HSV::selected () const +{ + /* XXX hack */ + return HSV (Color (0xff0000)); +} + + +void +HSV::print (std::ostream& o) const +{ + if (!is_gray()) { + o << '(' << h << ',' << s << ',' << v << ',' << a << ')'; + } else { + o << "gray(" << v << ')'; + } +} + + +std::ostream& operator<<(std::ostream& o, const Gtkmm2ext::HSV& hsv) { hsv.print (o); return o; } + +HSV +HSV::mod (SVAModifier const & svam) +{ + return svam (*this); +} + +SVAModifier::SVAModifier (string const &str) + : type (Add) + , _s (0) + , _v (0) + , _a (0) +{ + from_string (str); +} + +void +SVAModifier::from_string (string const & str) +{ + char op; + stringstream ss (str); + string mod; + + ss >> op; + + switch (op) { + case '*': + type = Multiply; + /* no-op values for multiply */ + _s = 1.0; + _v = 1.0; + _a = 1.0; + break; + case '+': + type = Add; + /* no-op values for add */ + _s = 0.0; + _v = 0.0; + _a = 0.0; + break; + case '=': + type = Assign; + /* this will avoid assignment in operator() (see below) */ + _s = -1.0; + _v = -1.0; + _a = -1.0; + break; + default: + throw failed_constructor (); + } + + string::size_type pos; + + while (ss) { + ss >> mod; + if ((pos = mod.find ("alpha:")) != string::npos) { + _a = PBD::string_to(mod.substr (pos+6)); + } else if ((pos = mod.find ("saturate:")) != string::npos) { + _s = PBD::string_to(mod.substr (pos+9)); + } else if ((pos = mod.find ("darkness:")) != string::npos) { + _v = PBD::string_to(mod.substr (pos+9)); + } else { + throw failed_constructor (); + } + } +} + +string +SVAModifier::to_string () const +{ + stringstream ss; + + switch (type) { + case Add: + ss << '+'; + break; + case Multiply: + ss << '*'; + break; + case Assign: + ss << '='; + break; + } + + if (_s >= 0.0) { + ss << " saturate:" << PBD::to_string(_s); + } + + if (_v >= 0.0) { + ss << " darker:" << PBD::to_string(_v); + } + + if (_a >= 0.0) { + ss << " alpha:" << PBD::to_string(_a); + } + + return ss.str(); +} + +HSV +SVAModifier::operator () (HSV& hsv) const +{ + HSV r (hsv); + + switch (type) { + case Add: + r.s += _s; + r.v += _v; + r.a += _a; + break; + case Multiply: + r.s *= _s; + r.v *= _v; + r.a *= _a; + break; + case Assign: + if (_s >= 0.0) { + r.s = _s; + } + if (_v >= 0.) { + r.v = _v; + } + if (_a >= 0.0) { + r.a = _a; + } + break; + } + + return r; +} + +Color +Gtkmm2ext::color_at_alpha (Gtkmm2ext::Color c, double a) +{ + double r, g, b, unused; + color_to_rgba (c, r, g, b, unused); + return rgba_to_color( r,g,b, a ); +} + +void +Gtkmm2ext::set_source_rgba (Cairo::RefPtr context, Color color) +{ + context->set_source_rgba ( + ((color >> 24) & 0xff) / 255.0, + ((color >> 16) & 0xff) / 255.0, + ((color >> 8) & 0xff) / 255.0, + ((color >> 0) & 0xff) / 255.0 + ); +} + +void +Gtkmm2ext::set_source_rgb_a (Cairo::RefPtr context, Color color, float alpha) +{ + context->set_source_rgba ( + ((color >> 24) & 0xff) / 255.0, + ((color >> 16) & 0xff) / 255.0, + ((color >> 8) & 0xff) / 255.0, + alpha + ); +} + +void +Gtkmm2ext::set_source_rgba (cairo_t *cr, Color color) +{ + cairo_set_source_rgba ( cr, + ((color >> 24) & 0xff) / 255.0, + ((color >> 16) & 0xff) / 255.0, + ((color >> 8) & 0xff) / 255.0, + ((color >> 0) & 0xff) / 255.0 + ); +} + +void +Gtkmm2ext::set_source_rgb_a (cairo_t *cr, Color color, float alpha) +{ + cairo_set_source_rgba ( cr, + ((color >> 24) & 0xff) / 255.0, + ((color >> 16) & 0xff) / 255.0, + ((color >> 8) & 0xff) / 255.0, + alpha + ); +} diff --git a/libs/gtkmm2ext/colorspace.cc b/libs/gtkmm2ext/colorspace.cc new file mode 100644 index 0000000000..b614616800 --- /dev/null +++ b/libs/gtkmm2ext/colorspace.cc @@ -0,0 +1,944 @@ +/** + * @file colorspace.c + * @author Pascal Getreuer 2005-2010 + * + * == Summary == + * This file implements routines for color transformations between the spaces + * sRGB, Y'UV, Y'CbCr, Y'PbPr, Y'DbDr, Y'IQ, HSV, HSL, HSI, CIEXYZ, CIELAB, + * CIELUV, CIELCH, and CIECAT02 LMS. + * + * == Usage == + * First call GetColorTransform, specifying the source and destination color + * spaces as "dest<-src" or "src->dest". Then call ApplyColorTransform to + * perform the transform: +@code + double S[3] = {173, 0.8, 0.5}; + double D[3]; + colortransform Trans; + + if(!(GetColorTransform(&Trans, "HSI -> Lab"))) + { + printf("Invalid syntax or unknown color space\n"); + return; + } + + ApplyColorTransform(Trans, &D[0], &D[1], &D[2], S[0], S[1], S[2]); +@endcode + * "num" is a typedef defined at the beginning of colorspace.h that may be set + * to either double or float, depending on the application. + * + * Specific transformation routines can also be called directly. The following + * converts an sRGB color to CIELAB and then back to sRGB: +@code + double R = 0.85, G = 0.32, B = 0.5; + double L, a, b; + Rgb2Lab(&L, &a, &b, R, G, B); + Lab2Rgb(&R, &G, &B, L, a, b); +@endcode + * Generally, the calling syntax is +@code + Foo2Bar(&B0, &B1, &B2, F0, F1, F2); +@endcode + * where (F0,F1,F2) are the coordinates of a color in space "Foo" and + * (B0,B1,B2) are the transformed coordinates in space "Bar." For any + * transformation routine, its inverse has the sytax +@code + Bar2Foo(&F0, &F1, &F2, B0, B1, B2); +@endcode + * + * The conversion routines are consistently named with the first letter of a + * color space capitalized with following letters in lower case and omitting + * prime symbols. For example, "Rgb2Ydbdr" converts sRGB to Y'DbDr. For + * any transformation routine Foo2Bar, its inverse is Bar2Foo. + * + * All transformations assume a two degree observer angle and a D65 illuminant. + * The white point can be changed by modifying the WHITEPOINT_X, WHITEPOINT_Y, + * WHITEPOINT_Z definitions at the beginning of colorspace.h. + * + * == List of transformation routines == + * - Rgb2Yuv(double *Y, double *U, double *V, double R, double G, double B) + * - Rgb2Ycbcr(double *Y, double *Cb, double *Cr, double R, double G, double B) + * - Rgb2Jpegycbcr(double *Y, double *Cb, double *Cr, double R, double G, double B) + * - Rgb2Ypbpr(double *Y, double *Pb, double *Pr, double R, double G, double B) + * - Rgb2Ydbdr(double *Y, double *Db, double *Dr, double R, double G, double B) + * - Rgb2Yiq(double *Y, double *I, double *Q, double R, double G, double B) + * - Rgb2Hsv(double *H, double *S, double *V, double R, double G, double B) + * - Rgb2Hsl(double *H, double *S, double *L, double R, double G, double B) + * - Rgb2Hsi(double *H, double *S, double *I, double R, double G, double B) + * - Rgb2Xyz(double *X, double *Y, double *Z, double R, double G, double B) + * - Xyz2Lab(double *L, double *a, double *b, double X, double Y, double Z) + * - Xyz2Luv(double *L, double *u, double *v, double X, double Y, double Z) + * - Xyz2Lch(double *L, double *C, double *h, double X, double Y, double Z) + * - Xyz2Cat02lms(double *L, double *M, double *S, double X, double Y, double Z) + * - Rgb2Lab(double *L, double *a, double *b, double R, double G, double B) + * - Rgb2Luv(double *L, double *u, double *v, double R, double G, double B) + * - Rgb2Lch(double *L, double *C, double *h, double R, double G, double B) + * - Rgb2Cat02lms(double *L, double *M, double *S, double R, double G, double B) + * (Similarly for the inverse transformations.) + * + * It is possible to transform between two arbitrary color spaces by first + * transforming from the source space to sRGB and then transforming from + * sRGB to the desired destination space. For transformations between CIE + * color spaces, it is convenient to use XYZ as the intermediate space. This + * is the strategy used by GetColorTransform and ApplyColorTransform. + * + * == References == + * The definitions of these spaces and the many of the transformation formulas + * can be found in + * + * Poynton, "Frequently Asked Questions About Gamma" + * http://www.poynton.com/notes/colour_and_gamma/GammaFAQ.html + * + * Poynton, "Frequently Asked Questions About Color" + * http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html + * + * and Wikipedia articles + * http://en.wikipedia.org/wiki/SRGB + * http://en.wikipedia.org/wiki/YUV + * http://en.wikipedia.org/wiki/YCbCr + * http://en.wikipedia.org/wiki/YPbPr + * http://en.wikipedia.org/wiki/YDbDr + * http://en.wikipedia.org/wiki/YIQ + * http://en.wikipedia.org/wiki/HSL_and_HSV + * http://en.wikipedia.org/wiki/CIE_1931_color_space + * http://en.wikipedia.org/wiki/Lab_color_space + * http://en.wikipedia.org/wiki/CIELUV_color_space + * http://en.wikipedia.org/wiki/LMS_color_space + * + * == License (BSD) == + * Copyright (c) 2005-2010, Pascal Getreuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include + +#include "gtkmm2ext/colorspace.h" + +namespace Gtkmm2ext { + +/** @brief Min of A and B */ +#define MIN(A,B) (((A) <= (B)) ? (A) : (B)) + +/** @brief Max of A and B */ +#define MAX(A,B) (((A) >= (B)) ? (A) : (B)) + +/** @brief Min of A, B, and C */ +#define MIN3(A,B,C) (((A) <= (B)) ? MIN(A,C) : MIN(B,C)) + +/** @brief Max of A, B, and C */ +#define MAX3(A,B,C) (((A) >= (B)) ? MAX(A,C) : MAX(B,C)) + +#ifndef M_PI +/** @brief The constant pi */ +#define M_PI 3.14159265358979323846264338327950288 +#endif + +/** + * @brief sRGB gamma correction, transforms R to R' + * http://en.wikipedia.org/wiki/SRGB + */ +#define GAMMACORRECTION(t) \ + (((t) <= 0.0031306684425005883) ? \ + (12.92*(t)) : (1.055*pow((t), 0.416666666666666667) - 0.055)) + +/** + * @brief Inverse sRGB gamma correction, transforms R' to R + */ +#define INVGAMMACORRECTION(t) \ + (((t) <= 0.0404482362771076) ? \ + ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4)) + +/** + * @brief CIE L*a*b* f function (used to convert XYZ to L*a*b*) + * http://en.wikipedia.org/wiki/Lab_color_space + */ +#define LABF(t) \ + ((t >= 8.85645167903563082e-3) ? \ + pow(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0)) + +/** + * @brief CIE L*a*b* inverse f function + * http://en.wikipedia.org/wiki/Lab_color_space + */ +#define LABINVF(t) \ + ((t >= 0.206896551724137931) ? \ + ((t)*(t)*(t)) : (108.0/841.0)*((t) - (4.0/29.0))) + +/** @brief u'v' coordinates of the white point for CIE Lu*v* */ +#define WHITEPOINT_U ((4*WHITEPOINT_X) \ + /(WHITEPOINT_X + 15*WHITEPOINT_Y + 3*WHITEPOINT_Z)) +#define WHITEPOINT_V ((9*WHITEPOINT_Y) \ + /(WHITEPOINT_X + 15*WHITEPOINT_Y + 3*WHITEPOINT_Z)) + +/** @brief Enumeration of the supported color spaces */ +#define UNKNOWN_SPACE 0 +#define RGB_SPACE 1 +#define YUV_SPACE 2 +#define YCBCR_SPACE 3 +#define JPEGYCBCR_SPACE 4 +#define YPBPR_SPACE 5 +#define YDBDR_SPACE 6 +#define YIQ_SPACE 7 +#define HSV_SPACE 8 +#define HSL_SPACE 9 +#define HSI_SPACE 10 +#define XYZ_SPACE 11 +#define LAB_SPACE 12 +#define LUV_SPACE 13 +#define LCH_SPACE 14 +#define CAT02LMS_SPACE 15 + +#define NUM_TRANSFORM_PAIRS 18 + + + +/* + * == Linear color transformations == + * + * The following routines implement transformations between sRGB and + * the linearly-related color spaces Y'UV, Y'PbPr, Y'DbDr, and Y'IQ. + */ + + +/** + * @brief Convert sRGB to NTSC/PAL Y'UV Luma + Chroma + * + * @param Y, U, V pointers to hold the result + * @param R, G, B the input sRGB values + * + * Wikipedia: http://en.wikipedia.org/wiki/YUV + */ +void Rgb2Yuv(double *Y, double *U, double *V, double R, double G, double B) +{ + *Y = (double)( 0.299*R + 0.587*G + 0.114*B); + *U = (double)(-0.147*R - 0.289*G + 0.436*B); + *V = (double)( 0.615*R - 0.515*G - 0.100*B); +} + + +/** + * @brief Convert NTSC/PAL Y'UV to sRGB + * + * @param R, G, B pointers to hold the result + * @param Y, U, V the input YUV values + */ +void Yuv2Rgb(double *R, double *G, double *B, double Y, double U, double V) +{ + *R = (double)(Y - 3.945707070708279e-05*U + 1.1398279671717170825*V); + *G = (double)(Y - 0.3946101641414141437*U - 0.5805003156565656797*V); + *B = (double)(Y + 2.0319996843434342537*U - 4.813762626262513e-04*V); +} + + +/** @brief sRGB to Y'CbCr Luma + Chroma */ +void Rgb2Ycbcr(double *Y, double *Cb, double *Cr, double R, double G, double B) +{ + *Y = (double)( 65.481*R + 128.553*G + 24.966*B + 16); + *Cb = (double)(-37.797*R - 74.203*G + 112.0 *B + 128); + *Cr = (double)(112.0 *R - 93.786*G - 18.214*B + 128); +} + + +/** @brief Y'CbCr to sRGB */ +void Ycbcr2Rgb(double *R, double *G, double *B, double Y, double Cr, double Cb) +{ + Y -= 16; + Cb -= 128; + Cr -= 128; + *R = (double)(0.00456621004566210107*Y + 1.1808799897946415e-09*Cr + 0.00625892896994393634*Cb); + *G = (double)(0.00456621004566210107*Y - 0.00153632368604490212*Cr - 0.00318811094965570701*Cb); + *B = (double)(0.00456621004566210107*Y + 0.00791071623355474145*Cr + 1.1977497040190077e-08*Cb); +} + + +/** @brief sRGB to JPEG-Y'CbCr Luma + Chroma */ +void Rgb2Jpegycbcr(double *Y, double *Cb, double *Cr, double R, double G, double B) +{ + Rgb2Ypbpr(Y, Cb, Cr, R, G, B); + *Cb += (double)0.5; + *Cr += (double)0.5; +} + +/** @brief JPEG-Y'CbCr to sRGB */ +void Jpegycbcr2Rgb(double *R, double *G, double *B, double Y, double Cb, double Cr) +{ + Cb -= (double)0.5; + Cr -= (double)0.5; + Ypbpr2Rgb(R, G, B, Y, Cb, Cr); +} + + +/** @brief sRGB to Y'PbPr Luma (ITU-R BT.601) + Chroma */ +void Rgb2Ypbpr(double *Y, double *Pb, double *Pr, double R, double G, double B) +{ + *Y = (double)( 0.299 *R + 0.587 *G + 0.114 *B); + *Pb = (double)(-0.1687367*R - 0.331264*G + 0.5 *B); + *Pr = (double)( 0.5 *R - 0.418688*G - 0.081312*B); +} + + +/** @brief Y'PbPr to sRGB */ +void Ypbpr2Rgb(double *R, double *G, double *B, double Y, double Pb, double Pr) +{ + *R = (double)(0.99999999999914679361*Y - 1.2188941887145875e-06*Pb + 1.4019995886561440468*Pr); + *G = (double)(0.99999975910502514331*Y - 0.34413567816504303521*Pb - 0.71413649331646789076*Pr); + *B = (double)(1.00000124040004623180*Y + 1.77200006607230409200*Pb + 2.1453384174593273e-06*Pr); +} + + +/** @brief sRGB to SECAM Y'DbDr Luma + Chroma */ +void Rgb2Ydbdr(double *Y, double *Db, double *Dr, double R, double G, double B) +{ + *Y = (double)( 0.299*R + 0.587*G + 0.114*B); + *Db = (double)(-0.450*R - 0.883*G + 1.333*B); + *Dr = (double)(-1.333*R + 1.116*G + 0.217*B); +} + + +/** @brief SECAM Y'DbDr to sRGB */ +void Ydbdr2Rgb(double *R, double *G, double *B, double Y, double Db, double Dr) +{ + *R = (double)(Y + 9.2303716147657e-05*Db - 0.52591263066186533*Dr); + *G = (double)(Y - 0.12913289889050927*Db + 0.26789932820759876*Dr); + *B = (double)(Y + 0.66467905997895482*Db - 7.9202543533108e-05*Dr); +} + + +/** @brief sRGB to NTSC YIQ */ +void Rgb2Yiq(double *Y, double *I, double *Q, double R, double G, double B) +{ + *Y = (double)(0.299 *R + 0.587 *G + 0.114 *B); + *I = (double)(0.595716*R - 0.274453*G - 0.321263*B); + *Q = (double)(0.211456*R - 0.522591*G + 0.311135*B); +} + + +/** @brief Convert NTSC YIQ to sRGB */ +void Yiq2Rgb(double *R, double *G, double *B, double Y, double I, double Q) +{ + *R = (double)(Y + 0.9562957197589482261*I + 0.6210244164652610754*Q); + *G = (double)(Y - 0.2721220993185104464*I - 0.6473805968256950427*Q); + *B = (double)(Y - 1.1069890167364901945*I + 1.7046149983646481374*Q); +} + + + +/* + * == Hue Saturation Value/Lightness/Intensity color transformations == + * + * The following routines implement transformations between sRGB and + * color spaces HSV, HSL, and HSI. + */ + + +/** + * @brief Convert an sRGB color to Hue-Saturation-Value (HSV) + * + * @param H, S, V pointers to hold the result + * @param R, G, B the input sRGB values scaled in [0,1] + * + * This routine transforms from sRGB to the hexcone HSV color space. The + * sRGB values are assumed to be between 0 and 1. The output values are + * H = hexagonal hue angle (0 <= H < 360), + * S = C/V (0 <= S <= 1), + * V = max(R',G',B') (0 <= V <= 1), + * where C = max(R',G',B') - min(R',G',B'). The inverse color transformation + * is given by Hsv2Rgb. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Rgb2Hsv(double *H, double *S, double *V, double R, double G, double B) +{ + double Max = MAX3(R, G, B); + double Min = MIN3(R, G, B); + double C = Max - Min; + + + *V = Max; + + if(C > 0) + { + if(Max == R) + { + *H = (G - B) / C; + + if(G < B) + *H += 6; + } + else if(Max == G) + *H = 2 + (B - R) / C; + else + *H = 4 + (R - G) / C; + + *H *= 60; + *S = C / Max; + } + else + *H = *S = 0; +} + + +/** + * @brief Convert a Hue-Saturation-Value (HSV) color to sRGB + * + * @param R, G, B pointers to hold the result + * @param H, S, V the input HSV values + * + * The input values are assumed to be scaled as + * 0 <= H < 360, + * 0 <= S <= 1, + * 0 <= V <= 1. + * The output sRGB values are scaled between 0 and 1. This is the inverse + * transformation of Rgb2Hsv. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Hsv2Rgb(double *R, double *G, double *B, double H, double S, double V) +{ + double C = S * V; + double Min = V - C; + double X; + + + H -= 360*floor(H/360); + H /= 60; + X = C*(1 - fabs(H - 2*floor(H/2) - 1)); + + switch((int)H) + { + case 0: + *R = Min + C; + *G = Min + X; + *B = Min; + break; + case 1: + *R = Min + X; + *G = Min + C; + *B = Min; + break; + case 2: + *R = Min; + *G = Min + C; + *B = Min + X; + break; + case 3: + *R = Min; + *G = Min + X; + *B = Min + C; + break; + case 4: + *R = Min + X; + *G = Min; + *B = Min + C; + break; + case 5: + *R = Min + C; + *G = Min; + *B = Min + X; + break; + default: + *R = *G = *B = 0; + } +} + + +/** + * @brief Convert an sRGB color to Hue-Saturation-Lightness (HSL) + * + * @param H, S, L pointers to hold the result + * @param R, G, B the input sRGB values scaled in [0,1] + * + * This routine transforms from sRGB to the double hexcone HSL color space + * The sRGB values are assumed to be between 0 and 1. The outputs are + * H = hexagonal hue angle (0 <= H < 360), + * S = { C/(2L) if L <= 1/2 (0 <= S <= 1), + * { C/(2 - 2L) if L > 1/2 + * L = (max(R',G',B') + min(R',G',B'))/2 (0 <= L <= 1), + * where C = max(R',G',B') - min(R',G',B'). The inverse color transformation + * is given by Hsl2Rgb. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Rgb2Hsl(double *H, double *S, double *L, double R, double G, double B) +{ + double Max = MAX3(R, G, B); + double Min = MIN3(R, G, B); + double C = Max - Min; + + + *L = (Max + Min)/2; + + if(C > 0) + { + if(Max == R) + { + *H = (G - B) / C; + + if(G < B) + *H += 6; + } + else if(Max == G) + *H = 2 + (B - R) / C; + else + *H = 4 + (R - G) / C; + + *H *= 60; + *S = (*L <= 0.5) ? (C/(2*(*L))) : (C/(2 - 2*(*L))); + } + else + *H = *S = 0; +} + + +/** + * @brief Convert a Hue-Saturation-Lightness (HSL) color to sRGB + * + * @param R, G, B pointers to hold the result + * @param H, S, L the input HSL values + * + * The input values are assumed to be scaled as + * 0 <= H < 360, + * 0 <= S <= 1, + * 0 <= L <= 1. + * The output sRGB values are scaled between 0 and 1. This is the inverse + * transformation of Rgb2Hsl. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Hsl2Rgb(double *R, double *G, double *B, double H, double S, double L) +{ + double C = (L <= 0.5) ? (2*L*S) : ((2 - 2*L)*S); + double Min = L - 0.5*C; + double X; + + + H -= 360*floor(H/360); + H /= 60; + X = C*(1 - fabs(H - 2*floor(H/2) - 1)); + + switch((int)H) + { + case 0: + *R = Min + C; + *G = Min + X; + *B = Min; + break; + case 1: + *R = Min + X; + *G = Min + C; + *B = Min; + break; + case 2: + *R = Min; + *G = Min + C; + *B = Min + X; + break; + case 3: + *R = Min; + *G = Min + X; + *B = Min + C; + break; + case 4: + *R = Min + X; + *G = Min; + *B = Min + C; + break; + case 5: + *R = Min + C; + *G = Min; + *B = Min + X; + break; + default: + *R = *G = *B = 0; + } +} + + +/** + * @brief Convert an sRGB color to Hue-Saturation-Intensity (HSI) + * + * @param H, S, I pointers to hold the result + * @param R, G, B the input sRGB values scaled in [0,1] + * + * This routine transforms from sRGB to the cylindrical HSI color space. The + * sRGB values are assumed to be between 0 and 1. The output values are + * H = polar hue angle (0 <= H < 360), + * S = 1 - min(R',G',B')/I (0 <= S <= 1), + * I = (R'+G'+B')/3 (0 <= I <= 1). + * The inverse color transformation is given by Hsi2Rgb. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Rgb2Hsi(double *H, double *S, double *I, double R, double G, double B) +{ + double alpha = 0.5*(2*R - G - B); + double beta = 0.866025403784439*(G - B); + + + *I = (R + G + B)/3; + + if(*I > 0) + { + *S = 1 - MIN3(R,G,B) / *I; + *H = atan2(beta, alpha)*(180/M_PI); + + if(*H < 0) + *H += 360; + } + else + *H = *S = 0; +} + + +/** + * @brief Convert a Hue-Saturation-Intesity (HSI) color to sRGB + * + * @param R, G, B pointers to hold the result + * @param H, S, I the input HSI values + * + * The input values are assumed to be scaled as + * 0 <= H < 360, + * 0 <= S <= 1, + * 0 <= I <= 1. + * The output sRGB values are scaled between 0 and 1. This is the inverse + * transformation of Rgb2Hsi. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Hsi2Rgb(double *R, double *G, double *B, double H, double S, double I) +{ + H -= 360*floor(H/360); + + if(H < 120) + { + *B = I*(1 - S); + *R = I*(1 + S*cos(H*(M_PI/180))/cos((60 - H)*(M_PI/180))); + *G = 3*I - *R - *B; + } + else if(H < 240) + { + H -= 120; + *R = I*(1 - S); + *G = I*(1 + S*cos(H*(M_PI/180))/cos((60 - H)*(M_PI/180))); + *B = 3*I - *R - *G; + } + else + { + H -= 240; + *G = I*(1 - S); + *B = I*(1 + S*cos(H*(M_PI/180))/cos((60 - H)*(M_PI/180))); + *R = 3*I - *G - *B; + } +} + + +/* + * == CIE color transformations == + * + * The following routines implement transformations between sRGB and + * the CIE color spaces XYZ, L*a*b, L*u*v*, and L*C*H*. These + * transforms assume a 2 degree observer angle and a D65 illuminant. + */ + + +/** + * @brief Transform sRGB to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param R, G, B the input sRGB values + * + * Poynton, "Frequently Asked Questions About Color," page 10 + * Wikipedia: http://en.wikipedia.org/wiki/SRGB + * Wikipedia: http://en.wikipedia.org/wiki/CIE_1931_color_space + */ +void Rgb2Xyz(double *X, double *Y, double *Z, double R, double G, double B) +{ + R = INVGAMMACORRECTION(R); + G = INVGAMMACORRECTION(G); + B = INVGAMMACORRECTION(B); + *X = (double)(0.4123955889674142161*R + 0.3575834307637148171*G + 0.1804926473817015735*B); + *Y = (double)(0.2125862307855955516*R + 0.7151703037034108499*G + 0.07220049864333622685*B); + *Z = (double)(0.01929721549174694484*R + 0.1191838645808485318*G + 0.9504971251315797660*B); +} + + +/** + * @brief Transform CIE XYZ to sRGB with the D65 white point + * + * @param R, G, B pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * Official sRGB specification (IEC 61966-2-1:1999) + * Poynton, "Frequently Asked Questions About Color," page 10 + * Wikipedia: http://en.wikipedia.org/wiki/SRGB + * Wikipedia: http://en.wikipedia.org/wiki/CIE_1931_color_space + */ +void Xyz2Rgb(double *R, double *G, double *B, double X, double Y, double Z) +{ + double R1, B1, G1, Min; + + + R1 = (double)( 3.2406*X - 1.5372*Y - 0.4986*Z); + G1 = (double)(-0.9689*X + 1.8758*Y + 0.0415*Z); + B1 = (double)( 0.0557*X - 0.2040*Y + 1.0570*Z); + + Min = MIN3(R1, G1, B1); + + /* Force nonnegative values so that gamma correction is well-defined. */ + if(Min < 0) + { + R1 -= Min; + G1 -= Min; + B1 -= Min; + } + + /* Transform from RGB to R'G'B' */ + *R = GAMMACORRECTION(R1); + *G = GAMMACORRECTION(G1); + *B = GAMMACORRECTION(B1); +} + + +/** + * Convert CIE XYZ to CIE L*a*b* (CIELAB) with the D65 white point + * + * @param L, a, b pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * Wikipedia: http://en.wikipedia.org/wiki/Lab_color_space + */ +void Xyz2Lab(double *L, double *a, double *b, double X, double Y, double Z) +{ + X /= WHITEPOINT_X; + Y /= WHITEPOINT_Y; + Z /= WHITEPOINT_Z; + X = LABF(X); + Y = LABF(Y); + Z = LABF(Z); + *L = 116*Y - 16; + *a = 500*(X - Y); + *b = 200*(Y - Z); +} + + +/** + * Convert CIE L*a*b* (CIELAB) to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param L, a, b the input L*a*b* values + * + * Wikipedia: http://en.wikipedia.org/wiki/Lab_color_space + */ +void Lab2Xyz(double *X, double *Y, double *Z, double L, double a, double b) +{ + L = (L + 16)/116; + a = L + a/500; + b = L - b/200; + *X = WHITEPOINT_X*LABINVF(a); + *Y = WHITEPOINT_Y*LABINVF(L); + *Z = WHITEPOINT_Z*LABINVF(b); +} + + +/** + * Convert CIE XYZ to CIE L*u*v* (CIELUV) with the D65 white point + * + * @param L, u, v pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * Wikipedia: http://en.wikipedia.org/wiki/CIELUV_color_space + */ +void Xyz2Luv(double *L, double *u, double *v, double X, double Y, double Z) +{ + double u1, v1, Denom; + + + if((Denom = X + 15*Y + 3*Z) > 0) + { + u1 = (4*X) / Denom; + v1 = (9*Y) / Denom; + } + else + u1 = v1 = 0; + + Y /= WHITEPOINT_Y; + Y = LABF(Y); + *L = 116*Y - 16; + *u = 13*(*L)*(u1 - WHITEPOINT_U); + *v = 13*(*L)*(v1 - WHITEPOINT_V); +} + + +/** + * Convert CIE L*u*v* (CIELUV) to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param L, u, v the input L*u*v* values + * + * Wikipedia: http://en.wikipedia.org/wiki/CIELUV_color_space + */ +void Luv2Xyz(double *X, double *Y, double *Z, double L, double u, double v) +{ + *Y = (L + 16)/116; + *Y = WHITEPOINT_Y*LABINVF(*Y); + + if(L != 0) + { + u /= L; + v /= L; + } + + u = u/13 + WHITEPOINT_U; + v = v/13 + WHITEPOINT_V; + *X = (*Y) * ((9*u)/(4*v)); + *Z = (*Y) * ((3 - 0.75*u)/v - 5); +} + + +/** + * Convert CIE XYZ to CIE L*C*H* with the D65 white point + * + * @param L, C, H pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * CIE L*C*H* is related to CIE L*a*b* by + * a* = C* cos(H* pi/180), + * b* = C* sin(H* pi/180). + */ +void Xyz2Lch(double *L, double *C, double *H, double X, double Y, double Z) +{ + double a, b; + + + Xyz2Lab(L, &a, &b, X, Y, Z); + *C = sqrt(a*a + b*b); + *H = atan2(b, a)*180.0/M_PI; + + if(*H < 0) + *H += 360; +} + +/** + * Convert CIE L*C*H* to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param L, C, H the input L*C*H* values + */ +void Lch2Xyz(double *X, double *Y, double *Z, double L, double C, double H) +{ + double a = C * cos(H*(M_PI/180.0)); + double b = C * sin(H*(M_PI/180.0)); + + + Lab2Xyz(X, Y, Z, L, a, b); +} + + +/** @brief XYZ to CAT02 LMS */ +void Xyz2Cat02lms(double *L, double *M, double *S, double X, double Y, double Z) +{ + *L = (double)( 0.7328*X + 0.4296*Y - 0.1624*Z); + *M = (double)(-0.7036*X + 1.6975*Y + 0.0061*Z); + *S = (double)( 0.0030*X + 0.0136*Y + 0.9834*Z); +} + + +/** @brief CAT02 LMS to XYZ */ +void Cat02lms2Xyz(double *X, double *Y, double *Z, double L, double M, double S) +{ + *X = (double)( 1.096123820835514*L - 0.278869000218287*M + 0.182745179382773*S); + *Y = (double)( 0.454369041975359*L + 0.473533154307412*M + 0.072097803717229*S); + *Z = (double)(-0.009627608738429*L - 0.005698031216113*M + 1.015325639954543*S); +} + + +/* + * == Glue functions for multi-stage transforms == + */ + +void Rgb2Lab(double *L, double *a, double *b, double R, double G, double B) +{ + double X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Lab(L, a, b, X, Y, Z); +} + + +void Lab2Rgb(double *R, double *G, double *B, double L, double a, double b) +{ + double X, Y, Z; + Lab2Xyz(&X, &Y, &Z, L, a, b); + Xyz2Rgb(R, G, B, X, Y, Z); +} + + +void Rgb2Luv(double *L, double *u, double *v, double R, double G, double B) +{ + double X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Luv(L, u, v, X, Y, Z); +} + + +void Luv2Rgb(double *R, double *G, double *B, double L, double u, double v) +{ + double X, Y, Z; + Luv2Xyz(&X, &Y, &Z, L, u, v); + Xyz2Rgb(R, G, B, X, Y, Z); +} + +void Rgb2Lch(double *L, double *C, double *H, double R, double G, double B) +{ + double X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Lch(L, C, H, X, Y, Z); +} + + +void Lch2Rgb(double *R, double *G, double *B, double L, double C, double H) +{ + double X, Y, Z; + Lch2Xyz(&X, &Y, &Z, L, C, H); + Xyz2Rgb(R, G, B, X, Y, Z); +} + + +void Rgb2Cat02lms(double *L, double *M, double *S, double R, double G, double B) +{ + double X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Cat02lms(L, M, S, X, Y, Z); +} + + +void Cat02lms2Rgb(double *R, double *G, double *B, double L, double M, double S) +{ + double X, Y, Z; + Cat02lms2Xyz(&X, &Y, &Z, L, M, S); + Xyz2Rgb(R, G, B, X, Y, Z); +} + +} /* namespace */ diff --git a/libs/gtkmm2ext/gtkmm2ext/colors.h b/libs/gtkmm2ext/gtkmm2ext/colors.h new file mode 100644 index 0000000000..3ec6ae3a81 --- /dev/null +++ b/libs/gtkmm2ext/gtkmm2ext/colors.h @@ -0,0 +1,138 @@ +/* + Copyright (C) 2011-2013 Paul Davis + Author: Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _GTKMM2EXT_COLORS_H_ +#define _GTKMM2EXT_COLORS_H_ + +#include + +#include "gtkmm2ext/visibility.h" + +namespace Gtkmm2ext +{ + +typedef uint32_t Color; + +/* conventient way to use Gtkmm2ext::Color with libcairo */ +extern LIBGTKMM2EXT_API void set_source_rgba (Cairo::RefPtr, Gtkmm2ext::Color); +extern LIBGTKMM2EXT_API void set_source_rgb_a (Cairo::RefPtr, Gtkmm2ext::Color, float alpha); //override the color's alpha + +extern LIBGTKMM2EXT_API void set_source_rgba (cairo_t*, Gtkmm2ext::Color); +extern LIBGTKMM2EXT_API void set_source_rgb_a (cairo_t*, Gtkmm2ext::Color, float alpha); //override the color's alpha + + +struct LIBGTKMM2EXT_API HSV; +struct LIBGTKMM2EXT_API HSVA; + +extern LIBGTKMM2EXT_API Color change_alpha (Color, double alpha); + +extern LIBGTKMM2EXT_API Color hsva_to_color (double h, double s, double v, double a = 1.0); +extern LIBGTKMM2EXT_API void color_to_hsva (Color color, double& h, double& s, double& v, double& a); +extern LIBGTKMM2EXT_API Color color_at_alpha (Color, double a); +extern LIBGTKMM2EXT_API void color_to_hsv (Color color, double& h, double& s, double& v); +extern LIBGTKMM2EXT_API void color_to_rgba (Color, double& r, double& g, double& b, double& a); +extern LIBGTKMM2EXT_API Color rgba_to_color (double r, double g, double b, double a); + +uint32_t LIBGTKMM2EXT_API contrasting_text_color (uint32_t c); + +struct LIBGTKMM2EXT_API HSV; + +class LIBGTKMM2EXT_API SVAModifier +{ +public: + enum Type { + Add, + Multiply, + Assign + }; + + SVAModifier (std::string const &); + SVAModifier (Type t, double ss, double vv, double aa) : type (t), _s (ss) , _v (vv) , _a (aa) {} + SVAModifier () : type (Add), _s (0), _v (0), _a (0) {} /* no-op modifier */ + + double s() const { return _s; } + double v() const { return _v; } + double a() const { return _a; } + + HSV operator () (HSV& hsv) const; + std::string to_string () const; + void from_string (std::string const &); + +private: + Type type; + double _s; + double _v; + double _a; +}; + +struct LIBGTKMM2EXT_API HSV +{ + HSV (); + HSV (double h, double s, double v, double a = 1.0); + HSV (Color); + + double h; + double s; + double v; + double a; + + std::string to_string() const; + bool is_gray() const; + + Color color() const { return hsva_to_color (h,s, v, a); } + operator Color() const { return color(); } + + HSV mod (SVAModifier const & svam); + + HSV operator+ (const HSV&) const; + HSV operator- (const HSV&) const; + + HSV& operator=(Color); + HSV& operator=(const std::string&); + + bool operator== (const HSV& other); + + double distance (const HSV& other) const; + HSV delta (const HSV& other) const; + + HSV darker (double factor = 1.3) const { return shade (factor); } + HSV lighter (double factor = 0.7) const { return shade (factor); } + + HSV shade (double factor) const; + HSV mix (const HSV& other, double amt) const; + + HSV opposite() const; + HSV complement() const { return opposite(); } + + HSV bw_text () const; + HSV text() const; + HSV selected () const; + HSV outline() const; + + void print (std::ostream&) const; + +protected: + void clamp (); +}; + +} /* namespace */ + +std::ostream& operator<<(std::ostream& o, const Gtkmm2ext::HSV& hsv); + +#endif diff --git a/libs/gtkmm2ext/gtkmm2ext/colorspace.h b/libs/gtkmm2ext/gtkmm2ext/colorspace.h new file mode 100644 index 0000000000..ed350133e8 --- /dev/null +++ b/libs/gtkmm2ext/gtkmm2ext/colorspace.h @@ -0,0 +1,59 @@ +/** + * @file colorspace.h + * @author Pascal Getreuer 2005-2010 + */ + +#ifndef _GTKMM2EXT_COLORSPACE_H_ +#define _GTKMM2EXT_COLORSPACE_H_ + +/** @brief XYZ color of the D65 white point */ +#define WHITEPOINT_X 0.950456 +#define WHITEPOINT_Y 1.0 +#define WHITEPOINT_Z 1.088754 + +namespace Gtkmm2ext +{ + +void Rgb2Yuv(double *Y, double *U, double *V, double R, double G, double B); +void Yuv2Rgb(double *R, double *G, double *B, double Y, double U, double V); +void Rgb2Ycbcr(double *Y, double *Cb, double *Cr, double R, double G, double B); +void Ycbcr2Rgb(double *R, double *G, double *B, double Y, double Cb, double Cr); +void Rgb2Jpegycbcr(double *R, double *G, double *B, double Y, double Cb, double Cr); +void Jpegycbcr2Rgb(double *R, double *G, double *B, double Y, double Cb, double Cr); +void Rgb2Ypbpr(double *Y, double *Pb, double *Pr, double R, double G, double B); +void Ypbpr2Rgb(double *R, double *G, double *B, double Y, double Pb, double Pr); +void Rgb2Ydbdr(double *Y, double *Db, double *Dr, double R, double G, double B); +void Ydbdr2Rgb(double *R, double *G, double *B, double Y, double Db, double Dr); +void Rgb2Yiq(double *Y, double *I, double *Q, double R, double G, double B); +void Yiq2Rgb(double *R, double *G, double *B, double Y, double I, double Q); + +void Rgb2Hsv(double *H, double *S, double *V, double R, double G, double B); +void Hsv2Rgb(double *R, double *G, double *B, double H, double S, double V); +void Rgb2Hsl(double *H, double *S, double *L, double R, double G, double B); +void Hsl2Rgb(double *R, double *G, double *B, double H, double S, double L); +void Rgb2Hsi(double *H, double *S, double *I, double R, double G, double B); +void Hsi2Rgb(double *R, double *G, double *B, double H, double S, double I); + +void Rgb2Xyz(double *X, double *Y, double *Z, double R, double G, double B); +void Xyz2Rgb(double *R, double *G, double *B, double X, double Y, double Z); +void Xyz2Lab(double *L, double *a, double *b, double X, double Y, double Z); +void Lab2Xyz(double *X, double *Y, double *Z, double L, double a, double b); +void Xyz2Luv(double *L, double *u, double *v, double X, double Y, double Z); +void Luv2Xyz(double *X, double *Y, double *Z, double L, double u, double v); +void Xyz2Lch(double *L, double *C, double *H, double X, double Y, double Z); +void Lch2Xyz(double *X, double *Y, double *Z, double L, double C, double H); +void Xyz2Cat02lms(double *L, double *M, double *S, double X, double Y, double Z); +void Cat02lms2Xyz(double *X, double *Y, double *Z, double L, double M, double S); + +void Rgb2Lab(double *L, double *a, double *b, double R, double G, double B); +void Lab2Rgb(double *R, double *G, double *B, double L, double a, double b); +void Rgb2Luv(double *L, double *u, double *v, double R, double G, double B); +void Luv2Rgb(double *R, double *G, double *B, double L, double u, double v); +void Rgb2Lch(double *L, double *C, double *H, double R, double G, double B); +void Lch2Rgb(double *R, double *G, double *B, double L, double C, double H); +void Rgb2Cat02lms(double *L, double *M, double *S, double R, double G, double B); +void Cat02lms2Rgb(double *R, double *G, double *B, double L, double M, double S); + +} /* namespace */ + +#endif diff --git a/libs/gtkmm2ext/wscript b/libs/gtkmm2ext/wscript index 700d1dee14..8da17f58b0 100644 --- a/libs/gtkmm2ext/wscript +++ b/libs/gtkmm2ext/wscript @@ -31,6 +31,8 @@ gtkmm2ext_sources = [ 'cell_renderer_color_selector.cc', 'cell_renderer_pixbuf_multi.cc', 'cell_renderer_pixbuf_toggle.cc', + 'colors.cc', + 'colorspace.cc', 'cursors.cc', 'debug.cc', 'dndtreeview.cc', -- cgit v1.2.3