1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "maus.h"
6#include "maus_font.h"
7
8static void glyph_to_buf(ule_i32 sgx, ule_i32 egx, ule_i32 sgy, ule_i32 egy,
9 ule_i32 gox, ule_i32 goy, MausGlyph* g, ule_u32 col_up,
10 ule_u32* buf, ule_u32 stride);
11static ule_i8 parse_glyphs(FILE* f, MausFont* font, char* line, size_t line_size,
12 ule_i32* encoding, ule_i32* advance, ule_i32* bw, ule_i32* bh,
13 ule_i32* xoff, ule_i32* yoff, ule_i8* seen);
14static ule_u64 strtou64(const char* str);
15
16/* "blit" a glpyh to some buffer */
17static void glyph_to_buf(ule_i32 sgx, ule_i32 egx, ule_i32 sgy, ule_i32 egy,
18 ule_i32 gox, ule_i32 goy, MausGlyph* g, ule_u32 col_up,
19 ule_u32* buf, ule_u32 stride)
20{
21 int gy;
22 int gx;
23 ule_i32 dstx;
24
25 ule_u32* row;
26 ule_i32 offset;
27
28 for (gy = sgy; gy < egy; gy++) {
29 int dsty = goy + gy;
30
31 /* calculate row pointer once per scanline */
32 row = buf + (dsty * stride);
33 offset = gy * g->w;
34
35 for (gx = sgx; gx < egx; gx++) {
36 if (g->bmp[offset + gx] > 0) {
37 dstx = gox + gx;
38 row[dstx] = col_up;
39 }
40 }
41 }
42}
43
44static ule_i8 parse_glyphs(FILE* f, MausFont* font, char* line, size_t line_size,
45 ule_i32* encoding, ule_i32* advance, ule_i32* bw, ule_i32* bh,
46 ule_i32* xoff, ule_i32* yoff, ule_i8* seen)
47{
48 ule_i32 rowbits;
49
50 ule_i32 x;
51 ule_i32 y;
52 ule_u64 bits;
53
54 MausGlyph* g;
55
56 while (fgets(line, line_size, f)) {
57 if (sscanf(line, "ENCODING %d", encoding) == 1)
58 continue;
59 if (sscanf(line, "DWIDTH %d", advance) == 1)
60 continue;
61 if (sscanf(line, "BBX %d %d %d %d", bw, bh, xoff, yoff) == 4)
62 continue;
63
64 if (strncmp(line, "BITMAP", 6) == 0) {
65 /* invalid character code
66 TODO: just skip it */
67 if (*encoding < 0 || *encoding >= MAUS_BDF_GLYPHS_MAX)
68 break;
69
70 /* empty/invalid size */
71 if (*bw <= 0 || *bh <= 0)
72 break;
73
74 g = &font->glyphs[*encoding];
75 free(g->bmp); /* prevent leak on hot reload */
76 memset(g, 0, sizeof(*g));
77
78 g->w = *bw;
79 g->h = *bh;
80 g->xoff = *xoff;
81 g->yoff = *yoff;
82 g->adv = *advance;
83
84 g->bmp = calloc((size_t)(*bw) * (*bh), 1);
85 if (!g->bmp) {
86 fclose(f);
87 maus_font_free(font);
88 return 0;
89 }
90
91 /* each bmp row in BDF is padded as a multiple
92 of 8 bits. this rounds width up to the next
93 multiple of 8 */
94 rowbits = (*bw + 7) & ~7;
95
96 for (y = 0; y < *bh; y++) {
97 /* missing row */
98 if (!fgets(line, line_size, f)) {
99 fclose(f);
100 maus_font_free(font);
101 return 0;
102 }
103
104 bits = strtou64(line);
105 for (x = 0; x < *bw; x++) {
106 int bit = rowbits - 1 - x;
107
108 if ((bits >> bit) & 1)
109 g->bmp[y * (*bw) + x] = 255; /* solid */
110 }
111 }
112
113 g->valid = 1;
114 if (*advance > font->cellw)
115 font->cellw = *advance;
116 *seen = 1;
117
118 continue;
119 }
120
121 if (strncmp(line, "ENDCHAR", 7) == 0)
122 break;
123 }
124
125 return 1;
126}
127
128static ule_u64 strtou64(const char* str)
129{
130 ule_u64 n = 0;
131 ule_u8 c;
132
133 while (*str) {
134 c = (ule_u8)*str++;
135
136 if (c >= '0' && c <= '9')
137 c = (ule_u8)(c - '0');
138 else if (c >= 'A' && c <= 'F')
139 c = (ule_u8)(c - 'A' + 10);
140 else if (c >= 'a' && c <= 'f')
141 c = (ule_u8)(c - 'a' + 10);
142 else
143 break;
144
145 n = (n << 4) | c;
146 }
147
148 return n;
149}
150
151void maus_draw_text(Maus* mw, MausFont* font, ule_i32 x, ule_i32 y,
152 const char* text, MausColor col)
153{
154 ule_u32 col_up = MAUS_UNPACK_COL(col);
155
156 size_t i;
157
158 /* current coordinates */
159 ule_i32 cx = x;
160 ule_i32 cy = y;
161
162 /* origins to start drawing glyph from */
163 ule_i32 gox;
164 ule_i32 goy;
165
166 /* starting glyph coordinates */
167 ule_i32 sgx;
168 ule_i32 sgy;
169
170 /* ending glyph coordinates */
171 ule_i32 egx;
172 ule_i32 egy;
173
174 /* current glyph pointer */
175 MausGlyph* g;
176
177 if (!mw || !mw->bfb || !font || !text)
178 return;
179
180 for (i = 0; text[i] != '\0'; i++) {
181 char c = text[i];
182
183 if (c == '\n') {
184 cx = x;
185 cy += font->cellh;
186 continue;
187 }
188
189 g = &font->glyphs[(ule_u8)c];
190 if (!g->valid) {
191 /* still advance cx if character invalid */
192 cx += (font->cellw > 0 ? font->cellw : 8);
193 continue;
194 }
195
196 /* origins to start drawing glyph from */
197 gox = cx + g->xoff;
198 goy = cy + font->asc - g->h - g->yoff;
199
200 /* starting coordinates */
201 sgx = (-gox > 0) ? -gox : 0;
202 sgy = (-goy > 0) ? -goy : 0;
203
204 egx = (mw->width - gox < g->w) ? (mw->width - gox) : g->w;
205 egy = (mw->height - goy < g->h) ?(mw->height - goy) : g->h;
206
207 /* cull glyphs which start off screen */
208 if (sgx >= egx || sgy >= egy) {
209 cx += g->adv;
210 continue;
211 }
212
213 glyph_to_buf(sgx, egx, sgy, egy, gox, goy, g, col_up, mw->bfb, mw->stride);
214 cx += g->adv;
215 }
216}
217
218MausFont* maus_font_load(const char* path)
219{
220 FILE* f = fopen(path, "r");
221 MausFont* font = calloc(1, sizeof(MausFont));
222 char line[1024];
223
224 ule_i32 asc = -1;
225 ule_i32 dsc = -1;
226 ule_i32 encoding = -1;
227 ule_i32 advance = 0;
228 ule_i32 bw = 0; /* bmp width */
229 ule_i32 bh = 0; /* bmp height */
230 ule_i32 xoff = 0;
231 ule_i32 yoff = 0;
232 ule_i8 seen = 0;
233
234 int i;
235
236 if (!f) {
237 maus_log(stderr, "failed to load font \"%s\"", path);
238 return NULL;
239 }
240
241 if (!font) {
242 maus_log(stderr, "failed to calloc font");
243 fclose(f);
244 return NULL;
245 }
246
247 while (fgets(line, sizeof(line), f)) {
248 if (sscanf(line, "FONT_ASCENT %d", &asc) == 1)
249 continue;
250 if (sscanf(line, "FONT_DESCENT %d", &dsc) == 1)
251 continue;
252
253 /* ignore everything until glyphs start */
254 if (strncmp(line, "STARTCHAR", 9) != 0)
255 continue;
256
257 /* reset vaules for new glyph */
258 encoding = -1;
259 advance = 0;
260 bw = 0;
261 bh = 0;
262 xoff = 0;
263 yoff = 0;
264
265 if (!parse_glyphs(f, font, line, sizeof(line), &encoding, &advance, &bw, &bh, &xoff, &yoff, &seen))
266 return NULL;
267 }
268
269
270 fclose(f);
271
272 if (!seen) {
273 free(font);
274 return NULL;
275 }
276
277 /* estimate asc/dsc if they werent provided */
278 if (asc < 0 || dsc < 0) {
279 asc = 0;
280 dsc = 0;
281
282 for (i = 0; i < MAUS_BDF_GLYPHS_MAX; i++) {
283 MausGlyph* g = &font->glyphs[i];
284
285 /* unloaded glpyh */
286 if (!g->valid)
287 continue;
288
289 /* highest point above baseline */
290 if (g->h + g->yoff > asc)
291 asc = g->h + g->yoff;
292
293 /* lowest point below baseline */
294 if (-g->yoff > dsc)
295 dsc = -g->yoff;
296 }
297 }
298
299 font->asc = asc;
300 font->dsc = -dsc;
301 font->cellh = asc + dsc;
302
303 if (font->cellw <= 0 || font->cellh <= 0) {
304 maus_font_free(font);
305 return NULL;
306 }
307
308 return font;
309}
310
311void maus_font_free(MausFont* font)
312{
313 int i;
314
315 if (!font)
316 return;
317
318 for (i = 0; i < MAUS_BDF_GLYPHS_MAX; i++)
319 free(font->glyphs[i].bmp);
320
321 free(font);
322}
323