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();
}
}
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.