|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=====================================================================================//
#include <locale.h>
#include "vgui_surfacelib/BitmapFont.h"
#include "vgui_surfacelib/FontManager.h"
#include <vgui/ISurface.h>
#include <tier0/dbg.h>
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
static CFontManager s_FontManager;
#ifdef WIN32
extern bool s_bSupportsUnicode; #endif
#if !defined( _X360 )
#define MAX_INITIAL_FONTS 100
#else
#define MAX_INITIAL_FONTS 1
#endif
//-----------------------------------------------------------------------------
// Purpose: singleton accessor
//-----------------------------------------------------------------------------
CFontManager &FontManager() { return s_FontManager; }
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CFontManager::CFontManager() { // add a single empty font, to act as an invalid font handle 0
m_FontAmalgams.EnsureCapacity( MAX_INITIAL_FONTS ); m_FontAmalgams.AddToTail(); m_Win32Fonts.EnsureCapacity( MAX_INITIAL_FONTS );
#ifdef LINUX
FT_Error error = FT_Init_FreeType( &library ); if ( error ) Error( "Unable to initalize freetype library, is it installed?" ); m_pFontDataHelper = NULL; #endif
// setup our text locale
setlocale( LC_CTYPE, "" ); setlocale( LC_TIME, "" ); setlocale( LC_COLLATE, "" ); setlocale( LC_MONETARY, "" );
m_pFileSystem = NULL; m_pMaterialSystem = NULL; }
//-----------------------------------------------------------------------------
// Purpose: language setting for font fallbacks
//-----------------------------------------------------------------------------
void CFontManager::SetLanguage(const char *language) { Q_strncpy(m_szLanguage, language, sizeof(m_szLanguage)); }
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CFontManager::~CFontManager() { ClearAllFonts(); m_FontAmalgams.RemoveAll(); #ifdef LINUX
FT_Done_FreeType( library ); #endif
}
//-----------------------------------------------------------------------------
// Purpose: frees the fonts
//-----------------------------------------------------------------------------
void CFontManager::ClearAllFonts() { // free the fonts
for (int i = 0; i < m_Win32Fonts.Count(); i++) { delete m_Win32Fonts[i]; } m_Win32Fonts.RemoveAll();
for (int i = 0; i < m_FontAmalgams.Count(); i++) { m_FontAmalgams[i].RemoveAll(); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
vgui::HFont CFontManager::CreateFont() { int i = m_FontAmalgams.AddToTail(); return i; }
//-----------------------------------------------------------------------------
// Purpose: Sets the valid glyph ranges for a font created by CreateFont()
//-----------------------------------------------------------------------------
bool CFontManager::SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags) { return SetFontGlyphSet( font, windowsFontName, tall, weight, blur, scanlines, flags, 0, 0); }
//-----------------------------------------------------------------------------
// Purpose: Sets the valid glyph ranges for a font created by CreateFont()
//-----------------------------------------------------------------------------
bool CFontManager::SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin, int nRangeMax) { // ignore all but the first font added
// need to rev vgui versions and change the name of this function
if ( m_FontAmalgams[font].GetCount() > 0 ) { // clear any existing fonts
m_FontAmalgams[font].RemoveAll(); }
bool bForceSingleFontForXbox = false; if ( IsX360() ) { // discovered xbox only allows glyphs from these languages from the foreign fallback font
// prefer to have the entire range of chars from the font so UI doesn't suffer from glyph disparity
if ( !V_stricmp( windowsFontName, "toolbox" ) ) { // only the toolbox font is allowed to pass
} else { if ( !V_stricmp( m_szLanguage, "polish" ) || !V_stricmp( m_szLanguage, "russian" ) || !V_stricmp( m_szLanguage, "japanese" ) || !V_stricmp( m_szLanguage, "korean" ) || !V_stricmp( m_szLanguage, "portuguese" ) || !V_stricmp( m_szLanguage, "schinese" ) || !V_stricmp( m_szLanguage, "tchinese" ) ) { windowsFontName = GetForeignFallbackFontName(); bForceSingleFontForXbox = true; } } } font_t *winFont = CreateOrFindWin32Font( windowsFontName, tall, weight, blur, scanlines, flags );
// cycle until valid english/extended font support has been created
do { // add to the amalgam
if ( bForceSingleFontForXbox || IsFontForeignLanguageCapable( windowsFontName ) ) { if ( winFont ) { // font supports the full range of characters
m_FontAmalgams[font].AddFont( winFont, 0x0000, 0xFFFF ); return true; } } else { // font cannot provide glyphs and just supports the normal range
// redirect to a font that can supply glyps
const char *localizedFontName = GetForeignFallbackFontName(); if ( winFont && !stricmp( localizedFontName, windowsFontName ) ) { // it's the same font and can support the full range
m_FontAmalgams[font].AddFont( winFont, 0x0000, 0xFFFF ); return true; }
// create the extended support font
font_t *pExtendedFont = CreateOrFindWin32Font( localizedFontName, tall, weight, blur, scanlines, flags ); if ( winFont && pExtendedFont ) { // use the normal font for english characters, and the extended font for the rest
int nMin = 0x0000, nMax = 0x00FF;
// did we specify a range?
if ( nRangeMin > 0 || nRangeMax > 0 ) { nMin = nRangeMin; nMax = nRangeMax;
// make sure they're in the correct order
if ( nMin > nMax ) { int nTemp = nMin; nMin = nMax; nMax = nTemp; } }
if ( nMin > 0 ) { m_FontAmalgams[font].AddFont( pExtendedFont, 0x0000, nMin - 1 ); }
m_FontAmalgams[font].AddFont( winFont, nMin, nMax );
if ( nMax < 0xFFFF ) { m_FontAmalgams[font].AddFont( pExtendedFont, nMax + 1, 0xFFFF ); }
return true; } else if ( pExtendedFont ) { // the normal font failed to create
// just use the extended font for the full range
m_FontAmalgams[font].AddFont( pExtendedFont, 0x0000, 0xFFFF ); return true; } } // no valid font has been created, so fallback to a different font and try again
} while ( NULL != ( windowsFontName = GetFallbackFontName( windowsFontName ) ) );
// nothing successfully created
return false; }
//-----------------------------------------------------------------------------
// Purpose: adds glyphs to a font created by CreateFont()
//-----------------------------------------------------------------------------
bool CFontManager::SetBitmapFontGlyphSet(HFont font, const char *windowsFontName, float scalex, float scaley, int flags) { if ( m_FontAmalgams[font].GetCount() > 0 ) { // clear any existing fonts
m_FontAmalgams[font].RemoveAll(); }
CBitmapFont *winFont = CreateOrFindBitmapFont( windowsFontName, scalex, scaley, flags ); if ( winFont ) { // bitmap fonts are only 0-255
m_FontAmalgams[font].AddFont( winFont, 0x0000, 0x00FF ); return true; }
// nothing successfully created
return false; }
//-----------------------------------------------------------------------------
// Purpose: Creates a new win32 font, or reuses one if possible
//-----------------------------------------------------------------------------
font_t *CFontManager::CreateOrFindWin32Font(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags) { // see if we already have the win32 font
font_t *winFont = NULL; int i; for (i = 0; i < m_Win32Fonts.Count(); i++) { if (m_Win32Fonts[i]->IsEqualTo(windowsFontName, tall, weight, blur, scanlines, flags)) { winFont = m_Win32Fonts[i]; break; } }
// create the new win32font if we didn't find it
if (!winFont) { MEM_ALLOC_CREDIT();
i = m_Win32Fonts.AddToTail(); m_Win32Fonts[i] = NULL;
#ifdef LINUX
int memSize = 0; void *pchFontData = m_pFontDataHelper( windowsFontName, memSize, NULL );
if( !pchFontData ) { // If we didn't find the font data in the font cache, then get the font filename...
char *filename = CLinuxFont::GetFontFileName( windowsFontName, flags ); if( filename ) { // ... and try to add it to the font cache.
pchFontData = m_pFontDataHelper( windowsFontName, memSize, filename ); free( filename ); } }
if ( pchFontData ) { m_Win32Fonts[i] = new font_t(); if (m_Win32Fonts[i]->CreateFromMemory( windowsFontName, pchFontData, memSize, tall, weight, blur, scanlines, flags)) { // add to the list
winFont = m_Win32Fonts[i]; } }
if( !winFont ) { // failed to create, remove
if ( m_Win32Fonts[i] ) delete m_Win32Fonts[i]; m_Win32Fonts.Remove(i); return NULL; }
#else
m_Win32Fonts[i] = new font_t(); if (m_Win32Fonts[i]->Create(windowsFontName, tall, weight, blur, scanlines, flags)) { // add to the list
winFont = m_Win32Fonts[i]; } else { // failed to create, remove
delete m_Win32Fonts[i]; m_Win32Fonts.Remove(i); return NULL; } #endif
}
return winFont; }
//-----------------------------------------------------------------------------
// Purpose: Creates a new win32 font, or reuses one if possible
//-----------------------------------------------------------------------------
CBitmapFont *CFontManager::CreateOrFindBitmapFont(const char *windowsFontName, float scalex, float scaley, int flags) { // see if we already have the font
CBitmapFont *winFont = NULL; int i; for ( i = 0; i < m_Win32Fonts.Count(); i++ ) { font_t *font = m_Win32Fonts[i];
// Only looking for bitmap fonts
int testflags = font->GetFlags(); if ( !( testflags & vgui::ISurface::FONTFLAG_BITMAP ) ) { continue; }
CBitmapFont *bitmapFont = reinterpret_cast< CBitmapFont* >( font ); if ( bitmapFont->IsEqualTo( windowsFontName, scalex, scaley, flags ) ) { winFont = bitmapFont; break; } }
// create the font if we didn't find it
if ( !winFont ) { MEM_ALLOC_CREDIT();
i = m_Win32Fonts.AddToTail();
CBitmapFont *bitmapFont = new CBitmapFont(); if ( bitmapFont->Create( windowsFontName, scalex, scaley, flags ) ) { // add to the list
m_Win32Fonts[i] = bitmapFont; winFont = bitmapFont; } else { // failed to create, remove
delete bitmapFont; m_Win32Fonts.Remove(i); return NULL; } }
return winFont; }
//-----------------------------------------------------------------------------
// Purpose: sets the scale of a bitmap font
//-----------------------------------------------------------------------------
void CFontManager::SetFontScale(vgui::HFont font, float sx, float sy) { m_FontAmalgams[font].SetFontScale( sx, sy ); }
const char *CFontManager::GetFontName( HFont font ) { // ignore the amalgam of disparate char ranges, assume the first font
return m_FontAmalgams[font].GetFontName( 0 ); }
const char *CFontManager::GetFontFamilyName( HFont font ) { return m_FontAmalgams[font].GetFontFamilyName( 0 ); }
//-----------------------------------------------------------------------------
// Purpose: gets the windows font for the particular font in the amalgam
//-----------------------------------------------------------------------------
font_t *CFontManager::GetFontForChar(vgui::HFont font, wchar_t wch) { return m_FontAmalgams[font].GetFontForChar(wch); }
//-----------------------------------------------------------------------------
// Purpose: returns the abc widths of a single character
//-----------------------------------------------------------------------------
void CFontManager::GetCharABCwide(HFont font, int ch, int &a, int &b, int &c) { if ( !m_FontAmalgams.IsValidIndex( font ) ) { a = b = c = 0; return; }
font_t *winFont = m_FontAmalgams[font].GetFontForChar(ch); if (winFont) { winFont->GetCharABCWidths(ch, a, b, c); } else { // no font for this range, just use the default width
a = c = 0; b = m_FontAmalgams[font].GetFontMaxWidth(); } }
//-----------------------------------------------------------------------------
// Purpose: returns the max height of a font
//-----------------------------------------------------------------------------
int CFontManager::GetFontTall(HFont font) { return m_FontAmalgams[font].GetFontHeight(); }
//-----------------------------------------------------------------------------
// Purpose: returns requested height of the font
//-----------------------------------------------------------------------------
int CFontManager::GetFontTallRequested(HFont font) { return m_FontAmalgams[font].GetFontHeightRequested(); }
//-----------------------------------------------------------------------------
// Purpose: returns the ascent of a font
//-----------------------------------------------------------------------------
int CFontManager::GetFontAscent(HFont font, wchar_t wch) { font_t *winFont = m_FontAmalgams[font].GetFontForChar(wch); if ( winFont ) { return winFont->GetAscent(); } else { return 0; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CFontManager::IsFontAdditive(HFont font) { return ( m_FontAmalgams[font].GetFlags( 0 ) & vgui::ISurface::FONTFLAG_ADDITIVE ) ? true : false; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CFontManager::IsBitmapFont(HFont font) { // A FontAmalgam is either some number of non-bitmap fonts, or a single bitmap font - so this check is valid
return ( m_FontAmalgams[font].GetFlags( 0 ) & vgui::ISurface::FONTFLAG_BITMAP ) ? true : false; }
//-----------------------------------------------------------------------------
// Purpose: returns the pixel width of a single character
//-----------------------------------------------------------------------------
int CFontManager::GetCharacterWidth(HFont font, int ch) { if ( !iswcntrl( ch ) ) { int a, b, c; GetCharABCwide(font, ch, a, b, c); return (a + b + c); } return 0; }
//-----------------------------------------------------------------------------
// Purpose: returns the area of a text string, including newlines
//-----------------------------------------------------------------------------
void CFontManager::GetTextSize(HFont font, const wchar_t *text, int &wide, int &tall) { wide = 0; tall = 0; if (!text) return;
tall = GetFontTall(font); float xx = 0; char chBefore = 0; char chAfter = 0; for (int i = 0; ; i++) { wchar_t ch = text[i]; if (ch == 0) { break; }
chAfter = text[i+1];
if (ch == '\n') { tall += GetFontTall(font); xx=0; } else if (ch == '&') { // underscore character, so skip
} else { float flWide, flabcA, flabcC; GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA, flabcC ); xx += flWide; if (xx > wide) { wide = ceil(xx); } } chBefore = ch; } }
// font validation functions
struct FallbackFont_t { const char *font; const char *fallbackFont; };
#ifdef WIN32
const char *g_szValidAsianFonts[] = { "Marlett", NULL }; // list of how fonts fallback
FallbackFont_t g_FallbackFonts[] = { { "Times New Roman", "Courier New" }, { "Courier New", "Courier" }, { "Verdana", "Arial" }, { "Trebuchet MS", "Arial" }, { "Tahoma", NULL }, { NULL, "Tahoma" }, // every other font falls back to this
}; #elif defined(OSX)
static const char *g_szValidAsianFonts[] = { "Apple Symbols", NULL }; // list of how fonts fallback
FallbackFont_t g_FallbackFonts[] = { { "Marlett", "Apple Symbols" }, { "Lucida Console", "Lucida Grande" }, { "Tahoma", "Helvetica" }, { "Helvetica", "Monaco" }, { "Monaco", NULL }, { NULL, "Monaco" } // every other font falls back to this
};
#elif defined(LINUX)
static const char *g_szValidAsianFonts[] = { "Marlett", "WenQuanYi Zen Hei", "unifont", NULL };
// list of how fonts fallback
FallbackFont_t g_FallbackFonts[] = { { "DejaVu Sans", NULL }, { NULL, "DejaVu Sans" }, // every other font falls back to this
}; #elif defined(_PS3)
// list of how fonts fallback
FallbackFont_t g_FallbackFonts[] = { { NULL, "Tahoma" }, // every other font falls back to this
}; #else
#error
#endif
//-----------------------------------------------------------------------------
// Purpose: returns true if the font is in the list of OK asian fonts
//-----------------------------------------------------------------------------
bool CFontManager::IsFontForeignLanguageCapable(const char *windowsFontName) { if ( IsX360() ) { return false; }
for (int i = 0; g_szValidAsianFonts[i] != NULL; i++) { if (!stricmp(g_szValidAsianFonts[i], windowsFontName)) return true; }
// typeface isn't supported by asian languages
return false; }
//-----------------------------------------------------------------------------
// Purpose: fallback fonts
//-----------------------------------------------------------------------------
const char *CFontManager::GetFallbackFontName(const char *windowsFontName) { int i; for ( i = 0; g_FallbackFonts[i].font != NULL; i++ ) { if (!stricmp(g_FallbackFonts[i].font, windowsFontName)) return g_FallbackFonts[i].fallbackFont; }
// the ultimate fallback
return g_FallbackFonts[i].fallbackFont; }
//-----------------------------------------------------------------------------
// Purpose: specialized fonts
//-----------------------------------------------------------------------------
const char *CFontManager::GetForeignFallbackFontName() { #ifdef WIN32
// tahoma has all the necessary characters for asian/russian languages for winXP/2K+
return "Tahoma"; #elif defined(OSX)
return "Helvetica"; #elif defined(LINUX)
return "WenQuanYi Zen Hei"; #elif defined(_PS3)
return "Tahoma"; #else
#error
#endif
}
#if defined( _X360 )
bool CFontManager::GetCachedXUIMetrics( const char *pFontName, int tall, int style, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] ) { // linear lookup is good enough
CUtlSymbol fontSymbol = pFontName; bool bFound = false; int i; for ( i = 0; i < m_XUIMetricCache.Count(); i++ ) { if ( m_XUIMetricCache[i].fontSymbol == fontSymbol && m_XUIMetricCache[i].tall == tall && m_XUIMetricCache[i].style == style ) { bFound = true; break; } } if ( !bFound ) { return false; }
// get from the cache
*pFontMetrics = m_XUIMetricCache[i].fontMetrics; V_memcpy( charMetrics, m_XUIMetricCache[i].charMetrics, 256 * sizeof( XUICharMetrics ) ); return true; } #endif
#if defined( _X360 )
void CFontManager::SetCachedXUIMetrics( const char *pFontName, int tall, int style, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] ) { MEM_ALLOC_CREDIT();
int i = m_XUIMetricCache.AddToTail();
m_XUIMetricCache[i].fontSymbol = pFontName; m_XUIMetricCache[i].tall = tall; m_XUIMetricCache[i].style = style; m_XUIMetricCache[i].fontMetrics = *pFontMetrics; V_memcpy( m_XUIMetricCache[i].charMetrics, charMetrics, 256 * sizeof( XUICharMetrics ) ); } #endif
void CFontManager::ClearTemporaryFontCache() { #if defined( _X360 )
COM_TimestampedLog( "ClearTemporaryFontCache(): Start" );
m_XUIMetricCache.Purge();
// many fonts are blindly precached by vgui and never used
// font will re-open if glyph is actually requested
for ( int i = 0; i < m_Win32Fonts.Count(); i++ ) { m_Win32Fonts[i]->CloseResource(); }
COM_TimestampedLog( "ClearTemporaryFontCache(): Finish" ); #endif
}
//-----------------------------------------------------------------------------
// Purpose: returns the max height of a font
//-----------------------------------------------------------------------------
bool CFontManager::GetFontUnderlined( HFont font ) { return m_FontAmalgams[font].GetUnderlined(); }
void CFontManager::GetKernedCharWidth( vgui::HFont font, wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &flabcA, float &flabcC ) { wide = 0.0f; flabcA = 0.0f; Assert( font != vgui::INVALID_FONT ); if ( font == vgui::INVALID_FONT ) return; font_t *pFont = m_FontAmalgams[font].GetFontForChar(ch); if ( !pFont ) { // no font for this range, just use the default width
flabcA = 0.0f; wide = m_FontAmalgams[font].GetFontMaxWidth(); return; } if ( m_FontAmalgams[font].GetFontForChar( chBefore ) != pFont ) chBefore = 0; if ( m_FontAmalgams[font].GetFontForChar( chAfter ) != pFont ) chAfter = 0; #if defined(LINUX)
pFont->GetKernedCharWidth( ch, chBefore, chAfter, wide, flabcA, flabcC ); #else
pFont->GetKernedCharWidth( ch, chBefore, chAfter, wide, flabcA ); #endif
}
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Ensure that all of our internal structures are consistent, and
// account for all memory that we've allocated.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CFontManager::Validate( CValidator &validator, char *pchName ) { validator.Push( "CFontManager", this, pchName );
ValidateObj( m_FontAmalgams ); for ( int iFont = 0; iFont < m_FontAmalgams.Count(); iFont++ ) { ValidateObj( m_FontAmalgams[iFont] ); }
ValidateObj( m_Win32Fonts ); for ( int iWin32Font = 0; iWin32Font < m_Win32Fonts.Count(); iWin32Font++ ) { ValidatePtr( m_Win32Fonts[ iWin32Font ] ); }
validator.Pop(); } #endif // DBGFLAG_VALIDATE
|