Hi All, I 've been playing around with colour (color!) formats and needed to create a class for converting different formats, so I thought I'd share it. It seems to work quite well and accepts a number of different convenient formats.
The class allows the conversion between HSL, HSV, RGB, CMYK, HEX (base16), Integer (base10) and 'HTML color names'. I found the formulae for conversion from pseudocode in the following locations:
http://www.rapidtables.com/convert/color/ (great colour references!)
http://stackoverflow.com/questions/15202079/convert-hex-color (for the sscanf method)
It should be noted that conversions involving CMYK can only be approximate as there is no absolute formula for them. See
http://en.wikipedia.org/wiki/CMYK_color_model ('Conversion')
A number of formats take an array of values, and as an user's format for any one of these can differ, e.g. associative array, indexed array, string (csv type), then I extended the processing to allow for this.
Internally the class converts all input to rgb and then converts onwards from this.
The class has only one method: convert()
and it takes 3 mandatory parameters $input
, $formatIn
, $formatOut
The possible values for formatIn (string, case-independent)
- hsv
- hsl
- hex
- name
- int
- rgb
- cmyk
The possible values for formatOut (string, case-independent)
- as above for formatIn
- all
The 'all' format returns (obviously) all formats as an array with the keys as listed for the formats. Any array-based formats are indexed, not associative and where values usually require '%' or '#' symbols, these are included.
input
Note that the names of any associative keys are ignored, order is the important factor
hsl: array(120,'100%','50%')
or '120,100%,50%'
or array(120,1,0.5)
or '120,1,0.5'
(for 'lime')
hsv: array(300,'100%','50%')
or '300,100%,50%'
or array(300,1,0.5)
or '300,1,0.5'
(for 'purple')
rgb: array(0,128,0)
or '0,128,0'
or array('red'=>0,'green'=>128,'blue'=>0)
(for 'green')
hex: #FF00FF
or FF00FF
or #F0F
or FOF
(or lowercase equivalents for 'magenta')
name: case-independent HTML names, e.g. 'teal'
, 'olive'
, 'deeppink'
, 'gainsboro'
...
int: a 24-bit integer e.g. 0
('black') to 16777215
('white')
cmyk: array(0,0,1,0)
or '0,0,1,0'
(for 'yellow')
The names 'cyan'
and 'aqua'
have identical values, therefore when converting to name, 'cyan'
will be returned.
The names 'magenta'
and 'fuchsia'
have identical values, therefore when converting to name, 'magenta'
will be returned.
Using 'aqua'
or 'fuchsia'
from name to something else is fine.
$col = new colorCon;
print_r($col->convert('olive','name','all'));
print_r($col->convert(array(20,30,249),'rgb','hex'));
print_r($col->convert('#ff23ac','hex','hsv'));
Array
(
[hex] => #808000
[int] => 8421376
[name] => olive
[hsl] => Array
(
[0] => 60
[1] => 100%
[2] => 25%
)
[cmyk] => Array
(
[0] => 0
[1] => 0
[2] => 1
[3] => 0.49803921568627
)
[hsv] => Array
(
[0] => 60
[1] => 100%
[2] => 50%
)
[rgb] => Array
(
[0] => 128
[1] => 128
[2] => 0
)
)
#141ef9
Array
(
[0] => 0
[1] => 86%
[2] => 100%
)
class colorCon
{
public function convert($input,$formatIn,$formatOut)
{
$formats = array('hex'=>'Hex','int'=>'Int','name'=>'Name','hsl'=>'HSL','cmyk'=>'CMYK','hsv'=>'HSV');
$in = strtolower(trim($formatIn));
$out = strtolower(trim($formatOut));
if(!is_array($input) && strpos($input, ',') !== false) $input = explode(',',$input);
$clean = (is_array($input)) ? array_map('trim',$input) : trim($input);
if((!in_array($in, array_keys($formats)) && $in != 'rgb') || (!in_array($out, array_keys($formats)) && $out != 'all' && $out != 'rgb')) return false;
if($in != 'rgb')
{
$method = 'from' . $formats[$in];
$rgb = $this->$method($clean);
}else{
$rgb = array_values($clean);
}
if($out != 'rgb')
{
if($out == 'all')
{
$output = array();
foreach($formats as $key=>$format)
{
if($key != $in)
{
$method = 'to' . $format;
$output[$key] = $this->$method($rgb);
}else{
$output[$key] = $clean;
}
}
$output['rgb'] = $rgb;
return $output;
}
$method = 'to' . $formats[$out];
return $this->$method($rgb);
}else{
return $rgb;
}
}
private function toHex($rgb)
{
list($r,$g,$b) = $rgb;
$hex = str_pad(base_convert($r,10,16),2,'0',STR_PAD_LEFT);
$hex .= str_pad(base_convert($g,10,16),2,'0',STR_PAD_LEFT);
$hex .= str_pad(base_convert($b,10,16),2,'0',STR_PAD_LEFT);
return '#' .$hex;
}
private function fromHex($hex)
{
switch (strlen($hex))
{
case 7:
$clean = $hex;
break;
case 6:
$clean = '#' . $hex;
break;
case 4:
$clean = '#' . $hex{1} . $hex{1} . $hex{2} . $hex{2} . $hex{3} . $hex{3};
break;
case 3:
$clean = '#' . $hex{0} . $hex{0} . $hex{1} . $hex{1} . $hex{2} . $hex{2};
break;
default:
return false;
}
return sscanf($clean, "#%02x%02x%02x");
}
private function toHSL($rgb)
{
list($r,$g,$b) = $rgb;
//Calculation from http://www.rapidtables.com/convert/color/
$R = $r/255;
$G = $g/255;
$B = $b/255;
$Cx = max(array($R,$G,$B));
$Cn = min(array($R,$G,$B));
$D = $Cx - $Cn;
if($Cx == $B)
{
$hue = ($D == 0) ? 0 : ((($R - $G)/$D) + 4) * 60;
}elseif($Cx == $G){
$hue = ($D == 0) ? 0 : ((($B - $R)/$D) + 2) * 60;
}elseif($Cx == $R){
$hue = ($D == 0) ? 0 : ((($G - $B)/$D) % 6) * 60;
}
if($hue < 0) $hue += 360;
$light = ($Cx + $Cn) / 2;
$sat = ($D == 0) ? 0 : $D/(1-abs((2*$light)-1));
return array( $hue, round($sat*100) .'%', round($light*100) .'%' );
}
private function fromHSL($hsl)
{
list($h,$s,$l) = $hsl;
if($h == 360) $h = 0;
if(!is_numeric($s))
{
$s = intval($s);
}
if($s > 1) $s /= 100;
if(!is_numeric($l))
{
$l = intval($l);
}
if($l > 1) $l /= 100;
//Calculation from http://www.rapidtables.com/convert/color/
$C = (1 - abs(2*$l - 1)) * $s;
$X = $C * (1 - abs(($h / 60) % 2 - 1));
$m = $l - $C/2;
switch (true)
{
case $h < 60:
$rgb = array($C+$m,$X+$m,$m);
break;
case $h < 120:
$rgb = array($X+$m,$C+$m,$m);
break;
case $h < 180:
$rgb = array($m,$C+$m,$X+$m);
break;
case $h < 240:
$rgb = array($m,$X+$m,$C+$m);
break;
case $h < 300:
$rgb = array($X+$m,$m,$C+$m);
break;
case $h < 360:
$rgb = array($C+$m,$m,$X+$m);
break;
}
return array(round($rgb[0]*255),round($rgb[1]*255),round($rgb[2]*255));
}
private function toName($rgb)
{
$int = $this->toInt($rgb);
$ints = array
(
0 => "black",
128 => "navy",
139 => "darkblue",
205 => "mediumblue",
255 => "blue",
25600 => "darkgreen",
32768 => "green",
32896 => "teal",
35723 => "darkcyan",
49151 => "deepskyblue",
52945 => "darkturquoise",
64154 => "mediumspringgreen",
65280 => "lime",
65407 => "springgreen",
65535 => "cyan",
1644912 => "midnightblue",
2003199 => "dodgerblue",
2142890 => "lightseagreen",
2263842 => "forestgreen",
3050327 => "seagreen",
3100495 => "darkslategray",
3329330 => "limegreen",
3978097 => "mediumseagreen",
4251856 => "turquoise",
4286945 => "royalblue",
4620980 => "steelblue",
4734347 => "darkslateblue",
4772300 => "mediumturquoise",
4915330 => "indigo ",
5597999 => "darkolivegreen",
6266528 => "cadetblue",
6591981 => "cornflowerblue",
6737322 => "mediumaquamarine",
6908265 => "dimgray",
6970061 => "slateblue",
7048739 => "olivedrab",
7372944 => "slategray",
7833753 => "lightslategray",
8087790 => "mediumslateblue",
8190976 => "lawngreen",
8388352 => "chartreuse",
8388564 => "aquamarine",
8388608 => "maroon",
8388736 => "purple",
8421376 => "olive",
8421504 => "gray",
8900331 => "skyblue",
8900346 => "lightskyblue",
9055202 => "blueviolet",
9109504 => "darkred",
9109643 => "darkmagenta",
9127187 => "saddlebrown",
9419919 => "darkseagreen",
9498256 => "lightgreen",
9662683 => "mediumpurple",
9699539 => "darkviolet",
10025880 => "palegreen",
10040012 => "darkorchid",
10145074 => "yellowgreen",
10506797 => "sienna",
10824234 => "brown",
11119017 => "darkgray",
11393254 => "lightblue",
11403055 => "greenyellow",
11529966 => "paleturquoise",
11584734 => "lightsteelblue",
11591910 => "powderblue",
11674146 => "firebrick",
12092939 => "darkgoldenrod",
12211667 => "mediumorchid",
12357519 => "rosybrown",
12433259 => "darkkhaki",
12632256 => "silver",
13047173 => "mediumvioletred",
13458524 => "indianred ",
13468991 => "peru",
13789470 => "chocolate",
13808780 => "tan",
13882323 => "lightgray",
14204888 => "thistle",
14315734 => "orchid",
14329120 => "goldenrod",
14381203 => "palevioletred",
14423100 => "crimson",
14474460 => "gainsboro",
14524637 => "plum",
14596231 => "burlywood",
14745599 => "lightcyan",
15132410 => "lavender",
15308410 => "darksalmon",
15631086 => "violet",
15657130 => "palegoldenrod",
15761536 => "lightcoral",
15787660 => "khaki",
15792383 => "aliceblue",
15794160 => "honeydew",
15794175 => "azure",
16032864 => "sandybrown",
16113331 => "wheat",
16119260 => "beige",
16119285 => "whitesmoke",
16121850 => "mintcream",
16316671 => "ghostwhite",
16416882 => "salmon",
16444375 => "antiquewhite",
16445670 => "linen",
16448210 => "lightgoldenrodyellow",
16643558 => "oldlace",
16711680 => "red",
16711935 => "magenta",
16716947 => "deeppink",
16729344 => "orangered",
16737095 => "tomato",
16738740 => "hotpink",
16744272 => "coral",
16747520 => "darkorange",
16752762 => "lightsalmon",
16753920 => "orange",
16758465 => "lightpink",
16761035 => "pink",
16766720 => "gold",
16767673 => "peachpuff",
16768685 => "navajowhite",
16770229 => "moccasin",
16770244 => "bisque",
16770273 => "mistyrose",
16772045 => "blanchedalmond",
16773077 => "papayawhip",
16773365 => "lavenderblush",
16774638 => "seashell",
16775388 => "cornsilk",
16775885 => "lemonchiffon",
16775920 => "floralwhite",
16775930 => "snow",
16776960 => "yellow",
16777184 => "lightyellow",
16777200 => "ivory",
16777215 => "white",
);
return (isset($ints[$int])) ? $ints[$int] : false;
}
private function fromName($name)
{
$colors = array
(
"aliceblue" => array(240,248,255),
"antiquewhite" => array(250,235,215),
"aqua" => array(0,255,255),
"aquamarine" => array(127,255,212),
"azure" => array(240,255,255),
"beige" => array(245,245,220),
"bisque" => array(255,228,196),
"black" => array(0,0,0),
"blanchedalmond" => array(255,235,205),
"blue" => array(0,0,255),
"blueviolet" => array(138,43,226),
"brown" => array(165,42,42),
"burlywood" => array(222,184,135),
"cadetblue" => array(95,158,160),
"chartreuse" => array(127,255,0),
"chocolate" => array(210,105,30),
"coral" => array(255,127,80),
"cornflowerblue" => array(100,149,237),
"cornsilk" => array(255,248,220),
"crimson" => array(220,20,60),
"cyan" => array(0,255,255),
"darkblue" => array(0,0,139),
"darkcyan" => array(0,139,139),
"darkgoldenrod" => array(184,134,11),
"darkgray" => array(169,169,169),
"darkgreen" => array(0,100,0),
"darkkhaki" => array(189,183,107),
"darkmagenta" => array(139,0,139),
"darkolivegreen" => array(85,107,47),
"darkorange" => array(255,140,0),
"darkorchid" => array(153,50,204),
"darkred" => array(139,0,0),
"darksalmon" => array(233,150,122),
"darkseagreen" => array(143,188,143),
"darkslateblue" => array(72,61,139),
"darkslategray" => array(47,79,79),
"darkturquoise" => array(0,206,209),
"darkviolet" => array(148,0,211),
"deeppink" => array(255,20,147),
"deepskyblue" => array(0,191,255),
"dimgray" => array(105,105,105),
"dodgerblue" => array(30,144,255),
"firebrick" => array(178,34,34),
"floralwhite" => array(255,250,240),
"forestgreen" => array(34,139,34),
"fuchsia" => array(255,0,255),
"gainsboro" => array(220,220,220),
"ghostwhite" => array(248,248,255),
"gold" => array(255,215,0),
"goldenrod" => array(218,165,32),
"gray" => array(128,128,128),
"green" => array(0,128,0),
"greenyellow" => array(173,255,47),
"honeydew" => array(240,255,240),
"hotpink" => array(255,105,180),
"indianred " => array(205,92,92),
"indigo " => array(75,0,130),
"ivory" => array(255,255,240),
"khaki" => array(240,230,140),
"lavender" => array(230,230,250),
"lavenderblush" => array(255,240,245),
"lawngreen" => array(124,252,0),
"lemonchiffon" => array(255,250,205),
"lightblue" => array(173,216,230),
"lightcoral" => array(240,128,128),
"lightcyan" => array(224,255,255),
"lightgoldenrodyellow" => array(250,250,210),
"lightgray" => array(211,211,211),
"lightgreen" => array(144,238,144),
"lightpink" => array(255,182,193),
"lightsalmon" => array(255,160,122),
"lightseagreen" => array(32,178,170),
"lightskyblue" => array(135,206,250),
"lightslategray" => array(119,136,153),
"lightsteelblue" => array(176,196,222),
"lightyellow" => array(255,255,224),
"lime" => array(0,255,0),
"limegreen" => array(50,205,50),
"linen" => array(250,240,230),
"magenta" => array(255,0,255),
"maroon" => array(128,0,0),
"mediumaquamarine" => array(102,205,170),
"mediumblue" => array(0,0,205),
"mediumorchid" => array(186,85,211),
"mediumpurple" => array(147,112,219),
"mediumseagreen" => array(60,179,113),
"mediumslateblue" => array(123,104,238),
"mediumspringgreen" => array(0,250,154),
"mediumturquoise" => array(72,209,204),
"mediumvioletred" => array(199,21,133),
"midnightblue" => array(25,25,112),
"mintcream" => array(245,255,250),
"mistyrose" => array(255,228,225),
"moccasin" => array(255,228,181),
"navajowhite" => array(255,222,173),
"navy" => array(0,0,128),
"oldlace" => array(253,245,230),
"olive" => array(128,128,0),
"olivedrab" => array(107,142,35),
"orange" => array(255,165,0),
"orangered" => array(255,69,0),
"orchid" => array(218,112,214),
"palegoldenrod" => array(238,232,170),
"palegreen" => array(152,251,152),
"paleturquoise" => array(175,238,238),
"palevioletred" => array(219,112,147),
"papayawhip" => array(255,239,213),
"peachpuff" => array(255,218,185),
"peru" => array(205,133,63),
"pink" => array(255,192,203),
"plum" => array(221,160,221),
"powderblue" => array(176,224,230),
"purple" => array(128,0,128),
"red" => array(255,0,0),
"rosybrown" => array(188,143,143),
"royalblue" => array(65,105,225),
"saddlebrown" => array(139,69,19),
"salmon" => array(250,128,114),
"sandybrown" => array(244,164,96),
"seagreen" => array(46,139,87),
"seashell" => array(255,245,238),
"sienna" => array(160,82,45),
"silver" => array(192,192,192),
"skyblue" => array(135,206,235),
"slateblue" => array(106,90,205),
"slategray" => array(112,128,144),
"snow" => array(255,250,250),
"springgreen" => array(0,255,127),
"steelblue" => array(70,130,180),
"tan" => array(210,180,140),
"teal" => array(0,128,128),
"thistle" => array(216,191,216),
"tomato" => array(255,99,71),
"turquoise" => array(64,224,208),
"violet" => array(238,130,238),
"wheat" => array(245,222,179),
"white" => array(255,255,255),
"whitesmoke" => array(245,245,245),
"yellow" => array(255,255,0),
"yellowgreen" => array(154,205,50),
);
$n = strtolower(trim($name));
if(in_array($n,array_keys($colors)))
{
return $colors[$n];
}
return false;
}
private function fromInt($int)
{
$clean = (int) $int;
$b = $clean & 255;
$g = ($clean >> 8) & 255;
$r = ($clean >> 16) & 255;
return array($r,$g,$b);
}
private function toInt($rgb)
{
list($r,$g,$b) = $rgb;
return 65536 * $r + 256 * $g + $b;
}
private function toCMYK($rgb)
{
list($r,$g,$b) = $rgb;
//Calculation from http://www.rapidtables.com/convert/color/
$R = $r/255;
$G = $g/255;
$B = $b/255;
$k = 1 - max($R,$G,$B);
$c = (1 - $R - $k) / (1 - $k);
$m = (1 - $G - $k) / (1 - $k);
$y = (1 - $B - $k) / (1 - $k);
return array($c,$m,$y,$k);
}
private function fromCMYK($cmyk)
{
list($c,$m,$y,$k) = $cmyk;
$r = 255 * (1 - $c) * (1 - $k);
$g = 255 * (1 - $m) * (1 - $k);
$b = 255 * (1 - $y) * (1 - $k);
return array(round($r),round($g),round($b));
}
private function toHSV($rgb)
{
list($r,$g,$b) = $rgb;
//Calculation from http://www.rapidtables.com/convert/color/
$R = $r/255;
$G = $g/255;
$B = $b/255;
$Cx = max(array($R,$G,$B));
$Cn = min(array($R,$G,$B));
$D = $Cx - $Cn;
if($Cx == $B)
{
$hue = ($D == 0) ? 0 : ((($R - $G)/$D) + 4) * 60;
}elseif($Cx == $G){
$hue = ($D == 0) ? 0 : ((($B - $R)/$D) + 2) * 60;
}elseif($Cx == $R){
$hue = ($D == 0) ? 0 : ((($G - $B)/$D) % 6) * 60;
}
if($hue < 0) $hue += 360;
$value = $Cx;
$sat = ($D == 0) ? 0 : $D/$Cx;
return array( $hue, round($sat*100) .'%', round($value*100) .'%' );
}
/**
*
* @param mixed $hsv (csv string or indexed/associative array)
*
* @return array ((int) red, (int) green, (int) blue) where int 0 - 255
*/
private function fromHSV($hsv)
{
list($h,$s,$v) = $hsv;
if($h == 360) $h = 0;
//To get rid of % signs
if(!is_numeric($s))
{
$s = intval($s);
}
//If value in %age -> decimal
if($s > 1) $s /= 100;
//To get rid of % signs
if(!is_numeric($v))
{
$v = intval($v);
}
//If value as %age -> decimal
if($v > 1) $v /= 100;
//Calculation from http://www.rapidtables.com/convert/color/
$C = $v * $s;
$X = $C * (1 - abs(($h / 60) % 2 - 1));
$m = $v - $C;
switch (true)
{
case $h < 60:
$rgb = array($C+$m,$X+$m,$m);
break;
case $h < 120:
$rgb = array($X+$m,$C+$m,$m);
break;
case $h < 180:
$rgb = array($m,$C+$m,$X+$m);
break;
case $h < 240:
$rgb = array($m,$X+$m,$C+$m);
break;
case $h < 300:
$rgb = array($X+$m,$m,$C+$m);
break;
case $h < 360:
$rgb = array($C+$m,$m,$X+$m);
break;
}
return array(round($rgb[0]*255),round($rgb[1]*255),round($rgb[2]*255));
}
}
Any feeback including possible improvements most welcome :)
I am thinking about extending it to include opacity for HSLa and RGBa. Maybe other formats too.