maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
retrofnt.h
Go to the documentation of this file.
1
2#ifndef RETROFNT_H
3#define RETROFNT_H
4
11#define RETROFONT_PRESENT 1
12
13#ifndef RETROFONT_LINE_SZ
14# define RETROFONT_LINE_SZ 80
15#endif /* !RETROFONT_LINE_SZ */
16
17#ifndef RETROFONT_TRACE_LVL
18# define RETROFONT_TRACE_LVL 0
19#endif /* !RETROFONT_TRACE_LVL */
20
27#define RETROFONT_FLAG_OUTLINE 0x04
28
29struct RETROFONT {
30 uint16_t sz;
31 uint16_t first_glyph;
32 uint16_t glyphs_count;
33 uint8_t glyph_w;
34 uint8_t glyph_h;
35 uint8_t glyph_sz;
36};
37
38MERROR_RETVAL retrofont_load(
39 const char* font_name, MAUG_MHANDLE* p_font_h,
40 uint8_t glyph_h, uint16_t first_glyph, uint16_t glyphs_count );
41
42void retrofont_blit_glyph(
43 struct RETROFLAT_BITMAP* target, RETROFLAT_COLOR color,
44 char c, struct RETROFONT* font, size_t x, size_t y, uint8_t flags );
45
46void retrofont_string(
47 struct RETROFLAT_BITMAP* target, RETROFLAT_COLOR color,
48 const char* str, size_t str_sz,
49 MAUG_MHANDLE font_h, size_t x, size_t y,
50 size_t max_w, size_t max_h, uint8_t flags );
51
52MERROR_RETVAL retrofont_string_sz(
53 struct RETROFLAT_BITMAP* target, const char* str, size_t str_sz,
54 MAUG_MHANDLE font_h, size_t max_w, size_t max_h,
55 size_t* out_w_p, size_t* out_h_p, uint8_t flags );
56
60#define retrofont_glyph_at( p_font, idx ) \
61 &(((uint8_t*)(p_font))[(p_font)->sz + \
62 ((p_font)->glyph_sz * (idx - (p_font)->first_glyph))])
63
64#ifdef RETROFNT_C
65
66void retrofont_dump_glyph( uint8_t* glyph, uint8_t w, uint8_t h ) {
67 size_t x = 0, y = 0;
68 char glyph_bin[65];
69
70 for( y = 0 ; h > y ; y++ ) {
71 memset( glyph_bin, '\0', 65 );
72
73 for( x = 0 ; w > x ; x++ ) {
74 glyph_bin[x] = 1 << (w - x) == (glyph[y] & (1 << (w - x))) ? 'x' : '.';
75 }
76
77 debug_printf( RETROFONT_TRACE_LVL, "%s", glyph_bin );
78 }
79}
80
81static size_t retrofont_sz_from_filename( const char* font_name ) {
82 const char* p_c = NULL;
83 size_t glyph_h = 0;
84 size_t i = 0;
85 char glyph_h_buf[10];
86
87 maug_mzero( glyph_h_buf, 10 );
88
89 assert( NULL != font_name );
90 assert( ' ' <= font_name[0] );
91
92 p_c = maug_strrchr( font_name, '.' );
93 while( p_c - 1 > font_name ) {
94 /* Start at the char before the '.' and work backwords until a '-'. */
95 p_c--;
96 if( '-' == *p_c ) {
97 break;
98 }
99
100 /* TODO: Break if not a digit! */
101
102 /* Shift existing numbers up by one. */
103 for( i = 9 ; 0 < i ; i-- ) {
104 glyph_h_buf[i] = glyph_h_buf[i - 1];
105 }
106
107 /* Add the most recent number to the beginning. */
108 glyph_h_buf[0] = *p_c;
109 }
110
111 glyph_h = atoi( glyph_h_buf );
112
113 debug_printf( 1, "detected glyph height: " SIZE_T_FMT, glyph_h );
114
115 return glyph_h;
116}
117
118MERROR_RETVAL retrofont_load(
119 const char* font_name, MAUG_MHANDLE* p_font_h,
120 uint8_t glyph_h, uint16_t first_glyph, uint16_t glyphs_count
121) {
122 MERROR_RETVAL retval = MERROR_OK;
123 mfile_t font_file;
124 char line[RETROFONT_LINE_SZ];
125 struct RETROFONT* font = NULL;
126 char* line_bytes = NULL;
127 uint16_t glyph_idx = 0;
128 uint8_t* p_glyph = NULL;
129 size_t i = 0;
130 uint8_t glyph_w_bytes = 0;
131 uint8_t glyph_w = 0;
132
133 maug_mzero( &font_file, sizeof( mfile_t ) );
134
135 /* TODO: Font loading seems to be very slow on a 486. This needs
136 * investigation.
137 */
138
139 if( 0 == glyph_h ) {
140 glyph_h = retrofont_sz_from_filename( font_name );
141 }
142 if( 0 == glyph_h ) {
143 error_printf( "unable to determine font height!" );
144 retval = MERROR_GUI;
145 goto cleanup;
146 }
147
148 /* Try to separate the string into index:glyph bytes. */
149 #define retrofont_split_glyph_line( line, line_bytes ) \
150 line_bytes = maug_strchr( line, ':' ); \
151 if( NULL == line_bytes ) { \
152 error_printf( "invalid line: %s", line ); \
153 retval = MERROR_PARSE; \
154 } \
155 line_bytes[0] = '\0'; \
156 line_bytes++;
157
158 retval = mfile_open_read( font_name, &font_file );
159 maug_cleanup_if_not_ok();
160
161 /* Figure out font width from file and alloc just enough. */
162 retval = font_file.read_line( &font_file, line, RETROFONT_LINE_SZ, 0 );
163 maug_cleanup_if_not_ok();
164 retrofont_split_glyph_line( line, line_bytes );
165 maug_cleanup_if_not_ok();
166 glyph_w_bytes = (maug_strlen( line_bytes ) / glyph_h) >> 1; /* 2 hex per byte */
167 debug_printf( RETROFONT_TRACE_LVL, "glyph_w_bytes: %u", glyph_w_bytes );
168 glyph_w = glyph_w_bytes * 8;
169
170#if 0 < RETROFONT_TRACE_LVL
171 debug_printf( RETROFONT_TRACE_LVL, "glyph_w: %u, glyph_sz: %u",
172 glyph_w, glyph_h * glyph_w_bytes );
173#endif
174
175 /* Alloc enough for each glyph, plus the size of the font header. */
176 *p_font_h = maug_malloc( 1,
177 sizeof( struct RETROFONT ) +
178 (glyph_h * glyph_w_bytes * (1 + glyphs_count)) );
179 maug_cleanup_if_null_alloc( MAUG_MHANDLE, *p_font_h );
180
181#if 0 < RETROFONT_TRACE_LVL
182 debug_printf( RETROFONT_TRACE_LVL, "allocated font %s: " SIZE_T_FMT " bytes",
183 font_name, (glyph_h * glyph_w_bytes * (1 + glyphs_count)) +
184 sizeof( struct RETROFONT ) );
185#endif
186
187 maug_mlock( *p_font_h, font );
188 maug_cleanup_if_null_alloc( struct RETROFONT*, font );
189
190 /* Set initial font parameters. */
191 font->sz = sizeof( struct RETROFONT );
192 font->first_glyph = first_glyph;
193 font->glyph_w = glyph_w;
194 font->glyph_h = glyph_h;
195 font->glyph_sz = glyph_h * glyph_w_bytes;
196
197 while( mfile_has_bytes( &font_file ) ) {
198 retval = font_file.read_line( &font_file, line, RETROFONT_LINE_SZ, 0 );
199
200 retrofont_split_glyph_line( line, line_bytes );
201 if( MERROR_PARSE == retval ) {
202 /* The line couldn't parse, so skip it, but don't give up entirely and
203 * keep going to the next line.
204 */
205 retval = MERROR_OK;
206 continue;
207 }
208
209 /* Figure out the index of this glyph. */
210 glyph_idx = maug_atou32( line, 0, 16 );
211 if( glyph_idx < first_glyph || glyph_idx > first_glyph + glyphs_count ) {
212 /* Skip glyph out of range. */
213 continue;
214 }
215
216 debug_printf( RETROFONT_TRACE_LVL, "found glyph: %u", glyph_idx );
217
218 /* Find where to put the decoded glyph. */
219 p_glyph = retrofont_glyph_at( font, glyph_idx );
220
221 for( i = 0 ; font->glyph_h > i ; i++ ) {
222 switch( font->glyph_w ) {
223 case 8:
224 p_glyph[i] = 0;
225 p_glyph[i] |= maug_hctoi( line_bytes[i << 1] ) << 4;
226 p_glyph[i] |= maug_hctoi( line_bytes[(i << 1) + 1] );
227 break;
228
229 case 16:
230 /* TODO */
231 break;
232
233 case 32:
234 /* TODO */
235 break;
236
237 default:
238 error_printf( "invalid font width: %u", font->glyph_w );
239 retval = MERROR_PARSE;
240 goto cleanup;
241 }
242 }
243
244#if 0 < RETROFONT_TRACE_LVL
245 /* Test dump to verify glyph integrity. */
246 if( glyph_idx == '0' ) {
247 retrofont_dump_glyph( p_glyph, glyph_w, glyph_h );
248 }
249#endif
250
251 font->glyphs_count++;
252
253 debug_printf( RETROFONT_TRACE_LVL,
254 "parsed %u glyphs...", font->glyphs_count );
255
256 /* If we're not tracing, try to omit printfs from the inner loop! */
257#if 0 < RETROFONT_TRACE_LVL
258 debug_printf( RETROFONT_TRACE_LVL,
259 "%u %s (" SIZE_T_FMT " hbytes)", glyph_idx - first_glyph, line_bytes,
260 maug_strlen( line_bytes ) );
261#endif
262 }
263
264cleanup:
265
266 if( NULL != font ) {
267 maug_munlock( *p_font_h, font );
268 }
269
270 mfile_close( &font_file );
271
272 return retval;
273}
274
275void retrofont_blit_glyph(
276 struct RETROFLAT_BITMAP* target, RETROFLAT_COLOR color,
277 char c, struct RETROFONT* font, size_t x, size_t y, uint8_t flags
278) {
279 uint8_t* glyph = retrofont_glyph_at( font, c );
280 int16_t x_iter, y_iter, y_start, y_end, x_end;
281 uint8_t prev_px_was_clear = 0;
282
283 debug_printf( RETROFONT_TRACE_LVL, "blit glyph: %c", c );
284
285 y_start = RETROFONT_FLAG_OUTLINE == (RETROFONT_FLAG_OUTLINE & flags) ?
286 -1 : 0;
288 font->glyph_h + 1 : font->glyph_h;
290 font->glyph_w + 1 : font->glyph_w;
291
292 #define _retrofont_px_is_clear( x_iter, y_iter ) \
293 (0 > (x_iter) || font->glyph_w <= (x_iter) || \
294 0 > (y_iter) || font->glyph_h <= (y_iter) || \
295 (1 << (font->glyph_w - (x_iter)) != \
296 (glyph[(y_iter)] & (1 << (font->glyph_w - (x_iter))))))
297
298 for( y_iter = y_start ; y_end > y_iter ; y_iter++ ) {
299 prev_px_was_clear = 1;
300 for( x_iter = 0 ; x_end > x_iter ; x_iter++ ) {
301 if( _retrofont_px_is_clear( x_iter, y_iter ) ) {
302 if(
304 (!prev_px_was_clear ||
305 !_retrofont_px_is_clear( x_iter + 1, y_iter ) ||
306 !_retrofont_px_is_clear( x_iter, y_iter + 1 ) ||
307 !_retrofont_px_is_clear( x_iter, y_iter - 1 ))
308 ) {
309 /* Draw outline pixel. */
310 retroflat_px(
311 target, RETROFLAT_COLOR_DARKBLUE,
312 x + x_iter, y + y_iter, 0 );
313 }
314
315 /* Save a little time on the next clear pixel check. */
316 prev_px_was_clear = 1;
317
318 } else {
319 /* Draw normal color pixel. */
320 retroflat_px( target, color, x + x_iter, y + y_iter, 0 );
321
322 prev_px_was_clear = 0;
323 }
324 }
325 }
326}
327
328void retrofont_string(
329 struct RETROFLAT_BITMAP* target, RETROFLAT_COLOR color,
330 const char* str, size_t str_sz,
331 MAUG_MHANDLE font_h, size_t x, size_t y,
332 size_t max_w, size_t max_h, uint8_t flags
333) {
334 size_t i = 0;
335 size_t x_iter = x;
336 size_t y_iter = y;
337 struct RETROFONT* font = NULL;
338
339 if( (MAUG_MHANDLE)NULL == font_h ) {
340 error_printf( "NULL font specified!" );
341 goto cleanup;
342 }
343
344 if( 0 == str_sz ) {
345 str_sz = maug_strlen( str );
346 }
347
348 maug_mlock( font_h, font );
349 if( NULL == font ) {
350 error_printf( "could not lock font!" );
351 goto cleanup;
352 }
353
354 /* TODO: Stop at max_w/max_h */
355
356 for( i = 0 ; str_sz > i ; i++ ) {
357 /* Terminate prematurely at null. */
358 if( '\0' == str[i] ) {
359 break;
360 }
361
362 /* Handle forced newline. */
363 if( '\r' == str[i] || '\n' == str[i] ) {
364 x_iter = x;
365 y_iter += font->glyph_h;
366 debug_printf(
367 RETROFONT_TRACE_LVL,
368 "newline: " SIZE_T_FMT ", " SIZE_T_FMT, x_iter, y_iter );
369 continue;
370 }
371
372 /* Filter out characters not present in this font. */
373 if(
374 ' ' != str[i] && (
375 str[i] < font->first_glyph ||
376 str[i] >= font->first_glyph + font->glyphs_count
377 )
378 ) {
379 error_printf( "invalid character: 0x%02x", str[i] );
380 continue;
381 }
382
383 /* TODO: More dynamic way to determine space character? */
384 if( ' ' != str[i] ) {
385 retrofont_blit_glyph(
386 target, color, str[i], font, x_iter, y_iter, flags );
387 }
388
389 x_iter += font->glyph_w;
390 if( 0 < max_w && (x + max_w) <= x_iter + font->glyph_w ) {
391 x_iter = x;
392 y_iter += font->glyph_h;
393 }
394 }
395
396cleanup:
397
398 if( NULL != font ) {
399 maug_munlock( font_h, font );
400 }
401}
402
403MERROR_RETVAL retrofont_string_sz(
404 struct RETROFLAT_BITMAP* target, const char* str, size_t str_sz,
405 MAUG_MHANDLE font_h, size_t max_w, size_t max_h,
406 size_t* out_w_p, size_t* out_h_p, uint8_t flags
407) {
408 size_t x_iter = 0;
409 size_t i = 0;
410 MERROR_RETVAL retval = MERROR_OK;
411 struct RETROFONT* font = NULL;
412
413 if( (MAUG_MHANDLE)NULL == font_h ) {
414 error_printf( "NULL font specified!" );
415 retval = MERROR_GUI;
416 goto cleanup;
417 }
418
419 if( 0 == str_sz ) {
420 str_sz = maug_strlen( str );
421 }
422
423 maug_mlock( font_h, font );
424 maug_cleanup_if_null_alloc( struct RETROFONT*, font );
425
426 for( i = 0 ; str_sz > i ; i++ ) {
427 /* Terminate prematurely at null. */
428 if( '\0' == str[i] ) {
429 break;
430 }
431
432 /* Handle forced newline. */
433 if( '\r' == str[i] || '\n' == str[i] ) {
434 x_iter = 0;
435 *out_h_p += font->glyph_h;
436 continue;
437 }
438
439 x_iter += font->glyph_w;
440
441 if( *out_w_p <= x_iter ) {
442 *out_w_p = x_iter;
443 }
444 if( 0 < max_w && max_w < x_iter + font->glyph_w ) {
445 x_iter = 0;
446 *out_h_p += font->glyph_h;
447 if( 0 < max_h && *out_h_p + font->glyph_h >= max_h && i < str_sz ) {
448 error_printf( "string will not fit!" );
449
450 /* Do not quit; just make a note and keep going. */
451 retval = MERROR_GUI;
452 }
453 }
454 }
455
456 /* Add the height of the last line. */
457 *out_h_p += font->glyph_h;
458 *out_w_p += 1;
459
460cleanup:
461
462 if( NULL != font ) {
463 maug_munlock( font_h, font );
464 }
465
466 return retval;
467}
468
469#endif /* RETROFNT_C */
470
471/* \} */ /* retrofnt */
472
473#endif /* !RETROFNT_H */
474
int MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
#define maug_mzero(ptr, sz)
Zero the block of memory pointed to by ptr.
Definition mmem.h:62
MERROR_RETVAL mfile_open_read(const char *filename, mfile_t *p_file)
Open a file and read it into memory or memory-map it.
void mfile_close(mfile_t *p_file)
Close a file opened with mfile_open_read().
int8_t RETROFLAT_COLOR
Defines an index in the platform-specific color-table.
Definition retroflt.h:307
#define maug_hctoi(c)
Convert a single char hex digit to the int it represents.
Definition uprintf.h:317
#define retrofont_glyph_at(p_font, idx)
Get a pointer to the glyph with the given index in the given font.
Definition retrofnt.h:60
#define RETROFONT_FLAG_OUTLINE
Flag for retroflat_string() and retroflat_string_sz() to print text as outline-only.
Definition retrofnt.h:27
Definition mfile.h:74
Platform-specific bitmap structure. retroflat_bitmap_ok() can be used on a pointer to it to determine...
Definition retpltd.h:21
Definition retrofnt.h:29