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