4 * Author: Arlo Carreon <http://arlocarreon.com>
5 * Info: http://mexitek.github.io/phpColors/
6 * License: http://arlo.mit-license.org/
11 * A color utility that helps manipulate HEX colors
12 * @todo convert space -> tab
21 * Auto darkens/lightens by 10% for sexily-subtle gradients.
22 * Set this to FALSE to adjust automatic shade to be between given color
23 * and black (for darken) or white (for lighten)
25 const DEFAULT_ADJUST = 10;
28 * Instantiates the class with a HEX value
30 * @throws Exception "Bad color format"
32 function __construct( $hex ) {
33 // Strip # sign is present
34 $color = str_replace("#", "", $hex);
36 // Make sure it's 6 digits
37 if ( strlen($color) === 3 ) {
38 $color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2];
39 } elseif ( strlen($color) != 6 ) {
40 throw new Exception("HEX color needs to be 6 or 3 digits long");
43 $this->_hsl = self::hexToHsl( $color );
45 $this->_rgb = self::hexToRgb( $color );
48 // ====================
49 // = Public Interface =
50 // ====================
53 * Given a HEX string returns a HSL array equivalent.
54 * @param string $color
55 * @return array HSL associative array
57 public static function hexToHsl( $color ){
60 $color = self::_checkHex($color);
63 $R = hexdec($color[0].$color[1]);
64 $G = hexdec($color[2].$color[3]);
65 $B = hexdec($color[4].$color[5]);
73 $var_Min = min($var_R, $var_G, $var_B);
74 $var_Max = max($var_R, $var_G, $var_B);
75 $del_Max = $var_Max - $var_Min;
77 $L = ($var_Max + $var_Min)/2;
86 if ( $L < 0.5 ) $S = $del_Max / ( $var_Max + $var_Min );
87 else $S = $del_Max / ( 2 - $var_Max - $var_Min );
89 $del_R = ( ( ( $var_Max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
90 $del_G = ( ( ( $var_Max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
91 $del_B = ( ( ( $var_Max - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
93 if ($var_R == $var_Max) $H = $del_B - $del_G;
94 else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B;
95 else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R;
101 $HSL['H'] = ($H*360);
109 * Given a HSL associative array returns the equivalent HEX string
111 * @return string HEX string
112 * @throws Exception "Bad HSL Array"
114 public static function hslToHex( $hsl = array() ){
115 // Make sure it's HSL
116 if (empty($hsl) || !isset($hsl["H"]) || !isset($hsl["S"]) || !isset($hsl["L"]) ) {
117 throw new Exception("Param was not an HSL array");
120 list($H,$S,$L) = array( $hsl['H']/360,$hsl['S'],$hsl['L'] );
131 $var_2 = ($L+$S) - ($S*$L);
134 $var_1 = 2 * $L - $var_2;
136 $r = round(255 * self::_huetorgb( $var_1, $var_2, $H + (1/3) ));
137 $g = round(255 * self::_huetorgb( $var_1, $var_2, $H ));
138 $b = round(255 * self::_huetorgb( $var_1, $var_2, $H - (1/3) ));
147 // Make sure we get 2 digits for decimals
148 $r = (strlen("".$r)===1) ? "0".$r:$r;
149 $g = (strlen("".$g)===1) ? "0".$g:$g;
150 $b = (strlen("".$b)===1) ? "0".$b:$b;
157 * Given a HEX string returns a RGB array equivalent.
158 * @param string $color
159 * @return array RGB associative array
161 public static function hexToRgb( $color ){
164 $color = self::_checkHex($color);
166 // Convert HEX to DEC
167 $R = hexdec($color[0].$color[1]);
168 $G = hexdec($color[2].$color[3]);
169 $B = hexdec($color[4].$color[5]);
180 * Given an RGB associative array returns the equivalent HEX string
182 * @return string RGB string
183 * @throws Exception "Bad RGB Array"
185 public static function rgbToHex( $rgb = array() ){
186 // Make sure it's RGB
187 if (empty($rgb) || !isset($rgb["R"]) || !isset($rgb["G"]) || !isset($rgb["B"]) ) {
188 throw new Exception("Param was not an RGB array");
191 // https://github.com/mexitek/phpColors/issues/25#issuecomment-88354815
192 // Convert RGB to HEX
193 $hex[0] = str_pad(dechex($rgb['R']), 2, '0', STR_PAD_LEFT);
194 $hex[1] = str_pad(dechex($rgb['G']), 2, '0', STR_PAD_LEFT);
195 $hex[2] = str_pad(dechex($rgb['B']), 2, '0', STR_PAD_LEFT);
197 return implode( '', $hex );
203 * Given a HEX value, returns a darker color. If no desired amount provided, then the color halfway between
204 * given HEX and black will be returned.
206 * @return string Darker HEX value
208 public function darken( $amount = self::DEFAULT_ADJUST ){
210 $darkerHSL = $this->_darken($this->_hsl, $amount);
212 return self::hslToHex($darkerHSL);
216 * Given a HEX value, returns a lighter color. If no desired amount provided, then the color halfway between
217 * given HEX and white will be returned.
219 * @return string Lighter HEX value
221 public function lighten( $amount = self::DEFAULT_ADJUST ){
223 $lighterHSL = $this->_lighten($this->_hsl, $amount);
225 return self::hslToHex($lighterHSL);
229 * Given a HEX value, returns a mixed color. If no desired amount provided, then the color mixed by this ratio
230 * @param string $hex2 Secondary HEX value to mix with
231 * @param int $amount = -100..0..+100
232 * @return string mixed HEX value
234 public function mix($hex2, $amount = 0){
235 $rgb2 = self::hexToRgb($hex2);
236 $mixed = $this->_mix($this->_rgb, $rgb2, $amount);
238 return self::rgbToHex($mixed);
242 * Creates an array with two shades that can be used to make a gradient
243 * @param int $amount Optional percentage amount you want your contrast color
244 * @return array An array with a 'light' and 'dark' index
246 public function makeGradient( $amount = self::DEFAULT_ADJUST ) {
247 // Decide which color needs to be made
248 if ( $this->isLight() ) {
249 $lightColor = $this->_hex;
250 $darkColor = $this->darken($amount);
252 $lightColor = $this->lighten($amount);
253 $darkColor = $this->_hex;
256 // Return our gradient array
257 return array( "light" => $lightColor, "dark" => $darkColor );
262 * Returns whether or not given color is considered "light"
263 * @param string|Boolean $color
264 * @param int $lighterThan
267 public function isLight( $color = FALSE, $lighterThan = 130 ){
269 $color = ($color) ? $color : $this->_hex;
271 // Calculate straight from rbg
272 $r = hexdec($color[0].$color[1]);
273 $g = hexdec($color[2].$color[3]);
274 $b = hexdec($color[4].$color[5]);
276 return (( $r*299 + $g*587 + $b*114 )/1000 > $lighterThan);
280 * Returns whether or not a given color is considered "dark"
281 * @param string|Boolean $color
282 * @param int $darkerThan
285 public function isDark( $color = FALSE, $darkerThan = 130 ){
287 $color = ($color) ? $color:$this->_hex;
289 // Calculate straight from rbg
290 $r = hexdec($color[0].$color[1]);
291 $g = hexdec($color[2].$color[3]);
292 $b = hexdec($color[4].$color[5]);
294 return (( $r*299 + $g*587 + $b*114 )/1000 <= $darkerThan);
298 * Returns the complimentary color
299 * @return string Complementary hex color
302 public function complementary() {
306 // Adjust Hue 180 degrees
307 $hsl['H'] += ($hsl['H']>180) ? -180:180;
309 // Return the new value in HEX
310 return self::hslToHex($hsl);
314 * Returns your color's HSL array
316 public function getHsl() {
320 * Returns your original color
322 public function getHex() {
326 * Returns your color's RGB array
328 public function getRgb() {
333 * Returns the cross browser CSS3 gradient
334 * @param int $amount Optional: percentage amount to light/darken the gradient
335 * @param boolean $vintageBrowsers Optional: include vendor prefixes for browsers that almost died out already
336 * @param string $prefix Optional: prefix for every lines
337 * @param string $suffix Optional: suffix for every lines
338 * @link http://caniuse.com/css-gradients Resource for the browser support
339 * @return string CSS3 gradient for chrome, safari, firefox, opera and IE10
341 public function getCssGradient( $amount = self::DEFAULT_ADJUST, $vintageBrowsers = FALSE, $suffix = "" , $prefix = "" ) {
343 // Get the recommended gradient
344 $g = $this->makeGradient($amount);
347 /* fallback/image non-cover color */
348 $css .= "{$prefix}background-color: #".$this->_hex.";{$suffix}";
351 $css .= "{$prefix}filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#".$g['light']."', endColorstr='#".$g['dark']."');{$suffix}";
353 /* Safari 4+, Chrome 1-9 */
354 if ( $vintageBrowsers ) {
355 $css .= "{$prefix}background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#".$g['light']."), to(#".$g['dark']."));{$suffix}";
358 /* Safari 5.1+, Mobile Safari, Chrome 10+ */
359 $css .= "{$prefix}background-image: -webkit-linear-gradient(top, #".$g['light'].", #".$g['dark'].");{$suffix}";
362 if ( $vintageBrowsers ) {
363 $css .= "{$prefix}background-image: -moz-linear-gradient(top, #".$g['light'].", #".$g['dark'].");{$suffix}";
367 if ( $vintageBrowsers ) {
368 $css .= "{$prefix}background-image: -o-linear-gradient(top, #".$g['light'].", #".$g['dark'].");{$suffix}";
371 /* Unprefixed version (standards): FF 16+, IE10+, Chrome 26+, Safari 7+, Opera 12.1+ */
372 $css .= "{$prefix}background-image: linear-gradient(to bottom, #".$g['light'].", #".$g['dark'].");{$suffix}";
378 // ===========================
379 // = Private Functions Below =
380 // ===========================
384 * Darkens a given HSL array
389 private function _darken( $hsl, $amount = self::DEFAULT_ADJUST){
390 // Check if we were provided a number
392 $hsl['L'] = ($hsl['L'] * 100) - $amount;
393 $hsl['L'] = ($hsl['L'] < 0) ? 0:$hsl['L']/100;
395 // We need to find out how much to darken
396 $hsl['L'] = $hsl['L']/2 ;
403 * Lightens a given HSL array
408 private function _lighten( $hsl, $amount = self::DEFAULT_ADJUST){
409 // Check if we were provided a number
411 $hsl['L'] = ($hsl['L'] * 100) + $amount;
412 $hsl['L'] = ($hsl['L'] > 100) ? 1:$hsl['L']/100;
414 // We need to find out how much to lighten
415 $hsl['L'] += (1-$hsl['L'])/2;
422 * Mix 2 rgb colors and return an rgb color
425 * @param int $amount ranged -100..0..+100
428 * ported from http://phpxref.pagelines.com/nav.html?includes/class.colors.php.source.html
430 private function _mix($rgb1, $rgb2, $amount = 0) {
432 $r1 = ($amount + 100) / 100;
435 $rmix = (($rgb1['R'] * $r1) + ($rgb2['R'] * $r2)) / 2;
436 $gmix = (($rgb1['G'] * $r1) + ($rgb2['G'] * $r2)) / 2;
437 $bmix = (($rgb1['B'] * $r1) + ($rgb2['B'] * $r2)) / 2;
439 return array('R' => $rmix, 'G' => $gmix, 'B' => $bmix);
443 * Given a Hue, returns corresponding RGB value
449 private static function _huetorgb( $v1,$v2,$vH ) {
459 return ($v1 + ($v2 - $v1) * 6 * $vH);
467 return ($v1 + ($v2-$v1) * ( (2/3)-$vH ) * 6);
475 * You need to check if you were given a good hex string
477 * @return string Color
478 * @throws Exception "Bad color format"
480 private static function _checkHex( $hex ) {
481 // Strip # sign is present
482 $color = str_replace("#", "", $hex);
484 // Make sure it's 6 digits
485 if ( strlen($color) == 3 ) {
486 $color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2];
487 } elseif ( strlen($color) != 6 ) {
488 throw new Exception("HEX color needs to be 6 or 3 digits long");