3

I am using a random color for the background in my Windows Forms application. Now I want to display a label.

The problem is that when the random color is white and the label is too, then the label is not visible.

How can I get a perfect color that is visible on my background color? (My background color is a random color from System.Drawing.Color.)

mfluehr
  • 2,832
  • 2
  • 23
  • 31
UnyMath
  • 29
  • 1
  • 9
  • Maybe look at contrast: https://stackoverflow.com/questions/25426819/finding-out-if-a-hex-color-is-dark-or-light or https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color/1855903#1855903 – rene May 26 '18 at 07:16
  • 1
    You need to construct a color with Color.FromArgb. The complement might work. But to be sure do use the getBrightness function and make sure to build a color with enough contrast so you won't end up with two middle-of the road colors. Or keep picking a random color until the difference in brightnes is greater than 0.5. – TaW May 26 '18 at 07:16
  • Can you give me a code snippet please? – UnyMath May 26 '18 at 07:18
  • https://stackoverflow.com/questions/1165107/how-do-i-invert-a-colour-color – Steve May 26 '18 at 07:21
  • `List colors = ((Color[])Enum.GetValues(typeof(KnownColor))).ToList(); Color c1 = colors[rnd.Next(colors.Count)]; Color c2 = colors[rnd.Next(colors.Count)]; while (Math.Abs(c1.GetBrightness() - c2.GetBrightness()) < 0.5f ) c2 = colors[rnd.Next(colors.Count)];` Or, to make it less crazy, pick either White or Black, depending on the GetBrightnes value – TaW May 26 '18 at 07:27
  • Edit your question. Do not post code in comments. – sɐunıɔןɐqɐp May 26 '18 at 07:38
  • TaW please write a Answer that I can mark it as answer – UnyMath May 26 '18 at 08:04

1 Answers1

11

There are various ways to ensure a proper contrast.

Option one : I usually stick to keeping the text Black or White, depending on the brightness of back color.

enter image description here

To get the brightness one could simply go for the built-in function Color.GetBrightness()

Unfortunately this is not really a good solution, as the result is not perceptually correct; to wit: Green and Yellow have the same values, which is obviously not what our eyes will perceive.

Instead this tiny function will help:

float getBrightness(Color c) 
{  return (c.R * 0.299f + c.G * 0.587f + c.B *0.114f) / 256f; }

Now we can pick either Black or White:

Label lbl = new Label();
lbl.BackColor = colors[rnd.Next(colors.Count)];
lbl.ForeColor = getBrightness(lbl.BackColor) < 0.55 ? Color.White : Color.Black;

The code uses a list of known colors:

List<Color> colors = ((KnownColor[])Enum.GetValues(typeof(KnownColor))).
                     Select(x => Color.FromKnownColor(x)).ToList();

Option two : If you want to get colors in the foreground you could pick it randomly and repeat until you get a decent contrast by comparing e.g.

while (Math.Abs(c1.GetBrightness() - c2.GetBrightness()) < 0.5f ) 
            c2 = colors[rnd.Next(colors.Count)];

Note that you must not push the epsilon value too high or else it won't find a suitable color. This happens when trying to find a color that is too far away from a medium brightness! You could add a counter and after a while pick simply black or white..


Option three : Yet another way would be to construct a color with Color.FromArgb().

You could start by inverting each channel, which will give nice color contrasts; but if the color one is of medium brightness and/or saturation you would have to correct, maybe again by picking black or white..


Note: for the above image I have enumerated all KnownColors, which already looks pretty random.

To add some order you may sort the list by color properties, e.g. by hue, then by brightness:

List<Color> allcolors = ((KnownColor[])Enum.GetValues(typeof(KnownColor)))
    .Select(x => Color.FromKnownColor(x))
    .OrderBy(x => x.GetHue()).ThenBy(x => getBrightness(x)).ToList();
TaW
  • 53,122
  • 8
  • 69
  • 111