Calculating Color Contrast for Legible Text in Ruby
charliepark.tumblr.com
charliepark.tumblr.com
http://en.wikipedia.org/wiki/Colorspace http://en.wikipedia.org/wiki/YUV http://en.wikipedia.org/wiki/HSL_and_HSV http://en.wikipedia.org/wiki/YIQ
The sample images at http://en.wikipedia.org/wiki/HSL_and_HSV#Disadvantages show the difference between various brightness calculations.
1. Take "gamma-compressed" RGB and scale to the range [0, 1] (if it's an 8-bit integer representation, divide by 255), and convert to linear RGB: undo the sRGB nonlinearity or for the lazy, take each component to the 2.2 power.
2. Take a weighted sum of R, G, B components. The weights for an sRGB/HDTV image are .2126, .7152, and .0722 for R, G, and B respectively. This gives you the luminance, Y.
3. Apply some non-linearity to the luminance to get a decent correlate of human-perceived lightness. A gamma curve w/ 2.2 gamma (that is, raise your value of Y to the (1/2.2) power) actually is an okay function to use. If you want a better one, feel free to use the CIELAB definition: L = 116 Y^(1/3) - 16, where L ranges from 0 to 100.
4. Anything with L >= 50, use black, anything with L < 50, use white.
(properly L*, but hacker news will think I'm using the asterisk to indicate emphasis if there is more than one of them)
Since this is likely only being calculated for a few colors (even up to dozens or hundreds), there's really not enough overhead in doing this math to worry about the speed.
@mixin fg-contrast($bg) {
background: $bg;
$textcolor: #ffffff;
@if lightness($bg) > 50% {
$textcolor: #000000;
}
color: $textcolor;
}
Could also be done by using the red(), green(), and blue() functions, but lightness was simpler. get_luminance_from_color() {
# Return the luminance of a color as a number 0-65535.
local red=$1
local green=$2
local blue=$3
# Colour weights from Wikipedia. Note bash doesn't have floats.
echo $(( (2126 * $red + 7152 * $green + 722 * $blue) / 10000 ))
}
It works like this: $ get_luminance_from_color 16384 32768 16384
28101
...which is to say, then input is an RGB tuple where each field is 0-65535, and the output is a single integer between 0-65535 representing the luminance. If the luminance is > 32767, it's a light colour, otherwise it's a dark colour.def contrasting_text_color(background_hex_color) (background_hex_color.scan(/../).map {|color| color.hex}).sum > 382.5 ? '#000' : '#fff' end
We have a color picker that sorts a hash of "available" hexadecimal colors by their brightness value, though, so we needed to have it available as an independent method.
'#000' if int(my_color, 16) > 0xffffff/2 else '#fff'
This approach isn't perfect, but it does the job.
"#000000" if sum([int(i,16) for i in re.findall("..",myColour)]) > 382.5 else "#ffffff"