Dark mode selector, part VII
[lw2-viewer.git] / color-scheme-convert.php
blob8bffba06b7ac177bd5223128d93873fe78012609
1 <?php
3 if (!isset($argv[1]))
4 die;
6 $debug_enabled = false;
8 ## Get command line arguments.
9 $stylesheet = file_get_contents($argv[1]);
10 $mode = @$argv[2] ?: 1;
12 ## Process and print.
13 $stylesheet = preg_replace_callback("/(#[0-9abcdef]+)([),; ])/i", 'ProcessColorValue', $stylesheet);
14 $stylesheet = preg_replace_callback("/rgba\\(\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9\.]+)\\s*\\)/i", 'ProcessColorValue_RGBA', $stylesheet);
15 echo $stylesheet;
17 /******************/
18 /* CSS PROCESSING */
19 /******************/
21 function ProcessColorValue($m) {
22 debug_log($m[1]);
23 $m[1] = HexFromRGB(RGBFromXYZ(XYZFromLab(CVT(LabFromXYZ(XYZFromRGB(RGBFromHex($m[1]))),"Lab"))));
25 return implode(array_slice($m,1));
27 function ProcessColorValue_RGBA($m) {
28 debug_log(PCC(array_slice($m, 1, 3)));
29 $rgba = RGBFromXYZ(XYZFromLab(CVT(LabFromXYZ(XYZFromRGB(array_slice($m, 1, 3))),"Lab")));
30 foreach ($rgba as $k => $v) {
31 $rgba[$k] = round($v);
33 $rgba[] = $m[4];
35 return "rgba(" . implode(", ", $rgba) . ")";
38 /***********/
39 /* HELPERS */
40 /***********/
42 function debug_log($string) {
43 global $debug_enabled;
44 if ($debug_enabled)
45 error_log($string);
48 /******************/
49 /* TRANSFORMATION */
50 /******************/
52 ## CVT = "Color Value Transform"
53 function CVT($value, $color_space) {
54 global $mode;
55 ## The mode is a bit field; set binary flags indicate specific transformations.
56 ## Flags are checked, and applied, in order from lowest bit position to highest.
58 ## 0x0001: lightness inversion (in Lab).
59 ## 0x0002: hue inversion (in Lab).
61 ## The following six flags are mutually exclusive:
63 ## 0x0004: maps whites to reds (in HSV; keeps V constant, sets H to 0°, S to maximum)
64 ## 0x0008: maps whites to yellows (in HSV; keeps V constant, sets H to 60°, S to maximum)
65 ## 0x0010: maps whites to greens (in HSV; keeps V constant, sets H to 120°, S to maximum)
66 ## 0x0020: maps whites to teal/turquoise (in HSV; keeps V constant, sets H to 180°, S to maximum)
67 ## 0x0040: maps whites to blue (in HSV; keeps V constant, sets H to 240°, S to maximum)
68 ## 0x0080: maps whites to magenta (in HSV; keeps V constant, sets H to 300°, S to maximum)
70 if ($mode & 0x0001) {
71 $value[0] = 100 - $value[0];
73 if ($mode & 0x0002) {
74 $value[1] *= -1;
75 $value[2] *= -1;
78 $hsv_value = HSVFromRGB(RGBFromXYZ(XYZFromLab($value)));
79 if ($mode & 0x0004) {
80 $hsv_value[0] = 0.0 / 360.0;
81 $hsv_value[1] = 1.0;
82 } else if ($mode & 0x0008) {
83 $hsv_value[0] = 60.0 / 360.0;
84 $hsv_value[1] = 1.0;
85 } else if ($mode & 0x0010) {
86 $hsv_value[0] = 120.0 / 360.0;
87 $hsv_value[1] = 1.0;
88 } else if ($mode & 0x0020) {
89 $hsv_value[0] = 180.0 / 360.0;
90 $hsv_value[1] = 1.0;
91 } else if ($mode & 0x0040) {
92 $hsv_value[0] = 240.0 / 360.0;
93 $hsv_value[1] = 1.0;
94 } else if ($mode & 0x0080) {
95 $hsv_value[0] = 300.0 / 360.0;
96 $hsv_value[1] = 1.0;
98 $value = LabFromXYZ(XYZFromRGB(RGBFromHSV($hsv_value)));
99 debug_log(" → {$color_space} ".PCC($value));
101 return $value;
104 /*********************/
105 /* FORMAT CONVERSION */
106 /*********************/
108 function RGBFromHex($hexColorString) {
109 if ($hexColorString[0] == '#')
110 $hexColorString = substr($hexColorString,1);
111 if (strlen($hexColorString) == 3)
112 $hexColorString = preg_replace("/./","$0$0",$hexColorString);
113 $components = str_split($hexColorString,2);
114 foreach ($components as $i => $hexColor)
115 $components[$i] = hexdec($hexColor);
116 debug_log(" → RGB ".PCC($components));
117 return $components;
120 function HexFromRGB($rgb_components) {
121 foreach ($rgb_components as $i => $component) {
122 $hex_value = dechex(round($component));
123 if (strlen($hex_value) == 1)
124 $hex_value = "0".$hex_value;
125 $rgb_components[$i] = $hex_value;
127 $hexColorString = "#" . implode($rgb_components);
128 $hexColorString = preg_replace("/([0-9abcdef])\\1([0-9abcdef])\\2([0-9abcdef])\\3/", "$1$2$3", $hexColorString);
129 debug_log(" → ".$hexColorString);
130 return $hexColorString;
133 ## PCC = "Print Color Components"
134 function PCC($components) {
135 foreach ($components as $k => $v) {
136 $components[$k] = round($v, 2);
138 return "( " . implode(", ", $components) . " )";
141 /**************************/
142 /* COLOR SPACE CONVERSION */
143 /**************************/
145 function HSVFromRGB($rgb_components) {
146 $var_R = $rgb_components[0] / 255.0;
147 $var_G = $rgb_components[1] / 255.0;
148 $var_B = $rgb_components[2] / 255.0;
150 $var_Min = min($var_R, $var_G, $var_B);
151 $var_Max = max($var_R, $var_G, $var_B);
152 $del_Max = $var_Max - $var_Min;
154 $V = $var_Max;
155 $H = 0;
156 $S = 0;
158 if ($del_Max != 0) {
159 $S = $del_Max / $var_Max;
161 $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;
162 $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;
163 $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;
165 if ($var_R == $var_Max) $H = $del_B - $del_G;
166 else if ($var_G == $var_Max) $H = (1.0/3.0) + $del_R - $del_B;
167 else if ($var_B == $var_Max) $H = (2.0/3.0) + $del_G - $del_R;
169 if ($H < 0) $H += 1;
170 else if ($H > 1) $H -= 1;
173 debug_log(" → HSV ".PCC([ $H, $S, $V ]));
174 return [ $H, $S, $V ];
177 function RGBFromHSV($hsv_components) {
178 $H = $hsv_components[0];
179 $S = $hsv_components[1];
180 $V = $hsv_components[2];
182 $R = $G = $B = $V * 255.0;
184 if ($S != 0) {
185 $var_h = $H * 6.0;
186 if ($var_h == 6.0)
187 $var_h = 0;
188 $var_i = floor($var_h);
189 $var_1 = $V * (1 - $S);
190 $var_2 = $V * (1 - $S * ($var_h - $var_i));
191 $var_3 = $V * (1 - $S * (1 - ($var_h - $var_i)));
193 $var_r = $var_g = $var_b = 0.0;
195 if ($var_i == 0) { $var_r = $V; $var_g = $var_3; $var_b = $var_1; }
196 else if ($var_i == 1) { $var_r = $var_2; $var_g = $V; $var_b = $var_1; }
197 else if ($var_i == 2) { $var_r = $var_1; $var_g = $V; $var_b = $var_3; }
198 else if ($var_i == 3) { $var_r = $var_1; $var_g = $var_2; $var_b = $V; }
199 else if ($var_i == 4) { $var_r = $var_3; $var_g = $var_1; $var_b = $V ; }
200 else { $var_r = $V; $var_g = $var_1 ; $var_b = $var_2; }
202 $R = $var_r * 255.0;
203 $G = $var_g * 255.0;
204 $B = $var_b * 255.0;
207 debug_log(" → RGB ".PCC([ $R, $G, $B ]));
208 return [ $R, $G, $B ];
211 function XYZFromRGB($rgb_components) {
212 foreach ($rgb_components as $i => $component) {
213 $component /= 255.0;
214 $rgb_components[$i] = ($component > 0.04045) ?
215 (pow((($component + 0.055) / 1.055), 2.4)) :
216 ($component / 12.92);
219 $var_R = $rgb_components[0] * 100.0;
220 $var_G = $rgb_components[1] * 100.0;
221 $var_B = $rgb_components[2] * 100.0;
223 $X = $var_R * 0.4124 + $var_G * 0.3576 + $var_B * 0.1805;
224 $Y = $var_R * 0.2126 + $var_G * 0.7152 + $var_B * 0.0722;
225 $Z = $var_R * 0.0193 + $var_G * 0.1192 + $var_B * 0.9505;
227 debug_log(" → XYZ ".PCC([ $X, $Y, $Z ]));
228 return [ $X, $Y, $Z ];
231 function LabFromXYZ($xyz_components) {
232 $xyz_components[0] /= 95.047;
233 $xyz_components[1] /= 100.000;
234 $xyz_components[2] /= 108.883;
236 foreach ($xyz_components as $i => $component) {
237 $xyz_components[$i] = ($component > 0.008856) ?
238 (pow($component, (1.0/3.0))) :
239 ((7.787 * $component) + (16.0/116.0));
242 $var_X = $xyz_components[0];
243 $var_Y = $xyz_components[1];
244 $var_Z = $xyz_components[2];
246 $L = (116.0 * $var_Y) - 16.0;
247 $a = 500.0 * ($var_X - $var_Y);
248 $b = 200.0 * ($var_Y - $var_Z);
250 debug_log(" → Lab ".PCC([ $L, $a, $b ]));
251 return [ $L, $a, $b ];
254 function XYZFromLab($lab_components) {
256 $var_Y = ($lab_components[0] + 16.0) / 116.0;
257 $var_X = $lab_components[1] / 500.0 + $var_Y;
258 $var_Z = $var_Y - $lab_components[2] / 200.0;
259 $xyz_components = [ $var_X, $var_Y, $var_Z ];
261 foreach ($xyz_components as $i => $component) {
262 $xyz_components[$i] = (pow($component, 3) > 0.008856) ?
263 (pow($component, 3)) :
264 (($component - 16.0/116.0) / 7.787);
267 $xyz_components[0] *= 95.047;
268 $xyz_components[1] *= 100.000;
269 $xyz_components[2] *= 108.883;
271 debug_log(" → XYZ ".PCC($xyz_components));
272 return $xyz_components;
275 function RGBFromXYZ($xyz_components) {
276 $var_X = $xyz_components[0] / 100.0;
277 $var_Y = $xyz_components[1] / 100.0;
278 $var_Z = $xyz_components[2] / 100.0;
280 $var_R = $var_X * 3.2406 + $var_Y * -1.5372 + $var_Z * -0.4986;
281 $var_G = $var_X * -0.9689 + $var_Y * 1.8758 + $var_Z * 0.0415;
282 $var_B = $var_X * 0.0557 + $var_Y * -0.2040 + $var_Z * 1.0570;
284 $rgb_components = [ $var_R, $var_G, $var_B ];
285 foreach ($rgb_components as $i => $component) {
286 $component = ($component > 0.0031308) ?
287 (1.055 * pow($component, (1.0/2.4)) - 0.055) :
288 (12.92 * $component);
289 $rgb_components[$i] = min(max($component, 0.0), 1.0) * 255.0;
292 debug_log(" → RGB ".PCC($rgb_components));
293 return $rgb_components;