Fix for space and punctuation characters. These one are weak/neutral and should not...
[open-ps2-loader.git] / src / fntsys.c
blob7789dc7242d70a6b201d11981239976349ed86ba
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;
34 static int screenWidth;
35 static int screenHeight;
37 static s32 gFontSemaId;
38 static ee_sema_t gFontSema;
40 static GSCLUT fontClut;
42 /** Single entry in the glyph cache */
43 typedef struct {
44 int isValid;
45 // size in pixels of the glyph
46 int width, height;
47 // offsetting of the glyph
48 int ox, oy;
49 // advancements in pixels after rendering this glyph
50 int shx, shy;
52 // atlas for which the allocation was done
53 atlas_t* atlas;
55 // atlas allocation position
56 struct atlas_allocation_t *allocation;
58 } fnt_glyph_cache_entry_t;
60 /** A whole font definition */
61 typedef struct {
62 /** GLYPH CACHE. Every glyph in the ASCII range is cached when first used
63 * this means no additional memory aside from the one needed to render the
64 * character set is used.
66 fnt_glyph_cache_entry_t **glyphCache;
68 /// Maximal font cache page index
69 int cacheMaxPageID;
71 /// Font face
72 FT_Face face;
74 /// Nonzero if font is used
75 int isValid;
77 /// Nonzero for custom fonts
78 int isDefault;
80 /// Texture atlases (default to NULL)
81 atlas_t *atlases[ATLAS_MAX];
83 /// Pointer to data, if allocation takeover was selected (will be freed)
84 void *dataPtr;
85 } font_t;
87 #define FNT_MAX_COUNT (16)
89 /// Array of font definitions
90 static font_t fonts[FNT_MAX_COUNT];
92 static rm_quad_t quad;
93 static uint32_t codepoint, state;
94 static fnt_glyph_cache_entry_t* glyph;
95 static FT_Bool use_kerning;
96 static FT_UInt glyph_index, previous;
97 static FT_Vector delta;
99 #define GLYPH_CACHE_PAGE_SIZE 256
101 #define GLYPH_PAGE_OK(font,page) ((pageid <= font->cacheMaxPageID) && (font->glyphCache[page]))
103 void fntReloadScreenExtents() {
104 rmGetScreenExtents(&screenWidth, &screenHeight);
107 static int fntPrepareGlyphCachePage(font_t *font, int pageid) {
108 if (pageid > font->cacheMaxPageID) {
109 fnt_glyph_cache_entry_t **np = realloc(font->glyphCache, (pageid + 1) * sizeof(fnt_glyph_cache_entry_t *));
111 if (!np)
112 return 0;
114 font->glyphCache = np;
116 unsigned int page;
117 for (page = font->cacheMaxPageID + 1; page <= pageid; ++page)
118 font->glyphCache[page] = NULL;
120 font->cacheMaxPageID = pageid;
123 // if it already was allocated, skip this
124 if (font->glyphCache[pageid])
125 return 1;
127 // allocate the page
128 font->glyphCache[pageid] = malloc(sizeof(fnt_glyph_cache_entry_t) * GLYPH_CACHE_PAGE_SIZE);
130 int i;
131 for (i = 0; i < GLYPH_CACHE_PAGE_SIZE; ++i) {
132 font->glyphCache[pageid][i].isValid = 0;
133 font->glyphCache[pageid][i].atlas = NULL;
134 font->glyphCache[pageid][i].allocation = NULL;
137 return 1;
140 static void fntResetFontDef(font_t *fnt) {
141 LOG("fntResetFontDef\n");
142 fnt->glyphCache = NULL;
143 fnt->cacheMaxPageID = -1;
144 fnt->isValid = 0;
145 fnt->isDefault = 0;
147 int aid;
148 for(aid = 0; aid < ATLAS_MAX; ++aid)
149 fnt->atlases[aid] = NULL;
151 fnt->dataPtr = NULL;
154 static void fntPrepareCLUT() {
155 fontClut.PSM = GS_PSM_T8;
156 fontClut.ClutPSM = GS_PSM_CT32;
157 fontClut.Clut = memalign(128, 256 * 4);
158 fontClut.VramClut = 0;
160 // generate the clut table
161 size_t i;
162 u32 *clut = fontClut.Clut;
163 for (i = 0; i < 256; ++i) {
164 u8 alpha = i > 0x080 ? 0x080 : i;
166 *clut = alpha << 24 | i << 16 | i << 8 | i;
167 clut++;
171 static void fntDestroyCLUT() {
172 free(fontClut.Clut);
173 fontClut.Clut = NULL;
176 void fntInit(void) {
177 LOG("fntInit\n");
178 int error = FT_Init_FreeType(&font_library);
180 if (error) {
181 // just report over the ps2link
182 LOG("Freetype init failed with %x!\n", error);
183 // SleepThread();
186 fntPrepareCLUT();
188 gFontSema.init_count = 1;
189 gFontSema.max_count = 1;
190 gFontSema.option = 0;
191 gFontSemaId = CreateSema(&gFontSema);
193 fntReloadScreenExtents();
195 int i;
196 for (i = 0; i < FNT_MAX_COUNT; ++i)
197 fntResetFontDef(&fonts[i]);
199 // load the default font (will be id=0)
200 fntLoad(NULL, -1, 0);
203 static void fntCacheFlushPage(fnt_glyph_cache_entry_t *page) {
204 LOG("fntCacheFlushPage\n");
205 int i;
207 for (i = 0; i < GLYPH_CACHE_PAGE_SIZE; ++i, ++page) {
208 page->isValid = 0;
209 // we're not doing any atlasFree or such - atlas has to be rebuild
210 page->allocation = NULL;
211 page->atlas = NULL;
215 static void fntCacheFlush(font_t *fnt) {
216 LOG("fntCacheFlush\n");
218 int i;
220 // Release all the glyphs from the cache
221 for (i = 0; i <= fnt->cacheMaxPageID; ++i) {
222 if (fnt->glyphCache[i]) {
223 fntCacheFlushPage(fnt->glyphCache[i]);
224 free(fnt->glyphCache[i]);
225 fnt->glyphCache[i] = NULL;
229 free(fnt->glyphCache);
230 fnt->glyphCache = NULL;
231 fnt->cacheMaxPageID = -1;
233 // free all atlasses too, they're invalid now anyway
234 int aid;
235 for(aid = 0; aid < ATLAS_MAX; ++aid) {
236 atlasFree(fnt->atlases[aid]);
237 fnt->atlases[aid] = NULL;
241 static int fntNewFont() {
242 LOG("fntNewFont\n");
243 int i;
244 for (i = 0; i < FNT_MAX_COUNT; ++i) {
245 if (fonts[i].isValid == 0) {
246 fntResetFontDef(&fonts[i]);
247 return i;
251 return FNT_ERROR;
254 void fntDeleteFont(font_t *font) {
255 LOG("fntDeleteFont\n");
257 // skip already deleted fonts
258 if (!font->isValid)
259 return;
261 // free the glyph cache, atlases, unload the font
262 fntCacheFlush(font);
264 FT_Done_Face(font->face);
266 if (font->dataPtr) {
267 free(font->dataPtr);
268 font->dataPtr = NULL;
271 font->isValid = 0;
274 int fntLoadSlot(font_t *fnt, void* buffer, int bufferSize) {
275 LOG("fntLoadSlot\n");
277 if (!buffer) {
278 buffer = &freesansfont_raw;
279 bufferSize = size_freesansfont_raw;
280 fnt->isDefault = 1;
283 // load the font via memory handle
284 int error = FT_New_Memory_Face(font_library, (FT_Byte*) buffer, bufferSize, 0, &fnt->face);
286 if (error) {
287 // just report over the ps2link
288 LOG("Freetype: Font loading failed with %x!\n", error);
289 // SleepThread();
290 return -1;
293 gCharHeight = 16;
295 error = FT_Set_Char_Size(fnt->face, 0, gCharHeight * 16, 300, 300);
296 /*error = FT_Set_Pixel_Sizes( face,
297 0, // pixel_width
298 gCharHeight ); // pixel_height*/
300 if (error) {
301 // just report over the ps2link
302 LOG("Freetype: Error setting font pixel size with %x!\n", error);
303 // SleepThread();
304 return -1;
307 fnt->isValid = 1;
308 return 0;
311 int fntLoad(void* buffer, int bufferSize, int takeover) {
312 LOG("fntLoad\n");
314 // we need a new slot in the font array
315 int fontID = fntNewFont();
317 if (fontID == FNT_ERROR)
318 return FNT_ERROR;
320 font_t *fnt = &fonts[fontID];
322 if (fntLoadSlot(fnt, buffer, bufferSize) == FNT_ERROR)
323 return FNT_ERROR;
325 if (takeover)
326 fnt->dataPtr = buffer;
328 return fontID;
331 int fntLoadFile(char* path) {
332 LOG("fntLoadFile\n");
333 // load the buffer with font
334 int size = -1;
335 void* customFont = readFile(path, -1, &size);
337 if (!customFont)
338 return FNT_ERROR;
340 int fontID = fntLoad(customFont, size, 1);
342 return fontID;
345 void fntRelease(int id) {
346 LOG("fntRelease\n");
347 if (id < FNT_MAX_COUNT)
348 fntDeleteFont(&fonts[id]);
351 /** Terminates the font rendering system */
352 void fntEnd(void) {
353 LOG("fntEnd\n");
354 // release all the fonts
355 int id;
356 for (id = 0; id < FNT_MAX_COUNT; ++id)
357 fntDeleteFont(&fonts[id]);
359 // deinit freetype system
360 FT_Done_FreeType(font_library);
362 DeleteSema(gFontSemaId);
364 fntDestroyCLUT();
367 static atlas_t *fntNewAtlas() {
368 atlas_t *atl = atlasNew(ATLAS_WIDTH, ATLAS_HEIGHT, GS_PSM_T8);
370 atl->surface.ClutPSM = GS_PSM_CT32;
371 atl->surface.Clut = (u32*)(&fontClut);
373 return atl;
376 static int fntGlyphAtlasPlace(font_t *fnt, fnt_glyph_cache_entry_t* glyph) {
377 FT_GlyphSlot slot = fnt->face->glyph;
379 LOG("fntGlyphAtlasPlace: Placing the glyph... %d x %d\n", slot->bitmap.width, slot->bitmap.rows);
381 if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) {
382 // no bitmap glyph, just skip
383 return 1;
386 int aid;
388 for (aid = 0; aid < ATLAS_MAX; ++aid) {
389 LOG(" * Placing aid %d...\n", aid);
390 atlas_t **atl = &fnt->atlases[aid];
391 if (!*atl) { // atlas slot not yet used
392 LOG(" * aid %d is new...\n", aid);
393 *atl = fntNewAtlas();
396 glyph->allocation =
397 atlasPlace(*atl, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.buffer);
399 if (glyph->allocation) {
400 LOG(" * found placement\n", aid);
401 glyph->atlas = *atl;
403 return 1;
407 LOG(" * ! no atlas free\n", aid);
408 return 0;
411 /** Internal method. Makes sure the bitmap data for particular character are pre-rendered to the glyph cache */
412 static fnt_glyph_cache_entry_t* fntCacheGlyph(font_t *fnt, uint32_t gid) {
413 // calc page id and in-page index from glyph id
414 int pageid = gid / GLYPH_CACHE_PAGE_SIZE;
415 int idx = gid % GLYPH_CACHE_PAGE_SIZE;
417 // do not call on every char of every font rendering call
418 if (!GLYPH_PAGE_OK(fnt,pageid))
419 if (!fntPrepareGlyphCachePage(fnt, pageid)) // failed to prepare the page...
420 return NULL;
422 fnt_glyph_cache_entry_t *page = fnt->glyphCache[pageid];
424 /* Should never happen.
425 if (!page) // safeguard
426 return NULL;
429 fnt_glyph_cache_entry_t* glyph = &page[idx];
431 if (glyph->isValid)
432 return glyph;
434 // not cached but valid. Cache
435 if (!fnt->face) {
436 LOG("FNT: Face is NULL!\n");
439 int error = FT_Load_Char(fnt->face, gid, FT_LOAD_RENDER);
441 if (error) {
442 LOG("FNT: Error loading glyph - %d\n", error);
443 return NULL;
446 // find atlas placement for the glyph
447 if (!fntGlyphAtlasPlace(fnt, glyph))
448 return NULL;
450 FT_GlyphSlot slot = fnt->face->glyph;
451 glyph->width = slot->bitmap.width;
452 glyph->height = slot->bitmap.rows;
453 glyph->shx = slot->advance.x;
454 glyph->shy = slot->advance.y;
455 glyph->ox = slot->bitmap_left;
456 glyph->oy = gCharHeight - slot->bitmap_top;
458 glyph->isValid = 1;
460 return glyph;
463 void fntSetAspectRatio(float aw, float ah) {
464 LOG("fntSetAspectRatio\n");
465 // flush cache - it will be invalid after the setting
466 int i;
468 for (i = 0; i < FNT_MAX_COUNT; ++i) {
469 if (fonts[i].isValid)
470 fntCacheFlush(&fonts[i]);
473 // set new aspect ratio (Is this correct, I wonder?)
474 // TODO: error = FT_Set_Char_Size(face, 0, gCharHeight*64, ah*300, aw*300);
477 static void fntRenderGlyph(fnt_glyph_cache_entry_t* glyph, int pen_x, int pen_y) {
478 // only if glyph has atlas placement
479 if (glyph->allocation) {
480 /* TODO: Ineffective on many parts:
481 * 1. Usage of floats for UV - fixed point should suffice (and is used internally by GS for UV)
483 * 2. GS_SETREG_TEX0 for every quad - why? gsKit should only set texture if demanded
484 * We should prepare a special fnt render method that would step over most of the
485 * performance problems under - begining with rmSetupQuad and continuing into gsKit
486 * - this method would handle the preparetion of the quads and GS upload itself,
487 * without the use of prim_quad_texture and rmSetupQuad...
489 * 3. We should use clut to keep the memory allocations sane - we're linear in the 32bit buffers
490 * anyway, no reason to waste like we do!
492 quad.ul.x = pen_x + glyph->ox;
493 quad.br.x = quad.ul.x + glyph->width;
494 quad.ul.y = pen_y + glyph->oy;
495 quad.br.y = quad.ul.y + glyph->height;
497 // UV is our own, custom thing here
498 quad.txt = &glyph->atlas->surface;
499 quad.ul.u = glyph->allocation->x;
500 quad.br.u = quad.ul.u + glyph->width;
501 quad.ul.v = glyph->allocation->y;
502 quad.br.v = quad.ul.v + glyph->height;
504 rmDrawQuad(&quad);
508 #ifndef __RTL
509 int fntRenderString(int font, int x, int y, short aligned, size_t width, size_t height, const unsigned char* string, u64 colour) {
510 // wait for font lock to unlock
511 WaitSema(gFontSemaId);
512 font_t *fnt = &fonts[font];
513 SignalSema(gFontSemaId);
515 if (aligned) {
516 if (width) {
517 x -= min(fntCalcDimensions(font, string), width) >> 1;
518 } else {
519 x -= fntCalcDimensions(font, string) >> 1;
521 y -= MENU_ITEM_HEIGHT >> 1;
524 rmApplyShiftRatio(&y);
525 quad.color = colour;
527 int pen_x = x;
528 int xmax = x + width;
529 int ymax = y + height;
531 use_kerning = FT_HAS_KERNING(fnt->face);
532 state = UTF8_ACCEPT;
533 previous = 0;
535 // Note: We need to change this so that we'll accumulate whole word before doing a layout with it
536 // for now this method breaks on any character - which is a bit ugly
538 // I don't want to do anything complicated though so I'd say
539 // we should instead have a dynamic layout routine that'll replace spaces with newlines as appropriate
540 // because that'll make the code run only once per N frames, not every frame
542 // cache glyphs and render as we go
543 for (; *string; ++string) {
544 if (utf8Decode(&state, &codepoint, *string)) // accumulate the codepoint value
545 continue;
547 glyph = fntCacheGlyph(fnt, codepoint);
548 if (!glyph)
549 continue;
551 // kerning
552 if (use_kerning && previous) {
553 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
554 if (glyph_index) {
555 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
556 pen_x += delta.x >> 6;
558 previous = glyph_index;
561 if (width) {
562 if (codepoint == '\n') {
563 pen_x = x;
564 y += MENU_ITEM_HEIGHT; // hmax is too tight and unordered, generally
565 continue;
568 if (y > ymax) // stepped over the max
569 break;
571 if (pen_x + glyph->width > xmax) {
572 pen_x = xmax + 1; // to be sure no other cahr will be written (even not a smaller one just following)
573 continue;
577 fntRenderGlyph(glyph, pen_x, y);
578 pen_x += glyph->shx >> 6;
581 return pen_x;
584 #else
585 static void fntRenderSubRTL(font_t *fnt, const unsigned char* startRTL, const unsigned char* string, fnt_glyph_cache_entry_t* glyph, int x, int y) {
586 if (glyph) {
587 x -= glyph->shx >> 6;
588 fntRenderGlyph(glyph, x, y);
591 for (; startRTL != string; ++startRTL) {
592 if (utf8Decode(&state, &codepoint, *startRTL))
593 continue;
595 glyph = fntCacheGlyph(fnt, codepoint);
596 if (!glyph)
597 continue;
599 if (use_kerning && previous) {
600 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
601 if (glyph_index) {
602 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
603 x -= delta.x >> 6;
605 previous = glyph_index;
608 x -= glyph->shx >> 6;
609 fntRenderGlyph(glyph, x, y);
613 int fntRenderString(int font, int x, int y, short aligned, size_t width, size_t height, const unsigned char* string, u64 colour) {
614 // wait for font lock to unlock
615 WaitSema(gFontSemaId);
616 font_t *fnt = &fonts[font];
617 SignalSema(gFontSemaId);
619 if (aligned) {
620 if (width) {
621 x -= min(fntCalcDimensions(font, string), width) >> 1;
622 } else {
623 x -= fntCalcDimensions(font, string) >> 1;
625 y -= MENU_ITEM_HEIGHT >> 1;
628 rmApplyShiftRatio(&y);
629 quad.color = colour;
631 int pen_x = x;
632 /*int xmax = x + width;
633 int ymax = y + height;*/
635 use_kerning = FT_HAS_KERNING(fnt->face);
636 state = UTF8_ACCEPT;
637 previous = 0;
639 short inRTL = 0;
640 int delta_x, pen_xRTL = 0;
641 fnt_glyph_cache_entry_t* glyphRTL = NULL;
642 const unsigned char* startRTL = NULL;
644 // cache glyphs and render as we go
645 for (; *string; ++string) {
646 if (utf8Decode(&state, &codepoint, *string)) // accumulate the codepoint value
647 continue;
649 glyph = fntCacheGlyph(fnt, codepoint);
650 if (!glyph)
651 continue;
653 // kerning
654 delta_x = 0;
655 if (use_kerning && previous) {
656 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
657 if (glyph_index) {
658 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
659 delta_x = delta.x >> 6;
661 previous = glyph_index;
665 /*if (width) {
666 if (codepoint == '\n') {
667 x = xori;
668 y += MENU_ITEM_HEIGHT; // hmax is too tight and unordered, generally
669 continue;
672 if ((x + glyph_w > xmax) || (y > ymax)) // stepped over the max
673 break;
676 if (codepoint > 0xFF) {
677 if (!inRTL) {
678 inRTL = 1;
679 pen_xRTL = pen_x;
680 glyphRTL = glyph;
681 startRTL = string + 1;
683 } else if ((codepoint > 96 && codepoint < 123) || (codepoint > 64 && codepoint < 91)) {
684 if (inRTL) { // render RTL
685 inRTL = 0;
686 pen_x = pen_xRTL;
687 fntRenderSubRTL(fnt, startRTL, string, glyphRTL, pen_xRTL, y);
691 if (inRTL) {
692 pen_xRTL += delta_x + (glyph->shx >> 6);
693 } else {
694 pen_x += delta_x;
695 fntRenderGlyph(glyph, pen_x, y);
696 pen_x += glyph->shx >> 6;
701 if (inRTL) {
702 pen_x = pen_xRTL;
703 fntRenderSubRTL(fnt, startRTL, string, glyphRTL, pen_xRTL, y);
706 return pen_x;
708 #endif
710 void fntFitString(int font, unsigned char *string, size_t width) {
711 size_t cw = 0;
712 unsigned char *str = string;
713 size_t spacewidth = fntCalcDimensions(font, " ");
714 unsigned char *psp = NULL;
716 while (*str) {
717 // scan forward to the next whitespace
718 unsigned char *sp = str;
719 for (; *sp && *sp != ' ' && *sp != '\n'; ++sp);
721 // store what was there before
722 unsigned char osp = *sp;
724 // newline resets the situation
725 if (osp == '\n') {
726 cw = 0;
727 str = ++sp;
728 psp = NULL;
729 continue;
732 // terminate after the word
733 *sp = '\0';
735 // Calc the font's width...
736 // NOTE: The word was terminated, so we're seeing a single word
737 // on that position
738 size_t ww = fntCalcDimensions(font, str);
740 if (cw + ww > width) {
741 if (psp) {
742 // we have a prev space to utilise (wrap on it)
743 *psp = '\n';
744 *sp = osp;
745 cw = ww;
746 psp = sp;
747 } else {
748 // no prev. space to hijack, must break after the word
749 // this will mean overflowed text...
750 *sp = '\n';
751 cw = 0;
753 } else {
754 cw += ww;
755 *sp = osp;
756 psp = sp;
759 cw += spacewidth;
760 str = ++sp;
764 int fntCalcDimensions(int font, const unsigned char* str) {
765 int w = 0;
767 WaitSema(gFontSemaId);
768 font_t *fnt = &fonts[font];
769 SignalSema(gFontSemaId);
771 uint32_t codepoint;
772 uint32_t state = UTF8_ACCEPT;
773 FT_Bool use_kerning = FT_HAS_KERNING(fnt->face);
774 FT_UInt glyph_index, previous = 0;
775 FT_Vector delta;
777 // cache glyphs and render as we go
778 for (; *str; ++str) {
779 if (utf8Decode(&state, &codepoint, *str)) // accumulate the codepoint value
780 continue;
782 // Could just as well only get the glyph dimensions
783 // but it is probable the glyphs will be needed anyway
784 fnt_glyph_cache_entry_t* glyph = fntCacheGlyph(fnt, codepoint);
785 if (!glyph)
786 continue;
788 // kerning
789 if (use_kerning && previous) {
790 glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
791 if (glyph_index) {
792 FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
793 w += delta.x >> 6;
795 previous = glyph_index;
798 w += glyph->shx >> 6;
801 return w;
804 void fntReplace(int id, void* buffer, int bufferSize, int takeover, int asDefault) {
805 font_t *fnt = &fonts[id];
807 font_t ndefault, old;
808 fntResetFontDef(&ndefault);
809 fntLoadSlot(&ndefault, buffer, bufferSize);
810 ndefault.isDefault = asDefault;
812 // copy over the new font definition
813 // we have to lock this phase, as the old font may still be used
814 WaitSema(gFontSemaId);
815 memcpy(&old, fnt, sizeof(font_t));
816 memcpy(fnt, &ndefault, sizeof(font_t));
818 if (takeover)
819 fnt->dataPtr = buffer;
821 SignalSema(gFontSemaId);
823 // delete the old font
824 fntDeleteFont(&old);
827 void fntSetDefault(int id) {
828 font_t *fnt = &fonts[id];
830 // already default
831 if (fnt->isDefault)
832 return;
834 font_t ndefault, old;
835 fntResetFontDef(&ndefault);
836 fntLoadSlot(&ndefault, NULL, -1);
838 // copy over the new font definition
839 // we have to lock this phase, as the old font may still be used
840 // Note: No check for concurrency is done here, which is kinda funky!
841 WaitSema(gFontSemaId);
842 memcpy(&old, fnt, sizeof(font_t));
843 memcpy(fnt, &ndefault, sizeof(font_t));
844 SignalSema(gFontSemaId);
846 // delete the old font
847 fntDeleteFont(&old);