Make foregroundcolor black or white depending on background
Asked Answered
V

6

41

Something like calculating the average value of rgb components and then decide whether to use black or white?

Do I have to convert RGB to HSV in first step 'cause RGB is not always what the human eyes see?

I'm using C#

Vries answered 11/2, 2010 at 0:35 Comment(2)
This question contains answers which will assist you: stackoverflow.com/questions/946544/…Dump
Related (but dupes themselves): stackoverflow.com/questions/3116260/… stackoverflow.com/questions/3942878/…Laveralavergne
V
7

what about that?

private static Color GetReadableForeColor(Color c)
{
    return (((c.R + c.B + c.G) / 3) > 128) ? Color.Black : Color.White;
}
Vries answered 11/2, 2010 at 1:15 Comment(2)
Try this using the following values: 128, 255, 0. It's a light green which is easier to read against a black background. However, the average value is 127, for which your method will return white.Laveralavergne
Of all solutions I have tried this is the one that gave me the best result, both visually as well as when programmatically determining the contrast between a range of two colors. +1.Kep
L
81

It just so happens I needed this function for a project not long ago.

private int PerceivedBrightness(Color c)
{
    return (int)Math.Sqrt(
    c.R * c.R * .241 +
    c.G * c.G * .691 +
    c.B * c.B * .068);
}

This formula I found on the web at Nbd Tech that dealt with perceived colors and color conversion formula. The site gives a lot of information that is helpful.

Here's how to use this to select black or white:

var foreColor = (PerceivedBrightness(backColor) > 130 ? Color.Black : Color.White);

You can use a value other than 130 as the cutoff; it is preference.


Update: According to Darel Rex Finley at his site:

The values I came up with by playing with Photoshop were actually .241, .691, and .068, but I have since been informed that the values .299, .587, and .114 are more accurate.

This specification follows ITU-R Recommendation BT.601 (or Rec. 601 for short). The site I mentioned above, Nbd Tech, hasn't yet been updated to reflect this.

Based on this, here is the updated method (thanks to DTI-Matt for the comment):

private int PerceivedBrightness(Color c)
{
    return (int)Math.Sqrt(
    c.R * c.R * .299 +
    c.G * c.G * .587 +
    c.B * c.B * .114);
}

Note on threshold preference:

Colors with a perceived brightness near the middle (e.g. 120-140) will be more subjective. For example, it's debatable whether red (FF0000), which evaluates to 139, is clearer with a black or white overlay.

White and Black on Red

Laveralavergne answered 11/2, 2010 at 0:41 Comment(4)
This is basically the formula for luma, squaring values to give outliers more weight. The result is a 0-255 scale. en.wikipedia.org/wiki/Luma_%28video%29Louislouisa
NOTE: The source for these values has been updated: alienryderflex.com/hsp.html They are now shown as: R: .299 G: .587 B: .114Hazeghi
How come 'red' comes up as being a 'light' color. Black text on red doesn't look good.Undersheriff
@Simon If you mean Color.Red (R 255, G 0, B 0), it evaluates to a perceived brightness of 139 based on the values above. I would agree that white-on-red is preferable, but black isn't unreadable. Values in the 120-140 range will be more subjective. Try a threshold of 140 with various colors to see if it suits your purpose.Laveralavergne
V
7

what about that?

private static Color GetReadableForeColor(Color c)
{
    return (((c.R + c.B + c.G) / 3) > 128) ? Color.Black : Color.White;
}
Vries answered 11/2, 2010 at 1:15 Comment(2)
Try this using the following values: 128, 255, 0. It's a light green which is easier to read against a black background. However, the average value is 127, for which your method will return white.Laveralavergne
Of all solutions I have tried this is the one that gave me the best result, both visually as well as when programmatically determining the contrast between a range of two colors. +1.Kep
D
6

The Color struct supports conversion to HSB natively.

if (Color.GetBrightness() > 0.5f) {
  // win
}

You may want to add a component of saturation as well, considering saturation also contributes to apparent 'lightness'.

Discus answered 25/2, 2010 at 16:20 Comment(0)
F
0

You could do a simple calculation depending on color depth, if say you had a #FFFFFF color format, you could just add the RGB values and calculate if they're under half way.

The max in that case is 255 (F x F = 256) per, so just check if it's under that threshold:

var colorCode = ((R + B + G) < 255*3) ? "#FFFFFF" : "#000000";

If the color's below, it's darker...use a white background. If it's above, it's lighter...use a black background. It's not perfect, but an idea to get started.

Fettling answered 11/2, 2010 at 0:41 Comment(1)
I tried something like this originally in my project, and it resulted in some occasions where the white/black labels were not as noticeable against their background colors.Laveralavergne
C
0

This isn't directly relevant to the question (since it doesn't mention Unity), but for any other readers out there who are using Unity & the UnityEngine.Color struct, note that they already have a property you can leverage to implement something like this: Color.grayscale, which returns a float 0-1, similar to the "PerceivedBrightness" function mentioned in the main answer (it uses the same values of r*.299, g*.587, b*.114). It will be closer to 0 for black/dark colors, and closer to 1 for white/bright colors.

Here is the function I'm using with Color.grayscale:

// get most visible color between black & white for a given color (e.g to put text on a color)
public static Color GetReadableForeColor(Color bg) => bg.grayscale > 0.509f ? Color.black : Color.white;
Coleslaw answered 11/6, 2023 at 22:42 Comment(0)
M
-1

If I'm understanding correctly, one approach might be to get hold of the desktop wallpaper image, check in some manor what colour it is and then change your application colour based on that.

There's an article on geekpedia about getting hold of the current desktop wallpaper (and lots of hits on google on that), but the basic premise is to grab the registry value of the current wallpaper:

RegistryKey rkWallPaper = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", false);
string WallpaperPath = rkWallPaper.GetValue("WallPaper").ToString();

You could then use that path to open the image. You can then get hold of lots of properties, such as the dimensions and individual pixel colours in RGB.

To work out whether it's "more white" or "more black" you have many options.

One idea would be to get each pixel colour in RGB, average the values (to get the greyscale value) and then average the greyscale value of each pixel across the image. If this comes out > 128 then it could be condidered to be "white". If < 128 then "black". Basically you are deciding which side of the mid-grey dividing line the images pixel intensities average out to.

// Psudo code - can't check the C# spec atm.
foreach(Pixel p in image)
{
    // get average of colour components.
    double greyScale = (p.Red + p.Green + p.Blue) / 3;
    totalIntensity += greyScale;
}

double averageImageIntensity = totalIntensity / image.NumPixels;

if(totalIntensity > 128) // image could be considered to be "white"
else // image could be considered "black".

Problems: could be a slow procedure, you might want to sample only some of the pixels (say, every 10th one etc.) to save time. More generally, it seems like a fairly hacky thing to be doing at all. Pulling user files at run-time and messing with them is hardly clean, and it provides potential security and stability concerns. What if the image is duff or corrupt etc.

Personally I'd suggest simply giving the user the choice of colour / theme / skin themselves, or better yet let them customise it!

Mulish answered 11/2, 2010 at 0:51 Comment(1)
I don't want to change the whole app color. I'm using a listview in which items have bg color and the text of each item should be readable...Vries

© 2022 - 2024 — McMap. All rights reserved.