diff options
Diffstat (limited to 'vgui2/vgui_surfacelib/FontManager.cpp')
| -rw-r--r-- | vgui2/vgui_surfacelib/FontManager.cpp | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/vgui2/vgui_surfacelib/FontManager.cpp b/vgui2/vgui_surfacelib/FontManager.cpp new file mode 100644 index 0000000..8942c15 --- /dev/null +++ b/vgui2/vgui_surfacelib/FontManager.cpp @@ -0,0 +1,787 @@ +//========= 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 + |