Created
January 20, 2010 07:36
-
-
Save soska/281685 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Color class | |
* | |
* Plugin forn CSScaffold for color manipulation and stuff | |
* use like this: | |
* body{ | |
* background: color-multiply(!colora,#900); | |
* } | |
* | |
* @author Armando Sosa (nolimit-studio.com) | |
* @dependencies None | |
**/ | |
class Color extends Scaffold_Module | |
{ | |
static private $helper; | |
/** | |
* Returns an single instance of the Color helper | |
* | |
* @return void | |
* @author Armando Sosa | |
*/ | |
static function getHelper(){ | |
if (!self::$helper) { | |
self::$helper = new ColorHelper; | |
} | |
return self::$helper; | |
} | |
/** | |
* The second last process, should only be getting everything | |
* syntaxically correct, rather than doing any heavy processing | |
* | |
* @author Armando Sosa | |
* @return $css string | |
*/ | |
public static function process() | |
{ | |
if($found = CSS::find_properties_with_value('[^:|^}]*?','color-(.*?)(\((.+?)\))?\s?')) | |
{ | |
$color = self::gethelper(); | |
$methods = $found[4]; | |
$argss = $found[6]; | |
foreach ($methods as $i=>$method) { | |
$args = explode(',',$argss[$i]); | |
if (method_exists($color,$method)) { | |
$r = call_user_func_array(array($color,$method),$args); | |
CSS::replace($found[3][$i], $r); | |
} | |
} | |
} | |
} | |
} | |
class ColorHelper{ | |
function add($top,$bottom){ | |
return $this->__basicRGBBlend($top,$bottom,'add'); | |
} | |
function substract($top,$bottom){ | |
return $this->__basicRGBBlend($top,$bottom,'substract'); | |
} | |
function screen($top,$bottom){ | |
return $this->__basicRGBBlend($top,$bottom,'screen'); | |
} | |
function multiply($top,$bottom){ | |
return $this->__basicRGBBlend($top,$bottom,'multiply'); | |
} | |
function divide($top,$bottom){ | |
return $this->__basicRGBBlend($top,$bottom,'divide'); | |
} | |
function difference($top,$bottom){ | |
return $this->__basicRGBBlend($top,$bottom,'difference'); | |
} | |
function lighten($top,$ratio=.5){ | |
// only accept numbers from 0 to 1; | |
$ratio = $this->__keepInrange($ratio,0,1); | |
return $this->__basicHSVBlend($top,$ratio,'lighten'); | |
} | |
function darken($top,$ratio=.5){ | |
// only accept numbers from 0 to 1; | |
$ratio = $this->__keepInrange($ratio,0,1); | |
return $this->__basicHSVBlend($top,$ratio,'darken'); | |
} | |
function invert($top,$ratio=1){ | |
// only accept numbers from 0 to 1; | |
$ratio = $this->__keepInrange($ratio,0,1); | |
return $this->__basicRGBBlend($top,$ratio,'invert'); | |
} | |
function shadow($top,$ratio=.5){ | |
// only accept numbers from 0 to 1; | |
$ratio = $this->__keepInrange($ratio,0,1); | |
return $this->__basicHSVBlend($top,$ratio,'shadow'); | |
} | |
function illuminate($top,$ratio=.5){ | |
// only accept numbers from 0 to 1; | |
$ratio = $this->__keepInrange($ratio,0,1); | |
return $this->__basicHSVBlend($top,$ratio,'illuminate'); | |
} | |
function contrast($top,$ratio=.5){ | |
// only accept numbers from 0 to 2; | |
$ratio = $this->__keepInrange($ratio,0,2); | |
return $this->__basicHSVBlend($top,$ratio,'contrast'); | |
} | |
function complement($color){ | |
if (!$this->__isHSV($color)) { | |
$color = $this->__RGBToHSV($this->__hexToRGB($color)); | |
} | |
$color = $this->adjust($color,'h',180); | |
return $this->__RGBToHex($this->__HSVToRGB($color)); | |
} | |
function __basicRGBBlend($top,$bottom,$type = 'multiply'){ | |
// first, transform colors into rgb arrays | |
$top = $this->__hexToRGB($top); | |
$bottom = $this->__hexToRGB($bottom); | |
$channels = array('r','g','b'); | |
$result = array(); | |
foreach ($channels as $channel) { | |
$a = $top[$channel]; | |
$b = $bottom[$channel]; | |
switch ($type) { | |
case 'multiply': | |
$result[$channel] = floor($a * $b / 255); | |
break; | |
case 'screen': | |
$result[$channel] = floor(255 - (((255 - $a) * (255 - $b)) / 255)); | |
break; | |
case 'add': | |
$value = $a+$b; | |
$result[$channel] = $this->__keepInRGBRange($value); | |
break; | |
case 'substract': | |
$value = $a-$b; | |
$result[$channel] = $this->__keepInRGBRange($value); | |
break; | |
case 'divide': | |
if ($b == 0) $b = 1; | |
$value = ceil(($a/$b))*4; | |
$result[$channel] = $this->__keepInRGBRange($value); | |
break; | |
case 'difference': | |
$value = $a-$b; | |
if ($value < 0) { | |
$value = $b-$a; | |
} | |
$result[$channel] = $value; | |
break; | |
case 'invert': | |
$value = $a-(255*$b); | |
if ($value<0) { | |
$value = 1-$value; | |
} | |
$result[$channel] = $this->__keepInRGBRange($value); | |
break; | |
default: | |
$result[$channel] = $top[$channel]; | |
break; | |
} | |
} | |
return $this->__RGBToHex($result); | |
} | |
function __basicHSVBlend($top,$ratio = 1,$type = 'lighten'){ | |
if (!$this->__isHSV($top)) { | |
$top = $this->__RGBToHSV($this->__hexToRGB($top)); | |
} | |
switch ($type) { | |
case 'lighten': | |
$top = $this->adjust($top,'v',($ratio * 100)); | |
break; | |
case 'darken': | |
$top = $this->adjust($top,'v',1-($ratio * 100)); | |
break; | |
case 'illuminate': | |
$top = $this->adjust($top,'v',($ratio * 100)); | |
$top = $this->adjust($top,'s',1-($ratio * 50)); | |
// yellowish colors deserve a special treatment, | |
if ($top['h']>29 && $top['h']<61) { | |
$top = $this->adjust($top,'s',($ratio * 15)); | |
$top = $this->adjust($top,'h',$ratio * 10); | |
} | |
break; | |
case 'shadow': | |
$top = $this->adjust($top,'v',1-($ratio * 100)); | |
$top = $this->adjust($top,'s',($ratio * 50)); | |
if ($top['h']>29 && $top['h']<70) { | |
$top = $this->adjust($top,'v',($ratio * 75)); | |
$top = $this->adjust($top,'h',1-($ratio * 10)); | |
} | |
break; | |
case 'contrast': | |
$ammount = (100-$top['v'])*$ratio; | |
if ($top['v'] == 50) { | |
$ammount = 50; | |
}elseif ($top['v'] == 100) { | |
$ammount = -50; | |
} elseif ($top['v']>50) { | |
$compensation = round(($top['v']-50)/2.5); | |
$ammount = 1-$ammount*$compensation; | |
} | |
$top = $this->adjust($top,'v',$ammount); | |
$top = $this->adjust($top,'s',1-($ammount/4)); | |
// $top = $this->adjust($top,'v',1-($top['v']-(50*$ratio))); | |
break; | |
} | |
return $this->__RGBToHex($this->__HSVToRGB($top)); | |
} | |
function __keepInRGBRange($value){ | |
return $this->__keepInRange($value,0,255); | |
} | |
function __keepInRange($value,$min,$max){ | |
if ($value <$min) { | |
return $min; | |
}elseif ($value>$max) { | |
return $max; | |
} | |
return $value; | |
} | |
function __RGBToHex($rgb){ | |
if (!$this->__isRGB($rgb)) { | |
return false; | |
} | |
$hex = "#"; | |
$channels = array('r','g','b'); | |
foreach ($channels as $channel) { | |
$hex.=str_pad(dechex($rgb[$channel]),2,"0",STR_PAD_LEFT); | |
} | |
return $hex; | |
} | |
function __hexToRGB($hex){ | |
if (!is_string($hex) && !is_array($hex)) { | |
$hex = array('r'=>$hex,'g'=>$hex,'b'=>$hex); | |
} | |
if ($this->__isRGB($hex)) { | |
return $hex; | |
} | |
$hex = trim(str_replace('#','',$hex)); | |
$len = strlen($hex); | |
if ( $len == 3) { | |
$chopsize = 1; | |
}elseif ( $len == 6) { | |
$chopsize = 2; | |
}else{ | |
return false; | |
} | |
$color = array(); | |
list($color['r'],$color['g'],$color['b']) = str_split($hex,$chopsize); | |
foreach ($color as &$channel) { | |
if (strlen($channel) == 1) { | |
$channel.=$channel; | |
} | |
$channel = hexdec($channel); | |
} | |
return $color; | |
} | |
function __isRGB($color){ | |
return (is_array($color) && isset($color['r']) && isset($color['g']) && isset($color['b'])); | |
} | |
function __isHSV($color){ | |
return (is_array($color) && isset($color['h']) && isset($color['s']) && isset($color['v'])); | |
} | |
function __RGBToHSV($rgb){ | |
if (!$this->__isRGB($rgb)) { | |
$rgb = $this->__hexToRGB($rgb); | |
} | |
$hsv = array(); | |
extract($rgb); | |
$max = max($r,$g,$b); | |
$min = min($r,$g,$b); | |
$difference = $max-$min; | |
$hsv['v'] = round(($max/255) * 100); | |
if ($difference == 0) { | |
$hsv['s'] = $max; | |
$hsv['h'] = false; | |
}else{ | |
$hsv['s'] = round($difference/$max * 100); | |
switch ($max) { | |
case $r: | |
$gminusb = ($g-$b); | |
if ($gminusb < 2){ | |
$hsv['h'] = 360 - $gminusb; | |
}else{ | |
$hsv['h'] = $gminusb / $difference; | |
} | |
// pr(compact('hsv','rgb','difference')); | |
// die; | |
break; | |
case $g: | |
$hsv['h'] = 2 + ($b-$r) / $difference; | |
break; | |
case $b: | |
$hsv['h'] = 4 + ($r-$g) / $difference; | |
break; | |
} | |
$hsv['h'] = round($hsv['h'] * 60); | |
if ($hsv['h'] > 360) { | |
$hsv['h'] = 360; | |
} | |
if ($hsv['h'] < 0) { | |
$hsv['h'] += 360; | |
} | |
// pr($hsv);die; | |
} | |
return $hsv; | |
} | |
function __HSVToRGB($hsv){ | |
extract($hsv); | |
$v = $v/100; | |
$s = $s/100; | |
if ($h == false) { | |
$v = floor($v*255); | |
list($r,$g,$b) = array($v,$v,$v); | |
}else{ | |
$h = $h / 60; | |
$i = floor($h); | |
$f = $h - $i; | |
if (!($i & 1)) { | |
$f = 1-$f; | |
} | |
$m = $v * (1 - $s); | |
$n = $v * (1 - $s * $f); | |
$v = floor(255 * $v); | |
$m = floor(255 * $m); | |
$n = floor(255 * $n); | |
switch ($i) { | |
case 6: | |
case 0: | |
$r = $v; | |
$g = $n; | |
$b = $m; | |
break; | |
case 1: | |
$r = $n; | |
$g = $v; | |
$b = $m; | |
break; | |
case 2: | |
$r = $m; | |
$g = $v; | |
$b = $n; | |
break; | |
case 3: | |
$r = $m; | |
$g = $n; | |
$b = $v; | |
break; | |
case 4: | |
$r = $n; | |
$g = $m; | |
$b = $v; | |
break; | |
case 5: | |
$r = $v; | |
$g = $m; | |
$b = $n; | |
break; | |
} | |
} | |
return compact('r','g','b'); | |
} | |
function adjust($hsv,$channel = 'h',$ratio = 1){ | |
if ($channel == "h"){ | |
$hsv['h'] += round($ratio); | |
if ($hsv['h'] < 0) { | |
$hsv['h'] += 360; | |
} | |
if ($hsv['h'] > 360) { | |
$hsv['h'] -= 360; | |
} | |
return $hsv; | |
} | |
$hsv[$channel] += round($ratio); | |
$hsv[$channel] = $this->__keepInRange($hsv[$channel],0,100); | |
return $hsv; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment