master uint/maus / source / maus_font.c
  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