Given a background color, how to get a foreground color that makes it readable on that background color?
Given a 开发者_如何学JAVAbackground color, how to get a foreground color that makes it readable on that background color?
I mean computing that foreground color automatically in a program.
Or simplify the problem, if the foreground color is chosen from white/black, how to do the choice in a program?
The safest bet is to conform with the World Wide Web Consortium’s (W3C) Web Content Accessibility Guidelines 2.0, which specify a brightness contrast ratio of 4.5:1 for regular text (12 pt or smaller), and 3.0:1 for large text. Contrast ratio is defined as:
[Y(b) + 0.05] / [Y(d) + 0.05]
Where Y(b) is the brightness (luminance) of the brighter color and Y(d) is the brightness of the darker color.
You calculate luminance Y by first converting each of the color’s RGB values to gamma adjusted normalize rgb values:
- r = (R/255)^2.2
- b = (B/255)^2.2
- g = (G/255)^2.2
Then combine them using sRGB constants (rounded to 4 places):
Y = 0.2126*r + 0.7151*g + 0.0721*b
This gives white a Y of 1 and black a Y of 0, so the maximum possible contrast is (1.05/ 0.05) = 21 (within rounding error).
Or let JuicyStudio do the math for you.
This calculation assumes a standard-performing monitor in a relatively dimly lit room (or a room that the user can make dim if she or he has to). That makes it adequate for home or office use, but I don’t know if it’s adequate for mobile apps or other devices that are used outdoors.
Here's one I did in both Java and Javascript. It's loosely based off this one in javascript. I took the Luminance formula from here. The sweet-spot of the threshold from my eye was about 140.
Java version:
public class Color {
private float CalculateLuminance(ArrayList<Integer> rgb){
return (float) (0.2126*rgb.get(0) + 0.7152*rgb.get(1) + 0.0722*rgb.get(2));
}
private ArrayList<Integer> HexToRBG(String colorStr) {
ArrayList<Integer> rbg = new ArrayList<Integer>();
rbg.add(Integer.valueOf( colorStr.substring( 1, 3 ), 16 ));
rbg.add(Integer.valueOf( colorStr.substring( 3, 5 ), 16 ));
rbg.add(Integer.valueOf( colorStr.substring( 5, 7 ), 16 ));
return rbg;
}
public String getInverseBW(String hex_color) {
float luminance = this.CalculateLuminance(this.HexToRBG(hex_color));
String inverse = (luminance < 140) ? "#fff" : "#000";
return inverse;
}
}
Javascript version:
Here's the same thing in javascript for your front-end things. RGB conversion taken from here:
hex_to_rgb: function(hex) {
let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
},
hex_inverse_bw: function(hex) {
let rgb = this.hex_to_rgb(hex);
let luminance = (0.2126*rgb["r"] + 0.7152*rgb["g"] + 0.0722*rgb["b"]);
return (luminance < 140) ? "#ffffff": "#000000";
}
- Calculate the lightness (see HSL)
- If the lightness is less than 50%, use white. Otherwise, use black.
Using colors as foreground color is difficult, because you have to take contrast and color blindness into account.
Here's some actual (ruby) code that'll actually do the lifting:
rgbval = "8A23C0".hex
r = rgbval >> 16
g = (rgbval & 65280) >> 8
b = rgbval & 255
brightness = r*0.299 + g*0.587 + b*0.114
return (brightness > 160) ? "#000" : "#fff"
You could compute the inverse colour, but you run the risk of contrast diminishing "in the middle" of the colour space.
In case this could still be useful for someone, this is the Dart implementation based on the answers above
Color getInverseBW(Color color) {
double luminance = (0.2126 * color.red + 0.7152 * color.green + 0.0722 * color.blue);
return (luminance < 140) ? Color(0xffffffff) : Color(0xff000000);
}
Pascal / Delphi version:
var
red,green,blue : Integer;
luminance : double;
// convert hexa-decimal values to RGB
red := (_BackgroundColor) and $FF;
green := (_BackgroundColor shr 8) and $FF;
blue := (_BackgroundColor shr 16) and $FF;
luminance := (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
if luminance < 140 then
Result := TColors.White
else
Result := TColors.Black;
PyQt5 version of Michael Zuschlag`s answer:
import sys
from PyQt5.QtGui import QColor
class MostReadableColor():
def getLuminance(self, color):
""" get color luminance.
Convert color RGB values to gamma adjusted normalized rgb values
then combine them using sRGB constants (rounded to 4 places).
"""
r, g, b, a = QColor(color).getRgb()
l = ((r/255)**2.2)*0.2126 + ((g/255)**2.2)*0.7151 + \
((b/255)**2.2)*0.0721
return(l)
def getContrastRation(self, color1, color2):
l1 = self.getLuminance(color1)
l2 = self.getLuminance(color2)
cr = (l1 + .05)/(l2+.05) if l1 > l2 else (l2+.05)/(l1 + .05)
return(cr)
def getMostReadable(self, color):
cr = []
for c in QColor.colorNames():
if c == 'transparent':
continue
cr.append([self.getContrastRation(color, c), c])
sorted_cr = sorted(cr, reverse=True)
return(sorted_cr[0][1])
def main():
if len(sys.argv) != 2:
print("usage: MostReadableColor color_name (ex: 'red')")
else:
mrc = MostReadableColor()
best_contrast_color = mrc.getMostReadable(sys.argv[1])
print(f"{best_contrast_color}")
if __name__ == "__main__":
main()
Based on the answers above I came up with a C# implementation, you may want to tweak the logic surrounding the alpha based on your requirements.
public static class ColorExtension
{
public static Color GetReadableColor(this Color color) => color.GetLuminance() > 140 || color.A != 255 ? Color.Black : Color.White;
public static double GetLuminance(this Color color) => (0.2126 * color.R) + (0.7151 * color.G) + (0.0721 * color.B);
}
精彩评论