Wiki CgX

Parce que j'ai un cerveau, mais pas trop.

Outils pour utilisateurs

Outils du site


code:php:colorsolver

CSS Color Solver

Comment transformer du noir en couleur, avec CSS, en utilisant seulement des filtres ?

Un superbe solution en javascript a été trouvée ici : https://stackoverflow.com/questions/42966641/how-to-transform-black-into-any-given-color-using-only-css-filters/43960991#43960991

La démo fonctionnelle est ici : https://codepen.io/sosuke/pen/Pjoqqp

Voici sa version en PHP : 1)

<?php 
 
class Color {
	public float $r;
	public float $g;
	public float $b;
 
	function __construct($r, $g, $b)
		{
	    $this->r=$r;
		$this->g=$g;
		$this->b=$b;
		}
 
	function clamp($value)
		{
		if ($value > 255)
			$value = 255;
		elseif ($value < 0)
			$value = 0;
		return $value;
		}
 
	function multiply($matrix)
		{
		$newr = $this->clamp($this->r * $matrix[0] + $this->g * $matrix[1] + $this->b * $matrix[2]);
		$newg = $this->clamp($this->r * $matrix[3] + $this->g * $matrix[4] + $this->b * $matrix[5]);
		$newb = $this->clamp($this->r * $matrix[6] + $this->g * $matrix[7] + $this->b * $matrix[8]);
 
		$this->r=$newr;
		$this->g=$newg;
		$this->b=$newb;
		}
 
	function hueRotate($angle=0)
		{
		$angle=$angle/180*M_PI;
		$angsin = sin($angle);
		$angcos = cos($angle);
 
		$this->multiply([	0.213 + $angcos * 0.787 - $angsin * 0.213,
							0.715 - $angcos * 0.715 - $angsin * 0.715,
							0.072 - $angcos * 0.072 + $angsin * 0.928,
							0.213 - $angcos * 0.213 + $angsin * 0.143,
							0.715 + $angcos * 0.285 + $angsin * 0.140,
							0.072 - $angcos * 0.072 - $angsin * 0.283,
							0.213 - $angcos * 0.213 - $angsin * 0.787,
							0.715 - $angcos * 0.715 + $angsin * 0.715,
							0.072 + $angcos * 0.928 + $angsin * 0.072,
							]);
		}
 
	function grayscale($value=1)
		{
		$this->multiply([	0.2126+0.7874*(1-$value),
							0.7152-0.7152*(1-$value),
							0.0722-0.0722*(1-$value),
							0.2126-0.2126*(1-$value),
							0.7152+0.2848*(1-$value),
							0.0722-0.0722*(1-$value),
							0.2126-0.2126*(1-$value),
							0.7152-0.7152*(1-$value),
							0.0722+0.9278*(1-$value),
							]);
		}
 
	function sepia($value=1)
		{
		$this->multiply([	0.393+0.607*(1-$value),
							0.769-0.769*(1-$value),
							0.189-0.189*(1-$value),
							0.349-0.349*(1-$value),
							0.686+0.314*(1-$value),
							0.168-0.168*(1-$value),
							0.272-0.272*(1-$value),
							0.534-0.534*(1-$value),
							0.131+0.869*(1-$value),
							]);
		}
 
	function saturate($value=1)
		{
		$this->multiply([	0.213 + 0.787 * $value,
							0.715 - 0.715 * $value,
							0.072 - 0.072 * $value,
							0.213 - 0.213 * $value,
							0.715 + 0.285 * $value,
							0.072 - 0.072 * $value,
							0.213 - 0.213 * $value,
							0.715 - 0.715 * $value,
							0.072 + 0.928 * $value,
							]);
		}
 
	function brightness($value=1)
		{
		$this->linear($value);
		}
 
  	function contrast($value=1)
		{
		$this->linear($value,-(0.5*$value)+0.5);
		}
 
	function linear($slope=1,$intercept=0)
		{
		$this->r=$this->clamp($this->r*$slope + $intercept*255);
		$this->g=$this->clamp($this->g*$slope + $intercept*255);
		$this->b=$this->clamp($this->b*$slope + $intercept*255);
		}
 
	function invert($value=1)
		{
		$this->r = $this->clamp(($value + $this->r/255 * (1- 2*$value)) * 255);
		$this->g = $this->clamp(($value + $this->g/255 * (1- 2*$value)) * 255);
		$this->b = $this->clamp(($value + $this->b/255 * (1- 2*$value)) * 255);
		}
 
	}
 
  	function hsl($r,$g,$b)
  		{
		$r = $r/255;
		$g = $g/255;
		$b = $b/255;
 
    	$max=max($r,$g,$b);
    	$min=min($r,$g,$b);
 
		$h=($max+$min)/2;
		$s=($max+$min)/2;
		$l=($max+$min)/2;
 
		if ($max==$min)
			{
			$h=0;
			$s=0;
			}
		else
			{
		  	$d=$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*100,"s"=>$s*100,"l"=>$l*100);
		}
 
function loss($filters)    // Argument is array of percentages.
	{
	global $target,$targetHSL;
 
    $color=new Color(0,0,0);
 
    $color->invert($filters[0] / 100);
    $color->sepia($filters[1] / 100);
    $color->saturate($filters[2] / 100);
    $color->hueRotate($filters[3] * 3.6);
    $color->brightness($filters[4] / 100);
    $color->contrast($filters[5] / 100);
 
	$colorHSL=hsl($color->r,$color->g,$color->b);
 
    return (
		  abs($color->r - $target->r) +
		  abs($color->g - $target->g) +
		  abs($color->b - $target->b) +
		  abs($colorHSL["h"] - $targetHSL["h"]) +
		  abs($colorHSL["s"] - $targetHSL["s"]) +
		  abs($colorHSL["l"] - $targetHSL["l"])
		  );
	}
 
function spsa($A, $a2, $c, $values, $iters)
	{
    $alpha = 1;
    $gamma = 0.16666666666666666;
 
    $best = NULL;
    $bestLoss = INF;
 
    $deltas=array();
    $highArgs=array();
    $lowArgs=array();
 
    for ($k=0;$k<$iters;$k++)
    	{
		$ck=$c/pow($k+1,$gamma);
 
		for ($i=0;$i<6;$i++)
			{
			$deltas[$i]=(rand(0,1)>0.5)?1:-1;
			$highArgs[$i]=$values[$i] + $ck * $deltas[$i];
			$lowArgs[$i]=$values[$i] - $ck * $deltas[$i];
			}
 
		$lossDiff = loss($highArgs) - loss($lowArgs);
 
		for ($i=0;$i<6;$i++)
			{
			$g = $lossDiff / (2 * $ck) * $deltas[$i];
			$ak = $a2[$i] / pow($A + $k + 1, $alpha);
			$values[$i] = fix($values[$i] - $ak * $g, $i);
			}
 
		$loss=loss($values);
 
		if ($loss<$bestLoss)
			{
			$best=$values;
			$bestLoss=$loss;
			}
		}
 
	return array("values"=>$best,"loss"=>$bestLoss);
	}
 
function fix($value, $idx)
	{
    $max=100;
 
	if ($idx==2 /* saturate */)
		$max=7500;
	elseif ($idx==4 /* brightness */ || $idx==5 /* contrast */)
		$max=200;
 
	if ($idx==3 /* hue-rotate */)
		{
		if ($value > $max)
			$value %= $max;
		elseif ($value<0)
			$value = $max + $value % $max;
		}
	elseif ($value<0)
		$value = 0;
	elseif ($value>$max)
		$value = $max;
 
	return $value;
	}
 
function solveWide()
	{
    $A = 5;
    $c = 15;
    $a2 = [60, 180, 18000, 600, 1.2, 1.2];
 
    $best=array("loss"=>INF);
    for ($i=0;$best["loss"]>25 && $i<3;$i++)
    	{
		$initial = [50, 20, 3750, 50, 100, 100];
		$result = spsa($A, $a2, $c, $initial, 1000);
		if ($result["loss"] < $best["loss"])
			{
			$best = $result;
			}
	    }
 
    return $best;
	}
 
function solveNarrow($wide)
	{
    $A=$wide["loss"];
    $c=2;
    $A1=$A+1;
    $a2 = [0.25*$A1, 0.25*$A1,$A1,0.25*$A1,0.2*$A1,0.2*$A1];
    return spsa($A,$a2,$c,$wide["values"],500);
	}
 
 
 
function solve()
	{
    $result = solveNarrow(solveWide());
 
    return array(
      "values"=> $result["values"],
      "loss"=> $result["loss"],
      "filter"=> css($result["values"]),
    );
  }
 
function css($filters)
	{
    return "filter: invert(".round($filters[0])."%) sepia(".round($filters[1])."%) saturate(".round($filters[2])."%) hue-rotate(".round($filters[3]*3.6)."deg) brightness(".round($filters[4])."%) contrast(".round($filters[5])."%);";
	}
 
function hexToRgb($hex)
	{
	$r=hexdec(substr($hex,1,2));
	$g=hexdec(substr($hex,3,2));
	$b=hexdec(substr($hex,5,2));
 
	return array($r,$g,$b);
	}
 
 
$targetColor="#00a4d6"; // Couleur à récréer
$rgb=hexToRgb($targetColor);
 
$target=new Color($rgb[0],$rgb[1],$rgb[2]);
$targetHSL=hsl($rgb[0],$rgb[1],$rgb[2]);
 
$result=solve();
 
echo "<div style=\"background-color:#000;width:200px;height:200px;{$result["filter"]}\"></div>";
 
?>
1)
un peu à l'arrache, le code n'est pas très élégant, il faut peaufiner
code/php/colorsolver.txt · Dernière modification : 15 Dec 2021 :: 08:12 de CgX