]> git.ktnx.net Git - mobile-ledger.git/blob - tools/gen-styles
use a constant for the color wheel step size
[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.NoActionBar";
142
143 use constant STEP_DEGREES => 15;
144
145 # # hsb
146 # for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
147 #       printf "<style name=\"%s.%d\" 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=\"drawer_background\">#ffffffff</item>\n";
156 #       printf "  <item name=\"table_row_dark_bg\">#28%s</item>\n",
157 #                       hsvHex($hue/360.0, 0.65, 0.83);
158 #       printf "  <item name=\"table_row_light_bg\">#28%s</item>\n",
159 #                       hsvHex($hue/360.0, 0.20, 1.00);
160 #       printf "  <item name=\"header_border\">#80%s</item>\n",
161 #                       hsvHex(($hue+6)/360.0, 0.86, 0.55);
162 #       printf "</style>\n";
163 # }
164
165 # HSL
166 sub outputThemes {
167         my $out = shift;
168         my $baseIndent = shift;
169         $out->print(hslStyleForHue($baseColorHue, undef, $baseIndent));
170         for( my $hue = 0; $hue < 360; $hue += STEP_DEGREES ) {
171                 $out->print("\n");
172                 $out->print(hslStyleForHue($hue, $baseTheme, $baseIndent));
173         }
174 }
175
176 sub hslStyleForHue {
177         my $hue = shift;
178         my $base = shift;
179         my $baseIndent = shift // '';
180
181         my $blueL = 0.665;
182         my $yellowL = 0.350;
183
184         my $blueL2 = 0.350;
185         my $yellowL2 = 0.500;
186
187         # $y == 0 for yellow
188         my $y = $hue - 60;
189         $y += 360 if $y < 0;
190         # $q == 0 for yellow, 1 for blue
191         my $q = cos(deg2rad(abs($y-180)/2.0));
192         my $l1 = $yellowL + ($blueL - $yellowL) * $q;
193         my $l2 = 0.250 + 0.250 * $q;
194         my $l3 = 0.950;
195         my $l4 = 0.980;
196
197         my $result = "";
198         my $indent = "$baseIndent    ";
199
200         if ($base) {
201                 $result .= sprintf "$baseIndent<style name=\"%s.%d\" parent=\"%s\">\n",
202                         $baseTheme, $hue, $baseTheme;
203         }
204         else {
205                 $result .= sprintf "$baseIndent<style name=\"%s\">\n",
206                         $baseTheme;
207                 $result .= "$indent<item name=\"windowActionBar\">false</item>\n";
208                 $result .= "$indent<item name=\"windowNoTitle\">true</item>\n";
209                 $result .= "$indent<item name=\"textColor\">#8a000000</item>\n";
210         }
211         my $S = 0.845;
212         $result .= sprintf "$indent<item name=\"colorPrimary\">#%s</item>\n",
213                 hslHex($hue, $S, $l1);
214         $result .= sprintf "$indent<item name=\"colorPrimaryTransparent\">#00%s</item>\n",
215                 hslHex($hue, $S, $l1);
216         $result .= sprintf "$indent<item name=\"colorAccent\">#%s</item>\n",
217                 hslHex($hue, $S, $l2);
218         $result .= "$indent<item name=\"drawer_background\">#ffffffff</item>\n";
219         $result .= sprintf "$indent<item name=\"table_row_dark_bg\">#%s</item>\n",
220                 hslHex($hue, $S, $l3);
221         $result .= sprintf "$indent<item name=\"table_row_light_bg\">#%s</item>\n",
222                 hslHex($hue, $S, $l4);
223         $result .= "$baseIndent</style>\n";
224
225         return $result;
226 }
227
228 my $xml = shift;
229
230 if ($xml) {
231         my $start_marker = '<!-- theme list start -->';
232         my $end_marker = '<!-- theme list end -->';
233         my ($fh, $filename) = tempfile(basename($0).'.XXXXXXXX', DIR => dirname($xml));
234         open(my $in, '<', $xml);
235         my $base_indent = '';
236         my $state = 'waiting-for-start-marker';
237         while (<$in>) {
238                 if ( $state eq 'waiting-for-start-marker' ) {
239                         print $fh $_;
240                         $state = 'skipping-styles', $base_indent = $1
241                                 if /^(\s*)\Q$start_marker\E/;
242                         next;
243                 }
244                 if ( $state eq 'skipping-styles' ) {
245                         next unless /^\s*\Q$end_marker\E/;
246                         outputThemes($fh, $base_indent);
247                         print $fh $_;
248                         $state = 'copying-the-rest';
249                         next;
250                 }
251                 if ( $state eq 'copying-the-rest') {
252                         print $fh $_;
253                         next;
254                 }
255
256                 die "Unexpected state '$state'";
257         }
258
259         close($fh);
260         close($in);
261
262         rename($filename, $xml);
263 }
264 else {
265         outputThemes(\*STDOUT);
266 }