You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
5.1 KiB
230 lines
5.1 KiB
<?php
|
|
|
|
/**
|
|
* Color
|
|
*
|
|
* @package Less
|
|
* @subpackage tree
|
|
*/
|
|
class Less_Tree_Color extends Less_Tree{
|
|
public $rgb;
|
|
public $alpha;
|
|
public $isTransparentKeyword;
|
|
public $type = 'Color';
|
|
|
|
public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){
|
|
|
|
if( $isTransparentKeyword ){
|
|
$this->rgb = $rgb;
|
|
$this->alpha = $a;
|
|
$this->isTransparentKeyword = true;
|
|
return;
|
|
}
|
|
|
|
$this->rgb = array();
|
|
if( is_array($rgb) ){
|
|
$this->rgb = $rgb;
|
|
}else if( strlen($rgb) == 6 ){
|
|
foreach(str_split($rgb, 2) as $c){
|
|
$this->rgb[] = hexdec($c);
|
|
}
|
|
}else{
|
|
foreach(str_split($rgb, 1) as $c){
|
|
$this->rgb[] = hexdec($c.$c);
|
|
}
|
|
}
|
|
$this->alpha = is_numeric($a) ? $a : 1;
|
|
}
|
|
|
|
public function compile(){
|
|
return $this;
|
|
}
|
|
|
|
public function luma(){
|
|
$r = $this->rgb[0] / 255;
|
|
$g = $this->rgb[1] / 255;
|
|
$b = $this->rgb[2] / 255;
|
|
|
|
$r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
|
|
$g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
|
|
$b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
|
|
|
|
return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
|
|
}
|
|
|
|
/**
|
|
* @see Less_Tree::genCSS
|
|
*/
|
|
public function genCSS( $output ){
|
|
$output->add( $this->toCSS() );
|
|
}
|
|
|
|
public function toCSS( $doNotCompress = false ){
|
|
$compress = Less_Parser::$options['compress'] && !$doNotCompress;
|
|
$alpha = Less_Functions::fround( $this->alpha );
|
|
|
|
|
|
//
|
|
// If we have some transparency, the only way to represent it
|
|
// is via `rgba`. Otherwise, we use the hex representation,
|
|
// which has better compatibility with older browsers.
|
|
// Values are capped between `0` and `255`, rounded and zero-padded.
|
|
//
|
|
if( $alpha < 1 ){
|
|
if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){
|
|
return 'transparent';
|
|
}
|
|
|
|
$values = array();
|
|
foreach($this->rgb as $c){
|
|
$values[] = Less_Functions::clamp( round($c), 255);
|
|
}
|
|
$values[] = $alpha;
|
|
|
|
$glue = ($compress ? ',' : ', ');
|
|
return "rgba(" . implode($glue, $values) . ")";
|
|
}else{
|
|
|
|
$color = $this->toRGB();
|
|
|
|
if( $compress ){
|
|
|
|
// Convert color to short format
|
|
if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
|
|
$color = '#'.$color[1] . $color[3] . $color[5];
|
|
}
|
|
}
|
|
|
|
return $color;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Operations have to be done per-channel, if not,
|
|
// channels will spill onto each other. Once we have
|
|
// our result, in the form of an integer triplet,
|
|
// we create a new Color node to hold the result.
|
|
//
|
|
|
|
/**
|
|
* @param string $op
|
|
*/
|
|
public function operate( $op, $other) {
|
|
$rgb = array();
|
|
$alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
|
|
for ($c = 0; $c < 3; $c++) {
|
|
$rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]);
|
|
}
|
|
return new Less_Tree_Color($rgb, $alpha);
|
|
}
|
|
|
|
public function toRGB(){
|
|
return $this->toHex($this->rgb);
|
|
}
|
|
|
|
public function toHSL(){
|
|
$r = $this->rgb[0] / 255;
|
|
$g = $this->rgb[1] / 255;
|
|
$b = $this->rgb[2] / 255;
|
|
$a = $this->alpha;
|
|
|
|
$max = max($r, $g, $b);
|
|
$min = min($r, $g, $b);
|
|
$l = ($max + $min) / 2;
|
|
$d = $max - $min;
|
|
|
|
$h = $s = 0;
|
|
if( $max !== $min ){
|
|
$s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
|
|
|
|
switch ($max) {
|
|
case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
|
|
case $g: $h = ($b - $r) / $d + 2; break;
|
|
case $b: $h = ($r - $g) / $d + 4; break;
|
|
}
|
|
$h /= 6;
|
|
}
|
|
return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
|
|
}
|
|
|
|
//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
|
public function toHSV() {
|
|
$r = $this->rgb[0] / 255;
|
|
$g = $this->rgb[1] / 255;
|
|
$b = $this->rgb[2] / 255;
|
|
$a = $this->alpha;
|
|
|
|
$max = max($r, $g, $b);
|
|
$min = min($r, $g, $b);
|
|
|
|
$v = $max;
|
|
|
|
$d = $max - $min;
|
|
if ($max === 0) {
|
|
$s = 0;
|
|
} else {
|
|
$s = $d / $max;
|
|
}
|
|
|
|
$h = 0;
|
|
if( $max !== $min ){
|
|
switch($max){
|
|
case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
|
|
case $g: $h = ($b - $r) / $d + 2; break;
|
|
case $b: $h = ($r - $g) / $d + 4; break;
|
|
}
|
|
$h /= 6;
|
|
}
|
|
return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a );
|
|
}
|
|
|
|
public function toARGB(){
|
|
$argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb);
|
|
return $this->toHex( $argb );
|
|
}
|
|
|
|
public function compare($x){
|
|
|
|
if( !property_exists( $x, 'rgb' ) ){
|
|
return -1;
|
|
}
|
|
|
|
|
|
return ($x->rgb[0] === $this->rgb[0] &&
|
|
$x->rgb[1] === $this->rgb[1] &&
|
|
$x->rgb[2] === $this->rgb[2] &&
|
|
$x->alpha === $this->alpha) ? 0 : -1;
|
|
}
|
|
|
|
public function toHex( $v ){
|
|
|
|
$ret = '#';
|
|
foreach($v as $c){
|
|
$c = Less_Functions::clamp( Less_Parser::round($c), 255);
|
|
if( $c < 16 ){
|
|
$ret .= '0';
|
|
}
|
|
$ret .= dechex($c);
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $keyword
|
|
*/
|
|
public static function fromKeyword( $keyword ){
|
|
$keyword = strtolower($keyword);
|
|
|
|
if( Less_Colors::hasOwnProperty($keyword) ){
|
|
// detect named color
|
|
return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
|
|
}
|
|
|
|
if( $keyword === 'transparent' ){
|
|
return new Less_Tree_Color( array(0, 0, 0), 0, true);
|
|
}
|
|
}
|
|
|
|
}
|
|
|