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(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