/* LIBPIKA - The PIKA 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 * . */ #include "config.h" #include #include #include "libpikamath/pikamath.h" #include "pikacolortypes.h" #include "pikacolorspace.h" #include "pikargb.h" #include "pikahsv.h" /** * SECTION: pikacolorspace * @title: PikaColorSpace * @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 * PIKA uses. This file provides utility functions to convert colors * between different color spaces. **/ #define PIKA_HSL_UNDEFINED -1.0 /* PikaRGB functions */ /** * pika_rgb_to_hsv: * @rgb: A color value in the RGB colorspace * @hsv: (out caller-allocates): The value converted to the HSV colorspace * * Does a conversion from RGB to HSV (Hue, Saturation, * Value) colorspace. **/ void pika_rgb_to_hsv (const PikaRGB *rgb, PikaHSV *hsv) { gdouble max, min, delta; g_return_if_fail (rgb != NULL); g_return_if_fail (hsv != NULL); max = pika_rgb_max (rgb); min = pika_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; } /** * pika_hsv_to_rgb: * @hsv: A color value in the HSV colorspace * @rgb: (out caller-allocates): The returned RGB value. * * Converts a color value from HSV to RGB colorspace **/ void pika_hsv_to_rgb (const PikaHSV *hsv, PikaRGB *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; } /** * pika_rgb_to_hsl: * @rgb: A color value in the RGB colorspace * @hsl: (out caller-allocates): The value converted to HSL * * Convert an RGB color value to a HSL (Hue, Saturation, Lightness) * color value. **/ void pika_rgb_to_hsl (const PikaRGB *rgb, PikaHSL *hsl) { gdouble max, min, delta; g_return_if_fail (rgb != NULL); g_return_if_fail (hsl != NULL); max = pika_rgb_max (rgb); min = pika_rgb_min (rgb); hsl->l = (max + min) / 2.0; if (max == min) { hsl->s = 0.0; hsl->h = PIKA_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 pika_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; } /** * pika_hsl_to_rgb: * @hsl: A color value in the HSL colorspace * @rgb: (out caller-allocates): The value converted to a value * in the RGB colorspace * * Convert a HSL color value to an RGB color value. **/ void pika_hsl_to_rgb (const PikaHSL *hsl, PikaRGB *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 = pika_hsl_value (m1, m2, hsl->h * 6.0 + 2.0); rgb->g = pika_hsl_value (m1, m2, hsl->h * 6.0); rgb->b = pika_hsl_value (m1, m2, hsl->h * 6.0 - 2.0); } rgb->a = hsl->a; } /** * pika_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: (out caller-allocates): 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 pika_rgb_to_cmyk (const PikaRGB *rgb, gdouble pullout, PikaCMYK *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; } /** * pika_cmyk_to_rgb: * @cmyk: A color value in the CMYK colorspace * @rgb: (out caller-allocates): 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 pika_cmyk_to_rgb (const PikaCMYK *cmyk, PikaRGB *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; }