]> git.ktnx.net Git - mobile-ledger.git/blob - tools/gen-styles
migrate to Matherial DayNight themes
[mobile-ledger.git] / tools / gen-styles
1 #!/usr/bin/perl
2
3 use strict; use warnings; use utf8;
4 use autodie;
5 use Math::Trig;
6 use File::Basename qw(basename dirname);
7 use File::Temp qw(tempfile);
8
9 sub hexTuple {
10         my ($r, $g, $b) = @_;
11         return sprintf('%02x%02x%02x', int(255*$r+0.5), int(255*$g+0.5), int(255*$b+0.5));
12 }
13 sub hsvHex {
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);
20
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;
27
28         die $h;
29 }
30
31 # https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
32 sub hslHex {
33         my ($hue, $sat, $lig ) = @_;
34         $hue = $hue / 360.0;
35         my $h = ($hue * 6.0);
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));
39         my ($r, $g, $b);
40         my $m = $lig - $c / 2.0;
41
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;
48
49         die $h;
50 }
51
52 my @hexDigit = split //, '0123456789abcdef';
53 my %hexValue = map(
54         (lc($hexDigit[$_]) => $_, uc($hexDigit[$_]) => $_ ),
55         0..15 );
56
57 sub min {
58         my $min = shift;
59
60         for (@_) { $min = $_ if $_ < $min }
61
62         return $min;
63 }
64
65 sub max {
66         my $max = shift;
67
68         for (@_) { $max = $_ if $_ > $max }
69
70         return $max;
71 }
72
73 sub hexToRGB {
74         my $hexTriplet = shift;
75
76         my @d = $hexTriplet =~ /^#?(.)(.)(.)(.)(.)(.)/;
77
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]});
81 }
82
83 sub hexToHSL {
84         my $hexTriplet = shift;
85
86         my ($r,$g,$b) = hexToRGB($hexTriplet);
87         warn "$hexTriplet -> $r:$g:$b";
88
89         for ($r, $g, $b ) { $_ = $_ / 255.0 }
90
91         my $M = max($r, $g, $b);
92         my $m = min($r, $g, $b);
93         my $C = $M - $m;
94
95         my $h;
96         if ($C == 0) {
97                 $h = 0;
98         }
99         elsif ( $r == $M ) {
100                 $h = ($g-$b)/$C;
101                 $h -= 6*int($h/6.0);
102         }
103         elsif ( $g == $M ) {
104                 $h = ($b-$r)/$C + 2;
105         }
106         elsif ( $b == $M ) {
107                 $h = ($r-$g)/$C + 4;
108         }
109         else { die "$C, $M, $r, $g, $b"; }
110
111         my $H = 60 * $h;
112         my $L = ($M + $m) / 2;
113
114         my $S = ( $L <= 0.5 ) ? $C/(2*$L) : $C / (2-2*$L);
115
116         return( $H, $S, $L );
117 }
118
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])
130                          + abs($g-$target[1])
131                          + abs($b-$target[2]);
132                 if (not defined($best) or $dist < $min_dist) {
133                         $best = [ $s, $l, $hexColor ];
134                         $min_dist = $dist;
135                 }
136         }
137 }
138 warn sprintf( 's%1.3f, l%1.3f -> %s',
139         @$best );
140
141 my $baseTheme = "AppTheme";
142
143 use constant STEP_DEGREES => 5;
144
145 # # hsb
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";
162 # }
163
164 # HSL
165 sub outputThemes {
166         my $out = shift;
167         my $baseIndent = shift;
168         for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
169                 $out->print("\n");
170                 $out->print(hslStyleForHue($hue, $baseTheme, $baseIndent));
171         }
172 }
173
174 sub hslStyleForHue {
175         my $hue = shift;
176         my $base = shift;
177         my $baseIndent = shift // '';
178
179         my %lQ = (
180                 0   => 0.450,   # red
181                 60  => 0.400,   # yellow
182                 120 => 0.400,   # green
183                 180 => 0.400,   # cyan
184                 240 => 0.750,   # blue
185                 300 => 0.500,   # magenta
186         );
187         $lQ{360} = $lQ{0};
188
189         my ($x0, $x1, $y0, $y1);
190         $x0 = (int( $hue / 60 ) * 60) % 360;
191         $x1 = $x0 + 60;
192         $y0 = $lQ{$x0};
193         $y1 = $lQ{$x1};
194
195         # linear interpolation
196         my $l1 = $y0 + 1.0 * ( $hue - $x0 ) * ( $y1 - $y0 ) / ( $x1 - $x0 );
197
198         my $l2 = $l1 * 0.80;
199         my $l3 = 0.950;
200         my $l4 = 0.980;
201
202         my $result = "";
203         my $indent = "$baseIndent    ";
204
205         if ($base) {
206                 $result .= sprintf "$baseIndent<style name=\"%s.%03d\" parent=\"%s\">\n",
207                         $baseTheme, $hue, $baseTheme;
208         }
209         else {
210                 $result .= sprintf "$baseIndent<style name=\"%s\">\n",
211                         $baseTheme;
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";
215         }
216         my $S = 0.845;
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";
230
231         return $result;
232 }
233
234 my $xml = shift;
235
236 if ($xml) {
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';
243         while (<$in>) {
244                 if ( $state eq 'waiting-for-start-marker' ) {
245                         print $fh $_;
246                         $state = 'skipping-styles', $base_indent = $1
247                                 if /^(\s*)\Q$start_marker\E/;
248                         next;
249                 }
250                 if ( $state eq 'skipping-styles' ) {
251                         next unless /^\s*\Q$end_marker\E/;
252                         outputThemes($fh, $base_indent);
253                         print $fh $_;
254                         $state = 'copying-the-rest';
255                         next;
256                 }
257                 if ( $state eq 'copying-the-rest') {
258                         print $fh $_;
259                         next;
260                 }
261
262                 die "Unexpected state '$state'";
263         }
264
265         close($fh);
266         close($in);
267
268         rename($filename, $xml);
269 }
270 else {
271         outputThemes(\*STDOUT);
272 }