My blog has been moved to ariya.ofilabs.com.

Wednesday, July 02, 2008

Converting between HSL and HSV

Amazingly, tons of code fragments on how to convert between RGB and HSV as well as between RGB and HSL do exist (in any imagineable programming languages). However, googling for HSL to HSV conversion did not reveal anything useful. Maybe I was blind, but there is really no point wasting minutes for something that (should be trivial). A quick glance at the wikipedia article on HSL and HSV gave me the following code:

 1 void hsv_to_hsl(double h, double s, double v,
 2 double* hh, double* ss, double *ll)
 3 {
 4     *hh = h;
 5     *ll = (2 - s) * v;
 6     *ss = s * v;
 7     *ss /= (*ll <= 1) ? (*ll) : 2 - (*ll);
 8     *ll /= 2;
 9 }
10
11 void hsl_to_hsv(double hh, double ss, double ll,
12 double* h, double* s, double *v)
13 {
14     *h = hh;
15     ll *= 2;
16     ss *= (ll <= 1) ? ll : 2 - ll;
17     *v = (ll + ss) / 2;
18     *s = (2 * ss) / (ll + ss);
19 }

Error checking is left as an exercise to the reader, corrections are welcomed. If the code seems to be cryptic, then there is a reason to take a napkin and jot some stuff there...

15 comments:

Anonymous said...

MAybe you should use 2.0 instead of 2 (and similar with other literals)
IIRC otherwise the literal can be taken as an integer, and then the result will be an integer, too. Maybe it does work here, but later on, maybe this will result in an difficult to find error.

Gunni

Ariya Hidayat said...

@Gunni: it's a floating point operation, so the result should stay floating point (otherwise, something weird with the compiler)

Kevin Kofler said...

The trailing zero is actually redundant, C/C++ also accepts just 1. and 2. without the zero.

Stephen Morley said...

Thank you so much for this algorithm. It's amazing that the information doesn't seem to be anywhere else on the internet - just dozens of pages saying to convert to RGB and then convert back.

Stephen Morley said...

There seems to be little point in doubling ll in the second algorithm. This functions identically and is simpler:

*h = hh;
ss *= (ll <= 0.5) ? ll : 1 - ll;
*v = ll + ss;
*s = 2 * ss / (ll + ss);

Ariya Hidayat said...

@Safalra: ll doubling is not necessary, but it keeps the symmetry (with the first function).

Stephen Morley said...

I've used these algorithms (in their less symmetrical form) in my JavaScript Colour functions:

http://www.safalra.com/web-design/javascript/colour-handling-and-processing/

Thanks so much for providing these algorithms - they reduced the hassle of writing the code.

Stephen Morley said...

Note that two special cases need to be handled to avoid a division by zero error: when the V or L values are 0.

Anonymous said...

The second one doesn't seem to be correct: HSL's saturation doesn't influence the result at all (since it gets overwriten)...

Ariya Hidayat said...

@Lea: no, it is not wrong. Hint: read the code more carefully :-)

Anonymous said...

'...no point wasting minutes for something that (should be trivial).'
I totally agree with you. And also:

if (trivial_problem && buggy_code) bad_programmer = true;
if (bad_programmer)
debug();

I copy pasted your code and got an error (or does your machine divide by zero?). I had to waste time. Please stop posting crap that somehow show's up high in Google.

Ariya Hidayat said...

@Anonymous: Can't you read? Which part of "Error checking is left as an exercise to the reader" is not clear to you? And if this code is so trivial, why did you copy+paste blindly instead of solving it yourself?

Anonymous said...

Sorry for my blunt reaction, I was kind of on a tight schedule working on an important project. In quick need for such a piece of code, I incorrectly assumed that your code was working correctly. Unfortunately I ended up studying the wiki article and writing up basically the same code you did, with an additional omission of the zero division occurence. What I meant to say is that you should update the code to cope with the division by zero, because based on the title and description above the code and the fact that you bothered to put it online, one would generally assume that it's a fully working piece of code (which it almost is).
And something trivial can still be quite labourous, so why reinvent the wheel. Would you rather use a pen instead of a Xerox-machine when copying a page from the phonebook? :)

Alan said...

Thanks for the code, it works beautifully. It's great for doing fades from white or black to a particular color.

Anonymous: there's really no excuse for being a jerk. At the very least, you could have posted your correction.

Anonymous said...

thanks for sharing,

I use it in Quartz Composer (OSX) to convert from HSV (sent from a Cocoa App) to HSL.