Clase PHP para convertir modelos de colores

Hace un tiempo atrás, en un proyecto de diseño web en el que estuve trabajando, tuve que convertir modelos de colores en PHP, con la particularidad de que necesitaba representarlos fácilmente en html usando CSS. Para realizar esta tarea creé una clase que se encargara de manejar esto.

Esta clase nos permite cambiar un modelo de color al vuelo enviándole una cadena de un estilo CSS o directamente un Array con los valores, podremos a nuestra conveniencia recibir una nueva cadena en el modelo de color seleccionado o un Array con los valores para poderlos modificar.

La clase trabaja con los siguientes modelos de color:

  • RGB: Color RGB. Ejemplo: rgb(R, G, B)
  • RGBA: Color RGB con canal alpha. Ejemplo: rgba(R, G, B, A)
  • HEX: Color RGB en representación hexadecimal. Ejemplo: #RRGGBB
  • HSL: Color HSL. Ejemplo: hsl(H, S, L)
  • HSLA: Color HSL con canal alpha. Ejemplo: hsla(H, S, L, A)

También acepta dos modelos de la futura notación de color de nivel 4 de la W3C:

  • HEXA: Color RGB con canal alpha en representación hexadecimal: #RRGGBBAA
  • CMYK: Representación de colores de tintas: device-cmyk(C, M, Y, K)

Clase PHP para convertir modelos de colores

class ColorConverter{

	private $regs = array(

		'hex3'	=> '/^#([a-f\d])([a-f\d])([a-f\d])$/i',
		'hex6'	=> '/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i',
		'hex8'	=> '/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i',
		'rgb'	=> '/^rgb\s*\(\s*([\d\.]+%?)\s*\,\s*([\d\.]+%?)\s*\,\s*([\d\.]+%?)\s*\)$/',
		'rgba'	=> '/^rgba\s*\(\s*([\d\.]+%?)\s*\,\s*([\d\.]+%?)\s*\,\s*([\d\.]+%?)\s*\,\s*([\d\.]+%?)\s*\)$/',
		'hsl'	=> '/^hsl\s*\(\s*([\d\.]+)\s*\,\s*([\d\.]+)%\s*\,\s*([\d\.]+)%s*\)$/',
		'hsla'	=> '/^hsla\s*\(\s*([\d\.]+)\s*\,\s*([\d\.]+)%\s*\,\s*([\d\.]+)%\s*\,\s*([\d\.]+%?)\s*\)$/',
		'cmyk'	=> '/^(?:device-cmyk|cmyk)\s*\(\s*([\d\.]+%?)\s*\,\s*([\d\.]+%?)\s*\,\s*([\d\.]+%?)\s*\,\s*([\d\.]*%?)\s*\)$/'
	);

	private $pcent = '/^([\d\.]+)%$/';

	//---Convertir a RGB
	public function toRGB($color, $string = false){

		$values = (is_array($color)) ? $this->getArrayValues($color) : $this->getValues($color);

		if(!$values) return false;

		if($string) return 'rgb(' . $values['r'] . ',' . $values['g'] . ',' . $values['b'] . ')';

		return $values;

	}

	//---Convertir a RGBA
	public function toRGBA($color, $string = false){

		$values = (is_array($color)) ? $this->getArrayValues($color) : $this->getValues($color);

		if(!$values) return false;

		if( !isset($values['a']) ) $values['a'] = 1;

		if($string) return 'rgba(' . $values['r'] . ',' . $values['g'] . ',' . $values['b'] . ',' . $values['a'] . ')';

		return $values;

	}

	//---Convertir a HEX RGB
	public function toHEX($color, $string = false){

		$values = (is_array($color)) ? $this->getArrayValues($color, true) : $this->getValues($color, true);

		if(!$values) return false;

		if($string) return '#' . $values['r'] . $values['g'] . $values['b'];

		return $values;

	}

	//---Convertir a HEX RGBA
	public function toHEXA($color, $string = false){

		$values = (is_array($color)) ? $this->getArrayValues($color, true) : $this->getValues($color, true);

		if(!$values) return false;

		if( !isset($values['a']) ) $values['a'] = 'FF';

		if($string) return '#' . $values['r'] . $values['g'] . $values['b'] . $values['a'];

		return $values;

	}

	//--Convertir a HSL
	public function toHSL($color, $string = false){

		$values = $this->toHSLA($color);

		if($string) return 'hsl(' . $values['h'] . ',' . $values['s'] . '%,' . $values['l'] . '%)';

		unset($values['a']);

		return $values;

	}

	//--Convertir a HSLA
	public function toHSLA($color, $string = false){

		$values = (is_array($color)) ? $this->getArrayValues($color) : $this->getValues($color);

		if(!$values) return false;

		$values['r'] /= 255;
		$values['g'] /= 255;
		$values['b'] /= 255;

		$max = max($values['r'], $values['g'], $values['b']);
		$min = min($values['r'], $values['g'], $values['b']);

		$h = 0;
		$s = 0;
		$l = ( $max + $min ) / 2;

		$d = $max - $min;

		if( $d == 0 ){

			$h = 0;
			$s = 0;
			
		}else{

			switch( $max ){

				case $values['r']:

					$h = fmod(($values['g'] - $values['b']) / $d, 6);

				break;

				case $values['g']:

					$h = ($values['b'] - $values['r']) / $d + 2;

				break;

				case $values['b']:

					$h = ($values['r'] - $values['g']) / $d + 4;

				break;

			}

			$h = round($h * 60);

			if( $h < 0 ) $h += 360;

			$s = $d / ( 1 - abs( 2 * $l - 1) );

		}

		if($string) return 'hsla(' . $h . ',' . round($s * 100) . '%,' . round($l * 100) . '%,' . $values['a'] . ')';

		$values = array(

			'h'	=> $h,
			's'	=> round($s * 100),
			'l'	=> round($l * 100),
			'a'	=> $values['a']

		);

		return $values;

	}

	//---Convertir a CMYK
	public function toCMYK($color, $string = false){

		$values = (is_array($color)) ? $this->getArrayValues($color) : $this->getValues($color);

		if(!$values) return false;

		$values['r'] /= 255;
		$values['g'] /= 255;
		$values['b'] /= 255;

		$k = round((1 - max($values['r'], $values['g'], $values['b'])) * 100);
		$c = round(((1 - $values['r'] - $k) / (1 - $k)) * 100);
		$m = round(((1 - $values['g'] - $k) / (1 - $k)) * 100);
		$y = round(((1 - $values['b'] - $k) / (1 - $k)) * 100);

		if($string) return 'device-cmyk(' . $c . '%,' . $m . '%,' . $y . '%,' . $k . '%)';

		$values = array(

			'c' => $c,
			'm' => $m,
			'y' => $y,
			'k' => $k

		);

		return $values;

	}

	//---Convertir un color a objeto
	private function getValues($color, $hex = false){

		$values = false;

		foreach ($this->regs as $k => $r) {		
			
			if( preg_match($r, $color) ){
				
				$values = array();

				switch($k){

					//---Hex RGB case
					case 'hex3':
					case 'hex6':
					case 'hex8':

						$values['r'] = preg_replace($r, '$1', $color);
						$values['g'] = preg_replace($r, '$2', $color);
						$values['b'] = preg_replace($r, '$3', $color);

						if($k == 'hex8') $values['a'] = preg_replace($r, '$4', $color);

						if(!$hex){

							$values['r'] = $this->getDEC( $values['r'] );
							$values['g'] = $this->getDEC( $values['g'] );
							$values['b'] = $this->getDEC( $values['b'] );

							if($k == 'hex8') $values['a'] = $this->buildAlpha( $this->getDEC(preg_replace($r, '$4', $color)) );

						}

					break;

					//---RGB case
					case 'rgb':
					case 'rgba':

						$values['r'] = preg_replace($r, '$1', $color);
						$values['g'] = preg_replace($r, '$2', $color);
						$values['b'] = preg_replace($r, '$3', $color);

						if($k == 'rgba') {

							$values['a'] = preg_replace($r, '$4', $color);

							if(preg_match($this->pcent, $values['a'])) {

								$values['a'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['a']) );

							}else{

								$values['a'] = round($values['a'] * 255);

							}

							if($values['a'] > 255) $values['a'] = 255;

						}

						if(preg_match($this->pcent, $values['r'])) $values['r'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['r']) );
						if(preg_match($this->pcent, $values['g'])) $values['g'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['g']) );
						if(preg_match($this->pcent, $values['b'])) $values['b'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['b']) );

						if($hex){

							$values['r'] = $this->getHEX( $values['r'] );
							$values['g'] = $this->getHEX( $values['g'] );
							$values['b'] = $this->getHEX( $values['b'] );

							if($k == 'rgba') $values['a'] = $this->getHEX( $values['a'] );

						}else if( isset($values['a']) ){

							$values['a'] = $this->buildAlpha( $values['a'] );

						}

					break;

					//---HSL Case
					case 'hsl':
					case 'hsla':

						$h = preg_replace($r, '$1', $color);
						$s = preg_replace($r, '$2', $color);
						$l = preg_replace($r, '$3', $color);

						$rgb = $this->hslToRGB($h, $s, $l);

						$values['r'] = $rgb['r'];
						$values['g'] = $rgb['g'];
						$values['b'] = $rgb['b'];

						if($k == 'hsla') {

							$values['a'] = preg_replace($r, '$4', $color);

							if(preg_match($this->pcent, $values['a'])) {

								$values['a'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['a']) );

							}else{

								$values['a'] = round($values['a'] * 255);

							}

							if($values['a'] > 255) $values['a'] = 255;

						}

						if($hex){

							$values['r'] = $this->getHEX( $values['r'] );
							$values['g'] = $this->getHEX( $values['g'] );
							$values['b'] = $this->getHEX( $values['b'] );

							if($k == 'hsla') $values['a'] = $this->getHEX( $values['a'] );

						}else if( isset($values['a']) ){

							$values['a'] = $this->buildAlpha( $values['a'] );

						}

					break;

					//---Case CMYK
					case 'cmyk':

						$c = preg_replace($r, '$1', $color);
						$m = preg_replace($r, '$2', $color);
						$y = preg_replace($r, '$3', $color);
						$k = preg_replace($r, '$4', $color);

						$rgb = $this->cmykToRGB($c, $m, $y, $k);

						$values['r'] = $rgb['r'];
						$values['g'] = $rgb['g'];
						$values['b'] = $rgb['b'];

						if($hex){

							$values['r'] = $this->getHEX( $values['r'] );
							$values['g'] = $this->getHEX( $values['g'] );
							$values['b'] = $this->getHEX( $values['b'] );

						}

					break;

				}

				break;

			}

		}

		return $values;

	}

	//---Convertir un array de color a objeto
	function getArrayValues($color, $hex = false){

		//---Tomar las keys
		$code = array_keys($color);

		sort($code);

		$code = implode('', $code);

		//---Array values
		$values = false;

		switch($code){

			//---RGB
			case 'bgr':
			case 'abgr':

				$values = array();

				$values['r'] = $color['r'];
				$values['g'] = $color['g'];
				$values['b'] = $color['b'];

				if(preg_match($this->pcent, $values['r'])) $values['r'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['r']) );
				if(preg_match($this->pcent, $values['g'])) $values['g'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['g']) );
				if(preg_match($this->pcent, $values['b'])) $values['b'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['b']) );

				if($code == 'abgr'){

					$values['a'] = $color['a'];

					if(preg_match($this->pcent, $values['a'])) $values['a'] = $this->fromPercent( preg_replace($this->pcent, '$1', $values['a']) );

				}

				if($hex){

					$values['r'] = $this->getHEX( $values['r'] );
					$values['g'] = $this->getHEX( $values['g'] );
					$values['b'] = $this->getHEX( $values['b'] );

					if($code == 'abgr') $values['a'] = $this->getHEX( $values['a'] );

				}else if( isset($values['a']) ){

					$values['a'] = $this->buildAlpha( $values['a'] );

				}

			break;

			//---HSL
			case 'hls':
			case 'ahls':

				$values = array();

				$rgb = $this->hslToRGB(
					$color['h'],
					preg_replace($this->pcent, '$1', $color['s']),
					preg_replace($this->pcent, '$1', $color['l'])
				);

				$values['r'] = $rgb['r'];
				$values['g'] = $rgb['g'];
				$values['b'] = $rgb['b'];

				if($code == 'ahls') {

					if(preg_match($this->pcent, $color['a'])) {

						$values['a'] = $this->fromPercent( preg_replace($this->pcent, '$1', $color['a']) );

					}else{

						$values['a'] = round($color['a'] * 255);

					}

					if($values['a'] > 255) $values['a'] = 255;

				}

				if($hex){

					$values['r'] = $this->getHEX( $values['r'] );
					$values['g'] = $this->getHEX( $values['g'] );
					$values['b'] = $this->getHEX( $values['b'] );

					if($code == 'ahls') $values['a'] = $this->getHEX( $values['a'] );

				}else if( isset($values['a']) ){

					$values['a'] = $this->buildAlpha( $values['a'] );

				}

			break;

			//---CMYK
			case 'ckmy':

				$values = array();

				$rgb = $this->cmykToRGB($color['c'], $color['m'], $color['y'], $color['k']);

				$values['r'] = $rgb['r'];
				$values['g'] = $rgb['g'];
				$values['b'] = $rgb['b'];

				if($hex){

					$values['r'] = $this->getHEX( $values['r'] );
					$values['g'] = $this->getHEX( $values['g'] );
					$values['b'] = $this->getHEX( $values['b'] );

				}

			break;

		}

		return $values;

	}

	//---Calcular un valor decimal en base a 255 a partir de un porciento
	private function fromPercent($number){

		$value = round( ($number / 100) * 255 );

		if($value > 255) return 255;

		return $value;

	}

	//---Calcular un valor decimal entre 0 y 1 a partir de un porciento CMYK
	private function fromCMYKPercent($number){

		$value = round( $number / 100, 2 );

		if($value > 1) return 1;

		return $value;

	}

	//---Calcular un valor decimal de alpha (entre 0 y 1)
	private function buildAlpha($alpha){

		$alpha = round($alpha / 255, 2);

		if($alpha > 1) $alpha = 1;

		return $alpha;

	}

	//---Convertir a hexadecimal
	private function getHEX($number){

		return str_pad( dechex($number), 2, '0', STR_PAD_LEFT);

	}

	//---Convertir a decimal
	private function getDEC($hex){

		if(strlen($hex) == 1) $hex .= $hex;

		return hexdec($hex);

	}

	//---HSL to RGB
	private function hslToRGB($h, $s, $l){

		$h /= 60;
		$s /= 100;
		$l /= 100;

		if( $l <= .5 ) {

			$t2 = $l * ($s + 1);

		} else {

			$t2 = $l + $s - ($l * $s);
		}

		$t1 = $l * 2 - $t2;

		$r = $this->hueToRGB($t1, $t2, $h + 2);
		$g = $this->hueToRGB($t1, $t2, $h);
		$b = $this->hueToRGB($t1, $t2, $h - 2);

		return array('r' => $r, 'g' => $g, 'b' => $b);

	}

	//---HUE to RGB
	private function hueToRGB($t1, $t2, $hue){

		if($hue < 0) $hue += 6;

		if($hue >= 6) $hue -= 6;

		if($hue < 1){

			return round((($t2 - $t1) * $hue + $t1) * 255);

		}else if($hue < 3){

			return round($t2 * 255);

		}else if($hue < 4){

			return round((($t2 - $t1) * (4 - $hue) + $t1) * 255);

		}else{

			return round($t1 * 255);

		}

	}

	//---CMYK To RGB
	private function cmykToRGB($c, $m, $y, $k){

		if(preg_match($this->pcent, $c)) $c = $this->fromCMYKPercent( preg_replace($this->pcent, '$1', $c) );
		if(preg_match($this->pcent, $m)) $m = $this->fromCMYKPercent( preg_replace($this->pcent, '$1', $m) );
		if(preg_match($this->pcent, $y)) $y = $this->fromCMYKPercent( preg_replace($this->pcent, '$1', $y) );
		if(preg_match($this->pcent, $k)) $k = $this->fromCMYKPercent( preg_replace($this->pcent, '$1', $k) );

		$r = round(255 * (1 - $c) * (1 - $k));
		$g = round(255 * (1 - $m) * (1 - $k));
		$b = round(255 * (1 - $y) * (1 - $k));

		return array('r' => $r, 'g' => $g, 'b' => $b);

	} 

}
Métodos públicos de la clase
//Convertir un color a RGB (rojo, verde y azul)
toRGB(mixed $color [, boolean $string = false])
//Convertir un color a RGBA (rojo, verde y azul) con canal alpha
toRGBA(mixed $color [, boolean $string = false])
//Convertir un color a Hexadecimal
toHEX(mixed $color [, boolean $string = false])
//Convertir un color a Hexadecimal con canal alpha
toHEXA(mixed $color [, boolean $string = false])
//Convertir un color a HSL (tono, saturación, luminosidad)
toHSL(mixed $color [, boolean $string = false])
//Convertir un color a HSL (tono, saturación, luminosidad) con canal alpha
toHSLA(mixed $color [, boolean $string = false])
//Convertir un color a CMYK (cian, magenta, amarillo y negro)
toCMYK(mixed $color [, boolean $string = false])
$color String de representación de un estilo o un Array con los valores del color.

Ejemplos:

  • rgba(255, 20, 10, .5)
  • #FF00FF
  • hsl(50, ‘20%’, ‘30%’)
  • array(‘c’ => 100, ‘m’ => 50, ‘y’ => ’10’, ‘k’ => 100 )
  • #CC0
  • rgb(‘50%’, ‘10%’, ‘20%’)

$string Valor booleano que indicará si se devolverá un string representando un estilo o un Array con los valores

Ejemplos de uso de la clase

Todos los métodos de la clase admiten como parámetro de entrada cualquiera de las representaciones de los modelos de color soportados, para convertir modelos de colores podemos usar notaciones como las siguientes:

//Incluir la clase
include('ColorConverter.php');

//Crear una instancia
$converter = new ColorConverter();

$rgb = $converter->toRGB('#FF00FF', true);
echo $rgb; // rgb(255,0,255)

$rgba = $converter->toRGBA('hsl(50, 20%, 90%)', true);
echo $rgba; // rgba(235,233,224,1)

$hsl = $converter->toHSL('rgb(255, 0, 0)', true);
echo $hsl; // hsl(0,100%,50%)

$hsla = $converter->toHSLA('rgba(0, 255, 255, .5)', true);
echo $hsla; // hsla(180,100%,50%,0.5)

$cmyk = $converter->toCMYK('#F0F');
print_r $cmyk; // Array ( [c] => 0 [m] => 100 [y] => 0 [k] => 0 )

$rgb = $converter->toRGB( array('h' => 115, 's' => '70%', 'l' => '45%'), true );
echo $rgb; // rgb(48,195,34)

$hsla = $converter->toHSLA( array('r' => 115, 'g' => 200, 'b' => 150, 'a' => '50%'), true );
echo $hsla; // hsla(145,44%,62%,0.5)

Demostración

Ahora haremos una pequeña demostración usando la clase. Tomaremos como base un color HSL de tono 25º, saturación 0% y luminosidad 30%. Después haremos un ciclo anidado dentro de otro e iremos variando los valores de saturación y luminosidad de este color. Convertiremos el color HSL resultante a RGB y se lo aplicaremos a una serie de elementos que iremos creando en cada iteración del ciclo y dentro de ellos situaremos el valor de rojo, verde y azul del color que representa. El código sería el siguiente:

include('classes/ColorConverter.php');

$converter = new ColorConverter();

echo "<div class='colors'>";

	for($i = 0; $i < 10; $i++){

		for($j = 0; $j < 10; $j++){

			$hsl = array(
				'h' => 25,
				's' => ($i * 10) . '%',
				'l' => ($j * 5 + 30) + '%'
			);

			$rgb = $converter->toHEX($hsl, true);
			$codes = $converter->toRGB($hsl);

			echo "<div class='box' style='background: {$rgb}'>";

				foreach($codes as $index => $value){

					echo $index . ":" . $value;
					echo "<br>";

				}

			echo "</div>";

		}

	}

echo "</div>";

Y el resultado será el siguiente:

Descargar el ejemplo

Descarga el ejemplo de convertir modelos de colores hsl a rgb.

Ahora haremos otra demostración. Tomaremos cinco colores RGB (básicos y combinaciones básicas) y los iremos desaturando hasta llegar a gris y copiaremos dentro de ellos la representación CMYK de cada uno. El código sería el siguiente:

include('classes/ColorConverter.php');

$converter = new ColorConverter();

//---Array de colores
$colors = array('#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF');

$reg = '/^([\d\.]+)%$/';

//---Crear los elementos
echo "<div class='colors'>";

	for($i = 0; $i < 5; $i++){

		$tint = $colors[$i];
		$hsl = $converter->toHSL($tint);
		$step = preg_replace($reg, '$1', $hsl['s']) / 4;

		for($j = 0; $j < 5; $j++){

			$rgb = $converter->toHEX($hsl, true);
			$cmyk = $converter->toCMYK($hsl);
			
			echo "<div class='box' style='background: {$rgb}'>";

				foreach($cmyk as $index => $value){

					echo $index . ":" . $value;
					echo "<br>";

				}

			echo "</div>";

			$hsl['s'] = (preg_replace($reg, '$1', $hsl['s']) - $step) . "%";

		}

	}

echo "</div>";

Y el resultado sería el siguiente:

Este ejemplo nos muestra algo curioso. Como hemos trabajado con colores RGB básicos (uno de los canales con intensidad máxima) y combinaciones básicas (más de un canal con intensidad máxima), todos estos colores tienen su saturación y luminosidad en el mismo nivel, por lo tanto si los desaturamos completamente quedaría el mismo color gris en los cinco casos (#808080) y como pueden notar el mismo color CMYK.

Descargar el ejemplo

Descarga el ejemplo de convertir modelos de colores rgb a cmyk.

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Puedes situar fragmentos de código dentro de etiquetas <pre></pre> y código HTML o XML entre etiquetas <xmp></xmp>.

Time limit is exhausted. Please reload CAPTCHA.