From c2673128f60447e2884d89455ebbcaffbc4f6bb3 Mon Sep 17 00:00:00 2001 From: vortex Date: Sat, 22 May 2010 22:18:08 +0000 Subject: [PATCH] formed DP_GFX_FONTS/DP_GFX_FONTS_FREETYPE/DP_UTF8 extensions, add loadfont()/findfont() builtins. Font structure is now expandable (grows by 8), so new fonts could be added at no limits, just use new names for loadfont. When loading font, check for given file path before add extensions, this make "loadfont user1 myfont.ttf 10" work correctly. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10211 d7cf8633-e32d-0410-b094-e92efae38249 --- clvm_cmds.c | 4 +- draw.h | 32 +++++++---- ft2.c | 22 +++++--- gl_draw.c | 68 ++++++++++++++++------ mvm_cmds.c | 7 ++- prvm_cmds.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++- prvm_cmds.h | 3 + svvm_cmds.c | 4 ++ 8 files changed, 259 insertions(+), 41 deletions(-) diff --git a/clvm_cmds.c b/clvm_cmds.c index 355f345a..20c38faa 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -4347,8 +4347,8 @@ VM_CL_registercmd, // #352 void(string cmdname) registercommand (EXT_CSQC) VM_wasfreed, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too) VM_CL_serverkey, // #354 string(string key) serverkey (EXT_CSQC) VM_CL_videoplaying, // #355 -NULL, // #356 -NULL, // #357 +VM_findfont, // #356 float(string fontname) loadfont (DP_GFX_FONTS) +VM_loadfont, // #357 float(string fontname, string fontmaps, string sizes, float slot) loadfont (DP_GFX_FONTS) NULL, // #358 NULL, // #359 VM_CL_ReadByte, // #360 float() readbyte (EXT_CSQC) diff --git a/draw.h b/draw.h index e1092f5a..538a44ba 100644 --- a/draw.h +++ b/draw.h @@ -118,18 +118,26 @@ typedef struct dp_font_s } dp_font_t; -#define MAX_FONTS 16 -extern dp_font_t dp_fonts[MAX_FONTS]; -#define FONT_DEFAULT (&dp_fonts[0]) // should be fixed width -#define FONT_CONSOLE (&dp_fonts[1]) // REALLY should be fixed width (ls!) -#define FONT_SBAR (&dp_fonts[2]) // must be fixed width -#define FONT_NOTIFY (&dp_fonts[3]) // free -#define FONT_CHAT (&dp_fonts[4]) // free -#define FONT_CENTERPRINT (&dp_fonts[5]) // free -#define FONT_INFOBAR (&dp_fonts[6]) // free -#define FONT_MENU (&dp_fonts[7]) // should be fixed width -#define FONT_USER (&dp_fonts[8]) // userdefined fonts -#define MAX_USERFONTS (MAX_FONTS - (FONT_USER - dp_fonts)) +typedef struct dp_fonts_s +{ + dp_font_t *f; + int maxsize; +} +dp_fonts_t; +extern dp_fonts_t dp_fonts; + +#define MAX_FONTS 16 // fonts at the start +#define FONTS_EXPAND 8 // fonts grow when no free slots +#define FONT_DEFAULT (&dp_fonts.f[0]) // should be fixed width +#define FONT_CONSOLE (&dp_fonts.f[1]) // REALLY should be fixed width (ls!) +#define FONT_SBAR (&dp_fonts.f[2]) // must be fixed width +#define FONT_NOTIFY (&dp_fonts.f[3]) // free +#define FONT_CHAT (&dp_fonts.f[4]) // free +#define FONT_CENTERPRINT (&dp_fonts.f[5]) // free +#define FONT_INFOBAR (&dp_fonts.f[6]) // free +#define FONT_MENU (&dp_fonts.f[7]) // should be fixed width +#define FONT_USER(i) (&dp_fonts.f[8+i]) // userdefined fonts +#define MAX_USERFONTS (dp_fonts.maxsize - 8) // shared color tag printing constants #define STRING_COLOR_TAG '^' diff --git a/ft2.c b/ft2.c index ce929355..99dff819 100644 --- a/ft2.c +++ b/ft2.c @@ -251,12 +251,12 @@ void font_start(void) void font_shutdown(void) { int i; - for (i = 0; i < MAX_FONTS; ++i) + for (i = 0; i < dp_fonts.maxsize; ++i) { - if (dp_fonts[i].ft2) + if (dp_fonts.f[i].ft2) { - Font_UnloadFont(dp_fonts[i].ft2); - dp_fonts[i].ft2 = NULL; + Font_UnloadFont(dp_fonts.f[i].ft2); + dp_fonts.f[i].ft2 = NULL; } } Font_CloseLibrary(); @@ -273,6 +273,7 @@ void Font_Init(void) Cvar_RegisterVariable(&r_font_size_snapping); Cvar_RegisterVariable(&r_font_kerning); Cvar_RegisterVariable(&developer_font); + // let's open it at startup already Font_OpenLibrary(); } @@ -463,14 +464,22 @@ static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *setti namelen = strlen(name); + // try load direct file memcpy(filename, name, namelen); - memcpy(filename + namelen, ".ttf", 5); data = FS_LoadFile(filename, font_mempool, false, &datasize); + // try load .ttf + if (!data) + { + memcpy(filename + namelen, ".ttf", 5); + data = FS_LoadFile(filename, font_mempool, false, &datasize); + } + // try load .otf if (!data) { memcpy(filename + namelen, ".otf", 5); data = FS_LoadFile(filename, font_mempool, false, &datasize); } + // try load .pfb/afm if (!data) { ft2_attachment_t afm; @@ -487,13 +496,12 @@ static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *setti Font_Attach(font, &afm); } } - if (!data) { // FS_LoadFile being not-quiet should print an error :) return false; } - Con_Printf("Loading font %s face %i...\n", filename, _face); + Con_DPrintf("Loading font %s face %i...\n", filename, _face); status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face); if (status && _face != 0) diff --git a/gl_draw.c b/gl_draw.c index 84dfbd81..9a3544f8 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "ft2.h" #include "ft2_fontdefs.h" -dp_font_t dp_fonts[MAX_FONTS] = {{0}}; +dp_fonts_t dp_fonts; cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"}; cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"}; @@ -540,7 +540,7 @@ void Draw_FreePic(const char *picname) static float snap_to_pixel_x(float x, float roundUpAt); extern int con_linewidth; // to force rewrapping -static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) +void LoadFont(qboolean override, const char *name, dp_font_t *fnt) { int i; float maxwidth, scale; @@ -686,12 +686,37 @@ static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) con_linewidth = -1; // rewrap console in next frame } -static dp_font_t *FindFont(const char *title) +extern cvar_t developer_font; +dp_font_t *FindFont(const char *title, qboolean allocate_new) { int i; - for(i = 0; i < MAX_FONTS; ++i) - if(!strcmp(dp_fonts[i].title, title)) - return &dp_fonts[i]; + + // find font + for(i = 0; i < dp_fonts.maxsize; ++i) + if(!strcmp(dp_fonts.f[i].title, title)) + return &dp_fonts.f[i]; + // if not found - try allocate + if (allocate_new) + { + // find any font with empty title + for(i = 0; i < dp_fonts.maxsize; ++i) + { + if(!strcmp(dp_fonts.f[i].title, "")) + { + strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title)); + return &dp_fonts.f[i]; + } + } + // if no any 'free' fonts - expand buffer + i = dp_fonts.maxsize; + dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND; + if (developer_font.integer) + Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize); + dp_fonts.f = Mem_Realloc(tempmempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize); + // register a font in first expanded slot + strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title)); + return &dp_fonts.f[i]; + } return NULL; } @@ -732,8 +757,9 @@ static void LoadFont_f(void) if(Cmd_Argc() < 2) { Con_Printf("Available font commands:\n"); - for(i = 0; i < MAX_FONTS; ++i) - Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title); + for(i = 0; i < dp_fonts.maxsize; ++i) + if (dp_fonts.f[i].title[0]) + Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts.f[i].title); Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n" "can specify multiple fonts and faces\n" "Like this: gfx/vera-sans:2,gfx/fallback:1\n" @@ -745,7 +771,7 @@ static void LoadFont_f(void) ); return; } - f = FindFont(Cmd_Argv(1)); + f = FindFont(Cmd_Argv(1), true); if(f == NULL) { Con_Printf("font function not found\n"); @@ -840,8 +866,10 @@ static void gl_draw_start(void) font_start(); - for(i = 0; i < MAX_FONTS; ++i) - LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]); + // load default font textures + for(i = 0; i < dp_fonts.maxsize; ++i) + if (dp_fonts.f[i].title[0]) + LoadFont(false, va("gfx/font_%s", &dp_fonts.f[i].title), &dp_fonts.f[i]); // draw the loading screen so people have something to see in the newly opened window SCR_UpdateLoadingScreen(true); @@ -865,6 +893,7 @@ static void gl_draw_newmap(void) void GL_Draw_Init (void) { int i, j; + Cvar_RegisterVariable(&r_font_postprocess_blur); Cvar_RegisterVariable(&r_font_postprocess_outline); Cvar_RegisterVariable(&r_font_postprocess_shadow_x); @@ -875,11 +904,15 @@ void GL_Draw_Init (void) Cvar_RegisterVariable(&r_textshadow); Cvar_RegisterVariable(&r_textbrightness); Cvar_RegisterVariable(&r_textcontrast); - Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions"); - R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap); + // allocate fonts storage + dp_fonts.maxsize = MAX_FONTS; + dp_fonts.f = Mem_Alloc(tempmempool, sizeof(dp_font_t) * dp_fonts.maxsize); + memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize); + + // assign starting font names strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title)); - strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath)); + strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath)); strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title)); strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title)); strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title)); @@ -888,8 +921,11 @@ void GL_Draw_Init (void) strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title)); strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title)); for(i = 0, j = 0; i < MAX_USERFONTS; ++i) - if(!FONT_USER[i].title[0]) - dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++); + if(!FONT_USER(i)->title[0]) + dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++); + + Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions"); + R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap); } void _DrawQ_Setup(void) diff --git a/mvm_cmds.c b/mvm_cmds.c index 9f6f6080..9ac5b878 100644 --- a/mvm_cmds.c +++ b/mvm_cmds.c @@ -12,6 +12,9 @@ char *vm_m_extensions = "BX_WAL_SUPPORT " "DP_CINEMATIC_DPV " +"DP_GFX_FONTS " +"DP_GFX_FONTS_FREETYPE " +"DP_UTF8 " "DP_FONT_VARIABLEWIDTH " "DP_GECKO_SUPPORT " "DP_MENU_EXTRESPONSEPACKET " @@ -1130,8 +1133,8 @@ NULL, // #352 NULL, // #353 NULL, // #354 VM_CL_videoplaying, // #355 -NULL, // #356 -NULL, // #357 +VM_findfont, // #356 float(string fontname) loadfont (DP_GFX_FONTS) +VM_loadfont, // #357 float(string fontname, string fontmaps, string sizes, float slot) loadfont (DP_GFX_FONTS) NULL, // #358 NULL, // #359 NULL, // #360 diff --git a/prvm_cmds.c b/prvm_cmds.c index bd6c751b..6b8fde31 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -3277,9 +3277,9 @@ dp_font_t *getdrawfont(void) if(prog->globaloffsets.drawfont >= 0) { int f = (int) PRVM_G_FLOAT(prog->globaloffsets.drawfont); - if(f < 0 || f >= MAX_FONTS) + if(f < 0 || f >= dp_fonts.maxsize) return FONT_DEFAULT; - return &dp_fonts[f]; + return &dp_fonts.f[f]; } else return FONT_DEFAULT; @@ -3478,8 +3478,164 @@ void VM_stringwidth(void) PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw */ +} + +/* +========= +VM_findfont + +float findfont(string s) +========= +*/ + +float getdrawfontnum(const char *fontname) +{ + int i; + + for(i = 0; i < dp_fonts.maxsize; ++i) + if(!strcmp(dp_fonts.f[i].title, fontname)) + return i; + return -1; +} + +void VM_findfont(void) +{ + VM_SAFEPARMCOUNT(1,VM_findfont); + PRVM_G_FLOAT(OFS_RETURN) = getdrawfontnum(PRVM_G_STRING(OFS_PARM0)); +} + +/* +========= +VM_loadfont + +float loadfont(string fontname, string fontmaps, string sizes, float slot) +========= +*/ + +dp_font_t *FindFont(const char *title, qboolean allocate_new); +void LoadFont(qboolean override, const char *name, dp_font_t *fnt); +void VM_loadfont(void) +{ + const char *fontname, *filelist, *sizes, *c, *cm; + char mainfont[MAX_QPATH]; + int i, numsizes; + float sz; + dp_font_t *f; + + VM_SAFEPARMCOUNTRANGE(3,4,VM_loadfont); + + fontname = PRVM_G_STRING(OFS_PARM0); + if (!fontname[0]) + fontname = "default"; + + filelist = PRVM_G_STRING(OFS_PARM1); + if (!filelist[0]) + filelist = "gfx/conchars"; + + sizes = PRVM_G_STRING(OFS_PARM2); + if (!sizes[0]) + sizes = "10"; + + // find a font + f = NULL; + if (prog->argc >= 4) + { + i = PRVM_G_FLOAT(OFS_PARM3); + if (i >= 0 && i < dp_fonts.maxsize) + { + f = &dp_fonts.f[i]; + strlcpy(f->title, fontname, sizeof(f->title)); // replace name + } + } + if (!f) + f = FindFont(fontname, true); + if (!f) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + return; // something go wrong + } + + memset(f->fallbacks, 0, sizeof(f->fallbacks)); + memset(f->fallback_faces, 0, sizeof(f->fallback_faces)); + + // first font is handled "normally" + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->req_face = atoi(c+1); + else + { + f->req_face = 0; + c = cm; + } + if(!c || (c - filelist) > MAX_QPATH) + strlcpy(mainfont, filelist, sizeof(mainfont)); + else + { + memcpy(mainfont, filelist, c - filelist); + mainfont[c - filelist] = 0; + } + + // handle fallbacks + for(i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + c = strchr(filelist, ','); + if(!c) + break; + filelist = c + 1; + if(!*filelist) + break; + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->fallback_faces[i] = atoi(c+1); + else + { + f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index + c = cm; + } + if(!c || (c-filelist) > MAX_QPATH) + { + strlcpy(f->fallbacks[i], filelist, sizeof(mainfont)); + } + else + { + memcpy(f->fallbacks[i], filelist, c - filelist); + f->fallbacks[i][c - filelist] = 0; + } + } + // handle sizes + for(i = 0; i < MAX_FONT_SIZES; ++i) + f->req_sizes[i] = -1; + for (numsizes = 0,c = sizes;;) + { + if (!COM_ParseToken_VM_Tokenize(&c, 0)) + break; + sz = atof(com_token); + // detect crap size + if (sz < 0.001f || sz > 1000.0f) + { + VM_Warning("VM_loadfont: crap size %s", com_token); + continue; + } + // check overflow + if (numsizes == MAX_FONT_SIZES) + { + VM_Warning("VM_loadfont: MAX_FONT_SIZES = %i exceeded", MAX_FONT_SIZES); + break; + } + f->req_sizes[numsizes] = sz; + numsizes++; + } + + // load + LoadFont(true, mainfont, f); + + // return index of loaded font + PRVM_G_FLOAT(OFS_RETURN) = (f - dp_fonts.f); } + /* ========= VM_drawpic diff --git a/prvm_cmds.h b/prvm_cmds.h index c4f330ac..cc9013d6 100644 --- a/prvm_cmds.h +++ b/prvm_cmds.h @@ -359,6 +359,9 @@ void VM_drawsetcliparea(void); void VM_drawresetcliparea(void); void VM_getimagesize(void); +void VM_findfont(void); +void VM_loadfont(void); + void VM_makevectors (void); void VM_vectorvectors (void); diff --git a/svvm_cmds.c b/svvm_cmds.c index 625f0ed7..a11eb7c8 100644 --- a/svvm_cmds.c +++ b/svvm_cmds.c @@ -57,6 +57,10 @@ char *vm_sv_extensions = "DP_GFX_QUAKE3MODELTAGS " "DP_GFX_SKINFILES " "DP_GFX_SKYBOX " +"DP_GFX_FONTS " +"DP_GFX_FONTS_FREETYPE " +"DP_UTF8 " +"DP_FONT_VARIABLEWIDTH " "DP_HALFLIFE_MAP " "DP_HALFLIFE_MAP_CVAR " "DP_HALFLIFE_SPRITE " -- 2.39.2