18

I don't know much about color composition, so I came up with this algorithm that will pick a background color based on the font color on a trial an errors basis:

public class BackgroundFromForegroundColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is Color))
            return value;
        Color color = (Color)value;
        if (color.R + color.G + color.B > 550)
            return new SolidColorBrush(Colors.Gray);
        else if (color.R + color.G + color.B > 400)
            return new SolidColorBrush(Colors.LightGray);
        else
            return new SolidColorBrush(Colors.White);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I did some googling about this, but I haven't found anything very formal about the different ways a background color can be calculated to get a good contrast effect with the font color.

So my question is: is there a more "formal" approach to pick a good background to get a good contrast? Alternatively, how would you handle picking a background color with the sole intent of having your text as readable as possible whatever its font color?

Quick update

A bit more context: I'm simply trying to show a preview of some text (eg "The quick brown fox jumps over the lazy dog") where the user picks the font color, weight, font, etc. I am however interested to see what can be done, whether it's super simple, or more complex.

Final edit

I decided to go with what H.B. suggested: it seems to work fine with all colors I tried unlike with my previous algorithm were the foreground would not always contrast properly with the background. I would've been curious to see if there is formula that gives you an "optimal" background for a given foreground, but for what I need black/white works just fine. This is my code in its current form:

public class BackgroundFromForegroundColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is Color))
            return value;
        Color color = (Color)value;
        double Y = 0.2126 * color.ScR + 0.7152 * color.ScG + 0.0722 * color.ScB;
        return Y > 0.4 ? Brushes.Black : Brushes.White;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
  • 1
    Similar ground covered here: [Make foregroundcolor black or white depending on background](http://stackoverflow.com/questions/2241447/make-foregroundcolor-black-or-white-depending-on-background), [Good text foreground color for a given background color](http://stackoverflow.com/questions/946544/good-text-foreground-color-for-a-given-background-color) and [What are your favorite extension methods for C#?](http://stackoverflow.com/questions/271398/what-are-your-favorite-extension-methods-for-c-codeplex-com-extensionoverflow/6031710#6031710) – takrl Jul 20 '11 at 14:52
  • As a side note, the `ConvertBack` in one way converters should throw a `NotSupportedException` since there will be no implementation. – H.B. Jul 20 '11 at 15:00

4 Answers4

22

There are some methods of calculating the brightness of a colour, based on that you could just take a black or white background and you would get a decent readability. See luma for example

Y = 0.2126 R + 0.7152 G + 0.0722 B

I think the threshold would be 0.5 if you use normalized input values (0.0 - 1.0), but it's been a while since i used this...

Edit: Example convert implementation sketch:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    var c = (Color)value;
    var l = 0.2126 * c.ScR + 0.7152 * c.ScG + 0.0722 * c.ScB;

    return l < 0.5 ? Brushes.White : Brushes.Black;
}

The threshold may actually be a bit dependent on the display and personal preference, i for one would prefer something lower resulting in a bigger share of black backgrounds.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Choosing black or white seems like it would work; now using the luma formula as you posted it, Y will vary between 0 and 255; any idea about where I would draw the line in the sand? –  Jul 20 '11 at 14:28
  • I think I understand what you mean with 0.5: 0.5*255=127.5; if > then use black background, otherwise use white. AFAICT with a series of random colors, it seems to work great! –  Jul 20 '11 at 14:34
  • 1
    That is one way to look at it (you could also divide all the channels by 255.0, or the result); it does work, that is why i used it before (two years ago). – H.B. Jul 20 '11 at 14:42
  • You might also want to test a few thresholds to see which is best, i would use one which is a lower (e.g. 0.25) since colors on a computer display tend to be rather bright. – H.B. Jul 20 '11 at 15:09
3

The most contrasting (different) color for any given color c you simply get by

new Color(c.R > .5 ? 0 : 1, c.G > .5 ? 0 : 1, c.B > .5 ? 0 : 1)

or if you need 0-255 range

new Color(c.R > 127 ? 0 : 255, c.G > 127 ? 0 : 255, c.B > 127 ? 0 : 255)

That text visibility on given background is allways the best, but not allways nice. In case you do not want to disturbe by crazy colors you just end up using black/white suggested by accepted answer.

Bohdan
  • 468
  • 5
  • 6
3

I think this is more than a design issue than having a 'formal' way. So it is a bit to your own decision. Can you show up some examples close to what you are tring to achieve? I am also working on something similar to your problem these days (creating online gallery which changes its BG according to the colors in the photos in the gallery dynamically), and I think I'm going pretty well, my advice is that, whatever you choose as a background, just create a layer of black or white in the background, depending on your foreground color (opposite brightness of it), and that way you are in a sense 'limiting' the BG, e.g. BG could be set to black, and your text is black too, but there's a white layer on top of BG that makes the text readable. The opacity of the layer is left to you, try and see what is the best value. And you can implement the whole thing in the value converter, and that BG + 'layer' composition is nothing more than a color value.

Can Poyrazoğlu
  • 33,241
  • 48
  • 191
  • 389
1

In a project I work on we decided on a formula that seperates the color by Red Green Blue values and subtract Red Green and Blue from 255 to get a near perfect constrasting color.

However...Gray is one of those colors that wouldn't work with this formula. You can simple check to see if the color is Gray before doing the subtraction. There are some other colors on both ends of the color spectrume where you might have to hold their hand to get a readable constrasting color.

Security Hound
  • 2,577
  • 3
  • 25
  • 42
  • I don't think that would be visually appealing, like red on cyan, or green on magenta wouldn't be really nice to look at, IMO. But otherwise nice to create contrast anyway unless the color in near gray of course. – Can Poyrazoğlu Jul 20 '11 at 14:13
  • @can poyrazoğlu - The author asked for a good method to find the contrast the color on the other end of the spectrum would give exactly that. The contrasting color for Red using this method is actually very good. Green - Yellow; I forget what color it is exactly. – Security Hound Jul 20 '11 at 14:31