2016-09-01 14:31:24 +00:00
# include "quakedef.h"
# ifndef SERVERONLY
# include "shader.h"
# include "gl_draw.h"
# ifdef _WIN32
2017-03-27 21:45:45 +00:00
# ifdef _XBOX
# include <xtl.h>
# else
# include <windows.h>
# endif
2016-09-01 14:31:24 +00:00
# endif
# include <ctype.h>
void Font_Init ( void ) ;
void Font_Shutdown ( void ) ;
2018-06-02 08:55:57 +00:00
struct font_s * Font_LoadFont ( const char * fontfilename , float height ) ;
2016-09-01 14:31:24 +00:00
void Font_Free ( struct font_s * f ) ;
void Font_BeginString ( struct font_s * font , float vx , float vy , int * px , int * py ) ;
void Font_BeginScaledString ( struct font_s * font , float vx , float vy , float szx , float szy , float * px , float * py ) ; /*avoid using*/
void Font_Transform ( float vx , float vy , int * px , int * py ) ;
int Font_CharHeight ( void ) ;
float Font_CharScaleHeight ( void ) ;
2018-10-11 10:31:23 +00:00
//FIXME: if we want to do emoji properly, then we're going to need support for variants somehow
// easiest is probably to make the next codepoint available too (and consumable)
// handling variants in the font cache is another issue. gah. not worth it.
2016-09-01 14:31:24 +00:00
int Font_CharWidth ( unsigned int charflags , unsigned int codepoint ) ;
float Font_CharScaleWidth ( unsigned int charflags , unsigned int codepoint ) ;
int Font_CharEndCoord ( struct font_s * font , int x , unsigned int charflags , unsigned int codepoint ) ;
int Font_DrawChar ( int px , int py , unsigned int charflags , unsigned int codepoint ) ;
float Font_DrawScaleChar ( float px , float py , unsigned int charflags , unsigned int codepoint ) ; /*avoid using*/
2018-10-11 10:31:23 +00:00
2016-09-01 14:31:24 +00:00
void Font_EndString ( struct font_s * font ) ;
int Font_LineBreaks ( conchar_t * start , conchar_t * end , int maxpixelwidth , int maxlines , conchar_t * * starts , conchar_t * * ends ) ;
struct font_s * font_default ;
struct font_s * font_console ;
struct font_s * font_tiny ;
static int font_be_flags ;
extern unsigned int r2d_be_flags ;
# ifdef AVAIL_FREETYPE
# include <ft2build.h>
# include FT_FREETYPE_H
2017-09-20 11:27:13 +00:00
# ifndef FT_LOAD_COLOR
# define FT_LOAD_COLOR (1L<<20)
# endif
2016-09-01 14:31:24 +00:00
static FT_Library fontlib ;
2018-03-24 04:02:09 +00:00
# ifdef FREETYPE_STATIC
# define pFT_Init_FreeType FT_Init_FreeType
# define pFT_Load_Char FT_Load_Char
# define pFT_Get_Char_Index FT_Get_Char_Index
# define pFT_Set_Pixel_Sizes FT_Set_Pixel_Sizes
# define pFT_Select_Size FT_Select_Size
# define pFT_New_Face FT_New_Face
# define pFT_New_Memory_Face FT_New_Memory_Face
# define pFT_Done_Face FT_Done_Face
# else
2016-09-01 14:31:24 +00:00
qboolean triedtoloadfreetype ;
dllhandle_t * fontmodule ;
FT_Error ( VARGS * pFT_Init_FreeType ) ( FT_Library * alibrary ) ;
FT_Error ( VARGS * pFT_Load_Char ) ( FT_Face face , FT_ULong char_code , FT_Int32 load_flags ) ;
FT_UInt ( VARGS * pFT_Get_Char_Index ) ( FT_Face face , FT_ULong charcode ) ;
FT_Error ( VARGS * pFT_Set_Pixel_Sizes ) ( FT_Face face , FT_UInt pixel_width , FT_UInt pixel_height ) ;
2017-09-20 11:27:13 +00:00
FT_Error ( VARGS * pFT_Select_Size ) ( FT_Face face , FT_Int strike_index ) ;
2016-09-01 14:31:24 +00:00
FT_Error ( VARGS * pFT_New_Face ) ( FT_Library library , const char * pathname , FT_Long face_index , FT_Face * aface ) ;
FT_Error ( VARGS * pFT_New_Memory_Face ) ( FT_Library library , const FT_Byte * file_base , FT_Long file_size , FT_Long face_index , FT_Face * aface ) ;
FT_Error ( VARGS * pFT_Done_Face ) ( FT_Face face ) ;
2018-03-24 04:02:09 +00:00
# endif
2017-09-20 11:27:13 +00:00
# else
typedef unsigned int FT_Pixel_Mode ; //for consistency even without freetype support.
# endif
# ifndef FT_PIXEL_MODE_GRAY
# define FT_PIXEL_MODE_GRAY 2
# endif
# ifndef FT_PIXEL_MODE_BGRA
# define FT_PIXEL_MODE_BGRA 7 //added in FT 2.5
2016-09-01 14:31:24 +00:00
# endif
2018-06-18 16:44:29 +00:00
# define FT_PIXEL_MODE_RGBA_SA (100) //RGBA, straight alpha. not in freetype.
# define FT_PIXEL_MODE_RGBA (101) //RGBA, premultiplied alpha. not in freetype.
2016-09-01 14:31:24 +00:00
2018-11-27 16:48:19 +00:00
# ifdef QUAKEHUD
2016-09-01 14:31:24 +00:00
static const char * imgs [ ] =
{
//0xe10X
" e100 " , " e101 " ,
" inv_shotgun " ,
" inv_sshotgun " ,
" inv_nailgun " ,
" inv_snailgun " ,
" inv_rlaunch " ,
" inv_srlaunch " ,
" inv_lightng " , //8
" e109 " , " e10a " , " e10b " , " e10c " , " e10d " , " e10e " , " e10f " ,
//0xe11X
" e110 " , " e111 " ,
" inv2_shotgun " ,
" inv2_sshotgun " ,
" inv2_nailgun " ,
" inv2_snailgun " ,
" inv2_rlaunch " ,
" inv2_srlaunch " ,
" inv2_lightng " ,
" e119 " , " e11a " , " e11b " , " e11c " , " e11d " , " e11e " , " e11f " ,
//0xe12X
" sb_shells " ,
" sb_nails " ,
" sb_rocket " ,
" sb_cells " ,
" sb_armor1 " ,
" sb_armor2 " ,
" sb_armor3 " ,
" e127 " , " e128 " , " e129 " , " e12a " , " e12b " , " e12c " , " e12d " , " e12e " , " e12f " ,
//0xe13X
" sb_key1 " ,
" sb_key2 " ,
" sb_invis " ,
" sb_invuln " ,
" sb_suit " ,
" sb_quad " ,
" sb_sigil1 " ,
" sb_sigil2 " ,
" sb_sigil3 " ,
" sb_sigil4 " ,
" e13a " , " e13b " , " e13c " , " e13d " , " e13e " , " e13f " ,
//0xe14X
" face1 " ,
" face_p1 " ,
" face2 " ,
" face_p2 " ,
" face3 " ,
" face_p3 " ,
" face4 " ,
" face_p4 " ,
" face5 " ,
" face_p5 " ,
" face_invis " ,
" face_invul2 " ,
" face_inv2 " ,
" face_quad " ,
" e14e " ,
" e14f " ,
//0xe15X
" e150 " ,
" e151 " ,
" e152 " ,
" e153 " ,
" e154 " ,
" e155 " ,
" e156 " ,
" e157 " ,
" e158 " ,
" e159 " ,
" e15a " ,
" e15b " ,
" e15c " ,
" e15d " ,
" e15e " ,
" e15f " ,
//0xe16X
" e160 " ,
" e161 " ,
" e162 " ,
" e163 " ,
" e164 " ,
" e165 " ,
" e166 " ,
" e167 " ,
" e168 " ,
" e169 " ,
" e16a " ,
" e16b " ,
" e16c " ,
" e16d " ,
" e16e " ,
" e16f "
} ;
2018-11-27 16:48:19 +00:00
# endif
2016-09-01 14:31:24 +00:00
2017-09-20 11:27:13 +00:00
# define FONT_MAXCHARS 0x110000 //as defined by UTF-16, and thus applied to all unicode because UTF-16 is the crappy limited one.
# define FONTBLOCKS ((FONT_MAXCHARS+FONTBLOCKSIZE-1) / FONTBLOCKSIZE)
# define FONTBLOCKSIZE 0x100 //must be power-of-two
# define FONTBLOCKMASK (FONTBLOCKSIZE-1)
# define FONTIMAGES (1<<2) //this is total, not per font.
# define FIMAGEIDXTYPE unsigned char
# define CHARIDXTYPE unsigned int
# define INVALIDPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-1)
# define BITMAPPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-2)
# define DEFAULTPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-3)
# define SINGLEPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-4)
# define TRACKERIMAGE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-5)
# define FIMAGEWIDTH (1<<10)
# define FIMAGEHEIGHT FIMAGEWIDTH
# define FONTPLANES FONTIMAGES
# define PLANEWIDTH FIMAGEWIDTH
# define PLANEHEIGHT FIMAGEHEIGHT
2016-09-01 14:31:24 +00:00
//windows' font linking allows linking multiple extra fonts to a main font.
//this means that a single selected font can use chars from lots of different files if the first one(s) didn't provide that font.
//they're provided as fallbacks.
2017-12-09 21:22:46 +00:00
# define MAX_FACES 32
2016-09-01 14:31:24 +00:00
2017-12-09 21:22:46 +00:00
typedef struct fontface_s
2016-09-01 14:31:24 +00:00
{
2017-12-09 21:22:46 +00:00
struct fontface_s * fnext ;
struct fontface_s * * flink ; //like prev, but not.
2016-09-01 14:31:24 +00:00
char name [ MAX_OSPATH ] ;
int refs ;
2017-12-09 21:22:46 +00:00
struct
{
qbyte * data ;
size_t width ;
size_t height ;
} horiz ;
# ifdef HALFLIFEMODELS
struct
{
int fontheight1 ;
int imgheight ;
int rows ;
int fontheight2 ;
struct
{
qbyte x ;
qbyte y ;
qbyte width ;
qbyte pad ;
} chartab [ 256 ] ;
// int unk[4];
qbyte data [ 1 ] ; //[256*imgheight];
//int pad
//palette[256*3];
} * halflife ;
# endif
# ifdef AVAIL_FREETYPE
struct
{
int activeheight ; //needs reconfiguring when different sizes are used
2018-10-11 10:31:23 +00:00
int actualsize ; //sometimes that activeheight isn't usable. :(
2017-12-09 21:22:46 +00:00
FT_Face face ;
void * membuf ;
} ft ;
2016-09-01 14:31:24 +00:00
# endif
2017-12-09 21:22:46 +00:00
} fontface_t ;
static fontface_t * faces ;
2016-09-01 14:31:24 +00:00
# define GEN_CONCHAR_GLYPHS 0 //set to 0 or 1 to define whether to generate glyphs from conchars too, or if it should just draw them as glquake always used to
extern cvar_t cl_noblink ;
extern cvar_t con_ocranaleds ;
typedef struct font_s
{
//FIXME: use a hash table? will need it if we go beyond ucs-2.
//currently they're in a single block so the font can be checked from scanning the active chars when shutting down. we could instead scan all 65k chars in every font instead...
struct charcache_s
{
struct charcache_s * nextchar ;
2017-09-20 11:27:13 +00:00
FIMAGEIDXTYPE texplane ;
2016-09-01 14:31:24 +00:00
unsigned char advance ; //how wide this char is, when drawn
2017-09-20 11:27:13 +00:00
unsigned short block ; //to quickly find the char again
2016-09-01 14:31:24 +00:00
2018-09-23 19:35:24 +00:00
//texture offsets.
2017-09-20 11:27:13 +00:00
unsigned short bmx ;
unsigned short bmy ;
2018-09-23 19:35:24 +00:00
//texture/screen pixel sizes.
2016-09-01 14:31:24 +00:00
unsigned char bmw ;
unsigned char bmh ;
2018-11-27 16:48:19 +00:00
unsigned short flags ;
# define CHARF_FORCEWHITE (1u<<0) //coloured emoji should not be tinted.
2016-09-01 14:31:24 +00:00
2018-09-23 19:35:24 +00:00
//screen offsets.
2016-09-01 14:31:24 +00:00
short top ;
short left ;
2017-09-20 11:27:13 +00:00
} * chars [ FONTBLOCKS ] ;
2016-09-01 14:31:24 +00:00
char name [ MAX_OSPATH ] ;
texid_t singletexture ;
2017-12-09 21:22:46 +00:00
unsigned short charheight ;
unsigned short faces ;
fontface_t * face [ MAX_FACES ] ;
2016-09-01 14:31:24 +00:00
struct font_s * alt ;
vec3_t tint ;
vec3_t alttint ;
} font_t ;
//shared between fonts.
typedef struct {
2017-09-20 11:27:13 +00:00
texid_t texnum [ FONTIMAGES ] ;
2016-09-01 14:31:24 +00:00
texid_t defaultfont ;
texid_t trackerimage ;
unsigned char plane [ PLANEWIDTH * PLANEHEIGHT ] [ 4 ] ; //tracks the current plane
2017-09-20 11:27:13 +00:00
FIMAGEIDXTYPE activeplane ;
unsigned short planerowx ;
unsigned short planerowy ;
unsigned short planerowh ;
2016-09-01 14:31:24 +00:00
qboolean planechanged ;
struct charcache_s * oldestchar ;
struct charcache_s * newestchar ;
shader_t * shader ;
shader_t * backshader ;
} fontplanes_t ;
static fontplanes_t fontplanes ;
# define FONT_CHAR_BUFFER 512
static index_t font_indicies [ FONT_CHAR_BUFFER * 6 ] ;
static vecV_t font_coord [ FONT_CHAR_BUFFER * 4 ] ;
2017-03-04 19:36:06 +00:00
static vecV_t font_backcoord [ FONT_CHAR_BUFFER * 4 ] ;
2016-09-01 14:31:24 +00:00
static vec2_t font_texcoord [ FONT_CHAR_BUFFER * 4 ] ;
static byte_vec4_t font_forecoloura [ FONT_CHAR_BUFFER * 4 ] ;
static byte_vec4_t font_backcoloura [ FONT_CHAR_BUFFER * 4 ] ;
static mesh_t font_foremesh ;
static mesh_t font_backmesh ;
static texid_t font_texture ;
static int font_colourmask ;
static byte_vec4_t font_forecolour ;
static byte_vec4_t font_backcolour ;
static avec4_t font_foretint ;
static struct font_s * curfont ;
static float curfont_scale [ 2 ] ;
static qboolean curfont_scaled ;
2017-08-29 02:29:06 +00:00
extern cvar_t r_font_linear ;
2016-09-01 14:31:24 +00:00
//^Ue2XX
static struct
{
image_t * image ;
char name [ 64 ] ;
} trackerimages [ 256 ] ;
static int numtrackerimages ;
# define TRACKERFIRST 0xe200
int Font_RegisterTrackerImage ( const char * image )
{
int i ;
for ( i = 0 ; i < numtrackerimages ; i + + )
{
if ( ! strcmp ( trackerimages [ i ] . name , image ) )
return TRACKERFIRST + i ;
}
if ( numtrackerimages = = 256 )
return 0 ;
trackerimages [ i ] . image = NULL ; //actually load it elsewhere, because we're lazy.
Q_strncpyz ( trackerimages [ i ] . name , image , sizeof ( trackerimages [ i ] . name ) ) ;
numtrackerimages + + ;
return TRACKERFIRST + i ;
}
//called from the font display code for tracker images
static image_t * Font_GetTrackerImage ( unsigned int imid )
{
if ( ! trackerimages [ imid ] . image )
{
if ( ! * trackerimages [ imid ] . name )
return NULL ;
trackerimages [ imid ] . image = Image_GetTexture ( trackerimages [ imid ] . name , NULL , 0 , NULL , NULL , 0 , 0 , TF_INVALID ) ;
}
if ( ! trackerimages [ imid ] . image )
return NULL ;
if ( trackerimages [ imid ] . image - > status ! = TEX_LOADED )
return NULL ;
return trackerimages [ imid ] . image ;
}
qboolean Font_TrackerValid ( unsigned int imid )
{
imid - = TRACKERFIRST ;
if ( imid > = countof ( trackerimages ) )
return false ;
if ( ! trackerimages [ imid ] . image )
{
if ( ! * trackerimages [ imid ] . name )
return false ;
trackerimages [ imid ] . image = Image_GetTexture ( trackerimages [ imid ] . name , NULL , 0 , NULL , NULL , 0 , 0 , TF_INVALID ) ;
}
if ( ! trackerimages [ imid ] . image )
return false ;
if ( trackerimages [ imid ] . image - > status = = TEX_LOADING )
COM_WorkerPartialSync ( trackerimages [ imid ] . image , & trackerimages [ imid ] . image - > status , TEX_LOADING ) ;
if ( trackerimages [ imid ] . image - > status ! = TEX_LOADED )
return false ;
return true ;
}
//called at load time - initalises font buffers
void Font_Init ( void )
{
int i ;
TEXASSIGN ( fontplanes . defaultfont , r_nulltex ) ;
//clear tracker images, just in case they were still set for the previous renderer context
for ( i = 0 ; i < sizeof ( trackerimages ) / sizeof ( trackerimages [ 0 ] ) ; i + + )
trackerimages [ i ] . image = NULL ;
font_foremesh . indexes = font_indicies ;
font_foremesh . xyz_array = font_coord ;
font_foremesh . st_array = font_texcoord ;
font_foremesh . colors4b_array = font_forecoloura ;
font_backmesh . indexes = font_indicies ;
2017-03-04 19:36:06 +00:00
font_backmesh . xyz_array = font_backcoord ;
2016-09-01 14:31:24 +00:00
font_backmesh . st_array = font_texcoord ;
font_backmesh . colors4b_array = font_backcoloura ;
for ( i = 0 ; i < FONT_CHAR_BUFFER ; i + + )
{
font_indicies [ i * 6 + 0 ] = i * 4 + 0 ;
font_indicies [ i * 6 + 1 ] = i * 4 + 1 ;
font_indicies [ i * 6 + 2 ] = i * 4 + 2 ;
font_indicies [ i * 6 + 3 ] = i * 4 + 0 ;
font_indicies [ i * 6 + 4 ] = i * 4 + 2 ;
font_indicies [ i * 6 + 5 ] = i * 4 + 3 ;
}
for ( i = 0 ; i < FONTPLANES ; i + + )
{
2017-08-29 02:29:06 +00:00
TEXASSIGN ( fontplanes . texnum [ i ] , Image_CreateTexture ( " ***fontplane*** " , NULL , IF_UIPIC | ( r_font_linear . ival ? IF_LINEAR : IF_NEAREST ) | IF_NOPICMIP | IF_NOMIPMAP | IF_NOGAMMA ) ) ;
2016-09-01 14:31:24 +00:00
}
2017-08-29 02:29:06 +00:00
fontplanes . shader = R_RegisterShader ( " ftefont " , SUF_2D ,
2016-09-01 14:31:24 +00:00
" { \n "
" if $nofixed \n "
" program default2d \n "
" endif \n "
" nomipmaps \n "
" { \n "
2017-12-20 08:23:41 +00:00
" if r_font_linear \n "
" map $linear:$diffuse \n "
" else \n "
" map $nearest:$diffuse \n "
" endif \n "
2016-09-01 14:31:24 +00:00
" rgbgen vertex \n "
" alphagen vertex \n "
2017-12-09 21:22:46 +00:00
" blendfunc gl_one gl_one_minus_src_alpha \n "
2016-09-01 14:31:24 +00:00
" } \n "
" } \n "
) ;
2017-08-29 02:29:06 +00:00
fontplanes . backshader = R_RegisterShader ( " ftefontback " , SUF_2D ,
2016-09-01 14:31:24 +00:00
" { \n "
" nomipmaps \n "
" { \n "
" map $whiteimage \n "
" rgbgen vertex \n "
" alphagen vertex \n "
2017-12-09 21:22:46 +00:00
" blendfunc gl_one gl_one_minus_src_alpha \n "
2016-09-01 14:31:24 +00:00
" } \n "
" } \n "
) ;
font_colourmask = ~ 0 ;
}
//flush the font buffer, by drawing it to the screen
static void Font_Flush ( void )
{
R2D_Flush = NULL ;
if ( ! font_foremesh . numindexes )
return ;
if ( fontplanes . planechanged )
{
Image_Upload ( fontplanes . texnum [ fontplanes . activeplane ] , TF_RGBA32 , ( void * ) fontplanes . plane , NULL , PLANEWIDTH , PLANEHEIGHT , IF_UIPIC | IF_NEAREST | IF_NOPICMIP | IF_NOMIPMAP | IF_NOGAMMA ) ;
fontplanes . planechanged = false ;
}
font_foremesh . istrifan = ( font_foremesh . numvertexes = = 4 ) ;
2017-03-04 19:36:06 +00:00
if ( ( font_colourmask & ( CON_RICHFORECOLOUR | CON_NONCLEARBG ) ) = = CON_NONCLEARBG & & font_foremesh . numindexes )
2016-09-01 14:31:24 +00:00
{
font_backmesh . numindexes = font_foremesh . numindexes ;
font_backmesh . numvertexes = font_foremesh . numvertexes ;
font_backmesh . istrifan = font_foremesh . istrifan ;
BE_DrawMesh_Single ( fontplanes . backshader , & font_backmesh , NULL , font_be_flags ) ;
}
TEXASSIGN ( fontplanes . shader - > defaulttextures - > base , font_texture ) ;
BE_DrawMesh_Single ( fontplanes . shader , & font_foremesh , NULL , font_be_flags ) ;
font_foremesh . numindexes = 0 ;
font_foremesh . numvertexes = 0 ;
}
static int Font_BeginChar ( texid_t tex )
{
int fvert ;
if ( font_foremesh . numindexes > = FONT_CHAR_BUFFER * 6 | | font_texture ! = tex )
{
Font_Flush ( ) ;
TEXASSIGNF ( font_texture , tex ) ;
}
fvert = font_foremesh . numvertexes ;
font_foremesh . numindexes + = 6 ;
font_foremesh . numvertexes + = 4 ;
return fvert ;
}
//clear the shared planes and free memory etc
void Font_Shutdown ( void )
{
int i ;
for ( i = 0 ; i < FONTPLANES ; i + + )
TEXASSIGN ( fontplanes . texnum [ i ] , r_nulltex ) ;
fontplanes . activeplane = 0 ;
fontplanes . oldestchar = NULL ;
fontplanes . newestchar = NULL ;
fontplanes . planechanged = 0 ;
fontplanes . planerowx = 0 ;
fontplanes . planerowy = 0 ;
fontplanes . planerowh = 0 ;
}
//we got too many chars and switched to a new plane - purge the chars in that plane
void Font_FlushPlane ( void )
{
/*
assumption :
oldest chars must be of the oldest plane
*/
//we've not broken anything yet, flush while we can
Font_Flush ( ) ;
if ( fontplanes . planechanged )
{
Image_Upload ( fontplanes . texnum [ fontplanes . activeplane ] , TF_RGBA32 , ( void * ) fontplanes . plane , NULL , PLANEWIDTH , PLANEHEIGHT , IF_UIPIC | IF_NEAREST | IF_NOPICMIP | IF_NOMIPMAP | IF_NOGAMMA ) ;
fontplanes . planechanged = false ;
}
fontplanes . activeplane + + ;
fontplanes . activeplane = fontplanes . activeplane % FONTPLANES ;
fontplanes . planerowh = 0 ;
fontplanes . planerowx = 0 ;
fontplanes . planerowy = 0 ;
while ( fontplanes . oldestchar )
{
if ( fontplanes . oldestchar - > texplane ! = fontplanes . activeplane )
break ;
//remove it from the list of active chars, and invalidate it
fontplanes . oldestchar - > texplane = INVALIDPLANE ;
fontplanes . oldestchar = fontplanes . oldestchar - > nextchar ;
}
if ( ! fontplanes . oldestchar )
fontplanes . newestchar = NULL ;
}
2017-09-20 11:27:13 +00:00
static struct charcache_s * Font_GetCharIfLoaded ( font_t * f , unsigned int charidx )
{
struct charcache_s * c = f - > chars [ charidx / FONTBLOCKSIZE ] ;
if ( c )
{
c + = charidx & FONTBLOCKMASK ;
if ( c - > texplane = = INVALIDPLANE )
c = NULL ;
}
return c ;
}
static struct charcache_s * Font_GetCharStore ( font_t * f , unsigned int charidx )
{ //should only be called if generating a char cache
struct charcache_s * c ;
size_t i ;
c = f - > chars [ charidx / FONTBLOCKSIZE ] ;
if ( ! c )
{
c = Z_Malloc ( sizeof ( struct charcache_s ) * FONTBLOCKSIZE ) ;
f - > chars [ charidx / FONTBLOCKSIZE ] = c ;
for ( i = 0 ; i < FONTBLOCKSIZE ; i + + )
{
c [ i ] . texplane = INVALIDPLANE ;
}
}
c + = charidx & FONTBLOCKMASK ;
c - > block = charidx / FONTBLOCKSIZE ;
return c ;
}
static struct charcache_s * Font_CopyChar ( font_t * f , unsigned int oldcharidx , unsigned int newcharidx )
{
struct charcache_s * new , * old = Font_GetCharIfLoaded ( f , oldcharidx ) ;
if ( ! old )
return NULL ;
new = Font_GetCharStore ( f , newcharidx ) ;
* new = * old ;
new - > block = newcharidx / FONTBLOCKSIZE ;
return new ;
}
2016-09-01 14:31:24 +00:00
//loads a new image into a given character slot for the given font.
//note: make sure it doesn't already exist or things will get cyclic
//alphaonly says if its a greyscale image. false means rgba.
2017-09-20 11:27:13 +00:00
static struct charcache_s * Font_LoadGlyphData ( font_t * f , CHARIDXTYPE charidx , FT_Pixel_Mode pixelmode , void * data , unsigned int bmw , unsigned int bmh , unsigned int pitch )
2016-09-01 14:31:24 +00:00
{
int x , y ;
unsigned char * out ;
2017-09-20 11:27:13 +00:00
struct charcache_s * c = Font_GetCharStore ( f , charidx ) ;
2017-08-29 02:29:06 +00:00
int pad = 0 ;
# define BORDERCOLOUR 0
if ( fontplanes . texnum [ 0 ] - > flags & IF_LINEAR )
pad + = 1 ; //pad the image data to avoid sampling outside
2016-09-01 14:31:24 +00:00
2017-08-29 02:29:06 +00:00
if ( fontplanes . planerowx + ( int ) bmw + pad * 2 > = PLANEWIDTH )
2016-09-01 14:31:24 +00:00
{
fontplanes . planerowx = 0 ;
fontplanes . planerowy + = fontplanes . planerowh ;
fontplanes . planerowh = 0 ;
}
2017-08-29 02:29:06 +00:00
if ( fontplanes . planerowy + ( int ) bmh + pad * 2 > = PLANEHEIGHT )
2016-09-01 14:31:24 +00:00
Font_FlushPlane ( ) ;
if ( fontplanes . newestchar )
fontplanes . newestchar - > nextchar = c ;
else
fontplanes . oldestchar = c ;
fontplanes . newestchar = c ;
c - > nextchar = NULL ;
2018-11-27 16:48:19 +00:00
c - > flags = 0 ;
2016-09-01 14:31:24 +00:00
c - > texplane = fontplanes . activeplane ;
2017-08-29 02:29:06 +00:00
c - > bmx = fontplanes . planerowx + pad ;
c - > bmy = fontplanes . planerowy + pad ;
2016-09-01 14:31:24 +00:00
c - > bmw = bmw ;
c - > bmh = bmh ;
2017-08-29 02:29:06 +00:00
if ( fontplanes . planerowh < ( int ) bmh + pad * 2 )
fontplanes . planerowh = bmh + pad * 2 ;
fontplanes . planerowx + = bmw + pad * 2 ;
2016-09-01 14:31:24 +00:00
2017-08-29 02:29:06 +00:00
out = ( unsigned char * ) & fontplanes . plane [ c - > bmx + ( ( int ) c - > bmy - pad ) * PLANEHEIGHT ] ;
2017-09-20 11:27:13 +00:00
if ( pixelmode = = FT_PIXEL_MODE_GRAY )
{ //8bit font
2017-08-29 02:29:06 +00:00
for ( y = - pad ; y < 0 ; y + + )
{
2017-12-09 21:22:46 +00:00
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
2017-08-29 02:29:06 +00:00
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
for ( ; y < bmh ; y + + )
2016-09-01 14:31:24 +00:00
{
2017-08-29 02:29:06 +00:00
for ( x = - pad ; x < 0 ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
for ( ; x < bmw ; x + + )
2017-12-09 21:22:46 +00:00
* ( unsigned int * ) & out [ x * 4 ] = 0x01010101 * ( ( unsigned char * ) data ) [ x ] ;
2017-08-29 02:29:06 +00:00
for ( ; x < bmw + pad ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
2016-09-01 14:31:24 +00:00
data = ( char * ) data + pitch ;
out + = PLANEWIDTH * 4 ;
}
2017-08-29 02:29:06 +00:00
for ( ; y < bmh + pad ; y + + )
{
2017-12-09 21:22:46 +00:00
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
2017-08-29 02:29:06 +00:00
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
2016-09-01 14:31:24 +00:00
}
2018-06-18 16:44:29 +00:00
else if ( ( unsigned int ) pixelmode = = FT_PIXEL_MODE_RGBA_SA )
2018-10-11 10:31:23 +00:00
{ //rgba source using standard alpha.
//(we'll multiply out the alpha for the gpu)
2017-08-29 02:29:06 +00:00
for ( y = - pad ; y < 0 ; y + + )
2016-09-01 14:31:24 +00:00
{
2017-12-09 21:22:46 +00:00
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
2017-08-29 02:29:06 +00:00
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
for ( ; y < bmh ; y + + )
{
for ( x = - pad ; x < 0 ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
for ( ; x < bmw ; x + + )
2017-12-09 21:22:46 +00:00
{
if ( ( ( unsigned char * ) data ) [ x * 4 + 3 ] = = 255 )
( ( unsigned int * ) out ) [ x ] = ( ( unsigned int * ) data ) [ x ] ;
else
{
out [ x * 4 + 0 ] = ( ( ( unsigned char * ) data ) [ x * 4 + 3 ] * ( ( unsigned char * ) data ) [ x * 4 + 0 ] ) < < 8 ;
out [ x * 4 + 1 ] = ( ( ( unsigned char * ) data ) [ x * 4 + 3 ] * ( ( unsigned char * ) data ) [ x * 4 + 1 ] ) < < 8 ;
out [ x * 4 + 2 ] = ( ( ( unsigned char * ) data ) [ x * 4 + 3 ] * ( ( unsigned char * ) data ) [ x * 4 + 2 ] ) < < 8 ;
out [ x * 4 + 3 ] = ( ( unsigned char * ) data ) [ x * 4 + 3 ] ;
}
}
2017-08-29 02:29:06 +00:00
for ( ; x < bmw + pad ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
2016-09-01 14:31:24 +00:00
data = ( char * ) data + pitch ;
out + = PLANEWIDTH * 4 ;
}
2017-12-09 21:22:46 +00:00
for ( ; y < ( int ) bmh + pad ; y + + )
2017-08-29 02:29:06 +00:00
{
2017-12-09 21:22:46 +00:00
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
2017-08-29 02:29:06 +00:00
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
2016-09-01 14:31:24 +00:00
}
2017-09-20 11:27:13 +00:00
else if ( pixelmode = = FT_PIXEL_MODE_BGRA )
{ //bgra srgb font, already premultiplied
for ( y = - pad ; y < 0 ; y + + )
{
2017-12-09 21:22:46 +00:00
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
2017-09-20 11:27:13 +00:00
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
for ( ; y < bmh ; y + + )
{
for ( x = - pad ; x < 0 ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
for ( ; x < bmw ; x + + )
{
out [ x * 4 + 0 ] = ( ( unsigned char * ) data ) [ x * 4 + 2 ] ;
out [ x * 4 + 1 ] = ( ( unsigned char * ) data ) [ x * 4 + 1 ] ;
out [ x * 4 + 2 ] = ( ( unsigned char * ) data ) [ x * 4 + 0 ] ;
out [ x * 4 + 3 ] = ( ( unsigned char * ) data ) [ x * 4 + 3 ] ;
}
for ( ; x < bmw + pad ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
data = ( char * ) data + pitch ;
out + = PLANEWIDTH * 4 ;
}
for ( ; y < bmh + pad ; y + + )
{
2017-12-09 21:22:46 +00:00
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
2018-11-27 16:48:19 +00:00
c - > flags = CHARF_FORCEWHITE ; //private glyph colours
2017-12-09 21:22:46 +00:00
}
2018-06-18 16:44:29 +00:00
else if ( ( unsigned int ) pixelmode = = FT_PIXEL_MODE_RGBA )
2017-12-09 21:22:46 +00:00
{ //bgra srgb font, already premultiplied
for ( y = - pad ; y < 0 ; y + + )
{
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
for ( ; y < bmh ; y + + )
{
for ( x = - pad ; x < 0 ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
for ( ; x < bmw ; x + + )
( ( unsigned int * ) out ) [ x ] = ( ( unsigned int * ) data ) [ x ] ;
for ( ; x < bmw + pad ; x + + )
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
data = ( char * ) data + pitch ;
out + = PLANEWIDTH * 4 ;
}
for ( ; y < bmh + pad ; y + + )
{
for ( x = - pad ; x < ( int ) bmw + pad ; x + + )
2017-09-20 11:27:13 +00:00
* ( unsigned int * ) & out [ x * 4 ] = BORDERCOLOUR ;
out + = PLANEWIDTH * 4 ;
}
2018-11-27 16:48:19 +00:00
c - > flags = CHARF_FORCEWHITE ; //private glyph colours
2017-09-20 11:27:13 +00:00
}
2016-09-01 14:31:24 +00:00
fontplanes . planechanged = true ;
return c ;
}
2017-09-20 11:27:13 +00:00
unsigned short hex [ 16 ] = {
/*0*/ ( 7 < < 0 ) | ( 5 < < 3 ) | ( 5 < < 6 ) | ( 5 < < 9 ) | ( 7 < < 12 ) ,
/*1*/ ( 2 < < 0 ) | ( 2 < < 3 ) | ( 2 < < 6 ) | ( 2 < < 9 ) | ( 2 < < 12 ) ,
/*2*/ ( 7 < < 0 ) | ( 1 < < 3 ) | ( 7 < < 6 ) | ( 4 < < 9 ) | ( 7 < < 12 ) ,
/*3*/ ( 7 < < 0 ) | ( 1 < < 3 ) | ( 3 < < 6 ) | ( 1 < < 9 ) | ( 7 < < 12 ) ,
/*4*/ ( 5 < < 0 ) | ( 5 < < 3 ) | ( 7 < < 6 ) | ( 1 < < 9 ) | ( 1 < < 12 ) ,
/*5*/ ( 7 < < 0 ) | ( 4 < < 3 ) | ( 7 < < 6 ) | ( 1 < < 9 ) | ( 7 < < 12 ) ,
/*6*/ ( 4 < < 0 ) | ( 4 < < 3 ) | ( 7 < < 6 ) | ( 5 < < 9 ) | ( 7 < < 12 ) ,
/*7*/ ( 7 < < 0 ) | ( 1 < < 3 ) | ( 1 < < 6 ) | ( 1 < < 9 ) | ( 1 < < 12 ) ,
/*8*/ ( 7 < < 0 ) | ( 5 < < 3 ) | ( 7 < < 6 ) | ( 5 < < 9 ) | ( 7 < < 12 ) ,
/*9*/ ( 7 < < 0 ) | ( 5 < < 3 ) | ( 7 < < 6 ) | ( 1 < < 9 ) | ( 1 < < 12 ) ,
/*A*/ ( 7 < < 0 ) | ( 5 < < 3 ) | ( 7 < < 6 ) | ( 5 < < 9 ) | ( 5 < < 12 ) ,
/*B*/ ( 6 < < 0 ) | ( 5 < < 3 ) | ( 7 < < 6 ) | ( 5 < < 9 ) | ( 6 < < 12 ) ,
/*C*/ ( 7 < < 0 ) | ( 5 < < 3 ) | ( 4 < < 6 ) | ( 5 < < 9 ) | ( 7 < < 12 ) ,
/*D*/ ( 6 < < 0 ) | ( 5 < < 3 ) | ( 5 < < 6 ) | ( 5 < < 9 ) | ( 6 < < 12 ) ,
/*E*/ ( 7 < < 0 ) | ( 4 < < 3 ) | ( 6 < < 6 ) | ( 4 < < 9 ) | ( 7 < < 12 ) ,
/*F*/ ( 7 < < 0 ) | ( 4 < < 3 ) | ( 6 < < 6 ) | ( 4 < < 9 ) | ( 4 < < 12 )
} ;
static struct charcache_s * Font_LoadPlaceholderGlyph ( font_t * f , CHARIDXTYPE charidx )
{
struct charcache_s * c ;
unsigned int out [ 169 * 4 ] , i , o , g , b , w , h , d , n ;
2018-01-24 12:13:32 +00:00
if ( charidx = = 0xe080 | | charidx = = 0xe081 | | charidx = = 0xe082 | | charidx = = 0xe083 )
{ //scrollbar glyps
w = min ( 16 , f - > charheight ) ;
h = max ( 1 , w / 8 ) ;
d = 0 ;
if ( charidx = = 0xe083 ) //centre
{
h * = 3 ;
memset ( out + w * h * 0 , 0xff , w * 4 * h ) ;
}
2017-09-20 11:27:13 +00:00
else
2018-01-24 12:13:32 +00:00
{
memset ( out + w * h * 0 , 0xff , w * 4 * h ) ;
memset ( out + w * h * 1 , 0x00 , w * 4 * h ) ;
memset ( out + w * h * 2 , 0xff , w * 4 * h ) ;
for ( i = 0 ; i < h ; i + + )
{
if ( charidx = = 0xe080 ) //left
memset ( out + w * ( i + h * 1 ) , 0xff , h * 4 ) ;
if ( charidx = = 0xe082 ) //right
memset ( out + w * ( i + h * 1 ) + w - h , 0xff , h * 4 ) ;
}
h * = 3 ;
}
}
else if ( charidx = = 0xe01d | | charidx = = 0xe01e | | charidx = = 0xe01f )
{ //horizontal separator
w = min ( 16 , f - > charheight ) ;
h = max ( 1 , w / 8 ) ;
d = 0 ;
memset ( out , 0xff , w * 4 * h ) ;
}
else if ( charidx = = 0xe00b )
2018-01-28 04:12:59 +00:00
{ //console input cursor
2018-01-24 12:13:32 +00:00
h = min ( 16 , f - > charheight ) ;
w = max ( 1 , h / 8 ) ;
d = 1 ;
memset ( out , 0xff , w * 4 * h ) ;
2017-09-20 11:27:13 +00:00
}
2018-01-28 04:12:59 +00:00
else if ( charidx = = 0xe00d )
{ //filled > arrow indicator (used by the menus)
h = min ( 16 , f - > charheight ) ;
w = ( h + 1 ) / 2 ;
d = 1 ;
memset ( out , 0xff , w * 4 * h ) ;
for ( i = 0 ; i < w ; i + + )
{
memset ( out + w * i + ( i + 1 ) , 0x0 , ( w - i - 1 ) * 4 ) ;
memset ( out + w * ( h - i - 1 ) + ( i + 1 ) , 0x0 , ( w - i - 1 ) * 4 ) ;
}
}
2017-09-20 11:27:13 +00:00
else
2018-01-24 12:13:32 +00:00
{
d = ( f - > charheight > = 11 ) ;
if ( d )
{ //two rows
h = 11 ;
if ( charidx > 0xffff )
n = 3 ;
else if ( charidx > 0xff )
n = 2 ;
else
n = 1 ;
w = n * 4 - 1 ;
n * = 2 ;
}
2017-09-20 11:27:13 +00:00
else
2018-01-24 12:13:32 +00:00
{ //single row. bye bye fixed-width.
if ( charidx > 0xfffff )
n = 6 ;
else if ( charidx > 0xffff )
n = 5 ;
else if ( charidx > 0xfff )
n = 4 ;
else if ( charidx > 0xff )
n = 3 ;
else
n = 2 ;
w = n * 4 - 1 ;
h = 5 ;
}
2017-09-20 11:27:13 +00:00
2018-01-24 12:13:32 +00:00
//figure out if we can get away with giving it a little more border to boost readability
b = ( h + 2 < f - > charheight ) ;
w + = b * 2 ;
h + = b * 2 ;
2017-09-20 11:27:13 +00:00
2018-01-24 12:13:32 +00:00
memset ( out , 0xff , sizeof ( out ) ) ;
2017-09-20 11:27:13 +00:00
2018-01-24 12:13:32 +00:00
for ( i = 0 ; i < n ; i + + )
{
g = hex [ 0xf & ( charidx > > ( i < < 2 ) ) ] ;
o = b + w * b ;
if ( d )
{ //stradle them over the two rows.
if ( i > = ( n > > 1 ) )
o + = 4 * ( n - i - 1 ) ;
else
{
o + = 4 * ( ( n > > 1 ) - i - 1 ) ;
o + = w * 6 ;
}
}
2017-09-20 11:27:13 +00:00
else
2018-01-24 12:13:32 +00:00
o + = 4 * ( n - i - 1 ) ; //just arrange them in order
for ( ; g ; g > > = 3 , o + = w )
2017-09-20 11:27:13 +00:00
{
2018-01-24 12:13:32 +00:00
if ( g & 4 ) out [ o + 0 ] = 0xff0000ff ;
if ( g & 2 ) out [ o + 1 ] = 0xff0000ff ;
if ( g & 1 ) out [ o + 2 ] = 0xff0000ff ;
2017-09-20 11:27:13 +00:00
}
}
2018-01-24 12:13:32 +00:00
d = 1 ;
2017-09-20 11:27:13 +00:00
}
2017-12-09 21:22:46 +00:00
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_RGBA , out , w , h , w * 4 ) ;
2017-09-20 11:27:13 +00:00
if ( c )
{
2018-01-24 12:13:32 +00:00
c - > advance = w + d ;
2017-09-20 11:27:13 +00:00
c - > left = 0 ;
c - > top = ( f - > charheight - h - 1 ) / 2 ;
}
return c ;
}
2016-09-01 14:31:24 +00:00
//loads the given charidx for the given font, importing from elsewhere if needed.
static struct charcache_s * Font_TryLoadGlyph ( font_t * f , CHARIDXTYPE charidx )
{
struct charcache_s * c ;
# if GEN_CONCHAR_GLYPHS != 0
if ( charidx > = 0xe000 & & charidx < = 0xe0ff )
{
int cpos = charidx & 0xff ;
unsigned int img [ 64 * 64 ] , * d ;
unsigned char * s ;
int scale ;
int x , y , ys ;
qbyte * draw_chars = W_GetLumpName ( " conchars " ) ;
if ( draw_chars )
{
d = img ;
s = draw_chars + 8 * ( cpos & 15 ) + 128 * 8 * ( cpos / 16 ) ;
scale = f - > charheight / 8 ;
if ( scale < 1 )
scale = 1 ;
if ( scale > 64 / 8 )
scale = 64 / 8 ;
for ( y = 0 ; y < 8 ; y + + )
{
for ( ys = 0 ; ys < scale ; ys + + )
{
for ( x = 0 ; x < 8 * scale ; x + + )
d [ x ] = d_8to24rgbtable [ s [ x / scale ] ] ;
d + = 8 * scale ;
}
s + = 128 ;
}
2017-12-09 21:22:46 +00:00
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_RGBA_SA , img , 8 * scale , 8 * scale , 8 * scale * 4 ) ;
2016-09-01 14:31:24 +00:00
if ( c )
{
c - > advance = 8 * scale ;
c - > left = 0 ;
c - > top = 7 * scale ;
}
return c ;
}
2018-01-24 12:13:32 +00:00
if ( ( charidx & 0x7f ) > 0x20 )
charidx & = 0x7f ;
2016-09-01 14:31:24 +00:00
}
# endif
2018-11-27 16:48:19 +00:00
# ifdef QUAKEHUD
2016-09-01 14:31:24 +00:00
if ( charidx > = 0xe100 & & charidx < = 0xe1ff )
{
qpic_t * wadimg ;
2017-12-09 21:22:46 +00:00
qbyte lumptype = 0 ;
2016-09-01 14:31:24 +00:00
unsigned char * src ;
unsigned int img [ 64 * 64 ] ;
int nw , nh ;
int x , y ;
unsigned int stepx , stepy ;
unsigned int srcx , srcy ;
size_t lumpsize = 0 ;
if ( charidx - 0xe100 > = sizeof ( imgs ) / sizeof ( imgs [ 0 ] ) )
wadimg = NULL ;
else
2017-12-09 21:22:46 +00:00
wadimg = W_GetLumpName ( imgs [ charidx - 0xe100 ] , & lumpsize , & lumptype ) ;
if ( wadimg & & lumptype = = TYP_QPIC & & lumpsize = = 8 + wadimg - > height * wadimg - > width )
2016-09-01 14:31:24 +00:00
{
nh = wadimg - > height ;
nw = wadimg - > width ;
while ( nh < f - > charheight )
{
nh * = 2 ;
nw * = 2 ;
}
if ( nh > f - > charheight )
{
nw = ( nw * f - > charheight ) / nh ;
nh = f - > charheight ;
}
stepy = 0x10000 * ( ( float ) wadimg - > height / nh ) ;
stepx = 0x10000 * ( ( float ) wadimg - > width / nw ) ;
if ( nh > 64 )
nh = 64 ;
if ( nw > 64 )
nw = 64 ;
srcy = 0 ;
for ( y = 0 ; y < nh ; y + + )
{
src = ( unsigned char * ) ( wadimg - > data ) ;
src + = wadimg - > width * ( srcy > > 16 ) ;
srcy + = stepy ;
srcx = 0 ;
for ( x = 0 ; x < nw ; x + + )
{
img [ x + y * 64 ] = d_8to24rgbtable [ src [ srcx > > 16 ] ] ;
srcx + = stepx ;
}
}
2017-12-09 21:22:46 +00:00
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_RGBA_SA , img , nw , nh , 64 * 4 ) ;
2016-09-01 14:31:24 +00:00
if ( c )
{
2018-11-27 16:48:19 +00:00
c - > flags = CHARF_FORCEWHITE ; //private glyph colours
2016-09-01 14:31:24 +00:00
c - > left = 0 ;
c - > top = f - > charheight - nh ;
c - > advance = nw ;
return c ;
}
}
}
2018-11-27 16:48:19 +00:00
# endif
2016-09-01 14:31:24 +00:00
/*make tab invisible*/
if ( charidx = = ' \t ' | | charidx = = ' \n ' )
{
2017-09-20 11:27:13 +00:00
c = & f - > chars [ charidx / FONTBLOCKSIZE ] [ charidx & FONTBLOCKMASK ] ;
2016-09-01 14:31:24 +00:00
c - > left = 0 ;
c - > advance = f - > charheight ;
c - > top = 0 ;
c - > texplane = 0 ;
c - > bmx = 0 ;
c - > bmy = 0 ;
c - > bmw = 0 ;
c - > bmh = 0 ;
return c ;
}
2017-12-09 21:22:46 +00:00
if ( f - > faces )
2016-09-01 14:31:24 +00:00
{
int file ;
2017-12-09 21:22:46 +00:00
for ( file = 0 ; file < f - > faces ; file + + )
2016-09-01 14:31:24 +00:00
{
2017-12-09 21:22:46 +00:00
fontface_t * qface = f - > face [ file ] ;
# ifdef AVAIL_FREETYPE
if ( qface - > ft . face )
{
FT_Face face = qface - > ft . face ;
2016-09-01 14:31:24 +00:00
2018-10-11 10:31:23 +00:00
if ( qface - > ft . activeheight ! = f - > charheight )
{
qface - > ft . activeheight = f - > charheight ;
2018-10-17 00:43:04 +00:00
if ( FT_HAS_FIXED_SIZES ( face ) & & ! FT_IS_SCALABLE ( face ) )
2018-10-11 10:31:23 +00:00
{ //freetype doesn't like scaling these for us, so we have to pick a usable size ourselves.
FT_Int best = 0 , s ;
int bestheight = 0 , h ;
for ( s = 0 ; s < qface - > ft . face - > num_fixed_sizes ; s + + )
{
h = qface - > ft . face - > available_sizes [ s ] . height ;
2018-10-17 00:43:04 +00:00
//always try to pick the smallest size that is also >= our target size
if ( ( h > bestheight & & bestheight < f - > charheight ) | | ( h > = f - > charheight & & h < bestheight ) )
2018-10-11 10:31:23 +00:00
{
bestheight = h ;
best = s ;
}
}
qface - > ft . actualsize = qface - > ft . face - > available_sizes [ best ] . height ;
pFT_Select_Size ( face , best ) ;
}
else
2017-12-09 21:22:46 +00:00
{
2018-10-11 10:31:23 +00:00
pFT_Set_Pixel_Sizes ( face , 0 , f - > charheight ) ;
qface - > ft . actualsize = f - > charheight ;
2017-12-09 21:22:46 +00:00
}
2018-10-11 10:31:23 +00:00
}
2017-12-09 21:22:46 +00:00
if ( charidx = = 0xfffe | | pFT_Get_Char_Index ( face , charidx ) ) //ignore glyph 0 (undefined)
if ( pFT_Load_Char ( face , charidx , FT_LOAD_RENDER | FT_LOAD_COLOR ) = = 0 )
{
FT_GlyphSlot slot ;
FT_Bitmap * bm ;
slot = face - > glyph ;
bm = & slot - > bitmap ;
2018-10-11 10:31:23 +00:00
if ( qface - > ft . activeheight ! = qface - > ft . actualsize )
2017-12-09 21:22:46 +00:00
{
2018-10-11 10:31:23 +00:00
//I'm just going to assume full-height raster glyphs here. I'm sure I'll be proven wrong some time but w/e.
2018-10-14 16:16:34 +00:00
int nw , nh ;
if ( bm - > rows )
{
nh = f - > charheight ;
nw = ( bm - > width * nh ) / bm - > rows ;
}
else
2018-10-17 00:43:04 +00:00
nw = f - > charheight , nh = 0 ;
if ( ! nw | | ! nh )
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_GRAY , NULL , nw , nh , 0 ) ;
else if ( bm - > pixel_mode = = FT_PIXEL_MODE_BGRA )
2018-10-11 10:31:23 +00:00
{
unsigned int * out = alloca ( nw * nh * sizeof ( * out ) ) ;
Image_ResampleTexture ( ( void * ) bm - > buffer , bm - > width , bm - > rows , out , nw , nh ) ;
c = Font_LoadGlyphData ( f , charidx , bm - > pixel_mode , out , nw , nh , nw * sizeof ( * out ) ) ;
}
else if ( bm - > pixel_mode = = FT_PIXEL_MODE_GRAY )
{
unsigned char * out = alloca ( nw * nh * sizeof ( * out ) ) ;
Image_ResampleTexture8 ( ( void * ) bm - > buffer , bm - > width , bm - > rows , out , nw , nh ) ;
c = Font_LoadGlyphData ( f , charidx , bm - > pixel_mode , out , nw , nh , nw * sizeof ( * out ) ) ;
}
else
c = NULL ;
2017-12-09 21:22:46 +00:00
if ( c )
{
2018-10-11 10:31:23 +00:00
c - > advance = nw ;
2017-12-09 21:22:46 +00:00
c - > left = 0 ;
c - > top = 0 ;
return c ;
}
}
else
{
2018-10-11 10:31:23 +00:00
c = Font_LoadGlyphData ( f , charidx , bm - > pixel_mode , bm - > buffer , bm - > width , bm - > rows , bm - > pitch ) ;
2017-12-09 21:22:46 +00:00
if ( c )
{
c - > advance = slot - > advance . x > > 6 ;
c - > left = slot - > bitmap_left ;
c - > top = f - > charheight * 3 / 4 - slot - > bitmap_top ;
return c ;
}
}
}
}
else
# endif
# ifdef HALFLIFEMODELS
if ( qface - > halflife )
{
size_t glyph = charidx ;
if ( glyph > 0xe000 )
glyph - = 0xe000 ;
if ( glyph < 0x100 )
2016-09-01 14:31:24 +00:00
{
2017-12-09 21:22:46 +00:00
int gw = qface - > halflife - > chartab [ glyph ] . width ;
int gh = qface - > halflife - > fontheight1 ;
qbyte * in = qface - > halflife - > data + 256 * qface - > halflife - > chartab [ glyph ] . y + qface - > halflife - > chartab [ glyph ] . x ;
qbyte * pal = qface - > halflife - > data + 256 * qface - > halflife - > imgheight + 2 ;
qbyte * out = alloca ( gw * gh * 4 ) ;
int x , y ;
for ( y = 0 ; y < gh ; y + + , in + = 256 - gw )
for ( x = 0 ; x < gw ; x + + , in + + )
{
if ( * in = = 0xff )
{
out [ ( x + y * gw ) * 4 + 0 ] = 0 ;
out [ ( x + y * gw ) * 4 + 1 ] = 0 ;
out [ ( x + y * gw ) * 4 + 2 ] = 0 ;
out [ ( x + y * gw ) * 4 + 3 ] = 0 ;
}
else
{
out [ ( x + y * gw ) * 4 + 0 ] = pal [ * in * 3 + 0 ] ;
out [ ( x + y * gw ) * 4 + 1 ] = pal [ * in * 3 + 1 ] ;
out [ ( x + y * gw ) * 4 + 2 ] = pal [ * in * 3 + 2 ] ;
out [ ( x + y * gw ) * 4 + 3 ] = 0xff ;
}
}
if ( f - > charheight ! = gh )
{
int ngw = ( gw * f - > charheight ) / gh ;
int ngh = f - > charheight ;
qbyte * out2 = alloca ( ngw * ngh * 4 ) ;
if ( ngw & & ngh )
Image_ResampleTexture ( ( unsigned int * ) out , gw , gh , ( unsigned int * ) out2 , ngw , ngh ) ;
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_RGBA , out2 , ngw , ngh , ngw * 4 ) ;
gw = ngw ;
}
else
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_RGBA , out , gw , gh , gw * 4 ) ;
if ( c )
{
c - > advance = gw ;
c - > left = 0 ;
c - > top = 0 ;
return c ;
}
2017-09-20 11:27:13 +00:00
}
2017-12-09 21:22:46 +00:00
}
else
# endif
if ( qface - > horiz . data )
{
# if 1
size_t maxchar = 256 ;
int gw = qface - > horiz . width / maxchar ;
# else
int gw = qface - > horiz . height - ( qface - > horiz . height / 12 ) ;
size_t maxchar = qface - > horiz . width / gw ;
# endif
size_t glyph = charidx /* - qface->horiz.firstcodepoint*/ ;
if ( glyph < maxchar )
2017-09-20 11:27:13 +00:00
{
2017-12-09 21:22:46 +00:00
unsigned int * glyphdata = ( unsigned int * ) qface - > horiz . data + glyph * gw ;
int gh = qface - > horiz . height ;
int gs = qface - > horiz . width ;
unsigned int * out = glyphdata ;
while ( gw > = 1 )
2017-09-20 11:27:13 +00:00
{
2017-12-09 21:22:46 +00:00
int y ;
gw - - ; //see if we can strip this column
for ( y = 0 ; y < gh ; y + + )
if ( glyphdata [ gw + y * gs ] & 0x00ffffff )
break ;
if ( y < gh )
2017-09-20 11:27:13 +00:00
{
2017-12-09 21:22:46 +00:00
gw + + ;
break ;
2017-09-20 11:27:13 +00:00
}
}
2017-12-09 21:22:46 +00:00
if ( f - > charheight ! = gh )
2017-09-20 11:27:13 +00:00
{
2017-12-09 21:22:46 +00:00
int ngw = ( gw * f - > charheight ) / gh ;
int ngh = f - > charheight ;
int x , y ;
unsigned int * out2 = alloca ( ngw * ngh * 4 ) ;
if ( ngw & & ngh )
{ //we need to repack the input, because Image_ResampleTexture can't handle strides
unsigned int * out1 = alloca ( gw * gh * 4 ) ;
for ( y = 0 ; y < gh ; y + + )
for ( x = 0 ; x < gw ; x + + )
out1 [ x + y * gw ] = out [ x + y * gs ] ;
Image_ResampleTexture ( ( unsigned int * ) out1 , gw , gh , ( unsigned int * ) out2 , ngw , ngh ) ;
2017-09-20 11:27:13 +00:00
}
2017-12-09 21:22:46 +00:00
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_RGBA , out2 , ngw , ngh , ngw * 4 ) ;
gw = ngw ;
}
else
c = Font_LoadGlyphData ( f , charidx , FT_PIXEL_MODE_RGBA , out , gw , gh , gs * 4 ) ;
if ( ! gw ) //for invisble glyphs (eg: space), we attempt to ensure that there's some substance there. missing spaces is weird.
gw = gh / 3 ;
if ( c )
{
c - > advance = gw ;
c - > left = 0 ;
c - > top = 0 ;
return c ;
2017-09-20 11:27:13 +00:00
}
2016-09-01 14:31:24 +00:00
}
2017-12-09 21:22:46 +00:00
}
2016-09-01 14:31:24 +00:00
}
}
if ( charidx = = ' \r ' )
2017-09-20 11:27:13 +00:00
return Font_CopyChar ( f , charidx | 0xe000 , charidx ) ;
2016-09-01 14:31:24 +00:00
return NULL ;
}
//obtains a cached char, null if not cached
static struct charcache_s * Font_GetChar ( font_t * f , unsigned int codepoint )
{
CHARIDXTYPE charidx ;
struct charcache_s * c ;
2017-09-20 11:27:13 +00:00
if ( codepoint > FONT_MAXCHARS )
2016-09-01 14:31:24 +00:00
charidx = 0xfffd ;
else
charidx = codepoint ;
2017-09-20 11:27:13 +00:00
c = Font_GetCharIfLoaded ( f , charidx ) ;
if ( ! c )
2016-09-01 14:31:24 +00:00
{
if ( charidx > = TRACKERFIRST & & charidx < TRACKERFIRST + 100 )
{
static struct charcache_s tc ;
tc . texplane = TRACKERIMAGE ;
fontplanes . trackerimage = Font_GetTrackerImage ( charidx - TRACKERFIRST ) ;
if ( ! fontplanes . trackerimage )
return Font_GetChar ( f , ' ? ' ) ;
tc . advance = fontplanes . trackerimage - > width * ( ( float ) f - > charheight / fontplanes . trackerimage - > height ) ;
return & tc ;
}
//not cached, can't get.
c = Font_TryLoadGlyph ( f , charidx ) ;
if ( ! c & & charidx > = 0x400 & & charidx < = 0x45f )
{ //apparently there's a lot of russian players out there.
//if we just replace all their chars with a '?', they're gonna get pissed.
//so lets at least attempt to provide some default mapping that makes sense even if they don't have a full font.
//koi8-u is a mapping useful with 7-bit email because the message is still vaugely readable in latin if the high bits get truncated.
//not being a language specialist, I'm just going to use that mapping, with the high bit truncated to ascii (which mostly exists in the quake charset).
//this exact table is from ezquake. because I'm too lazy to figure out the proper mapping. (beware of triglyphs)
static char * wc2koi_table =
" ?3??4?67?? " " ?? " " ?? " " >? "
" abwgdevzijklmnop "
" rstufhc~{}/yx|`q "
" ABWGDEVZIJKLMNOP "
" RSTUFHC^[]_YX \\ @Q "
" ?#??$?&'?? " " ?? " " ??.? " ;
charidx = wc2koi_table [ charidx - 0x400 ] ;
if ( charidx ! = ' ? ' )
{
2017-09-20 11:27:13 +00:00
c = Font_GetCharIfLoaded ( f , charidx ) ;
if ( ! c )
2016-09-01 14:31:24 +00:00
c = Font_TryLoadGlyph ( f , charidx ) ;
}
}
2017-09-20 11:27:13 +00:00
if ( ! c )
c = Font_LoadPlaceholderGlyph ( f , charidx ) ;
2016-09-01 14:31:24 +00:00
if ( ! c )
{
charidx = 0xfffd ; //unicode's replacement char
2017-09-20 11:27:13 +00:00
c = Font_GetCharIfLoaded ( f , charidx ) ;
if ( ! c )
2016-09-01 14:31:24 +00:00
c = Font_TryLoadGlyph ( f , charidx ) ;
}
if ( ! c )
{
charidx = ' ? ' ; //meh
2017-09-20 11:27:13 +00:00
c = Font_GetCharIfLoaded ( f , charidx ) ;
if ( ! c )
2016-09-01 14:31:24 +00:00
c = Font_TryLoadGlyph ( f , charidx ) ;
}
}
return c ;
}
2017-12-09 21:22:46 +00:00
qboolean Font_LoadHorizontalFont ( struct font_s * f , int fheight , const char * fontfilename )
{ //halflife-style.
fontface_t * qface ;
void * rawdata ;
qofs_t rawsize ;
qbyte * rgbadata = NULL ;
int width = 0 , height = 0 ;
2018-09-29 17:31:58 +00:00
uploadfmt_t format ;
2017-12-09 21:22:46 +00:00
if ( fheight < 1 )
fheight = 1 ;
//ran out of font slots.
if ( f - > faces = = MAX_FACES )
return false ;
for ( qface = faces ; qface ; qface = qface - > fnext )
{
if ( ! strcmp ( qface - > name , fontfilename ) & & qface - > horiz . data )
{
qface - > refs + + ;
f - > face [ f - > faces + + ] = qface ;
return true ;
}
}
rawdata = FS_MallocFile ( fontfilename , FS_GAME , & rawsize ) ;
if ( rawdata )
2018-09-29 17:31:58 +00:00
rgbadata = ReadRawImageFile ( rawdata , rawsize , & width , & height , & format , true , fontfilename ) ;
2017-12-09 21:22:46 +00:00
FS_FreeFile ( rawdata ) ;
if ( rgbadata )
{
/*success!*/
qface = Z_Malloc ( sizeof ( * qface ) ) ;
qface - > flink = & faces ;
qface - > fnext = * qface - > flink ;
* qface - > flink = qface ;
if ( qface - > fnext )
qface - > fnext - > flink = & qface - > fnext ;
qface - > horiz . data = rgbadata ;
qface - > horiz . width = width ;
qface - > horiz . height = height ;
qface - > refs + + ;
Q_strncpyz ( qface - > name , fontfilename , sizeof ( qface - > name ) ) ;
f - > face [ f - > faces + + ] = qface ;
return true ;
}
return false ;
}
# ifdef AVAIL_FREETYPE
2016-09-01 14:31:24 +00:00
qboolean Font_LoadFreeTypeFont ( struct font_s * f , int height , const char * fontfilename )
{
2017-12-09 21:22:46 +00:00
fontface_t * qface ;
2016-09-01 14:31:24 +00:00
FT_Face face = NULL ;
FT_Error error ;
flocation_t loc ;
void * fbase = NULL ;
if ( ! * fontfilename )
return false ;
2017-09-20 11:27:13 +00:00
if ( height < 1 )
height = 1 ;
2016-09-01 14:31:24 +00:00
//ran out of font slots.
2017-12-09 21:22:46 +00:00
if ( f - > faces = = MAX_FACES )
2016-09-01 14:31:24 +00:00
return false ;
2017-12-09 21:22:46 +00:00
for ( qface = faces ; qface ; qface = qface - > fnext )
2016-09-01 14:31:24 +00:00
{
2017-12-09 21:22:46 +00:00
if ( ! strcmp ( qface - > name , fontfilename ) & & qface - > ft . face )
2016-09-01 14:31:24 +00:00
{
qface - > refs + + ;
2017-12-09 21:22:46 +00:00
f - > face [ f - > faces + + ] = qface ;
2016-09-01 14:31:24 +00:00
return true ;
}
}
if ( ! fontlib )
{
2018-03-24 04:02:09 +00:00
# ifndef FREETYPE_STATIC
2016-09-01 14:31:24 +00:00
dllfunction_t ft2funcs [ ] =
{
{ ( void * * ) & pFT_Init_FreeType , " FT_Init_FreeType " } ,
{ ( void * * ) & pFT_Load_Char , " FT_Load_Char " } ,
{ ( void * * ) & pFT_Get_Char_Index , " FT_Get_Char_Index " } ,
{ ( void * * ) & pFT_Set_Pixel_Sizes , " FT_Set_Pixel_Sizes " } ,
2017-09-20 11:27:13 +00:00
{ ( void * * ) & pFT_Select_Size , " FT_Select_Size " } ,
2016-09-01 14:31:24 +00:00
{ ( void * * ) & pFT_New_Face , " FT_New_Face " } ,
{ ( void * * ) & pFT_New_Memory_Face , " FT_New_Memory_Face " } ,
{ ( void * * ) & pFT_Init_FreeType , " FT_Init_FreeType " } ,
{ ( void * * ) & pFT_Done_Face , " FT_Done_Face " } ,
{ NULL , NULL }
} ;
if ( triedtoloadfreetype )
return false ;
triedtoloadfreetype = true ;
# ifdef _WIN32
2017-09-20 11:27:13 +00:00
fontmodule = Sys_LoadLibrary ( " libfreetype-6 " , ft2funcs ) ;
if ( ! fontmodule )
fontmodule = Sys_LoadLibrary ( " freetype6 " , ft2funcs ) ;
2016-09-01 14:31:24 +00:00
# else
fontmodule = Sys_LoadLibrary ( " libfreetype.so.6 " , ft2funcs ) ;
# endif
if ( ! fontmodule )
{
Con_DPrintf ( " Couldn't load freetype library. \n " ) ;
return false ;
}
2017-09-20 11:27:13 +00:00
# endif
2016-09-01 14:31:24 +00:00
error = pFT_Init_FreeType ( & fontlib ) ;
if ( error )
{
Con_Printf ( " FT_Init_FreeType failed. \n " ) ;
2018-03-24 04:02:09 +00:00
# ifndef FREETYPE_STATIC
2016-09-01 14:31:24 +00:00
Sys_CloseLibrary ( fontmodule ) ;
2018-03-24 04:02:09 +00:00
# endif
2016-09-01 14:31:24 +00:00
return false ;
}
/*any other errors leave freetype open*/
}
error = FT_Err_Cannot_Open_Resource ;
2017-08-10 19:40:47 +00:00
if ( FS_FLocateFile ( fontfilename , FSLF_IFFOUND , & loc ) | | FS_FLocateFile ( va ( " %s.ttf " , fontfilename ) , FSLF_IFFOUND , & loc ) )
2016-09-01 14:31:24 +00:00
{
if ( * loc . rawname & & ! loc . offset )
{
fbase = NULL ;
/*File is directly fopenable with no bias (not in a pk3/pak). Use the system-path form, so we don't have to eat the memory cost*/
error = pFT_New_Face ( fontlib , loc . rawname , 0 , & face ) ;
}
else
{
/*File is inside an archive, we need to read it and pass it as memory (and keep it available)*/
vfsfile_t * f ;
f = FS_OpenReadLocation ( & loc ) ;
if ( f & & loc . len > 0 )
{
fbase = BZ_Malloc ( loc . len ) ;
VFS_READ ( f , fbase , loc . len ) ;
VFS_CLOSE ( f ) ;
error = pFT_New_Memory_Face ( fontlib , fbase , loc . len , 0 , & face ) ;
}
}
}
# if defined(_WIN32)
if ( error )
{
static qboolean firsttime = true ;
static char fontdir [ MAX_OSPATH ] ;
if ( firsttime )
{
HRESULT ( WINAPI * dSHGetFolderPath ) ( HWND hwndOwner , int nFolder , HANDLE hToken , DWORD dwFlags , LPTSTR pszPath ) ;
dllfunction_t shfolderfuncs [ ] =
{
{ ( void * * ) & dSHGetFolderPath , " SHGetFolderPathA " } ,
{ NULL , NULL }
} ;
dllhandle_t * shfolder = Sys_LoadLibrary ( " shfolder.dll " , shfolderfuncs ) ;
firsttime = false ;
if ( shfolder )
{
// 0x14 == CSIDL_FONTS
if ( dSHGetFolderPath ( NULL , 0x14 , NULL , 0 , fontdir ) ! = S_OK )
* fontdir = 0 ;
Sys_CloseLibrary ( shfolder ) ;
}
}
if ( * fontdir )
{
error = pFT_New_Face ( fontlib , va ( " %s/%s " , fontdir , fontfilename ) , 0 , & face ) ;
if ( error )
error = pFT_New_Face ( fontlib , va ( " %s/%s.ttf " , fontdir , fontfilename ) , 0 , & face ) ;
}
}
2018-10-11 10:31:23 +00:00
# else
if ( error )
{ //eg: /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf
error = pFT_New_Face ( fontlib , va ( " /usr/share/fonts/%s " , fontfilename ) , 0 , & face ) ;
if ( error )
error = pFT_New_Face ( fontlib , va ( " /usr/share/fonts/truetype/%s.ttf " , fontfilename ) , 0 , & face ) ;
2018-10-23 07:09:06 +00:00
if ( error ) //just to give a chance of the same names working on more than one os, with the right package installed.
error = pFT_New_Face ( fontlib , va ( " /usr/share/fonts/truetype/msttcorefonts/%s.ttf " , fontfilename ) , 0 , & face ) ;
2018-10-11 10:31:23 +00:00
}
2016-09-01 14:31:24 +00:00
# endif
if ( ! error )
{
2018-10-17 00:43:04 +00:00
if ( FT_HAS_FIXED_SIZES ( face ) & & ! FT_IS_SCALABLE ( face ) )
2017-09-20 11:27:13 +00:00
{
height = 0 ; //will need to rescale manually I guess
error = pFT_Select_Size ( face , 0 ) ;
}
else
error = pFT_Set_Pixel_Sizes ( face , 0 , height ) ;
2016-09-01 14:31:24 +00:00
if ( ! error )
{
/*success!*/
qface = Z_Malloc ( sizeof ( * qface ) ) ;
2017-12-09 21:22:46 +00:00
qface - > flink = & faces ;
2016-09-01 14:31:24 +00:00
qface - > fnext = * qface - > flink ;
* qface - > flink = qface ;
if ( qface - > fnext )
qface - > fnext - > flink = & qface - > fnext ;
2017-12-09 21:22:46 +00:00
qface - > ft . face = face ;
2018-10-11 10:31:23 +00:00
qface - > ft . activeheight = qface - > ft . actualsize = height ;
2017-12-09 21:22:46 +00:00
qface - > ft . membuf = fbase ;
2016-09-01 14:31:24 +00:00
qface - > refs + + ;
Q_strncpyz ( qface - > name , fontfilename , sizeof ( qface - > name ) ) ;
2017-12-09 21:22:46 +00:00
f - > face [ f - > faces + + ] = qface ;
2016-09-01 14:31:24 +00:00
return true ;
}
}
if ( error & & error ! = FT_Err_Cannot_Open_Resource )
Con_Printf ( " Freetype error: %i \n " , error ) ;
if ( fbase )
BZ_Free ( fbase ) ;
return false ;
}
2017-12-09 21:22:46 +00:00
# endif
2016-09-01 14:31:24 +00:00
static texid_t Font_LoadReplacementConchars ( void )
{
texid_t tex ;
//q1 replacement
2017-12-20 08:23:41 +00:00
tex = R_LoadHiResTexture ( " gfx/conchars.lmp " , NULL , ( r_font_linear . ival ? IF_LINEAR : IF_NEAREST ) | IF_PREMULTIPLYALPHA | IF_LOADNOW | IF_UIPIC | IF_NOMIPMAP | IF_NOGAMMA ) ;
2016-09-01 14:31:24 +00:00
TEXDOWAIT ( tex ) ;
if ( TEXLOADED ( tex ) )
return tex ;
//q2
2017-12-09 21:22:46 +00:00
tex = R_LoadHiResTexture ( " pics/conchars.pcx " , NULL , ( r_font_linear . ival ? IF_LINEAR : IF_NEAREST ) | IF_PREMULTIPLYALPHA | IF_LOADNOW | IF_UIPIC | IF_NOMIPMAP | IF_NOGAMMA ) ;
2016-09-01 14:31:24 +00:00
TEXDOWAIT ( tex ) ;
if ( TEXLOADED ( tex ) )
return tex ;
//q3
2017-12-09 21:22:46 +00:00
tex = R_LoadHiResTexture ( " gfx/2d/bigchars.tga " , NULL , ( r_font_linear . ival ? IF_LINEAR : IF_NEAREST ) | IF_PREMULTIPLYALPHA | IF_LOADNOW | IF_UIPIC | IF_NOMIPMAP | IF_NOGAMMA ) ;
2016-09-01 14:31:24 +00:00
TEXDOWAIT ( tex ) ;
if ( TEXLOADED ( tex ) )
return tex ;
return r_nulltex ;
}
static texid_t Font_LoadQuakeConchars ( void )
{
/*unsigned int i;
qbyte * lump ;
lump = W_SafeGetLumpName ( " conchars " ) ;
if ( lump )
{
// add ocrana leds
if ( con_ocranaleds . ival )
{
if ( con_ocranaleds . ival ! = 2 | | QCRC_Block ( lump , 128 * 128 ) = = 798 )
AddOcranaLEDsIndexed ( lump , 128 , 128 ) ;
}
for ( i = 0 ; i < 128 * 128 ; i + + )
if ( lump [ i ] = = 0 )
lump [ i ] = 255 ; // proper transparent color
return R_LoadTexture8 ( " charset " , 128 , 128 , ( void * ) lump , IF_LOADNOW | IF_UIPIC | IF_NOMIPMAP | IF_NOGAMMA , 1 ) ;
} */
return r_nulltex ;
}
2017-08-29 02:29:06 +00:00
# ifdef HEXEN2
2016-09-01 14:31:24 +00:00
static texid_t Font_LoadHexen2Conchars ( qboolean iso88591 )
{
//gulp... so it's come to this has it? rework the hexen2 conchars into the q1 system.
texid_t tex ;
unsigned int i , x ;
unsigned char * tempchars ;
unsigned char * in , * out , * outbuf ;
FS_LoadFile ( " gfx/menu/conchars.lmp " , ( void * * ) & tempchars ) ;
/*hexen2's conchars are arranged 32-wide, 16 high.
the upper 8 rows are 256 8859 - 1 chars
the lower 8 rows are a separate set of recoloured 8859 - 1 chars .
if we ' re loading for the fallback then we ' re loading this data for quake compatibility ,
so we grab only the first 4 rows of each set of chars ( 128 low chars , 128 high chars ) .
if we ' re loading a proper charset , then we load only the first set of chars , we can recolour the rest anyway ( com_parseutf8 will do so anyway ) .
as a final note , parsing iso8859 - 1 french / german / etc as utf8 will generally result in decoding errors which can gracefully revert to 8859 - 1 safely . If this premise fails too much , we can always change the parser for different charsets - the engine always uses unicode and thus 8859 - 1 internally .
*/
if ( tempchars )
{
outbuf = BZ_Malloc ( 8 * 8 * 256 * 8 ) ;
out = outbuf ;
i = 0 ;
/*read the low chars*/
for ( ; i < 8 * 8 * 1 ; i + = 1 )
{
if ( i & ( 1 < < 3 ) )
in = tempchars + ( i > > 3 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 - 256 * 4 + 128 ;
else
in = tempchars + ( i > > 3 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 ;
for ( x = 0 ; x < 16 * 8 ; x + + )
* out + + = * in + + ;
}
if ( iso88591 )
{
/*read the non 8859-1 quake-compat control chars*/
for ( ; i < 8 * 8 * 1 + 16 ; i + = 1 )
{
if ( i & ( 1 < < 3 ) )
in = tempchars + 128 * 128 + ( ( i > > 3 ) & 7 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 - 256 * 4 + 128 ;
else
in = tempchars + 128 * 128 + ( ( i > > 3 ) & 7 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 ;
for ( x = 0 ; x < 16 * 8 ; x + + )
* out + + = * in + + ;
}
/*read the final low chars (final if 8859-1 anyway)*/
for ( ; i < 8 * 8 * 2 ; i + = 1 )
{
if ( i & ( 1 < < 3 ) )
in = tempchars + ( i > > 3 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 - 256 * 4 + 128 ;
else
in = tempchars + ( i > > 3 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 ;
for ( x = 0 ; x < 16 * 8 ; x + + )
* out + + = * in + + ;
}
}
else
{
/*read the high chars*/
for ( ; i < 8 * 8 * 2 ; i + = 1 )
{
if ( i & ( 1 < < 3 ) )
in = tempchars + 128 * 128 + ( ( i > > 3 ) & 15 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 - 256 * 4 + 128 ;
else
in = tempchars + 128 * 128 + ( ( i > > 3 ) & 15 ) * 16 * 8 * 8 + ( i & 7 ) * 32 * 8 ;
for ( x = 0 ; x < 16 * 8 ; x + + )
* out + + = * in + + ;
}
}
FS_FreeFile ( tempchars ) ;
// add ocrana leds
if ( ! iso88591 & & con_ocranaleds . value & & con_ocranaleds . value ! = 2 )
AddOcranaLEDsIndexed ( outbuf , 128 , 128 ) ;
for ( i = 0 ; i < 128 * 128 ; i + + )
if ( outbuf [ i ] = = 0 )
outbuf [ i ] = 255 ; // proper transparent color
2017-12-09 21:22:46 +00:00
tex = R_LoadTexture8 ( iso88591 ? " gfx/menu/8859-1.lmp " : " charset " , 128 , 128 , outbuf , IF_PREMULTIPLYALPHA | IF_LOADNOW | IF_UIPIC | IF_NOMIPMAP | IF_NOGAMMA , 1 ) ;
2016-09-01 14:31:24 +00:00
Z_Free ( outbuf ) ;
return tex ;
}
return r_nulltex ;
}
2017-08-29 02:29:06 +00:00
# endif
2016-09-01 14:31:24 +00:00
FTE_ALIGN ( 4 ) qbyte default_conchar [ /*11356*/ ] =
{
# include "lhfont.h"
} ;
static void Font_CopyGlyph ( int src , int dst , void * data )
{
int glyphsize = 16 ;
int y ;
int x ;
char * srcptr = ( char * ) data + ( src & 15 ) * glyphsize * 4 + ( src > > 4 ) * glyphsize * 256 * 4 ;
char * dstptr = ( char * ) data + ( dst & 15 ) * glyphsize * 4 + ( dst > > 4 ) * glyphsize * 256 * 4 ;
for ( y = 0 ; y < glyphsize ; y + + )
{
for ( x = 0 ; x < glyphsize ; x + + )
{
dstptr [ x * 4 + 0 ] = srcptr [ x * 4 + 0 ] ;
dstptr [ x * 4 + 1 ] = srcptr [ x * 4 + 1 ] ;
dstptr [ x * 4 + 2 ] = srcptr [ x * 4 + 2 ] ;
dstptr [ x * 4 + 3 ] = srcptr [ x * 4 + 3 ] ;
}
dstptr + = 256 * 4 ;
srcptr + = 256 * 4 ;
}
}
static texid_t Font_LoadFallbackConchars ( void )
{
texid_t tex ;
int width , height ;
unsigned int i ;
qbyte * lump ;
2018-09-29 17:31:58 +00:00
uploadfmt_t format ;
lump = ReadTargaFile ( default_conchar , sizeof ( default_conchar ) , & width , & height , & format , false , PTI_INVALID ) ;
2018-10-11 10:31:23 +00:00
if ( ! lump | | ( format ! = PTI_RGBX8 & & format ! = PTI_RGBA8 & & format ! = PTI_LLLX8 ) )
Sys_Error ( " Corrupt internal drawchars (%i) " , format ) ;
2016-09-01 14:31:24 +00:00
/*convert greyscale to alpha*/
for ( i = 0 ; i < width * height ; i + + )
{
lump [ i * 4 + 3 ] = lump [ i * 4 ] ;
lump [ i * 4 + 0 ] = 255 ;
lump [ i * 4 + 1 ] = 255 ;
lump [ i * 4 + 2 ] = 255 ;
}
if ( width = = 256 & & height = = 256 )
{ //make up some scroll-bar/download-progress-bar chars, so that webgl doesn't look so buggy with the initial pak file(s).
Font_CopyGlyph ( ' [ ' , 128 , lump ) ;
Font_CopyGlyph ( ' - ' , 129 , lump ) ;
Font_CopyGlyph ( ' ] ' , 130 , lump ) ;
2018-11-19 06:37:25 +00:00
Font_CopyGlyph ( ' | ' , 131 , lump ) ;
Font_CopyGlyph ( ' > ' , 13 , lump ) ;
2016-09-01 14:31:24 +00:00
}
2017-12-09 21:22:46 +00:00
tex = R_LoadTexture32 ( " charset " , width , height , ( void * ) lump , IF_PREMULTIPLYALPHA | IF_LOADNOW | IF_UIPIC | IF_NOMIPMAP | IF_NOGAMMA ) ;
2016-09-01 14:31:24 +00:00
BZ_Free ( lump ) ;
return tex ;
}
2018-11-19 06:37:25 +00:00
enum fontfmt_e
{
FMT_AUTO , //freetype, or quake
FMT_QUAKE , //first is default
FMT_ISO88591 , //latin-1 (first 256 chars of unicode too, c1 glyphs are usually invisible)
FMT_WINDOWS1252 , //variation of latin-1 with extra glyphs
FMT_KOI8U , //image is 16*16 koi8-u codepage.
FMT_HORIZONTAL , //unicode, charcount=width/(height-2). single strip of chars, like halflife.
} ;
2016-09-01 14:31:24 +00:00
/*loads a fallback image. not allowed to fail (use syserror if needed)*/
2018-11-19 06:37:25 +00:00
static texid_t Font_LoadDefaultConchars ( enum fontfmt_e * fmt )
2016-09-01 14:31:24 +00:00
{
texid_t tex ;
tex = Font_LoadReplacementConchars ( ) ;
if ( TEXLOADED ( tex ) )
return tex ;
tex = Font_LoadQuakeConchars ( ) ;
if ( tex & & tex - > status = = TEX_LOADING )
COM_WorkerPartialSync ( tex , & tex - > status , TEX_LOADING ) ;
if ( TEXLOADED ( tex ) )
return tex ;
2017-09-20 11:27:13 +00:00
# ifdef HEXEN2
2016-09-01 14:31:24 +00:00
tex = Font_LoadHexen2Conchars ( true ) ;
if ( tex & & tex - > status = = TEX_LOADING )
COM_WorkerPartialSync ( tex , & tex - > status , TEX_LOADING ) ;
if ( TEXLOADED ( tex ) )
2018-11-19 06:37:25 +00:00
{
* fmt = FMT_ISO88591 ;
2016-09-01 14:31:24 +00:00
return tex ;
2018-11-19 06:37:25 +00:00
}
2017-09-20 11:27:13 +00:00
# endif
2016-09-01 14:31:24 +00:00
tex = Font_LoadFallbackConchars ( ) ;
if ( tex & & tex - > status = = TEX_LOADING )
COM_WorkerPartialSync ( tex , & tex - > status , TEX_LOADING ) ;
if ( TEXLOADED ( tex ) )
2018-11-19 06:37:25 +00:00
{
* fmt = FMT_QUAKE ;
2016-09-01 14:31:24 +00:00
return tex ;
2018-11-19 06:37:25 +00:00
}
2016-09-01 14:31:24 +00:00
Sys_Error ( " Unable to load any conchars \n " ) ;
}
typedef struct
{
short width ;
short height ;
short leftoffset ; // pixels to the left of origin
short topoffset ; // pixels below the origin
int columnofs [ 1 ] ;
} doompatch_t ;
typedef struct
{
unsigned char topdelta ; // -1 is the last post in a column
unsigned char length ; // length data bytes follows
} doomcolumn_t ;
void Doom_ExpandPatch ( doompatch_t * p , unsigned char * b , int stride )
{
doomcolumn_t * col ;
unsigned char * src , * dst ;
int x , y ;
for ( x = 0 ; x < p - > width ; x + + )
{
col = ( doomcolumn_t * ) ( ( unsigned char * ) p + p - > columnofs [ x ] ) ;
while ( col - > topdelta ! = 0xff )
{
//exploit protection
if ( col - > length + col - > topdelta > p - > height )
break ;
src = ( unsigned char * ) col + 2 ; /*why 3? why not, I suppose*/
dst = b + stride * col - > topdelta ;
for ( y = 0 ; y < col - > length ; y + + )
{
* dst = * src + + ;
dst + = stride ;
}
src + + ;
col = ( doomcolumn_t * ) ( ( unsigned char * ) col + col - > length + 4 ) ;
}
b + + ;
}
}
//creates a new font object from the given file, with each text row with the given height.
//width is implicit and scales with height and choice of font.
2018-06-02 08:55:57 +00:00
struct font_s * Font_LoadFont ( const char * fontfilename , float vheight )
2016-09-01 14:31:24 +00:00
{
struct font_s * f ;
int i = 0 ;
int defaultplane ;
char * aname ;
char * parms ;
int height = ( ( vheight * vid . rotpixelheight ) / vid . height ) + 0.5 ;
2018-08-04 19:00:19 +00:00
char facename [ MAX_QPATH * 12 ] ;
2017-09-20 11:27:13 +00:00
struct charcache_s * c ;
2018-06-02 08:55:57 +00:00
float aspect = 1 ;
2018-11-19 06:37:25 +00:00
enum fontfmt_e fmt = FMT_AUTO ;
2016-09-01 14:31:24 +00:00
2018-08-04 19:00:19 +00:00
Q_strncpyz ( facename , fontfilename , sizeof ( facename ) ) ;
2016-09-01 14:31:24 +00:00
aname = strstr ( facename , " : " ) ;
if ( aname )
* aname + + = 0 ;
parms = strstr ( facename , " ? " ) ;
if ( parms )
* parms + + = 0 ;
f = Z_Malloc ( sizeof ( * f ) ) ;
f - > charheight = height ;
Q_strncpyz ( f - > name , fontfilename , sizeof ( f - > name ) ) ;
switch ( M_GameType ( ) )
{
case MGT_QUAKE2 :
VectorSet ( f - > alttint , 0.44 , 1.0 , 0.2 ) ;
break ;
default :
VectorSet ( f - > alttint , 1.16 , 0.54 , 0.41 ) ;
break ;
}
VectorSet ( f - > tint , 1 , 1 , 1 ) ;
fontfilename = facename ;
if ( parms )
{
while ( * parms )
{
if ( ! strncmp ( parms , " col= " , 4 ) )
{
char * t = parms + 4 ;
f - > tint [ 0 ] = strtod ( t , & t ) ;
if ( * t = = ' , ' ) t + + ;
if ( * t = = ' ' ) t + + ;
f - > tint [ 1 ] = strtod ( t , & t ) ;
if ( * t = = ' , ' ) t + + ;
if ( * t = = ' ' ) t + + ;
f - > tint [ 2 ] = strtod ( t , & t ) ;
parms = t ;
}
2017-12-09 21:22:46 +00:00
if ( ! strncmp ( parms , " fmt= " , 4 ) )
{
char * t = parms + 4 ;
fmt = 0 ;
if ( * t = = ' q ' )
fmt = FMT_QUAKE ;
else if ( * t = = ' l ' )
fmt = FMT_ISO88591 ;
2018-01-24 12:13:32 +00:00
else if ( * t = = ' w ' )
fmt = FMT_WINDOWS1252 ;
else if ( * t = = ' k ' )
fmt = FMT_KOI8U ;
2017-12-09 21:22:46 +00:00
else if ( * t = = ' h ' )
fmt = FMT_HORIZONTAL ;
}
2018-06-02 08:55:57 +00:00
if ( ! strncmp ( parms , " aspect= " , 7 ) )
{
char * t = parms + 7 ;
aspect = strtod ( t , & t ) ;
parms = t ;
}
2017-12-09 21:22:46 +00:00
2016-09-01 14:31:24 +00:00
while ( * parms & & * parms ! = ' & ' )
parms + + ;
if ( * parms = = ' & ' )
{
parms + + ;
continue ;
}
}
}
2018-03-04 14:41:16 +00:00
if ( vid . flags & VID_SRGBAWARE )
{
f - > tint [ 0 ] = M_SRGBToLinear ( f - > tint [ 0 ] , 1 ) ;
f - > tint [ 1 ] = M_SRGBToLinear ( f - > tint [ 1 ] , 1 ) ;
f - > tint [ 2 ] = M_SRGBToLinear ( f - > tint [ 2 ] , 1 ) ;
f - > alttint [ 0 ] = M_SRGBToLinear ( f - > alttint [ 0 ] , 1 ) ;
f - > alttint [ 1 ] = M_SRGBToLinear ( f - > alttint [ 1 ] , 1 ) ;
f - > alttint [ 2 ] = M_SRGBToLinear ( f - > alttint [ 2 ] , 1 ) ;
}
2017-02-19 00:15:42 +00:00
# ifdef PACKAGE_DOOMWAD
2016-09-01 14:31:24 +00:00
if ( ! * fontfilename )
{
unsigned char buf [ PLANEWIDTH * PLANEHEIGHT ] ;
int i ;
int x = 0 , y = 0 , h = 0 ;
doompatch_t * dp ;
memset ( buf , 0 , sizeof ( buf ) ) ;
for ( i = ' ! ' ; i < = ' _ ' ; i + + )
{
dp = NULL ;
FS_LoadFile ( va ( " wad/stcfn%.3d " , i ) , ( void * * ) & dp ) ;
if ( ! dp )
break ;
/*make sure it can fit*/
if ( x + dp - > width > PLANEWIDTH )
{
x = 0 ;
y + = h ;
h = 0 ;
}
f - > chars [ i ] . advance = dp - > width ; /*this is how much line space the char takes*/
f - > chars [ i ] . left = - dp - > leftoffset ;
f - > chars [ i ] . top = - dp - > topoffset ;
f - > chars [ i ] . nextchar = 0 ;
f - > chars [ i ] . pad = 0 ;
f - > chars [ i ] . texplane = SINGLEPLANE ;
f - > chars [ i ] . bmx = x ;
f - > chars [ i ] . bmy = y ;
f - > chars [ i ] . bmh = dp - > height ;
f - > chars [ i ] . bmw = dp - > width ;
Doom_ExpandPatch ( dp , & buf [ y * PLANEWIDTH + x ] , PLANEWIDTH ) ;
x + = dp - > width ;
if ( dp - > height > h )
{
h = dp - > height ;
if ( h > f - > charheight )
f - > charheight = h ;
}
FS_FreeFile ( dp ) ;
}
/*if all loaded okay, replicate the chars to the quake-compat range (both white+red chars)*/
if ( i = = ' _ ' + 1 )
{
//doom doesn't have many chars, so make sure the lower case chars exist.
for ( i = ' a ' ; i < = ' z ' ; i + + )
f - > chars [ i ] = f - > chars [ i - ' a ' + ' A ' ] ;
//no space char either
f - > chars [ ' ' ] . advance = 8 ;
f - > singletexture = R_LoadTexture8 ( " doomfont " , PLANEWIDTH , PLANEHEIGHT , buf , 0 , true ) ;
for ( i = 0xe000 ; i < = 0xe0ff ; i + + )
{
f - > chars [ i ] = f - > chars [ toupper ( i & 0x7f ) ] ;
}
return f ;
}
}
# endif
2017-08-29 02:29:06 +00:00
# ifdef HEXEN2
2016-09-01 14:31:24 +00:00
if ( ! strcmp ( fontfilename , " gfx/tinyfont " ) )
{
unsigned int * img ;
int x , y ;
size_t lumpsize ;
2017-12-09 21:22:46 +00:00
qbyte lumptype ;
unsigned char * w = W_GetLumpName ( fontfilename + 4 , & lumpsize , & lumptype ) ;
2018-08-23 06:03:31 +00:00
if ( ! w | | lumpsize ! = 32 * 128 | | lumptype ! = ' D ' )
2016-09-01 14:31:24 +00:00
{
Z_Free ( f ) ;
return NULL ;
}
img = Z_Malloc ( PLANEWIDTH * PLANEWIDTH * 4 ) ;
for ( y = 0 ; y < 32 ; y + + )
for ( x = 0 ; x < 128 ; x + + )
img [ x + y * PLANEWIDTH ] = w [ x + y * 128 ] ? d_8to24rgbtable [ w [ x + y * 128 ] ] : 0 ;
2017-12-09 21:22:46 +00:00
f - > singletexture = R_LoadTexture ( " tinyfont " , PLANEWIDTH , PLANEWIDTH , TF_RGBA32 , img , IF_PREMULTIPLYALPHA | IF_UIPIC | IF_NOPICMIP | IF_NOMIPMAP ) ;
2016-09-01 14:31:24 +00:00
if ( f - > singletexture - > status = = TEX_LOADING )
COM_WorkerPartialSync ( f - > singletexture , & f - > singletexture - > status , TEX_LOADING ) ;
Z_Free ( img ) ;
for ( i = 0x00 ; i < = 0xff ; i + + )
{
2017-09-20 11:27:13 +00:00
c = Font_GetCharStore ( f , i ) ;
c - > advance = ( height * 3 ) / 4 ;
c - > left = 0 ;
c - > top = 0 ;
c - > nextchar = 0 ; //these chars are not linked in
c - > texplane = BITMAPPLANE ; /*if its a 'raster' font, don't use the default chars, always use the raster images*/
2016-09-01 14:31:24 +00:00
if ( i > = ' a ' & & i < = ' z ' )
{
2017-09-20 11:27:13 +00:00
c - > bmx = ( ( i - 64 ) & 15 ) * 8 ;
c - > bmy = ( ( i - 64 ) / 16 ) * 8 ;
c - > bmh = 8 ;
c - > bmw = 8 ;
2016-09-01 14:31:24 +00:00
}
else if ( i > = 32 & & i < 96 )
{
2017-09-20 11:27:13 +00:00
c - > bmx = ( ( i - 32 ) & 15 ) * 8 ;
c - > bmy = ( ( i - 32 ) / 16 ) * 8 ;
c - > bmh = 8 ;
c - > bmw = 8 ;
2016-09-01 14:31:24 +00:00
}
else
{
2017-09-20 11:27:13 +00:00
c - > bmh = 0 ;
c - > bmw = 0 ;
c - > bmx = 0 ;
c - > bmy = 0 ;
2016-09-01 14:31:24 +00:00
}
2017-09-20 11:27:13 +00:00
Font_CopyChar ( f , i , i | 0xe0ff ) ;
2016-09-01 14:31:24 +00:00
}
return f ;
}
2017-08-29 02:29:06 +00:00
# endif
2016-09-01 14:31:24 +00:00
if ( aname )
{
if ( ! strncmp ( aname , " ?col= " , 5 ) )
{
char * t = aname + 5 ;
f - > alttint [ 0 ] = strtod ( t , & t ) ;
if ( * t = = ' , ' ) t + + ;
if ( * t = = ' ' ) t + + ;
f - > alttint [ 1 ] = strtod ( t , & t ) ;
if ( * t = = ' , ' ) t + + ;
if ( * t = = ' ' ) t + + ;
f - > alttint [ 2 ] = strtod ( t , & t ) ;
}
else
{
2018-06-02 08:55:57 +00:00
f - > alt = Font_LoadFont ( aname , vheight ) ;
2016-09-01 14:31:24 +00:00
if ( f - > alt )
{
VectorCopy ( f - > alt - > tint , f - > alttint ) ;
VectorCopy ( f - > alt - > tint , f - > alt - > alttint ) ;
}
}
}
{
const char * start ;
start = fontfilename ;
for ( ; ; )
{
char * end = strchr ( start , ' , ' ) ;
if ( end )
* end = 0 ;
2017-12-09 21:22:46 +00:00
if ( fmt = = FMT_HORIZONTAL )
Font_LoadHorizontalFont ( f , height , start ) ;
# ifdef AVAIL_FREETYPE
else if ( fmt = = FMT_AUTO )
Font_LoadFreeTypeFont ( f , height , start ) ;
# endif
2016-09-01 14:31:24 +00:00
if ( end )
{
* end = ' , ' ;
start = end + 1 ;
}
else
break ;
}
}
2017-12-09 21:22:46 +00:00
# ifdef HALFLIFEMODELS
if ( ! f - > faces )
{
if ( f - > faces < MAX_FACES )
{
size_t lumpsize ;
qbyte lumptype ;
void * lumpdata ;
lumpdata = W_GetLumpName ( " conchars " , & lumpsize , & lumptype ) ;
if ( lumpdata & & lumptype = = TYP_HLFONT )
{
fontface_t * fa = Z_Malloc ( sizeof ( * fa ) ) ;
fa - > halflife = lumpdata ;
fa - > flink = & fa - > fnext ;
fa - > refs = 1 ;
f - > face [ f - > faces + + ] = fa ;
// f->charheight = fa->halflife->fontheight1; //force the font to a specific size.
return f ;
}
}
}
2016-09-01 14:31:24 +00:00
# endif
2017-12-09 21:22:46 +00:00
if ( ! f - > faces )
2016-09-01 14:31:24 +00:00
{
//default to only map the ascii-compatible chars from the quake font.
if ( * fontfilename )
{
2017-12-20 08:23:41 +00:00
f - > singletexture = R_LoadHiResTexture ( fontfilename , " fonts:charsets " , IF_PREMULTIPLYALPHA | ( r_font_linear . ival ? IF_LINEAR : IF_NEAREST ) | IF_UIPIC | IF_NOPICMIP | IF_NOMIPMAP ) ;
2016-09-01 14:31:24 +00:00
if ( f - > singletexture - > status = = TEX_LOADING )
COM_WorkerPartialSync ( f - > singletexture , & f - > singletexture - > status , TEX_LOADING ) ;
}
}
2017-12-09 21:22:46 +00:00
defaultplane = INVALIDPLANE ; /*assume the bitmap plane - don't use the fallback as people don't think to use com_parseutf8*/
if ( TEXLOADED ( f - > singletexture ) )
defaultplane = BITMAPPLANE ;
2017-12-20 08:23:41 +00:00
else if ( TEXLOADED ( fontplanes . defaultfont ) )
2017-12-09 21:22:46 +00:00
defaultplane = DEFAULTPLANE ;
if ( defaultplane = = INVALIDPLANE )
2016-09-01 14:31:24 +00:00
{
if ( ! TEXLOADED ( fontplanes . defaultfont ) )
2017-12-09 21:22:46 +00:00
{
2018-11-19 06:37:25 +00:00
fontplanes . defaultfont = Font_LoadDefaultConchars ( & fmt ) ;
2017-12-09 21:22:46 +00:00
}
2016-09-01 14:31:24 +00:00
2017-08-29 02:29:06 +00:00
# ifdef HEXEN2
2016-09-01 14:31:24 +00:00
if ( ! strcmp ( fontfilename , " gfx/hexen2 " ) )
{
f - > singletexture = Font_LoadHexen2Conchars ( false ) ;
defaultplane = DEFAULTPLANE ;
}
2017-08-29 02:29:06 +00:00
# endif
2016-09-01 14:31:24 +00:00
if ( ! TEXLOADED ( f - > singletexture ) )
f - > singletexture = fontplanes . defaultfont ;
2017-12-09 21:22:46 +00:00
if ( TEXLOADED ( f - > singletexture ) )
defaultplane = BITMAPPLANE ;
2017-12-20 08:23:41 +00:00
else if ( TEXLOADED ( fontplanes . defaultfont ) )
2017-12-09 21:22:46 +00:00
defaultplane = DEFAULTPLANE ;
2016-09-01 14:31:24 +00:00
}
2017-12-09 21:22:46 +00:00
if ( defaultplane ! = INVALIDPLANE )
2016-09-01 14:31:24 +00:00
{
2018-01-28 04:12:59 +00:00
if ( fmt = = FMT_AUTO )
fmt = FMT_QUAKE ;
2017-12-14 21:12:11 +00:00
if ( ! f - > faces )
2018-01-24 12:13:32 +00:00
{
static const unsigned short iso88591 [ ] = {
0x80 , 0x81 , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8a , 0x8b , 0x8c , 0x8d , 0x8e , 0x8f ,
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9a , 0x9b , 0x9c , 0x9d , 0x9e , 0x9f } ;
static const unsigned short win1252 [ ] = {
0x20ac , 0x81 , 0x201a , 0x0192 , 0x201e , 0x2026 , 0x2020 , 0x2021 , 0x02c6 , 0x2030 , 0x0160 , 0x2039 , 0x0152 , 0x8d , 0x017d , 0x8f ,
0x90 , 0x2018 , 0x2019 , 0x101c , 0x201d , 0x2022 , 0x2013 , 0x2014 , 0x02dc , 0x2122 , 0x0161 , 0x203a , 0x0153 , 0x9d , 0x017e , 0x0178 } ;
static const unsigned short koi8u [ ] = {
0x2500 , 0x2502 , 0x250C , 0x2510 , 0x2514 , 0x2518 , 0x251C , 0x2524 , 0x252C , 0x2534 , 0x253C , 0x2580 , 0x2584 , 0x2588 , 0x258C , 0x2590 ,
0x2591 , 0x2592 , 0x2593 , 0x2320 , 0x25A0 , 0x2219 , 0x221A , 0x2248 , 0x2264 , 0x2265 , 0x00A0 , 0x2321 , 0x00B0 , 0x00B2 , 0x00B7 , 0x00F7 ,
0x2550 , 0x2551 , 0x2552 , 0x0451 , 0x0454 , 0x2554 , 0x0456 , 0x0457 , 0x2557 , 0x2558 , 0x2559 , 0x255A , 0x255B , 0x0491 , 0x255D , 0x255E ,
0x255F , 0x2560 , 0x2561 , 0x0401 , 0x0404 , 0x2563 , 0x0406 , 0x0407 , 0x2566 , 0x2567 , 0x2568 , 0x2569 , 0x256A , 0x0490 , 0x256C , 0x00A9 ,
0x044E , 0x0430 , 0x0431 , 0x0446 , 0x0434 , 0x0435 , 0x0444 , 0x0433 , 0x0445 , 0x0438 , 0x0439 , 0x043A , 0x043B , 0x043C , 0x043D , 0x043E ,
0x043F , 0x044F , 0x0440 , 0x0441 , 0x0442 , 0x0443 , 0x0436 , 0x0432 , 0x044C , 0x044B , 0x0437 , 0x0448 , 0x044D , 0x0449 , 0x0447 , 0x044A ,
0x042E , 0x0410 , 0x0411 , 0x0426 , 0x0414 , 0x0415 , 0x0424 , 0x0413 , 0x0425 , 0x0418 , 0x0419 , 0x041A , 0x041B , 0x041C , 0x041D , 0x041E ,
0x041F , 0x042F , 0x0420 , 0x0421 , 0x0422 , 0x0423 , 0x0416 , 0x0412 , 0x042C , 0x042B , 0x0417 , 0x0428 , 0x042D , 0x0429 , 0x0427 , 0x042A } ;
const unsigned short * c1 ;
unsigned int c1size ;
if ( fmt = = FMT_WINDOWS1252 )
{ //some tools use these extra ones (latin-1 has no visible c1 entries)
c1 = win1252 ;
c1size = countof ( win1252 ) ;
}
else if ( fmt = = FMT_KOI8U )
{ //lots of russians in the quake scene
c1 = koi8u ;
c1size = countof ( koi8u ) ;
}
else
{
c1 = iso88591 ;
c1size = countof ( iso88591 ) ;
}
c1size + = 128 ;
2017-12-14 21:12:11 +00:00
/*force it to load, even if there's nothing there*/
for ( i = ( ( fmt = = FMT_QUAKE ) ? 32 : 0 ) ; i < ( ( fmt = = FMT_QUAKE ) ? 128 : 256 ) ; i + + )
{
2018-01-24 12:13:32 +00:00
if ( i > = 128 & & i < c1size )
c = Font_GetCharStore ( f , c1 [ i - 128 ] ) ;
else
c = Font_GetCharStore ( f , i ) ;
2017-12-14 21:12:11 +00:00
2018-06-02 08:55:57 +00:00
c - > advance = f - > charheight * aspect ;
2017-12-14 21:12:11 +00:00
c - > bmh = PLANEWIDTH / 16 ;
c - > bmw = PLANEWIDTH / 16 ;
c - > bmx = ( i & 15 ) * ( PLANEWIDTH / 16 ) ;
c - > bmy = ( i / 16 ) * ( PLANEWIDTH / 16 ) ;
c - > left = 0 ;
c - > top = 0 ;
c - > nextchar = 0 ; //these chars are not linked in
c - > texplane = defaultplane ;
}
}
2018-01-24 12:13:32 +00:00
if ( fmt = = FMT_QUAKE )
2017-12-09 21:22:46 +00:00
{
2018-01-24 12:13:32 +00:00
/*pack the default chars into it*/
for ( i = 0xe000 ; i < = 0xe0ff ; i + + )
{
c = Font_GetCharStore ( f , i ) ;
2018-06-02 08:55:57 +00:00
c - > advance = f - > charheight * aspect ;
2018-01-24 12:13:32 +00:00
c - > bmh = PLANEWIDTH / 16 ;
c - > bmw = PLANEWIDTH / 16 ;
c - > bmx = ( ( i & 15 ) ) * ( PLANEWIDTH / 16 ) ;
c - > bmy = ( ( i & 0xf0 ) / 16 ) * ( PLANEWIDTH / 16 ) ;
c - > left = 0 ;
c - > top = 0 ;
c - > nextchar = 0 ; //these chars are not linked in
c - > texplane = defaultplane ;
}
2017-12-09 21:22:46 +00:00
}
2016-09-01 14:31:24 +00:00
}
return f ;
}
//removes a font from memory.
void Font_Free ( struct font_s * f )
{
2018-03-06 16:46:57 +00:00
size_t i ;
2016-09-01 14:31:24 +00:00
struct charcache_s * * link , * c , * valid ;
2018-06-02 08:55:57 +00:00
if ( ! f )
return ;
2016-09-01 14:31:24 +00:00
//kill the alt font first.
if ( f - > alt )
{
Font_Free ( f - > alt ) ;
f - > alt = NULL ;
}
valid = NULL ;
//walk all chars, unlinking any that appear to be within this font's char cache
for ( link = & fontplanes . oldestchar ; * link ; )
{
c = * link ;
2017-09-20 11:27:13 +00:00
if ( f - > chars [ c - > block ] & & c > = f - > chars [ c - > block ] & & c < = f - > chars [ c - > block ] + FONTBLOCKSIZE )
2016-09-01 14:31:24 +00:00
{
c = c - > nextchar ;
if ( ! c )
fontplanes . newestchar = valid ;
* link = c ;
}
else
{
valid = c ;
link = & c - > nextchar ;
}
}
2017-12-09 21:22:46 +00:00
while ( f - > faces - - > 0 )
2016-09-01 14:31:24 +00:00
{
2017-12-09 21:22:46 +00:00
fontface_t * qface = f - > face [ f - > faces ] ;
2016-09-01 14:31:24 +00:00
qface - > refs - - ;
if ( ! qface - > refs )
{
2017-12-09 21:22:46 +00:00
# ifdef AVAIL_FREETYPE
if ( qface - > ft . face )
pFT_Done_Face ( qface - > ft . face ) ;
if ( qface - > ft . membuf )
BZ_Free ( qface - > ft . membuf ) ;
# endif
2016-09-01 14:31:24 +00:00
* qface - > flink = qface - > fnext ;
if ( qface - > fnext )
qface - > fnext - > flink = qface - > flink ;
Z_Free ( qface ) ;
}
}
2018-03-06 16:46:57 +00:00
for ( i = 0 ; i < FONTBLOCKS ; i + + )
if ( f - > chars [ i ] )
Z_Free ( f - > chars [ i ] ) ;
2016-09-01 14:31:24 +00:00
Z_Free ( f ) ;
}
//maps a given virtual screen coord to a pixel coord, which matches the font's height/width values
void Font_BeginString ( struct font_s * font , float vx , float vy , int * px , int * py )
{
if ( R2D_Flush & & ( R2D_Flush ! = Font_Flush | | curfont ! = font | | font_be_flags ! = r2d_be_flags ) )
R2D_Flush ( ) ;
R2D_Flush = Font_Flush ;
font_be_flags = r2d_be_flags ;
curfont = font ;
* px = ( vx * ( int ) vid . rotpixelwidth ) / ( float ) vid . width ;
* py = ( vy * ( int ) vid . rotpixelheight ) / ( float ) vid . height ;
curfont_scale [ 0 ] = curfont - > charheight ;
curfont_scale [ 1 ] = curfont - > charheight ;
curfont_scaled = false ;
}
void Font_Transform ( float vx , float vy , int * px , int * py )
{
if ( px )
* px = ( vx * ( int ) vid . rotpixelwidth ) / ( float ) vid . width ;
if ( py )
* py = ( vy * ( int ) vid . rotpixelheight ) / ( float ) vid . height ;
}
void Font_BeginScaledString ( struct font_s * font , float vx , float vy , float szx , float szy , float * px , float * py )
{
if ( R2D_Flush & & ( R2D_Flush ! = Font_Flush | | curfont ! = font | | font_be_flags ! = r2d_be_flags ) )
R2D_Flush ( ) ;
R2D_Flush = Font_Flush ;
font_be_flags = r2d_be_flags ;
curfont = font ;
* px = ( vx * ( float ) vid . rotpixelwidth ) / ( float ) vid . width ;
* py = ( vy * ( float ) vid . rotpixelheight ) / ( float ) vid . height ;
//now that its in pixels, clamp it so the text is at least consistant with its position.
//an individual char may end straddling a pixel boundary, but at least the pixels won't jiggle around as the text moves.
* px = ( int ) * px ;
* py = ( int ) * py ;
if ( ( int ) ( szx * vid . rotpixelheight / vid . height ) = = curfont - > charheight & & ( int ) ( szy * vid . rotpixelheight / vid . height ) = = curfont - > charheight )
curfont_scaled = false ;
else
curfont_scaled = true ;
curfont_scale [ 0 ] = ( szx * ( float ) vid . rotpixelheight ) / ( curfont - > charheight * ( float ) vid . height ) ;
curfont_scale [ 1 ] = ( szy * ( float ) vid . rotpixelheight ) / ( curfont - > charheight * ( float ) vid . height ) ;
}
void Font_EndString ( struct font_s * font )
{
// Font_Flush();
// curfont = NULL;
R2D_Flush = Font_Flush ;
}
//obtains the font's row height (each row of chars should be drawn using this increment)
int Font_CharHeight ( void )
{
return curfont - > charheight ;
}
//obtains the font's row height (each row of chars should be drawn using this increment)
float Font_CharScaleHeight ( void )
{
return curfont - > charheight * curfont_scale [ 1 ] ;
}
int Font_TabWidth ( int x )
{
int tabwidth = Font_CharWidth ( CON_WHITEMASK , ' ' ) ;
tabwidth * = 8 ;
x + + ;
x = x + ( ( tabwidth - ( x % tabwidth ) ) % tabwidth ) ;
return x ;
}
/*
This is where the character ends .
Note : this function supports tabs - x must always be based off 0 , with Font_LineDraw actually used to draw the line .
*/
int Font_CharEndCoord ( struct font_s * font , int x , unsigned int charflags , unsigned int codepoint )
{
struct charcache_s * c ;
if ( charflags & CON_HIDDEN )
return x ;
if ( codepoint = = ' \t ' )
return Font_TabWidth ( x ) ;
if ( ( charflags & CON_2NDCHARSETTEXT ) & & font - > alt )
font = font - > alt ;
c = Font_GetChar ( font , codepoint ) ;
if ( ! c )
{
return x + 0 ;
}
return x + c - > advance ;
}
//obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result.
//FIXME: this function cannot cope with tab and should not be used.
int Font_CharWidth ( unsigned int charflags , unsigned int codepoint )
{
struct charcache_s * c ;
struct font_s * font = curfont ;
if ( charflags & CON_HIDDEN )
return 0 ;
if ( ( charflags & CON_2NDCHARSETTEXT ) & & font - > alt )
font = font - > alt ;
c = Font_GetChar ( curfont , codepoint ) ;
if ( ! c )
{
return 0 ;
}
return c - > advance ;
}
//obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result.
//FIXME: this function cannot cope with tab and should not be used.
float Font_CharScaleWidth ( unsigned int charflags , unsigned int codepoint )
{
struct charcache_s * c ;
struct font_s * font = curfont ;
if ( charflags & CON_HIDDEN )
return 0 ;
if ( ( charflags & CON_2NDCHARSETTEXT ) & & font - > alt )
font = font - > alt ;
c = Font_GetChar ( curfont , codepoint ) ;
if ( ! c )
{
return 0 ;
}
return c - > advance * curfont_scale [ 0 ] ;
}
conchar_t * Font_DecodeReverse ( conchar_t * start , conchar_t * stop , unsigned int * codeflags , unsigned int * codepoint )
{
if ( start < = stop )
{
* codeflags = 0 ;
* codepoint = 0 ;
return stop ;
}
start - - ;
if ( start > stop & & start [ - 1 ] & CON_LONGCHAR )
if ( ! ( start [ - 1 ] & CON_RICHFORECOLOUR ) )
{
start - - ;
* codeflags = start [ 1 ] ;
* codepoint = ( ( start [ 0 ] & CON_CHARMASK ) < < 16 ) | ( start [ 1 ] & CON_CHARMASK ) ;
return start ;
}
* codeflags = start [ 0 ] ;
* codepoint = start [ 0 ] & CON_CHARMASK ;
return start ;
}
//for a given font, calculate the line breaks and word wrapping for a block of text
//start+end are the input string
//starts+ends are an array of line start and end points, which have maxlines elements.
//(end is the terminator, null or otherwise)
//maxpixelwidth is the width of the display area in pixels
int Font_LineBreaks ( conchar_t * start , conchar_t * end , int maxpixelwidth , int maxlines , conchar_t * * starts , conchar_t * * ends )
{
conchar_t * l , * bt , * n ;
int px ;
int foundlines = 0 ;
struct font_s * font = curfont ;
unsigned int codeflags , codepoint ;
while ( start < end )
{
// scan the width of the line
for ( px = 0 , l = start ; px < = maxpixelwidth ; )
{
if ( l > = end )
break ;
n = Font_Decode ( l , & codeflags , & codepoint ) ;
if ( ! ( codeflags & CON_HIDDEN ) & & ( codepoint = = ' \n ' | | codepoint = = ' \v ' ) )
break ;
px = Font_CharEndCoord ( font , px , codeflags , codepoint ) ;
l = n ;
}
//if we did get to the end
if ( px > maxpixelwidth )
{
bt = l ;
//backtrack until we find a space
for ( ; ; )
{
n = Font_DecodeReverse ( l , start , & codeflags , & codepoint ) ;
if ( codepoint > ' ' )
l = n ;
else
break ;
}
if ( l = = start & & bt > start )
l = Font_DecodeReverse ( bt , start , & codeflags , & codepoint ) ;
}
starts [ foundlines ] = start ;
ends [ foundlines ] = l ;
foundlines + + ;
if ( foundlines = = maxlines )
break ;
start = l ;
if ( start = = end )
break ;
if ( ( * start & ( CON_CHARMASK | CON_HIDDEN ) ) = = ' \n ' | | ( * start & ( CON_CHARMASK | CON_HIDDEN ) ) = = ' \v ' )
start + + ; // skip the \n
}
return foundlines ;
}
int Font_LineWidth ( conchar_t * start , conchar_t * end )
{
//fixme: does this do the right thing with tabs?
int x = 0 ;
struct font_s * font = curfont ;
unsigned int codeflags , codepoint ;
for ( ; start < end ; )
{
start = Font_Decode ( start , & codeflags , & codepoint ) ;
x = Font_CharEndCoord ( font , x , codeflags , codepoint ) ;
}
return x ;
}
float Font_LineScaleWidth ( conchar_t * start , conchar_t * end )
{
int x = 0 ;
struct font_s * font = curfont ;
unsigned int codeflags , codepoint ;
while ( start < end )
{
start = Font_Decode ( start , & codeflags , & codepoint ) ;
x = Font_CharEndCoord ( font , x , codeflags , codepoint ) ;
}
return x * curfont_scale [ 0 ] ;
}
void Font_LineDraw ( int x , int y , conchar_t * start , conchar_t * end )
{
int lx = 0 ;
struct font_s * font = curfont ;
unsigned int codeflags , codepoint ;
for ( ; start < end ; )
{
start = Font_Decode ( start , & codeflags , & codepoint ) ;
Font_DrawChar ( x + lx , y , codeflags , codepoint ) ;
lx = Font_CharEndCoord ( font , lx , codeflags , codepoint ) ;
}
}
2017-07-28 01:49:25 +00:00
conchar_t * Font_CharAt ( int x , conchar_t * start , conchar_t * end )
{
int lx = 0 , nx ;
struct font_s * font = curfont ;
unsigned int codeflags , codepoint ;
conchar_t * nc ;
for ( ; start < end ; lx = nx , start = nc )
{
nc = Font_Decode ( start , & codeflags , & codepoint ) ;
nx = Font_CharEndCoord ( font , lx , codeflags , codepoint ) ;
if ( x > = lx & & x < nx )
return start ;
}
return NULL ;
}
2016-09-01 14:31:24 +00:00
/*Note: *all* strings after the current one will inherit the same colour, until one changes it explicitly
correct usage of this function thus requires calling this with 1111 before Font_EndString */
void Font_InvalidateColour ( vec4_t newcolour )
{
if ( font_foretint [ 0 ] = = newcolour [ 0 ] & & font_foretint [ 1 ] = = newcolour [ 1 ] & & font_foretint [ 2 ] = = newcolour [ 2 ] & & font_foretint [ 3 ] = = newcolour [ 3 ] )
return ;
if ( font_colourmask & CON_NONCLEARBG )
{
2017-11-24 18:40:17 +00:00
if ( R2D_Flush )
R2D_Flush ( ) ;
2016-09-01 14:31:24 +00:00
R2D_Flush = Font_Flush ;
}
font_colourmask = CON_WHITEMASK ;
2017-12-09 21:22:46 +00:00
VectorScale ( newcolour , newcolour [ 3 ] , font_foretint ) ;
font_foretint [ 3 ] = newcolour [ 3 ] ;
2016-09-01 14:31:24 +00:00
Vector4Scale ( font_foretint , 255 , font_forecolour ) ;
font_backcolour [ 3 ] = 0 ;
/*Any drawchars that are now drawn will get the forced colour*/
}
//draw a character from the current font at a pixel location.
int Font_DrawChar ( int px , int py , unsigned int charflags , unsigned int codepoint )
{
struct charcache_s * c ;
float s0 , s1 ;
float t0 , t1 ;
float nextx ;
float sx , sy , sw , sh ;
int col ;
int v ;
struct font_s * font = curfont ;
# ifdef D3D11QUAKE
float dxbias = 0 ; //(qrenderer == QR_DIRECT3D11)?0.5:0;
# else
# define dxbias 0
# endif
if ( charflags & CON_HIDDEN )
return px ;
if ( charflags & CON_2NDCHARSETTEXT )
{
if ( font - > alt )
{
font = font - > alt ;
// charflags &= ~CON_2NDCHARSETTEXT;
}
else if ( ( codepoint ) > = 0xe000 & & ( codepoint ) < = 0xe0ff )
charflags & = ~ CON_2NDCHARSETTEXT ; //don't double-dip
}
//crash if there is no current font.
c = Font_GetChar ( font , codepoint ) ;
if ( ! c )
return px ;
nextx = px + c - > advance ;
if ( codepoint = = ' \t ' )
return Font_TabWidth ( px ) ;
2017-03-04 19:36:06 +00:00
if ( codepoint = = ' ' & & ( charflags & ( CON_RICHFORECOLOUR | CON_NONCLEARBG ) ) ! = CON_NONCLEARBG )
2016-09-01 14:31:24 +00:00
return nextx ;
/* if (charcode & CON_BLINKTEXT)
{
if ( ! cl_noblink . ival )
if ( ( int ) ( realtime * 3 ) & 1 )
return nextx ;
}
*/
if ( charflags & CON_RICHFORECOLOUR )
{
col = charflags & ( CON_2NDCHARSETTEXT | CON_BLINKTEXT | CON_RICHFORECOLOUR | ( 0xfff < < CON_RICHBSHIFT ) ) ;
if ( col ! = font_colourmask )
{
vec4_t rgba ;
if ( font_colourmask & CON_NONCLEARBG )
{
Font_Flush ( ) ;
R2D_Flush = Font_Flush ;
}
font_colourmask = col ;
rgba [ 0 ] = ( ( col > > CON_RICHRSHIFT ) & 0xf ) * 0x11 ;
rgba [ 1 ] = ( ( col > > CON_RICHGSHIFT ) & 0xf ) * 0x11 ;
rgba [ 2 ] = ( ( col > > CON_RICHBSHIFT ) & 0xf ) * 0x11 ;
rgba [ 3 ] = 255 ;
font_backcolour [ 0 ] = 0 ;
font_backcolour [ 1 ] = 0 ;
font_backcolour [ 2 ] = 0 ;
font_backcolour [ 3 ] = 0 ;
if ( charflags & CON_2NDCHARSETTEXT )
{
rgba [ 0 ] * = font - > alttint [ 0 ] ;
rgba [ 1 ] * = font - > alttint [ 1 ] ;
rgba [ 2 ] * = font - > alttint [ 2 ] ;
}
else
{
rgba [ 0 ] * = font - > tint [ 0 ] ;
rgba [ 1 ] * = font - > tint [ 1 ] ;
rgba [ 2 ] * = font - > tint [ 2 ] ;
}
rgba [ 0 ] * = font_foretint [ 0 ] ;
rgba [ 1 ] * = font_foretint [ 1 ] ;
rgba [ 2 ] * = font_foretint [ 2 ] ;
rgba [ 3 ] * = font_foretint [ 3 ] ;
if ( charflags & CON_BLINKTEXT )
{
float a = ( sin ( realtime * 3 ) + 1 ) * 0.4 + 0.2 ;
2017-12-09 21:22:46 +00:00
Vector4Scale ( rgba , a , rgba ) ;
2016-09-01 14:31:24 +00:00
}
font_forecolour [ 0 ] = min ( rgba [ 0 ] , 255 ) ;
font_forecolour [ 1 ] = min ( rgba [ 1 ] , 255 ) ;
font_forecolour [ 2 ] = min ( rgba [ 2 ] , 255 ) ;
font_forecolour [ 3 ] = min ( rgba [ 3 ] , 255 ) ;
}
}
else
{
col = charflags & ( CON_2NDCHARSETTEXT | CON_NONCLEARBG | CON_BGMASK | CON_FGMASK | CON_HALFALPHA | CON_BLINKTEXT ) ;
if ( col ! = font_colourmask )
{
vec4_t rgba ;
if ( ( col ^ font_colourmask ) & CON_NONCLEARBG )
{
Font_Flush ( ) ;
R2D_Flush = Font_Flush ;
}
font_colourmask = col ;
col = ( charflags & CON_FGMASK ) > > CON_FGSHIFT ;
2017-12-09 21:22:46 +00:00
if ( charflags & CON_HALFALPHA )
{
rgba [ 0 ] = consolecolours [ col ] . fr * 0x7f ;
rgba [ 1 ] = consolecolours [ col ] . fg * 0x7f ;
rgba [ 2 ] = consolecolours [ col ] . fb * 0x7f ;
rgba [ 3 ] = 0x7f ;
}
else
{
rgba [ 0 ] = consolecolours [ col ] . fr * 255 ;
rgba [ 1 ] = consolecolours [ col ] . fg * 255 ;
rgba [ 2 ] = consolecolours [ col ] . fb * 255 ;
rgba [ 3 ] = 255 ;
}
2016-09-01 14:31:24 +00:00
2018-03-04 14:41:16 +00:00
if ( vid . flags & VID_SRGBAWARE )
{
rgba [ 0 ] = M_SRGBToLinear ( rgba [ 0 ] , 255 ) ;
rgba [ 1 ] = M_SRGBToLinear ( rgba [ 1 ] , 255 ) ;
rgba [ 2 ] = M_SRGBToLinear ( rgba [ 2 ] , 255 ) ;
}
2016-09-01 14:31:24 +00:00
col = ( charflags & CON_BGMASK ) > > CON_BGSHIFT ;
2017-12-09 21:22:46 +00:00
if ( charflags & CON_NONCLEARBG )
{
font_backcolour [ 0 ] = consolecolours [ col ] . fr * 255 ;
font_backcolour [ 1 ] = consolecolours [ col ] . fg * 255 ;
font_backcolour [ 2 ] = consolecolours [ col ] . fb * 255 ;
font_backcolour [ 3 ] = ( charflags & CON_NONCLEARBG ) ? 0xc0 : 0 ;
}
else
Vector4Set ( font_backcolour , 0 , 0 , 0 , 0 ) ;
2016-09-01 14:31:24 +00:00
if ( charflags & CON_2NDCHARSETTEXT )
{
rgba [ 0 ] * = font - > alttint [ 0 ] ;
rgba [ 1 ] * = font - > alttint [ 1 ] ;
rgba [ 2 ] * = font - > alttint [ 2 ] ;
}
else
{
rgba [ 0 ] * = font - > tint [ 0 ] ;
rgba [ 1 ] * = font - > tint [ 1 ] ;
rgba [ 2 ] * = font - > tint [ 2 ] ;
}
rgba [ 0 ] * = font_foretint [ 0 ] ;
rgba [ 1 ] * = font_foretint [ 1 ] ;
rgba [ 2 ] * = font_foretint [ 2 ] ;
rgba [ 3 ] * = font_foretint [ 3 ] ;
if ( charflags & CON_BLINKTEXT )
{
float a = ( sin ( realtime * 3 ) + 1 ) * 0.4 + 0.2 ;
2017-12-09 21:22:46 +00:00
Vector4Scale ( rgba , a , rgba ) ;
2016-09-01 14:31:24 +00:00
}
font_forecolour [ 0 ] = min ( rgba [ 0 ] , 255 ) ;
font_forecolour [ 1 ] = min ( rgba [ 1 ] , 255 ) ;
font_forecolour [ 2 ] = min ( rgba [ 2 ] , 255 ) ;
font_forecolour [ 3 ] = min ( rgba [ 3 ] , 255 ) ;
}
}
s0 = ( float ) c - > bmx / PLANEWIDTH ;
t0 = ( float ) c - > bmy / PLANEWIDTH ;
s1 = ( float ) ( c - > bmx + c - > bmw ) / PLANEWIDTH ;
t1 = ( float ) ( c - > bmy + c - > bmh ) / PLANEWIDTH ;
switch ( c - > texplane )
{
case TRACKERIMAGE :
s0 = t0 = 0 ;
s1 = t1 = 1 ;
sx = ( ( px + c - > left + dxbias ) * ( int ) vid . width ) / ( float ) vid . rotpixelwidth ;
sy = ( ( py + c - > top + dxbias ) * ( int ) vid . height ) / ( float ) vid . rotpixelheight ;
sw = ( c - > advance * vid . width ) / ( float ) vid . rotpixelwidth ;
sh = ( font - > charheight * vid . height ) / ( float ) vid . rotpixelheight ;
v = Font_BeginChar ( fontplanes . trackerimage ) ;
break ;
case DEFAULTPLANE :
sx = ( ( px + c - > left + dxbias ) * ( int ) vid . width ) / ( float ) vid . rotpixelwidth ;
sy = ( ( py + c - > top + dxbias ) * ( int ) vid . height ) / ( float ) vid . rotpixelheight ;
2018-06-02 08:55:57 +00:00
sw = ( ( c - > advance ) * vid . width ) / ( float ) vid . rotpixelwidth ;
2016-09-01 14:31:24 +00:00
sh = ( ( font - > charheight ) * vid . height ) / ( float ) vid . rotpixelheight ;
v = Font_BeginChar ( fontplanes . defaultfont ) ;
break ;
case BITMAPPLANE :
sx = ( ( px + c - > left + dxbias ) * ( int ) vid . width ) / ( float ) vid . rotpixelwidth ;
sy = ( ( py + c - > top + dxbias ) * ( int ) vid . height ) / ( float ) vid . rotpixelheight ;
2018-06-02 08:55:57 +00:00
sw = ( ( c - > advance ) * vid . width ) / ( float ) vid . rotpixelwidth ;
2016-09-01 14:31:24 +00:00
sh = ( ( font - > charheight ) * vid . height ) / ( float ) vid . rotpixelheight ;
v = Font_BeginChar ( font - > singletexture ) ;
break ;
case SINGLEPLANE :
sx = ( ( px + c - > left + dxbias ) * ( int ) vid . width ) / ( float ) vid . rotpixelwidth ;
sy = ( ( py + c - > top + dxbias ) * ( int ) vid . height ) / ( float ) vid . rotpixelheight ;
sw = ( ( c - > bmw ) * vid . width ) / ( float ) vid . rotpixelwidth ;
sh = ( ( c - > bmh ) * vid . height ) / ( float ) vid . rotpixelheight ;
v = Font_BeginChar ( font - > singletexture ) ;
break ;
default :
sx = ( ( px + c - > left + dxbias ) * ( int ) vid . width ) / ( float ) vid . rotpixelwidth ;
sy = ( ( py + c - > top + dxbias ) * ( int ) vid . height ) / ( float ) vid . rotpixelheight ;
sw = ( ( c - > bmw ) * vid . width ) / ( float ) vid . rotpixelwidth ;
sh = ( ( c - > bmh ) * vid . height ) / ( float ) vid . rotpixelheight ;
v = Font_BeginChar ( fontplanes . texnum [ c - > texplane ] ) ;
break ;
}
font_texcoord [ v + 0 ] [ 0 ] = s0 ;
font_texcoord [ v + 0 ] [ 1 ] = t0 ;
font_texcoord [ v + 1 ] [ 0 ] = s1 ;
font_texcoord [ v + 1 ] [ 1 ] = t0 ;
font_texcoord [ v + 2 ] [ 0 ] = s1 ;
font_texcoord [ v + 2 ] [ 1 ] = t1 ;
font_texcoord [ v + 3 ] [ 0 ] = s0 ;
font_texcoord [ v + 3 ] [ 1 ] = t1 ;
font_coord [ v + 0 ] [ 0 ] = sx ;
font_coord [ v + 0 ] [ 1 ] = sy ;
font_coord [ v + 1 ] [ 0 ] = sx + sw ;
font_coord [ v + 1 ] [ 1 ] = sy ;
font_coord [ v + 2 ] [ 0 ] = sx + sw ;
font_coord [ v + 2 ] [ 1 ] = sy + sh ;
font_coord [ v + 3 ] [ 0 ] = sx ;
font_coord [ v + 3 ] [ 1 ] = sy + sh ;
2018-11-27 16:48:19 +00:00
if ( c - > flags & CHARF_FORCEWHITE )
{
* ( int * ) font_forecoloura [ v + 0 ] =
* ( int * ) font_forecoloura [ v + 1 ] =
* ( int * ) font_forecoloura [ v + 2 ] =
* ( int * ) font_forecoloura [ v + 3 ] = 0xffffffff ;
}
else
{
* ( int * ) font_forecoloura [ v + 0 ] =
* ( int * ) font_forecoloura [ v + 1 ] =
* ( int * ) font_forecoloura [ v + 2 ] =
* ( int * ) font_forecoloura [ v + 3 ] = * ( int * ) font_forecolour ;
}
2017-03-04 19:36:06 +00:00
if ( font_colourmask & CON_NONCLEARBG )
{
sx = ( ( px + dxbias ) * ( int ) vid . width ) / ( float ) vid . rotpixelwidth ;
sy = ( ( py + dxbias ) * ( int ) vid . height ) / ( float ) vid . rotpixelheight ;
sw = sx + ( ( c - > advance ) * vid . width ) / ( float ) vid . rotpixelwidth ;
sh = sy + ( ( font - > charheight ) * vid . height ) / ( float ) vid . rotpixelheight ;
//don't care about texcoords
font_backcoord [ v + 0 ] [ 0 ] = sx ;
font_backcoord [ v + 0 ] [ 1 ] = sy ;
font_backcoord [ v + 1 ] [ 0 ] = sw ;
font_backcoord [ v + 1 ] [ 1 ] = sy ;
font_backcoord [ v + 2 ] [ 0 ] = sw ;
font_backcoord [ v + 2 ] [ 1 ] = sh ;
font_backcoord [ v + 3 ] [ 0 ] = sx ;
font_backcoord [ v + 3 ] [ 1 ] = sh ;
* ( int * ) font_backcoloura [ v + 0 ] = * ( int * ) font_backcolour ;
* ( int * ) font_backcoloura [ v + 1 ] = * ( int * ) font_backcolour ;
* ( int * ) font_backcoloura [ v + 2 ] = * ( int * ) font_backcolour ;
* ( int * ) font_backcoloura [ v + 3 ] = * ( int * ) font_backcolour ;
}
2016-09-01 14:31:24 +00:00
return nextx ;
}
/*there is no sane way to make this pixel-correct*/
float Font_DrawScaleChar ( float px , float py , unsigned int charflags , unsigned int codepoint )
{
struct charcache_s * c ;
float s0 , s1 ;
float t0 , t1 ;
float nextx ;
float sx , sy , sw , sh ;
int col ;
int v ;
struct font_s * font = curfont ;
float cw , ch ;
# ifdef D3D11QUAKE
float dxbias = 0 ; //(qrenderer == QR_DIRECT3D11)?0.5:0;
# else
# define dxbias 0
# endif
// if (!curfont_scaled)
// return Font_DrawChar(px, py, charcode);
if ( charflags & CON_2NDCHARSETTEXT )
{
if ( font - > alt )
{
font = font - > alt ;
charflags & = ~ CON_2NDCHARSETTEXT ;
}
else if ( codepoint > = 0xe000 & & codepoint < = 0xe0ff )
charflags & = ~ CON_2NDCHARSETTEXT ; //don't double-dip
}
cw = curfont_scale [ 0 ] ;
ch = curfont_scale [ 1 ] ;
//crash if there is no current font.
c = Font_GetChar ( font , codepoint ) ;
if ( ! c )
return px ;
nextx = px + c - > advance * cw ;
2017-03-04 19:36:06 +00:00
if ( codepoint = = ' ' & & ( charflags & ( CON_RICHFORECOLOUR | CON_NONCLEARBG ) ) ! = CON_NONCLEARBG )
2016-09-01 14:31:24 +00:00
return nextx ;
if ( charflags & CON_BLINKTEXT )
{
if ( ! cl_noblink . ival )
if ( ( int ) ( realtime * 3 ) & 1 )
return nextx ;
}
if ( charflags & CON_RICHFORECOLOUR )
{
col = charflags & ( CON_2NDCHARSETTEXT | CON_RICHFORECOLOUR | ( 0xfff < < CON_RICHBSHIFT ) ) ;
if ( col ! = font_colourmask )
{
vec4_t rgba ;
if ( font_backcolour [ 3 ] )
{
Font_Flush ( ) ;
R2D_Flush = Font_Flush ;
}
font_colourmask = col ;
rgba [ 0 ] = ( ( col > > CON_RICHRSHIFT ) & 0xf ) * 0x11 ;
rgba [ 1 ] = ( ( col > > CON_RICHGSHIFT ) & 0xf ) * 0x11 ;
rgba [ 2 ] = ( ( col > > CON_RICHBSHIFT ) & 0xf ) * 0x11 ;
rgba [ 3 ] = 255 ;
font_backcolour [ 0 ] = 0 ;
font_backcolour [ 1 ] = 0 ;
font_backcolour [ 2 ] = 0 ;
font_backcolour [ 3 ] = 0 ;
if ( charflags & CON_2NDCHARSETTEXT )
{
rgba [ 0 ] * = font - > alttint [ 0 ] ;
rgba [ 1 ] * = font - > alttint [ 1 ] ;
rgba [ 2 ] * = font - > alttint [ 2 ] ;
}
else
{
rgba [ 0 ] * = font - > tint [ 0 ] ;
rgba [ 1 ] * = font - > tint [ 1 ] ;
rgba [ 2 ] * = font - > tint [ 2 ] ;
}
rgba [ 0 ] * = font_foretint [ 0 ] ;
rgba [ 1 ] * = font_foretint [ 1 ] ;
rgba [ 2 ] * = font_foretint [ 2 ] ;
rgba [ 3 ] * = font_foretint [ 3 ] ;
font_forecolour [ 0 ] = min ( rgba [ 0 ] , 255 ) ;
font_forecolour [ 1 ] = min ( rgba [ 1 ] , 255 ) ;
font_forecolour [ 2 ] = min ( rgba [ 2 ] , 255 ) ;
font_forecolour [ 3 ] = min ( rgba [ 3 ] , 255 ) ;
}
}
else
{
col = charflags & ( CON_2NDCHARSETTEXT | CON_NONCLEARBG | CON_BGMASK | CON_FGMASK | CON_HALFALPHA ) ;
if ( col ! = font_colourmask )
{
vec4_t rgba ;
if ( font_backcolour [ 3 ] ! = ( ( charflags & CON_NONCLEARBG ) ? 127 : 0 ) )
{
Font_Flush ( ) ;
R2D_Flush = Font_Flush ;
}
font_colourmask = col ;
col = ( charflags & CON_FGMASK ) > > CON_FGSHIFT ;
2017-12-09 21:22:46 +00:00
if ( charflags & CON_HALFALPHA )
{
rgba [ 0 ] = consolecolours [ col ] . fr * 0x7f ;
rgba [ 1 ] = consolecolours [ col ] . fg * 0x7f ;
rgba [ 2 ] = consolecolours [ col ] . fb * 0x7f ;
rgba [ 3 ] = 0x7f ;
}
else
{
rgba [ 0 ] = consolecolours [ col ] . fr * 255 ;
rgba [ 1 ] = consolecolours [ col ] . fg * 255 ;
rgba [ 2 ] = consolecolours [ col ] . fb * 255 ;
rgba [ 3 ] = 255 ;
}
2016-09-01 14:31:24 +00:00
col = ( charflags & CON_BGMASK ) > > CON_BGSHIFT ;
2017-12-09 21:22:46 +00:00
if ( charflags & CON_NONCLEARBG )
{
font_backcolour [ 0 ] = consolecolours [ col ] . fr * 0xc0 ;
font_backcolour [ 1 ] = consolecolours [ col ] . fg * 0xc0 ;
font_backcolour [ 2 ] = consolecolours [ col ] . fb * 0xc0 ;
font_backcolour [ 3 ] = 0xc0 ;
}
else
Vector4Set ( font_backcolour , 0 , 0 , 0 , 0 ) ;
2016-09-01 14:31:24 +00:00
if ( charflags & CON_2NDCHARSETTEXT )
{
rgba [ 0 ] * = font - > alttint [ 0 ] ;
rgba [ 1 ] * = font - > alttint [ 1 ] ;
rgba [ 2 ] * = font - > alttint [ 2 ] ;
}
else
{
rgba [ 0 ] * = font - > tint [ 0 ] ;
rgba [ 1 ] * = font - > tint [ 1 ] ;
rgba [ 2 ] * = font - > tint [ 2 ] ;
}
rgba [ 0 ] * = font_foretint [ 0 ] ;
rgba [ 1 ] * = font_foretint [ 1 ] ;
rgba [ 2 ] * = font_foretint [ 2 ] ;
rgba [ 3 ] * = font_foretint [ 3 ] ;
font_forecolour [ 0 ] = min ( rgba [ 0 ] , 255 ) ;
font_forecolour [ 1 ] = min ( rgba [ 1 ] , 255 ) ;
font_forecolour [ 2 ] = min ( rgba [ 2 ] , 255 ) ;
font_forecolour [ 3 ] = min ( rgba [ 3 ] , 255 ) ;
}
}
s0 = ( float ) c - > bmx / PLANEWIDTH ;
t0 = ( float ) c - > bmy / PLANEWIDTH ;
s1 = ( float ) ( c - > bmx + c - > bmw ) / PLANEWIDTH ;
t1 = ( float ) ( c - > bmy + c - > bmh ) / PLANEWIDTH ;
if ( c - > texplane > = DEFAULTPLANE )
{
sx = ( ( px + c - > left * cw ) ) ;
sy = ( ( py + c - > top * ch ) ) ;
sw = ( ( font - > charheight * cw ) ) ;
sh = ( ( font - > charheight * ch ) ) ;
if ( c - > texplane = = DEFAULTPLANE )
v = Font_BeginChar ( fontplanes . defaultfont ) ;
else
v = Font_BeginChar ( font - > singletexture ) ;
}
else
{
sx = ( px + c - > left * cw ) ;
sy = ( py + c - > top * ch ) ;
sw = ( ( c - > bmw * cw ) ) ;
sh = ( ( c - > bmh * ch ) ) ;
v = Font_BeginChar ( fontplanes . texnum [ c - > texplane ] ) ;
}
sx + = dxbias ;
sy + = dxbias ;
sx * = ( int ) vid . width / ( float ) vid . rotpixelwidth ;
sy * = ( int ) vid . height / ( float ) vid . rotpixelheight ;
sw * = ( int ) vid . width / ( float ) vid . rotpixelwidth ;
sh * = ( int ) vid . height / ( float ) vid . rotpixelheight ;
font_texcoord [ v + 0 ] [ 0 ] = s0 ;
font_texcoord [ v + 0 ] [ 1 ] = t0 ;
font_texcoord [ v + 1 ] [ 0 ] = s1 ;
font_texcoord [ v + 1 ] [ 1 ] = t0 ;
font_texcoord [ v + 2 ] [ 0 ] = s1 ;
font_texcoord [ v + 2 ] [ 1 ] = t1 ;
font_texcoord [ v + 3 ] [ 0 ] = s0 ;
font_texcoord [ v + 3 ] [ 1 ] = t1 ;
font_coord [ v + 0 ] [ 0 ] = sx ;
font_coord [ v + 0 ] [ 1 ] = sy ;
font_coord [ v + 1 ] [ 0 ] = sx + sw ;
font_coord [ v + 1 ] [ 1 ] = sy ;
font_coord [ v + 2 ] [ 0 ] = sx + sw ;
font_coord [ v + 2 ] [ 1 ] = sy + sh ;
font_coord [ v + 3 ] [ 0 ] = sx ;
font_coord [ v + 3 ] [ 1 ] = sy + sh ;
* ( int * ) font_forecoloura [ v + 0 ] = * ( int * ) font_forecolour ;
* ( int * ) font_forecoloura [ v + 1 ] = * ( int * ) font_forecolour ;
* ( int * ) font_forecoloura [ v + 2 ] = * ( int * ) font_forecolour ;
* ( int * ) font_forecoloura [ v + 3 ] = * ( int * ) font_forecolour ;
2017-03-04 19:36:06 +00:00
if ( font_colourmask & CON_NONCLEARBG )
{
sx = px + dxbias ;
sy = py + dxbias ;
sw = sx + c - > advance ;
sh = sy + font - > charheight ;
sx * = ( int ) vid . width / ( float ) vid . rotpixelwidth ;
sy * = ( int ) vid . height / ( float ) vid . rotpixelheight ;
sw * = ( int ) vid . width / ( float ) vid . rotpixelwidth ;
sh * = ( int ) vid . height / ( float ) vid . rotpixelheight ;
//don't care about texcoords
font_backcoord [ v + 0 ] [ 0 ] = sx ;
font_backcoord [ v + 0 ] [ 1 ] = sy ;
font_backcoord [ v + 1 ] [ 0 ] = sw ;
font_backcoord [ v + 1 ] [ 1 ] = sy ;
font_backcoord [ v + 2 ] [ 0 ] = sw ;
font_backcoord [ v + 2 ] [ 1 ] = sh ;
font_backcoord [ v + 3 ] [ 0 ] = sx ;
font_backcoord [ v + 3 ] [ 1 ] = sh ;
* ( int * ) font_backcoloura [ v + 0 ] = * ( int * ) font_backcolour ;
* ( int * ) font_backcoloura [ v + 1 ] = * ( int * ) font_backcolour ;
* ( int * ) font_backcoloura [ v + 2 ] = * ( int * ) font_backcolour ;
* ( int * ) font_backcoloura [ v + 3 ] = * ( int * ) font_backcolour ;
}
2016-09-01 14:31:24 +00:00
return nextx ;
}
# endif //!SERVERONLY