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
10
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 retroflat_blit_t* target, RETROFLAT_COLOR color,
44 char c, struct RETROFONT* font, size_t x, size_t y, uint8_t flags );
45
46void retrofont_string(
47 retroflat_blit_t* 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 retroflat_blit_t* target, const char* str, size_t str_sz,
54 MAUG_MHANDLE font_h, size_t max_w, size_t max_h,
55 size_t* p_out_w, size_t* p_out_h, 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 || '_' == *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(
114 RETROFONT_TRACE_LVL, "detected glyph height: " SIZE_T_FMT, glyph_h );
115
116 return glyph_h;
117}
118
119MERROR_RETVAL retrofont_load(
120 const char* font_name, MAUG_MHANDLE* p_font_h,
121 uint8_t glyph_h, uint16_t first_glyph, uint16_t glyphs_count
122) {
123 MERROR_RETVAL retval = MERROR_OK;
124 mfile_t font_file;
125 char line[RETROFONT_LINE_SZ];
126 struct RETROFONT* font = NULL;
127 char* line_bytes = NULL;
128 uint16_t glyph_idx = 0;
129 uint8_t* p_glyph = NULL;
130 size_t i = 0;
131 uint8_t glyph_w_bytes = 0;
132 uint8_t glyph_w = 0;
133
134 maug_mzero( &font_file, sizeof( mfile_t ) );
135
136 /* TODO: Font loading seems to be very slow on a 486. This needs
137 * investigation.
138 */
139
140 if( 0 == glyph_h ) {
141 glyph_h = retrofont_sz_from_filename( font_name );
142 }
143 if( 0 == glyph_h ) {
144 error_printf( "unable to determine font height!" );
145 retval = MERROR_GUI;
146 goto cleanup;
147 }
148
149 /* Try to separate the string into index:glyph bytes. */
150 #define retrofont_split_glyph_line( line, line_bytes ) \
151 line_bytes = maug_strchr( line, ':' ); \
152 if( NULL == line_bytes ) { \
153 error_printf( "invalid line: %s", line ); \
154 retval = MERROR_PARSE; \
155 } \
156 line_bytes[0] = '\0'; \
157 line_bytes++;
158
159 retval = mfile_open_read( font_name, &font_file );
160 maug_cleanup_if_not_ok();
161
162 debug_printf( RETROFONT_TRACE_LVL, "font file opened, reading..." );
163
164 /* Figure out font width from file and alloc just enough. */
165 retval = font_file.read_line( &font_file, line, RETROFONT_LINE_SZ, 0 );
166 maug_cleanup_if_not_ok();
167 retrofont_split_glyph_line( line, line_bytes );
168 maug_cleanup_if_not_ok();
169 glyph_w_bytes = (maug_strlen( line_bytes ) / glyph_h) >> 1; /* 2 hex per byte */
170 debug_printf( RETROFONT_TRACE_LVL, "glyph_w_bytes: %u", glyph_w_bytes );
171 glyph_w = glyph_w_bytes * 8;
172
173#if 0 < RETROFONT_TRACE_LVL
174 debug_printf( RETROFONT_TRACE_LVL, "glyph_w: %u, glyph_sz: %u",
175 glyph_w, glyph_h * glyph_w_bytes );
176#endif
177
178 /* Alloc enough for each glyph, plus the size of the font header. */
179 *p_font_h = maug_malloc( 1,
180 sizeof( struct RETROFONT ) +
181 (glyph_h * glyph_w_bytes * (1 + glyphs_count)) );
182 maug_cleanup_if_null_alloc( MAUG_MHANDLE, *p_font_h );
183
184#if 0 < RETROFONT_TRACE_LVL
185 debug_printf( RETROFONT_TRACE_LVL, "allocated font %s: " SIZE_T_FMT " bytes",
186 font_name, (glyph_h * glyph_w_bytes * (1 + glyphs_count)) +
187 sizeof( struct RETROFONT ) );
188#endif
189
190 maug_mlock( *p_font_h, font );
191 maug_cleanup_if_null_alloc( struct RETROFONT*, font );
192
193 /* Set initial font parameters. */
194 font->sz = sizeof( struct RETROFONT );
195 font->first_glyph = first_glyph;
196 font->glyph_w = glyph_w;
197 font->glyph_h = glyph_h;
198 font->glyph_sz = glyph_h * glyph_w_bytes;
199
200 while( font_file.has_bytes( &font_file ) ) {
201 retval = font_file.read_line( &font_file, line, RETROFONT_LINE_SZ, 0 );
202
203 retrofont_split_glyph_line( line, line_bytes );
204 if( MERROR_PARSE == retval ) {
205 /* The line couldn't parse, so skip it, but don't give up entirely and
206 * keep going to the next line.
207 */
208 retval = MERROR_OK;
209 continue;
210 }
211
212 /* Figure out the index of this glyph. */
213 glyph_idx = maug_atou32( line, 0, 16 );
214 if( glyph_idx < first_glyph || glyph_idx > first_glyph + glyphs_count ) {
215 /* Skip glyph out of range. */
216 continue;
217 }
218
219 debug_printf( RETROFONT_TRACE_LVL, "found glyph: %u", glyph_idx );
220
221 /* Find where to put the decoded glyph. */
222 p_glyph = retrofont_glyph_at( font, glyph_idx );
223
224 for( i = 0 ; font->glyph_h > i ; i++ ) {
225 switch( font->glyph_w ) {
226 case 8:
227 p_glyph[i] = 0;
228 p_glyph[i] |= maug_hctoi( line_bytes[i << 1] ) << 4;
229 p_glyph[i] |= maug_hctoi( line_bytes[(i << 1) + 1] );
230 break;
231
232 case 16:
233 /* TODO */
234 break;
235
236 case 32:
237 /* TODO */
238 break;
239
240 default:
241 error_printf( "invalid font width: %u", font->glyph_w );
242 retval = MERROR_PARSE;
243 goto cleanup;
244 }
245 }
246
247#if 0 < RETROFONT_TRACE_LVL
248 /* Test dump to verify glyph integrity. */
249 if( glyph_idx == '0' ) {
250 retrofont_dump_glyph( p_glyph, glyph_w, glyph_h );
251 }
252#endif
253
254 font->glyphs_count++;
255
256 debug_printf( RETROFONT_TRACE_LVL,
257 "parsed %u glyphs...", font->glyphs_count );
258
259 /* If we're not tracing, try to omit printfs from the inner loop! */
260#if 0 < RETROFONT_TRACE_LVL
261 debug_printf( RETROFONT_TRACE_LVL,
262 "%u %s (" SIZE_T_FMT " hbytes)", glyph_idx - first_glyph, line_bytes,
263 maug_strlen( line_bytes ) );
264#endif
265 }
266
267cleanup:
268
269 if( NULL != font ) {
270 maug_munlock( *p_font_h, font );
271 }
272
273 mfile_close( &font_file );
274
275 return retval;
276}
277
278void retrofont_blit_glyph(
279 retroflat_blit_t* target, RETROFLAT_COLOR color,
280 char c, struct RETROFONT* font, size_t x, size_t y, uint8_t flags
281) {
282 uint8_t* glyph = retrofont_glyph_at( font, c );
283 int16_t x_iter, y_iter, y_start, y_end, x_end, x_pen_down = -1;
284 uint8_t prev_px_was_clear = 0;
285
286 debug_printf( RETROFONT_TRACE_LVL, "blit glyph: %c", c );
287
288 y_start = RETROFONT_FLAG_OUTLINE == (RETROFONT_FLAG_OUTLINE & flags) ?
289 -1 : 0;
291 font->glyph_h + 1 : font->glyph_h;
293 font->glyph_w + 1 : font->glyph_w;
294
295 #define _retrofont_px_is_clear( x_iter, y_iter ) \
296 (0 > (x_iter) || font->glyph_w <= (x_iter) || \
297 0 > (y_iter) || font->glyph_h <= (y_iter) || \
298 (1 << (font->glyph_w - (x_iter)) != \
299 (glyph[(y_iter)] & (1 << (font->glyph_w - (x_iter))))))
300
301 for( y_iter = y_start ; y_end > y_iter ; y_iter++ ) {
302 /* Reset drawing state for new "scanline." */
303 prev_px_was_clear = 1;
304 x_pen_down = -1;
305
306 /* Note the >= here... slightly "overscan" on X so we can close any
307 * end-terminating horizontal lines.
308 */
309 for( x_iter = 0 ; x_end >= x_iter ; x_iter++ ) {
310 if( _retrofont_px_is_clear( x_iter, y_iter ) ) {
311
312 if( 0 <= x_pen_down ) {
313 /* Draw this "scanline" of the glyph in one go. */
314 retroflat_2d_line(
315 target, color,
316 x + x_pen_down, y + y_iter, x + x_iter, y + y_iter, 0 );
317 x_pen_down = -1;
318 }
319
320 if(
322 (!prev_px_was_clear ||
323 !_retrofont_px_is_clear( x_iter + 1, y_iter ) ||
324 !_retrofont_px_is_clear( x_iter, y_iter + 1 ) ||
325 !_retrofont_px_is_clear( x_iter, y_iter - 1 ))
326 ) {
327 /* Draw outline pixel. */
328 retroflat_2d_px(
329 target, RETROFLAT_COLOR_DARKBLUE,
330 x + x_iter, y + y_iter, 0 );
331 }
332
333 /* Save a little time on the next clear pixel check. */
334 prev_px_was_clear = 1;
335
336 } else {
337 /* This pixel is filled. */
338
339 if( 0 > x_pen_down ) {
340 /* Store the X coord to start drawing this "scanline." */
341 x_pen_down = x_iter;
342 }
343
344 prev_px_was_clear = 0;
345 }
346 }
347 }
348}
349
350void retrofont_string(
351 retroflat_blit_t* target, RETROFLAT_COLOR color,
352 const char* str, size_t str_sz,
353 MAUG_MHANDLE font_h, size_t x, size_t y,
354 size_t max_w, size_t max_h, uint8_t flags
355) {
356 size_t i = 0;
357 size_t x_iter = x;
358 size_t y_iter = y;
359 struct RETROFONT* font = NULL;
360
361 if( (MAUG_MHANDLE)NULL == font_h ) {
362 error_printf( "NULL font specified!" );
363 goto cleanup;
364 }
365
366 if( 0 == str_sz ) {
367 str_sz = maug_strlen( str );
368 }
369
370 maug_mlock( font_h, font );
371 if( NULL == font ) {
372 error_printf( "could not lock font!" );
373 goto cleanup;
374 }
375
376 /* TODO: Stop at max_w/max_h */
377
378 for( i = 0 ; str_sz > i ; i++ ) {
379 /* Terminate prematurely at null. */
380 if( '\0' == str[i] ) {
381 break;
382 }
383
384 /* Handle forced newline. */
385 if( '\r' == str[i] || '\n' == str[i] ) {
386 x_iter = x;
387 y_iter += font->glyph_h;
388 debug_printf(
389 RETROFONT_TRACE_LVL,
390 "newline: " SIZE_T_FMT ", " SIZE_T_FMT, x_iter, y_iter );
391 continue;
392 }
393
394 /* Filter out characters not present in this font. */
395 if(
396 ' ' != str[i] && (
397 str[i] < font->first_glyph ||
398 str[i] >= font->first_glyph + font->glyphs_count
399 )
400 ) {
401 error_printf( "invalid character: 0x%02x", str[i] );
402 continue;
403 }
404
405 /* TODO: More dynamic way to determine space character? */
406 if( ' ' != str[i] ) {
407 retrofont_blit_glyph(
408 target, color, str[i], font, x_iter, y_iter, flags );
409 }
410
411 x_iter += font->glyph_w;
412 if( 0 < max_w && (x + max_w) <= x_iter + font->glyph_w ) {
413 x_iter = x;
414 y_iter += font->glyph_h;
415 }
416 }
417
418cleanup:
419
420 if( NULL != font ) {
421 maug_munlock( font_h, font );
422 }
423}
424
425MERROR_RETVAL retrofont_string_sz(
426 retroflat_blit_t* target, const char* str, size_t str_sz,
427 MAUG_MHANDLE font_h, size_t max_w, size_t max_h,
428 size_t* p_out_w, size_t* p_out_h, uint8_t flags
429) {
430 size_t x_iter = 0;
431 size_t i = 0;
432 size_t out_h = 0; /* Only used if p_out_h is NULL. */
433 MERROR_RETVAL retval = MERROR_OK;
434 struct RETROFONT* font = NULL;
435
436 if( (MAUG_MHANDLE)NULL == font_h ) {
437 error_printf( "NULL font specified!" );
438 retval = MERROR_GUI;
439 goto cleanup;
440 }
441
442 if( NULL == p_out_h ) {
443 p_out_h = &out_h;
444 }
445
446 if( 0 == str_sz ) {
447 str_sz = maug_strlen( str );
448 }
449
450 maug_mlock( font_h, font );
451 maug_cleanup_if_null_alloc( struct RETROFONT*, font );
452
453 for( i = 0 ; str_sz > i ; i++ ) {
454 /* Terminate prematurely at null. */
455 if( '\0' == str[i] ) {
456 break;
457 }
458
459 /* Handle forced newline. */
460 if( '\r' == str[i] || '\n' == str[i] ) {
461 x_iter = 0;
462 *p_out_h += font->glyph_h;
463 continue;
464 }
465
466 x_iter += font->glyph_w;
467
468 if( NULL != p_out_w && *p_out_w <= x_iter ) {
469 *p_out_w = x_iter;
470 }
471 if( 0 < max_w && max_w < x_iter + font->glyph_w ) {
472 x_iter = 0;
473 *p_out_h += font->glyph_h;
474 if( 0 < max_h && *p_out_h + font->glyph_h >= max_h && i < str_sz ) {
475 error_printf( "string will not fit!" );
476
477 /* Do not quit; just make a note and keep going. */
478 retval = MERROR_GUI;
479 }
480 }
481 }
482
483 /* Add the height of the last line. */
484 *p_out_h += font->glyph_h;
485 if( NULL != p_out_w ) {
486 *p_out_w += 1;
487 }
488
489cleanup:
490
491 if( NULL != font ) {
492 maug_munlock( font_h, font );
493 }
494
495 return retval;
496}
497
498#endif /* RETROFNT_C */
499
500/* \} */ /* retrofnt */
501
502#endif /* !RETROFNT_H */
503
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:65
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:325
#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 retrofnt.h:29