├── Color.php ├── LICENSE ├── README.md └── composer.json /Color.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Color 13 | { 14 | /** 15 | * @var int 16 | */ 17 | protected $color = 0; 18 | 19 | /** 20 | * Initialize object 21 | * 22 | * @param int $color An integer color, such as a return value from imagecolorat() 23 | */ 24 | public function __construct($intColor = null) 25 | { 26 | if ($intColor) { 27 | $this->fromInt($intColor); 28 | } 29 | } 30 | 31 | /** 32 | * Init color from hex value 33 | * 34 | * @param string $hexValue 35 | * 36 | * @return Color 37 | */ 38 | public function fromHex($hexValue) 39 | { 40 | $this->color = hexdec($hexValue); 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * Init color from integer RGB values 47 | * 48 | * @param int $red 49 | * @param int $green 50 | * @param int $blue 51 | * 52 | * @return Color 53 | */ 54 | public function fromRgbInt($red, $green, $blue) 55 | { 56 | $this->color = (int)(($red << 16) + ($green << 8) + $blue); 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Init color from hex RGB values 63 | * 64 | * @param string $red 65 | * @param string $green 66 | * @param string $blue 67 | * 68 | * @return Color 69 | */ 70 | public function fromRgbHex($red, $green, $blue) 71 | { 72 | return $this->fromRgbInt(hexdec($red), hexdec($green), hexdec($blue)); 73 | } 74 | 75 | /** 76 | * Init color from integer value 77 | * 78 | * @param int $intValue 79 | * 80 | * @return Color 81 | */ 82 | public function fromInt($intValue) 83 | { 84 | $this->color = $intValue; 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * Convert color to hex 91 | * 92 | * @return string 93 | */ 94 | public function toHex() 95 | { 96 | return str_pad(dechex($this->color),6,"0",STR_PAD_LEFT); 97 | } 98 | 99 | /** 100 | * Convert color to RGB array (integer values) 101 | * 102 | * @return array 103 | */ 104 | public function toRgbInt() 105 | { 106 | return array( 107 | 'red' => (int)(255 & ($this->color >> 16)), 108 | 'green' => (int)(255 & ($this->color >> 8)), 109 | 'blue' => (int)(255 & ($this->color)) 110 | ); 111 | } 112 | 113 | /** 114 | * Convert color to RGB array (hex values) 115 | * 116 | * @return array 117 | */ 118 | public function toRgbHex() 119 | { 120 | return array_map(function($item){ 121 | return dechex($item); 122 | }, $this->toRgbInt()); 123 | } 124 | 125 | /** 126 | * Get Hue/Saturation/Value for the current color 127 | * (float values, slow but accurate) 128 | * 129 | * @return array 130 | */ 131 | public function toHsvFloat() 132 | { 133 | $rgb = $this->toRgbInt(); 134 | 135 | $rgbMin = min($rgb); 136 | $rgbMax = max($rgb); 137 | 138 | $hsv = array( 139 | 'hue' => 0, 140 | 'sat' => 0, 141 | 'val' => $rgbMax 142 | ); 143 | 144 | // If v is 0, color is black 145 | if ($hsv['val'] == 0) { 146 | return $hsv; 147 | } 148 | 149 | // Normalize RGB values to 1 150 | $rgb['red'] /= $hsv['val']; 151 | $rgb['green'] /= $hsv['val']; 152 | $rgb['blue'] /= $hsv['val']; 153 | $rgbMin = min($rgb); 154 | $rgbMax = max($rgb); 155 | 156 | // Calculate saturation 157 | $hsv['sat'] = $rgbMax - $rgbMin; 158 | if ($hsv['sat'] == 0) { 159 | $hsv['hue'] = 0; 160 | return $hsv; 161 | } 162 | 163 | // Normalize saturation to 1 164 | $rgb['red'] = ($rgb['red'] - $rgbMin) / ($rgbMax - $rgbMin); 165 | $rgb['green'] = ($rgb['green'] - $rgbMin) / ($rgbMax - $rgbMin); 166 | $rgb['blue'] = ($rgb['blue'] - $rgbMin) / ($rgbMax - $rgbMin); 167 | $rgbMin = min($rgb); 168 | $rgbMax = max($rgb); 169 | 170 | // Calculate hue 171 | if ($rgbMax == $rgb['red']) { 172 | $hsv['hue'] = 0.0 + 60 * ($rgb['green'] - $rgb['blue']); 173 | if ($hsv['hue'] < 0) { 174 | $hsv['hue'] += 360; 175 | } 176 | } else if ($rgbMax == $rgb['green']) { 177 | $hsv['hue'] = 120 + (60 * ($rgb['blue'] - $rgb['red'])); 178 | } else { 179 | $hsv['hue'] = 240 + (60 * ($rgb['red'] - $rgb['green'])); 180 | } 181 | 182 | return $hsv; 183 | } 184 | 185 | /** 186 | * Get HSV values for color 187 | * (integer values from 0-255, fast but less accurate) 188 | * 189 | * @return int 190 | */ 191 | public function toHsvInt() 192 | { 193 | $rgb = $this->toRgbInt(); 194 | 195 | $rgbMin = min($rgb); 196 | $rgbMax = max($rgb); 197 | 198 | $hsv = array( 199 | 'hue' => 0, 200 | 'sat' => 0, 201 | 'val' => $rgbMax 202 | ); 203 | 204 | // If value is 0, color is black 205 | if ($hsv['val'] == 0) { 206 | return $hsv; 207 | } 208 | 209 | // Calculate saturation 210 | $hsv['sat'] = round(255 * ($rgbMax - $rgbMin) / $hsv['val']); 211 | if ($hsv['sat'] == 0) { 212 | $hsv['hue'] = 0; 213 | return $hsv; 214 | } 215 | 216 | // Calculate hue 217 | if ($rgbMax == $rgb['red']) { 218 | $hsv['hue'] = round(0 + 43 * ($rgb['green'] - $rgb['blue']) / ($rgbMax - $rgbMin)); 219 | } else if ($rgbMax == $rgb['green']) { 220 | $hsv['hue'] = round(85 + 43 * ($rgb['blue'] - $rgb['red']) / ($rgbMax - $rgbMin)); 221 | } else { 222 | $hsv['hue'] = round(171 + 43 * ($rgb['red'] - $rgb['green']) / ($rgbMax - $rgbMin)); 223 | } 224 | if ($hsv['hue'] < 0) { 225 | $hsv['hue'] += 255; 226 | } 227 | 228 | return $hsv; 229 | } 230 | 231 | /** 232 | * Get current color in XYZ format 233 | * 234 | * @return array 235 | */ 236 | public function toXyz() 237 | { 238 | $rgb = $this->toRgbInt(); 239 | 240 | // Normalize RGB values to 1 241 | $rgb = array_map(function($item){ 242 | return $item / 255; 243 | }, $rgb); 244 | 245 | $rgb = array_map(function($item){ 246 | if ($item > 0.04045) { 247 | $item = pow((($item + 0.055) / 1.055), 2.4); 248 | } else { 249 | $item = $item / 12.92; 250 | } 251 | return ($item * 100); 252 | }, $rgb); 253 | 254 | //Observer. = 2°, Illuminant = D65 255 | $xyz = array( 256 | 'x' => ($rgb['red'] * 0.4124) + ($rgb['green'] * 0.3576) + ($rgb['blue'] * 0.1805), 257 | 'y' => ($rgb['red'] * 0.2126) + ($rgb['green'] * 0.7152) + ($rgb['blue'] * 0.0722), 258 | 'z' => ($rgb['red'] * 0.0193) + ($rgb['green'] * 0.1192) + ($rgb['blue'] * 0.9505) 259 | ); 260 | 261 | return $xyz; 262 | } 263 | 264 | /** 265 | * Get color CIE-Lab values 266 | * 267 | * @return array 268 | */ 269 | public function toLabCie() 270 | { 271 | $xyz = $this->toXyz(); 272 | 273 | //Ovserver = 2*, Iluminant=D65 274 | $xyz['x'] /= 95.047; 275 | $xyz['y'] /= 100; 276 | $xyz['z'] /= 108.883; 277 | 278 | $xyz = array_map(function($item){ 279 | if ($item > 0.008856) { 280 | //return $item ^ (1/3); 281 | return pow($item, 1/3); 282 | } else { 283 | return (7.787 * $item) + (16 / 116); 284 | } 285 | }, $xyz); 286 | 287 | $lab = array( 288 | 'l' => (116 * $xyz['y']) - 16, 289 | 'a' => 500 * ($xyz['x'] - $xyz['y']), 290 | 'b' => 200 * ($xyz['y'] - $xyz['z']) 291 | ); 292 | 293 | return $lab; 294 | } 295 | 296 | /** 297 | * Convert color to integer 298 | * 299 | * @return int 300 | */ 301 | public function toInt() 302 | { 303 | return $this->color; 304 | } 305 | 306 | /** 307 | * Alias of toString() 308 | * 309 | * @return string 310 | */ 311 | public function __toString() 312 | { 313 | return $this->toString(); 314 | } 315 | 316 | /** 317 | * Get color as string 318 | * 319 | * @return string 320 | */ 321 | public function toString() 322 | { 323 | $str = (string)$this->toHex(); 324 | if (strlen($str) < 6) { 325 | $str = str_pad($str, 6, '0', STR_PAD_LEFT); 326 | } 327 | return strtoupper("#{$str}"); 328 | } 329 | 330 | /** 331 | * Get the distance between this color and the given color 332 | * 333 | * @param Color $color 334 | * 335 | * @return int 336 | */ 337 | public function getDistanceRgbFrom(Color $color) 338 | { 339 | $rgb1 = $this->toRgbInt(); 340 | $rgb2 = $color->toRgbInt(); 341 | 342 | $rDiff = abs($rgb1['red'] - $rgb2['red']); 343 | $gDiff = abs($rgb1['green'] - $rgb2['green']); 344 | $bDiff = abs($rgb1['blue'] - $rgb2['blue']); 345 | 346 | // Sum of RGB differences 347 | $diff = $rDiff + $gDiff + $bDiff; 348 | return $diff; 349 | } 350 | 351 | /** 352 | * Get distance from the given color using the Delta E method 353 | * 354 | * @param Color $color 355 | * 356 | * @return float 357 | */ 358 | public function getDistanceLabFrom(Color $color) 359 | { 360 | $lab1 = $this->toLabCie(); 361 | $lab2 = $color->toLabCie(); 362 | 363 | $lDiff = abs($lab2['l'] - $lab1['l']); 364 | $aDiff = abs($lab2['a'] - $lab1['a']); 365 | $bDiff = abs($lab2['b'] - $lab1['b']); 366 | 367 | $delta = sqrt($lDiff + $aDiff + $bDiff); 368 | 369 | return $delta; 370 | } 371 | 372 | /** 373 | * Detect if color is grayscale 374 | * 375 | * @param int @threshold 376 | * 377 | * @return bool 378 | */ 379 | public function isGrayscale($threshold = 16) 380 | { 381 | $rgb = $this->toRgbInt(); 382 | 383 | // Get min and max rgb values, then difference between them 384 | $rgbMin = min($rgb); 385 | $rgbMax = max($rgb); 386 | $diff = $rgbMax - $rgbMin; 387 | 388 | return $diff < $threshold; 389 | } 390 | 391 | /** 392 | * Get the closest matching color from the given array of colors 393 | * 394 | * @param array $colors array of integers or Color objects 395 | * 396 | * @return mixed the array key of the matched color 397 | */ 398 | public function getClosestMatch(array $colors) 399 | { 400 | $matchDist = 10000; 401 | $matchKey = null; 402 | foreach($colors as $key => $color) { 403 | if (false === ($color instanceof Color)) { 404 | $c = new Color($color); 405 | } 406 | $dist = $this->getDistanceLabFrom($c); 407 | if ($dist < $matchDist) { 408 | $matchDist = $dist; 409 | $matchKey = $key; 410 | } 411 | } 412 | 413 | return $matchKey; 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Harold Asbridge 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Color Utility Class 2 | 3 | This class is intended to make it easier to convert between colorspaces, 4 | as well as compare one color to another. 5 | 6 | ## Requirements 7 | - PHP 5.3 or greater (closure support is required) 8 | 9 | ## Examples: 10 | 11 | ### Initialize object (using hex notation is easier if you are familiar with CSS colors): 12 | 13 | $color = new Color(0xFFFFFF); 14 | 15 | 16 | ### Get distance from another color using the RGB colorspace: 17 | 18 | $color1 = new Color(0xFFFFFF); 19 | $color2 = new Color(0x888888); 20 | 21 | $distance = $color1->getDistanceRgbFrom($color2); 22 | 23 | ### Get closest matching color using the Lab(CIE) colorspace: 24 | 25 | $color = new Color(0xFFFFFF); 26 | 27 | $palette = array( 28 | 0x000000, 29 | 0x888888, 30 | 0xAAAAAA 31 | ); 32 | 33 | $matchIndex = $color->getClosestMatch($palette); 34 | $matchColor = $palette[$matchIndex]; -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasbridge/php-color", 3 | "description": "Color utility class for PHP 5.3 that allows for easy conversion between RGB, HSV, XYZ, and Lab colorspaces, as well as color comparison", 4 | "license": "MIT", 5 | "autoload": { 6 | "psr-0": {"Color": ""} 7 | }, 8 | "require": { 9 | "php": "~5.3" 10 | } 11 | } 12 | --------------------------------------------------------------------------------