RGB and HSL Colour Space Conversions.
This has been updated to include VB
listings of the RGBHSL utility and the test programs.
The classic RGB
colour space used in GDI+ is excellent for choosing or defining a specific
colour as a mixture of primary
colour and intensity values but what happens if you want to take a
particular colour and make it a bit lighter or a bit darker or change its
saturation. For this you need to be able to use the HSL (Hue, Saturation
and Luminance) colour space.
The .NET framework
actually provides RGB-HSL conversion but for some reason, the conversion
of HSL to RGB is not provided as a public method. The Color class has
three methods, GetHue, GetSaturation and GetBrightness that provide the
basic components of the HSL (or HSB) colour space. The conversion of HSL
to RGB is a well known algorithm that you can find in numerous places on
the web. Oddly enough, down in the bowels of the ControlPaint class, RGB
to HSL is employed when you use the ControlPaint.Light(…) or
ControlPaint.DarkDark(…) methods but also unfortunately the scaling of
the Light, Dark and DarkDark settings are, to say the least, brutal and
offer no finesse.
A specific use for
RGB-HSL conversions I have used in the past, is to provide colours that
enhance objects drawn in a psuedo-3D or isometric style. In such a
drawing, a cube having a specific colour, say red, may be shown with a
lighter red on top to simulate a shine on the object and a darker colour
on one side to simulate a shadow. Figure1 shows two such cubes with and
without shading yet still using red as the basic colour.

Figure
1
In addition to
making a chosen colour lighter or darker, you may wish to choose a range
of colour values that all
have a similar brightness. Not an easy task where RGB is concerned but
trivial using the HSL colour space. Similarly, you may wish to choose
random colours but ensure that they have a uniform level of saturation to
ensure bright, non-pastel colours for ease of visibility. Once again, a
task eminently possible if using HSL colour definitions.
If you're
interested in further reading on the differences between colour spaces, this
link will take you to an excellent article on the Apple site which
explains them nicely.
To accompany this
FAQ entry I have provided the source code shown in listing 1. It provides
an RGB-HSL and HSL-RGB conversion plus a few methods that assist in
setting or modifying a brightness.
Listing 1:
C#.
/* This tool is part of the xRay Toolkit and is provided free of
charge by Bob Powell.
* This code is not
guaranteed to be free from defects or fit for merchantability in any way.
* By using this tool
in your own programs you agree to hold Robert W. Powell free from all
* damages direct or
incidental that arise from such use.
* You may use this
code free of charge in your own projects on condition that you place the
* following paragraph
(enclosed in quotes below) in your applications help or about dialog.
* "Portions of
this code provided by Bob Powell. http://www.bobpowell.net"
*/
using System;
using System.Drawing;
namespace xRay.Toolkit.Utilities
{
public class
RGBHSL
{
public class
HSL
{
public
HSL()
{
_h=0;
_s=0;
_l=0;
}
double
_h;
double
_s;
double
_l;
public
double H
{
get{return
_h;}
set
{
_h=value;
_h=_h>1
? 1 : _h<0 ? 0 : _h;
}
}
public
double S
{
get{return
_s;}
set
{
_s=value;
_s=_s>1
? 1 : _s<0 ? 0 : _s;
}
}
public
double L
{
get{return
_l;}
set
{
_l=value;
_l=_l>1
? 1 : _l<0 ? 0 : _l;
}
}
}
public RGBHSL()
{
}
///
<summary>
///
Sets the absolute brightness of a colour
///
</summary>
///
<param name="c">Original
colour</param>
///
<param name="brightness">The
luminance level to impose</param>
///
<returns>an
adjusted colour</returns>
public static
Color SetBrightness(Color c, double
brightness)
{
HSL hsl = RGB_to_HSL(c);
hsl.L=brightness;
return
HSL_to_RGB(hsl);
}
///
<summary>
///
Modifies an existing brightness level
///
</summary>
///
<remarks>
///
To reduce brightness use a number smaller than 1. To increase brightness
use a number larger tnan 1
///
</remarks>
///
<param name="c">The
original colour</param>
///
<param name="brightness">The
luminance delta</param>
///
<returns>An
adjusted colour</returns>
public static
Color ModifyBrightness(Color c, double
brightness)
{
HSL hsl = RGB_to_HSL(c);
hsl.L*=brightness;
return
HSL_to_RGB(hsl);
}
///
<summary>
///
Sets the absolute saturation level
///
</summary>
///
<remarks>Accepted
values 0-1</remarks>
///
<param name="c">An
original colour</param>
///
<param name="Saturation">The
saturation value to impose</param>
///
<returns>An
adjusted colour</returns>
public static
Color SetSaturation(Color c, double
Saturation)
{
HSL hsl = RGB_to_HSL(c);
hsl.S=Saturation;
return
HSL_to_RGB(hsl);
}
///
<summary>
///
Modifies an existing Saturation level
///
</summary>
///
<remarks>
///
To reduce Saturation use a number smaller than 1. To increase Saturation
use a number larger tnan 1
///
</remarks>
///
<param name="c">The
original colour</param>
///
<param name="Saturation">The
saturation delta</param>
///
<returns>An
adjusted colour</returns>
public static
Color ModifySaturation(Color c, double
Saturation)
{
HSL hsl = RGB_to_HSL(c);
hsl.S*=Saturation;
return
HSL_to_RGB(hsl);
}
///
<summary>
///
Sets the absolute Hue level
///
</summary>
///
<remarks>Accepted
values 0-1</remarks>
///
<param name="c">An
original colour</param>
///
<param name="Hue">The
Hue value to impose</param>
///
<returns>An
adjusted colour</returns>
public static
Color SetHue(Color c, double Hue)
{
HSL hsl = RGB_to_HSL(c);
hsl.H=Hue;
return
HSL_to_RGB(hsl);
}
///
<summary>
///
Modifies an existing Hue level
///
</summary>
///
<remarks>
///
To reduce Hue use a number smaller than 1. To increase Hue use a number
larger tnan 1
///
</remarks>
///
<param name="c">The
original colour</param>
///
<param name="Hue">The
Hue delta</param>
///
<returns>An
adjusted colour</returns>
public static
Color ModifyHue(Color c, double
Hue)
{
HSL hsl = RGB_to_HSL(c);
hsl.H*=Hue;
return
HSL_to_RGB(hsl);
}
///
<summary>
///
Converts a colour from HSL to RGB
///
</summary>
///
<remarks>Adapted
from the algoritm in Foley and Van-Dam</remarks>
///
<param name="hsl">The
HSL value</param>
///
<returns>A
Color structure containing the equivalent RGB values</returns>
public static
Color HSL_to_RGB(HSL hsl)
{
double
r=0,g=0,b=0;
double
temp1,temp2;
if(hsl.L==0)
{
r=g=b=0;
}
else
{
if(hsl.S==0)
{
r=g=b=hsl.L;
}
else
{
temp2
= ((hsl.L<=0.5) ? hsl.L*(1.0+hsl.S) : hsl.L+hsl.S-(hsl.L*hsl.S));
temp1
= 2.0*hsl.L-temp2;
double[]
t3=new double[]{hsl.H+1.0/3.0,hsl.H,hsl.H-1.0/3.0};
double[]
clr=new double[]{0,0,0};
for(int
i=0;i<3;i++)
{
if(t3[i]<0)
t3[i]+=1.0;
if(t3[i]>1)
t3[i]-=1.0;
if(6.0*t3[i]
< 1.0)
clr[i]=temp1+(temp2-temp1)*t3[i]*6.0;
else
if(2.0*t3[i] < 1.0)
clr[i]=temp2;
else
if(3.0*t3[i] < 2.0)
clr[i]=(temp1+(temp2-temp1)*((2.0/3.0)-t3[i])*6.0);
else
clr[i]=temp1;
}
r=clr[0];
g=clr[1];
b=clr[2];
}
}
return
Color.FromArgb((int)(255*r),(int)(255*g),(int)(255*b));
}
//
///
<summary>
///
Converts RGB to HSL
///
</summary>
///
<remarks>Takes
advantage of whats already built in to .NET by using the Color.GetHue,
Color.GetSaturation and Color.GetBrightness methods</remarks>
///
<param name="c">A
Color to convert</param>
///
<returns>An
HSL value</returns>
public static
HSL RGB_to_HSL (Color c)
{
HSL hsl =
new HSL();
hsl.H=c.GetHue()/360.0; //
we store hue as 0-1 as opposed to 0-360
hsl.L=c.GetBrightness();
hsl.S=c.GetSaturation();
return
hsl;
}
}
}
VB
' This tool is part of the xRay Toolkit and is provided free of charge by
Bob Powell.
' This code is not guaranteed to be free from defects or fit for
mechantability in any way.
' By using this tool in your own programs you agree to hold Robert W.
Powell free from all
' damages direct or incidental that arise from such use.
' You may use this code free of charge in your own projects on condition
that you place the
' following paragraph (enclosed in quotes below) in your applications help
or about dialog.
' "Portions of this code provided by Bob Powell. Http://www.bobpowell.net"
Imports
System
Imports
System.Drawing
Namespace
xRay.Toolkit.Utilities
Public Class
RGBHSL
Public
Class HSL
Public
Sub New()
_h = 0
_s = 0
_l = 0
End
Sub 'New
Private
_h As Double
Private
_s As Double
Private
_l As Double
Public
Property H() As
Double
Get
Return _h
End
Get
Set
_h = value
_h = IIf(_h > 1, 1, IIf(_h < 0, 0, _h))
End
Set
End
Property
Public
Property S() As
Double
Get
Return _s
End
Get
Set
_s = value
_s = IIf(_s > 1, 1, IIf(_s < 0, 0, _s))
End
Set
End
Property
Public
Property L() As
Double
Get
Return _l
End
Get
Set
_l = value
_l = IIf(_l > 1, 1, IIf(_l < 0, 0, _l))
End
Set
End
Property
End
Class 'HSL
Public
Sub New()
End
Sub 'New
'/
<summary>
'/
Sets the absolute brightness of a colour
'/
</summary>
'/
<param name="c">Original colour</param>
'/
<param name="brightness">The luminance level to impose</param>
'/
<returns>an adjusted colour</returns>
Public
Shared Function
SetBrightness(c As Color, brightness
As Double)
As Color
Dim
hsl As HSL = RGB_to_HSL(c)
hsl.L
= brightness
Return
HSL_to_RGB(hsl)
End
Function 'SetBrightness
'/
<summary>
'/
Modifies an existing brightness level
'/
</summary>
'/
<remarks>
'/
To reduce brightness use a number smaller than 1. To increase brightness
use a number larger tnan 1
'/
</remarks>
'/
<param name="c">The original colour</param>
'/
<param name="brightness">The luminance delta</param>
'/
<returns>An adjusted colour</returns>
Public
Shared Function
ModifyBrightness(c As Color, brightness
As Double)
As Color
Dim
hsl As HSL = RGB_to_HSL(c)
hsl.L
*= brightness
Return
HSL_to_RGB(hsl)
End
Function 'ModifyBrightness
'/
<summary>
'/
Sets the absolute saturation level
'/
</summary>
'/
<remarks>Accepted values 0-1</remarks>
'/
<param name="c">An original colour</param>
'/
<param name="Saturation">The saturation value to impose</param>
'/
<returns>An adjusted colour</returns>
Public
Shared Function
SetSaturation(c As Color, Saturation
As Double)
As Color
Dim
hsl As HSL = RGB_to_HSL(c)
hsl.S
= Saturation
Return
HSL_to_RGB(hsl)
End
Function 'SetSaturation
'/
<summary>
'/
Modifies an existing Saturation level
'/
</summary>
'/
<remarks>
'/
To reduce Saturation use a number smaller than 1. To increase Saturation
use a number larger tnan 1
'/
</remarks>
'/
<param name="c">The original colour</param>
'/
<param name="Saturation">The saturation delta</param>
'/
<returns>An adjusted colour</returns>
Public
Shared Function
ModifySaturation(c As Color, Saturation
As Double)
As Color
Dim
hsl As HSL = RGB_to_HSL(c)
hsl.S
*= Saturation
Return
HSL_to_RGB(hsl)
End
Function 'ModifySaturation
'/
<summary>
'/
Sets the absolute
Hue level
'/
</summary>
'/
<remarks>Accepted values 0-1</remarks>
'/
<param name="c">An original colour</param>
'/
<param name="Hue">The
Hue value to impose</param>
'/
<returns>An adjusted colour</returns>
Public
Shared Function
SetHue(c As Color,
Hue As
Double) As
Color
Dim
hsl As HSL = RGB_to_HSL(c)
hsl.H
= Hue
Return
HSL_to_RGB(hsl)
End
Function 'SetHue
'/
<summary>
'/
Modifies an existing
Hue level
'/
</summary>
'/
<remarks>
'/
To reduce
Hue use a number smaller than 1. To increase
Hue
use a number larger tnan 1
'/
</remarks>
'/
<param name="c">The original colour</param>
'/
<param name="Hue">The
Hue delta</param>
'/
<returns>An adjusted colour</returns>
Public |