From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- src/color.cpp | 496 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) create mode 100644 src/color.cpp (limited to 'src/color.cpp') diff --git a/src/color.cpp b/src/color.cpp new file mode 100644 index 0000000..038609f --- /dev/null +++ b/src/color.cpp @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Colors. + * + * Author: + * Lauris Kaplinski + * bulia byak + * Jon A. Cruz + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include +#include + +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +#include "color.h" +#include "hsluv.h" +#include "svg/svg-icc-color.h" +#include "svg/svg-color.h" + +#include "svg/css-ostringstream.h" + +#define return_if_fail(x) if (!(x)) { printf("assertion failed: " #x); return; } +#define return_val_if_fail(x, val) if (!(x)) { printf("assertion failed: " #x); return val; } + +using Inkscape::CSSOStringStream; + +static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second ); + +#define PROFILE_EPSILON 0.00000001 + +SPColor::SPColor() : + icc(nullptr) +{ + v.c[0] = 0; + v.c[1] = 0; + v.c[2] = 0; +} + +SPColor::SPColor( SPColor const& other ) : + icc(nullptr) +{ + *this = other; +} + +SPColor::SPColor( float r, float g, float b ) : + icc(nullptr) +{ + set( r, g, b ); +} + +SPColor::SPColor( guint32 value ) : + icc(nullptr) +{ + set( value ); +} + +SPColor::~SPColor() +{ + delete icc; + icc = nullptr; +} + + +SPColor& SPColor::operator= (SPColor const& other) +{ + if (this == &other){ + return *this; + } + + SVGICCColor* tmp_icc = other.icc ? new SVGICCColor(*other.icc) : nullptr; + + v.c[0] = other.v.c[0]; + v.c[1] = other.v.c[1]; + v.c[2] = other.v.c[2]; + if ( icc ) { + delete icc; + } + icc = tmp_icc; + + return *this; +} + + +/** + * Returns true if colors match. + */ +bool SPColor::operator == (SPColor const& other) const +{ + bool match = + (v.c[0] == other.v.c[0]) && + (v.c[1] == other.v.c[1]) && + (v.c[2] == other.v.c[2]); + + match &= profileMatches( icc, other.icc ); + + return match; +} + +/** + * Returns true if no RGB value differs epsilon or more in both colors, + * false otherwise. + */ +bool SPColor::isClose( SPColor const& other, float epsilon ) const +{ + bool match = (fabs((v.c[0]) - (other.v.c[0])) < epsilon) + && (fabs((v.c[1]) - (other.v.c[1])) < epsilon) + && (fabs((v.c[2]) - (other.v.c[2])) < epsilon); + + match &= profileMatches( icc, other.icc ); + + return match; +} + +static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second ) { + bool match = false; + if ( !first && !second ) { + match = true; + } else { + match = first && second + && (first->colorProfile == second->colorProfile) + && (first->colors.size() == second->colors.size()); + if ( match ) { + for ( unsigned i = 0; i < first->colors.size(); i++ ) { + match &= (fabs(first->colors[i] - second->colors[i]) < PROFILE_EPSILON); + } + } + } + return match; +} + +/** + * Sets RGB values and colorspace in color. + * \pre 0 <={r,g,b}<=1 + */ +void SPColor::set( float r, float g, float b ) +{ + return_if_fail(r >= 0.0); + return_if_fail(r <= 1.0); + return_if_fail(g >= 0.0); + return_if_fail(g <= 1.0); + return_if_fail(b >= 0.0); + return_if_fail(b <= 1.0); + + // TODO clear icc if set? + v.c[0] = r; + v.c[1] = g; + v.c[2] = b; +} + +/** + * Converts 32bit value to RGB floats and sets color. + */ +void SPColor::set( guint32 value ) +{ + // TODO clear icc if set? + v.c[0] = (value >> 24) / 255.0F; + v.c[1] = ((value >> 16) & 0xff) / 255.0F; + v.c[2] = ((value >> 8) & 0xff) / 255.0F; +} + +/** + * Convert SPColor with integer alpha value to 32bit RGBA value. + * \pre alpha < 256 + */ +guint32 SPColor::toRGBA32( int alpha ) const +{ + return_val_if_fail (alpha <= 0xff, 0x0); + + guint32 rgba = SP_RGBA32_U_COMPOSE( SP_COLOR_F_TO_U(v.c[0]), + SP_COLOR_F_TO_U(v.c[1]), + SP_COLOR_F_TO_U(v.c[2]), + alpha ); + return rgba; +} + +/** + * Convert SPColor with float alpha value to 32bit RGBA value. + * \pre color != NULL && 0 <= alpha <= 1 + */ +guint32 SPColor::toRGBA32( double alpha ) const +{ + return_val_if_fail(alpha >= 0.0, 0x0); + return_val_if_fail(alpha <= 1.0, 0x0); + + return toRGBA32( static_cast(SP_COLOR_F_TO_U(alpha)) ); +} + +std::string SPColor::toString() const +{ + CSSOStringStream css; + char tmp[64] = {0}; + + sp_svg_write_color(tmp, sizeof(tmp), toRGBA32(0x0ff)); + css << tmp; + + if ( icc ) { + if ( !css.str().empty() ) { + css << " "; + } + css << "icc-color(" << icc->colorProfile; + for (double color : icc->colors) { + css << ", " << color; + } + css << ')'; + } + + return css.str(); +} + + +/** + * Fill rgb float array with values from SPColor. + * \pre color != NULL && rgb != NULL && rgb[0-2] is meaningful + */ +void +SPColor::get_rgb_floatv(float *rgb) const +{ + return_if_fail (rgb != nullptr); + + rgb[0] = v.c[0]; + rgb[1] = v.c[1]; + rgb[2] = v.c[2]; +} + +/** + * Fill cmyk float array with values from SPColor. + * \pre color != NULL && cmyk != NULL && cmyk[0-3] is meaningful + */ +void +SPColor::get_cmyk_floatv(float *cmyk) const +{ + return_if_fail (cmyk != nullptr); + + SPColor::rgb_to_cmyk_floatv( cmyk, + v.c[0], + v.c[1], + v.c[2] ); +} + +/* Plain mode helpers */ + +/** + * Fill hsv float array from r,g,b float values. + */ +void +SPColor::rgb_to_hsv_floatv (float *hsv, float r, float g, float b) +{ + float max, min, delta; + + max = MAX (MAX (r, g), b); + min = MIN (MIN (r, g), b); + delta = max - min; + + hsv[2] = max; + + if (max > 0) { + hsv[1] = delta / max; + } else { + hsv[1] = 0.0; + } + + if (hsv[1] != 0.0) { + if (r == max) { + hsv[0] = (g - b) / delta; + } else if (g == max) { + hsv[0] = 2.0 + (b - r) / delta; + } else { + hsv[0] = 4.0 + (r - g) / delta; + } + + hsv[0] = hsv[0] / 6.0; + + if (hsv[0] < 0) hsv[0] += 1.0; + } + else + hsv[0] = 0.0; +} + +/** + * Fill rgb float array from h,s,v float values. + */ +void +SPColor::hsv_to_rgb_floatv (float *rgb, float h, float s, float v) +{ + double f, w, q, t, d; + + d = h * 5.99999999; + f = d - floor (d); + w = v * (1.0 - s); + q = v * (1.0 - (s * f)); + t = v * (1.0 - (s * (1.0 - f))); + + if (d < 1.0) { + *rgb++ = v; + *rgb++ = t; + *rgb++ = w; + } else if (d < 2.0) { + *rgb++ = q; + *rgb++ = v; + *rgb++ = w; + } else if (d < 3.0) { + *rgb++ = w; + *rgb++ = v; + *rgb++ = t; + } else if (d < 4.0) { + *rgb++ = w; + *rgb++ = q; + *rgb++ = v; + } else if (d < 5.0) { + *rgb++ = t; + *rgb++ = w; + *rgb++ = v; + } else { + *rgb++ = v; + *rgb++ = w; + *rgb++ = q; + } +} + +/** + * Fill hsl float array from r,g,b float values. + */ +void +SPColor::rgb_to_hsl_floatv (float *hsl, float r, float g, float b) +{ + float max = MAX (MAX (r, g), b); + float min = MIN (MIN (r, g), b); + float delta = max - min; + + hsl[2] = (max + min)/2.0; + + if (delta == 0) { + hsl[0] = 0; + hsl[1] = 0; + } else { + if (hsl[2] <= 0.5) + hsl[1] = delta / (max + min); + else + hsl[1] = delta / (2 - max - min); + + if (r == max) hsl[0] = (g - b) / delta; + else if (g == max) hsl[0] = 2.0 + (b - r) / delta; + else if (b == max) hsl[0] = 4.0 + (r - g) / delta; + + hsl[0] = hsl[0] / 6.0; + + if (hsl[0] < 0) hsl[0] += 1; + if (hsl[0] > 1) hsl[0] -= 1; + } +} + +static float +hue_2_rgb (float v1, float v2, float h) +{ + if (h < 0) h += 6.0; + if (h > 6) h -= 6.0; + + if (h < 1) return v1 + (v2 - v1) * h; + if (h < 3) return v2; + if (h < 4) return v1 + (v2 - v1) * (4 - h); + return v1; +} + +/** + * Fill rgb float array from h,s,l float values. + */ +void +SPColor::hsl_to_rgb_floatv (float *rgb, float h, float s, float l) +{ + if (s == 0) { + rgb[0] = l; + rgb[1] = l; + rgb[2] = l; + } else { + float v2; + if (l < 0.5) { + v2 = l * (1 + s); + } else { + v2 = l + s - l*s; + } + float v1 = 2*l - v2; + + rgb[0] = hue_2_rgb (v1, v2, h*6 + 2.0); + rgb[1] = hue_2_rgb (v1, v2, h*6); + rgb[2] = hue_2_rgb (v1, v2, h*6 - 2.0); + } +} + +/** + * Fill cmyk float array from r,g,b float values. + */ +void +SPColor::rgb_to_cmyk_floatv (float *cmyk, float r, float g, float b) +{ + float c, m, y, k, kd; + + c = 1.0 - r; + m = 1.0 - g; + y = 1.0 - b; + k = MIN (MIN (c, m), y); + + c = c - k; + m = m - k; + y = y - k; + + kd = 1.0 - k; + + if (kd > 1e-9) { + c = c / kd; + m = m / kd; + y = y / kd; + } + + cmyk[0] = c; + cmyk[1] = m; + cmyk[2] = y; + cmyk[3] = k; +} + +/** + * Fill rgb float array from c,m,y,k float values. + */ +void +SPColor::cmyk_to_rgb_floatv (float *rgb, float c, float m, float y, float k) +{ + float kd; + + kd = 1.0 - k; + + c = c * kd; + m = m * kd; + y = y * kd; + + c = c + k; + m = m + k; + y = y + k; + + rgb[0] = 1.0 - c; + rgb[1] = 1.0 - m; + rgb[2] = 1.0 - y; +} + +/** + * Fill hsluv float array from r,g,b float values. + */ +void +SPColor::rgb_to_hsluv_floatv (float *hsluv, float r, float g, float b) +{ + double h, s, l; + Hsluv::rgb_to_hsluv(r, g, b, &h, &s, &l); + + h /= 360.0; + s /= 100.0; + l /= 100.0; + + hsluv[0] = std::min(1.0, std::max(0.0, h)); + hsluv[1] = std::min(1.0, std::max(0.0, s)); + hsluv[2] = std::min(1.0, std::max(0.0, l)); +} + +/** + * Fill rgb float array from h,s,l float values. + */ +void +SPColor::hsluv_to_rgb_floatv (float *rgb, float h, float s, float l) +{ + h *= 360.0; + s *= 100.0; + l *= 100.0; + + double r, g, b; + Hsluv::hsluv_to_rgb(h, s, l, &r, &g, &b); + + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : -- cgit v1.2.3