engine: client: introduce bare-bones font manager and text drawing manager

* wire hud_fontscale so HUD font scaling can be used independently from
  hud_scale
* allow small optimizatinons, like optional UTF-8 decoding, or not calling
  SetRenderMode for each character
* even less copypasted code in text drawing between client code and console
* get rid of direct DrawCharacter calls when it can be just DrawString
* fix net_speeds, r_speeds with scaled console fonts
* try to fix MobilityAPI's pfnDrawCharacterScaled
* center keyboard keys in OSK code
This commit is contained in:
Alibek Omarov 2023-02-03 08:51:16 +03:00
parent 402a0f129d
commit 77ea03a62c
9 changed files with 469 additions and 522 deletions

291
engine/client/cl_font.c Normal file
View File

@ -0,0 +1,291 @@
/*
cl_font.c - bare bones engine font manager
Copyright (C) 2023 Alibek Omarov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "common.h"
#include "filesystem.h"
#include "client.h"
#include "qfont.h"
qboolean CL_FixedFont( cl_font_t *font )
{
return font && font->valid && font->type == FONT_FIXED;
}
static int CL_LoadFontTexture( const char *fontname, uint texFlags, int *width )
{
int font_width;
int tex;
if( !g_fsapi.FileExists( fontname, false ))
return 0;
tex = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags );
if( !tex )
return 0;
font_width = REF_GET_PARM( PARM_TEX_WIDTH, tex );
if( !font_width )
{
ref.dllFuncs.GL_FreeTexture( tex );
return 0;
}
*width = font_width;
return tex;
}
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags )
{
int font_width, i;
if( font->valid )
return true; // already loaded
font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width );
if( !font->hFontTexture )
return false;
font->type = FONT_FIXED;
font->valid = true;
font->scale = scale;
font->nearest = FBitSet( texFlags, TF_NEAREST );
font->rendermode = rendermode;
font->charHeight = Q_rint( font_width / 16 * scale );
for( i = 0; i < ARRAYSIZE( font->fontRc ); i++ )
{
font->fontRc[i].left = ( i * font_width / 16 ) % font_width;
font->fontRc[i].right = font->fontRc[i].left + font_width / 16;
font->fontRc[i].top = ( i / 16 ) * ( font_width / 16 );
font->fontRc[i].bottom = font->fontRc[i].top + font_width / 16;
font->charWidths[i] = Q_rint( font_width / 16 * scale );
}
return true;
}
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags )
{
qfont_t src;
file_t *fd;
int font_width, i;
if( font->valid )
return true;
fd = g_fsapi.Open( fontname, "r", false );
if( !fd )
return false;
if( g_fsapi.Read( fd, &src, sizeof( qfont_t )) != sizeof( qfont_t ))
{
g_fsapi.Close( fd );
return false;
}
g_fsapi.Close( fd );
font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width );
if( !font->hFontTexture )
return false;
font->type = FONT_VARIABLE;
font->valid = true;
font->scale = scale;
font->nearest = FBitSet( texFlags, TF_NEAREST );
font->rendermode = rendermode;
font->charHeight = Q_rint( src.rowheight * scale );
for( i = 0; i < ARRAYSIZE( font->fontRc ); i++ )
{
const charinfo *ci = &src.fontinfo[i];
font->fontRc[i].left = (word)ci->startoffset % font_width;
font->fontRc[i].right = font->fontRc[i].left + ci->charwidth;
font->fontRc[i].top = (word)ci->startoffset / font_width;
font->fontRc[i].bottom = font->fontRc[i].top + src.rowheight;
font->charWidths[i] = Q_rint( src.fontinfo[i].charwidth * scale );
}
return true;
}
void CL_FreeFont( cl_font_t *font )
{
if( !font || !font->valid )
return;
ref.dllFuncs.GL_FreeTexture( font->hFontTexture );
memset( font, 0, sizeof( *font ));
}
int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags )
{
wrect_t *rc;
float w, h;
float s1, t1, s2, t2, half = 0.5f;
int texw, texh;
if( !font || !font->valid || y < -font->charHeight )
return 0;
if( FBitSet( flags, FONT_DRAW_UTF8 ))
number = Con_UtfProcessChar( number & 255 );
else number &= 255;
if( !number || !font->charWidths[number])
return 0;
R_GetTextureParms( &texw, &texh, font->hFontTexture );
if( !texw || !texh )
return 0;
rc = &font->fontRc[number];
if( font->nearest )
half = 0;
s1 = ((float)rc->left + half ) / texw;
t1 = ((float)rc->top + half ) / texh;
s2 = ((float)rc->right - half ) / texw;
t2 = ((float)rc->bottom - half ) / texh;
w = ( rc->right - rc->left ) * font->scale;
h = ( rc->bottom - rc->top ) * font->scale;
if( FBitSet( flags, FONT_DRAW_HUD ))
SPR_AdjustSize( &x, &y, &w, &h );
if( !FBitSet( flags, FONT_DRAW_NORENDERMODE ))
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
// don't apply color to fixed fonts it's already colored
if( font->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, font->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8
ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] );
else ref.dllFuncs.Color4ub( 255, 255, 255, color[3] );
ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, font->hFontTexture );
ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); // don't forget reset color
return font->charWidths[number];
}
int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags )
{
rgba_t current_color;
int draw_len = 0;
if( !font || !font->valid )
return 0;
if( FBitSet( flags, FONT_DRAW_UTF8 ))
Con_UtfProcessChar( 0 ); // clear utf state
if( !FBitSet( flags, FONT_DRAW_NORENDERMODE ))
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
Vector4Copy( color, current_color );
while( *s )
{
if( *s == '\n' )
{
s++;
if( !*s )
break;
draw_len = 0;
y += font->charHeight;
}
if( IsColorString( s ))
{
if( FBitSet( flags, FONT_DRAW_FORCECOL ))
VectorCopy( g_color_table[ColorIndex(*( s + 1 ))], current_color );
s += 2;
continue;
}
// skip setting rendermode, it was changed for this string already
draw_len += CL_DrawCharacter( x + draw_len, y, (byte)*s, color, font, flags | FONT_DRAW_NORENDERMODE );
s++;
}
return draw_len;
}
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height )
{
if( !font || !font->valid ) return;
if( width ) *width = font->charWidths[number & 255];
if( height ) *height = font->charHeight;
}
void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags )
{
int draw_len = 0;
if( !font || !font->valid )
return;
if( height )
*height = font->charHeight;
if( width )
*width = 0;
if( !COM_CheckString( s ))
return;
if( FBitSet( flags, FONT_DRAW_UTF8 ))
Con_UtfProcessChar( 0 ); // reset utf state
while( *s )
{
int number;
if( *s == '\n' )
{
// BUG: no check for end string here
// but high chances somebody's relying on this
s++;
draw_len = 0;
if( height )
*height += font->charHeight;
}
if( IsColorString( s ))
{
s += 2;
continue;
}
if( FBitSet( flags, FONT_DRAW_UTF8 ))
number = Con_UtfProcessChar( (byte)*s );
else number = (byte)*s;
if( number )
{
draw_len += font->charWidths[*s];
if( draw_len > *width )
*width = draw_len;
}
s++;
}
}

View File

@ -306,9 +306,7 @@ print centerscreen message
*/
void CL_CenterPrint( const char *text, float y )
{
int length = 0;
int width = 0;
char *s;
cl_font_t *font = Con_GetCurFont();
if( !COM_CheckString( text ))
return;
@ -317,24 +315,13 @@ void CL_CenterPrint( const char *text, float y )
clgame.centerPrint.totalWidth = 0;
clgame.centerPrint.time = cl.mtime[0]; // allow pause for centerprint
Q_strncpy( clgame.centerPrint.message, text, sizeof( clgame.centerPrint.message ));
s = clgame.centerPrint.message;
// count the number of lines for centering
while( *s )
{
if( *s == '\n' )
{
clgame.centerPrint.lines++;
if( width > clgame.centerPrint.totalWidth )
clgame.centerPrint.totalWidth = width;
width = 0;
}
else width += clgame.scrInfo.charWidths[*s];
s++;
length++;
}
CL_DrawStringLen( font,
clgame.centerPrint.message,
&clgame.centerPrint.totalWidth,
&clgame.centerPrint.totalHeight,
FONT_DRAW_HUD | FONT_DRAW_UTF8 );
clgame.centerPrint.totalHeight = ( clgame.centerPrint.lines * clgame.scrInfo.iCharHeight );
clgame.centerPrint.y = CL_AdjustYPos( y, clgame.centerPrint.totalHeight );
}
@ -502,6 +489,7 @@ called each frame
*/
void CL_DrawCenterPrint( void )
{
cl_font_t *font = Con_GetCurFont();
char *pText;
int i, j, x, y;
int width, lineLength;
@ -521,8 +509,10 @@ void CL_DrawCenterPrint( void )
y = clgame.centerPrint.y; // start y
colorDefault = g_color_table[7];
pText = clgame.centerPrint.message;
Con_DrawCharacterLen( 0, NULL, &charHeight );
CL_DrawCharacterLen( font, 0, NULL, &charHeight );
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
for( i = 0; i < clgame.centerPrint.lines; i++ )
{
lineLength = 0;
@ -532,7 +522,7 @@ void CL_DrawCenterPrint( void )
{
byte c = *pText;
line[lineLength] = c;
Con_DrawCharacterLen( c, &charWidth, NULL );
CL_DrawCharacterLen( font, c, &charWidth, NULL );
width += charWidth;
lineLength++;
pText++;
@ -549,7 +539,7 @@ void CL_DrawCenterPrint( void )
for( j = 0; j < lineLength; j++ )
{
if( x >= 0 && y >= 0 && x <= refState.width )
x += Con_DrawCharacter( x, y, line[j], colorDefault );
x += CL_DrawCharacter( x, y, line[j], colorDefault, font, FONT_DRAW_UTF8 | FONT_DRAW_HUD | FONT_DRAW_NORENDERMODE );
}
y += charHeight;
}
@ -1600,6 +1590,14 @@ int GAME_EXPORT CL_GetScreenInfo( SCREENINFO *pscrinfo )
{
float scale_factor = hud_scale->value;
if( FBitSet( hud_fontscale->flags, FCVAR_CHANGED ))
{
CL_FreeFont( &cls.creditsFont );
SCR_LoadCreditsFont();
ClearBits( hud_fontscale->flags, FCVAR_CHANGED );
}
// setup screen info
clgame.scrInfo.iSize = sizeof( clgame.scrInfo );
clgame.scrInfo.iFlags = SCRINFO_SCREENFLASH;
@ -1844,24 +1842,13 @@ returns drawed chachter width (in real screen pixels)
*/
static int GAME_EXPORT pfnDrawCharacter( int x, int y, int number, int r, int g, int b )
{
if( !cls.creditsFont.valid )
return 0;
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD;
if( hud_utf8->value )
number = Con_UtfProcessChar( number );
flags |= FONT_DRAW_UTF8;
number &= 255;
if( number < 32 ) return 0;
if( y < -clgame.scrInfo.iCharHeight )
return 0;
clgame.ds.adjust_size = true;
pfnPIC_Set( cls.creditsFont.hFontTexture, r, g, b, 255 );
pfnPIC_DrawAdditive( x, y, -1, -1, &cls.creditsFont.fontRc[number] );
clgame.ds.adjust_size = false;
return clgame.scrInfo.charWidths[number];
return CL_DrawCharacter( x, y, number, color, &cls.creditsFont, flags );
}
/*
@ -1873,20 +1860,12 @@ drawing string like a console string
*/
int GAME_EXPORT pfnDrawConsoleString( int x, int y, char *string )
{
int drawLen;
cl_font_t *font = Con_GetFont( con_fontsize->value );
rgba_t color;
Vector4Copy( clgame.ds.textColor, color );
Vector4Set( clgame.ds.textColor, 255, 255, 255, 255 );
if( !COM_CheckString( string ))
return 0; // silent ignore
Con_SetFont( con_fontsize->value );
clgame.ds.adjust_size = true;
drawLen = Con_DrawString( x, y, string, clgame.ds.textColor );
MakeRGBA( clgame.ds.textColor, 255, 255, 255, 255 );
clgame.ds.adjust_size = false;
Con_RestoreFont();
return (x + drawLen); // exclude color prexfixes
return x + CL_DrawString( x, y, string, color, font, FONT_DRAW_UTF8 | FONT_DRAW_HUD );
}
/*
@ -1914,9 +1893,9 @@ compute string length in screen pixels
*/
void GAME_EXPORT pfnDrawConsoleStringLen( const char *pText, int *length, int *height )
{
Con_SetFont( con_fontsize->value );
Con_DrawStringLen( pText, length, height );
Con_RestoreFont();
cl_font_t *font = Con_GetFont( con_fontsize->value );
CL_DrawStringLen( font, pText, length, height, FONT_DRAW_UTF8 | FONT_DRAW_HUD );
}
/*
@ -2839,23 +2818,7 @@ pfnVGUI2DrawCharacter
*/
static int GAME_EXPORT pfnVGUI2DrawCharacter( int x, int y, int number, unsigned int font )
{
if( !cls.creditsFont.valid )
return 0;
number &= 255;
number = Con_UtfProcessChar( number );
if( number < 32 ) return 0;
if( y < -clgame.scrInfo.iCharHeight )
return 0;
clgame.ds.adjust_size = true;
gameui.ds.gl_texturenum = cls.creditsFont.hFontTexture;
pfnPIC_DrawAdditive( x, y, -1, -1, &cls.creditsFont.fontRc[number] );
clgame.ds.adjust_size = false;
return clgame.scrInfo.charWidths[number];
return pfnDrawCharacter( x, y, number, 255, 255, 255 );
}
/*
@ -2866,9 +2829,6 @@ pfnVGUI2DrawCharacterAdditive
*/
static int GAME_EXPORT pfnVGUI2DrawCharacterAdditive( int x, int y, int ch, int r, int g, int b, unsigned int font )
{
if( !hud_utf8->value )
ch = Con_UtfProcessChar( ch );
return pfnDrawCharacter( x, y, ch, r, g, b );
}
@ -2880,16 +2840,13 @@ pfnDrawString
*/
static int GAME_EXPORT pfnDrawString( int x, int y, const char *str, int r, int g, int b )
{
int iWidth = 0;
Con_UtfProcessChar(0);
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD;
// draw the string until we hit the null character or a newline character
for ( ; *str != 0 && *str != '\n'; str++ )
{
iWidth += pfnVGUI2DrawCharacterAdditive( x + iWidth, y, (unsigned char)*str, r, g, b, 0 );
}
if( hud_utf8->value )
SetBits( flags, FONT_DRAW_UTF8 );
return iWidth;
return CL_DrawString( x, y, str, color, &cls.creditsFont, flags );
}
/*
@ -2900,11 +2857,19 @@ pfnDrawStringReverse
*/
static int GAME_EXPORT pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b )
{
// find the end of the string
char *szIt;
for( szIt = (char*)str; *szIt != 0; szIt++ )
x -= clgame.scrInfo.charWidths[ (unsigned char) *szIt ];
return pfnDrawString( x, y, str, r, g, b );
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD;
int width, height;
if( hud_utf8->value )
SetBits( flags, FONT_DRAW_UTF8 );
CL_DrawStringLen( &cls.creditsFont, str, &width, &height, flags );
x -= width;
y -= height;
return CL_DrawString( x, y, str, color, &cls.creditsFont, flags );
}
/*

View File

@ -487,22 +487,10 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const
if( prc )
{
// calc user-defined rectangle
s1 = prc->left;
t1 = prc->top;
s2 = prc->right;
t2 = prc->bottom;
if( clgame.ds.adjust_size )
{
SPR_AdjustTexCoords( w, h, &s1, &t1, &s2, &t2 );
}
else
{
s1 /= (float)w;
t1 /= (float)h;
s2 /= (float)w;
t2 /= (float)h;
}
s1 = prc->left / (float)w;
t1 = prc->top / (float)h;
s2 = prc->right / (float)w;
t2 = prc->bottom / (float)h;
if( width == -1 && height == -1 )
{
@ -526,8 +514,6 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const
if( gameui.ds.scissor_test && !PIC_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 ))
return;
if( clgame.ds.adjust_size )
SPR_AdjustSize( &x, &y, &width, &height );
ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, gameui.ds.gl_texturenum );
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
}

View File

@ -25,6 +25,9 @@ mobile_engfuncs_t *gMobileEngfuncs;
convar_t *vibration_length;
convar_t *vibration_enable;
static cl_font_t g_scaled_font;
static float g_font_scale;
static void pfnVibrate( float life, char flags )
{
if( !vibration_enable->value )
@ -60,28 +63,28 @@ static void pfnEnableTextInput( int enable )
static int pfnDrawScaledCharacter( int x, int y, int number, int r, int g, int b, float scale )
{
int width = clgame.scrInfo.charWidths[number] * scale * hud_scale->value;
int height = clgame.scrInfo.iCharHeight * scale * hud_scale->value;
// this call is very ineffective and possibly broken!
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD;
if( !cls.creditsFont.valid )
return 0;
if( hud_utf8->value )
SetBits( flags, FONT_DRAW_UTF8 );
x *= hud_scale->value;
y *= hud_scale->value;
if( fabs( g_font_scale - scale ) > 0.1f ||
g_scaled_font.hFontTexture != cls.creditsFont.hFontTexture )
{
int i;
number &= 255;
number = Con_UtfProcessChar( number );
g_scaled_font = cls.creditsFont;
g_scaled_font.scale *= scale;
g_scaled_font.charHeight *= scale;
for( i = 0; i < ARRAYSIZE( g_scaled_font.charWidths ); i++ )
g_scaled_font.charWidths[i] *= scale;
if( number < 32 )
return 0;
g_font_scale = scale;
}
if( y < -height )
return 0;
pfnPIC_Set( cls.creditsFont.hFontTexture, r, g, b, 255 );
pfnPIC_DrawAdditive( x, y, width, height, &cls.creditsFont.fontRc[number] );
return width;
return CL_DrawCharacter( x, y, number, color, &g_scaled_font, flags );
}
static void *pfnGetNativeObject( const char *obj )

View File

@ -364,6 +364,7 @@ NetGraph_DrawTextFields
static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int count, float avg, int packet_loss, int packet_choke, int graphtype )
{
static int lastout;
cl_font_t *font = Con_GetFont( 0 );
rgba_t colors = { 0.9 * 255, 0.9 * 255, 0.7 * 255, 255 };
int ptx = Q_max( x + w - NETGRAPH_LERP_HEIGHT - 1, 1 );
int pty = Q_max( rect.top + rect.bottom - NETGRAPH_LERP_HEIGHT - 3, 1 );
@ -385,16 +386,17 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun
// move rolling average
framerate = FRAMERATE_AVG_FRAC * host.frametime + ( 1.0f - FRAMERATE_AVG_FRAC ) * framerate;
Con_SetFont( 0 );
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
if( framerate > 0.0f )
{
y -= net_graphheight->value;
Con_DrawString( x, y, va( "%.1f fps" , 1.0f / framerate ), colors );
CL_DrawString( x, y, va( "%.1f fps" , 1.0f / framerate ), colors, font, FONT_DRAW_NORENDERMODE );
if( avg > 1.0f )
Con_DrawString( x + 75, y, va( "%i ms" , (int)avg ), colors );
CL_DrawString( x + 75, y, va( "%i ms" , (int)avg ), colors, font, FONT_DRAW_NORENDERMODE );
y += 15;
@ -402,10 +404,10 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun
if( !out ) out = lastout;
else lastout = out;
Con_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors );
CL_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors, font, FONT_DRAW_NORENDERMODE );
y += 15;
Con_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors );
CL_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors, font, FONT_DRAW_NORENDERMODE );
y += 15;
if( graphtype > 2 )
@ -413,16 +415,14 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun
int loss = (int)(( packet_loss + PACKETLOSS_AVG_FRAC ) - 0.01f );
int choke = (int)(( packet_choke + PACKETCHOKE_AVG_FRAC ) - 0.01f );
Con_DrawString( x, y, va( "loss: %i choke: %i", loss, choke ), colors );
CL_DrawString( x, y, va( "loss: %i choke: %i", loss, choke ), colors, font, FONT_DRAW_NORENDERMODE );
}
}
if( graphtype < 3 )
Con_DrawString( ptx, pty, va( "%i/s", (int)cl_cmdrate->value ), colors );
CL_DrawString( ptx, pty, va( "%i/s", (int)cl_cmdrate->value ), colors, font, FONT_DRAW_NORENDERMODE );
Con_DrawString( ptx, last_y, va( "%i/s" , (int)cl_updaterate->value ), colors );
Con_RestoreFont();
CL_DrawString( ptx, last_y, va( "%i/s" , (int)cl_updaterate->value ), colors, font, FONT_DRAW_NORENDERMODE );
}
/*

View File

@ -160,6 +160,7 @@ void SCR_NetSpeeds( void )
static int max_clfps = 0;
int cur_clfps = 0;
rgba_t color;
cl_font_t *font = Con_GetCurFont();
if( !host.allow_console )
return;
@ -196,25 +197,11 @@ void SCR_NetSpeeds( void )
Q_memprint( cls.netchan.total_sended )
);
x = refState.width - 320;
x = refState.width - 320 * font->scale;
y = 384;
Con_DrawStringLen( NULL, NULL, &height );
MakeRGBA( color, 255, 255, 255, 255 );
p = start = msg;
do
{
end = Q_strchr( p, '\n' );
if( end ) msg[end-start] = '\0';
Con_DrawString( x, y, p, color );
y += height;
if( end ) p = end + 1;
else break;
} while( 1 );
CL_DrawString( x, y, msg, color, font, 0 );
}
/*
@ -234,28 +221,13 @@ void SCR_RSpeeds( void )
int x, y, height;
char *p, *start, *end;
rgba_t color;
cl_font_t *font = Con_GetCurFont();
x = refState.width - 340;
x = refState.width - 340 * font->scale;
y = 64;
Con_DrawStringLen( NULL, NULL, &height );
MakeRGBA( color, 255, 255, 255, 255 );
p = start = msg;
do
{
end = Q_strchr( p, '\n' );
if( end ) msg[end-start] = '\0';
Con_DrawString( x, y, p, color );
y += height;
// handle '\n\n'
if( *p == '\n' )
y += height;
if( end ) p = end + 1;
else break;
} while( 1 );
CL_DrawString( x, y, msg, color, font, 0 );
}
}
@ -588,6 +560,7 @@ void SCR_LoadCreditsFont( void )
{
cl_font_t *const font = &cls.creditsFont;
qboolean success = false;
float scale = hud_fontscale->value;
dword crc = 0;
// replace default gfx.wad textures by current charset's font
@ -599,15 +572,15 @@ void SCR_LoadCreditsFont( void )
"creditsfont_%s.fnt", Cvar_VariableString( "con_charset" )) > 0 )
{
if( FS_FileExists( charsetFnt, false ))
success = Con_LoadVariableWidthFont( charsetFnt, font, 1.0f, TF_FONT );
success = Con_LoadVariableWidthFont( charsetFnt, font, scale, kRenderTransAdd, TF_FONT );
}
}
if( !success )
success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, 1.0f, TF_FONT );
success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, scale, kRenderTransAdd, TF_FONT );
if( !success )
success = Con_LoadFixedWidthFont( "gfx/conchars", font, 1.0f, TF_FONT );
success = Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransAdd, TF_FONT );
// copy font size for client.dll
if( success )

View File

@ -318,17 +318,25 @@ typedef struct
pfnEventHook func; // user-defined function
} cl_user_event_t;
#define FONT_FIXED 0
#define FONT_VARIABLE 1
#define FONT_FIXED 0
#define FONT_VARIABLE 1
#define FONT_DRAW_HUD BIT( 0 ) // pass to drawing function to apply hud_scale
#define FONT_DRAW_UTF8 BIT( 1 ) // call UtfProcessChar
#define FONT_DRAW_FORCECOL BIT( 2 ) // ignore colorcodes
#define FONT_DRAW_NORENDERMODE BIT( 3 ) // ignore font's default rendermode
typedef struct
{
int hFontTexture; // handle to texture
wrect_t fontRc[256]; // rectangles
byte charWidths[256];
int charHeight;
int type;
qboolean valid; // all rectangles are valid
int hFontTexture; // handle to texture
wrect_t fontRc[256]; // tex coords
float scale; // scale factor
byte charWidths[256]; // scaled widths
int charHeight; // scaled height
int type; // fixed width font or variable
int rendermode; // default rendermode
qboolean nearest; // nearest filtering enabled
qboolean valid; // all rectangles are valid
} cl_font_t;
typedef struct
@ -342,7 +350,6 @@ typedef struct
int scissor_width;
int scissor_height;
qboolean scissor_test;
qboolean adjust_size; // allow to adjust scale for fonts
int renderMode; // override kRenderMode from TriAPI
TRICULLSTYLE cullMode; // override CULL FACE from TriAPI
@ -796,6 +803,19 @@ void CL_ResetEvent( event_info_t *ei );
word CL_EventIndex( const char *name );
void CL_FireEvents( void );
//
// cl_font.c
//
qboolean CL_FixedFont( cl_font_t *font );
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags );
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags );
void CL_FreeFont( cl_font_t *font );
int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags );
int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags );
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height );
void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags );
//
// cl_game.c
//
@ -1030,13 +1050,13 @@ int Con_UtfProcessChar( int in );
int Con_UtfProcessCharForce( int in );
int Con_UtfMoveLeft( char *str, int pos );
int Con_UtfMoveRight( char *str, int pos, int length );
void Con_DrawStringLen( const char *pText, int *length, int *height );
int Con_DrawString( int x, int y, const char *string, rgba_t setColor );
int Con_DrawCharacter( int x, int y, int number, rgba_t color );
void Con_DrawCharacterLen( int number, int *width, int *height );
void Con_DefaultColor( int r, int g, int b );
void Con_InvalidateFonts( void );
void Con_SetFont( int fontNum );
cl_font_t *Con_GetCurFont( void );
cl_font_t *Con_GetFont( int num );
void Con_DrawCharacterLen( int number, int *width, int *height );
int Con_DrawString( int x, int y, const char *string, rgba_t setColor ); // legacy, use cl_font.c
void GAME_EXPORT Con_DrawStringLen( const char *pText, int *length, int *height ); // legacy, use cl_font.c
void Con_CharEvent( int key );
void Con_RestoreFont( void );
void Key_Console( int key );
@ -1046,8 +1066,6 @@ void Con_Bottom( void );
void Con_Top( void );
void Con_PageDown( int lines );
void Con_PageUp( int lines );
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags );
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags );
//
// s_main.c

View File

@ -116,7 +116,7 @@ typedef struct
// console fonts
cl_font_t chars[CON_NUMFONTS];// fonts.wad/font1.fnt
cl_font_t *curFont, *lastUsedFont;
cl_font_t *curFont;
// console input
field_t input;
@ -555,90 +555,9 @@ Con_FixedFont
*/
qboolean Con_FixedFont( void )
{
if( con.curFont && con.curFont->valid && con.curFont->type == FONT_FIXED )
return true;
return false;
return CL_FixedFont( con.curFont );
}
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags )
{
int fontWidth;
int i;
if( font->valid )
return true; // already loaded
if( !FS_FileExists( fontname, false ))
return false;
font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags );
R_GetTextureParms( &fontWidth, NULL, font->hFontTexture );
if( font->hFontTexture && fontWidth != 0 )
{
font->charHeight = fontWidth / 16 * scale;
font->type = FONT_FIXED;
// build fixed rectangles
for( i = 0; i < 256; i++ )
{
font->fontRc[i].left = (i * (fontWidth / 16)) % fontWidth;
font->fontRc[i].right = font->fontRc[i].left + fontWidth / 16;
font->fontRc[i].top = (i / 16) * (fontWidth / 16);
font->fontRc[i].bottom = font->fontRc[i].top + fontWidth / 16;
font->charWidths[i] = fontWidth / 16 * scale;
}
font->valid = true;
}
return true;
}
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags )
{
fs_offset_t length;
qfont_t *src;
byte *buffer;
int fontWidth;
int i;
if( font->valid )
return true; // already loaded
if( !FS_FileExists( fontname, false ))
return false;
font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags );
R_GetTextureParms( &fontWidth, NULL, font->hFontTexture );
// setup consolefont
if( font->hFontTexture && fontWidth != 0 )
{
// half-life font with variable chars witdh
buffer = FS_LoadFile( fontname, &length, false );
if( buffer && length >= sizeof( qfont_t ))
{
src = (qfont_t *)buffer;
font->charHeight = src->rowheight * scale;
font->type = FONT_VARIABLE;
// build rectangles
for( i = 0; i < 256; i++ )
{
font->fontRc[i].left = (word)src->fontinfo[i].startoffset % fontWidth;
font->fontRc[i].right = font->fontRc[i].left + src->fontinfo[i].charwidth;
font->fontRc[i].top = (word)src->fontinfo[i].startoffset / fontWidth;
font->fontRc[i].bottom = font->fontRc[i].top + src->rowheight;
font->charWidths[i] = src->fontinfo[i].charwidth * scale;
}
font->valid = true;
}
if( buffer ) Mem_Free( buffer );
}
return true;
}
/*
================
@ -650,6 +569,7 @@ INTERNAL RESOURCE
static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
{
qboolean success = false;
float scale = con_fontscale->value;
if( font->valid )
return; // already loaded
@ -657,7 +577,7 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
// loading conchars
if( Sys_CheckParm( "-oldfont" ))
{
success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, con_fontscale->value, TF_FONT|TF_NEAREST );
success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
}
else
{
@ -670,14 +590,14 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
if( Q_snprintf( path, sizeof( path ),
"font%i_%s.fnt", fontNumber, Cvar_VariableString( "con_charset" )) > 0 )
{
success = Con_LoadVariableWidthFont( path, font, con_fontscale->value, TF_FONT|TF_NEAREST );
success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
}
}
if( !success )
{
Q_snprintf( path, sizeof( path ), "fonts/font%i", fontNumber );
success = Con_LoadVariableWidthFont( path, font, con_fontscale->value, TF_FONT|TF_NEAREST );
success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
}
}
@ -685,7 +605,7 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
{
// quake fixed font as fallback
// keep source to print directly into conback image
if( !Con_LoadFixedWidthFont( "gfx/conchars", font, con_fontscale->value, TF_FONT|TF_KEEP_SOURCE ))
if( !Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransTexture, TF_FONT|TF_KEEP_SOURCE ))
Con_DPrintf( S_ERROR "failed to load console font\n" );
}
}
@ -716,7 +636,7 @@ static void Con_LoadConchars( void )
fontSize = CON_NUMFONTS - 1;
// sets the current font
con.lastUsedFont = con.curFont = &con.chars[fontSize];
con.curFont = &con.chars[fontSize];
}
// CP1251 table
@ -889,129 +809,25 @@ static void Con_DrawCharToConback( int num, const byte *conchars, byte *dest )
/*
====================
Con_TextAdjustSize
Con_GetFont
draw charcters routine
====================
*/
static void Con_TextAdjustSize( int *x, int *y, int *w, int *h )
cl_font_t *Con_GetFont( int num )
{
float xscale, yscale;
if( !x && !y && !w && !h ) return;
// scale for screen sizes
xscale = (float)refState.width / (float)clgame.scrInfo.iWidth;
yscale = (float)refState.height / (float)clgame.scrInfo.iHeight;
if( x ) *x *= xscale;
if( y ) *y *= yscale;
if( w ) *w *= xscale;
if( h ) *h *= yscale;
num = bound( 0, num, CON_NUMFONTS - 1 );
return &con.chars[num];
}
/*
====================
Con_DrawGenericChar
Con_GetCurFont
draw console single character
====================
*/
static int Con_DrawGenericChar( int x, int y, int number, rgba_t color )
cl_font_t *Con_GetCurFont( void )
{
int width, height;
float s1, t1, s2, t2;
wrect_t *rc;
number &= 255;
if( !con.curFont || !con.curFont->valid )
return 0;
number = Con_UtfProcessChar( number );
if( !number )
return 0;
if( y < -con.curFont->charHeight )
return 0;
rc = &con.curFont->fontRc[number];
R_GetTextureParms( &width, &height, con.curFont->hFontTexture );
if( !width || !height )
return con.curFont->charWidths[number];
// don't apply color to fixed fonts it's already colored
if( con.curFont->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, con.curFont->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8
ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] );
else ref.dllFuncs.Color4ub( 255, 255, 255, color[3] );
// calc rectangle
s1 = (float)rc->left / width;
t1 = (float)rc->top / height;
s2 = (float)rc->right / width;
t2 = (float)rc->bottom / height;
width = ( rc->right - rc->left ) * con_fontscale->value;
height = ( rc->bottom - rc->top ) * con_fontscale->value;
if( clgame.ds.adjust_size )
Con_TextAdjustSize( &x, &y, &width, &height );
ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, con.curFont->hFontTexture );
ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); // don't forget reset color
return con.curFont->charWidths[number];
}
/*
====================
Con_SetFont
choose font size
====================
*/
void Con_SetFont( int fontNum )
{
fontNum = bound( 0, fontNum, CON_NUMFONTS - 1 );
con.curFont = &con.chars[fontNum];
}
/*
====================
Con_RestoreFont
restore auto-selected console font
(that based on screen resolution)
====================
*/
void Con_RestoreFont( void )
{
con.curFont = con.lastUsedFont;
}
/*
====================
Con_DrawCharacter
client version of routine
====================
*/
int Con_DrawCharacter( int x, int y, int number, rgba_t color )
{
ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
return Con_DrawGenericChar( x, y, number, color );
}
/*
====================
Con_DrawCharacterLen
returns character sizes in screen pixels
====================
*/
void Con_DrawCharacterLen( int number, int *width, int *height )
{
if( width && con.curFont ) *width = con.curFont->charWidths[number];
if( height && con.curFont ) *height = con.curFont->charHeight;
return con.curFont;
}
/*
@ -1023,105 +839,7 @@ compute string width and height in screen pixels
*/
void GAME_EXPORT Con_DrawStringLen( const char *pText, int *length, int *height )
{
int curLength = 0;
if( !con.curFont )
return;
if( height )
*height = con.curFont->charHeight;
if (!length)
return;
*length = 0;
while( *pText )
{
byte c = *pText;
if( *pText == '\n' )
{
pText++;
curLength = 0;
}
// skip color strings they are not drawing
if( IsColorString( pText ))
{
pText += 2;
continue;
}
// Convert to unicode
c = Con_UtfProcessChar( c );
if( c )
curLength += con.curFont->charWidths[c];
pText++;
if( curLength > *length )
*length = curLength;
}
}
/*
==================
Con_DrawString
Draws a multi-colored string, optionally forcing
to a fixed color.
==================
*/
int Con_DrawGenericString( int x, int y, const char *string, rgba_t setColor, qboolean forceColor, int hideChar )
{
rgba_t color;
int drawLen = 0;
int numDraws = 0;
const char *s;
if( !con.curFont ) return 0; // no font set
Con_UtfProcessChar( 0 );
// draw the colored text
memcpy( color, setColor, sizeof( color ));
s = string;
while( *s )
{
if( *s == '\n' )
{
s++;
if( !*s ) break; // at end the string
drawLen = 0; // begin new row
y += con.curFont->charHeight;
}
if( IsColorString( s ))
{
if( !forceColor )
{
memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ));
color[3] = setColor[3];
}
s += 2;
numDraws++;
continue;
}
// hide char for overstrike mode
if( hideChar == numDraws )
drawLen += con.curFont->charWidths[*s];
else drawLen += Con_DrawCharacter( x + drawLen, y, *s, color );
numDraws++;
s++;
}
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
return drawLen;
return CL_DrawStringLen( con.curFont, pText, length, height, FONT_DRAW_UTF8 );
}
/*
@ -1133,10 +851,9 @@ client version of routine
*/
int Con_DrawString( int x, int y, const char *string, rgba_t setColor )
{
return Con_DrawGenericString( x, y, string, setColor, false, -1 );
return CL_DrawString( x, y, string, setColor, con.curFont, FONT_DRAW_UTF8 );
}
/*
================
Con_Init
@ -1628,7 +1345,7 @@ Field_DrawInputLine
void Field_DrawInputLine( int x, int y, field_t *edit )
{
int len, cursorChar;
int drawLen, hideChar = -1;
int drawLen;
int prestep, curPos;
char str[MAX_SYSPATH];
byte *colorDefault;
@ -1665,35 +1382,23 @@ void Field_DrawInputLine( int x, int y, field_t *edit )
// save char for overstrike
cursorChar = str[edit->cursor - prestep];
if( host.key_overstrike && cursorChar && !((int)( host.realtime * 4 ) & 1 ))
hideChar = edit->cursor - prestep; // skip this char
// draw it
Con_DrawGenericString( x, y, str, colorDefault, false, hideChar );
CL_DrawString( x, y, str, colorDefault, con.curFont, FONT_DRAW_UTF8 );
// draw the cursor
if((int)( host.realtime * 4 ) & 1 ) return; // off blink
// calc cursor position
str[edit->cursor - prestep] = 0;
Con_DrawStringLen( str, &curPos, NULL );
Con_UtfProcessChar( 0 );
CL_DrawStringLen( con.curFont, str, &curPos, NULL, FONT_DRAW_UTF8 );
if( host.key_overstrike && cursorChar )
if( host.key_overstrike )
{
// overstrike cursor
#if 0
pglEnable( GL_BLEND );
pglDisable( GL_ALPHA_TEST );
pglBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
#endif
Con_DrawGenericChar( x + curPos, y, cursorChar, colorDefault );
CL_DrawCharacter( x + curPos, y, '|', colorDefault, con.curFont, 0 );
}
else
{
Con_UtfProcessChar( 0 );
Con_DrawCharacter( x + curPos, y, '_', colorDefault );
CL_DrawCharacter( x + curPos, y, '_', colorDefault, con.curFont, 0 );
}
}
@ -1997,7 +1702,7 @@ void Con_DrawInput( int lines )
return;
y = lines - ( con.curFont->charHeight * 2 );
Con_DrawCharacter( con.curFont->charWidths[' '], y, ']', g_color_table[7] );
CL_DrawCharacter( con.curFont->charWidths[' '], y, ']', g_color_table[7], con.curFont, 0 );
Field_DrawInputLine( con.curFont->charWidths[' ']*2, y, &con.input );
}
@ -2142,7 +1847,11 @@ int Con_DrawConsoleLine( int y, int lineno )
return 0; // this string will be shown only at notify
if( y >= con.curFont->charHeight )
Con_DrawGenericString( con.curFont->charWidths[' '], y, li->start, g_color_table[7], false, -1 );
{
float x = con.curFont->charWidths[' '];
CL_DrawString( x, y, li->start, g_color_table[7], con.curFont, FONT_DRAW_UTF8 );
}
return con.curFont->charHeight;
}
@ -2212,14 +1921,12 @@ void Con_DrawSolidConsole( int lines )
Q_snprintf( curbuild, MAX_STRING, XASH_ENGINE_NAME " %i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( ));
Con_DrawStringLen( curbuild, &stringLen, &charH );
start = refState.width - stringLen;
stringLen = Con_StringLength( curbuild );
start = refState.width - stringLen;
fraction = lines / (float)refState.height;
color[3] = Q_min( fraction * 2.0f, 1.0f ) * 255; // fadeout version number
for( i = 0; i < stringLen; i++ )
width += Con_DrawCharacter( start + width, 0, curbuild[i], color );
Con_DrawString( start, 0, curbuild, color );
// draw the text
if( CON_LINES_COUNT > 0 )
@ -2236,7 +1943,7 @@ void Con_DrawSolidConsole( int lines )
// draw red arrows to show the buffer is backscrolled
for( x = 0; x < con.linewidth; x += 4 )
Con_DrawCharacter(( x + 1 ) * start, y, '^', g_color_table[1] );
CL_DrawCharacter( ( x + 1 ) * start, y, '^', g_color_table[1], con.curFont, 0 );
y -= con.curFont->charHeight;
}
x = lastline;
@ -2339,7 +2046,7 @@ void Con_DrawVersion( void )
{
// draws the current build
byte *color = g_color_table[7];
int i, stringLen, width = 0, charH = 0;
int stringLen, charH = 0;
int start, height = refState.height;
qboolean draw_version = false;
string curbuild;
@ -2370,8 +2077,7 @@ void Con_DrawVersion( void )
stringLen = Con_StringLength( curbuild );
height -= charH * 1.05f;
for( i = 0; i < stringLen; i++ )
width += Con_DrawCharacter( start + width, height, curbuild[i], color );
Con_DrawString( start, height, curbuild, color );
}
/*
@ -2414,7 +2120,7 @@ void Con_RunConsole( void )
if( FBitSet( con_charset->flags, FCVAR_CHANGED ) ||
FBitSet( con_fontscale->flags, FCVAR_CHANGED ) ||
FBitSet( con_fontnum->flags, FCVAR_CHANGED ) ||
FBitSet( cl_charset->flags, FCVAR_CHANGED ) )
FBitSet( cl_charset->flags, FCVAR_CHANGED ))
{
// update codepage parameters
if( !Q_stricmp( con_charset->string, "cp1251" ))
@ -2436,8 +2142,6 @@ void Con_RunConsole( void )
g_utf8 = !Q_stricmp( cl_charset->string, "utf-8" );
Con_InvalidateFonts();
Con_LoadConchars();
cls.creditsFont.valid = false;
SCR_LoadCreditsFont();
ClearBits( con_charset->flags, FCVAR_CHANGED );
ClearBits( con_fontnum->flags, FCVAR_CHANGED );
ClearBits( con_fontscale->flags, FCVAR_CHANGED );
@ -2576,8 +2280,10 @@ Con_InvalidateFonts
*/
void Con_InvalidateFonts( void )
{
memset( con.chars, 0, sizeof( con.chars ));
con.curFont = con.lastUsedFont = NULL;
int i;
for( i = 0; i < ARRAYSIZE( con.chars ); i++ )
CL_FreeFont( &con.chars[i] );
con.curFont = NULL;
}
/*

View File

@ -1148,7 +1148,7 @@ Draw button with symbol on it
*/
static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height )
{
char str[] = {symb & 255, 0};
cl_font_t *font = Con_GetCurFont();
byte color[] = { 255, 255, 255, 255 };
int x1 = x * refState.width,
y1 = y * refState.height,
@ -1156,14 +1156,15 @@ static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float
h = height * refState.height;
if( symb == osk.curbutton.val )
{
ref.dllFuncs.FillRGBABlend( x1, y1, w, h, 255, 160, 0, 100 );
}
if( !symb || symb == ' ' || (symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ) )
return;
Con_DrawCharacter( x1 + 1, y1, symb, color );
CL_DrawCharacter(
x1 + width * 0.4 * refState.width,
y1 + height * 0.4 * refState.height,
symb, color, font, 0 );
}
/*
@ -1177,7 +1178,11 @@ static void OSK_DrawSpecialButton( const char *name, float x, float y, float wid
{
byte color[] = { 0, 255, 0, 255 };
Con_DrawString( x * refState.width, y * refState.height, name, color );
Con_DrawString(
x * refState.width + width * 0.4 * refState.width,
y * refState.height + height * 0.4 * refState.height,
name,
color );
}