Integer conversion from HSL to RGB
A hue-saturation-lumination to RGB conversion that uses no floating point.
Inputs and outputs are in the range 0 to 255.
Derived from an incredibly old floating-point algorithm.
void HSL_to_RGB(int hue, int sat, int lum, int* r, int* g, int* b)
{
int v;
v = (lum < 128) ? (lum * (256 + sat)) >> 8 :
(((lum + sat) << 8) - lum * sat) >> 8;
if (v <= 0) {
*r = *g = *b = 0;
} else {
int m;
int sextant;
int fract, vsf, mid1, mid2;
m = lum + lum - v;
hue *= 6;
sextant = hue >> 8;
fract = hue - (sextant << 8);
vsf = v * fract * (v - m) / v >> 8;
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;
}
}
}
10 Comments:
Hi,
I found your code to convert HSL to RGB. I am working on to implement your code for 8bit µC.
Could you please give me description what does it mean "vsf"?
I don't know what it means. All I did was to find a floating-point algorithm and convert it to 8-bit fixed point.
Let's see. v is a fixed-point number between lum and 1.0 (=256). If sat=0 then v=lum; as you increase the saturation, the value of v increases, to a maximum of 1.0 if lum>0.5 and 2*lum if lum<0.5.
m = 2*lum-v. So if there is no saturation, m=lum. As you increase the saturation, m approaches 0 if lum<0.5. If lum>0.5 then m = lum + (lum - 1)*sat, a formula which attenuates the effect of sat as lum approaches 1. That is, if lum is near 1.0 then m is near 1.0 and sat has almost no effect. But if lum is lower, then as sat increases, the value of m decreases toward 0.
I can see that sextant is a number in the range 0..5 representing which of 6 color regions the hue represents, and fract is the fraction (0..1.0) within that region.
It looks like vsf contains a redundancy, as v * fract * (v - m) / v should be the same as fract * (v - m). It turns out that v-m = 2*(v-lum). v-lum = lum*sat if lum<0.5; v-lum = sat - lum*sat if lum>0.5. In both cases, v-lum=0 if sat=0, and as sat increases toward 1.0, v-lum increases toward lum (or a lower value if lum>0.5).
I'll leave the rest as an exercise to the reader. So there you have it. A complicated mess that eventually gives you a color.
Oh wait, I see how this works. vsf is just a common factor between mid1 and mid2. Both mid1 and mid2 have a value between v and m:
mid1 = m + fract * (v-m)
mid2 = v - fract * (v-m)
(Note that v >= m.)
As fract increases, mid1 goes from m up to v, while mid2 goes from v down to m.
Depending on which sextant the hue belongs to, the result always uses v, and m, and either mid1 or mid2. I get it now! Cool.
So in summary: v represents the MOST DOMINANT COLOR, max(r,g,b). m represents the LEAST DOMINANT COLOR, min(r,g,b). mid1 and mid2 are in between these two; as you change the hue, mid1 and mid2 are used to transition from one sextant of the "color wheel" to the next.
Thanks for your algorithm.
I'm looking for an integer conversion from RGB to HSL. Do you know any pointer towards such an algorithm ? Thanks.
Ahh yes, here is my C# code for RGB to HSL.
/// <summary>Converts a color to Hue-Saturation-Luminosity representation
/// with each component in the range 0 to 255.</summary>
/// <remarks>If the color is monochrome, the hue argument is left
/// unchanged.
///
/// Note: RgbToHsl() came from a different source than HslToRgb(). The
/// conversions probably don't match up perfectly.</remarks>
public static void RgbToHsl(Color c, ref int hue, out int sat, out int lum)
{
int r = c.R, g = c.G, b = c.B;
int min = Math.Min(Math.Min(r, g), b);
int max = Math.Max(Math.Max(r, g), b);
if (min != max)
{
if (r == max && g >= b)
hue = (g - b) * 85 / (max - min) / 2 + 0;
else if (r == max && g < b)
hue = (g - b) * 85 / (max - min) / 2 + 255;
else if (g == max)
hue = (b - r) * 85 / (max - min) / 2 + 85;
else if (b == max)
hue = (r - g) * 85 / (max - min) / 2 + 171;
}
lum = (min + max) / 2;
if (min == max)
sat = 0; // gray
else if (min + max < 256)
sat = (max - min) << 8 / (min + max);
else
sat = (max - min) << 8 / (512 - min - max);
}
This comment has been removed by the author.
Hello
Your code executed accurately on my PC. thank you.
but I need it on my avr project. I used code vision for recompling the code, then I faced a strange problem. when I send lum greater than 80 to the function, it returns red=green=blue= 0.
I don't why?
my IC is atmega88pa.
could you help me plz?
I don't know why either. Maybe your ints are 16-bit and 32-bit is required?
I have already changed int to long int.
OK.
Thanks.
YES!
I found it.:
v = (lum < 128) ? (long int)(lum * (256 + sat)) >> 8 : (((long int)(lum + sat) << 8) - (long int)lum * sat) >> 8;
Post a Comment
<< Home