Saya mencari konverter ruang warna dari RGB ke HSV, khususnya untuk kisaran 0 hingga 255 untuk kedua ruang warna.
Jawaban:
Saya telah menggunakan ini untuk waktu yang lama - tidak tahu dari mana asalnya pada saat ini ... Perhatikan bahwa input dan output, kecuali untuk sudut dalam derajat, berada dalam kisaran 0 hingga 1,0.
CATATAN: kode ini tidak memeriksa kewarasan nyata pada input. Lanjutkan dengan hati hati!
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;
}
>=
kesalahan kompilator dan adalah karena double == double
tidak valid dan ilegal di sebagian besar kompiler. Aritmatika floating point dan penyimpanan floating point berarti bahwa dua nilai dapat sama dalam nilai perkiraan , tetapi tidak sama dalam nilai yang disimpan meskipun secara rumus keduanya sama. Anda seharusnya melakukan di abs(double_a - double_b) <= epsilon
mana epsilon memiliki nilai, biasanya 1e-4
.
Anda juga dapat mencoba kode ini tanpa float (lebih cepat tetapi kurang akurat):
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;
}
Perhatikan bahwa algoritme ini menggunakan 0-255
rentangnya (bukan 0-360
) seperti yang diminta oleh penulis pertanyaan ini.
Saya menulis ini di HLSL untuk mesin rendering kami, tidak ada ketentuan di dalamnya:
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
}
Berikut adalah implementasi C berdasarkan Grafik Komputer dan Pemodelan Geometris Agoston : Implementasi dan Algoritma p. 304, dengan H ∈ [0, 360] dan 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;
}
ini seharusnya ada di sini: tetap berfungsi. Dan itu terlihat bagus dibandingkan dengan yang di atas.
kode 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 adalah tipe data vektor3 presisi 16 bit, yaitu float3 hue () mengembalikan jenis data (x, y, z) mis. (r, g, b), setengahnya sama dengan presisi setengah, 8bit, float4 adalah (r, g, b, a) 4 nilai.
half
, half4
, half3
, float3
, dan sebagainya.
Jawaban @ fins memiliki masalah melimpah di Arduio saat Anda menurunkan saturasi. Ini dia dengan beberapa nilai yang diubah menjadi int untuk mencegahnya.
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;
}
Ini bukan C, tapi pasti berhasil. Semua metode lain yang saya lihat di sini bekerja dengan membungkus semuanya menjadi bagian-bagian segi enam, dan mendekati "sudut" dari itu. Dengan memulai dengan persamaan berbeda menggunakan cosinus, dan menyelesaikan hs dan v, Anda mendapatkan hubungan yang jauh lebih baik antara hsv dan rgb, dan tweening menjadi lebih mulus (dengan biaya yang jauh lebih lambat).
Asumsikan semuanya floating point. Jika rg dan b dari 0 ke 1, h dari 0 ke 2pi, v dari 0 ke 4/3, dan s dari 0 ke 2/3.
Kode berikut ini ditulis dalam Lua. Ini mudah diterjemahkan ke dalam hal lain.
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 versi berdasarkan jawaban Patapom:
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
}
Saya bukan pengembang C ++ jadi saya tidak akan memberikan kode. Tetapi saya dapat memberikan algoritma hsv2rgb sederhana (rgb2hsv di sini ) yang saat ini saya temukan - saya memperbarui wiki dengan deskripsi: HSV dan HLS . Perbaikan utamanya adalah saya mengamati r, g, b dengan cermat sebagai fungsi rona dan memperkenalkan fungsi bentuk yang lebih sederhana untuk mendeskripsikannya (tanpa kehilangan akurasi). Algoritma - pada input kita memiliki: h (0-255), s (0-255), v (0-255)
r = 255*f(5), g = 255*f(3), b = 255*f(1)
Kami menggunakan fungsi f yang dijelaskan sebagai berikut
f(n) = v/255 - (v/255)*(s/255)*max(min(k,4-k,1),0)
dimana (mod dapat mengembalikan bagian pecahan; k adalah bilangan floating point)
k = (n+h*360/(255*60)) mod 6;
min(k,4-k,1)
. Mengapa ada tiga nilai dan apa yang sebenarnya terjadi di sini? Terima kasih sebelumnya!
Berikut adalah konverter online dengan artikel setelah menjelaskan semua algoritma untuk konversi warna.
Anda mungkin lebih suka versi C yang sudah jadi tetapi tidak akan lama untuk diterapkan dan ini bisa membantu orang lain mencoba melakukan hal yang sama dalam bahasa lain atau dengan ruang warna lain.
Tautan ini memiliki rumus untuk apa yang Anda inginkan. Maka itu masalah kinerja (teknik numerik) jika Anda menginginkannya dengan cepat.
Inilah yang baru saja saya tulis pagi ini berdasarkan matematika yang hampir sama seperti di atas:
/* 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);
}
Saya membuat implementasi yang mungkin lebih cepat dengan menggunakan rentang 0-1 untuk RGBS dan V dan rentang 0-6 untuk Hue (menghindari pembagian), dan mengelompokkan kasus menjadi dua kategori:
#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;
}
}
}
}
Untuk rentang 0-255 cukup * 255.0f + 0.5f dan tetapkan ke karakter unsigned (atau bagi dengan 255.0 untuk mendapatkan yang sebaliknya).
// 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)));
}