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