Choosing foreground using Luminosity Contrast Ratio

Sample Source Code

When a background color is chosen by the user, you can use a converter that calculates luminosity contrast ratio to choose the best foreground color for the background given.  In the code sample attached, I use a converter that does this getting the luminosity contrast for the background with a white foreground and a black foreground.  Whichever has the better luminosity contrast ratio, is the foreground color I use.

I got formula for the luminosity contrast ratio from the W3C Recommendation for Web Content Accessiblity Guidelines (WCAG) 2.0.  Outside of code, the formula looks like:

(L1 + 0.05) / (L2 + 0.05), where

  • L1 is the relative luminance of the lighter color
  • L2 is the relative luminance of the darker color

Next question I had was “how do I calculate relative luminance”?  The formula for this is also within the guidelines.  First of all, a definition:  relative luminance is “the relative brightness of any point in a colorspace, normalized to 0 for the darkest black and 1 for the lightest white.”

It can be calculated using the formula below:

Relative Luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B, where:

R:  If RsRGB <= 0.03928 then R = RsRGB  / 12.92 else R = ((RsRGB  + 0.055)/1.055)2.4
G:  If GsRGB <= 0.03928 then G = GsRGB  / 12.92 else G = ((GsRGB  + 0.055)/1.055)2.4
B:  If BsRGB <= 0.03928 then B = BsRGB  / 12.92 else B = ((BsRGB  + 0.055)/1.055)2.4

Ok, but another variable is introduced here, how are RsRGB, GsRGB, BsRGB each calculated?  This is a bit easier and is defined in the same area as the relative luminance formula.

  • RsRGB = R8bit/255
  • GsRGB = G8bit/255
  • BsRGB = B8bit/255

Finally, now how does all of this look in C#?

Calculate Luminosity Contrast
  1. private double LuminosityContrast(Color foreground, Color background)
  2. {
  3.     var foregroundLuminosity = RelativeLuminance(foreground.R, foreground.G, foreground.B);
  4.     var backgroundLuminosity = RelativeLuminance(background.R, background.G, background.B);
  5.  
  6.     if (foregroundLuminosity > backgroundLuminosity)
  7.     {
  8.         return (foregroundLuminosity + 0.05) / (backgroundLuminosity + 0.05);
  9.     }
  10.     else
  11.     {
  12.         return (backgroundLuminosity + 0.05) / (foregroundLuminosity + 0.05);
  13.     }
  14. }
  15.  
  16. private double RelativeLuminance(byte r, byte g, byte b)
  17. {
  18.     double rs = ((double)r / 255);
  19.     double gs = ((double)g / 255);
  20.     double bs = ((double)b / 255);
  21.     double R = 0;
  22.     double G = 0;
  23.     double B = 0;
  24.  
  25.     R = (rs <= 0.03928) ? (double)(rs / 12.92) : Math.Pow(((rs + 0.055) / 1.055), 2.4);
  26.     G = (gs <= 0.03928) ? (double)(gs / 12.92) : Math.Pow(((gs + 0.055) / 1.055), 2.4);
  27.     B = (bs <= 0.03928) ? (double)(bs / 12.92) : Math.Pow(((bs + 0.055) / 1.055), 2.4);
  28.  
  29.     return ((double)(0.2126 * R)) + ((double)(0.7152 * G)) + ((double)(0.0722 * B));
  30. }

 

And now, I can use this in my Convert method of my IValueConverter:

IValueConverter – Convert
  1. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  2. {
  3.     if (value != null && value is Color)
  4.     {
  5.         Color backgroundColor = (Color)value;
  6.  
  7.         // Calculate the luminosity constrast ratio for the given color against black and white.
  8.         var blackContrast = LuminosityContrast(Colors.Black, backgroundColor);
  9.         var whiteContrast = LuminosityContrast(Colors.White, backgroundColor);
  10.  
  11.         // return a solid color brush using either black or white, depending on which had the higher luminosity constrast ratio.
  12.         return blackContrast >= whiteContrast ? new SolidColorBrush(Colors.Black) : new SolidColorBrush(Colors.White);
  13.     }
  14.     else
  15.     {
  16.         return new SolidColorBrush(Colors.Black);
  17.     }
  18. }

 

Finally, I can (declare and) use my converter in XAML to pick the better color for use against the background the user of my application has chosen.

Using the converter
  1. <Converters:ForegroundColorFromBackgroundColorConverter x:Key="foregroundDetector" />
  2. <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
  3.            Foreground="{Binding SelectedBrush.Color, Converter={StaticResource ResourceKey=foregroundDetector}, Mode=OneWay}">
  4.     <Run FontWeight="Bold">Selected Color</Run>
  5. </TextBlock>

This entry was posted in C#, WPF and tagged , . Bookmark the permalink.

5 Responses to Choosing foreground using Luminosity Contrast Ratio

  1. Pingback: Windows Client Developer Roundup 062 for 3/7/2011 - Pete Brown's 10rem.net

  2. Pingback: Windows Client Developer Roundup 062 for 3/7/2011 · All About Computer

  3. Chris Hutchinson says:

    Brilliant! That was very useful for grid headers where the user can change the column colors.

  4. cyanos says:

    This was perfect for what I needed to accomplish. Thank you!

  5. freddy_hh says:

    very, very helpfull, thank you!

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s