altos/draw: Use <> for include files
[fw/altos] / src / draw / font-convert
1 #!/usr/bin/nickle
2
3 autoimport ParseArgs
4 import File;
5
6 typedef struct {
7         int[]   bytes;
8         int     width;
9         int     height;
10         int     x_off;
11         int     y_off;
12         int     advance;
13         int     encoding;
14         int     location;
15 } glyph_t;
16
17 typedef struct {
18         glyph_t[...]    glyphs;
19         int             default_char;
20         int             ascent;
21 } font_t;
22
23 glyph_t
24 read_glyph(file f)
25 {
26         glyph_t glyph = { .encoding = -1, .bytes = (int[...]){}, .width = 0 };
27
28         while (!File::end(f)) {
29                 string  l = fgets(f);
30
31                 string[*] tokens = String::split(l, " ");
32                 if (dim(tokens) == 0)
33                         continue;
34
35                 switch (tokens[0]) {
36                 case "ENCODING":
37                         glyph.encoding = atoi(tokens[1]);
38                         break;
39                 case "DWIDTH":
40                         glyph.advance = atoi(tokens[1]);
41                         break;
42                 case "BBX":
43                         glyph.width = atoi(tokens[1]);
44                         glyph.height = atoi(tokens[2]);
45                         glyph.x_off = atoi(tokens[3]);
46                         glyph.y_off = glyph.height + atoi(tokens[4]);
47                         break;
48                 case "ENDCHAR":
49                         return glyph;
50                 case "BITMAP":
51                         int byte_stride = ceil(glyph.width/8);
52                         for (int y = 0; y < glyph.height; y++) {
53                                 string l = fgets(f);
54                                 for (int x = 0; x < byte_stride; x++) {
55                                         string v = String::substr(l, x * 2, 2);
56                                         glyph.bytes[dim(glyph.bytes)] = atoi(v, 16);
57                                 }
58                         }
59                         break;
60                 }
61         }
62         return glyph;
63 }
64
65 font_t read_font(file f) {
66         font_t  font = { .glyphs = {}, .default_char = -1 };
67         bool in_head = true;
68
69         while (in_head && !File::end(f)) {
70                 string l = File::fgets(f);
71
72                 string[*] tokens = String::split(l, " ");
73                 switch (tokens[0]) {
74                 case "DEFAULT_CHAR":
75                         font.default_char = atoi(tokens[1]);
76                         break;
77                 case "FONT_ASCENT":
78                         font.ascent = atoi(tokens[1]);
79                         break;
80                 case "CHARS":
81                         in_head = false;
82                         break;
83                 }
84         }
85         while (!File::end(f)) {
86                 glyph_t glyph = read_glyph(f);
87                 if (glyph.encoding == -1)
88                         break;
89                 font.glyphs[dim(font.glyphs)] = glyph;
90         }
91         return font;
92 }
93
94 int
95 flip_byte(int x)
96 {
97         int     dest = 0;
98
99         for (int i = 0; i < 8; i++)
100                 dest |= ((x >> (7 - i)) & 1) << i;
101         return dest;
102 }
103
104 void print_font(file out, font_t font, string font_name) {
105         int     width = font.glyphs[0].width;
106         int     height = font.glyphs[0].height;
107         int     max_width = width;
108         int     max_height = height;
109         int[128] pos = { 0 ... };
110         int[...] bytes;
111         bool fixed_size = true;
112
113         for (int i = 1; i < dim(font.glyphs); i++) {
114                 if (font.glyphs[i].width != width ||
115                    font.glyphs[i].height != height)
116                 {
117                         fixed_size = false;
118                 }
119                 max_width = max(max_width, font.glyphs[i].width);
120                 max_height = max(max_height, font.glyphs[i].height);
121         }
122
123         if (font.default_char == -1)
124                 font.default_char = font.glyphs[0].encoding;
125
126         /* build byte array */
127         for (int i = 0; i < dim(font.glyphs); i++) {
128                 if (font.glyphs[i].encoding < dim(pos))
129                 {
130                         pos[font.glyphs[i].encoding] = dim(bytes);
131                         for (int b = 0; b < dim(font.glyphs[i].bytes); b++)
132                                 bytes[dim(bytes)] = font.glyphs[i].bytes[b];
133                 }
134         }
135
136         /* Fill in default glyph */
137         for (int i = 0; i < dim(pos); i++)
138                 if (pos[i] == -1)
139                         pos[i] = pos[font.default_char];
140
141         fprintf(out, "#include <ao_draw.h>\n");
142         fprintf(out, "static const uint8_t %s_bytes[%d] = {", font_name, dim(bytes));
143         for (int b = 0; b < dim(bytes); b++) {
144                 if ((b & 15) == 0)
145                         fprintf(out, "\n\t");
146                 else
147                         fprintf(out, " ");
148                 fprintf(out, "0x%02x,", flip_byte(bytes[b]));
149         }
150         fprintf(out, "\n};\n\n");
151
152         fprintf(out, "static const uint16_t %s_pos[%d] = {", font_name, dim(pos));
153         for (int i = 0; i < dim(pos); i++) {
154                 if ((i & 7) == 0)
155                         fprintf(out, "\n\t");
156                 else
157                         fprintf(out, " ");
158                 fprintf(out, "%6d,", pos[i]);
159         }
160         fprintf(out, "\n};\n\n");
161
162         fprintf(out, "#define GLYPH_WIDTH_MAX %d\n", max_width);
163         fprintf(out, "#define GLYPH_HEIGHT_MAX %d\n", max_height);
164         if (fixed_size) {
165                 fprintf(out, "#define GLYPH_WIDTH %d\n", width);
166                 fprintf(out, "#define GLYPH_HEIGHT %d\n", height);
167                 fprintf(out, "#define GLYPH_ASCENT %d\n", font.ascent);
168         } else {
169                 fprintf(out, "static const struct ao_glyph_metrics %s_metrics[%d] = {\n", font_name, dim(pos));
170                 for (int i = 0; i < dim(pos); i++) {
171                         int g = 0;
172                         for (int j = 0; j < dim(font.glyphs); j++) {
173                                 if (font.glyphs[j].encoding == i) {
174                                         g = j;
175                                         break;
176                                 }
177                         }
178                         string c;
179                         if (' ' <= i && i <= '~')
180                                 c = sprintf("%c", i);
181                         else
182                                 c = sprintf("%02x", i);
183                         fprintf(out, "    { .width = %d, .height = %d, .x_off = %d, .y_off = %d, .advance = %d }, /* %s */\n",
184                                font.glyphs[g].width, font.glyphs[g].height,
185                                font.glyphs[g].x_off, font.glyphs[g].y_off, font.glyphs[g].advance, c);
186                 }
187                 fprintf(out, "};\n");
188         }
189         fprintf(out, "const struct ao_font %s_font = {\n", font_name);
190         fprintf(out, "\t.bytes = %s_bytes,\n", font_name);
191         fprintf(out, "\t.pos = %s_pos,\n", font_name);
192         if (!fixed_size)
193                 fprintf(out, "\t.metrics = %s_metrics,\n", font_name);
194         fprintf(out, "\t.max_width = %d,\n", max_width);
195         fprintf(out, "\t.max_height = %d,\n", max_height);
196         fprintf(out, "\t.ascent = %d,\n", font.ascent);
197         fprintf(out, "};\n");
198 }
199
200 string
201 to_c(string a)
202 {
203         string r = "";
204         for (int i = String::rindex(a, "/") + 1; i < String::length(a); i++) {
205                 int c = a[i];
206                 if (c == '.')
207                         break;
208                 if (!(Ctype::isalnum(c) || c == '_'))
209                         c = '_';
210                 r += String::new(c);
211         }
212         if (!Ctype::isalpha(r[0]))
213                 r = "_" + r;
214         return r;
215 }
216
217 void
218 do_font(string name, file out)
219 {
220         twixt (file f = File::open(name, "r"); File::close(f)) {
221                 font_t font = read_font(f);
222                 print_font(out, font, to_c(name));
223         }
224 }
225
226 argdesc argd = {
227         args = {
228                 {
229                         .var = (arg_var.arg_string) &(string output),
230                         .name = "output",
231                         .abbr = 'o',
232                         .expr_name = "file",
233                         .desc = "Output file name"
234                 },
235         },
236         posn_args = {
237                 {
238                         var = (arg_var.arg_string) &(string input),
239                         name = "input",
240                 }
241         }
242 };
243
244 parseargs(&argd, &argv)
245
246 if (is_uninit(&output))
247         do_font(input, stdout);
248 else {
249         twixt(file f = File::open(output, "w"); File::close(f))
250                 do_font(input, f);
251 }