Converting HSL to RGB

There seems to be 5 flavours of the “HSL”:

  • HSL: Hue, Saturation, Lightness
  • HSL: Hue, Saturation, Luminance
  • HSV: Hue, Saturation, Value
  • HSB: Hue, Saturation, Brightness
  • HSI: Hue, Saturation, Intensity

I don’t understand why there are so many variants. I like RGB.

I needed a way to convert HSL (the luminance version) to RGB. Now .NET doesn’t have a way to convert any of the variants to its built-in System.Drawing.Color structure. Although the structure offers the GetHue(), GetSaturation() and GetBrightness() functions.

Generally speaking, the hue is between 0 and 360 degrees of a colour wheel. Saturation determines “how much” of that colour is displayed. And luminance determines “how bright/dark” that colour is.

So here’s the code to convert HSL to RGB (in the form of a System.Drawing.Color struct):

public System.Drawing.Color RgbFromHsl(double Hue, double Saturation, double Luminance)
    double fChroma = (1.0 - Math.Abs(2.0 * Luminance - 1.0)) * Saturation;
    double fHue = Hue / 60.0;
    double fHueMod2 = fHue;
    while (fHueMod2 >= 2.0) fHueMod2 -= 2.0;
    double fTemp = fChroma * (1.0 - Math.Abs(fHueMod2 - 1.0));

    double fRed = 0, fGreen = 0, fBlue = 0;
    if (fHue < 1.0)
        fRed = fChroma;
        fGreen = fTemp;
        fBlue = 0;
    else if (fHue < 2.0)
        fRed = fTemp;
        fGreen = fChroma;
        fBlue = 0;
    else if (fHue < 3.0)
        fRed = 0;
        fGreen = fChroma;
        fBlue = fTemp;
    else if (fHue < 4.0)
        fRed = 0;
        fGreen = fTemp;
        fBlue = fChroma;
    else if (fHue < 5.0)
        fRed = fTemp;
        fGreen = 0;
        fBlue = fChroma;
    else if (fHue < 6.0)
        fRed = fChroma;
        fGreen = 0;
        fBlue = fTemp;
        fRed = 0;
        fGreen = 0;
        fBlue = 0;

    double fMin = Luminance - 0.5 * fChroma;
    fRed += fMin;
    fGreen += fMin;
    fBlue += fMin;

    fRed *= 255.0;
    fGreen *= 255.0;
    fBlue *= 255.0;

    int iRed = 0, iGreen = 0, iBlue = 0;
    // the default seems to be to truncate rather than round
    iRed = Convert.ToInt32(Math.Truncate(fRed));
    iGreen = Convert.ToInt32(Math.Truncate(fGreen));
    iBlue = Convert.ToInt32(Math.Truncate(fBlue));
    if (iRed < 0) iRed = 0;
    if (iRed > 255) iRed = 255;
    if (iGreen < 0) iGreen = 0;
    if (iGreen > 255) iGreen = 255;
    if (iBlue < 0) iBlue = 0;
    if (iBlue > 255) iBlue = 255;

    return System.Drawing.Color.FromArgb(iRed, iGreen, iBlue);

The algorithm is taken from the Wikipedia page on HSL and HSV. I added some checks at the end to make sure the RGB values are within 0 to 255.

About the only problem I had was the mod function. It’s not “integer modulus”, it’s “floating point modulus” (is that a thing?). So 5.5 mod 2 is 1.5 (because the remainder of 5.5/2 is 1.5). That took me a couple of hours to figure out. This is one of the few cases where thinking in programming terms instead of the mathematical terms cost me.

Remember the stride

I was reading this article by Raymond Chen where he mentioned this:

Most of the time, your code won’t care (there are just pixels out there that you aren’t using), but if you use the Get­Buffered­Paint­Bits function to obtain direct access to the bits, don’t forget to take the stride into account.

The word “stride” evoked a ton of memories where I used to track (laboriously) the dimensions of a bitmap. This was back in the days when I was fiddling with writing my own computer games and computer graphics. Specifically, I was working with the screen graphics buffer.

Double buffers, 8-bit colours and screen resolutions

What happens (happened?) is that for fast visuals, we need a decent frame rate, and 30 frames per second is fast enough to fool human eyes that we’re looking at smooth uninterrupted motion. Back in those days, computer monitors weren’t that advanced, so the double buffer trick was used. (Is it still used? I don’t know…).

The double buffer trick refers to having 2 sets of screen buffers. The first one is used to “blit” the contents onto the screen. I’m using blit as a verb too, but this will take too long to explain if I told the story of blitting too… While this blitting is being done, the pixels of the next frame is drawn on the second buffer. When the drawing of pixels is completed, this second buffer is used to blit to the screen.

When you force the program to use double buffering, the next frame is held until the buffer has the drawing completed. This is what “lag” means. The frame rate is sort of ignored, because only when the next frame is completely drawn, will the buffer contents be displayed. Usually this isn’t a problem because the pixel drawing for one frame is within 0.03333 seconds (based on 30 FPS). As you go for higher resolutions and more complex objects being drawn and more complex calculations being done (such as calculating hit points and bullet trajectories), this next-frame-drawing gets slowed down.

In code, what you have are 2 bitmaps in memory and 1 pointer (yay, pointers!). You actually point the pointer to either bitmap based on which bitmap’s contents are to be blit.

  • First bitmap on screen, second bitmap drawing next frame, point to first bitmap.
  • Second bitmap done drawing.
  • Point to second bitmap.
  • Draw next frame on first bitmap.
  • Continue till application (most probably a game) is done.

“So what’s the stride got to do with this? And what’s a stride?”

Well, the thing is, when you request a bitmap from within your code, you might not get the exact dimensions you want. What you get is, as Raymond mentioned:

it only promises that the bitmap will be at least the size you requested.

So the bitmap given to you can be larger in size. And the larger size is based on the stride. I’ve not done a whole lot of research, and the following explanation is based on what I remember from those game-developing days. Let’s say the stride is 4 bytes. This means the memory size of the bitmap given to you will be in multiples of 4. … Uh, yeah, I think that’s about it.

If you ask for a bitmap with dimensions such that the projected final size memory is not in multiples of 4, you will be given a bitmap such that it is.

This “problem” is compounded by the fact that you also have to take care of the red, green and blue bytes (3 contiguous bytes for 1 pixel). Sometimes, there’s an alpha component. There’s also the 8-bit colour, where the first 2 bits are for red, next 3 bits are for green, and the final 2 bits are for blue. Not 1 byte for each colour component. (Just FYI, green has more bits because we can differentiate more shades of green than red or blue).

This is why you might find that in graphics programming, you are advised to have dimensions in multiples of 2. For example, your texture images used for mapping onto 2D planes and 3D objects should preferably have dimensions in powers of 2.

Wait, why powers of 2, and not just multiples of 2? I believe it has something to do with the texture mapping functions, because those functions don’t work well when the bitmap/image/texture argument doesn’t have dimensions with powers of 2. This is why I prefer to use square images at 128, 256 or 512 pixels. Mipmaps were used to alleviate this, but that’s another topic…

And the final complication? “What? There’s more?!?” Yes.

The bitmap you requested in code, the one where you might have to take note of the stride? That bitmap might have a different dimension than the screen dimension of the computer monitor. Computer monitors weren’t quite “standard” back then (I’m starting to feel old…). The computer monitor also has its own stride (I’m basing this on the memories of my research. Don’t just take my word for it). This means blitting pixels from a bitmap buffer to the screen isn’t quite so straightforward.

For example, if you’re working on a screen resolution of 800 by 600 pixels (yes, I know that’s like ancient. Work with me here…), and then you ask for a bitmap to represent that. Well, you might get a bitmap with dimensions 1024 by 768 pixels. Maybe it’s because it’s more memory efficient that way. 1024 = 2^10, and 768 = 2^9 + 2^8. Wait, now we have sums of powers of 2?!? I don’t know a whole lot about these things… I’m just trying to make a computer game…

So based on the above example, you have an “extra” width of 224 pixels (1024 – 800) and “extra” height of 168 pixels (768 – 600). So even if the stride is taken note of, the computer might just decide to throw you more memory space. Just for the heck of it.

In summary…

The bitmap you request in code might have a different dimension than what you wanted. The computer monitor might have a different dimension to work with. You have to remember each pixel has a red, green, blue and even alpha component (each of which uses a byte of storage. Or not, in the case of 8-bit colours). Then you have to take note of the dimensions of the textures you’re using to map onto 2D/3D objects.

And none of that has very much to do with the “fun” part of game programming. You’re just trying to work with one image.

I hope you learnt something. Because I sure as heck don’t know what’s the point I was trying to make, other than bombard you with seemingly random pieces of information…

Colour of numbers

I was mucking around in my image editor (Paint.NET) because I was doing some CSS colour editing. While I was playing around with the HSV of the colour, I saw this in the RGB box: 314159. You know what that reminds me of? PI. No, not that pie, PI! Great, now I’m hungry…

So I wondered what numbers would look like if they had colours.

First, we have PI as 3.14159 (ratio of a circle’s circumference to its diameter)
Colour of PI

Then we have the constant e, 2.71828
Colour of e

Fibonacci and his sequence also make an appearance: 1, 1, 2, 3, 5, 8 (13, 21, 34, …)
Colour of Fibonacci sequence

We also have the golden ratio, 1.61803. It’s also the limit as the (n+1)th term divide by the nth term in the Fibonacci sequence.
Colour of golden ratio

For some reason, I remember the Avogadro constant, even though I don’t do chemistry or physics anymore… It’s 6.02214179 x 10^23. Yes, it’s a big number… The colour also reminds me of bromine gas, which is reddish-brown in colour. Wait, how come I still remember these things?
Colour of Avogadro constant

Here’s an interesting one. Absolute zero is the theoretical temperature where everything barely has energy. It’s defined as 0 Kelvin, and is equal to -273.15 degree Celsius. And you thought ice at 0 degree Celsius was cold…
Colour of Absolute Zero

Basic colour theory for programmers

Alright, this is a crash course in basic colour theory. If you’re a game programmer, or web developer or anyone who will deal with colour at some point in his programming career, what you will read here is crucial. Let’s begin.

Colour models

You will most often deal with the RGB model, where red, green and blue are the primary colours. It’s an additive model. This is the traditional “adding” of colours together to produce new colours.

There’s also the CMYK model, referring to cyan, magenta, yellow and black. K is for key colour, and black was used. It’s also a subtractive model. It’s subtractive because mixing colours together prevents certain wavelengths of light from being reflected.

[Short digression]
When you see a blue cup, what happens is only wavelengths corresponding to the blue colour is reflected off the surface of the cup. All other wavelengths are absorbed by the cup. That’s why you see blue. You might find it helpful to review your science lessons…
[End digression]

Then there’s the HSV model, referring to hue, saturation and value. Some similar notations are HSL, HSI and HSB. The difference is lightness (HSL), intensity (HSI), and brightness (HSB). There are some differences between them. We’ll concentrate on HSV here, and you’re encouraged to read up on the others.

Hue refers to the colour itself. Saturation refers to “how much” of that colour. And value is “how light” is that colour. It’s a bit hard to explain. I’ll show how this looks like in a section further down.

What’s the deal with red, YELLOW and blue then?

If I understand it correctly, it’s basically a misunderstanding (read more about RGB and RYB). RGB is used in light-related situations, such as television and computer monitors.

The RYB model is a simplification of the CMYK model (as I understand it). It’s not easy for mortals to associate cyan with blue, or magenta with red… Can you imagine teaching a 6 year old to say “magenta” instead of “red”?

The RYB (or CMYK) model is used in pigment-related situations, such as printing and painting. Our water colour and crayon art classes fall into this.

Why do you need to know this? Normally, you don’t need to. Unless you’re writing code for a printer driver. Then this difference is crucial to your understanding.

Colour representations

You might have created colour wheels in your science class or art class (I created mine in an art class). It looks like this:

Colour wheel
[original image]

Then there’s the colour cone.

Colour cone
[original image]

This is the usual HSV representation. Hue refers to the edge of the circular plane containing the “pure” colours. Saturation refers to how far the colour tint is from the centre of the circle. The closer a tint is to the centre, the whiter the tint. The closer a tint is to the edge, the more saturated the tint is with that particular “pure” colour.

Value refers to how light the colour is. Visually, it’s how close a tint is to the circular plane. The lower the value (close to point of cone), the darker the tint. The higher the value (close to circular plan), the lighter the tint.

You might find it useful to play around the colour control in Paint.NET:

HSV controls in Paint.NET

And then we have the colour cube, which might be easier to understand:

Colour cube

Red, green and blue values are used as the 3 axes, similar to our 3D X-, Y- and Z- axes.

We see more than is representable

Humans see a greater range of colours than is represented. Have a look at this:

Visible colour range
[original image]

You see that triangle there in the centre? That’s the representable range of colours. We see a whole lot more colours than our computers can represent.

Notice how much more green we can see? A possible reason is our ancestors lived in an environment surrounded by plant life (no citations… remnant from my early student days…). Maybe we needed to distinguish more variations of green…

It’s also a reason when colours are stored in 16 bits, green is given more priority. 5 bits for red, 6 bits for green and 5 bits for blue. We’ll look at this in a future post.

Colour temperature

This section deals with subjective opinions about colours. As a programmer, you’re probably not too interested in this. But if you’re a web developer, or a programmer pressed into design work, you should care about this.

So, there are warm colours and cool colours. Warm colours typically include the reds, oranges and browns. Cool colours typically include the blues, greens and greys. Warm colours incite feelings. Cool colours calm feelings.

There’s this internal web application I’m maintaining. The user interface is almost totally “warm”. The header is red. The datagrids are red. Buttons are pinkish. Oh my goodness, how can a user come to a screen like this and not get angry?

On a usability point, how can an error message be displayed so the user takes notice of it? Error messages are typically in red, or highlighted with red (or warm colour) backgrounds. Since everything is reddish, nothing stands out.

There’s no excuse for making a simple design mistake like this, even if you’re “only a programmer” and not a designer.

Colour contrast

A basic contrast principle is this: 2 colours opposite each other on the colour wheel form a contrast pair. For example, blue and orange form a contrast pair.

It doesn’t mean they form the best contrast pair. But it does give you a starting point. Orange and green is fine too (I didn’t say they’d be pretty…).

For web design, 3 base colours are usually chosen. (Sorry, I can’t find any reference on this. Purely from a vague memory in the recesses of my brain…) A rule of thumb is to choose 3 colours on the colour wheel such that they form a triangle. For example, red, yellow and blue. Or orange, green and magenta.

This way, the 3 colours form enough of a contrast to each other. But it will look terrible if every colour on a web page contrast with each other. Which brings us to…

Complementary colours

Complementary colours enhance each other. For example, red, orange and brown. Or green, blue and yellow. How do you choose them? They are next to each other on the colour wheel, give or take a couple of colour steps.

How does this work with contrast colours? Choose a predominant colour out of the 3, then choose complementary colours based on this predominant colour. Use the predominant colour and its complementary colours throughout the web page/application, and use the other 2 for contrast.

End of crash course

This has been quite long already, so I’m wrapping up. Understand the different colour models, colour representations and basic colour relations. This is ground work for future posts.

My plan is to slowly work towards 3D programming, and I needed to start off with basic colour theory. It’s going to be hard to explain diffuse colours, ambience, and specular highlights without this. Which are used in understanding 3D rendering. Which is used in games.

Exciting, right? Stay tuned.

CSS colours and hexadecimal

CSS, which stands for Cascading Style Sheets, allows you to work design wonders with your HTML. A web page is split into two parts, the design and look versus the data and functions of the page. And CSS is the main driving force behind the former. Aaron’s article gives an excellent introduction to this separation of form and function of a web page. Then stroll through the breathtaking CSS Zen Garden to gain inspiration for your CSS design project.

So why do you need to know anything about hexadecimals? Because a major component of CSS values come from declaring colours. And colours are represented in mainly hexadecimal values.

In CSS, colours are commonly represented as RGB triplets, such as #336699. RGB is a short form of Red, Green and Blue. A discussion of colour theory and representation is beyond the scope of this article. Simply know the six characters (not including the hex sign #) is divided into 3 parts. First two characters represent red, middle two for green and last two for blue.

These two-character parts are the hexadecimal values of the colour component (red or green or blue), and range from 0 to 255. The higher the number, the more of that colour component. So #0000ff represents pure blue. If it’s still new to you, rest easy, because most standard image processing software has a hexadecimal colour converter.

There are other ways of declaring colours in style sheets, such as color:Red. To a non-technical person, that is very useful. But we’re programmers. What if we need to dynamically generate the style sheet?