You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
348 lines
7.9 KiB
348 lines
7.9 KiB
/* |
|
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->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 ) |
|
{ |
|
fs_offset_t length; |
|
qfont_t src; |
|
byte *pfile; |
|
int font_width, i; |
|
|
|
if( font->valid ) |
|
return true; |
|
|
|
pfile = g_fsapi.LoadFile( fontname, &length, false ); |
|
if( !pfile ) |
|
return false; |
|
|
|
if( length < sizeof( src )) |
|
{ |
|
Mem_Free( pfile ); |
|
return false; |
|
} |
|
|
|
memcpy( &src, pfile, sizeof( src )); |
|
Mem_Free( pfile ); |
|
|
|
font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width ); |
|
if( !font->hFontTexture ) |
|
return false; |
|
|
|
font->type = FONT_VARIABLE; |
|
font->valid = true; |
|
font->scale = scale; |
|
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 )); |
|
} |
|
|
|
static int CL_CalcTabStop( const cl_font_t *font, int x ) |
|
{ |
|
int space = font->charWidths[' ']; |
|
int tab = space * 6; // 6 spaces |
|
int stop = tab - x % tab; |
|
|
|
if( stop < space ) |
|
return tab * 2 - x % tab; // select next |
|
|
|
return stop; |
|
} |
|
|
|
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; |
|
|
|
// check if printable |
|
if( number <= 32 ) |
|
{ |
|
if( number == ' ' ) |
|
return font->charWidths[' ']; |
|
else if( number == '\t' ) |
|
return CL_CalcTabStop( font, x ); |
|
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 font->charWidths[number]; |
|
|
|
rc = &font->fontRc[number]; |
|
if( font->scale <= 1.0f || REF_GET_PARM( PARM_TEX_FILTERING, font->hFontTexture )) |
|
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 ); |
|
|
|
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; |
|
|
|
// some client functions ignore newlines |
|
if( !FBitSet( flags, FONT_DRAW_NOLF )) |
|
{ |
|
draw_len = 0; |
|
y += font->charHeight; |
|
} |
|
|
|
if( FBitSet( flags, FONT_DRAW_RESETCOLORONLF )) |
|
Vector4Copy( color, current_color ); |
|
continue; |
|
} |
|
|
|
if( IsColorString( s )) |
|
{ |
|
// don't copy alpha |
|
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, current_color, font, flags | FONT_DRAW_NORENDERMODE ); |
|
|
|
s++; |
|
} |
|
|
|
return draw_len; |
|
} |
|
|
|
int CL_DrawStringf( cl_font_t *font, float x, float y, rgba_t color, int flags, const char *fmt, ... ) |
|
{ |
|
va_list va; |
|
char buf[MAX_VA_STRING]; |
|
|
|
va_start( va, fmt ); |
|
Q_vsnprintf( buf, sizeof( buf ), fmt, va ); |
|
va_end( va ); |
|
|
|
return CL_DrawString( x, y, buf, color, font, flags ); |
|
} |
|
|
|
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height ) |
|
{ |
|
if( !font || !font->valid ) return; |
|
if( width ) |
|
{ |
|
if( number == '\t' ) |
|
*width = CL_CalcTabStop( font, 0 ); // at least return max tabstop |
|
else *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( !FBitSet( flags, FONT_DRAW_NOLF )) |
|
{ |
|
if( height ) |
|
*height += font->charHeight; |
|
} |
|
continue; |
|
} |
|
else if( *s == '\t' ) |
|
{ |
|
draw_len += CL_CalcTabStop( font, 0 ); // at least return max tabstop |
|
s++; |
|
continue; |
|
} |
|
|
|
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[number]; |
|
|
|
if( draw_len > *width ) |
|
*width = draw_len; |
|
} |
|
|
|
s++; |
|
} |
|
}
|
|
|