]> git.ktnx.net Git - mobile-ledger-staging.git/blob - tools/gen-styles
prepare v0.21.0 (49) for release
[mobile-ledger-staging.git] / tools / gen-styles
1 #!/usr/bin/perl
2
3 use strict; use warnings; use utf8::all;
4 use autodie;
5 use Math::Trig;
6 use File::Basename qw(basename dirname);
7 use File::Temp qw(tempfile);
8 use Getopt::Long;
9
10 my $opt_night;
11
12 GetOptions(
13     'night!'    => \$opt_night,
14 ) or exit 1;
15
16 my $DEFAULT_HUE = 261.2245;
17
18 sub hexTuple {
19         my ($r, $g, $b) = @_;
20         return sprintf('%02x%02x%02x', int(255*$r+0.5), int(255*$g+0.5), int(255*$b+0.5));
21 }
22 sub hsvHex {
23         my ($hue, $sat, $val ) = @_;
24         my $h = int($hue * 6);
25         my $f = $hue * 6 - $h;
26         my $p = $val * (1 - $sat);
27         my $q = $val * ( 1 - $f * $sat);
28         my $t = $val * ( 1 - (1-$f) * $sat);
29
30         return hexTuple($val, $t, $p) if $h == 0 or $h == 6;
31         return hexTuple($q, $val, $p) if $h == 1;
32         return hexTuple($p, $val, $t) if $h == 2;
33         return hexTuple($p, $q, $val) if $h == 3;
34         return hexTuple($t, $p, $val) if $h == 4;
35         return hexTuple($val, $p, $q) if $h == 5;
36
37         die $h;
38 }
39
40 # https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
41 sub hslHex {
42         my ($hue, $sat, $lig ) = @_;
43         $hue = $hue / 360.0;
44         my $h = ($hue * 6.0);
45         my $c = (1 - abs(2.0*$lig - 1)) * $sat;
46         my $h_mod_2 = $h - 2.0*int($h/2);
47         my $x = $c * (1 - abs($h_mod_2 - 1));
48         my ($r, $g, $b);
49         my $m = $lig - $c / 2.0;
50
51         return hexTuple($c + $m, $x + $m,  0 + $m) if $h < 1 or $h == 6;
52         return hexTuple($x + $m, $c + $m,  0 + $m) if $h < 2;
53         return hexTuple( 0 + $m, $c + $m, $x + $m) if $h < 3;
54         return hexTuple( 0 + $m, $x + $m, $c + $m) if $h < 4;
55         return hexTuple($x + $m,  0 + $m, $c + $m) if $h < 5;
56         return hexTuple($c + $m,  0 + $m, $x + $m) if $h < 6;
57
58         die $h;
59 }
60
61 my @hexDigit = split //, '0123456789abcdef';
62 my %hexValue = map(
63         (lc($hexDigit[$_]) => $_, uc($hexDigit[$_]) => $_ ),
64         0..15 );
65
66 sub min {
67         my $min = shift;
68
69         for (@_) { $min = $_ if $_ < $min }
70
71         return $min;
72 }
73
74 sub max {
75         my $max = shift;
76
77         for (@_) { $max = $_ if $_ > $max }
78
79         return $max;
80 }
81
82 sub hexToRGB {
83         my $hexTriplet = shift;
84
85         my @d = $hexTriplet =~ /^#?(.)(.)(.)(.)(.)(.)/;
86
87         return (16 * $hexValue{$d[0]} + $hexValue{$d[1]},
88                 16 * $hexValue{$d[2]} + $hexValue{$d[3]},
89                 16 * $hexValue{$d[4]} + $hexValue{$d[5]});
90 }
91
92 sub hexToHSL {
93         my $hexTriplet = shift;
94
95         my ($r,$g,$b) = hexToRGB($hexTriplet);
96         #warn "$hexTriplet -> $r:$g:$b";
97
98         for ($r, $g, $b ) { $_ = $_ / 255.0 }
99
100         my $M = max($r, $g, $b);
101         my $m = min($r, $g, $b);
102         my $C = $M - $m;
103
104         my $h;
105         if ($C == 0) {
106                 $h = 0;
107         }
108         elsif ( $r == $M ) {
109                 $h = ($g-$b)/$C;
110                 $h -= 6*int($h/6.0);
111         }
112         elsif ( $g == $M ) {
113                 $h = ($b-$r)/$C + 2;
114         }
115         elsif ( $b == $M ) {
116                 $h = ($r-$g)/$C + 4;
117         }
118         else { die "$C, $M, $r, $g, $b"; }
119
120         my $H = 60 * $h;
121         my $L = ($M + $m) / 2;
122
123         my $S = ( $L <= 0.5 ) ? $C/(2*$L) : $C / (2-2*$L);
124
125         return( $H, $S, $L );
126 }
127
128 my $baseColor = '#935ff2';
129 my $baseColorHSV = [ hexToHSL($baseColor) ];
130 my $baseColorHue = $baseColorHSV->[0];
131 warn sprintf( '%s → H:%1.4f S:%1.4f V:%1.4f', $baseColor, @$baseColorHSV );
132 my @target = hexToRGB($baseColor);
133 my ($best, $min_dist);
134 for (my $s = 0.50; $s < 0.90; $s += 0.001) {
135         for ( my $l = 0.50; $l <= 0.80; $l += 0.001 ) {
136                 my $hexColor = hslHex($baseColorHue, $s, $l);
137                 my ($r,$g,$b) = hexToRGB( $hexColor );
138                 my $dist = abs($r-$target[0])
139                          + abs($g-$target[1])
140                          + abs($b-$target[2]);
141                 if (not defined($best) or $dist < $min_dist) {
142                         $best = [ $s, $l, $hexColor ];
143                         $min_dist = $dist;
144                 }
145         }
146 }
147 warn sprintf( 's%1.3f, l%1.3f → %s',
148         @$best );
149
150 my $baseTheme = "AppTheme";
151
152 use constant STEP_DEGREES => 5;
153
154 # # hsb
155 # for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
156 #       printf "<style name=\"%s.%03d\" parent=\"%s\">\n",
157 #               $baseTheme, $hue, $baseTheme;
158 #       printf "  <item name=\"colorPrimary\">#%s</item>\n",
159 #                       hsvHex($hue/360.0, 0.61, 0.95);
160 #       printf "  <item name=\"colorPrimaryDark\">#%s</item>\n",
161 #                       hsvHex($hue/360.0, 0.86, 0.55);
162 #       printf "  <item name=\"colorAccent\">#%s</item>\n",
163 #                       hsvHex(($hue-4)/360.0, 0.72, 0.82);
164 #       printf "  <item name=\"table_row_dark_bg\">#28%s</item>\n",
165 #                       hsvHex($hue/360.0, 0.65, 0.83);
166 #       printf "  <item name=\"table_row_light_bg\">#28%s</item>\n",
167 #                       hsvHex($hue/360.0, 0.20, 1.00);
168 #       printf "  <item name=\"header_border\">#80%s</item>\n",
169 #                       hsvHex(($hue+6)/360.0, 0.86, 0.55);
170 #       printf "</style>\n";
171 # }
172
173 # HSL
174 sub outputThemes {
175         my $out = shift;
176         my $baseIndent = shift;
177         $out->print("\n");
178         $out->print(hslStyleForHue($DEFAULT_HUE, $baseTheme, $baseIndent, 'default'));
179         for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
180                 $out->print("\n");
181                 $out->print(hslStyleForHue($hue, $baseTheme, $baseIndent));
182         }
183 }
184
185 sub hslStyleForHue {
186         my $hue = shift;
187         my $base = shift;
188         my $baseIndent = shift // '';
189         my $subTheme = shift // sprintf('%03d', $hue);
190
191         my %lQ = (
192                 0   => 0.450,   # red
193                 60  => 0.400,   # yellow
194                 120 => 0.400,   # green
195                 180 => 0.390,   # cyan
196                 240 => 0.745,   # blue
197                 300 => 0.505,   # magenta
198         );
199         $lQ{360} = $lQ{0};
200
201         my ($x0, $x1, $y0, $y1);
202         $x0 = (int( $hue / 60 ) * 60) % 360;
203         $x1 = $x0 + 60;
204         $y0 = $lQ{$x0};
205         $y1 = $lQ{$x1};
206
207         # linear interpolation
208         my $l1 = $y0 + 1.0 * ( $hue - $x0 ) * ( $y1 - $y0 ) / ( $x1 - $x0 );
209
210         my $l2 = $l1 * ( $opt_night ? 1.10 : 0.80 );
211         my $l3 = $opt_night ? 0.200 : 0.950;
212         my $l4 = $opt_night ? 0.100 : 0.980;
213
214         my $result = "";
215         my $indent = "$baseIndent    ";
216
217         if ($base) {
218                 $result .= sprintf "$baseIndent<style name=\"%s.%s\" parent=\"%s\">\n",
219                         $baseTheme, $subTheme, $baseTheme;
220         }
221         else {
222                 $result .= sprintf "$baseIndent<style name=\"%s\">\n",
223                         $baseTheme;
224 #                $result .= "$indent<item name=\"windowActionBar\">false</item>\n";
225 #                $result .= "$indent<item name=\"windowNoTitle\">true</item>\n";
226 #                $result .= "$indent<item name=\"textColor\">#757575</item>\n";
227         }
228         my $S = 0.845;
229         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
230             'colorPrimary', hslHex( $hue, $S, $l1 );
231         $result .= sprintf "$indent<item name=\"%s\">#00%s</item>\n",
232             'colorPrimaryTransparent', hslHex( $hue, $S, $l1 );
233         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
234             'colorAccent', hslHex( $hue, $S, $l2 );
235         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
236             'colorPrimaryDark', hslHex( $hue, $S, $l2 );
237         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
238             'table_row_dark_bg', hslHex( $hue, $S, $l3 );
239         $result .= sprintf "$indent<item name=\"%s\">#%s</item>\n",
240             'table_row_light_bg', hslHex( $hue, $S, $l4 );
241         $result .= "$baseIndent</style>\n";
242
243         return $result;
244 }
245
246 my $xml = shift;
247
248 if ($xml) {
249         my $start_marker = '<!-- theme list start -->';
250         my $end_marker = '<!-- theme list end -->';
251         my ($fh, $filename) = tempfile(basename($0).'.XXXXXXXX', DIR => dirname($xml));
252         $fh->binmode(':utf8');
253         open(my $in, '<', $xml);
254         my $base_indent = '';
255         my $state = 'waiting-for-start-marker';
256         while (<$in>) {
257                 if ( $state eq 'waiting-for-start-marker' ) {
258                         print $fh $_;
259                         $state = 'skipping-styles', $base_indent = $1
260                                 if /^(\s*)\Q$start_marker\E/;
261                         next;
262                 }
263                 if ( $state eq 'skipping-styles' ) {
264                         next unless /^\s*\Q$end_marker\E/;
265                         outputThemes($fh, $base_indent);
266                         print $fh $_;
267                         $state = 'copying-the-rest';
268                         next;
269                 }
270                 if ( $state eq 'copying-the-rest') {
271                         print $fh $_;
272                         next;
273                 }
274
275                 die "Unexpected state '$state'";
276         }
277
278         close($fh);
279         close($in);
280
281         rename($filename, $xml);
282 }
283 else {
284         outputThemes(\*STDOUT);
285 }