* reverted some changes from rev500, finally crazyc was correct (when he skipped...
[open-ps2-loader.git] / src / fntsys.c
blob769f2cb6963b66703d5a977e848a4c3f77d5bc5f
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 LOG("fntResetFontDef\n");
136 fnt->glyphCache = NULL;
137 fnt->cacheMaxPageID = -1;
138 fnt->isValid = 0;
139 fnt->isDefault = 0;
141 int aid;
142 for(aid = 0; aid < ATLAS_MAX; ++aid)
143 fnt->atlases[aid] = NULL;
145 fnt->dataPtr = NULL;
148 static void fntPrepareCLUT() {
149 fontClut.PSM = GS_PSM_T8;
150 fontClut.ClutPSM = GS_PSM_CT32;
151 fontClut.Clut = memalign(128, 256 * 4);
152 fontClut.VramClut = 0;
154 // generate the clut table
155 size_t i;
156 u32 *clut = fontClut.Clut;
157 for (i = 0; i < 256; ++i) {
158 u8 alpha = i > 0x080 ? 0x080 : i;
160 *clut = alpha << 24 | i << 16 | i << 8 | i;
161 clut++;
165 static void fntDestroyCLUT() {
166 free(fontClut.Clut);
167 fontClut.Clut = NULL;
170 void fntInit(void) {
171 LOG("fntInit\n");
172 int error = FT_Init_FreeType(&font_library);
174 if (error) {
175 // just report over the ps2link
176 LOG("Freetype init failed with %x!\n", error);
177 // SleepThread();
180 fntPrepareCLUT();
182 gFontSema.init_count = 1;
183 gFontSema.max_count = 1;
184 gFontSema.option = 0;
185 gFontSemaId = CreateSema(&gFontSema);
187 int i;
188 for (i = 0; i < FNT_MAX_COUNT; ++i)
189 fntResetFontDef(&fonts[i]);
191 // load the default font (will be id=0)
192 fntLoad(NULL, -1, 0);
195 static void fntCacheFlushPage(fnt_glyph_cache_entry_t *page) {
196 LOG("fntCacheFlushPage\n");
197 int i;
199 for (i = 0; i < GLYPH_CACHE_PAGE_SIZE; ++i, ++page) {
200 page->isValid = 0;
201 // we're not doing any atlasFree or such - atlas has to be rebuild
202 page->allocation = NULL;
203 page->atlas = NULL;
207 static void fntCacheFlush(font_t *fnt) {
208 LOG("fntCacheFlush\n");
210 int i;
212 // Release all the glyphs from the cache
213 for (i = 0; i <= fnt->cacheMaxPageID; ++i) {
214 if (fnt->glyphCache[i]) {
215 fntCacheFlushPage(fnt->glyphCache[i]);
216 free(fnt->glyphCache[i]);
217 fnt->glyphCache[i] = NULL;
221 free(fnt->glyphCache);
222 fnt->glyphCache = NULL;
223 fnt->cacheMaxPageID = -1;
225 // free all atlasses too, they're invalid now anyway
226 int aid;
227 for(aid = 0; aid < ATLAS_MAX; ++aid) {
228 atlasFree(fnt->atlases[aid]);
229 fnt->atlases[aid] = NULL;
233 static int fntNewFont() {
234 LOG("fntNewFont\n");
235 int i;
236 for (i = 0; i < FNT_MAX_COUNT; ++i) {
237 if (fonts[i].isValid == 0) {
238 fntResetFontDef(&fonts[i]);
239 return i;
243 return FNT_ERROR;
246 void fntDeleteFont(font_t *font) {
247 LOG("fntDeleteFont\n");
249 // skip already deleted fonts
250 if (!font->isValid)
251 return;
253 // free the glyph cache, atlases, unload the font
254 fntCacheFlush(font);
256 FT_Done_Face(font->face);
258 if (font->dataPtr) {
259 free(font->dataPtr);
260 font->dataPtr = NULL;
263 font->isValid = 0;
266 int fntLoadSlot(font_t *fnt, void* buffer, int bufferSize) {
267 LOG("fntLoadSlot\n");
269 if (!buffer) {
270 buffer = &freesansfont_raw;
271 bufferSize = size_freesansfont_raw;
272 fnt->isDefault = 1;
275 // load the font via memory handle
276 int error = FT_New_Memory_Face(font_library, (FT_Byte*) buffer, bufferSize, 0, &fnt->face);
278 if (error) {
279 // just report over the ps2link
280 LOG("Freetype: Font loading failed with %x!\n", error);
281 // SleepThread();
282 return -1;
285 gCharHeight = 16;
287 error = FT_Set_Char_Size(fnt->face, 0, gCharHeight * 16, 300, 300);
288 /*error = FT_Set_Pixel_Sizes( face,
289 0, // pixel_width
290 gCharHeight ); // pixel_height*/
292 if (error) {
293 // just report over the ps2link
294 LOG("Freetype: Error setting font pixel size with %x!\n", error);
295 // SleepThread();
296 return -1;
299 fnt->isValid = 1;
300 return 0;
303 int fntLoad(void* buffer, int bufferSize, int takeover) {
304 LOG("fntLoad\n");
306 // we need a new slot in the font array
307 int fontID = fntNewFont();
309 if (fontID == FNT_ERROR)
310 return FNT_ERROR;
312 font_t *fnt = &fonts[fontID];
314 if (fntLoadSlot(fnt, buffer, bufferSize) == FNT_ERROR)
315 return FNT_ERROR;
317 if (takeover)
318 fnt->dataPtr = buffer;
320 return fontID;
323 int fntLoadFile(char* path) {
324 LOG("fntLoadFile\n");
325 // load the buffer with font
326 int size = -1;
327 void* customFont = readFile(path, -1, &size);
329 if (!customFont)
330 return FNT_ERROR;
332 int fontID = fntLoad(customFont, size, 1);
334 return fontID;
337 void fntRelease(int id) {
338 LOG("fntRelease\n");
339 if (id < FNT_MAX_COUNT)
340 fntDeleteFont(&fonts[id]);
343 /** Terminates the font rendering system */
344 void fntEnd(void) {
345 LOG("fntEnd\n");
346 // release all the fonts
347 int id;
348 for (id = 0; id < FNT_MAX_COUNT; ++id)
349 fntDeleteFont(&fonts[id]);
351 // deinit freetype system
352 FT_Done_FreeType(font_library);
354 DeleteSema(gFontSemaId);
356 fntDestroyCLUT();
359 static atlas_t *fntNewAtlas() {
360 atlas_t *atl = atlasNew(ATLAS_WIDTH, ATLAS_HEIGHT, GS_PSM_T8);
362 atl->surface.ClutPSM = GS_PSM_CT32;
363 atl->surface.Clut = (u32*)(&fontClut);
365 return atl;
368 static int fntGlyphAtlasPlace(font_t *fnt, fnt_glyph_cache_entry_t* glyph) {
369 FT_GlyphSlot slot = fnt->face->glyph;
371 //LOG("fntGlyphAtlasPlace: Placing the glyph... %d x %d\n", slot->bitmap.width, slot->bitmap.rows);
373 if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) {
374 // no bitmap glyph, just skip
375 return 1;
378 int aid;
380 for (aid = 0; aid < ATLAS_MAX; ++aid) {
381 //LOG(" * Placing aid %d...\n", aid);
382 atlas_t **atl = &fnt->atlases[aid];
383 if (!*atl) { // atlas slot not yet used
384 //LOG(" * aid %d is new...\n", aid);
385 *atl = fntNewAtlas();
388 glyph->allocation =
389 atlasPlace(*atl, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.buffer);
391 if (glyph->allocation) {
392 //LOG(" * found placement\n", aid);
393 glyph->atlas = *atl;
395 return 1;
399 LOG(" * ! no atlas free\n", aid);
400 return 0;
403 /** Internal method. Makes sure the bitmap data for particular character are pre-rendered to the glyph cache */
404 static fnt_glyph_cache_entry_t* fntCacheGlyph(font_t *fnt, uint32_t gid) {
405 // calc page id and in-page index from glyph id
406 int pageid = gid / GLYPH_CACHE_PAGE_SIZE;
407 int idx = gid % GLYPH_CACHE_PAGE_SIZE;
409 // do not call on every char of every font rendering call
410 if (!GLYPH_PAGE_OK(fnt,pageid))
411 if (!fntPrepareGlyphCachePage(fnt, pageid)) // failed to prepare the page...
412 return NULL;
414 fnt_glyph_cache_entry_t *page = fnt->glyphCache[pageid];
416 /* Should never happen.
417 if (!page) // safeguard
418 return NULL;
421 fnt_glyph_cache_entry_t* glyph = &page[idx];
423 if (glyph->isValid)
424 return glyph;
426 // not cached but valid. Cache
427 if (!fnt->face) {
428 LOG("FNT: Face is NULL!\n");
431 int error = FT_Load_Char(fnt->face, gid, FT_LOAD_RENDER);
433 if (error) {
434 LOG("FNT: Error loading glyph - %d\n", error);
435 return NULL;
438 // find atlas placement for the glyph
439 if (!fntGlyphAtlasPlace(fnt, glyph))
440 return NULL;
442 FT_GlyphSlot slot = fnt->face->glyph;
443 glyph->width = slot->bitmap.width;
444 glyph->height = slot->bitmap.rows;
445 glyph->shx = slot->advance.x;
446 glyph->shy = slot->advance.y;
447 glyph->ox = slot->bitmap_left;
448 glyph->oy = gCharHeight - slot->bitmap_top;
450 glyph->isValid = 1;
452 return glyph;
455 void fntSetAspectRatio(float aw, float ah) {
456 LOG("fntSetAspectRatio\n");
457 // flush cache - it will be invalid after the setting
458 int i;
460 for (i = 0; i < FNT_MAX_COUNT; ++i) {
461 if (fonts[i].isValid)
462 fntCacheFlush(&fonts[i]);
465 // set new aspect ratio (Is this correct, I wonder?)
466 // TODO: error = FT_Set_Char_Size(face, 0, gCharHeight*64, ah*300, aw*300);
469 static void fntRenderGlyph(fnt_glyph_cache_entry_t* glyph, int pen_x, int pen_y) {
470 // only if glyph has atlas placement
471 if (glyph->allocation) {
472 /* TODO: Ineffective on many parts:
473 * 1. Usage of floats for UV - fixed point should suffice (and is used internally by GS for UV)
475 * 2. GS_SETREG_TEX0 for every quad - why? gsKit should only set texture if demanded
476 * We should prepare a special fnt render method that would step over most of the
477 * performance problems under - begining with rmSetupQuad and continuing into gsKit
478 * - this method would handle the preparetion of the quads and GS upload itself,
479 * without the use of prim_quad_texture and rmSetupQuad...
481 * 3. We should use clut to keep the memory allocations sane - we're linear in the 32bit buffers
482 * anyway, no reason to waste like we do!
484 quad.ul.x = pen_x + glyph->ox;
485 quad.br.x = quad.ul.x + glyph->width;
486 quad.ul.y = pen_y + glyph->oy;
487 quad.br.y = quad.ul.y + glyph->height;
489 // UV is our own, custom thing here
490 quad.txt = &glyph->atlas->surface;
491 quad.ul.u = glyph->allocation->x;
492 quad.br.u = quad.ul.u + glyph->width;
493 quad.ul.v = glyph->allocation->y;
494 quad.br.v = quad.ul.v + glyph->height;
496 rmDrawQuad(&quad);
500 #ifndef __RTL
501 int fntRenderString(int font, int x, int y, short aligned, size_t width, size_t height, const unsigned char* string, u64 colour) {
502 // wait for font lock to unlock
503 WaitSema(gFontSemaId);
504 font_t *fnt = &fonts[font];
505 SignalSema(gFontSemaId);
507 if (aligned) {
508 if (width) {
509 x -= min(fntCalcDimensions(font, string), width) >> 1;
510 } else {
511 x -= fntCalcDimensions(font, string) >> 1;
513 y -= MENU_ITEM_HEIGHT >> 1;
516 rmApplyShiftRatio(&y);
517 quad.color = colour;
519 int pen_x = x;
520 int xmax = x + width;
521 int ymax = y + height;
523 use_kerning = FT_HAS_KERNING(fnt->face);
524 state = UTF8_ACCEPT;
525 previous = 0;
527 // Note: We need to change this so that we'll accumulate whole word before doing a layout with it
528 // for now this method breaks on any character - which is a bit ugly
530 // I don't want to do anything complicated though so I'd say
531 // we should instead have a dynamic layout routine that'll replace spaces with newlines as appropriate
532 // because that'll make the code run only once per N frames, not every frame
534 // cache glyphs and render as we go
535 for (; *string; ++string) {
536 if (utf8Decode(&state, &codepoint, *string)) // accumulate the codepoint value
537 continue;
539 glyph = fntCacheGlyph(fnt, codepoint);
540 if (!glyph)
541 continue;
543 // kerning
544 if (use_kerning && previous) {
545 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
546 if (glyph_index) {
547 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
548 pen_x += delta.x >> 6;
550 previous = glyph_index;
553 if (width) {
554 if (codepoint == '\n') {
555 pen_x = x;
556 y += MENU_ITEM_HEIGHT; // hmax is too tight and unordered, generally
557 continue;
560 if (y > ymax) // stepped over the max
561 break;
563 if (pen_x + glyph->width > xmax) {
564 pen_x = xmax + 1; // to be sure no other cahr will be written (even not a smaller one just following)
565 continue;
569 fntRenderGlyph(glyph, pen_x, y);
570 pen_x += glyph->shx >> 6;
573 return pen_x;
576 #else
577 static void fntRenderSubRTL(font_t *fnt, const unsigned char* startRTL, const unsigned char* string, fnt_glyph_cache_entry_t* glyph, int x, int y) {
578 if (glyph) {
579 x -= glyph->shx >> 6;
580 fntRenderGlyph(glyph, x, y);
583 for (; startRTL != string; ++startRTL) {
584 if (utf8Decode(&state, &codepoint, *startRTL))
585 continue;
587 glyph = fntCacheGlyph(fnt, codepoint);
588 if (!glyph)
589 continue;
591 if (use_kerning && previous) {
592 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
593 if (glyph_index) {
594 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
595 x -= delta.x >> 6;
597 previous = glyph_index;
600 x -= glyph->shx >> 6;
601 fntRenderGlyph(glyph, x, y);
605 int fntRenderString(int font, int x, int y, short aligned, size_t width, size_t height, const unsigned char* string, u64 colour) {
606 // wait for font lock to unlock
607 WaitSema(gFontSemaId);
608 font_t *fnt = &fonts[font];
609 SignalSema(gFontSemaId);
611 if (aligned) {
612 if (width) {
613 x -= min(fntCalcDimensions(font, string), width) >> 1;
614 } else {
615 x -= fntCalcDimensions(font, string) >> 1;
617 y -= MENU_ITEM_HEIGHT >> 1;
620 rmApplyShiftRatio(&y);
621 quad.color = colour;
623 int pen_x = x;
624 /*int xmax = x + width;
625 int ymax = y + height;*/
627 use_kerning = FT_HAS_KERNING(fnt->face);
628 state = UTF8_ACCEPT;
629 previous = 0;
631 short inRTL = 0;
632 int delta_x, pen_xRTL = 0;
633 fnt_glyph_cache_entry_t* glyphRTL = NULL;
634 const unsigned char* startRTL = NULL;
636 // cache glyphs and render as we go
637 for (; *string; ++string) {
638 if (utf8Decode(&state, &codepoint, *string)) // accumulate the codepoint value
639 continue;
641 glyph = fntCacheGlyph(fnt, codepoint);
642 if (!glyph)
643 continue;
645 // kerning
646 delta_x = 0;
647 if (use_kerning && previous) {
648 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
649 if (glyph_index) {
650 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
651 delta_x = delta.x >> 6;
653 previous = glyph_index;
657 /*if (width) {
658 if (codepoint == '\n') {
659 x = xori;
660 y += MENU_ITEM_HEIGHT; // hmax is too tight and unordered, generally
661 continue;
664 if ((x + glyph_w > xmax) || (y > ymax)) // stepped over the max
665 break;
668 if (codepoint > 0xFF) {
669 if (!inRTL) {
670 inRTL = 1;
671 pen_xRTL = pen_x;
672 glyphRTL = glyph;
673 startRTL = string + 1;
675 } else if ((codepoint > 96 && codepoint < 123) || (codepoint > 64 && codepoint < 91)) {
676 if (inRTL) { // render RTL
677 inRTL = 0;
678 pen_x = pen_xRTL;
679 fntRenderSubRTL(fnt, startRTL, string, glyphRTL, pen_xRTL, y);
683 if (inRTL) {
684 pen_xRTL += delta_x + (glyph->shx >> 6);
685 } else {
686 pen_x += delta_x;
687 fntRenderGlyph(glyph, pen_x, y);
688 pen_x += glyph->shx >> 6;
693 if (inRTL) {
694 pen_x = pen_xRTL;
695 fntRenderSubRTL(fnt, startRTL, string, glyphRTL, pen_xRTL, y);
698 return pen_x;
700 #endif
702 void fntFitString(int font, unsigned char *string, size_t width) {
703 size_t cw = 0;
704 unsigned char *str = string;
705 size_t spacewidth = fntCalcDimensions(font, " ");
706 unsigned char *psp = NULL;
708 while (*str) {
709 // scan forward to the next whitespace
710 unsigned char *sp = str;
711 for (; *sp && *sp != ' ' && *sp != '\n'; ++sp);
713 // store what was there before
714 unsigned char osp = *sp;
716 // newline resets the situation
717 if (osp == '\n') {
718 cw = 0;
719 str = ++sp;
720 psp = NULL;
721 continue;
724 // terminate after the word
725 *sp = '\0';
727 // Calc the font's width...
728 // NOTE: The word was terminated, so we're seeing a single word
729 // on that position
730 size_t ww = fntCalcDimensions(font, str);
732 if (cw + ww > width) {
733 if (psp) {
734 // we have a prev space to utilise (wrap on it)
735 *psp = '\n';
736 *sp = osp;
737 cw = ww;
738 psp = sp;
739 } else {
740 // no prev. space to hijack, must break after the word
741 // this will mean overflowed text...
742 *sp = '\n';
743 cw = 0;
745 } else {
746 cw += ww;
747 *sp = osp;
748 psp = sp;
751 cw += spacewidth;
752 str = ++sp;
756 int fntCalcDimensions(int font, const unsigned char* str) {
757 int w = 0;
759 WaitSema(gFontSemaId);
760 font_t *fnt = &fonts[font];
761 SignalSema(gFontSemaId);
763 uint32_t codepoint;
764 uint32_t state = UTF8_ACCEPT;
765 FT_Bool use_kerning = FT_HAS_KERNING(fnt->face);
766 FT_UInt glyph_index, previous = 0;
767 FT_Vector delta;
769 // cache glyphs and render as we go
770 for (; *str; ++str) {
771 if (utf8Decode(&state, &codepoint, *str)) // accumulate the codepoint value
772 continue;
774 // Could just as well only get the glyph dimensions
775 // but it is probable the glyphs will be needed anyway
776 fnt_glyph_cache_entry_t* glyph = fntCacheGlyph(fnt, codepoint);
777 if (!glyph)
778 continue;
780 // kerning
781 if (use_kerning && previous) {
782 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
783 if (glyph_index) {
784 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
785 w += delta.x >> 6;
787 previous = glyph_index;
790 w += glyph->shx >> 6;
793 return w;
796 void fntReplace(int id, void* buffer, int bufferSize, int takeover, int asDefault) {
797 font_t *fnt = &fonts[id];
799 font_t ndefault, old;
800 fntResetFontDef(&ndefault);
801 fntLoadSlot(&ndefault, buffer, bufferSize);
802 ndefault.isDefault = asDefault;
804 // copy over the new font definition
805 // we have to lock this phase, as the old font may still be used
806 WaitSema(gFontSemaId);
807 memcpy(&old, fnt, sizeof(font_t));
808 memcpy(fnt, &ndefault, sizeof(font_t));
810 if (takeover)
811 fnt->dataPtr = buffer;
813 SignalSema(gFontSemaId);
815 // delete the old font
816 fntDeleteFont(&old);
819 void fntSetDefault(int id) {
820 font_t *fnt = &fonts[id];
822 // already default
823 if (fnt->isDefault)
824 return;
826 font_t ndefault, old;
827 fntResetFontDef(&ndefault);
828 fntLoadSlot(&ndefault, NULL, -1);
830 // copy over the new font definition
831 // we have to lock this phase, as the old font may still be used
832 // Note: No check for concurrency is done here, which is kinda funky!
833 WaitSema(gFontSemaId);
834 memcpy(&old, fnt, sizeof(font_t));
835 memcpy(fnt, &ndefault, sizeof(font_t));
836 SignalSema(gFontSemaId);
838 // delete the old font
839 fntDeleteFont(&old);