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
20 * Auto darkens/lightens by 10% for sexily-subtle gradients.
21 * Set this to FALSE to adjust automatic shade to be between given color
22 * and black (for darken) or white (for lighten)
24 const DEFAULT_ADJUST = 10;
27 * Instantiates the class with a HEX value
29 * @throws Exception "Bad color format"
31 function __construct( $hex ) {
32 // Strip # sign is present
33 $color = str_replace("#", "", $hex);
35 // Make sure it's 6 digits
36 if( strlen($color) === 3 ) {
37 $color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2];
38 } else if( strlen($color) != 6 ) {
39 throw new Exception("HEX color needs to be 6 or 3 digits long");
42 $this->_hsl = self::hexToHsl( $color );
44 $this->_rgb = self::hexToRgb( $color );
47 // ====================
48 // = Public Interface =
49 // ====================
52 * Given a HEX string returns a HSL array equivalent.
53 * @param string $color
54 * @return array HSL associative array
56 public static function hexToHsl( $color ){
59 $color = self::_checkHex($color);
62 $R = hexdec($color[0].$color[1]);
63 $G = hexdec($color[2].$color[3]);
64 $B = hexdec($color[4].$color[5]);
72 $var_Min = min($var_R, $var_G, $var_B);
73 $var_Max = max($var_R, $var_G, $var_B);
74 $del_Max = $var_Max - $var_Min;
76 $L = ($var_Max + $var_Min)/2;
85 if ( $L < 0.5 ) $S = $del_Max / ( $var_Max + $var_Min );
86 else $S = $del_Max / ( 2 - $var_Max - $var_Min );
88 $del_R = ( ( ( $var_Max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
89 $del_G = ( ( ( $var_Max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
90 $del_B = ( ( ( $var_Max - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
92 if ($var_R == $var_Max) $H = $del_B - $del_G;
93 else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B;
94 else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R;
100 $HSL['H'] = ($H*360);
108 * Given a HSL associative array returns the equivalent HEX string
110 * @return string HEX string
111 * @throws Exception "Bad HSL Array"
113 public static function hslToHex( $hsl = [] ){
114 // Make sure it's HSL
115 if(empty($hsl) || !isset($hsl["H"]) || !isset($hsl["S"]) || !isset($hsl["L"]) ) {
116 throw new Exception("Param was not an HSL array");
119 list($H,$S,$L) = [ $hsl['H']/360,$hsl['S'],$hsl['L'] ];
130 $var_2 = ($L+$S) - ($S*$L);
133 $var_1 = 2 * $L - $var_2;
135 $r = round(255 * self::_huetorgb( $var_1, $var_2, $H + (1/3) ));
136 $g = round(255 * self::_huetorgb( $var_1, $var_2, $H ));
137 $b = round(255 * self::_huetorgb( $var_1, $var_2, $H - (1/3) ));
146 // Make sure we get 2 digits for decimals
147 $r = (strlen("".$r)===1) ? "0".$r:$r;
148 $g = (strlen("".$g)===1) ? "0".$g:$g;
149 $b = (strlen("".$b)===1) ? "0".$b:$b;
156 * Given a HEX string returns a RGB array equivalent.
157 * @param string $color
158 * @return array RGB associative array
160 public static function hexToRgb( $color ){
163 $color = self::_checkHex($color);
165 // Convert HEX to DEC
166 $R = hexdec($color[0].$color[1]);
167 $G = hexdec($color[2].$color[3]);
168 $B = hexdec($color[4].$color[5]);
179 * Given an RGB associative array returns the equivalent HEX string
181 * @return string RGB string
182 * @throws Exception "Bad RGB Array"
184 public static function rgbToHex( $rgb = [] ){
185 // Make sure it's RGB
186 if(empty($rgb) || !isset($rgb["R"]) || !isset($rgb["G"]) || !isset($rgb["B"]) ) {
187 throw new Exception("Param was not an RGB array");
190 // https://github.com/mexitek/phpColors/issues/25#issuecomment-88354815
191 // Convert RGB to HEX
192 $hex[0] = str_pad(dechex($rgb['R']), 2, '0', STR_PAD_LEFT);
193 $hex[1] = str_pad(dechex($rgb['G']), 2, '0', STR_PAD_LEFT);
194 $hex[2] = str_pad(dechex($rgb['B']), 2, '0', STR_PAD_LEFT);
196 return implode( '', $hex );
202 * Given a HEX value, returns a darker color. If no desired amount provided, then the color halfway between
203 * given HEX and black will be returned.
205 * @return string Darker HEX value
207 public function darken( $amount = self::DEFAULT_ADJUST ){
209 $darkerHSL = $this->_darken($this->_hsl, $amount);
211 return self::hslToHex($darkerHSL);
215 * Given a HEX value, returns a lighter color. If no desired amount provided, then the color halfway between
216 * given HEX and white will be returned.
218 * @return string Lighter HEX value
220 public function lighten( $amount = self::DEFAULT_ADJUST ){
222 $lighterHSL = $this->_lighten($this->_hsl, $amount);
224 return self::hslToHex($lighterHSL);
228 * Given a HEX value, returns a mixed color. If no desired amount provided, then the color mixed by this ratio
229 * @param string $hex2 Secondary HEX value to mix with
230 * @param int $amount = -100..0..+100
231 * @return string mixed HEX value
233 public function mix($hex2, $amount = 0){
234 $rgb2 = self::hexToRgb($hex2);
235 $mixed = $this->_mix($this->_rgb, $rgb2, $amount);
237 return self::rgbToHex($mixed);
241 * Creates an array with two shades that can be used to make a gradient
242 * @param int $amount Optional percentage amount you want your contrast color
243 * @return array An array with a 'light' and 'dark' index
245 public function makeGradient( $amount = self::DEFAULT_ADJUST ) {
246 // Decide which color needs to be made
247 if( $this->isLight() ) {
248 $lightColor = $this->_hex;
249 $darkColor = $this->darken($amount);
251 $lightColor = $this->lighten($amount);
252 $darkColor = $this->_hex;
255 // Return our gradient array
256 return [ "light" => $lightColor, "dark" => $darkColor ];
261 * Returns whether or not given color is considered "light"
262 * @param string|Boolean $color
263 * @param int $lighterThan
266 public function isLight( $color = FALSE, $lighterThan = 130 ){
268 $color = ($color) ? $color : $this->_hex;
270 // Calculate straight from rbg
271 $r = hexdec($color[0].$color[1]);
272 $g = hexdec($color[2].$color[3]);
273 $b = hexdec($color[4].$color[5]);
275 return (( $r*299 + $g*587 + $b*114 )/1000 > $lighterThan);
279 * Returns whether or not a given color is considered "dark"
280 * @param string|Boolean $color
281 * @param int $darkerThan
284 public function isDark( $color = FALSE, $darkerThan = 130 ){
286 $color = ($color) ? $color:$this->_hex;
288 // Calculate straight from rbg
289 $r = hexdec($color[0].$color[1]);
290 $g = hexdec($color[2].$color[3]);
291 $b = hexdec($color[4].$color[5]);
293 return (( $r*299 + $g*587 + $b*114 )/1000 <= $darkerThan);
297 * Returns the complimentary color
298 * @return string Complementary hex color
301 public function complementary() {
305 // Adjust Hue 180 degrees
306 $hsl['H'] += ($hsl['H']>180) ? -180:180;
308 // Return the new value in HEX
309 return self::hslToHex($hsl);
313 * Returns your color's HSL array
315 public function getHsl() {
319 * Returns your original color
321 public function getHex() {
325 * Returns your color's RGB array
327 public function getRgb() {
332 * Returns the cross browser CSS3 gradient
333 * @param int $amount Optional: percentage amount to light/darken the gradient
334 * @param boolean $vintageBrowsers Optional: include vendor prefixes for browsers that almost died out already
335 * @param string $prefix Optional: prefix for every lines
336 * @param string $suffix Optional: suffix for every lines
337 * @link http://caniuse.com/css-gradients Resource for the browser support
338 * @return string CSS3 gradient for chrome, safari, firefox, opera and IE10
340 public function getCssGradient( $amount = self::DEFAULT_ADJUST, $vintageBrowsers = FALSE, $suffix = "" , $prefix = "" ) {
342 // Get the recommended gradient
343 $g = $this->makeGradient($amount);
346 /* fallback/image non-cover color */
347 $css .= "{$prefix}background-color: #".$this->_hex.";{$suffix}";
350 $css .= "{$prefix}filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#".$g['light']."', endColorstr='#".$g['dark']."');{$suffix}";
352 /* Safari 4+, Chrome 1-9 */
353 if ( $vintageBrowsers ) {
354 $css .= "{$prefix}background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#".$g['light']."), to(#".$g['dark']."));{$suffix}";
357 /* Safari 5.1+, Mobile Safari, Chrome 10+ */
358 $css .= "{$prefix}background-image: -webkit-linear-gradient(top, #".$g['light'].", #".$g['dark'].");{$suffix}";
361 if ( $vintageBrowsers ) {
362 $css .= "{$prefix}background-image: -moz-linear-gradient(top, #".$g['light'].", #".$g['dark'].");{$suffix}";
366 if ( $vintageBrowsers ) {
367 $css .= "{$prefix}background-image: -o-linear-gradient(top, #".$g['light'].", #".$g['dark'].");{$suffix}";
370 /* Unprefixed version (standards): FF 16+, IE10+, Chrome 26+, Safari 7+, Opera 12.1+ */
371 $css .= "{$prefix}background-image: linear-gradient(to bottom, #".$g['light'].", #".$g['dark'].");{$suffix}";
377 // ===========================
378 // = Private Functions Below =
379 // ===========================
383 * Darkens a given HSL array
388 private function _darken( $hsl, $amount = self::DEFAULT_ADJUST){
389 // Check if we were provided a number
391 $hsl['L'] = ($hsl['L'] * 100) - $amount;
392 $hsl['L'] = ($hsl['L'] < 0) ? 0:$hsl['L']/100;
394 // We need to find out how much to darken
395 $hsl['L'] = $hsl['L']/2 ;
402 * Lightens a given HSL array
407 private function _lighten( $hsl, $amount = self::DEFAULT_ADJUST){
408 // Check if we were provided a number
410 $hsl['L'] = ($hsl['L'] * 100) + $amount;
411 $hsl['L'] = ($hsl['L'] > 100) ? 1:$hsl['L']/100;
413 // We need to find out how much to lighten
414 $hsl['L'] += (1-$hsl['L'])/2;
421 * Mix 2 rgb colors and return an rgb color
424 * @param int $amount ranged -100..0..+100
427 * ported from http://phpxref.pagelines.com/nav.html?includes/class.colors.php.source.html
429 private function _mix($rgb1, $rgb2, $amount = 0) {
431 $r1 = ($amount + 100) / 100;
434 $rmix = (($rgb1['R'] * $r1) + ($rgb2['R'] * $r2)) / 2;
435 $gmix = (($rgb1['G'] * $r1) + ($rgb2['G'] * $r2)) / 2;
436 $bmix = (($rgb1['B'] * $r1) + ($rgb2['B'] * $r2)) / 2;
438 return ['R' => $rmix, 'G' => $gmix, 'B' => $bmix];
442 * Given a Hue, returns corresponding RGB value
448 private static function _huetorgb( $v1,$v2,$vH ) {
458 return ($v1 + ($v2 - $v1) * 6 * $vH);
466 return ($v1 + ($v2-$v1) * ( (2/3)-$vH ) * 6);
474 * You need to check if you were given a good hex string
476 * @return string Color
477 * @throws Exception "Bad color format"
479 private static function _checkHex( $hex ) {
480 // Strip # sign is present
481 $color = str_replace("#", "", $hex);
483 // Make sure it's 6 digits
484 if( strlen($color) == 3 ) {
485 $color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2];
486 } else if( strlen($color) != 6 ) {
487 throw new Exception("HEX color needs to be 6 or 3 digits long");