summaryrefslogtreecommitdiffstats
path: root/libgimpcolor/gimpcolorspace.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgimpcolor/gimpcolorspace.c')
-rw-r--r--libgimpcolor/gimpcolorspace.c1103
1 files changed, 1103 insertions, 0 deletions
diff --git a/libgimpcolor/gimpcolorspace.c b/libgimpcolor/gimpcolorspace.c
new file mode 100644
index 0000000..b8885ce
--- /dev/null
+++ b/libgimpcolor/gimpcolorspace.c
@@ -0,0 +1,1103 @@
+/* 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);
+}