Как инвертировать шестнадцатеричные значения RGB по контрасту в PHP
До сих пор у меня есть код ниже:
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);
}
Проблема: Мне нужно инвертировать цвета на основе контраста. Описанная выше функция работает для некоторых вещей, но не для других.
Пример: Если вход 9d702f
, то выход будет 9d702f
. (2 цвета с низкой контрастностью)
Мне не повезло искать что-либо еще в StackOverflow, так как большинство ответов, похоже, используют тот же алгоритм, который я уже использую.
Далее Примеры:
Предположим, что я пытаюсь найти контрастную противоположность #FFFFFF
(белому). Это очень прямолинейно, потому что белый является основным цветом, поэтому его противоположность можно легко вычислить. (Для чего вышеуказанная функция будет отлично работать.) Противоположностью #FFFFFF
, конечно, является #000000
(черный), и когда вы сравниваете 2 цвета, вы получаете коэффициент контрастности 21:1.
Однако, если мы попытаемся использовать ту же функцию выше для цвета #808080
, это даст нам цвет #7F7F7F
. Эти 2 цвета почти идентичны и имеют коэффициент контрастности всего 1,01:1. Это происходит потому, что чем ближе вы подходите к шестнадцатеричному 80
(десятичному 128
), тем меньше контраста может обеспечить эта функция.
В конкретном случае #808080
цвет #000000
обеспечил бы наибольшее соответствие в 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
);
}
2 answers
str_pad
добавляет символы справа по умолчанию - его необязательный аргумент $pad_type
по умолчанию равен STR_PAD_RIGHT
.
Вам нужно заставить его добавить нули слева:
str_pad(base_convert(255 - $c, 10, 16), 2, '0', STR_PAD_LEFT)
Ваш пример в деталях:
- Ввод:
6ff060
, принимая во внимание только значениеG
:$c = 0xf0
(десятичное число: 240). -
255 - 240 = 15
(шестнадцатеричный: f) -
base_convert(255 - $c, 10, 16)
производит:'f'
(в виде строки!) -
str_pad(base_convert(255 - $c, 10, 16), 2, '0')
добавляет один ноль справа , таким образом, производя'f0'
. - Настройка
$pad_type = STR_PAD_LEFT
устраняет проблему.
Попробовав несколько десятков различных методов, чтобы попытаться рассчитать наилучший контрастный противоположный цвет для любого данного цвета, я, наконец, сломался и начал вручную тестировать отдельные цветовые контрасты.
Я использовал этот скрипт для поиска всего цветового пространства RGB в поисках наилучшего соответствия для любого заданного цвета:
$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);
Короче говоря, со всеми моими результатами выявилась очень очевидная закономерность. Эта функция работает по этому шаблону:
function rgb_best_contrast($r, $g, $b) {
return array(
'r' => ($r < 128) ? 255 : 0,
'g' => ($g < 128) ? 255 : 0,
'b' => ($b < 128) ? 255 : 0
);
}
Работает точно так, как ожидалось. Всегда дает лучший контрастный цвет.