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