Ben
Ben

Reputation: 11208

Calculate colordifference

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

Answers (4)

Ben
Ben

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

reko_t
reko_t

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

WhoaItsAFactorial
WhoaItsAFactorial

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

Ja͢ck
Ja͢ck

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

Related Questions