3 use strict; use warnings; use utf8;
6 use File::Basename qw(basename dirname);
7 use File::Temp qw(tempfile);
11 return sprintf('%02x%02x%02x', int(255*$r+0.5), int(255*$g+0.5), int(255*$b+0.5));
14 my ($hue, $sat, $val ) = @_;
15 my $h = int($hue * 6);
16 my $f = $hue * 6 - $h;
17 my $p = $val * (1 - $sat);
18 my $q = $val * ( 1 - $f * $sat);
19 my $t = $val * ( 1 - (1-$f) * $sat);
21 return hexTuple($val, $t, $p) if $h == 0 or $h == 6;
22 return hexTuple($q, $val, $p) if $h == 1;
23 return hexTuple($p, $val, $t) if $h == 2;
24 return hexTuple($p, $q, $val) if $h == 3;
25 return hexTuple($t, $p, $val) if $h == 4;
26 return hexTuple($val, $p, $q) if $h == 5;
31 # https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
33 my ($hue, $sat, $lig ) = @_;
36 my $c = (1 - abs(2.0*$lig - 1)) * $sat;
37 my $h_mod_2 = $h - 2.0*int($h/2);
38 my $x = $c * (1 - abs($h_mod_2 - 1));
40 my $m = $lig - $c / 2.0;
42 return hexTuple($c + $m, $x + $m, 0 + $m) if $h < 1 or $h == 6;
43 return hexTuple($x + $m, $c + $m, 0 + $m) if $h < 2;
44 return hexTuple( 0 + $m, $c + $m, $x + $m) if $h < 3;
45 return hexTuple( 0 + $m, $x + $m, $c + $m) if $h < 4;
46 return hexTuple($x + $m, 0 + $m, $c + $m) if $h < 5;
47 return hexTuple($c + $m, 0 + $m, $x + $m) if $h < 6;
52 my @hexDigit = split //, '0123456789abcdef';
54 (lc($hexDigit[$_]) => $_, uc($hexDigit[$_]) => $_ ),
60 for (@_) { $min = $_ if $_ < $min }
68 for (@_) { $max = $_ if $_ > $max }
74 my $hexTriplet = shift;
76 my @d = $hexTriplet =~ /^#?(.)(.)(.)(.)(.)(.)/;
78 return (16 * $hexValue{$d[0]} + $hexValue{$d[1]},
79 16 * $hexValue{$d[2]} + $hexValue{$d[3]},
80 16 * $hexValue{$d[4]} + $hexValue{$d[5]});
84 my $hexTriplet = shift;
86 my ($r,$g,$b) = hexToRGB($hexTriplet);
87 warn "$hexTriplet -> $r:$g:$b";
89 for ($r, $g, $b ) { $_ = $_ / 255.0 }
91 my $M = max($r, $g, $b);
92 my $m = min($r, $g, $b);
109 else { die "$C, $M, $r, $g, $b"; }
112 my $L = ($M + $m) / 2;
114 my $S = ( $L <= 0.5 ) ? $C/(2*$L) : $C / (2-2*$L);
116 return( $H, $S, $L );
119 my $baseColorHSV = [ hexToHSL('#935ff2') ];
120 my $baseColorHue = $baseColorHSV->[0];
121 warn sprintf( 'H:%1.4f S:%1.4f V:%1.4f', @$baseColorHSV );
122 warn sprintf( 'H:%1.4f S:%1.4f L:%1.4f', hexToHSL('#3e148c') );
123 my @target = hexToRGB('#935ff2');
124 my ($best, $min_dist);
125 for (my $s = 0.50; $s < 0.90; $s += 0.001) {
126 for ( my $l = 0.50; $l <= 0.80; $l += 0.001 ) {
127 my $hexColor = hslHex($baseColorHue, $s, $l);
128 my ($r,$g,$b) = hexToRGB( $hexColor );
129 my $dist = abs($r-$target[0])
131 + abs($b-$target[2]);
132 if (not defined($best) or $dist < $min_dist) {
133 $best = [ $s, $l, $hexColor ];
138 warn sprintf( 's%1.3f, l%1.3f -> %s',
141 my $baseTheme = "AppTheme";
143 use constant STEP_DEGREES => 5;
146 # for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
147 # printf "<style name=\"%s.%03d\" parent=\"%s\">\n",
148 # $baseTheme, $hue, $baseTheme;
149 # printf " <item name=\"colorPrimary\">#%s</item>\n",
150 # hsvHex($hue/360.0, 0.61, 0.95);
151 # printf " <item name=\"colorPrimaryDark\">#%s</item>\n",
152 # hsvHex($hue/360.0, 0.86, 0.55);
153 # printf " <item name=\"colorAccent\">#%s</item>\n",
154 # hsvHex(($hue-4)/360.0, 0.72, 0.82);
155 # printf " <item name=\"table_row_dark_bg\">#28%s</item>\n",
156 # hsvHex($hue/360.0, 0.65, 0.83);
157 # printf " <item name=\"table_row_light_bg\">#28%s</item>\n",
158 # hsvHex($hue/360.0, 0.20, 1.00);
159 # printf " <item name=\"header_border\">#80%s</item>\n",
160 # hsvHex(($hue+6)/360.0, 0.86, 0.55);
161 # printf "</style>\n";
167 my $baseIndent = shift;
168 for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
170 $out->print(hslStyleForHue($hue, $baseTheme, $baseIndent));
177 my $baseIndent = shift // '';
181 60 => 0.400, # yellow
182 120 => 0.400, # green
185 300 => 0.500, # magenta
189 my ($x0, $x1, $y0, $y1);
190 $x0 = (int( $hue / 60 ) * 60) % 360;
195 # linear interpolation
196 my $l1 = $y0 + 1.0 * ( $hue - $x0 ) * ( $y1 - $y0 ) / ( $x1 - $x0 );
203 my $indent = "$baseIndent ";
206 $result .= sprintf "$baseIndent<style name=\"%s.%03d\" parent=\"%s\">\n",
207 $baseTheme, $hue, $baseTheme;
210 $result .= sprintf "$baseIndent<style name=\"%s\">\n",
212 # $result .= "$indent<item name=\"windowActionBar\">false</item>\n";
213 # $result .= "$indent<item name=\"windowNoTitle\">true</item>\n";
214 # $result .= "$indent<item name=\"textColor\">#757575</item>\n";
217 $result .= sprintf "$indent<item name=\"colorPrimary\">#%s</item>\n",
218 hslHex($hue, $S, $l1);
219 $result .= sprintf "$indent<item name=\"colorPrimaryTransparent\">#00%s</item>\n",
220 hslHex($hue, $S, $l1);
221 $result .= sprintf "$indent<item name=\"colorAccent\">#%s</item>\n",
222 hslHex($hue, $S, $l2);
223 $result .= sprintf "$indent<item name=\"colorPrimaryDark\">#%s</item>\n",
224 hslHex($hue, $S, $l2);
225 $result .= sprintf "$indent<item name=\"table_row_dark_bg\">#%s</item>\n",
226 hslHex($hue, $S, $l3);
227 $result .= sprintf "$indent<item name=\"table_row_light_bg\">#%s</item>\n",
228 hslHex($hue, $S, $l4);
229 $result .= "$baseIndent</style>\n";
237 my $start_marker = '<!-- theme list start -->';
238 my $end_marker = '<!-- theme list end -->';
239 my ($fh, $filename) = tempfile(basename($0).'.XXXXXXXX', DIR => dirname($xml));
240 open(my $in, '<', $xml);
241 my $base_indent = '';
242 my $state = 'waiting-for-start-marker';
244 if ( $state eq 'waiting-for-start-marker' ) {
246 $state = 'skipping-styles', $base_indent = $1
247 if /^(\s*)\Q$start_marker\E/;
250 if ( $state eq 'skipping-styles' ) {
251 next unless /^\s*\Q$end_marker\E/;
252 outputThemes($fh, $base_indent);
254 $state = 'copying-the-rest';
257 if ( $state eq 'copying-the-rest') {
262 die "Unexpected state '$state'";
268 rename($filename, $xml);
271 outputThemes(\*STDOUT);