Thus far I have the code below:
function hexrgb_invert($hex) {
$arr = str_split($hex, 2);
foreach ($arr as &$value) {
$c = base_convert($value, 16, 10);
$value = str_pad(base_convert(255 - $c, 10, 16), 2, '0', STR_PAD_LEFT);
}
return implode('', $arr);
}
The Problem: I need to invert colors based on contrast. The above function works for some things but not others.
Example: If the input is 9d702f the output will be 9d702f. (2 colors that have a low contrast)
I haven't found any luck looking elsewhere on StackOverflow, as most answers seem to use the same algorithm I am already using.
Further Examples:
Let's say that I am trying to find the contrasting opposite of #FFFFFF (white). This is very straight forward because white is a primary color so its opposite can be easily calculated. (Which the above function will work perfectly for.) The opposite of #FFFFFF is of course #000000 (black) and when you compare the 2 colors you get a contrast ratio of 21:1.
However, if we try to use the same function above on the color #808080 it will give us the color #7F7F7F. Those 2 colors are almost identical and have a contrast ratio of only 1.01:1. This is because the closer you get to hex 80 (decimal 128) the less contrast that function can provide.
In the specific case of #808080 the color #000000 would provide the most constrast at 5.32:1.
function rgb_best_contrast($r, $g, $b) {
return array(
'r' => ($r < 128) ? 255 : 0,
'g' => ($g < 128) ? 255 : 0,
'b' => ($b < 128) ? 255 : 0
);
}
str_pad adds characters on the right by default — its optional $pad_type argument defaults to STR_PAD_RIGHT.
You need to force it add zeros on the left:
str_pad(base_convert(255 - $c, 10, 16), 2, '0', STR_PAD_LEFT)
Your example in details:
6ff060, taking only the G value into consideration: $c = 0xf0 (decimal: 240). 255 - 240 = 15 (hex: f)base_convert(255 - $c, 10, 16) produces: 'f' (as string!)str_pad(base_convert(255 - $c, 10, 16), 2, '0') adds one zero on the right, thus producing 'f0'. $pad_type = STR_PAD_LEFT fixes the problem. After trying a few dozen different methods to try and calculate the best contrasting opposite color to any given color, I finally broke down and started manually testing individual color contrasts.
I used this script to search the entire RGB color space for the best match for any given color:
$test_a = ['r' => 128, 'g' => 128, 'b' => 128];
$best = [
'color' => ['r' => 128, 'g' => 128, 'b' => 128],
'diff' => 0.0
];
foreach (range(0, 255) as $r) {
foreach (range(0, 255) as $g) {
foreach (range(0, 255) as $b) {
$test_b = ['r' => $r, 'g' => $g, 'b' => $b];
// YQI sensitive contrast check
$diff = check::rgb_contrast($test_a, $test_b);
if ($diff > $best['diff']) {
$best = [
'color' => $test_b,
'diff' => $diff
];
}
}
}
}
var_dump($best);
In short, a very obvious pattern emerged with all of my results. This function works off that pattern:
function rgb_best_contrast($r, $g, $b) {
return array(
'r' => ($r < 128) ? 255 : 0,
'g' => ($g < 128) ? 255 : 0,
'b' => ($b < 128) ? 255 : 0
);
}
Works exactly as expected. Always gives the best contrasting color.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With