Rich Newman

HSLColor Class

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace ColorDemo
{
    public class HSLColor
    {
        // Private data members below are on scale 0-1
        // They are scaled for use externally based on scale
        private double hue = 1.0;
        private double saturation = 1.0;
        private double luminosity = 1.0;

        private const double scale = 240.0;

        public double Hue
        {
            get { return hue * scale; }
            set { hue = CheckRange(value / scale); }
        }
        public double Saturation
        {
            get { return saturation * scale; }
            set { saturation = CheckRange(value / scale); }
        }
        public double Luminosity
        {
            get { return luminosity * scale; }
            set { luminosity = CheckRange(value / scale); }
        }

        private double CheckRange(double value)
        {
            if (value < 0.0)
                value = 0.0;
            else if (value > 1.0)
                value = 1.0;
            return value;
        }

        public override string ToString()
        {
            return String.Format("H: {0:#0.##} S: {1:#0.##} L: {2:#0.##}"HueSaturationLuminosity);
        }

        public string ToRGBString()
        {
            Color color = (Color)this;
            return String.Format("R: {0:#0.##} G: {1:#0.##} B: {2:#0.##}"color.Rcolor.Gcolor.B);
        }

        #region Casts to/from System.Drawing.Color
        public static implicit operator Color(HSLColor hslColor)
        {
            double r = 0, g = 0, b = 0;
            if (hslColor.luminosity != 0)
            {
                if (hslColor.saturation == 0)
                    r = g = b = hslColor.luminosity;
                else
                {
                    double temp2 = GetTemp2(hslColor);
                    double temp1 = 2.0 * hslColor.luminosity - temp2;

                    r = GetColorComponent(temp1temp2hslColor.hue + 1.0 / 3.0);
                    g = GetColorComponent(temp1temp2hslColor.hue);
                    b = GetColorComponent(temp1temp2hslColor.hue - 1.0 / 3.0);
                }
            }
            return Color.FromArgb((int)(255 * r), (int)(255 * g), (int)(255 * b));
        }

        private static double GetColorComponent(double temp1double temp2double temp3)
        {
            temp3 = MoveIntoRange(temp3);
            if (temp3 < 1.0 / 6.0)
                return temp1 + (temp2 - temp1) * 6.0 * temp3;
            else if (temp3 < 0.5)
                return temp2;
            else if (temp3 < 2.0 / 3.0)
                return temp1 + ((temp2 - temp1) * ((2.0 / 3.0) - temp3) * 6.0);
            else
                return temp1;
        }
        private static double MoveIntoRange(double temp3)
        {
            if (temp3 < 0.0)
                temp3 += 1.0;
            else if (temp3 > 1.0)
                temp3 -= 1.0;
            return temp3;
        }
        private static double GetTemp2(HSLColor hslColor)
        {
            double temp2;
            if (hslColor.luminosity < 0.5)  //<=??
                temp2 = hslColor.luminosity * (1.0 + hslColor.saturation);
            else
                temp2 = hslColor.luminosity + hslColor.saturation - (hslColor.luminosity * hslColor.saturation);
            return temp2;
        }

        public static implicit operator HSLColor(Color color)
        {
            HSLColor hslColor = new HSLColor();
            hslColor.hue = color.GetHue() / 360.0; // we store hue as 0-1 as opposed to 0-360 
            hslColor.luminosity = color.GetBrightness();
            hslColor.saturation = color.GetSaturation();
            return hslColor;
        }
        #endregion

        public void SetRGB(int redint greenint blue)
        {
            HSLColor hslColor = (HSLColor)Color.FromArgb(redgreenblue);
            this.hue = hslColor.hue;
            this.saturation = hslColor.saturation;
            this.luminosity = hslColor.luminosity;
        }

        public HSLColor() { }
        public HSLColor(Color color)
        {
            SetRGB(color.Rcolor.Gcolor.B);
        }
        public HSLColor(int redint greenint blue)
        {
            SetRGB(redgreenblue);
        }
        public HSLColor(double huedouble saturationdouble luminosity)
        {
            this.Hue = hue;
            this.Saturation = saturation;
            this.Luminosity = luminosity;
        }

    }
}

22 Comments »

  1. Awesome! Just what I was looking for, Thanks!

    Comment by Felix D. — June 19, 2009 @ 8:54 pm

  2. Perfect just what i was looking for, thanks !!

    Comment by Boudewijn — May 21, 2010 @ 12:49 pm

  3. Many thanks, just I was looking for.

    Comment by Rytis Ūsalis — August 4, 2010 @ 8:29 pm

  4. Brilliant thanks for sharing! Got to wonder why this isn’t built into .NET – or don’t .NET developers care about colors?

    Comment by Luke Sampson — November 18, 2010 @ 7:38 am

  5. Thanks for this.

    @LukeSampson – haven’t you ever created a windows forms application?

    Comment by David kemp — March 7, 2011 @ 12:09 pm

  6. Rich,

    Thank you very much. This code has been a godsend. I really wish that there was something native in .NET…

    One minor point – in the cast from color to HSLColor you are directly casting the RGB values to int, which is truncating them (253.99 -> 253). So, if you do a round-trip conversion of a color to an HSLColor and back, you might get a different color. I discovered this when I started with “#fefffe” translated to HSL and back to HTML which came back as “#fdfffd”.

    The correct code should be:

    return Color.FromArgb(
    Convert.ToInt32(255 * r),
    Convert.ToInt32(255 * g),
    Convert.ToInt32(255 * b));

    Thanks again posting. Very much appreciated.

    Comment by Steve Holmes — April 8, 2011 @ 7:13 pm

    • I don’t think this is accurate either. I’m having the same problem, but the color is hsl(210,83,48) which is a blue comes back in rgb as a red in both the OP and your correction. :/

      Comment by jstafford — October 16, 2014 @ 12:19 am

      • I think your problem is that you are using the standard scale for S and L of 0 – 100, while this program (and Microsoft) use a scale of 0 – 240 for S and L.

        Comment by renniepet — November 17, 2014 @ 3:39 am

    • Steve, thanks for this fix, and thanks to Rich Newman for the original series of articles. I’ve seen a couple of similar programs on the Internet, and they all seem to share this same bug.

      Comment by renniepet — November 17, 2014 @ 3:37 am

  7. Fantastic work, my humble contrib

    Hey you can remove the setRGB method by using constructor chaining

    public HslColor(Color Color) : this(Color.R, Color.G, Color.B)
    {
    }

    public HslColor(int Red, int Green, int Blue)
    {
    HslColor HslColor = Color.FromArgb(Red, Green, Blue);
    _Hue = HslColor._Hue;
    _Saturation = HslColor._Saturation;
    _Luminosity = HslColor._Luminosity;
    }

    Comment by Chandru — June 7, 2011 @ 8:31 am

  8. And you can remove the 4 redundant else keyword in GetColorComponent() and use the objectinitializer in Color to HSLColor implicit conversion method

    Comment by Chandru — June 7, 2011 @ 8:39 am

  9. Great, thanks! I need this for random hues with the same sat/lum.

    Comment by Frankie Bagnardi — August 20, 2011 @ 12:25 am

  10. I spoke too soon. It doesn’t seem to play nicely with XNA.

    Comment by Frankie Bagnardi — August 20, 2011 @ 12:28 am

  11. Great stuff, work like a charm, I was taken aback by the lack of functionality of the .NET color class. And HSV is just a much more intuitive color space!

    Comment by Simon Ejsing — August 30, 2011 @ 10:20 am

  12. I seem to be having problems getting certain colors to show up as anything but white.

    Here’s an example (scale in hslcolor is 360):

    System.Drawing.Color[] colorArray = new System.Drawing.Color[19];
    for (int hue = 0; hue < 19; hue++)
    {
    colorArray[hue] = (System.Drawing.Color)new HSLColor(hue * 20, 0.25 * 360, 1.0 * 360);
    }

    Every single one of these colors will come out as RGB(255,255,255)

    Comment by dewyroad — March 10, 2012 @ 10:11 am

  13. Please what “value” represents in this code?

    Comment by Anonymous — March 10, 2012 @ 4:47 pm

  14. Can this be used to change the color of some part of the image? can anybody guide me how?

    Comment by prashanth — May 22, 2012 @ 11:24 am

  15. Hi guys,Can this be used to change the color of some part of the image? can anybody guide me how?

    Comment by prashanth — May 22, 2012 @ 11:24 am

  16. Thanks for sharing this code. It worked fine with a gradient coloring test app I made. I need to know, are there any license terms and conditions this listing is under?
    Thanks!

    Petar T.

    Comment by Petar Todorov — December 4, 2012 @ 5:12 pm

  17. Petar – No licensing terms apply, you are free to use how you want.

    Comment by richnewman — December 16, 2012 @ 2:09 am

  18. Does anyone have a VB version of this?

    Comment by Adam — March 12, 2013 @ 6:40 am

  19. How can I use this for changing a label background color in WPF?

    Comment by Brian — November 13, 2013 @ 10:13 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Shocking Blue Green Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 83 other followers

%d bloggers like this: