From ae02591c9d6219ba946542d0bfb434af1d9c8cf2 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 20 Oct 2011 21:53:33 -0400
Subject: [PATCH] Added font and string caching to Mac_FontServ This works very
well for Maelstrom since there is very little dynamic text.
---
controls.cpp | 6 +--
init.cpp | 2 +-
maclib/Mac_FontServ.cpp | 91 +++++++++++++++++++++++++++++++++++------
maclib/Mac_FontServ.h | 17 ++++++--
main.cpp | 74 ++++++++++++++-------------------
netlogic/about.cpp | 2 +-
netlogic/game.cpp | 9 ++--
scores.cpp | 4 +-
8 files changed, 135 insertions(+), 70 deletions(-)
diff --git a/controls.cpp b/controls.cpp
index 991f6ff0..9793e4b5 100644
--- a/controls.cpp
+++ b/controls.cpp
@@ -260,7 +260,7 @@ void ConfigureControls(void)
}
if ( (splash = Load_Title(screen, 100)) == NULL ) {
error("Can't load configuration splash!\n");
- delete chicago;
+ fontserv->FreeFont(chicago);
return;
}
X=(SCREEN_WIDTH-CTL_DIALOG_WIDTH)/2;
@@ -322,7 +322,7 @@ void ConfigureControls(void)
for ( i=0; i<NUM_CTLS; ++i ) {
fontserv->FreeText(keynames[i]);
}
- delete chicago;
+ fontserv->FreeFont(chicago);
delete dialog;
if ( valid ) {
memcpy(&controls, &newcontrols, sizeof(controls));
@@ -546,7 +546,7 @@ void ShowDawn(void)
screen->FreeImage(splash);
for ( i=0; i<6; ++i )
fontserv->FreeText(text[i]);
- delete chicago;
+ fontserv->FreeFont(chicago);
delete dialog;
return;
}
diff --git a/init.cpp b/init.cpp
index bd100b5e..69b1073b 100644
--- a/init.cpp
+++ b/init.cpp
@@ -121,7 +121,7 @@ void DoIntroScreen(void)
Yoff+20-text->h/2, text, NOCLIP);
fontserv->FreeText(text);
}
- delete geneva;
+ fontserv->FreeFont(geneva);
screen->Update();
} // -- DoIntroScreen
diff --git a/maclib/Mac_FontServ.cpp b/maclib/Mac_FontServ.cpp
index 31d48dbc..7cc7c798 100644
--- a/maclib/Mac_FontServ.cpp
+++ b/maclib/Mac_FontServ.cpp
@@ -31,6 +31,7 @@
#include "SDL_types.h"
#include "bitesex.h"
+#include "hashtable.h"
#include "Mac_FontServ.h"
#define copy_short(S, D) memcpy(&S, D, 2); D += 2;
@@ -71,12 +72,22 @@ struct FOND {
/* The Kerning Table */
};
+static void
+hash_nuke_string_texture(const void *key, const void *value, void *data)
+{
+ FrameBuf *screen = (FrameBuf *)data;
+
+ delete[] (char*)key;
+ screen->FreeImage((SDL_Texture *)value);
+}
FontServ:: FontServ(FrameBuf *_screen, const char *fontfile)
{
screen = _screen;
fontres = new Mac_Resource(fontfile);
- text_allocated = 0;
+ fonts = NULL;
+ strings = hash_create(screen, hash_hash_string, hash_keymatch_string, hash_nuke_string_texture);
+
if ( fontres->Error() ) {
SetError("Couldn't load resources from %s", fontfile);
return;
@@ -90,11 +101,13 @@ FontServ:: FontServ(FrameBuf *_screen, const char *fontfile)
FontServ:: ~FontServ()
{
- if ( text_allocated != 0 ) {
- fprintf(stderr,
- "FontServ: Warning: %d text surfaces extant\n",
- text_allocated);
+ while (fonts) {
+ MFont *next = fonts->next;
+ delete fonts;
+ fonts = next;
}
+ hash_destroy(strings);
+
delete fontres;
}
@@ -109,8 +122,22 @@ FontServ:: NewFont(const char *fontname, int ptsize)
int nchars; /* number of chars including 'missing char' */
int nwords; /* bit image size, in words */
int i, swapfont;
- MFont *font;
-
+ MFont *prev, *font;
+
+ /* First see if we can find the font in our cache */
+ prev = NULL;
+ for (font = fonts; font; prev = font, font = font->next) {
+ if (strcmp(fontname, font->name) == 0 && ptsize == font->ptsize) {
+ /* Move this font to the front so it's found faster */
+ if (prev) {
+ prev->next = font->next;
+ font->next = fonts;
+ fonts = font;
+ }
+ return font;
+ }
+ }
+
/* Get the font family */
fond = fontres->Resource("FOND", fontname);
if ( fond == NULL ) {
@@ -152,6 +179,8 @@ FontServ:: NewFont(const char *fontname, int ptsize)
/* Now, Fent.ID is the ID of the correct NFNT resource */
font = new MFont;
+ font->name = fontname;
+ font->ptsize = ptsize;
font->nfnt = fontres->Resource("NFNT", Fent.ID);
if ( font->nfnt == NULL ) {
delete font;
@@ -205,9 +234,21 @@ FontServ:: NewFont(const char *fontname, int ptsize)
byteswap(font->locTable, nchars+1);
byteswap((Uint16 *)font->owTable, nchars);
}
+
+ /* Save this font in the cache */
+ font->next = fonts;
+ fonts = font;
+
return(font);
}
+void
+FontServ:: FreeFont(MFont *font)
+{
+ /* We'll likely be asked for this again soon, leave it alone */
+ return;
+}
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#define HiByte(word) ((word>>8)&0xFF)
#define LoByte(word) (word&0xFF)
@@ -263,6 +304,8 @@ SDL_Texture *
FontServ:: TextImage(const char *text, MFont *font, Uint8 style,
SDL_Color foreground, SDL_Color background)
{
+ char *key, *keycopy;
+ int keysize;
int width, height;
SDL_Texture *image;
Uint32 *bitmap;
@@ -277,6 +320,14 @@ FontServ:: TextImage(const char *text, MFont *font, Uint8 style,
int ascii, i, y;
int bit;
+ /* First see if we can find it in our cache */
+ keysize = strlen(font->name)+1+8+1+strlen(text)+1;
+ key = SDL_stack_alloc(char, keysize);
+ sprintf(key, "%s:%d:%s", font->name, font->ptsize, text);
+ if (hash_find(strings, key, (const void**)&image)) {
+ return image;
+ }
+
switch (style) {
case STYLE_NORM: bold_offset = 0;
break;
@@ -327,6 +378,7 @@ FontServ:: TextImage(const char *text, MFont *font, Uint8 style,
width = TextWidth(text, font, style);
if ( width == 0 ) {
SetError("No text to convert");
+ SDL_stack_free(key);
return(NULL);
}
height = (font->header)->fRectHeight;
@@ -381,17 +433,30 @@ FontServ:: TextImage(const char *text, MFont *font, Uint8 style,
bitmap[bit_offset++] = color;
}
- /* Map the image and return */
+ /* Create the image */
image = screen->LoadImage(width, height, bitmap);
- if (image) {
- ++text_allocated;
- }
delete[] bitmap;
+
+ /* Add it to our cache */
+ keycopy = new char[keysize];
+ strcpy(keycopy, key);
+ hash_insert(strings, keycopy, image);
+ SDL_stack_free(key);
+
return(image);
}
+
void
FontServ:: FreeText(SDL_Texture *text)
{
- --text_allocated;
- screen->FreeImage(text);
+ /* We'll likely be asked for this again soon, leave it alone */
+ return;
+}
+
+void
+FontServ:: FlushCache(void)
+{
+ /* We'll flush any strings in the cache and leave the fonts around */
+ hash_destroy(strings);
+ strings = hash_create(screen, hash_hash_string, hash_keymatch_string, hash_nuke_string_texture);
}
diff --git a/maclib/Mac_FontServ.h b/maclib/Mac_FontServ.h
index f66ea8b9..af9b6fcf 100644
--- a/maclib/Mac_FontServ.h
+++ b/maclib/Mac_FontServ.h
@@ -73,7 +73,12 @@ struct FontHdr {
rowWords; /* Row width of bit image in words */
};
-typedef struct {
+typedef struct MFont {
+ /* Data useful for caching */
+ const char *name;
+ int ptsize;
+ struct MFont *next;
+
struct FontHdr *header; /* The NFNT header! */
/* Variable-length tables */
@@ -85,6 +90,8 @@ typedef struct {
Mac_ResData *nfnt;
} MFont;
+struct HashTable;
+
class FontServ {
public:
@@ -94,8 +101,9 @@ class FontServ {
FontServ(FrameBuf *screen, const char *fontfile);
~FontServ();
- /* The font returned by NewFont() should be delete'd */
+ /* The font returned by NewFont() should be freed with FreeFont() */
MFont *NewFont(const char *fontname, int ptsize);
+ void FreeFont(MFont *font);
/* Determine the final width/height of a text block (in pixels) */
Uint16 TextWidth(const char *text, MFont *font, Uint8 style);
@@ -118,6 +126,8 @@ class FontServ {
}
void FreeText(SDL_Texture *text);
+ void FlushCache();
+
/* Returns NULL if everything is okay, or an error message if not */
char *Error(void) {
return(errstr);
@@ -126,7 +136,8 @@ class FontServ {
private:
FrameBuf *screen;
Mac_Resource *fontres;
- int text_allocated;
+ MFont *fonts;
+ HashTable *strings;
/* Useful for getting error feedback */
void SetError(const char *fmt, ...) {
diff --git a/main.cpp b/main.cpp
index e2b02dce..0955e4d2 100644
--- a/main.cpp
+++ b/main.cpp
@@ -448,27 +448,21 @@ int DrawText(int x, int y, const char *text, MFont *font, Uint8 style,
/* -- Draw the current sound volume */
static void DrawSoundLevel(void)
{
- static int need_init=1;
- static MFont *geneva;
- static char text[12];
- static int xOff, yOff;
-
- if ( need_init ) {
- if ( (geneva = fontserv->NewFont("Geneva", 9)) == NULL ) {
- error("Can't use Geneva font! -- Exiting.\n");
- exit(255);
- }
- xOff = (SCREEN_WIDTH - 512) / 2;
- yOff = (SCREEN_HEIGHT - 384) / 2;
- need_init = 0;
- } else {
- DrawText(xOff+309-7, yOff+240-6, text, geneva, STYLE_BOLD,
- 0x00, 0x00, 0x00);
+ MFont *geneva;
+ char text[12];
+ int xOff, yOff;
+
+ if ( (geneva = fontserv->NewFont("Geneva", 9)) == NULL ) {
+ error("Can't use Geneva font! -- Exiting.\n");
+ exit(255);
}
+
+ xOff = (SCREEN_WIDTH - 512) / 2;
+ yOff = (SCREEN_HEIGHT - 384) / 2;
sprintf(text, "%d", gSoundLevel);
DrawText(xOff+309-7, yOff+240-6, text, geneva, STYLE_BOLD,
30000>>8, 30000>>8, 0xFF);
- screen->Update();
+ fontserv->FreeFont(geneva);
} /* -- DrawSoundLevel */
@@ -576,7 +570,7 @@ void DrawMainScreen(void)
DrawText(wRt-sw, botDiv+42+(index*18), buffer,
font, STYLE_BOLD, R, G, B);
}
- delete font;
+ fontserv->FreeFont(font);
DrawText(xOff+5, botDiv+46+(10*18)+3, "Last Score: ",
bigfont, STYLE_NORM, 0xFF, 0xFF, 0xFF);
@@ -584,7 +578,7 @@ void DrawMainScreen(void)
sw = fontserv->TextWidth("Last Score: ", bigfont, STYLE_NORM);
DrawText(xOff+5+sw, botDiv+46+(index*18)+3, buffer,
bigfont, STYLE_NORM, 0xFF, 0xFF, 0xFF);
- delete bigfont;
+ fontserv->FreeFont(bigfont);
/* -- Draw the Instructions */
offset = 34;
@@ -636,7 +630,7 @@ void DrawMainScreen(void)
DrawText(xOff+20, yOff+151, VERSION_STRING,
font, STYLE_NORM, 0xFF, 0xFF, 0xFF);
- delete font;
+ fontserv->FreeFont(font);
DrawSoundLevel();
@@ -658,14 +652,14 @@ static void DrawKey(MPoint *pt, const char *key, const char *text, void (*callba
error("Can't use Geneva font! -- Exiting.\n");
exit(255);
}
+
screen->QueueBlit(pt->h, pt->v, gKeyIcon);
- screen->Update();
DrawText(pt->h+14, pt->v+20, key, geneva, STYLE_BOLD, 0xFF, 0xFF, 0xFF);
DrawText(pt->h+13, pt->v+19, key, geneva, STYLE_BOLD, 0x00, 0x00, 0x00);
DrawText(pt->h+gKeyIcon->w+3, pt->v+19, text,
geneva, STYLE_BOLD, 0xFF, 0xFF, 0x00);
- delete geneva;
+ fontserv->FreeFont(geneva);
buttons.Add_Button(pt->h, pt->v, gKeyIcon->w, gKeyIcon->h, callback);
} /* -- DrawKey */
@@ -673,30 +667,22 @@ static void DrawKey(MPoint *pt, const char *key, const char *text, void (*callba
void Message(const char *message)
{
- static MFont *font;
- static int xOff;
- static char *last_message;
+ MFont *font;
+ int xOff;
- if ( ! last_message ) { /* Initialize everything */
- /* This was taken from the DrawMainScreen function */
- xOff = (SCREEN_WIDTH - 512) / 2;
-
- if ( (font = fontserv->NewFont("New York", 14)) == NULL ) {
- error("Can't use New York(14) font! -- Exiting.\n");
- exit(255);
- }
- } else {
- DrawText(xOff, 25, last_message, font, STYLE_BOLD, 0, 0, 0);
- delete[] last_message;
+ if (!message) {
+ return;
}
- if ( message ) {
- DrawText(xOff, 25, message, font, STYLE_BOLD, 0xCC,0xCC,0xCC);
- last_message = new char[strlen(message)+1];
- strcpy(last_message, message);
- } else {
- last_message = new char[1];
- last_message[0] = '\0';
+
+ if ( (font = fontserv->NewFont("New York", 14)) == NULL ) {
+ error("Can't use New York(14) font! -- Exiting.\n");
+ exit(255);
}
- screen->Update();
+
+ /* This was taken from the DrawMainScreen function */
+ xOff = (SCREEN_WIDTH - 512) / 2;
+ DrawText(xOff, 25, message, font, STYLE_BOLD, 0xCC,0xCC,0xCC);
+
+ fontserv->FreeFont(font);
}
diff --git a/netlogic/about.cpp b/netlogic/about.cpp
index 0898459d..d48c53ae 100644
--- a/netlogic/about.cpp
+++ b/netlogic/about.cpp
@@ -191,7 +191,7 @@ void DoAbout(void)
text2, NOCLIP);
fontserv->FreeText(text1);
fontserv->FreeText(text2);
- delete font;
+ fontserv->FreeFont(font);
}
screen->Update();
screen->Fade();
diff --git a/netlogic/game.cpp b/netlogic/game.cpp
index aa698a14..5177473d 100644
--- a/netlogic/game.cpp
+++ b/netlogic/game.cpp
@@ -327,7 +327,7 @@ void NewGame(void)
DoGameOver();
screen->ShowCursor();
- delete geneva;
+ fontserv->FreeFont(geneva);
} /* -- NewGame */
@@ -405,6 +405,9 @@ static void NextWave(void)
int NewRoids;
short temp;
+ /* Flush the font text cache */
+ fontserv->FlushCache();
+
gEnemySprite = NULL;
/* -- Initialize some variables */
@@ -592,7 +595,7 @@ static void DoGameOver(void)
DrawText(160, 380+i*newyork_height, buffer,
newyork, STYLE_NORM, 30000>>8, 30000>>8, 0xFF);
}
- delete newyork;
+ fontserv->FreeFont(newyork);
}
screen->Update();
@@ -680,7 +683,7 @@ static void DoGameOver(void)
screen->Update();
}
}
- delete newyork;
+ fontserv->FreeFont(newyork);
SDL_StopTextInput();
/* In case the user just pressed <Return> */
diff --git a/scores.cpp b/scores.cpp
index 2aaf1c16..41e5456c 100644
--- a/scores.cpp
+++ b/scores.cpp
@@ -156,7 +156,7 @@ int ZapHighScores(void)
/* Clean up and return */
screen->FreeImage(splash);
- delete chicago;
+ fontserv->FreeFont(chicago);
delete dialog;
if ( do_clear ) {
memset(hScores, 0, sizeof(hScores));
@@ -252,7 +252,7 @@ int GetStartLevel(void)
fontserv->FreeText(text2);
fontserv->FreeText(text3);
fontserv->FreeText(text4);
- delete chicago;
+ fontserv->FreeFont(chicago);
delete dialog;
if ( do_level ) {
if ( ! startlives || (startlives > 40) )