commit 0c80f70
uint
·
2026-06-11 22:34:02 +0000 UTC
parent a3d4e82
Rip BDF loading from cterm, add font loading, font drawing [cterm](https://github.com/uint23/cterm)
2 files changed,
+266,
-0
+226,
-0
1@@ -0,0 +1,226 @@
2+#include <stdio.h>
3+#include <stdlib.h>
4+#include <string.h>
5+
6+#include "maus.h"
7+#include "maus_font.h"
8+
9+void maus_draw_text(Maus* mw, MausFont* font, int32_t x, int32_t y,
10+ const char* text, MausColor col)
11+{
12+ if (!mw || !mw->fb || !font || !text)
13+ return;
14+
15+ uint32_t col_up = MAUS_UNPACK_COL(col);
16+ int32_t cx = x; /* current x */
17+ int32_t cy = y; /* current y */
18+
19+ for (size_t i = 0; text[i] != '\0'; i++) {
20+ char c = text[i];
21+
22+ if (c == '\n') {
23+ cx = x;
24+ cy += font->cellh;
25+ continue;
26+ }
27+
28+ MausGlyph* g = &font->glyphs[(uint8_t)c];
29+ if (!g->valid) {
30+ /* still advance cx if character invalid */
31+ cx += (font->cellw > 0 ? font->cellw : 8);
32+ continue;
33+ }
34+
35+ /* origins to start drawing glpyh from */
36+ int32_t gox = cx + g->xoff;
37+ int32_t goy = cy + font->asc - g->h - g->yoff;
38+
39+ /* "blit" font to framebuffer */
40+ for (uint16_t gy = 0; gy < g->h; gy++) {
41+ for (uint16_t gx = 0; gx < g->w; gx++) {
42+ bool solid = g->bmp[gy * g->w + gx] > 0;
43+ if (solid) {
44+ int32_t dstx = gox + gx;
45+ int32_t dsty = goy + gy;
46+ if (dstx >= 0 && dstx < (int)mw->width &&
47+ dsty >= 0 && dsty < (int)mw->height) {
48+ MAUS_PIXEL_AT(mw, dstx, dsty) = col_up;
49+ }
50+ }
51+ }
52+ }
53+
54+ cx += g->adv;
55+ }
56+}
57+
58+MausFont* maus_font_load(const char* path)
59+{
60+ FILE* fp = fopen(path, "r");
61+ if (!fp) {
62+ maus_log(stderr, "failed to load font \"%s\"", path);
63+ return NULL;
64+ }
65+
66+ MausFont* font = calloc(1, sizeof(MausFont));
67+ if (!font) {
68+ maus_log(stderr, "failed to calloc font");
69+ fclose(fp);
70+ return NULL;
71+ }
72+
73+ char line[1024];
74+ int asc = -1;
75+ int dsc = -1;
76+ int encoding = -1;
77+ int advance = 0;
78+ int bw = 0; /* bmp width */
79+ int bh = 0; /* bmp height */
80+ int xoff = 0;
81+ int yoff = 0;
82+ bool seen_any_glyph = 0;
83+
84+ while (fgets(line, sizeof(line), fp)) {
85+ if (sscanf(line, "FONT_ASCENT %d", &asc) == 1)
86+ continue;
87+ if (sscanf(line, "FONT_DESCENT %d", &dsc) == 1)
88+ continue;
89+
90+ /* ignore everything until glyphs start */
91+ if (strncmp(line, "STARTCHAR", 9) != 0)
92+ continue;
93+
94+ /* reset vaules for new glyph */
95+ encoding = -1;
96+ advance = 0;
97+ bw = 0;
98+ bh = 0;
99+ xoff = 0;
100+ yoff = 0;
101+
102+ while (fgets(line, sizeof(line), fp)) {
103+ if (sscanf(line, "ENCODING %d", &encoding) == 1)
104+ continue;
105+ if (sscanf(line, "DWIDTH %d", &advance) == 1)
106+ continue;
107+ if (sscanf(line, "BBX %d %d %d %d", &bw, &bh, &xoff, &yoff) == 4)
108+ continue;
109+
110+ if (strncmp(line, "BITMAP", 6) == 0) {
111+ int rowbits;
112+
113+ /* invalid character code
114+ TODO: just skip it */
115+ if (encoding < 0 || encoding >= MAUS_BDF_GLYPHS_MAX)
116+ break;
117+
118+ /* empty/invalid size */
119+ if (bw <= 0 || bh <= 0)
120+ break;
121+
122+ MausGlyph* g = &font->glyphs[encoding];
123+ free(g->bmp); /* prevent leak on hot reload */
124+ memset(g, 0, sizeof(*g));
125+
126+ g->w = bw;
127+ g->h = bh;
128+ g->xoff = xoff;
129+ g->yoff = yoff;
130+ g->adv = advance;
131+
132+ g->bmp = calloc((size_t)bw * bh, 1);
133+ if (!g->bmp) {
134+ fclose(fp);
135+ maus_font_free(font);
136+ return NULL;
137+ }
138+
139+ /* each bmp row in BDF is padded as a multiple
140+ of 8 bits. this rounds width up to the next
141+ multiple of 8 */
142+ rowbits = (bw + 7) & ~7;
143+
144+ for (int y = 0; y < bh; y++) {
145+ uint64_t bits;
146+
147+ /* missing row */
148+ if (!fgets(line, sizeof(line), fp)) {
149+ fclose(fp);
150+ maus_font_free(font);
151+ return NULL;
152+ }
153+
154+ bits = strtoull(line, NULL, 16); /* hex to bits */
155+ for (int x = 0; x < bw; x++) {
156+ int bit = rowbits - 1 - x;
157+
158+ if ((bits >> bit) & 1)
159+ g->bmp[y * bw + x] = 255; /* solid */
160+ }
161+ }
162+
163+ g->valid = true;
164+ if (advance > font->cellw)
165+ font->cellw = advance;
166+ seen_any_glyph = true;
167+
168+ continue;
169+ }
170+
171+ if (strncmp(line, "ENDCHAR", 7) == 0)
172+ break;
173+ }
174+ }
175+
176+ fclose(fp);
177+
178+ if (!seen_any_glyph) {
179+ free(font);
180+ return NULL;
181+ }
182+
183+ /* estimate asc/dsc if they werent provided */
184+ if (asc < 0 || dsc < 0) {
185+ asc = 0;
186+ dsc = 0;
187+
188+ for (int i = 0; i < MAUS_BDF_GLYPHS_MAX; i++) {
189+ MausGlyph* g = &font->glyphs[i];
190+
191+ /* unloaded glpyh */
192+ if (!g->valid)
193+ continue;
194+
195+ /* highest point above baseline */
196+ if (g->h + g->yoff > asc)
197+ asc = g->h + g->yoff;
198+
199+ /* lowest point below baseline */
200+ if (-g->yoff > dsc)
201+ dsc = -g->yoff;
202+ }
203+ }
204+
205+ font->asc = asc;
206+ font->dsc = -dsc;
207+ font->cellh = asc + dsc;
208+
209+ if (font->cellw <= 0 || font->cellh <= 0) {
210+ maus_font_free(font);
211+ return NULL;
212+ }
213+
214+ return font;
215+}
216+
217+void maus_font_free(MausFont* font)
218+{
219+ if (!font)
220+ return;
221+
222+ for (int i = 0; i < MAUS_BDF_GLYPHS_MAX; i++)
223+ free(font->glyphs[i].bmp);
224+
225+ free(font);
226+}
227+
+40,
-0
1@@ -0,0 +1,40 @@
2+#ifndef MAUS_FONT_H
3+#define MAUS_FONT_H
4+
5+#include <stdint.h>
6+#include <stdbool.h>
7+
8+#include "maus.h"
9+
10+#define MAUS_BDF_GLYPHS_MAX 256
11+
12+typedef struct {
13+ uint8_t* bmp;
14+ uint16_t w;
15+ uint16_t h;
16+ int16_t xoff;
17+ int16_t yoff;
18+ uint16_t adv;
19+ bool valid;
20+} MausGlyph;
21+
22+typedef struct {
23+ MausGlyph glyphs[MAUS_BDF_GLYPHS_MAX];
24+ int32_t asc;
25+ int32_t dsc;
26+ int32_t cellh;
27+ int32_t cellw;
28+} MausFont;
29+
30+/* Loops through text passing parameters to glyph drawer */
31+void maus_draw_text(Maus* mw, MausFont* font, int32_t x, int32_t y,
32+ const char* text, MausColor col);
33+
34+/* load a bdf font into MausFont. returns NULL on fail */
35+MausFont* maus_font_load(const char* path);
36+
37+/* release all allocations made with a MausFont */
38+void maus_font_free(MausFont* font);
39+
40+#endif /* MAUS_FONT_H */
41+