fix for wrapping artefact when display_mode=ALWAYS
[open-ps2-loader.git] / src / fntsys.c
blob722b058d0d65b94b9c0a74daa536c4f6e89fbeae
1 /*
2 Copyright 2010, Volca
3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
5 */
7 #include "include/integers.h"
8 #include "include/usbld.h"
9 #include "include/fntsys.h"
10 #include "include/renderman.h"
11 #include "include/ioman.h"
12 #include "include/utf8.h"
13 #include "include/util.h"
14 #include "include/atlas.h"
16 #include <ft2build.h>
18 #include FT_FREETYPE_H
20 extern void* freesansfont_raw;
21 extern int size_freesansfont_raw;
23 /// Maximal count of atlases per font
24 #define ATLAS_MAX 4
25 /// Atlas width in pixels
26 #define ATLAS_WIDTH 128
27 /// Atlas height in pixels
28 #define ATLAS_HEIGHT 128
30 // freetype vars
31 static FT_Library font_library;
33 static int gCharHeight;
35 static s32 gFontSemaId;
36 static ee_sema_t gFontSema;
38 static GSCLUT fontClut;
40 /** Single entry in the glyph cache */
41 typedef struct {
42 int isValid;
43 // size in pixels of the glyph
44 int width, height;
45 // offsetting of the glyph
46 int ox, oy;
47 // advancements in pixels after rendering this glyph
48 int shx, shy;
50 // atlas for which the allocation was done
51 atlas_t* atlas;
53 // atlas allocation position
54 struct atlas_allocation_t *allocation;
56 } fnt_glyph_cache_entry_t;
58 /** A whole font definition */
59 typedef struct {
60 /** GLYPH CACHE. Every glyph in the ASCII range is cached when first used
61 * this means no additional memory aside from the one needed to render the
62 * character set is used.
64 fnt_glyph_cache_entry_t **glyphCache;
66 /// Maximal font cache page index
67 int cacheMaxPageID;
69 /// Font face
70 FT_Face face;
72 /// Nonzero if font is used
73 int isValid;
75 /// Nonzero for custom fonts
76 int isDefault;
78 /// Texture atlases (default to NULL)
79 atlas_t *atlases[ATLAS_MAX];
81 /// Pointer to data, if allocation takeover was selected (will be freed)
82 void *dataPtr;
83 } font_t;
85 #define FNT_MAX_COUNT (16)
87 /// Array of font definitions
88 static font_t fonts[FNT_MAX_COUNT];
90 static rm_quad_t quad;
91 static uint32_t codepoint, state;
92 static fnt_glyph_cache_entry_t* glyph;
93 static FT_Bool use_kerning;
94 static FT_UInt glyph_index, previous;
95 static FT_Vector delta;
97 #define GLYPH_CACHE_PAGE_SIZE 256
99 #define GLYPH_PAGE_OK(font,page) ((pageid <= font->cacheMaxPageID) && (font->glyphCache[page]))
101 static int fntPrepareGlyphCachePage(font_t *font, int pageid) {
102 if (pageid > font->cacheMaxPageID) {
103 fnt_glyph_cache_entry_t **np = realloc(font->glyphCache, (pageid + 1) * sizeof(fnt_glyph_cache_entry_t *));
105 if (!np)
106 return 0;
108 font->glyphCache = np;
110 unsigned int page;
111 for (page = font->cacheMaxPageID + 1; page <= pageid; ++page)
112 font->glyphCache[page] = NULL;
114 font->cacheMaxPageID = pageid;
117 // if it already was allocated, skip this
118 if (font->glyphCache[pageid])
119 return 1;
121 // allocate the page
122 font->glyphCache[pageid] = malloc(sizeof(fnt_glyph_cache_entry_t) * GLYPH_CACHE_PAGE_SIZE);
124 int i;
125 for (i = 0; i < GLYPH_CACHE_PAGE_SIZE; ++i) {
126 font->glyphCache[pageid][i].isValid = 0;
127 font->glyphCache[pageid][i].atlas = NULL;
128 font->glyphCache[pageid][i].allocation = NULL;
131 return 1;
134 static void fntResetFontDef(font_t *fnt) {
135 fnt->glyphCache = NULL;
136 fnt->cacheMaxPageID = -1;
137 fnt->isValid = 0;
138 fnt->isDefault = 0;
140 int aid;
141 for(aid = 0; aid < ATLAS_MAX; ++aid)
142 fnt->atlases[aid] = NULL;
144 fnt->dataPtr = NULL;
147 static void fntPrepareCLUT() {
148 fontClut.PSM = GS_PSM_T8;
149 fontClut.ClutPSM = GS_PSM_CT32;
150 fontClut.Clut = memalign(128, 256 * 4);
151 fontClut.VramClut = 0;
153 // generate the clut table
154 size_t i;
155 u32 *clut = fontClut.Clut;
156 for (i = 0; i < 256; ++i) {
157 u8 alpha = i > 0x080 ? 0x080 : i;
159 *clut = alpha << 24 | i << 16 | i << 8 | i;
160 clut++;
164 static void fntDestroyCLUT() {
165 free(fontClut.Clut);
166 fontClut.Clut = NULL;
169 void fntInit(void) {
170 LOG("FNTSYS Init\n");
171 int error = FT_Init_FreeType(&font_library);
173 if (error) {
174 // just report over the ps2link
175 LOG("FNTSYS Freetype init failed with %x!\n", error);
176 // SleepThread();
179 fntPrepareCLUT();
181 gFontSema.init_count = 1;
182 gFontSema.max_count = 1;
183 gFontSema.option = 0;
184 gFontSemaId = CreateSema(&gFontSema);
186 int i;
187 for (i = 0; i < FNT_MAX_COUNT; ++i)
188 fntResetFontDef(&fonts[i]);
190 // load the default font (will be id=0)
191 fntLoad(NULL, -1, 0);
194 static void fntCacheFlushPage(fnt_glyph_cache_entry_t *page) {
195 int i;
197 for (i = 0; i < GLYPH_CACHE_PAGE_SIZE; ++i, ++page) {
198 page->isValid = 0;
199 // we're not doing any atlasFree or such - atlas has to be rebuild
200 page->allocation = NULL;
201 page->atlas = NULL;
205 static void fntCacheFlush(font_t *fnt) {
206 int i;
208 // Release all the glyphs from the cache
209 for (i = 0; i <= fnt->cacheMaxPageID; ++i) {
210 if (fnt->glyphCache[i]) {
211 fntCacheFlushPage(fnt->glyphCache[i]);
212 free(fnt->glyphCache[i]);
213 fnt->glyphCache[i] = NULL;
217 free(fnt->glyphCache);
218 fnt->glyphCache = NULL;
219 fnt->cacheMaxPageID = -1;
221 // free all atlasses too, they're invalid now anyway
222 int aid;
223 for(aid = 0; aid < ATLAS_MAX; ++aid) {
224 atlasFree(fnt->atlases[aid]);
225 fnt->atlases[aid] = NULL;
229 static int fntNewFont() {
230 int i;
231 for (i = 0; i < FNT_MAX_COUNT; ++i) {
232 if (fonts[i].isValid == 0) {
233 fntResetFontDef(&fonts[i]);
234 return i;
238 return FNT_ERROR;
241 void fntDeleteFont(font_t *font) {
242 // skip already deleted fonts
243 if (!font->isValid)
244 return;
246 // free the glyph cache, atlases, unload the font
247 fntCacheFlush(font);
249 FT_Done_Face(font->face);
251 if (font->dataPtr) {
252 free(font->dataPtr);
253 font->dataPtr = NULL;
256 font->isValid = 0;
259 int fntLoadSlot(font_t *fnt, void* buffer, int bufferSize) {
260 if (!buffer) {
261 buffer = &freesansfont_raw;
262 bufferSize = size_freesansfont_raw;
263 fnt->isDefault = 1;
266 // load the font via memory handle
267 int error = FT_New_Memory_Face(font_library, (FT_Byte*) buffer, bufferSize, 0, &fnt->face);
269 if (error) {
270 // just report over the ps2link
271 LOG("FNTSYS Freetype font loading failed with %x!\n", error);
272 // SleepThread();
273 return -1;
276 gCharHeight = 16;
278 error = FT_Set_Char_Size(fnt->face, 0, gCharHeight * 16, 300, 300);
279 /*error = FT_Set_Pixel_Sizes( face,
280 0, // pixel_width
281 gCharHeight ); // pixel_height*/
283 if (error) {
284 // just report over the ps2link
285 LOG("FNTSYS Freetype error setting font pixel size with %x!\n", error);
286 // SleepThread();
287 return -1;
290 fnt->isValid = 1;
291 return 0;
294 int fntLoad(void* buffer, int bufferSize, int takeover) {
295 // we need a new slot in the font array
296 int fontID = fntNewFont();
298 if (fontID == FNT_ERROR)
299 return FNT_ERROR;
301 font_t *fnt = &fonts[fontID];
303 if (fntLoadSlot(fnt, buffer, bufferSize) == FNT_ERROR)
304 return FNT_ERROR;
306 if (takeover)
307 fnt->dataPtr = buffer;
309 return fontID;
312 int fntLoadFile(char* path) {
313 LOG("FNTSYS LoadFile: %s\n", path);
314 // load the buffer with font
315 int size = -1;
316 void* customFont = readFile(path, -1, &size);
318 if (!customFont)
319 return FNT_ERROR;
321 int fontID = fntLoad(customFont, size, 1);
323 return fontID;
326 void fntRelease(int id) {
327 if (id < FNT_MAX_COUNT)
328 fntDeleteFont(&fonts[id]);
331 /** Terminates the font rendering system */
332 void fntEnd(void) {
333 LOG("FNTSYS End\n");
334 // release all the fonts
335 int id;
336 for (id = 0; id < FNT_MAX_COUNT; ++id)
337 fntDeleteFont(&fonts[id]);
339 // deinit freetype system
340 FT_Done_FreeType(font_library);
342 DeleteSema(gFontSemaId);
344 fntDestroyCLUT();
347 static atlas_t *fntNewAtlas() {
348 atlas_t *atl = atlasNew(ATLAS_WIDTH, ATLAS_HEIGHT, GS_PSM_T8);
350 atl->surface.ClutPSM = GS_PSM_CT32;
351 atl->surface.Clut = (u32*)(&fontClut);
353 return atl;
356 static int fntGlyphAtlasPlace(font_t *fnt, fnt_glyph_cache_entry_t* glyph) {
357 FT_GlyphSlot slot = fnt->face->glyph;
359 //LOG("FNTSYS GlyphAtlasPlace: Placing the glyph... %d x %d\n", slot->bitmap.width, slot->bitmap.rows);
361 if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) {
362 // no bitmap glyph, just skip
363 return 1;
366 int aid;
368 for (aid = 0; aid < ATLAS_MAX; ++aid) {
369 //LOG("FNTSYS Placing aid %d...\n", aid);
370 atlas_t **atl = &fnt->atlases[aid];
371 if (!*atl) { // atlas slot not yet used
372 //LOG("FNTSYS aid %d is new...\n", aid);
373 *atl = fntNewAtlas();
376 glyph->allocation =
377 atlasPlace(*atl, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.buffer);
379 if (glyph->allocation) {
380 //LOG("FNTSYS Found placement\n", aid);
381 glyph->atlas = *atl;
383 return 1;
387 LOG("FNTSYS No atlas free\n", aid);
388 return 0;
391 /** Internal method. Makes sure the bitmap data for particular character are pre-rendered to the glyph cache */
392 static fnt_glyph_cache_entry_t* fntCacheGlyph(font_t *fnt, uint32_t gid) {
393 // calc page id and in-page index from glyph id
394 int pageid = gid / GLYPH_CACHE_PAGE_SIZE;
395 int idx = gid % GLYPH_CACHE_PAGE_SIZE;
397 // do not call on every char of every font rendering call
398 if (!GLYPH_PAGE_OK(fnt,pageid))
399 if (!fntPrepareGlyphCachePage(fnt, pageid)) // failed to prepare the page...
400 return NULL;
402 fnt_glyph_cache_entry_t *page = fnt->glyphCache[pageid];
404 /* Should never happen.
405 if (!page) // safeguard
406 return NULL;
409 fnt_glyph_cache_entry_t* glyph = &page[idx];
411 if (glyph->isValid)
412 return glyph;
414 // not cached but valid. Cache
415 if (!fnt->face) {
416 LOG("FNTSYS Face is NULL!\n");
419 int error = FT_Load_Char(fnt->face, gid, FT_LOAD_RENDER);
421 if (error) {
422 LOG("FNTSYS Error loading glyph - %d\n", error);
423 return NULL;
426 // find atlas placement for the glyph
427 if (!fntGlyphAtlasPlace(fnt, glyph))
428 return NULL;
430 FT_GlyphSlot slot = fnt->face->glyph;
431 glyph->width = slot->bitmap.width;
432 glyph->height = slot->bitmap.rows;
433 glyph->shx = slot->advance.x;
434 glyph->shy = slot->advance.y;
435 glyph->ox = slot->bitmap_left;
436 glyph->oy = gCharHeight - slot->bitmap_top;
438 glyph->isValid = 1;
440 return glyph;
443 void fntSetAspectRatio(float aw, float ah) {
444 // flush cache - it will be invalid after the setting
445 int i;
447 for (i = 0; i < FNT_MAX_COUNT; ++i) {
448 if (fonts[i].isValid)
449 fntCacheFlush(&fonts[i]);
452 // set new aspect ratio (Is this correct, I wonder?)
453 // TODO: error = FT_Set_Char_Size(face, 0, gCharHeight*64, ah*300, aw*300);
456 static void fntRenderGlyph(fnt_glyph_cache_entry_t* glyph, int pen_x, int pen_y) {
457 // only if glyph has atlas placement
458 if (glyph->allocation) {
459 /* TODO: Ineffective on many parts:
460 * 1. Usage of floats for UV - fixed point should suffice (and is used internally by GS for UV)
462 * 2. GS_SETREG_TEX0 for every quad - why? gsKit should only set texture if demanded
463 * We should prepare a special fnt render method that would step over most of the
464 * performance problems under - begining with rmSetupQuad and continuing into gsKit
465 * - this method would handle the preparetion of the quads and GS upload itself,
466 * without the use of prim_quad_texture and rmSetupQuad...
468 * 3. We should use clut to keep the memory allocations sane - we're linear in the 32bit buffers
469 * anyway, no reason to waste like we do!
471 quad.ul.x = pen_x + glyph->ox;
472 quad.br.x = quad.ul.x + glyph->width;
473 quad.ul.y = pen_y + glyph->oy;
474 quad.br.y = quad.ul.y + glyph->height;
476 // UV is our own, custom thing here
477 quad.txt = &glyph->atlas->surface;
478 quad.ul.u = glyph->allocation->x;
479 quad.br.u = quad.ul.u + glyph->width;
480 quad.ul.v = glyph->allocation->y;
481 quad.br.v = quad.ul.v + glyph->height;
483 rmDrawQuad(&quad);
487 #ifndef __RTL
488 int fntRenderString(int font, int x, int y, short aligned, size_t width, size_t height, const unsigned char* string, u64 colour) {
489 // wait for font lock to unlock
490 WaitSema(gFontSemaId);
491 font_t *fnt = &fonts[font];
492 SignalSema(gFontSemaId);
494 if (aligned) {
495 if (width) {
496 x -= min(fntCalcDimensions(font, string), width) >> 1;
497 } else {
498 x -= fntCalcDimensions(font, string) >> 1;
500 y -= MENU_ITEM_HEIGHT >> 1;
503 rmApplyShiftRatio(&y);
504 quad.color = colour;
506 int pen_x = x;
507 int xmax = x + width;
508 int ymax = y + height;
510 use_kerning = FT_HAS_KERNING(fnt->face);
511 state = UTF8_ACCEPT;
512 previous = 0;
514 // Note: We need to change this so that we'll accumulate whole word before doing a layout with it
515 // for now this method breaks on any character - which is a bit ugly
517 // I don't want to do anything complicated though so I'd say
518 // we should instead have a dynamic layout routine that'll replace spaces with newlines as appropriate
519 // because that'll make the code run only once per N frames, not every frame
521 // cache glyphs and render as we go
522 for (; *string; ++string) {
523 if (utf8Decode(&state, &codepoint, *string)) // accumulate the codepoint value
524 continue;
526 glyph = fntCacheGlyph(fnt, codepoint);
527 if (!glyph)
528 continue;
530 // kerning
531 if (use_kerning && previous) {
532 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
533 if (glyph_index) {
534 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
535 pen_x += delta.x >> 6;
537 previous = glyph_index;
540 if (width) {
541 if (codepoint == '\n') {
542 pen_x = x;
543 y += MENU_ITEM_HEIGHT; // hmax is too tight and unordered, generally
544 continue;
547 if (y > ymax) // stepped over the max
548 break;
550 if (pen_x + glyph->width > xmax) {
551 pen_x = xmax + 1; // to be sure no other cahr will be written (even not a smaller one just following)
552 continue;
556 fntRenderGlyph(glyph, pen_x, y);
557 pen_x += glyph->shx >> 6;
560 return pen_x;
563 #else
564 static void fntRenderSubRTL(font_t *fnt, const unsigned char* startRTL, const unsigned char* string, fnt_glyph_cache_entry_t* glyph, int x, int y) {
565 if (glyph) {
566 x -= glyph->shx >> 6;
567 fntRenderGlyph(glyph, x, y);
570 for (; startRTL != string; ++startRTL) {
571 if (utf8Decode(&state, &codepoint, *startRTL))
572 continue;
574 glyph = fntCacheGlyph(fnt, codepoint);
575 if (!glyph)
576 continue;
578 if (use_kerning && previous) {
579 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
580 if (glyph_index) {
581 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
582 x -= delta.x >> 6;
584 previous = glyph_index;
587 x -= glyph->shx >> 6;
588 fntRenderGlyph(glyph, x, y);
592 int fntRenderString(int font, int x, int y, short aligned, size_t width, size_t height, const unsigned char* string, u64 colour) {
593 // wait for font lock to unlock
594 WaitSema(gFontSemaId);
595 font_t *fnt = &fonts[font];
596 SignalSema(gFontSemaId);
598 if (aligned) {
599 if (width) {
600 x -= min(fntCalcDimensions(font, string), width) >> 1;
601 } else {
602 x -= fntCalcDimensions(font, string) >> 1;
604 y -= MENU_ITEM_HEIGHT >> 1;
607 rmApplyShiftRatio(&y);
608 quad.color = colour;
610 int pen_x = x;
611 /*int xmax = x + width;
612 int ymax = y + height;*/
614 use_kerning = FT_HAS_KERNING(fnt->face);
615 state = UTF8_ACCEPT;
616 previous = 0;
618 short inRTL = 0;
619 int delta_x, pen_xRTL = 0;
620 fnt_glyph_cache_entry_t* glyphRTL = NULL;
621 const unsigned char* startRTL = NULL;
623 // cache glyphs and render as we go
624 for (; *string; ++string) {
625 if (utf8Decode(&state, &codepoint, *string)) // accumulate the codepoint value
626 continue;
628 glyph = fntCacheGlyph(fnt, codepoint);
629 if (!glyph)
630 continue;
632 // kerning
633 delta_x = 0;
634 if (use_kerning && previous) {
635 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
636 if (glyph_index) {
637 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
638 delta_x = delta.x >> 6;
640 previous = glyph_index;
644 /*if (width) {
645 if (codepoint == '\n') {
646 x = xori;
647 y += MENU_ITEM_HEIGHT; // hmax is too tight and unordered, generally
648 continue;
651 if ((x + glyph_w > xmax) || (y > ymax)) // stepped over the max
652 break;
655 if (codepoint > 0xFF) {
656 if (!inRTL) {
657 inRTL = 1;
658 pen_xRTL = pen_x;
659 glyphRTL = glyph;
660 startRTL = string + 1;
662 } else if ((codepoint > 96 && codepoint < 123) || (codepoint > 64 && codepoint < 91)) {
663 if (inRTL) { // render RTL
664 inRTL = 0;
665 pen_x = pen_xRTL;
666 fntRenderSubRTL(fnt, startRTL, string, glyphRTL, pen_xRTL, y);
670 if (inRTL) {
671 pen_xRTL += delta_x + (glyph->shx >> 6);
672 } else {
673 pen_x += delta_x;
674 fntRenderGlyph(glyph, pen_x, y);
675 pen_x += glyph->shx >> 6;
680 if (inRTL) {
681 pen_x = pen_xRTL;
682 fntRenderSubRTL(fnt, startRTL, string, glyphRTL, pen_xRTL, y);
685 return pen_x;
687 #endif
689 void fntFitString(int font, unsigned char *string, size_t width) {
690 size_t cw = 0;
691 unsigned char *str = string;
692 size_t spacewidth = fntCalcDimensions(font, " ");
693 unsigned char *psp = NULL;
695 while (*str) {
696 // scan forward to the next whitespace
697 unsigned char *sp = str;
698 for (; *sp && *sp != ' ' && *sp != '\n'; ++sp);
700 // store what was there before
701 unsigned char osp = *sp;
703 // newline resets the situation
704 if (osp == '\n') {
705 cw = 0;
706 str = ++sp;
707 psp = NULL;
708 continue;
711 // terminate after the word
712 *sp = '\0';
714 // Calc the font's width...
715 // NOTE: The word was terminated, so we're seeing a single word
716 // on that position
717 size_t ww = fntCalcDimensions(font, str);
719 if (cw + ww > width) {
720 if (psp) {
721 // we have a prev space to utilise (wrap on it)
722 *psp = '\n';
723 *sp = osp;
724 cw = ww;
725 psp = sp;
726 } else {
727 // no prev. space to hijack, must break after the word
728 // this will mean overflowed text...
729 *sp = '\n';
730 cw = 0;
732 } else {
733 cw += ww;
734 *sp = osp;
735 psp = sp;
738 cw += spacewidth;
739 str = ++sp;
743 int fntCalcDimensions(int font, const unsigned char* str) {
744 int w = 0;
746 WaitSema(gFontSemaId);
747 font_t *fnt = &fonts[font];
748 SignalSema(gFontSemaId);
750 uint32_t codepoint;
751 uint32_t state = UTF8_ACCEPT;
752 FT_Bool use_kerning = FT_HAS_KERNING(fnt->face);
753 FT_UInt glyph_index, previous = 0;
754 FT_Vector delta;
756 // cache glyphs and render as we go
757 for (; *str; ++str) {
758 if (utf8Decode(&state, &codepoint, *str)) // accumulate the codepoint value
759 continue;
761 // Could just as well only get the glyph dimensions
762 // but it is probable the glyphs will be needed anyway
763 fnt_glyph_cache_entry_t* glyph = fntCacheGlyph(fnt, codepoint);
764 if (!glyph)
765 continue;
767 // kerning
768 if (use_kerning && previous) {
769 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
770 if (glyph_index) {
771 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
772 w += delta.x >> 6;
774 previous = glyph_index;
777 w += glyph->shx >> 6;
780 return w;
783 void fntReplace(int id, void* buffer, int bufferSize, int takeover, int asDefault) {
784 font_t *fnt = &fonts[id];
786 font_t ndefault, old;
787 fntResetFontDef(&ndefault);
788 fntLoadSlot(&ndefault, buffer, bufferSize);
789 ndefault.isDefault = asDefault;
791 // copy over the new font definition
792 // we have to lock this phase, as the old font may still be used
793 WaitSema(gFontSemaId);
794 memcpy(&old, fnt, sizeof(font_t));
795 memcpy(fnt, &ndefault, sizeof(font_t));
797 if (takeover)
798 fnt->dataPtr = buffer;
800 SignalSema(gFontSemaId);
802 // delete the old font
803 fntDeleteFont(&old);
806 void fntSetDefault(int id) {
807 font_t *fnt = &fonts[id];
809 // already default
810 if (fnt->isDefault)
811 return;
813 font_t ndefault, old;
814 fntResetFontDef(&ndefault);
815 fntLoadSlot(&ndefault, NULL, -1);
817 // copy over the new font definition
818 // we have to lock this phase, as the old font may still be used
819 // Note: No check for concurrency is done here, which is kinda funky!
820 WaitSema(gFontSemaId);
821 memcpy(&old, fnt, sizeof(font_t));
822 memcpy(fnt, &ndefault, sizeof(font_t));
823 SignalSema(gFontSemaId);
825 // delete the old font
826 fntDeleteFont(&old);