Я ищу преобразователь цветового пространства из RGB в HSV, особенно для диапазона от 0 до 255 для обоих цветовых пространств.
93
Я использовал их долгое время - не знаю, откуда они взялись ... Обратите внимание, что входы и выходы, за исключением угла в градусах, находятся в диапазоне от 0 до 1.0.
ПРИМЕЧАНИЕ: этот код не выполняет реальной проверки работоспособности входных данных. Продолжить с осторожностью!
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;
}
>=
ошибки компилятора и в том, чтоdouble == double
она недопустима и недопустима в большинстве компиляторов. Арифметика с плавающей запятой и хранение с плавающей запятой означают, что два значения могут быть равны по приблизительному значению, но не равны по сохраненному значению, даже если по формуле они одинаковы.abs(double_a - double_b) <= epsilon
Обычно вы должны делать то, что есть значение epsilon1e-4
.Вы также можете попробовать этот код без поплавков (быстрее, но менее точно):
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; }
Обратите внимание, что этот алгоритм использует
0-255
диапазон (а не0-360
), как это было запрошено автором этого вопроса.источник
Я написал это на HLSL для нашего движка рендеринга, в нем нет условий:
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 }
источник
Вот реализация C на основе компьютерной графики и геометрического моделирования Агостона: реализация и алгоритмы стр. 304, где H ∈ [0, 360] и 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; }
источник
это должно быть здесь: оно все равно работает. И выглядит неплохо по сравнению с вышеперечисленными.
код 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 - это тип данных vector3 с точностью 16 бит, то есть float3 hue () возвращает тип данных (x, y, z), например (r, g, b), половина - то же самое с половинной точностью, 8 бит, float4 - (r, ж, б, а) 4 значения.
источник
half
,half4
,half3
,float3
, и так далее.Ответ @ fins имеет проблему переполнения на Arduio, когда вы уменьшаете насыщенность. Здесь некоторые значения преобразованы в int, чтобы этого не произошло.
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; }
источник
Это не C, но, безусловно, работает. Все остальные методы, которые я здесь вижу, работают, когда все окружают части шестиугольника и аппроксимируют "углы" от этого. Вместо этого, начав с другого уравнения с использованием косинусов и решив для hs и v, вы получите намного более приятную взаимосвязь между hsv и rgb, а анимация станет более плавной (за счет того, что она будет намного медленнее).
Предположим, что все с плавающей точкой. Если rg и b изменяются от 0 до 1, h изменяется от 0 до 2pi, v изменяется от 0 до 4/3, а s изменяется от 0 до 2/3.
Следующий код написан на Lua. Его легко перевести на что угодно.
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
источник
Версия GLSL Shader на основе ответа 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 }
источник
Я не разработчик на C ++, поэтому не буду приводить код. Но я могу предоставить простой алгоритм hsv2rgb ( здесь rgb2hsv ), который я сейчас обнаружил - я обновляю вики с описанием: HSV и HLS . Основное улучшение состоит в том, что я внимательно наблюдаю за функциями цвета, г, б, и ввожу более простую функцию формы для их описания (без потери точности). Алгоритм - на входе имеем: h (0-255), s (0-255), v (0-255)
r = 255*f(5), g = 255*f(3), b = 255*f(1)
Мы используем функцию f, описанную следующим образом
f(n) = v/255 - (v/255)*(s/255)*max(min(k,4-k,1),0)
где (мод может возвращать дробную часть; k - число с плавающей запятой)
k = (n+h*360/(255*60)) mod 6;
Вот фрагменты / PoV в SO в JS: HSV и HSL
источник
min(k,4-k,1)
. Почему здесь три ценности и что именно здесь происходит? Заранее спасибо!Вот онлайн-конвертер со статьей после объяснения всех алгоритмов преобразования цвета.
Вы, вероятно, предпочтете готовую версию C, но она не займет много времени, и она может помочь другим людям, пытающимся сделать то же самое на другом языке или с другим цветовым пространством.
источник
По этой ссылке есть формулы того, что вы хотите. Тогда это вопрос производительности (численные методы), если вы хотите быстро.
источник
Вот тот, который я написал сегодня утром, почти на той же математике, что и выше:
/* 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); }
источник
Я создал, возможно, более быструю реализацию, используя диапазон 0-1 для RGBS и V и диапазон 0-6 для Hue (избегая разделения) и сгруппировав случаи в две категории:
#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; } } } }
Для диапазона 0-255 просто * 255.0f + 0.5f и назначьте его беззнаковому char (или разделите на 255.0, чтобы получить противоположное).
источник
// 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))); }
источник