Commits

Anonymous committed b9d4e96

* Added theme support for multiple fonts (default_font, font0, font1... + _font=ID in the components)
* fixed ttf allocation leak (from lang.c)

Comments (0)

Files changed (7)

 
 /// default (built-in) font id
 #define FNT_DEFAULT (0)
+/// Value returned on errors
+#define FNT_ERROR (-1)
 
 /** Initializes the font subsystem */
 void fntInit(void);
 /** Loads a font
   * @param buffer The memory buffer containing the font
   * @param bufferSize Size of the buffer
-  * @return font slot id */
-int fntLoad(void* buffer, int bufferSize);
+  * @param takeover Set to nonzero
+  * @return font slot id (negative value means error happened) */
+int fntLoad(void* buffer, int bufferSize, int takeover);
+
+/** Loads a font from a file path
+  * @param path The path to the font file
+  * @return font slot id (negative value means error happened) */
+int fntLoadFile(char* path);
 
 /** Replaces the given font slot with the defined font */
-void fntReplace(int id, void* buffer, int bufferSize);
+void fntReplace(int id, void* buffer, int bufferSize, int takeover);
 
 /** Reloads the default font into the given font slot */
 void fntSetDefault(int id);
 #include "include/textures.h"
 
 #define MAX_THEMES_FILES 32
+#define THM_MAX_FONTS 16
 
 typedef struct {
 	char* filePath;
 	int width;
 	int height;
 	u64 color;
+        int font;
 } theme_element_t;
 
 typedef struct {
 	int coverBlend_blx, coverBlend_bly, coverBlend_brx, coverBlend_bry;
 
 	GSTEXTURE textures[TEXTURES_COUNT];
+        int fonts[THM_MAX_FONTS]; //!< Storage of font handles for removal once not needed
 
 	void (*drawBackground)();
 	void (*drawAltBackground)();
 #include "include/renderman.h"
 #include "include/ioman.h"
 #include "include/utf8.h"
+#include "include/util.h"
 
 #include <ft2build.h>
 
 
 static int gCharHeight;
 
+static s32 gFontSemaId;
+static ee_sema_t gFontSema;
+
 /** Single entry in the glyph cache */
 typedef struct {
 	int isValid;
 
     /// Nonzero for custom fonts
     int isDefault;
+
+    /// Pointer to data, if allocation takeover was selected (will be freed)
+    void *dataPtr;
 } font_t;
 
+#define FNT_MAX_COUNT (16)
+
 /// Array of font definitions
-static font_t *fonts = NULL;
-/// Last valid font ID
-static int fontMaxID = -1;
+static font_t fonts[FNT_MAX_COUNT];
 
 #define GLYPH_CACHE_PAGE_SIZE 256
 
 	return 1;
 }
 
+static void fntResetFontDef(font_t *fnt) {
+    fnt->glyphCache = NULL;
+    fnt->cacheMaxPageID = -1;
+    fnt->isValid = 0;
+    fnt->isDefault = 0;
+    fnt->dataPtr = NULL;
+}
+
 void fntInit(void) {
 	int error = FT_Init_FreeType( &font_library );
 
 		// SleepThread();
 	}
 
-        fonts = NULL;
-        fontMaxID = -1;
+        gFontSema.init_count = 1;
+        gFontSema.max_count = 1;
+        gFontSema.option = 0;
+        gFontSemaId = CreateSema(&gFontSema);
+
+        int i;
+        for (i = 0; i < FNT_MAX_COUNT; ++i)
+            fntResetFontDef(&fonts[i]);
 
         // load the default font (will be id=0)
-        fntLoad(NULL, -1);
+        fntLoad(NULL, -1, 0);
 }
 
 static void fntCacheFlushPage(fnt_glyph_cache_entry_t *page) {
         fnt->cacheMaxPageID = -1;
 }
 
-static void fntResetFontDef(font_t *fnt) {
-    fnt->glyphCache = NULL;
-    fnt->cacheMaxPageID = -1;
-    fnt->isValid = 0;
-    fnt->isDefault = 0;
-}
-
 static int fntNewFont() {
-    if (!fonts) {
-        fontMaxID = 0;
-        fonts = (font_t*)malloc(sizeof(font_t));
-
-        fntResetFontDef(&fonts[0]);
-
-        return fontMaxID;
+    int i;
+    for (i = 0; i < FNT_MAX_COUNT; ++i) {
+        if (fonts[i].isValid == 0) {
+            fntResetFontDef(&fonts[i]);
+            return i;
+        }
     }
 
-    int i;
-
-    // already some fonts loaded - search for free slot
-    for (i = 0; i <= fontMaxID; ++i) {
-        if (fonts[i].isValid == 0)
-            return i;
-    }
-
-    // have no font slots left, realloc the font array
-    ++fontMaxID;
-    font_t *nf = (font_t*)realloc(fonts, fontMaxID * sizeof(font_t));
-
-    if (!nf)
-        return -1;
-
-    fntResetFontDef(&nf[fontMaxID]);
-    fonts = nf;
-
-    return fontMaxID;
+    return FNT_ERROR;
 }
 
 void fntDeleteFont(font_t *font) {
+    // skip already deleted fonts
+    if (!font->isValid)
+        return;
+
     // free the glyph cache, unload the font
     fntCacheFlush(font);
 
     FT_Done_Face(font->face);
 
+    if (font->dataPtr) {
+        free(font->dataPtr);
+        font->dataPtr = NULL;
+    }
+
     font->isValid = 0;
 }
 
-void fntLoadSlot(font_t *fnt, void* buffer, int bufferSize) {
+int fntLoadSlot(font_t *fnt, void* buffer, int bufferSize) {
 	if (!buffer) {
 		buffer = &freesansfont_raw;
 		bufferSize = size_freesansfont_raw;
 		// just report over the ps2link
 		LOG("Freetype: Font loading failed with %x!\n", error);
 		// SleepThread();
+                return -1;
 	} 
 	
 	gCharHeight = 16;
 		// just report over the ps2link
 		LOG("Freetype: Error setting font pixel size with %x!\n", error);
 		// SleepThread();
+                return -1;
 	}
+
+
+        fnt->isValid = 1;
+        return 0;
 }
 
 
-int fntLoad(void* buffer, int bufferSize) {
+int fntLoad(void* buffer, int bufferSize, int takeover) {
         // we need a new slot in the font array
         int fontID = fntNewFont();
+
+        if (fontID == FNT_ERROR)
+            return FNT_ERROR;
+
         font_t *fnt = &fonts[fontID];
 
-        fntLoadSlot(fnt, buffer, bufferSize);
+        if (fntLoadSlot(fnt, buffer, bufferSize) == FNT_ERROR)
+            return FNT_ERROR;
+
+        if (takeover)
+            fnt->dataPtr = buffer;
 
         return fontID;
 }
 
+int fntLoadFile(char* path) {
+    // load the buffer with font
+    int size = -1;
+    void* customFont = readFile(path, -1, &size);
+
+    if (!customFont)
+        return FNT_ERROR;
+
+    int fontID = fntLoad(customFont, size, 1);
+
+    return fontID;
+}
+
 void fntRelease(int id) {
-    if (id <= fontMaxID)
+    if (id < FNT_MAX_COUNT)
         fntDeleteFont(&fonts[id]);
 }
 
 void fntEnd(void) {
         // release all the fonts
         int id;
-        for (id = 0 ; id < fontMaxID; ++id)
+        for (id = 0 ; id < FNT_MAX_COUNT; ++id)
             fntDeleteFont(&fonts[id]);
 
-        free(fonts);
-        fonts = NULL;
-        fontMaxID = -1;
-
 	// deinit freetype system
 	FT_Done_FreeType( font_library );
+
+        DeleteSema(gFontSemaId);
 }
 
 /** Internal method. Updates texture part of glyph cache entry */
 	glyph->texture.Width = glyph->width;
 	glyph->texture.Height = glyph->height;
 	glyph->texture.PSM = GS_PSM_CT32;
-	glyph->texture.Filter = GS_FILTER_LINEAR;
+        glyph->texture.Filter = GS_FILTER_LINEAR;
 	glyph->texture.Mem = (u32*)glyph->glyphData;
 	glyph->texture.Vram = 0;
 }
 	}
 	
 	// copy the data
-	int i, j = 0;
+        int i = 0;
         int pixelcount = slot->bitmap.width * slot->bitmap.rows;
 	
 	// If we would know how to render grayscale (single channel) it could save memory
-	glyph->glyphData = malloc(pixelcount * 4);
-	
+        glyph->glyphData = memalign(128, pixelcount * 4);
+
+        // incrementing pointers should be cheaper than array indexing...
+        char *src = slot->bitmap.buffer;
+        char *data = glyph->glyphData;
+
 	for (i = 0; i < pixelcount; ++i) {
-                char c = slot->bitmap.buffer[i];
+                char c = *src++;
 		
-		glyph->glyphData[j++] = c;
-		glyph->glyphData[j++] = c;
-		glyph->glyphData[j++] = c;
-		glyph->glyphData[j++] = c;
+                *(data++) = c;
+                *(data++) = c;
+                *(data++) = c;
+                *(data++) = c;
 	}
-	
+
         glyph->width = slot->bitmap.width;
         glyph->height = slot->bitmap.rows;
         glyph->shx = slot->advance.x;
 	// flush cache - it will be invalid after the setting
         int i;
 
-        for (i = 0; i <= fontMaxID; ++i)
-            fntCacheFlush(&fonts[i]);
+        for (i = 0; i < FNT_MAX_COUNT; ++i) {
+            if (fonts[i].isValid)
+                fntCacheFlush(&fonts[i]);
+        }
 	
 	// set new aspect ratio (Is this correct, I wonder?)
 	// TODO: error = FT_Set_Char_Size(face, 0, gCharHeight*64, ah*300, aw*300);
 }
 
 int fntRenderString(int font, int x, int y, short aligned, const unsigned char* string, u64 colour) {
+        // wait for font lock to unlock
+        WaitSema(gFontSemaId);
         font_t *fnt = &fonts[font];
+        SignalSema(gFontSemaId);
 
 	if (aligned) {
 		int w = 0, h = 0;
 	}
 }
 
-void fntReplace(int id, void* buffer, int bufferSize) {
+void fntReplace(int id, void* buffer, int bufferSize, int takeover) {
     font_t *fnt = &fonts[id];
-    fntDeleteFont(fnt);
-    fntLoadSlot(fnt, buffer, bufferSize);
+
+    font_t ndefault, old;
+    fntResetFontDef(&ndefault);
+    fntLoadSlot(&ndefault, buffer, bufferSize);
+
+    // copy over the new font definition
+    // we have to lock this phase, as the old font may still be used
+    WaitSema(gFontSemaId);
+    memcpy(&old, fnt, sizeof(font_t));
+    memcpy(fnt, &ndefault, sizeof(font_t));
+
+    if (takeover)
+        fnt->dataPtr = buffer;
+
+    SignalSema(gFontSemaId);
+
+    // delete the old font
+    fntDeleteFont(&old);
 }
 
 void fntSetDefault(int id) {
     if (fnt->isDefault)
         return;
 
-    fntDeleteFont(fnt);
-    fntLoadSlot(fnt, NULL, -1);
+    font_t ndefault, old;
+    fntResetFontDef(&ndefault);
+    fntLoadSlot(&ndefault, NULL, -1);
+    ndefault.isDefault = 1;
 
-    fnt->isDefault = 1;
+    // copy over the new font definition
+    // we have to lock this phase, as the old font may still be used
+    // Note: No check for concurrency is done here, which is kinda funky!
+    WaitSema(gFontSemaId);
+    memcpy(&old, fnt, sizeof(font_t));
+    memcpy(fnt, &ndefault, sizeof(font_t));
+    SignalSema(gFontSemaId);
+
+    // delete the old font
+    fntDeleteFont(&old);
 }
 	
 	int fade = wfadeout > 0xFF ? 0xFF : wfadeout;
 
-	u64 mycolor = GS_SETREG_RGBA(0x40, 0x40, 0x40, fade >> 1);
+        u64 mycolor = GS_SETREG_RGBA(0x10, 0x10, 0x10, fade >> 1);
 	
 	
 	rmDrawRect(0, 0, ALIGN_NONE, DIM_INF, DIM_INF, mycolor);
 			colour = gTheme->selTextColor;
 		
 		// render, advance
+                // TODO: Theme support for main menu (font)
                 fntRenderString(FNT_DEFAULT, x - (w >> 1), y, ALIGN_NONE, text, colour);
 		
 		y += spacing;
 		rmFlush();
 		
 		// render the old screen, transposed
-		rmSetTransposition(-transition, 0);
+                rmSetTransposition(-transition, 0);
 		screenHandler->renderScreen();
 		
 		// render new screen transposed again
 		
 		void* customFont = readFile(path, -1, &size);
                 if (customFont) {
-                        fntReplace(FNT_DEFAULT, customFont, size);
+                        fntReplace(FNT_DEFAULT, customFont, size, 1);
                 }
                 else {
                         fntSetDefault(FNT_DEFAULT);
 			if (otherTex && otherTex->Mem)
 				rmDrawPixmap(otherTex, gTheme->menuText.posX + gTheme->menuText.width, gTheme->menuText.posY, gTheme->menuText.aligned, DIM_UNDEF, DIM_UNDEF, gDefaultCol);
 		}
-                fntRenderString(FNT_DEFAULT, gTheme->menuText.posX, gTheme->menuText.posY, gTheme->menuText.aligned, GetMenuItemText(selected_item->item), gTheme->menuText.color);
+                fntRenderString(gTheme->menuText.font, gTheme->menuText.posX, gTheme->menuText.posY, gTheme->menuText.aligned, GetMenuItemText(selected_item->item), gTheme->menuText.color);
 	}
 	
 	if (cur) {
 			else
 				color = gTheme->itemsList.color;
 
-                        fntRenderString(FNT_DEFAULT, itemsList->posX + stretchedSize, curpos + others * MENU_ITEM_HEIGHT, ALIGN_NONE, submenuItemGetText(&ps->item), color);
+                        fntRenderString(itemsList->font, itemsList->posX + stretchedSize, curpos + others * MENU_ITEM_HEIGHT, ALIGN_NONE, submenuItemGetText(&ps->item), color);
 		
 			ps = ps->next;
 			others++;
 		if (gTheme->itemText.enabled) {
 			if (selected_item->item->userdata && (cur->item.id != -1)) {
 				item_list_t *support = selected_item->item->userdata;
-                                fntRenderString(FNT_DEFAULT, gTheme->itemText.posX, gTheme->itemText.posY, gTheme->itemText.aligned, support->itemGetStartup(cur->item.id), gTheme->itemText.color);
+                                fntRenderString(gTheme->itemText.font, gTheme->itemText.posX, gTheme->itemText.posY, gTheme->itemText.aligned, support->itemGetStartup(cur->item.id), gTheme->itemText.color);
 			}
 		}
 	}
 				x += someTex->Width + 2;
 			}
 			
-                        x += fntRenderString(FNT_DEFAULT, x, y, ALIGN_NONE, _l(hint->text_id), gTheme->hintText.color);
+                        x += fntRenderString(gTheme->hintText.font, x, y, ALIGN_NONE, _l(hint->text_id), gTheme->hintText.color);
 			x+= 12;
 		}
 	}
 #include "include/renderman.h"
 #include "include/textures.h"
 #include "include/ioman.h"
+#include "include/fntsys.h"
 
 #define MENU_POS_V 50
 #define HINT_HEIGHT 32
 static theme_file_t themes[MAX_THEMES_FILES];
 static char **guiThemesNames = NULL;
 
-
 GSTEXTURE* thmGetTexture(unsigned int id) {
 	if (id >= TEXTURES_COUNT)
 		return NULL;
 static void thmFree(theme_t* theme) {
 	if (theme) {
 		GSTEXTURE* texture;
-		int texId = 0;
-		for(; texId < TEXTURES_COUNT; texId++) {
-			texture = &theme->textures[texId];
+                int id = 0;
+
+                // free textures
+                for(; id < TEXTURES_COUNT; id++) {
+                        texture = &theme->textures[id];
 			if (texture->Mem != NULL) {
 				free(texture->Mem);
 				texture->Mem = NULL;
 			}
 		}
 
+                // free fonts
+                for (id = 0; id < THM_MAX_FONTS; ++id) {
+                    int fntid = theme->fonts[id];
+
+                    if (fntid != FNT_DEFAULT)
+                        fntRelease(fntid);
+                }
+
 		free(theme);
 		theme = NULL;
 	}
 	snprintf(elemProp, 64, "%s_color", elem->name);
 	if (configGetColor(themeConfig, elemProp, charColor))
 		elem->color = GS_SETREG_RGBA(charColor[0], charColor[1], charColor[2], 0xff);
+
+        snprintf(elemProp, 64, "%s_font", elem->name);
+        if (!configGetInt(themeConfig, elemProp, &intValue))
+                intValue = -1;
+
+        if (intValue >= 0 && intValue < THM_MAX_FONTS)
+            elem->font = theme->fonts[intValue];
+        else
+            elem->font = FNT_DEFAULT;
 }
 
 static void setColors(theme_t* theme) {
 	theme->selTextColor = GS_SETREG_RGBA(gDefaultSelTextColor[0], gDefaultSelTextColor[1], gDefaultSelTextColor[2], 0xff);
 }
 
+static void thmLoadFonts(config_set_t* themeConfig, const char* themePath, theme_t* theme) {
+    int fntID; // theme side font id, not the fntSys handle
+    for (fntID = -1; fntID < THM_MAX_FONTS; ++fntID) {
+            // does the font by the key exist?
+            char fntKey[16];
+
+            // -1 is a placeholder for default font...
+            if (fntID >= 0) {
+                // Default font handle...
+                theme->fonts[fntID] = FNT_DEFAULT;
+                snprintf(fntKey, 16, "font%d", fntID);
+            }
+            else {
+                snprintf(fntKey, 16, "default_font");
+            }
+
+            char *fntFile;
+            int cfgKeyOK = configGetStr(themeConfig, fntKey, &fntFile);
+            if (!cfgKeyOK && (fntID >= 0))
+                continue;
+
+            char fullPath[128];
+
+            if (fntID < 0) {
+                // replace the default font
+                if (cfgKeyOK) {
+                        snprintf(fullPath, 128, "%s%s", themePath, fntFile);
+
+                        int size = -1;
+                        void* customFont = readFile(fullPath, -1, &size);
+
+                        if (customFont)
+                            fntReplace(FNT_DEFAULT, customFont, size, 1);
+                }
+                else {
+                        fntSetDefault(FNT_DEFAULT);
+                }
+            } else {
+                snprintf(fullPath, 128, "%s%s", themePath, fntFile);
+                int fntHandle = fntLoadFile(fullPath);
+
+                // Do we have a valid font? Assign the font handle to the theme font slot
+                if (fntHandle != FNT_ERROR)
+                        theme->fonts[fntID] = fntHandle;
+            };
+    };
+}
+
 static void thmLoad(char* themePath) {
 	LOG("thmLoad() path=%s\n", themePath);
 	theme_t* curT = gTheme;
 	if (configGetColor(themeConfig, "sel_text_color", color))
 		newT->selTextColor = GS_SETREG_RGBA(color[0], color[1], color[2], 0xff);
 
+        // before loading the element definitions, we have to have the fonts prepared
+        // for that, we load the fonts and a translation table
+        if (themePath)
+            thmLoadFonts(themeConfig, themePath, newT);
+        else
+            // reset the default font to be sure
+            fntSetDefault(FNT_DEFAULT);
+
 	getElem(themeConfig, newT, &newT->menuIcon, "menu_icon", 1, 40, 40, ALIGN_CENTER, DIM_UNDEF, DIM_UNDEF, 1);
 	getElem(themeConfig, newT, &newT->menuText, "menu_text", 1, screenWidth >> 1, 20, ALIGN_CENTER, 200, 20, 0);
 	getElem(themeConfig, newT, &newT->itemsList, "items_list", 1, 150, MENU_POS_V, ALIGN_NONE, DIM_INF, newT->displayedItems * MENU_ITEM_HEIGHT, 0);