Je recherche un convertisseur d'espace colorimétrique de RVB à HSV, en particulier pour la plage 0 à 255 pour les deux espaces colorimétriques.
Réponses:
Je les utilise depuis longtemps - aucune idée d'où ils viennent à ce stade ... Notez que les entrées et les sorties, à l'exception de l'angle en degrés, sont dans la plage de 0 à 1,0.
REMARQUE: ce code ne fait pas de véritable vérification de cohérence sur les entrées. Procéder avec prudence!
typedef struct {
double r; // a fraction between 0 and 1
double g; // a fraction between 0 and 1
double b; // a fraction between 0 and 1
} rgb;
typedef struct {
double h; // angle in degrees
double s; // a fraction between 0 and 1
double v; // a fraction between 0 and 1
} hsv;
static hsv rgb2hsv(rgb in);
static rgb hsv2rgb(hsv in);
hsv rgb2hsv(rgb in)
{
hsv out;
double min, max, delta;
min = in.r < in.g ? in.r : in.g;
min = min < in.b ? min : in.b;
max = in.r > in.g ? in.r : in.g;
max = max > in.b ? max : in.b;
out.v = max; // v
delta = max - min;
if (delta < 0.00001)
{
out.s = 0;
out.h = 0; // undefined, maybe nan?
return out;
}
if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
out.s = (delta / max); // s
} else {
// if max is 0, then r = g = b = 0
// s = 0, h is undefined
out.s = 0.0;
out.h = NAN; // its now undefined
return out;
}
if( in.r >= max ) // > is bogus, just keeps compilor happy
out.h = ( in.g - in.b ) / delta; // between yellow & magenta
else
if( in.g >= max )
out.h = 2.0 + ( in.b - in.r ) / delta; // between cyan & yellow
else
out.h = 4.0 + ( in.r - in.g ) / delta; // between magenta & cyan
out.h *= 60.0; // degrees
if( out.h < 0.0 )
out.h += 360.0;
return out;
}
rgb hsv2rgb(hsv in)
{
double hh, p, q, t, ff;
long i;
rgb out;
if(in.s <= 0.0) { // < is bogus, just shuts up warnings
out.r = in.v;
out.g = in.v;
out.b = in.v;
return out;
}
hh = in.h;
if(hh >= 360.0) hh = 0.0;
hh /= 60.0;
i = (long)hh;
ff = hh - i;
p = in.v * (1.0 - in.s);
q = in.v * (1.0 - (in.s * ff));
t = in.v * (1.0 - (in.s * (1.0 - ff)));
switch(i) {
case 0:
out.r = in.v;
out.g = t;
out.b = p;
break;
case 1:
out.r = q;
out.g = in.v;
out.b = p;
break;
case 2:
out.r = p;
out.g = in.v;
out.b = t;
break;
case 3:
out.r = p;
out.g = q;
out.b = in.v;
break;
case 4:
out.r = t;
out.g = p;
out.b = in.v;
break;
case 5:
default:
out.r = in.v;
out.g = p;
out.b = q;
break;
}
return out;
}
>=
erreur du compilateur et est qu'elle double == double
est invalide et illégale dans la plupart des compilateurs. L'arithmétique en virgule flottante et le stockage en virgule flottante signifient que deux valeurs peuvent être égales en valeur approximative , mais pas égales en valeur stockée, même si, selon la formule, elles sont identiques. Vous êtes censé faire abs(double_a - double_b) <= epsilon
là où epsilon a une certaine valeur, généralement 1e-4
.
Vous pouvez également essayer ce code sans flotteurs (plus rapide mais moins précis):
typedef struct RgbColor
{
unsigned char r;
unsigned char g;
unsigned char b;
} RgbColor;
typedef struct HsvColor
{
unsigned char h;
unsigned char s;
unsigned char v;
} HsvColor;
RgbColor HsvToRgb(HsvColor hsv)
{
RgbColor rgb;
unsigned char region, remainder, p, q, t;
if (hsv.s == 0)
{
rgb.r = hsv.v;
rgb.g = hsv.v;
rgb.b = hsv.v;
return rgb;
}
region = hsv.h / 43;
remainder = (hsv.h - (region * 43)) * 6;
p = (hsv.v * (255 - hsv.s)) >> 8;
q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
rgb.r = hsv.v; rgb.g = t; rgb.b = p;
break;
case 1:
rgb.r = q; rgb.g = hsv.v; rgb.b = p;
break;
case 2:
rgb.r = p; rgb.g = hsv.v; rgb.b = t;
break;
case 3:
rgb.r = p; rgb.g = q; rgb.b = hsv.v;
break;
case 4:
rgb.r = t; rgb.g = p; rgb.b = hsv.v;
break;
default:
rgb.r = hsv.v; rgb.g = p; rgb.b = q;
break;
}
return rgb;
}
HsvColor RgbToHsv(RgbColor rgb)
{
HsvColor hsv;
unsigned char rgbMin, rgbMax;
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
hsv.v = rgbMax;
if (hsv.v == 0)
{
hsv.h = 0;
hsv.s = 0;
return hsv;
}
hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
if (hsv.s == 0)
{
hsv.h = 0;
return hsv;
}
if (rgbMax == rgb.r)
hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
else if (rgbMax == rgb.g)
hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
else
hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
return hsv;
}
Notez que cet algorithme utilise 0-255
comme plage (non 0-360
) comme cela a été demandé par l'auteur de cette question.
J'ai écrit ceci en HLSL pour notre moteur de rendu, il n'y a aucune condition:
float3 HSV2RGB( float3 _HSV )
{
_HSV.x = fmod( 100.0 + _HSV.x, 1.0 ); // Ensure [0,1[
float HueSlice = 6.0 * _HSV.x; // In [0,6[
float HueSliceInteger = floor( HueSlice );
float HueSliceInterpolant = HueSlice - HueSliceInteger; // In [0,1[ for each hue slice
float3 TempRGB = float3( _HSV.z * (1.0 - _HSV.y),
_HSV.z * (1.0 - _HSV.y * HueSliceInterpolant),
_HSV.z * (1.0 - _HSV.y * (1.0 - HueSliceInterpolant)) );
// The idea here to avoid conditions is to notice that the conversion code can be rewritten:
// if ( var_i == 0 ) { R = V ; G = TempRGB.z ; B = TempRGB.x }
// else if ( var_i == 2 ) { R = TempRGB.x ; G = V ; B = TempRGB.z }
// else if ( var_i == 4 ) { R = TempRGB.z ; G = TempRGB.x ; B = V }
//
// else if ( var_i == 1 ) { R = TempRGB.y ; G = V ; B = TempRGB.x }
// else if ( var_i == 3 ) { R = TempRGB.x ; G = TempRGB.y ; B = V }
// else if ( var_i == 5 ) { R = V ; G = TempRGB.x ; B = TempRGB.y }
//
// This shows several things:
// . A separation between even and odd slices
// . If slices (0,2,4) and (1,3,5) can be rewritten as basically being slices (0,1,2) then
// the operation simply amounts to performing a "rotate right" on the RGB components
// . The base value to rotate is either (V, B, R) for even slices or (G, V, R) for odd slices
//
float IsOddSlice = fmod( HueSliceInteger, 2.0 ); // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
float ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
float3 ScrollingRGBForEvenSlices = float3( _HSV.z, TempRGB.zx ); // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
float3 ScrollingRGBForOddSlices = float3( TempRGB.y, _HSV.z, TempRGB.x ); // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
float3 ScrollingRGB = lerp( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
float IsNotFirstSlice = saturate( ThreeSliceSelector ); // 1 if NOT the first slice (true for slices 1 and 2)
float IsNotSecondSlice = saturate( ThreeSliceSelector-1.0 ); // 1 if NOT the first or second slice (true only for slice 2)
return lerp( ScrollingRGB.xyz, lerp( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice ); // Make the RGB rotate right depending on final slice index
}
Voici une implémentation C basée sur l' infographie et la modélisation géométrique d' Agoston : Implémentation et algorithmes p. 304, avec H ∈ [0, 360] et S , V ∈ [0, 1].
#include <math.h>
typedef struct {
double r; // ∈ [0, 1]
double g; // ∈ [0, 1]
double b; // ∈ [0, 1]
} rgb;
typedef struct {
double h; // ∈ [0, 360]
double s; // ∈ [0, 1]
double v; // ∈ [0, 1]
} hsv;
rgb hsv2rgb(hsv HSV)
{
rgb RGB;
double H = HSV.h, S = HSV.s, V = HSV.v,
P, Q, T,
fract;
(H == 360.)?(H = 0.):(H /= 60.);
fract = H - floor(H);
P = V*(1. - S);
Q = V*(1. - S*fract);
T = V*(1. - S*(1. - fract));
if (0. <= H && H < 1.)
RGB = (rgb){.r = V, .g = T, .b = P};
else if (1. <= H && H < 2.)
RGB = (rgb){.r = Q, .g = V, .b = P};
else if (2. <= H && H < 3.)
RGB = (rgb){.r = P, .g = V, .b = T};
else if (3. <= H && H < 4.)
RGB = (rgb){.r = P, .g = Q, .b = V};
else if (4. <= H && H < 5.)
RGB = (rgb){.r = T, .g = P, .b = V};
else if (5. <= H && H < 6.)
RGB = (rgb){.r = V, .g = P, .b = Q};
else
RGB = (rgb){.r = 0., .g = 0., .b = 0.};
return RGB;
}
cela devrait être ici: ça marche quand même. Et cela a l'air bien comparé à ceux ci-dessus.
code hlsl
float3 Hue(float H)
{
half R = abs(H * 6 - 3) - 1;
half G = 2 - abs(H * 6 - 2);
half B = 2 - abs(H * 6 - 4);
return saturate(half3(R,G,B));
}
half4 HSVtoRGB(in half3 HSV)
{
return half4(((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z,1);
}
float3 est un type de données vector3 de précision 16 bits, c'est-à-dire float3 hue () renvoie un type de données (x, y, z) par exemple (r, g, b), la moitié est la même avec une demi-précision, 8 bits, un float4 est (r, g, b, a) 4 valeurs.
half
, half4
, half3
, float3
, et ainsi de suite.
La réponse de @ fins a un problème de débordement sur Arduio lorsque vous réduisez la saturation. Le voici avec quelques valeurs converties en int pour éviter cela.
typedef struct RgbColor
{
unsigned char r;
unsigned char g;
unsigned char b;
} RgbColor;
typedef struct HsvColor
{
unsigned char h;
unsigned char s;
unsigned char v;
} HsvColor;
RgbColor HsvToRgb(HsvColor hsv)
{
RgbColor rgb;
unsigned char region, p, q, t;
unsigned int h, s, v, remainder;
if (hsv.s == 0)
{
rgb.r = hsv.v;
rgb.g = hsv.v;
rgb.b = hsv.v;
return rgb;
}
// converting to 16 bit to prevent overflow
h = hsv.h;
s = hsv.s;
v = hsv.v;
region = h / 43;
remainder = (h - (region * 43)) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
rgb.r = v;
rgb.g = t;
rgb.b = p;
break;
case 1:
rgb.r = q;
rgb.g = v;
rgb.b = p;
break;
case 2:
rgb.r = p;
rgb.g = v;
rgb.b = t;
break;
case 3:
rgb.r = p;
rgb.g = q;
rgb.b = v;
break;
case 4:
rgb.r = t;
rgb.g = p;
rgb.b = v;
break;
default:
rgb.r = v;
rgb.g = p;
rgb.b = q;
break;
}
return rgb;
}
HsvColor RgbToHsv(RgbColor rgb)
{
HsvColor hsv;
unsigned char rgbMin, rgbMax;
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
hsv.v = rgbMax;
if (hsv.v == 0)
{
hsv.h = 0;
hsv.s = 0;
return hsv;
}
hsv.s = 255 * ((long)(rgbMax - rgbMin)) / hsv.v;
if (hsv.s == 0)
{
hsv.h = 0;
return hsv;
}
if (rgbMax == rgb.r)
hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
else if (rgbMax == rgb.g)
hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
else
hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
return hsv;
}
Ce n'est pas C, mais ça marche certainement. Toutes les autres méthodes que je vois ici fonctionnent en enveloppant tout dans des parties d'un hexagone, et en se rapprochant des «angles» à partir de cela. En commençant par une équation différente utilisant des cosinus et en résolvant hs et v, vous obtenez une relation beaucoup plus agréable entre hsv et rgb, et l'interpolation devient plus fluide (au prix d'être beaucoup plus lente).
Supposons que tout soit en virgule flottante. Si rg et b vont de 0 à 1, h va de 0 à 2pi, v va de 0 à 4/3, et s va de 0 à 2/3.
Le code suivant est écrit en Lua. C'est facilement traduisible en autre chose.
local hsv do
hsv ={}
local atan2 =math.atan2
local cos =math.cos
local sin =math.sin
function hsv.fromrgb(r,b,g)
local c=r+g+b
if c<1e-4 then
return 0,2/3,0
else
local p=2*(b*b+g*g+r*r-g*r-b*g-b*r)^0.5
local h=atan2(b-g,(2*r-b-g)/3^0.5)
local s=p/(c+p)
local v=(c+p)/3
return h,s,v
end
end
function hsv.torgb(h,s,v)
local r=v*(1+s*(cos(h)-1))
local g=v*(1+s*(cos(h-2.09439)-1))
local b=v*(1+s*(cos(h+2.09439)-1))
return r,g,b
end
function hsv.tween(h0,s0,v0,h1,s1,v1,t)
local dh=(h1-h0+3.14159)%6.28318-3.14159
local h=h0+t*dh
local s=s0+t*(s1-s0)
local v=v0+t*(v1-v0)
return h,s,v
end
end
Version GLSL Shader basée sur la réponse Patapoms:
vec3 HSV2RGB( vec3 hsv )
{
hsv.x = mod( 100.0 + hsv.x, 1.0 ); // Ensure [0,1[
float HueSlice = 6.0 * hsv.x; // In [0,6[
float HueSliceInteger = floor( HueSlice );
float HueSliceInterpolant = HueSlice - HueSliceInteger; // In [0,1[ for each hue slice
vec3 TempRGB = vec3( hsv.z * (1.0 - hsv.y), hsv.z * (1.0 - hsv.y * HueSliceInterpolant), hsv.z * (1.0 - hsv.y * (1.0 - HueSliceInterpolant)) );
float IsOddSlice = mod( HueSliceInteger, 2.0 ); // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
float ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
vec3 ScrollingRGBForEvenSlices = vec3( hsv.z, TempRGB.zx ); // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
vec3 ScrollingRGBForOddSlices = vec3( TempRGB.y, hsv.z, TempRGB.x ); // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
vec3 ScrollingRGB = mix( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
float IsNotFirstSlice = clamp( ThreeSliceSelector, 0.0,1.0 ); // 1 if NOT the first slice (true for slices 1 and 2)
float IsNotSecondSlice = clamp( ThreeSliceSelector-1.0, 0.0,1. ); // 1 if NOT the first or second slice (true only for slice 2)
return mix( ScrollingRGB.xyz, mix( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice ); // Make the RGB rotate right depending on final slice index
}
Je ne suis pas développeur C ++ donc je ne fournirai pas de code. Mais je peux fournir un algorithme simple hsv2rgb (rgb2hsv ici ) que je découvre actuellement - je mets à jour le wiki avec la description: HSV et HLS . La principale amélioration est que j'observe attentivement r, g, b en tant que fonctions de teinte et j'introduis une fonction de forme plus simple pour les décrire (sans perdre de précision). L'algorithme - en entrée, nous avons: h (0-255), s (0-255), v (0-255)
r = 255*f(5), g = 255*f(3), b = 255*f(1)
Nous utilisons la fonction f décrite comme suit
f(n) = v/255 - (v/255)*(s/255)*max(min(k,4-k,1),0)
où (mod peut renvoyer une partie de fraction; k est un nombre à virgule flottante)
k = (n+h*360/(255*60)) mod 6;
min(k,4-k,1)
. Pourquoi y a-t-il trois valeurs et que se passe-t-il exactement ici? Merci d'avance!
Voici un convertisseur en ligne avec un article après avoir expliqué tous les algorithmes de conversion des couleurs.
Vous préféreriez probablement une version C prête à l'emploi, mais elle ne devrait pas être longue à appliquer et cela pourrait aider d'autres personnes à essayer de faire de même dans une autre langue ou avec un autre espace colorimétrique.
En voici un que je viens d'écrire ce matin sur la base à peu près des mêmes calculs que ci-dessus:
/* math adapted from: http://www.rapidtables.com/convert/color/rgb-to-hsl.htm
* reasonably optimized for speed, without going crazy */
void rgb_to_hsv (int r, int g, int b, float *r_h, float *r_s, float *r_v) {
float rp, gp, bp, cmax, cmin, delta, l;
int cmaxwhich, cminwhich;
rp = ((float) r) / 255;
gp = ((float) g) / 255;
bp = ((float) b) / 255;
//debug ("rgb=%d,%d,%d rgbprime=%f,%f,%f", r, g, b, rp, gp, bp);
cmax = rp;
cmaxwhich = 0; /* faster comparison afterwards */
if (gp > cmax) { cmax = gp; cmaxwhich = 1; }
if (bp > cmax) { cmax = bp; cmaxwhich = 2; }
cmin = rp;
cminwhich = 0;
if (gp < cmin) { cmin = gp; cminwhich = 1; }
if (bp < cmin) { cmin = bp; cminwhich = 2; }
//debug ("cmin=%f,cmax=%f", cmin, cmax);
delta = cmax - cmin;
/* HUE */
if (delta == 0) {
*r_h = 0;
} else {
switch (cmaxwhich) {
case 0: /* cmax == rp */
*r_h = HUE_ANGLE * (fmod ((gp - bp) / delta, 6));
break;
case 1: /* cmax == gp */
*r_h = HUE_ANGLE * (((bp - rp) / delta) + 2);
break;
case 2: /* cmax == bp */
*r_h = HUE_ANGLE * (((rp - gp) / delta) + 4);
break;
}
if (*r_h < 0)
*r_h += 360;
}
/* LIGHTNESS/VALUE */
//l = (cmax + cmin) / 2;
*r_v = cmax;
/* SATURATION */
/*if (delta == 0) {
*r_s = 0;
} else {
*r_s = delta / (1 - fabs (1 - (2 * (l - 1))));
}*/
if (cmax == 0) {
*r_s = 0;
} else {
*r_s = delta / cmax;
}
//debug ("rgb=%d,%d,%d ---> hsv=%f,%f,%f", r, g, b, *r_h, *r_s, *r_v);
}
void hsv_to_rgb (float h, float s, float v, int *r_r, int *r_g, int *r_b) {
if (h > 360)
h -= 360;
if (h < 0)
h += 360;
h = CLAMP (h, 0, 360);
s = CLAMP (s, 0, 1);
v = CLAMP (v, 0, 1);
float c = v * s;
float x = c * (1 - fabsf (fmod ((h / HUE_ANGLE), 2) - 1));
float m = v - c;
float rp, gp, bp;
int a = h / 60;
//debug ("h=%f, a=%d", h, a);
switch (a) {
case 0:
rp = c;
gp = x;
bp = 0;
break;
case 1:
rp = x;
gp = c;
bp = 0;
break;
case 2:
rp = 0;
gp = c;
bp = x;
break;
case 3:
rp = 0;
gp = x;
bp = c;
break;
case 4:
rp = x;
gp = 0;
bp = c;
break;
default: // case 5:
rp = c;
gp = 0;
bp = x;
break;
}
*r_r = (rp + m) * 255;
*r_g = (gp + m) * 255;
*r_b = (bp + m) * 255;
//debug ("hsv=%f,%f,%f, ---> rgb=%d,%d,%d", h, s, v, *r_r, *r_g, *r_b);
}
J'ai créé une implémentation peut-être plus rapide en utilisant la plage 0-1 pour RGBS et V et la plage 0-6 pour Hue (en évitant la division), et en regroupant les cas en deux catégories:
#include <math.h>
#include <float.h>
void fromRGBtoHSV(float rgb[], float hsv[])
{
// for(int i=0; i<3; ++i)
// rgb[i] = max(0.0f, min(1.0f, rgb[i]));
hsv[0] = 0.0f;
hsv[2] = max(rgb[0], max(rgb[1], rgb[2]));
const float delta = hsv[2] - min(rgb[0], min(rgb[1], rgb[2]));
if (delta < FLT_MIN)
hsv[1] = 0.0f;
else
{
hsv[1] = delta / hsv[2];
if (rgb[0] >= hsv[2])
{
hsv[0] = (rgb[1] - rgb[2]) / delta;
if (hsv[0] < 0.0f)
hsv[0] += 6.0f;
}
else if (rgb[1] >= hsv[2])
hsv[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
else
hsv[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
}
}
void fromHSVtoRGB(const float hsv[], float rgb[])
{
if(hsv[1] < FLT_MIN)
rgb[0] = rgb[1] = rgb[2] = hsv[2];
else
{
const float h = hsv[0];
const int i = (int)h;
const float f = h - i;
const float p = hsv[2] * (1.0f - hsv[1]);
if (i & 1) {
const float q = hsv[2] * (1.0f - (hsv[1] * f));
switch(i) {
case 1:
rgb[0] = q;
rgb[1] = hsv[2];
rgb[2] = p;
break;
case 3:
rgb[0] = p;
rgb[1] = q;
rgb[2] = hsv[2];
break;
default:
rgb[0] = hsv[2];
rgb[1] = p;
rgb[2] = q;
break;
}
}
else
{
const float t = hsv[2] * (1.0f - (hsv[1] * (1.0f - f)));
switch(i) {
case 0:
rgb[0] = hsv[2];
rgb[1] = t;
rgb[2] = p;
break;
case 2:
rgb[0] = p;
rgb[1] = hsv[2];
rgb[2] = t;
break;
default:
rgb[0] = t;
rgb[1] = p;
rgb[2] = hsv[2];
break;
}
}
}
}
Pour une plage de 0 à 255, il suffit de * 255,0f + 0,5f et de l'affecter à un caractère non signé (ou de diviser par 255,0 pour obtenir le contraire).
// This pair of functions convert HSL to RGB and vice-versa.
// It's pretty optimized for execution speed
typedef unsigned char BYTE
typedef struct _RGB
{
BYTE R;
BYTE G;
BYTE B;
} RGB, *pRGB;
typedef struct _HSL
{
float H; // color Hue (0.0 to 360.0 degrees)
float S; // color Saturation (0.0 to 1.0)
float L; // Luminance (0.0 to 1.0)
float V; // Value (0.0 to 1.0)
} HSL, *pHSL;
float *fMin (float *a, float *b)
{
return *a <= *b? a : b;
}
float *fMax (float *a, float *b)
{
return *a >= *b? a : b;
}
void RGBtoHSL (pRGB rgb, pHSL hsl)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
// rgb->R, rgb->G, rgb->B: [0 to 255]
float r = (float) rgb->R / 255;
float g = (float) rgb->G / 255;
float b = (float) rgb->B / 255;
float *min = fMin(fMin(&r, &g), &b);
float *max = fMax(fMax(&r, &g), &b);
float delta = *max - *min;
// L, V [0.0 to 1.0]
hsl->L = (*max + *min)/2;
hsl->V = *max;
// Special case for H and S
if (delta == 0)
{
hsl->H = 0.0f;
hsl->S = 0.0f;
}
else
{
// Special case for S
if((*max == 0) || (*min == 1))
hsl->S = 0;
else
// S [0.0 to 1.0]
hsl->S = (2 * *max - 2*hsl->L)/(1 - fabsf(2*hsl->L - 1));
// H [0.0 to 360.0]
if (max == &r) hsl->H = fmod((g - b)/delta, 6); // max is R
else if (max == &g) hsl->H = (b - r)/delta + 2; // max is G
else hsl->H = (r - g)/delta + 4; // max is B
hsl->H *= 60;
}
}
void HSLtoRGB (pHSL hsl, pRGB rgb)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
float a, k, fm1, fp1, f1, f2, *f3;
// L, V, S: [0.0 to 1.0]
// rgb->R, rgb->G, rgb->B: [0 to 255]
fm1 = -1;
fp1 = 1;
f1 = 1-hsl->L;
a = hsl->S * *fMin(&hsl->L, &f1);
k = fmod(0 + hsl->H/30, 12);
f1 = k - 3;
f2 = 9 - k;
f3 = fMin(fMin(&f1, &f2), &fp1) ;
rgb->R = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
k = fmod(8 + hsl->H/30, 12);
f1 = k - 3;
f2 = 9 - k;
f3 = fMin(fMin(&f1, &f2), &fp1) ;
rgb->G = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
k = fmod(4 + hsl->H/30, 12);
f1 = k - 3;
f2 = 9 - k;
f3 = fMin(fMin(&f1, &f2), &fp1) ;
rgb->B = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
}