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;
    }
    else
    {
        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.