forked from openkylin/gimp
1104 lines
24 KiB
C
1104 lines
24 KiB
C
/* LIBGIMP - The GIMP Library
|
|
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
|
*
|
|
* This library is free software: you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <babl/babl.h>
|
|
#include <glib-object.h>
|
|
|
|
#include "libgimpmath/gimpmath.h"
|
|
|
|
#include "gimpcolortypes.h"
|
|
|
|
#include "gimpcolorspace.h"
|
|
#include "gimprgb.h"
|
|
#include "gimphsv.h"
|
|
|
|
|
|
|
|
/**
|
|
* SECTION: gimpcolorspace
|
|
* @title: GimpColorSpace
|
|
* @short_description: Utility functions which convert colors between
|
|
* different color models.
|
|
*
|
|
* When programming pixel data manipulation functions you will often
|
|
* use algorithms operating on a color model different from the one
|
|
* GIMP uses. This file provides utility functions to convert colors
|
|
* between different color spaces.
|
|
**/
|
|
|
|
|
|
#define GIMP_HSV_UNDEFINED -1.0
|
|
#define GIMP_HSL_UNDEFINED -1.0
|
|
|
|
/*********************************
|
|
* color conversion routines *
|
|
*********************************/
|
|
|
|
|
|
/* GimpRGB functions */
|
|
|
|
|
|
/**
|
|
* gimp_rgb_to_hsv:
|
|
* @rgb: A color value in the RGB colorspace
|
|
* @hsv: The value converted to the HSV colorspace
|
|
*
|
|
* Does a conversion from RGB to HSV (Hue, Saturation,
|
|
* Value) colorspace.
|
|
**/
|
|
void
|
|
gimp_rgb_to_hsv (const GimpRGB *rgb,
|
|
GimpHSV *hsv)
|
|
{
|
|
gdouble max, min, delta;
|
|
|
|
g_return_if_fail (rgb != NULL);
|
|
g_return_if_fail (hsv != NULL);
|
|
|
|
max = gimp_rgb_max (rgb);
|
|
min = gimp_rgb_min (rgb);
|
|
|
|
hsv->v = max;
|
|
delta = max - min;
|
|
|
|
if (delta > 0.0001)
|
|
{
|
|
hsv->s = delta / max;
|
|
|
|
if (rgb->r == max)
|
|
{
|
|
hsv->h = (rgb->g - rgb->b) / delta;
|
|
if (hsv->h < 0.0)
|
|
hsv->h += 6.0;
|
|
}
|
|
else if (rgb->g == max)
|
|
{
|
|
hsv->h = 2.0 + (rgb->b - rgb->r) / delta;
|
|
}
|
|
else
|
|
{
|
|
hsv->h = 4.0 + (rgb->r - rgb->g) / delta;
|
|
}
|
|
|
|
hsv->h /= 6.0;
|
|
}
|
|
else
|
|
{
|
|
hsv->s = 0.0;
|
|
hsv->h = 0.0;
|
|
}
|
|
|
|
hsv->a = rgb->a;
|
|
}
|
|
|
|
/**
|
|
* gimp_hsv_to_rgb:
|
|
* @hsv: A color value in the HSV colorspace
|
|
* @rgb: The returned RGB value.
|
|
*
|
|
* Converts a color value from HSV to RGB colorspace
|
|
**/
|
|
void
|
|
gimp_hsv_to_rgb (const GimpHSV *hsv,
|
|
GimpRGB *rgb)
|
|
{
|
|
gint i;
|
|
gdouble f, w, q, t;
|
|
|
|
gdouble hue;
|
|
|
|
g_return_if_fail (rgb != NULL);
|
|
g_return_if_fail (hsv != NULL);
|
|
|
|
if (hsv->s == 0.0)
|
|
{
|
|
rgb->r = hsv->v;
|
|
rgb->g = hsv->v;
|
|
rgb->b = hsv->v;
|
|
}
|
|
else
|
|
{
|
|
hue = hsv->h;
|
|
|
|
if (hue == 1.0)
|
|
hue = 0.0;
|
|
|
|
hue *= 6.0;
|
|
|
|
i = (gint) hue;
|
|
f = hue - i;
|
|
w = hsv->v * (1.0 - hsv->s);
|
|
q = hsv->v * (1.0 - (hsv->s * f));
|
|
t = hsv->v * (1.0 - (hsv->s * (1.0 - f)));
|
|
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
rgb->r = hsv->v;
|
|
rgb->g = t;
|
|
rgb->b = w;
|
|
break;
|
|
case 1:
|
|
rgb->r = q;
|
|
rgb->g = hsv->v;
|
|
rgb->b = w;
|
|
break;
|
|
case 2:
|
|
rgb->r = w;
|
|
rgb->g = hsv->v;
|
|
rgb->b = t;
|
|
break;
|
|
case 3:
|
|
rgb->r = w;
|
|
rgb->g = q;
|
|
rgb->b = hsv->v;
|
|
break;
|
|
case 4:
|
|
rgb->r = t;
|
|
rgb->g = w;
|
|
rgb->b = hsv->v;
|
|
break;
|
|
case 5:
|
|
rgb->r = hsv->v;
|
|
rgb->g = w;
|
|
rgb->b = q;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rgb->a = hsv->a;
|
|
}
|
|
|
|
|
|
/**
|
|
* gimp_rgb_to_hsl:
|
|
* @rgb: A color value in the RGB colorspace
|
|
* @hsl: The value converted to HSL
|
|
*
|
|
* Convert an RGB color value to a HSL (Hue, Saturation, Lightness)
|
|
* color value.
|
|
**/
|
|
void
|
|
gimp_rgb_to_hsl (const GimpRGB *rgb,
|
|
GimpHSL *hsl)
|
|
{
|
|
gdouble max, min, delta;
|
|
|
|
g_return_if_fail (rgb != NULL);
|
|
g_return_if_fail (hsl != NULL);
|
|
|
|
max = gimp_rgb_max (rgb);
|
|
min = gimp_rgb_min (rgb);
|
|
|
|
hsl->l = (max + min) / 2.0;
|
|
|
|
if (max == min)
|
|
{
|
|
hsl->s = 0.0;
|
|
hsl->h = GIMP_HSL_UNDEFINED;
|
|
}
|
|
else
|
|
{
|
|
if (hsl->l <= 0.5)
|
|
hsl->s = (max - min) / (max + min);
|
|
else
|
|
hsl->s = (max - min) / (2.0 - max - min);
|
|
|
|
delta = max - min;
|
|
|
|
if (delta == 0.0)
|
|
delta = 1.0;
|
|
|
|
if (rgb->r == max)
|
|
{
|
|
hsl->h = (rgb->g - rgb->b) / delta;
|
|
}
|
|
else if (rgb->g == max)
|
|
{
|
|
hsl->h = 2.0 + (rgb->b - rgb->r) / delta;
|
|
}
|
|
else
|
|
{
|
|
hsl->h = 4.0 + (rgb->r - rgb->g) / delta;
|
|
}
|
|
|
|
hsl->h /= 6.0;
|
|
|
|
if (hsl->h < 0.0)
|
|
hsl->h += 1.0;
|
|
}
|
|
|
|
hsl->a = rgb->a;
|
|
}
|
|
|
|
static inline gdouble
|
|
gimp_hsl_value (gdouble n1,
|
|
gdouble n2,
|
|
gdouble hue)
|
|
{
|
|
gdouble val;
|
|
|
|
if (hue > 6.0)
|
|
hue -= 6.0;
|
|
else if (hue < 0.0)
|
|
hue += 6.0;
|
|
|
|
if (hue < 1.0)
|
|
val = n1 + (n2 - n1) * hue;
|
|
else if (hue < 3.0)
|
|
val = n2;
|
|
else if (hue < 4.0)
|
|
val = n1 + (n2 - n1) * (4.0 - hue);
|
|
else
|
|
val = n1;
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
/**
|
|
* gimp_hsl_to_rgb:
|
|
* @hsl: A color value in the HSL colorspace
|
|
* @rgb: The value converted to a value in the RGB colorspace
|
|
*
|
|
* Convert a HSL color value to an RGB color value.
|
|
**/
|
|
void
|
|
gimp_hsl_to_rgb (const GimpHSL *hsl,
|
|
GimpRGB *rgb)
|
|
{
|
|
g_return_if_fail (hsl != NULL);
|
|
g_return_if_fail (rgb != NULL);
|
|
|
|
if (hsl->s == 0)
|
|
{
|
|
/* achromatic case */
|
|
rgb->r = hsl->l;
|
|
rgb->g = hsl->l;
|
|
rgb->b = hsl->l;
|
|
}
|
|
else
|
|
{
|
|
gdouble m1, m2;
|
|
|
|
if (hsl->l <= 0.5)
|
|
m2 = hsl->l * (1.0 + hsl->s);
|
|
else
|
|
m2 = hsl->l + hsl->s - hsl->l * hsl->s;
|
|
|
|
m1 = 2.0 * hsl->l - m2;
|
|
|
|
rgb->r = gimp_hsl_value (m1, m2, hsl->h * 6.0 + 2.0);
|
|
rgb->g = gimp_hsl_value (m1, m2, hsl->h * 6.0);
|
|
rgb->b = gimp_hsl_value (m1, m2, hsl->h * 6.0 - 2.0);
|
|
}
|
|
|
|
rgb->a = hsl->a;
|
|
}
|
|
|
|
|
|
/**
|
|
* gimp_rgb_to_cmyk:
|
|
* @rgb: A value in the RGB colorspace
|
|
* @pullout: A scaling value (0-1) indicating how much black should be
|
|
* pulled out
|
|
* @cmyk: The input value naively converted to the CMYK colorspace
|
|
*
|
|
* Does a naive conversion from RGB to CMYK colorspace. A simple
|
|
* formula that doesn't take any color-profiles into account is used.
|
|
* The amount of black pullout how can be controlled via the @pullout
|
|
* parameter. A @pullout value of 0 makes this a conversion to CMY.
|
|
* A value of 1 causes the maximum amount of black to be pulled out.
|
|
**/
|
|
void
|
|
gimp_rgb_to_cmyk (const GimpRGB *rgb,
|
|
gdouble pullout,
|
|
GimpCMYK *cmyk)
|
|
{
|
|
gdouble c, m, y, k;
|
|
|
|
g_return_if_fail (rgb != NULL);
|
|
g_return_if_fail (cmyk != NULL);
|
|
|
|
c = 1.0 - rgb->r;
|
|
m = 1.0 - rgb->g;
|
|
y = 1.0 - rgb->b;
|
|
|
|
k = 1.0;
|
|
if (c < k) k = c;
|
|
if (m < k) k = m;
|
|
if (y < k) k = y;
|
|
|
|
k *= pullout;
|
|
|
|
if (k < 1.0)
|
|
{
|
|
cmyk->c = (c - k) / (1.0 - k);
|
|
cmyk->m = (m - k) / (1.0 - k);
|
|
cmyk->y = (y - k) / (1.0 - k);
|
|
}
|
|
else
|
|
{
|
|
cmyk->c = 0.0;
|
|
cmyk->m = 0.0;
|
|
cmyk->y = 0.0;
|
|
}
|
|
|
|
cmyk->k = k;
|
|
cmyk->a = rgb->a;
|
|
}
|
|
|
|
/**
|
|
* gimp_cmyk_to_rgb:
|
|
* @cmyk: A color value in the CMYK colorspace
|
|
* @rgb: The value converted to the RGB colorspace
|
|
*
|
|
* Does a simple transformation from the CMYK colorspace to the RGB
|
|
* colorspace, without taking color profiles into account.
|
|
**/
|
|
void
|
|
gimp_cmyk_to_rgb (const GimpCMYK *cmyk,
|
|
GimpRGB *rgb)
|
|
{
|
|
gdouble c, m, y, k;
|
|
|
|
g_return_if_fail (cmyk != NULL);
|
|
g_return_if_fail (rgb != NULL);
|
|
|
|
k = cmyk->k;
|
|
|
|
if (k < 1.0)
|
|
{
|
|
c = cmyk->c * (1.0 - k) + k;
|
|
m = cmyk->m * (1.0 - k) + k;
|
|
y = cmyk->y * (1.0 - k) + k;
|
|
}
|
|
else
|
|
{
|
|
c = 1.0;
|
|
m = 1.0;
|
|
y = 1.0;
|
|
}
|
|
|
|
rgb->r = 1.0 - c;
|
|
rgb->g = 1.0 - m;
|
|
rgb->b = 1.0 - y;
|
|
rgb->a = cmyk->a;
|
|
}
|
|
|
|
|
|
#define GIMP_RETURN_RGB(x, y, z) { rgb->r = x; rgb->g = y; rgb->b = z; return; }
|
|
|
|
/****************************************************************************
|
|
* Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms.
|
|
* Pure red always maps to 6 in this implementation. Therefore UNDEFINED can
|
|
* be defined as 0 in situations where only unsigned numbers are desired.
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* gimp_rgb_to_hwb:
|
|
* @rgb: A color value in the RGB colorspace
|
|
* @hue: The hue value of the above color, in the range 0 to 6
|
|
* @whiteness: The whiteness value of the above color, in the range 0 to 1
|
|
* @blackness: The blackness value of the above color, in the range 0 to 1
|
|
*
|
|
* Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms.
|
|
* Pure red always maps to 6 in this implementation. Therefore UNDEFINED can
|
|
* be defined as 0 in situations where only unsigned numbers are desired.
|
|
*
|
|
* RGB are each on [0, 1]. Whiteness and Blackness are returned in the
|
|
* range [0, 1] and H is returned in the range [0, 6]. If W == 1 - B, H is
|
|
* undefined.
|
|
**/
|
|
void
|
|
gimp_rgb_to_hwb (const GimpRGB *rgb,
|
|
gdouble *hue,
|
|
gdouble *whiteness,
|
|
gdouble *blackness)
|
|
{
|
|
/* RGB are each on [0, 1]. W and B are returned on [0, 1] and H is */
|
|
/* returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B. */
|
|
/* ====================================================================== */
|
|
|
|
gdouble R = rgb->r, G = rgb->g, B = rgb->b, w, v, b, f;
|
|
gint i;
|
|
|
|
w = gimp_rgb_min (rgb);
|
|
v = gimp_rgb_max (rgb);
|
|
b = 1.0 - v;
|
|
|
|
if (v == w)
|
|
{
|
|
*hue = GIMP_HSV_UNDEFINED;
|
|
*whiteness = w;
|
|
*blackness = b;
|
|
}
|
|
else
|
|
{
|
|
f = (R == w) ? G - B : ((G == w) ? B - R : R - G);
|
|
i = (R == w) ? 3.0 : ((G == w) ? 5.0 : 1.0);
|
|
|
|
*hue = (360.0 / 6.0) * (i - f / (v - w));
|
|
*whiteness = w;
|
|
*blackness = b;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_hwb_to_rgb:
|
|
* @hue: A hue value, in the range 0 to 6
|
|
* @whiteness: A whiteness value, in the range 0 to 1
|
|
* @blackness: A blackness value, in the range 0 to 1
|
|
* @rgb: The above color converted to the RGB colorspace
|
|
*
|
|
* H is defined in the range [0, 6] or UNDEFINED, B and W are both in the
|
|
* range [0, 1]. The returned RGB values are all in the range [0, 1].
|
|
**/
|
|
void
|
|
gimp_hwb_to_rgb (gdouble hue,
|
|
gdouble whiteness,
|
|
gdouble blackness,
|
|
GimpRGB *rgb)
|
|
{
|
|
/* H is given on [0, 6] or UNDEFINED. whiteness and
|
|
* blackness are given on [0, 1].
|
|
* RGB are each returned on [0, 1].
|
|
*/
|
|
|
|
gdouble h = hue, w = whiteness, b = blackness, v, n, f;
|
|
gint i;
|
|
|
|
h = 6.0 * h/ 360.0;
|
|
|
|
v = 1.0 - b;
|
|
if (h == GIMP_HSV_UNDEFINED)
|
|
{
|
|
rgb->r = v;
|
|
rgb->g = v;
|
|
rgb->b = v;
|
|
}
|
|
else
|
|
{
|
|
i = floor (h);
|
|
f = h - i;
|
|
|
|
if (i & 1)
|
|
f = 1.0 - f; /* if i is odd */
|
|
|
|
n = w + f * (v - w); /* linear interpolation between w and v */
|
|
|
|
switch (i)
|
|
{
|
|
case 6:
|
|
case 0: GIMP_RETURN_RGB (v, n, w);
|
|
break;
|
|
case 1: GIMP_RETURN_RGB (n, v, w);
|
|
break;
|
|
case 2: GIMP_RETURN_RGB (w, v, n);
|
|
break;
|
|
case 3: GIMP_RETURN_RGB (w, n, v);
|
|
break;
|
|
case 4: GIMP_RETURN_RGB (n, w, v);
|
|
break;
|
|
case 5: GIMP_RETURN_RGB (v, w, n);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/* gint functions */
|
|
|
|
/**
|
|
* gimp_rgb_to_hsv_int:
|
|
* @red: The red channel value, returns the Hue channel
|
|
* @green: The green channel value, returns the Saturation channel
|
|
* @blue: The blue channel value, returns the Value channel
|
|
*
|
|
* The arguments are pointers to int representing channel values in
|
|
* the RGB colorspace, and the values pointed to are all in the range
|
|
* [0, 255].
|
|
*
|
|
* The function changes the arguments to point to the HSV value
|
|
* corresponding, with the returned values in the following
|
|
* ranges: H [0, 359], S [0, 255], V [0, 255].
|
|
**/
|
|
void
|
|
gimp_rgb_to_hsv_int (gint *red,
|
|
gint *green,
|
|
gint *blue)
|
|
{
|
|
gdouble r, g, b;
|
|
gdouble h, s, v;
|
|
gint min;
|
|
gdouble delta;
|
|
|
|
r = *red;
|
|
g = *green;
|
|
b = *blue;
|
|
|
|
if (r > g)
|
|
{
|
|
v = MAX (r, b);
|
|
min = MIN (g, b);
|
|
}
|
|
else
|
|
{
|
|
v = MAX (g, b);
|
|
min = MIN (r, b);
|
|
}
|
|
|
|
delta = v - min;
|
|
|
|
if (v == 0.0)
|
|
s = 0.0;
|
|
else
|
|
s = delta / v;
|
|
|
|
if (s == 0.0)
|
|
{
|
|
h = 0.0;
|
|
}
|
|
else
|
|
{
|
|
if (r == v)
|
|
h = 60.0 * (g - b) / delta;
|
|
else if (g == v)
|
|
h = 120 + 60.0 * (b - r) / delta;
|
|
else
|
|
h = 240 + 60.0 * (r - g) / delta;
|
|
|
|
if (h < 0.0)
|
|
h += 360.0;
|
|
|
|
if (h > 360.0)
|
|
h -= 360.0;
|
|
}
|
|
|
|
*red = ROUND (h);
|
|
*green = ROUND (s * 255.0);
|
|
*blue = ROUND (v);
|
|
|
|
/* avoid the ambiguity of returning different values for the same color */
|
|
if (*red == 360)
|
|
*red = 0;
|
|
}
|
|
|
|
/**
|
|
* gimp_hsv_to_rgb_int:
|
|
* @hue: The hue channel, returns the red channel
|
|
* @saturation: The saturation channel, returns the green channel
|
|
* @value: The value channel, returns the blue channel
|
|
*
|
|
* The arguments are pointers to int, with the values pointed to in the
|
|
* following ranges: H [0, 360], S [0, 255], V [0, 255].
|
|
*
|
|
* The function changes the arguments to point to the RGB value
|
|
* corresponding, with the returned values all in the range [0, 255].
|
|
**/
|
|
void
|
|
gimp_hsv_to_rgb_int (gint *hue,
|
|
gint *saturation,
|
|
gint *value)
|
|
{
|
|
gdouble h, s, v, h_temp;
|
|
gdouble f, p, q, t;
|
|
gint i;
|
|
|
|
if (*saturation == 0)
|
|
{
|
|
*hue = *value;
|
|
*saturation = *value;
|
|
*value = *value;
|
|
}
|
|
else
|
|
{
|
|
h = *hue;
|
|
s = *saturation / 255.0;
|
|
v = *value / 255.0;
|
|
|
|
if (h == 360)
|
|
h_temp = 0;
|
|
else
|
|
h_temp = h;
|
|
|
|
h_temp = h_temp / 60.0;
|
|
i = floor (h_temp);
|
|
f = h_temp - i;
|
|
p = v * (1.0 - s);
|
|
q = v * (1.0 - (s * f));
|
|
t = v * (1.0 - (s * (1.0 - f)));
|
|
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
*hue = ROUND (v * 255.0);
|
|
*saturation = ROUND (t * 255.0);
|
|
*value = ROUND (p * 255.0);
|
|
break;
|
|
|
|
case 1:
|
|
*hue = ROUND (q * 255.0);
|
|
*saturation = ROUND (v * 255.0);
|
|
*value = ROUND (p * 255.0);
|
|
break;
|
|
|
|
case 2:
|
|
*hue = ROUND (p * 255.0);
|
|
*saturation = ROUND (v * 255.0);
|
|
*value = ROUND (t * 255.0);
|
|
break;
|
|
|
|
case 3:
|
|
*hue = ROUND (p * 255.0);
|
|
*saturation = ROUND (q * 255.0);
|
|
*value = ROUND (v * 255.0);
|
|
break;
|
|
|
|
case 4:
|
|
*hue = ROUND (t * 255.0);
|
|
*saturation = ROUND (p * 255.0);
|
|
*value = ROUND (v * 255.0);
|
|
break;
|
|
|
|
case 5:
|
|
*hue = ROUND (v * 255.0);
|
|
*saturation = ROUND (p * 255.0);
|
|
*value = ROUND (q * 255.0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_rgb_to_hsl_int:
|
|
* @red: Red channel, returns Hue channel
|
|
* @green: Green channel, returns Lightness channel
|
|
* @blue: Blue channel, returns Saturation channel
|
|
*
|
|
* The arguments are pointers to int representing channel values in the
|
|
* RGB colorspace, and the values pointed to are all in the range [0, 255].
|
|
*
|
|
* The function changes the arguments to point to the corresponding HLS
|
|
* value with the values pointed to in the following ranges: H [0, 360],
|
|
* L [0, 255], S [0, 255].
|
|
**/
|
|
void
|
|
gimp_rgb_to_hsl_int (gint *red,
|
|
gint *green,
|
|
gint *blue)
|
|
{
|
|
gint r, g, b;
|
|
gdouble h, s, l;
|
|
gint min, max;
|
|
gint delta;
|
|
|
|
r = *red;
|
|
g = *green;
|
|
b = *blue;
|
|
|
|
if (r > g)
|
|
{
|
|
max = MAX (r, b);
|
|
min = MIN (g, b);
|
|
}
|
|
else
|
|
{
|
|
max = MAX (g, b);
|
|
min = MIN (r, b);
|
|
}
|
|
|
|
l = (max + min) / 2.0;
|
|
|
|
if (max == min)
|
|
{
|
|
s = 0.0;
|
|
h = 0.0;
|
|
}
|
|
else
|
|
{
|
|
delta = (max - min);
|
|
|
|
if (l < 128)
|
|
s = 255 * (gdouble) delta / (gdouble) (max + min);
|
|
else
|
|
s = 255 * (gdouble) delta / (gdouble) (511 - max - min);
|
|
|
|
if (r == max)
|
|
h = (g - b) / (gdouble) delta;
|
|
else if (g == max)
|
|
h = 2 + (b - r) / (gdouble) delta;
|
|
else
|
|
h = 4 + (r - g) / (gdouble) delta;
|
|
|
|
h = h * 42.5;
|
|
|
|
if (h < 0)
|
|
h += 255;
|
|
else if (h > 255)
|
|
h -= 255;
|
|
}
|
|
|
|
*red = ROUND (h);
|
|
*green = ROUND (s);
|
|
*blue = ROUND (l);
|
|
}
|
|
|
|
/**
|
|
* gimp_rgb_to_l_int:
|
|
* @red: Red channel
|
|
* @green: Green channel
|
|
* @blue: Blue channel
|
|
*
|
|
* Calculates the lightness value of an RGB triplet with the formula
|
|
* L = (max(R, G, B) + min (R, G, B)) / 2
|
|
*
|
|
* Return value: Luminance value corresponding to the input RGB value
|
|
**/
|
|
gint
|
|
gimp_rgb_to_l_int (gint red,
|
|
gint green,
|
|
gint blue)
|
|
{
|
|
gint min, max;
|
|
|
|
if (red > green)
|
|
{
|
|
max = MAX (red, blue);
|
|
min = MIN (green, blue);
|
|
}
|
|
else
|
|
{
|
|
max = MAX (green, blue);
|
|
min = MIN (red, blue);
|
|
}
|
|
|
|
return ROUND ((max + min) / 2.0);
|
|
}
|
|
|
|
static inline gint
|
|
gimp_hsl_value_int (gdouble n1,
|
|
gdouble n2,
|
|
gdouble hue)
|
|
{
|
|
gdouble value;
|
|
|
|
if (hue > 255)
|
|
hue -= 255;
|
|
else if (hue < 0)
|
|
hue += 255;
|
|
|
|
if (hue < 42.5)
|
|
value = n1 + (n2 - n1) * (hue / 42.5);
|
|
else if (hue < 127.5)
|
|
value = n2;
|
|
else if (hue < 170)
|
|
value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
|
|
else
|
|
value = n1;
|
|
|
|
return ROUND (value * 255.0);
|
|
}
|
|
|
|
/**
|
|
* gimp_hsl_to_rgb_int:
|
|
* @hue: Hue channel, returns Red channel
|
|
* @saturation: Saturation channel, returns Green channel
|
|
* @lightness: Lightness channel, returns Blue channel
|
|
*
|
|
* The arguments are pointers to int, with the values pointed to in the
|
|
* following ranges: H [0, 360], L [0, 255], S [0, 255].
|
|
*
|
|
* The function changes the arguments to point to the RGB value
|
|
* corresponding, with the returned values all in the range [0, 255].
|
|
**/
|
|
void
|
|
gimp_hsl_to_rgb_int (gint *hue,
|
|
gint *saturation,
|
|
gint *lightness)
|
|
{
|
|
gdouble h, s, l;
|
|
|
|
h = *hue;
|
|
s = *saturation;
|
|
l = *lightness;
|
|
|
|
if (s == 0)
|
|
{
|
|
/* achromatic case */
|
|
*hue = l;
|
|
*lightness = l;
|
|
*saturation = l;
|
|
}
|
|
else
|
|
{
|
|
gdouble m1, m2;
|
|
|
|
if (l < 128)
|
|
m2 = (l * (255 + s)) / 65025.0;
|
|
else
|
|
m2 = (l + s - (l * s) / 255.0) / 255.0;
|
|
|
|
m1 = (l / 127.5) - m2;
|
|
|
|
/* chromatic case */
|
|
*hue = gimp_hsl_value_int (m1, m2, h + 85);
|
|
*saturation = gimp_hsl_value_int (m1, m2, h);
|
|
*lightness = gimp_hsl_value_int (m1, m2, h - 85);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_rgb_to_cmyk_int:
|
|
* @red: the red channel; returns the cyan value (0-255)
|
|
* @green: the green channel; returns the magenta value (0-255)
|
|
* @blue: the blue channel; returns the yellow value (0-255)
|
|
* @pullout: the percentage of black to pull out (0-100); returns
|
|
* the black value (0-255)
|
|
*
|
|
* Does a naive conversion from RGB to CMYK colorspace. A simple
|
|
* formula that doesn't take any color-profiles into account is used.
|
|
* The amount of black pullout how can be controlled via the @pullout
|
|
* parameter. A @pullout value of 0 makes this a conversion to CMY.
|
|
* A value of 100 causes the maximum amount of black to be pulled out.
|
|
**/
|
|
void
|
|
gimp_rgb_to_cmyk_int (gint *red,
|
|
gint *green,
|
|
gint *blue,
|
|
gint *pullout)
|
|
{
|
|
gint c, m, y;
|
|
|
|
c = 255 - *red;
|
|
m = 255 - *green;
|
|
y = 255 - *blue;
|
|
|
|
if (*pullout == 0)
|
|
{
|
|
*red = c;
|
|
*green = m;
|
|
*blue = y;
|
|
}
|
|
else
|
|
{
|
|
gint k = 255;
|
|
|
|
if (c < k) k = c;
|
|
if (m < k) k = m;
|
|
if (y < k) k = y;
|
|
|
|
k = (k * CLAMP (*pullout, 0, 100)) / 100;
|
|
|
|
*red = ((c - k) << 8) / (256 - k);
|
|
*green = ((m - k) << 8) / (256 - k);
|
|
*blue = ((y - k) << 8) / (256 - k);
|
|
*pullout = k;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_cmyk_to_rgb_int:
|
|
* @cyan: the cyan channel; returns the red value (0-255)
|
|
* @magenta: the magenta channel; returns the green value (0-255)
|
|
* @yellow: the yellow channel; returns the blue value (0-255)
|
|
* @black: the black channel (0-255); doesn't change
|
|
*
|
|
* Does a naive conversion from CMYK to RGB colorspace. A simple
|
|
* formula that doesn't take any color-profiles into account is used.
|
|
**/
|
|
void
|
|
gimp_cmyk_to_rgb_int (gint *cyan,
|
|
gint *magenta,
|
|
gint *yellow,
|
|
gint *black)
|
|
{
|
|
gint c, m, y, k;
|
|
|
|
c = *cyan;
|
|
m = *magenta;
|
|
y = *yellow;
|
|
k = *black;
|
|
|
|
if (k)
|
|
{
|
|
c = ((c * (256 - k)) >> 8) + k;
|
|
m = ((m * (256 - k)) >> 8) + k;
|
|
y = ((y * (256 - k)) >> 8) + k;
|
|
}
|
|
|
|
*cyan = 255 - c;
|
|
*magenta = 255 - m;
|
|
*yellow = 255 - y;
|
|
}
|
|
|
|
/**
|
|
* gimp_rgb_to_hsv4:
|
|
* @rgb: RGB triplet, rgb[0] is red channel, rgb[1] is green,
|
|
* rgb[2] is blue (0..255)
|
|
* @hue: Pointer to hue channel (0..1)
|
|
* @saturation: Pointer to saturation channel (0..1)
|
|
* @value: Pointer to value channel (0..1)
|
|
**/
|
|
void
|
|
gimp_rgb_to_hsv4 (const guchar *rgb,
|
|
gdouble *hue,
|
|
gdouble *saturation,
|
|
gdouble *value)
|
|
{
|
|
gdouble red, green, blue;
|
|
gdouble h, s, v;
|
|
gdouble min, max;
|
|
gdouble delta;
|
|
|
|
red = rgb[0] / 255.0;
|
|
green = rgb[1] / 255.0;
|
|
blue = rgb[2] / 255.0;
|
|
|
|
if (red > green)
|
|
{
|
|
max = MAX (red, blue);
|
|
min = MIN (green, blue);
|
|
}
|
|
else
|
|
{
|
|
max = MAX (green, blue);
|
|
min = MIN (red, blue);
|
|
}
|
|
|
|
v = max;
|
|
|
|
if (max != 0.0)
|
|
s = (max - min) / max;
|
|
else
|
|
s = 0.0;
|
|
|
|
if (s == 0.0)
|
|
h = 0.0;
|
|
else
|
|
{
|
|
delta = max - min;
|
|
|
|
if (delta == 0.0)
|
|
delta = 1.0;
|
|
|
|
if (red == max)
|
|
h = (green - blue) / delta;
|
|
else if (green == max)
|
|
h = 2 + (blue - red) / delta;
|
|
else
|
|
h = 4 + (red - green) / delta;
|
|
|
|
h /= 6.0;
|
|
|
|
if (h < 0.0)
|
|
h += 1.0;
|
|
else if (h > 1.0)
|
|
h -= 1.0;
|
|
}
|
|
|
|
*hue = h;
|
|
*saturation = s;
|
|
*value = v;
|
|
}
|
|
|
|
/**
|
|
* gimp_hsv_to_rgb4:
|
|
* @rgb: RGB triplet, rgb[0] is red channel, rgb[1] is green,
|
|
* rgb[2] is blue (0..255)
|
|
* @hue: Hue channel (0..1)
|
|
* @saturation: Saturation channel (0..1)
|
|
* @value: Value channel (0..1)
|
|
**/
|
|
void
|
|
gimp_hsv_to_rgb4 (guchar *rgb,
|
|
gdouble hue,
|
|
gdouble saturation,
|
|
gdouble value)
|
|
{
|
|
gdouble h, s, v;
|
|
gdouble f, p, q, t;
|
|
|
|
if (saturation == 0.0)
|
|
{
|
|
hue = value;
|
|
saturation = value;
|
|
/*value = value;*/
|
|
}
|
|
else
|
|
{
|
|
h = hue * 6.0;
|
|
s = saturation;
|
|
v = value;
|
|
|
|
if (h == 6.0)
|
|
h = 0.0;
|
|
|
|
f = h - (gint) h;
|
|
p = v * (1.0 - s);
|
|
q = v * (1.0 - s * f);
|
|
t = v * (1.0 - s * (1.0 - f));
|
|
|
|
switch ((int) h)
|
|
{
|
|
case 0:
|
|
hue = v;
|
|
saturation = t;
|
|
value = p;
|
|
break;
|
|
|
|
case 1:
|
|
hue = q;
|
|
saturation = v;
|
|
value = p;
|
|
break;
|
|
|
|
case 2:
|
|
hue = p;
|
|
saturation = v;
|
|
value = t;
|
|
break;
|
|
|
|
case 3:
|
|
hue = p;
|
|
saturation = q;
|
|
value = v;
|
|
break;
|
|
|
|
case 4:
|
|
hue = t;
|
|
saturation = p;
|
|
value = v;
|
|
break;
|
|
|
|
case 5:
|
|
hue = v;
|
|
saturation = p;
|
|
value = q;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rgb[0] = ROUND (hue * 255.0);
|
|
rgb[1] = ROUND (saturation * 255.0);
|
|
rgb[2] = ROUND (value * 255.0);
|
|
}
|