:: The story of a lone developer's quest to build an online world
:: MMO programming, design, and industry commentary
[Miranda] [Blog] [Gallery] [About]

The One Man MMO Project: Learning More Weird Stuff - HSL Color Space

By Robert Basler on 2012-08-19 16:57:02
Homepage: www.onemanmmo.com email:one at onemanmmo dot com

I needed to have a way to take a color and change its brightness but to be sure that the color would remain true as I adjusted the brightness. I've used RGB a lot over the years so I was familiar with scaling the color components by a value, but I wasn't sure that technique would retain the color properly or that it would let me use the full brightness range available.

Researching the problem, I quickly found this Wikipedia article with a description of the HSL (Hue, Saturation and Lightness) representation of color and an algorithm for how to convert to and from RGB. The HSL color space looks like this:

197px-HSL_color_solid_cylinder_alpha_lowgamma.png


You'll note that one of the axes is lightness -- just what I wanted. And it works like a charm.

Here's my code to convert from RGBA to HSLA based on the algorithm described. It uses a bunch of types and functionality specific to my game, but it should be easier to adapt than the algorithm alone. ColorElement is an unsigned 16-bit int, PackedColor is a 64-bit combination of RGBA, float64 is double (should rename that at some point.) Note that hue doesn't work for greys, so you may have problems if you try to use this with greys - I never tested that.


void GetHSLA( PackedColor color, float64& hue, float64& saturation, float64& lightness, ColorElement& alpha )
{
ColorElement R = GetRed( color );
ColorElement G = GetGreen( color );
ColorElement B = GetBlue( color );
ColorElement M = max( max ( R, G ), B );
ColorElement m = min( min ( R, G ), B );
ColorElement C = M - m;
// Does not work for grays.
if ( C == 0 )
{
hue = 0.0;
}
else if ( M == R )
{
hue = 60.0 * Math::FMod( static_cast< float64 >( G - B ) / static_cast< float64 >( C ), 6.0 );
}
else if ( M == G )
{
hue = 60.0 * ( static_cast< float64 >( B - R ) / static_cast< float64 >( C ) + 2.0 );
}
else if ( M == B )
{
hue = 60.0 * ( static_cast< float64 >( R - G ) / static_cast< float64 >( C ) + 4.0 );
}
lightness = ( static_cast< float64 >( M ) + static_cast< float64 >( m ) ) / 2.0 / 65535.0;
saturation = ( static_cast< float64 >( C ) / 65535.0 ) / ( 1.0 - Math::Abs( 2.0 * lightness - 1.0 ) );
alpha = GetAlpha( color );
}

PackedColor SetHSLA( float64 hue, float64 saturation, float64 lightness, ColorElement alpha )
{
float64 Hp = hue / 60.0;
float64 C = ( 1.0 - Math::Abs( 2.0 * lightness - 1.0 ) ) * saturation;
float64 X = C * ( 1.0 - Math::Abs( Math::FMod( Hp, 2.0 ) - 1 ) );
float64 m = lightness - ( C / 2.0 );
float64 R = 0.0;
float64 G = 0.0;
float64 B = 0.0;
if ( ( 0.0 <= Hp ) && ( Hp < 1.0 ) )
{
R = C;
G = X;
B = 0.0;
}
else if ( ( 1.0 <= Hp ) && ( Hp < 2.0 ) )
{
R = X;
G = C;
B = 0.0;
}
else if ( ( 2.0 <= Hp ) && ( Hp < 3.0 ) )
{
R = 0.0;
G = C;
B = X;
}
else if ( ( 3.0 <= Hp ) && ( Hp < 4.0 ) )
{
R = 0.0;
G = X;
B = C;
}
else if ( ( 4.0 <= Hp ) && ( Hp < 5.0 ) )
{
R = X;
G = 0.0;
B = C;
}
else if ( ( 5.0 <= Hp ) && ( Hp < 6.0 ) )
{
R = C;
G = 0.0;
B = X;
}
return SetPackedColor( static_cast< ColorElement >( ( R + m ) * 65535.0 ), static_cast< ColorElement >( ( G + m ) * 65535.0 ), static_cast< ColorElement >( ( B + m ) * 65535.0 ), alpha );
}

Add New Comment to this Topic

Admin Log In