Changing Color Tint with C#

I’m currently doing a web application that has some branding. I’m doing a bunch of charts on metrics and there are specific color combinations that are used with charts. Each combination, or theme, has 3 main colors and 1 tint of that color, so 6 total. If you need more colors, you can do a tint percentage of the original color. Doing this in .NET wasn’t trivial and there isn’t a whole lot of information on the net about this.

First off, what does tinting mean? Basically, if you have red and want to tint it 10% lighter, you would add 10% of white to the red.

How this is done in .NET is by taking the System.Drawing.Color and converting it’s RGB values to HSL values. You then can bump the lighting up or down a certain percent. After it’s converted back into RGB color values it will be a tint of the original color.

Converting RGB to HSL:

This is simple in .NET. The color object has 3 methods for this: GetHue(), GetSaturation(), and GetBrightness(). But wait a second here, are brightness and lighting the same thing? No, they are not. Wikipedia has a good description on the difference http://en.wikipedia.org/wiki/HSL_and_HSV. So why are we not converting into HSB instead of HSL? Apparently .NET is actually giving back lighting and not brightness. Chris Jackson has a great post on this http://blogs.msdn.com/cjacks/archive/2006/04/12/575476.aspx. His conversion from HSL to RGB is also the one I’m using in this post. So we just assume that GetBrightness() is actually giving us the lighting that we need.

Converting HSL to RGB:

Here is the tricky part. Thanks to Chris Jackson for posting this code.

public static class ColorHelper
{
    /// <summary>
    /// Converts the HSL values to a Color.
    /// </summary>
    /// <param name="alpha">The alpha.</param>
    /// <param name="hue">The hue.</param>
    /// <param name="saturation">The saturation.</param>
    /// <param name="lighting">The lighting.</param>
    /// <returns></returns>
    public static Color FromHsl( int alpha, float hue, float saturation, float lighting )
    {
        if( 0 > alpha || 255 < alpha )
        {
            throw new ArgumentOutOfRangeException( "alpha" );
        }
        if( 0f > hue || 360f < hue )
        {
            throw new ArgumentOutOfRangeException( "hue" );
        }
        if( 0f > saturation || 1f < saturation )
        {
            throw new ArgumentOutOfRangeException( "saturation" );
        }
        if( 0f > lighting || 1f < lighting )
        {
            throw new ArgumentOutOfRangeException( "lighting" );
        }

        if( 0 == saturation )
        {
            return Color.FromArgb( alpha, Convert.ToInt32( lighting * 255 ), Convert.ToInt32( lighting * 255 ), Convert.ToInt32( lighting * 255 ) );
        }

        float fMax, fMid, fMin;
        int iSextant, iMax, iMid, iMin;

        if( 0.5 < lighting )
        {
            fMax = lighting - ( lighting * saturation ) + saturation;
            fMin = lighting + ( lighting * saturation ) - saturation;
        }
        else
        {
            fMax = lighting + ( lighting * saturation );
            fMin = lighting - ( lighting * saturation );
        }

        iSextant = (int)Math.Floor( hue / 60f );
        if( 300f <= hue )
        {
            hue -= 360f;
        }
        hue /= 60f;
        hue -= 2f * (float)Math.Floor( ( ( iSextant + 1f ) % 6f ) / 2f );
        if( 0 == iSextant % 2 )
        {
            fMid = hue * ( fMax - fMin ) + fMin;
        }
        else
        {
            fMid = fMin - hue * ( fMax - fMin );
        }

        iMax = Convert.ToInt32( fMax * 255 );
        iMid = Convert.ToInt32( fMid * 255 );
        iMin = Convert.ToInt32( fMin * 255 );

        switch( iSextant )
        {
            case 1:
                return Color.FromArgb( alpha, iMid, iMax, iMin );
            case 2:
                return Color.FromArgb( alpha, iMin, iMax, iMid );
            case 3:
                return Color.FromArgb( alpha, iMin, iMid, iMax );
            case 4:
                return Color.FromArgb( alpha, iMid, iMin, iMax );
            case 5:
                return Color.FromArgb( alpha, iMax, iMin, iMid );
            default:
                return Color.FromArgb( alpha, iMax, iMid, iMin );
        }
    }
}

Now that we can do the conversion, let’s create some extension methods on Color to do lightening and darkening.

public static class ColorExtensions
{
    /// <summary>
    /// Tints the color by the given percent.
    /// </summary>
    /// <param name="color">The color being tinted.</param>
    /// <param name="percent">The percent to tint. Ex: 0.1 will make the color 10% lighter.</param>
    /// <returns>The new tinted color.</returns>
    public static Color Lighten( this Color color, float percent )
    {
        var lighting = color.GetBrightness();
        lighting = lighting + lighting * percent;
        if( lighting > 1.0 )
        {
            lighting = 1;
        }
        else if( lighting <= 0 )
        {
            lighting = 0.1f;
        }
        var tintedColor = ColorHelper.FromHsl( color.A, color.GetHue(), color.GetSaturation(), lighting );

        return tintedColor;
    }

    /// <summary>
    /// Tints the color by the given percent.
    /// </summary>
    /// <param name="color">The color being tinted.</param>
    /// <param name="percent">The percent to tint. Ex: 0.1 will make the color 10% darker.</param>
    /// <returns>The new tinted color.</returns>
    public static Color Darken( this Color color, float percent )
    {
        var lighting = color.GetBrightness();
        lighting = lighting - lighting * percent;
        if( lighting > 1.0 )
        {
            lighting = 1;
        }
        else if( lighting <= 0 )
        {
            lighting = 0;
        }
        var tintedColor = ColorHelper.FromHsl( color.A, color.GetHue(), color.GetSaturation(), lighting );

        return tintedColor;
    }
}

Now this makes it easy to create a tint of any color. To use this in a web page, we can use the System.Drawing.ColorTranslator.ToHtml( Color c ) method to give us our html color.

Leave a Reply