/* * Copyright (c) 2005 Boudewijn Rempt * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "kis_color_conversions.h" /** * A number of often-used conversions between color models */ void rgb_to_hsv(int R, int G, int B, int *H, int *S, int *V) { unsigned int max = R; unsigned int min = R; unsigned char maxValue = 0; // r = 0, g = 1, b = 2 // find maximum and minimum RGB values if(static_cast(G) > max) { max = G; maxValue = 1; } if (static_cast(B) > max) { max = B; maxValue = 2; } if(static_cast(G) < min) min = G; if(static_cast(B) < min ) min = B; int delta = max - min; // To prevent division by zero later on. if (delta == 0) delta = 1; *V = max; // value *S = max ? (510 * delta + max) / ( 2 * max) : 0; // saturation // calc hue if(*S == 0) *H = -1; // undefined hue else { switch(maxValue) { case 0: // red if(G >= B) *H = (120 * (G - B) + delta) / (2 * delta); else *H = (120 * (G - B + delta) + delta) / (2 * delta) + 300; break; case 1: // green if(B > R) *H = 120 + (120 * (B - R) + delta) / (2 * delta); else *H = 60 + (120 * (B - R + delta) + delta) / (2 * delta); break; case 2: // blue if(R > G) *H = 240 + (120 * (R - G) + delta) / (2 * delta); else *H = 180 + (120 * (R - G + delta) + delta) / (2 * delta); break; } } } void hsv_to_rgb(int H, int S, int V, int *R, int *G, int *B) { *R = *G = *B = V; if (S != 0 && H != -1) { // chromatic if (H >= 360) { // angle > 360 H %= 360; } unsigned int f = H % 60; H /= 60; unsigned int p = static_cast(2*V*(255-S)+255)/510; unsigned int q, t; if (H & 1) { q = static_cast(2 * V * (15300 - S * f) + 15300) / 30600; switch (H) { case 1: *R = static_cast(q); *G = static_cast(V); *B = static_cast(p); break; case 3: *R = static_cast(p); *G = static_cast(q); *B = static_cast(V); break; case 5: *R = static_cast(V); *G = static_cast(p); *B = static_cast(q); break; } } else { t = static_cast(2 * V * (15300 - (S * (60 - f))) + 15300) / 30600; switch (H) { case 0: *R = static_cast(V); *G = static_cast(t); *B = static_cast(p); break; case 2: *R = static_cast(p); *G = static_cast(V); *B = static_cast(t); break; case 4: *R = static_cast(t); *G = static_cast(p); *B = static_cast(V); break; } } } } #define EPSILON 1e-6 #define UNDEFINED_HUE -1 void RGBToHSV(float r, float g, float b, float *h, float *s, float *v) { float max = TQMAX(r, TQMAX(g, b)); float min = TQMIN(r, TQMIN(g, b)); *v = max; if (max > EPSILON) { *s = (max - min) / max; } else { *s = 0; } if (*s < EPSILON) { *h = UNDEFINED_HUE; } else { float delta = max - min; if (r == max) { *h = (g - b) / delta; } else if (g == max) { *h = 2 + (b - r) / delta; } else { *h = 4 + (r - g) / delta; } *h *= 60; if (*h < 0) { *h += 360; } } } void HSVToRGB(float h, float s, float v, float *r, float *g, float *b) { if (s < EPSILON || h == UNDEFINED_HUE) { // Achromatic case *r = v; *g = v; *b = v; } else { float f, p, q, t; int i; if (h > 360 - EPSILON) { h -= 360; } h /= 60; i = static_cast(floor(h)); f = h - i; p = v * (1 - s); q = v * (1 - (s * f)); t = v * (1 - (s * (1 - f))); switch (i) { case 0: *r = v; *g = t; *b = p; break; case 1: *r = q; *g = v; *b = p; break; case 2: *r = p; *g = v; *b = t; break; case 3: *r = p; *g = q; *b = v; break; case 4: *r = t; *g = p; *b = v; break; case 5: *r = v; *g = p; *b = q; break; } } } void rgb_to_hls(TQ_UINT8 red, TQ_UINT8 green, TQ_UINT8 blue, float * hue, float * lightness, float * saturation) { float r = red / 255.0; float g = green / 255.0; float b = blue / 255.0; float h = 0; float l = 0; float s = 0; float max, min, delta; max = TQMAX(r, g); max = TQMAX(max, b); min = TQMIN(r, g); min = TQMIN(min, b); delta = max - min; l = (max + min) / 2; if (delta == 0) { // This is a gray, no chroma... h = 0; s = 0; } else { if ( l < 0.5) s = delta / ( max + min ); else s = delta / ( 2 - max - min ); float delta_r, delta_g, delta_b; delta_r = (( max - r ) / 6 ) / delta; delta_g = (( max - g ) / 6 ) / delta; delta_b = (( max - b ) / 6 ) / delta; if ( r == max ) h = delta_b - delta_g; else if ( g == max) h = ( 1.0 / 3 ) + delta_r - delta_b; else if ( b == max) h = ( 2.0 / 3 ) + delta_g - delta_r; if (h < 0) h += 1; if (h > 1) h += 1; } *hue = h * 360; *saturation = s; *lightness = l; } float hue_value(float n1, float n2, float hue) { if (hue > 360 ) hue = hue -360; else if (hue < 0 ) hue = hue +360; if (hue < 60 ) return n1 + (((n2 - n1) * hue) / 60); else if (hue < 180 ) return n2; else if (hue < 240 ) return n1 + (((n2 - n1) * (240 - hue)) / 60); else return n1; } void hls_to_rgb(float h, float l, float s, TQ_UINT8 * r, TQ_UINT8 * g, TQ_UINT8 * b) { float m1, m2; if (l <= 0.5 ) m2 = l * ( 1 + s ); else m2 = l + s - l * s; m1 = 2 * l - m2; *r = (TQ_UINT8)(hue_value(m1, m2, h + 120) * 255 + 0.5); *g = (TQ_UINT8)(hue_value(m1, m2, h) * 255 + 0.5); *b = (TQ_UINT8)(hue_value(m1, m2, h - 120) * 255 + 0.5); } void rgb_to_hls(TQ_UINT8 r, TQ_UINT8 g, TQ_UINT8 b, int * h, int * l, int * s) { float hue, saturation, lightness; rgb_to_hls(r, g, b, &hue, &lightness, &saturation); *h = (int)(hue + 0.5); *l = (int)(lightness * 255 + 0.5); *s = (int)(saturation * 255 + 0.5); } void hls_to_rgb(int h, int l, int s, TQ_UINT8 * r, TQ_UINT8 * g, TQ_UINT8 * b) { float hue = h; float lightness = l / 255.0; float saturation = s / 255.0; hls_to_rgb(hue, lightness, saturation, r, g, b); } /* A Fast HSL-to-RGB Transform by Ken Fishkin from "Graphics Gems", Academic Press, 1990 */ void RGBToHSL(float r, float g, float b, float *h, float *s, float *l) { float v; float m; float vm; float r2, g2, b2; v = TQMAX(r,g); v = TQMAX(v,b); m = TQMIN(r,g); m = TQMIN(m,b); if ((*l = (m + v) / 2.0) <= 0.0) { *h = UNDEFINED_HUE; *s = 0; return; } if ((*s = vm = v - m) > 0.0) { *s /= (*l <= 0.5) ? (v + m ) : (2.0 - v - m) ; } else { *h = UNDEFINED_HUE; return; } r2 = (v - r) / vm; g2 = (v - g) / vm; b2 = (v - b) / vm; if (r == v) *h = (g == m ? 5.0 + b2 : 1.0 - g2); else if (g == v) *h = (b == m ? 1.0 + r2 : 3.0 - b2); else *h = (r == m ? 3.0 + g2 : 5.0 - r2); *h *= 60; } void HSLToRGB(float h, float sl, float l, float *r, float *g, float *b) { float v; v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); if (v <= 0) { *r = *g = *b = 0.0; } else { float m; float sv; int sextant; float fract, vsf, mid1, mid2; m = l + l - v; sv = (v - m ) / v; h /= 60.0; sextant = static_cast(h); fract = h - sextant; vsf = v * sv * fract; mid1 = m + vsf; mid2 = v - vsf; switch (sextant) { case 0: *r = v; *g = mid1; *b = m; break; case 1: *r = mid2; *g = v; *b = m; break; case 2: *r = m; *g = v; *b = mid1; break; case 3: *r = m; *g = mid2; *b = v; break; case 4: *r = mid1; *g = m; *b = v; break; case 5: *r = v; *g = m; *b = mid2; break; } } }