Search code examples
c#windowsformscolorsdrawing

C# get good color for label


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.)


Solution

  • 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)];   // get a random color
    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();