Reputation: 11208
Is it possible to check if a certain hexadecimal color is closer to FFF or 000 based on a defined 'center-value'?
I want to check if a color lies closer to #FFF or #000 based on #888. So if I check for #EFEFEF it should return #FFF and if I try #878787 it should return #000.
How can this be achieved? I'm not sure what to search for on Google...
Thanks in advance
Upvotes: 5
Views: 169
Reputation: 11208
Thanks everybody for the help!
In my startpost I made a little typo as mentioned by oezi. For me the solution was a small modification to the accepted answer (by reko_t):
function color_distance($sColor1, $sColor2)
{
$decA = hexdec(substr($sColor1, 1));
$decB = hexdec(substr($sColor2, 1));
$avgA = (($decA & 0xFF) + (($decA >> 8) & 0xFF) + (($decA >> 16) & 0xFF)) / 3;
$avgB = (($decB & 0xFF) + (($decB >> 8) & 0xFF) + (($decB >> 16) & 0xFF)) / 3;
return abs($avgA - $avgB);
}
function determine_color($sInputColor, $sRefColor, $sMinColor, $sMaxColor)
{
$distRef = color_distance($sInputColor, $sRefColor);
$distMin = color_distance($sInputColor, $sMinColor);
$distMax = color_distance($sInputColor, $sMaxColor);
return $distMax - $distRef < $distMin - $distRef ? $sMinColor : $sMaxColor;
}
I needed this function to determine text-color on a background-color which can be set by some setting. Thanks! :) Credits to reko_t!
Upvotes: 0
Reputation: 56440
The easiest way to solve your problem is to calculate the distance between colors using their greyscale values (there are other ways, but this is simple). So something like:
// returns a distance between two colors by comparing each component
// using average of the RGB components, eg. a grayscale value
function color_distance($a, $b)
{
$decA = hexdec(substr($a, 1));
$decB = hexdec(substr($a, 1));
$avgA = (($decA & 0xFF) + (($decA >> 8) & 0xFF) + (($decA >> 16) & 0xFF)) / 3;
$avgB = (($decB & 0xFF) + (($decB >> 8) & 0xFF) + (($decB >> 16) & 0xFF)) / 3;
return abs($avgA - $avgB);
}
// I am going to leave the naming of the function to you ;)
// How this works is that it'll return $minColor if $color is closer to $refColorMin
// and $maxColor if $color is closer to $refColorMax
// all colors should be passed in format #RRGGBB
function foo($color, $refColorMin, $refColorMax, $minColor, $maxColor)
{
$distMin = color_distance($color, $refColorMin);
$distMax = color_distance($color, $refColorMax);
return ($distMin < $distMax) ? $minColor : $maxColor;
}
// Example usage to answer your original question:
$colorA = foo('#EFEFEF', '#888888', '#FFFFFF', '#000000', '#FFFFFF');
$colorA = foo('#898989', '#888888', '#FFFFFF', '#000000', '#FFFFFF');
// Check the values
var_dump($colorA, $colorB);
The output is:
string(7) "#FFFFFF"
string(7) "#000000"
Upvotes: 2
Reputation: 3558
You could do something like the following:
function hex2rgb($hex) {
$hex = str_replace("#", "", $hex);
if(strlen($hex) == 3) {
$r = hexdec(substr($hex,0,1).substr($hex,0,1));
$g = hexdec(substr($hex,1,1).substr($hex,1,1));
$b = hexdec(substr($hex,2,1).substr($hex,2,1));
} else {
$r = hexdec(substr($hex,0,2));
$g = hexdec(substr($hex,2,2));
$b = hexdec(substr($hex,4,2));
}
$rgb = array($r, $g, $b);
//return implode(",", $rgb); // returns the rgb values separated by commas
return $rgb; // returns an array with the rgb values
}
$rgb = hex2rgb("#cc0");
From that you could take the values of $rgb and see if their values, on average, area greater than or less than 122.5. If its greater than 122.5 you'd be closer to #FFFFFF, lower than 122.5 you'd be closer to #000000.
Upvotes: 1
Reputation: 173642
You could convert the colours to numbers:
$color_num = hexdec(substr($color, 1)); // skip the initial #
Then compare them to either 0x0
or 0xffffff
.
You could also break them down into R, G and B and make three comparisons; then average them? Not sure how precise you want this thing :)
Upvotes: 3