summaryrefslogtreecommitdiff
path: root/vgui2/vgui_surfacelib
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vgui2/vgui_surfacelib
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'vgui2/vgui_surfacelib')
-rw-r--r--vgui2/vgui_surfacelib/BitmapFont.cpp324
-rw-r--r--vgui2/vgui_surfacelib/FontAmalgam.cpp227
-rw-r--r--vgui2/vgui_surfacelib/FontEffects.cpp241
-rw-r--r--vgui2/vgui_surfacelib/FontEffects.h20
-rw-r--r--vgui2/vgui_surfacelib/FontManager.cpp787
-rw-r--r--vgui2/vgui_surfacelib/Win32Font.cpp638
-rw-r--r--vgui2/vgui_surfacelib/Win32Font_x360.cpp346
-rw-r--r--vgui2/vgui_surfacelib/linuxfont.cpp777
-rw-r--r--vgui2/vgui_surfacelib/osxfont.cpp769
-rw-r--r--vgui2/vgui_surfacelib/vgui_surfacelib.vpc45
10 files changed, 4174 insertions, 0 deletions
diff --git a/vgui2/vgui_surfacelib/BitmapFont.cpp b/vgui2/vgui_surfacelib/BitmapFont.cpp
new file mode 100644
index 0000000..36d6be9
--- /dev/null
+++ b/vgui2/vgui_surfacelib/BitmapFont.cpp
@@ -0,0 +1,324 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: XBox Compiled Bitmap Fonts
+//
+//=============================================================================//
+
+// conversion from 'double' to 'float', possible loss of data
+#pragma warning( disable : 4244 )
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <malloc.h>
+#include "vgui_surfacelib/BitmapFont.h"
+#include "vgui_surfacelib/FontManager.h"
+#include <tier0/dbg.h>
+#include <vgui/ISurface.h>
+#include <tier0/mem.h>
+#include <utlbuffer.h>
+#include "filesystem.h"
+#include "materialsystem/itexture.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+struct BitmapFontTable_t
+{
+ BitmapFontTable_t()
+ {
+ m_szName = UTL_INVAL_SYMBOL;
+ m_pBitmapFont = NULL;
+ m_pBitmapGlyphs = NULL;
+ m_pTexture = NULL;
+ }
+
+ CUtlSymbol m_szName;
+ BitmapFont_t *m_pBitmapFont;
+ BitmapGlyph_t *m_pBitmapGlyphs;
+ ITexture *m_pTexture;
+};
+
+static CUtlVector< BitmapFontTable_t > g_BitmapFontTable( 1, 4 );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CBitmapFont::CBitmapFont()
+{
+ m_scalex = 1.0f;
+ m_scaley = 1.0f;
+
+ m_bitmapFontHandle = g_BitmapFontTable.InvalidIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CBitmapFont::~CBitmapFont()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: creates the font. returns false if the font cannot be mounted.
+//-----------------------------------------------------------------------------
+bool CBitmapFont::Create( const char *pFontFilename, float scalex, float scaley, int flags )
+{
+ MEM_ALLOC_CREDIT();
+
+ if ( !pFontFilename || !pFontFilename[0] )
+ {
+ return false;
+ }
+
+ CUtlSymbol symbol;
+ char fontName[MAX_PATH];
+ Q_FileBase( pFontFilename, fontName, MAX_PATH );
+ Q_strlower( fontName );
+ symbol = fontName;
+
+ // find a match that can use same entries
+ BitmapFontTable_t *pFontTable = NULL;
+ for ( int i=0; i<g_BitmapFontTable.Count(); i++ )
+ {
+ if ( symbol == g_BitmapFontTable[i].m_szName )
+ {
+ m_bitmapFontHandle = i;
+ pFontTable = &g_BitmapFontTable[m_bitmapFontHandle];
+ break;
+ }
+ }
+
+ if ( !pFontTable )
+ {
+ void *pBuf = NULL;
+ int nLength;
+
+ nLength = FontManager().FileSystem()->ReadFileEx( pFontFilename, "GAME", &pBuf );
+ if ( nLength <= 0 || !pBuf )
+ {
+ // not found
+ return false;
+ }
+
+ if ( ((BitmapFont_t*)pBuf)->m_id != LittleLong( BITMAPFONT_ID ) || ((BitmapFont_t*)pBuf)->m_Version != LittleLong( BITMAPFONT_VERSION ) )
+ {
+ // bad version
+ return false;
+ }
+
+ if ( IsX360() )
+ {
+ CByteswap swap;
+ swap.ActivateByteSwapping( true );
+ swap.SwapFieldsToTargetEndian( (BitmapFont_t*)pBuf );
+ swap.SwapFieldsToTargetEndian( (BitmapGlyph_t*)((char*)pBuf + sizeof( BitmapFont_t )), ((BitmapFont_t*)pBuf)->m_NumGlyphs );
+ }
+
+ // create it
+ m_bitmapFontHandle = g_BitmapFontTable.AddToTail();
+ pFontTable = &g_BitmapFontTable[m_bitmapFontHandle];
+
+ pFontTable->m_szName = fontName;
+
+ pFontTable->m_pBitmapFont = new BitmapFont_t;
+ memcpy( pFontTable->m_pBitmapFont, pBuf, sizeof( BitmapFont_t ) );
+
+ pFontTable->m_pBitmapGlyphs = new BitmapGlyph_t[pFontTable->m_pBitmapFont->m_NumGlyphs];
+ memcpy( pFontTable->m_pBitmapGlyphs, (unsigned char*)pBuf + sizeof(BitmapFont_t), pFontTable->m_pBitmapFont->m_NumGlyphs*sizeof(BitmapGlyph_t) );
+
+ FontManager().FileSystem()->FreeOptimalReadBuffer( pBuf );
+
+ // load the art resources
+ char textureName[MAX_PATH];
+ Q_snprintf( textureName, MAX_PATH, "vgui/fonts/%s", fontName );
+ pFontTable->m_pTexture = FontManager().MaterialSystem()->FindTexture( textureName, TEXTURE_GROUP_VGUI );
+
+#if defined( _DEBUG ) && !defined( DX_TO_GL_ABSTRACTION )
+ if ( pFontTable->m_pBitmapFont->m_PageWidth != pFontTable->m_pTexture->GetActualWidth() ||
+ pFontTable->m_pBitmapFont->m_PageHeight != pFontTable->m_pTexture->GetActualHeight() )
+ {
+ // font is out of sync with its art
+ Assert( 0 );
+ return false;
+ }
+#endif
+ // the font texture lives forever, ensure it doesn't get purged
+ pFontTable->m_pTexture->IncrementReferenceCount();
+ }
+
+ // setup font properties
+ m_scalex = scalex;
+ m_scaley = scaley;
+
+ // flags are derived from the baked font
+ m_iFlags = vgui::ISurface::FONTFLAG_BITMAP;
+ int bitmapFlags = pFontTable->m_pBitmapFont->m_Flags;
+
+ if ( bitmapFlags & BF_ANTIALIASED )
+ {
+ m_iFlags |= vgui::ISurface::FONTFLAG_ANTIALIAS;
+ }
+
+ if ( bitmapFlags & BF_ITALIC )
+ {
+ m_iFlags |= vgui::ISurface::FONTFLAG_ITALIC;
+ }
+
+ if ( bitmapFlags & BF_BLURRED )
+ {
+ m_iFlags |= vgui::ISurface::FONTFLAG_GAUSSIANBLUR;
+ m_iBlur = 1;
+ }
+
+ if ( bitmapFlags & BF_SCANLINES )
+ {
+ m_iScanLines = 1;
+ }
+
+ if ( bitmapFlags & BF_OUTLINED )
+ {
+ m_iFlags |= vgui::ISurface::FONTFLAG_OUTLINE;
+ m_iOutlineSize = 1;
+ }
+
+ if ( bitmapFlags & BF_DROPSHADOW )
+ {
+ m_iFlags |= vgui::ISurface::FONTFLAG_DROPSHADOW;
+ m_iDropShadowOffset = 1;
+ }
+
+ if ( flags & vgui::ISurface::FONTFLAG_ADDITIVE )
+ {
+ m_bAdditive = true;
+ m_iFlags |= vgui::ISurface::FONTFLAG_ADDITIVE;
+ }
+
+ m_iMaxCharWidth = (float)pFontTable->m_pBitmapFont->m_MaxCharWidth * m_scalex;
+ m_iHeight = (float)pFontTable->m_pBitmapFont->m_MaxCharHeight * m_scaley;
+ m_iAscent = (float)pFontTable->m_pBitmapFont->m_Ascent * m_scaley;
+
+ // mark as valid
+ m_szName = fontName;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the font is equivalent to that specified
+//-----------------------------------------------------------------------------
+bool CBitmapFont::IsEqualTo( const char *windowsFontName, float scalex, float scaley, int flags )
+{
+ char fontname[MAX_PATH];
+ Q_FileBase( windowsFontName, fontname, MAX_PATH );
+
+ if ( !Q_stricmp( fontname, m_szName.String() ) &&
+ m_scalex == scalex &&
+ m_scaley == scaley )
+ {
+ int commonFlags = m_iFlags & flags;
+ if ( commonFlags & vgui::ISurface::FONTFLAG_ADDITIVE )
+ {
+ // an exact match
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the scale for a font
+//-----------------------------------------------------------------------------
+void CBitmapFont::SetScale( float sx, float sy )
+{
+ m_scalex = sx;
+ m_scaley = sy;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the abc widths for a character
+//-----------------------------------------------------------------------------
+void CBitmapFont::GetCharABCWidths( int ch, int &a, int &b, int &c )
+{
+ Assert( IsValid() && ch >= 0 && ch <= 255 );
+
+ BitmapFontTable_t *pFont = &g_BitmapFontTable[m_bitmapFontHandle];
+
+ ch = pFont->m_pBitmapFont->m_TranslateTable[ch];
+ a = (float)pFont->m_pBitmapGlyphs[ch].a * m_scalex;
+ b = (float)pFont->m_pBitmapGlyphs[ch].b * m_scalex;
+ c = (float)pFont->m_pBitmapGlyphs[ch].c * m_scalex;
+}
+
+void CBitmapFont::GetCharRGBA( wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *prgba )
+{
+ // CBitmapFont derives off CLinuxFont, etc. But you should never call GetCharRGBA on a bitmap font.
+ // If we let this fall into the CLinuxFont code, we'd have a difficult to track down bug - so crash
+ // hard here...
+ Error( "GetCharRGBA called on CBitmapFont." );
+}
+
+void CBitmapFont::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA, float &abcC )
+{
+ Assert( IsValid() && ch >= 0 && ch <= 255 );
+
+ float abcB;
+ BitmapFontTable_t *pFont = &g_BitmapFontTable[m_bitmapFontHandle];
+
+ ch = pFont->m_pBitmapFont->m_TranslateTable[ch];
+ abcA = (float)pFont->m_pBitmapGlyphs[ch].a * m_scalex;
+ abcB = (float)pFont->m_pBitmapGlyphs[ch].b * m_scalex;
+ abcC = (float)pFont->m_pBitmapGlyphs[ch].c * m_scalex;
+
+ wide = ( abcA + abcB + abcC );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the texcoords for a character
+//-----------------------------------------------------------------------------
+void CBitmapFont::GetCharCoords( int ch, float *left, float *top, float *right, float *bottom )
+{
+ Assert( IsValid() && ch >= 0 && ch <= 255 );
+
+ BitmapFontTable_t *pFont = &g_BitmapFontTable[m_bitmapFontHandle];
+
+ ch = pFont->m_pBitmapFont->m_TranslateTable[ch];
+ *left = (float)pFont->m_pBitmapGlyphs[ch].x/(float)pFont->m_pBitmapFont->m_PageWidth;
+ *top = (float)pFont->m_pBitmapGlyphs[ch].y/(float)pFont->m_pBitmapFont->m_PageHeight;
+ *right = (float)(pFont->m_pBitmapGlyphs[ch].x+pFont->m_pBitmapGlyphs[ch].w)/(float)pFont->m_pBitmapFont->m_PageWidth;
+ *bottom = (float)(pFont->m_pBitmapGlyphs[ch].y+pFont->m_pBitmapGlyphs[ch].h)/(float)pFont->m_pBitmapFont->m_PageHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the texture page
+//-----------------------------------------------------------------------------
+ITexture *CBitmapFont::GetTexturePage()
+{
+ Assert( IsValid() );
+
+ return g_BitmapFontTable[m_bitmapFontHandle].m_pTexture;
+}
+
+BEGIN_BYTESWAP_DATADESC( BitmapGlyph_t )
+ DEFINE_FIELD( x, FIELD_SHORT ),
+ DEFINE_FIELD( y, FIELD_SHORT ),
+ DEFINE_FIELD( w, FIELD_SHORT ),
+ DEFINE_FIELD( h, FIELD_SHORT ),
+ DEFINE_FIELD( a, FIELD_SHORT ),
+ DEFINE_FIELD( b, FIELD_SHORT ),
+ DEFINE_FIELD( c, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( BitmapFont_t )
+ DEFINE_FIELD( m_id, FIELD_INTEGER ),
+ DEFINE_FIELD( m_Version, FIELD_INTEGER ),
+ DEFINE_FIELD( m_PageWidth, FIELD_SHORT ),
+ DEFINE_FIELD( m_PageHeight, FIELD_SHORT ),
+ DEFINE_FIELD( m_MaxCharWidth, FIELD_SHORT ),
+ DEFINE_FIELD( m_MaxCharHeight, FIELD_SHORT ),
+ DEFINE_FIELD( m_Flags, FIELD_SHORT ),
+ DEFINE_FIELD( m_Ascent, FIELD_SHORT ),
+ DEFINE_FIELD( m_NumGlyphs, FIELD_SHORT ),
+ DEFINE_ARRAY( m_TranslateTable, FIELD_CHARACTER, 256 ),
+END_BYTESWAP_DATADESC()
diff --git a/vgui2/vgui_surfacelib/FontAmalgam.cpp b/vgui2/vgui_surfacelib/FontAmalgam.cpp
new file mode 100644
index 0000000..3401014
--- /dev/null
+++ b/vgui2/vgui_surfacelib/FontAmalgam.cpp
@@ -0,0 +1,227 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui_surfacelib/FontAmalgam.h"
+#include <tier0/dbg.h>
+#include <vgui/VGUI.h>
+#include <vgui/ISurface.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CFontAmalgam::CFontAmalgam()
+{
+ m_Fonts.EnsureCapacity( 4 );
+ m_iMaxHeight = 0;
+ m_iMaxWidth = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CFontAmalgam::~CFontAmalgam()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Data accessor
+//-----------------------------------------------------------------------------
+const char *CFontAmalgam::Name()
+{
+ return m_szName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Data accessor
+//-----------------------------------------------------------------------------
+void CFontAmalgam::SetName(const char *name)
+{
+ Q_strncpy(m_szName, name, sizeof(m_szName));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a font to the amalgam
+//-----------------------------------------------------------------------------
+void CFontAmalgam::AddFont(font_t *font, int lowRange, int highRange)
+{
+ int i = m_Fonts.AddToTail();
+
+ m_Fonts[i].font = font;
+ m_Fonts[i].lowRange = lowRange;
+ m_Fonts[i].highRange = highRange;
+
+ m_iMaxHeight = max(font->GetHeight(), m_iMaxHeight);
+ m_iMaxWidth = max(font->GetMaxCharWidth(), m_iMaxWidth);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears the fonts
+//-----------------------------------------------------------------------------
+void CFontAmalgam::RemoveAll()
+{
+ // clear out
+ m_Fonts.RemoveAll();
+ m_iMaxHeight = 0;
+ m_iMaxWidth = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the font for the given character
+//-----------------------------------------------------------------------------
+font_t *CFontAmalgam::GetFontForChar(int ch)
+{
+ for (int i = 0; i < m_Fonts.Count(); i++)
+ {
+#if defined(LINUX)
+ if ( ch >= m_Fonts[i].lowRange && ch <= m_Fonts[i].highRange && m_Fonts[i].font->HasChar(ch))
+#else
+ if (ch >= m_Fonts[i].lowRange && ch <= m_Fonts[i].highRange)
+#endif
+ {
+ Assert( m_Fonts[i].font->IsValid() );
+ return m_Fonts[i].font;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the scale of the font
+//-----------------------------------------------------------------------------
+void CFontAmalgam::SetFontScale(float sx, float sy)
+{
+ if (!m_Fonts.Count())
+ return;
+
+ // Make sure this is a bitmap font!
+ if ( GetFlags( 0 ) & vgui::ISurface::FONTFLAG_BITMAP )
+ {
+ reinterpret_cast< CBitmapFont* >( m_Fonts[0].font )->SetScale( sx, sy );
+ }
+ else
+ {
+ Warning( "%s: Can't set font scale on a non-bitmap font!\n", m_Fonts[0].font->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the max height of the font set
+//-----------------------------------------------------------------------------
+int CFontAmalgam::GetFontHeight()
+{
+ if (!m_Fonts.Count())
+ {
+ return m_iMaxHeight;
+ }
+ return m_Fonts[0].font->GetHeight();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns requested height of the font
+//-----------------------------------------------------------------------------
+int CFontAmalgam::GetFontHeightRequested()
+{
+ if (!m_Fonts.Count())
+ {
+ return m_iMaxHeight;
+ }
+ return m_Fonts[0].font->GetHeightRequested();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the maximum width of a character in a font
+//-----------------------------------------------------------------------------
+int CFontAmalgam::GetFontMaxWidth()
+{
+ return m_iMaxWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the name of the font that is loaded
+//-----------------------------------------------------------------------------
+const char *CFontAmalgam::GetFontName(int i)
+{
+ if ( m_Fonts.IsValidIndex( i ) && m_Fonts[ i ].font )
+ {
+ return m_Fonts[ i ].font->GetName();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the family name of the font that is loaded
+//-----------------------------------------------------------------------------
+const char *CFontAmalgam::GetFontFamilyName( int i )
+{
+ if ( m_Fonts.IsValidIndex( i ) && m_Fonts[ i ].font )
+ {
+ return m_Fonts[ i ].font->GetFamilyName();
+ }
+
+ return "";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the name of the font that is loaded
+//-----------------------------------------------------------------------------
+int CFontAmalgam::GetFlags(int i)
+{
+ if ( m_Fonts.Count() && m_Fonts[i].font )
+ {
+ return m_Fonts[i].font->GetFlags();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the number of fonts this amalgam contains
+//-----------------------------------------------------------------------------
+int CFontAmalgam::GetCount()
+{
+ return m_Fonts.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the max height of the font set
+//-----------------------------------------------------------------------------
+bool CFontAmalgam::GetUnderlined()
+{
+ if (!m_Fonts.Count())
+ {
+ return false;
+ }
+ return m_Fonts[0].font->GetUnderlined();
+}
+
+
+#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 CFontAmalgam::Validate( CValidator &validator, char *pchName )
+{
+ validator.Push( "CFontAmalgam", this, pchName );
+
+ ValidateObj( m_Fonts );
+
+ validator.Pop();
+}
+#endif // DBGFLAG_VALIDATE
diff --git a/vgui2/vgui_surfacelib/FontEffects.cpp b/vgui2/vgui_surfacelib/FontEffects.cpp
new file mode 100644
index 0000000..9acea90
--- /dev/null
+++ b/vgui2/vgui_surfacelib/FontEffects.cpp
@@ -0,0 +1,241 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Font effects that operate on linear rgba data
+//
+//=====================================================================================//
+
+#include "tier0/platform.h"
+#include <tier0/dbg.h>
+#include <math.h>
+#include "FontEffects.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds center line to font
+//-----------------------------------------------------------------------------
+void ApplyRotaryEffectToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, bool bRotary )
+{
+ if ( !bRotary )
+ return;
+
+ int y = rgbaTall * 0.5;
+
+ unsigned char *line = &rgba[(y * rgbaWide) * 4];
+
+ // Draw a line down middle
+ for (int x = 0; x < rgbaWide; x++, line+=4)
+ {
+ line[0] = 127;
+ line[1] = 127;
+ line[2] = 127;
+ line[3] = 255;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds scanlines to the texture
+//-----------------------------------------------------------------------------
+void ApplyScanlineEffectToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iScanLines )
+{
+ if ( iScanLines < 2 )
+ return;
+
+ float scale;
+ scale = 0.7f;
+
+ // darken all the areas except the scanlines
+ for (int y = 0; y < rgbaTall; y++)
+ {
+ // skip the scan lines
+ if (y % iScanLines == 0)
+ continue;
+
+ unsigned char *pBits = &rgba[(y * rgbaWide) * 4];
+
+ // darken the other lines
+ for (int x = 0; x < rgbaWide; x++, pBits += 4)
+ {
+ pBits[0] *= scale;
+ pBits[1] *= scale;
+ pBits[2] *= scale;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a dropshadow the the font texture
+//-----------------------------------------------------------------------------
+void ApplyDropShadowToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iDropShadowOffset )
+{
+ if ( !iDropShadowOffset )
+ return;
+
+ // walk the original image from the bottom up
+ // shifting it down and right, and turning it black (the dropshadow)
+ for (int y = rgbaTall - 1; y >= iDropShadowOffset; y--)
+ {
+ for (int x = rgbaWide - 1; x >= iDropShadowOffset; x--)
+ {
+ unsigned char *dest = &rgba[(x + (y * rgbaWide)) * 4];
+ if (dest[3] == 0)
+ {
+ // there is nothing in this spot, copy in the dropshadow
+ unsigned char *src = &rgba[(x - iDropShadowOffset + ((y - iDropShadowOffset) * rgbaWide)) * 4];
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = 0;
+ dest[3] = src[3];
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds an outline to the font texture
+//-----------------------------------------------------------------------------
+void ApplyOutlineToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iOutlineSize )
+{
+ if ( !iOutlineSize )
+ return;
+
+ int x, y;
+ for( y = 0; y < rgbaTall; y++ )
+ {
+ for( x = 0; x < rgbaWide; x++ )
+ {
+ unsigned char *src = &rgba[(x + (y * rgbaWide)) * 4];
+ if( src[3] == 0 )
+ {
+ // We have a valid font texel. Make all the alpha == 0 neighbors black.
+ int shadowX, shadowY;
+ for( shadowX = -(int)iOutlineSize; shadowX <= (int)iOutlineSize; shadowX++ )
+ {
+ for( shadowY = -(int)iOutlineSize; shadowY <= (int)iOutlineSize; shadowY++ )
+ {
+ if( shadowX == 0 && shadowY == 0 )
+ {
+ continue;
+ }
+ int testX, testY;
+ testX = shadowX + x;
+ testY = shadowY + y;
+ if( testX < 0 || testX >= rgbaWide ||
+ testY < 0 || testY >= rgbaTall )
+ {
+ continue;
+ }
+ unsigned char *test = &rgba[(testX + (testY * rgbaWide)) * 4];
+ if( test[0] != 0 && test[1] != 0 && test[2] != 0 && test[3] != 0 )
+ {
+ src[0] = 0;
+ src[1] = 0;
+ src[2] = 0;
+ src[3] = 255;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+namespace
+{
+
+ unsigned char CalculatePixelBlur(const unsigned char* src, int nStride, const float* distribution, int nValues)
+ {
+ float accum = 0.0;
+ for ( int n = 0; n != nValues; ++n )
+ {
+ accum += distribution[n]*static_cast<float>(src[n*nStride]);
+ }
+
+ return static_cast<unsigned char>(accum);
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: blurs the texture
+//-----------------------------------------------------------------------------
+void ApplyGaussianBlurToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int nBlur )
+{
+ if ( !nBlur )
+ return;
+
+ // generate the gaussian field
+ float *pGaussianDistribution = (float*) stackalloc( (nBlur*2+1) * sizeof(float) );
+ double sigma = 0.683 * nBlur;
+ for (int x = 0; x <= (nBlur * 2); x++)
+ {
+ int val = x - nBlur;
+ pGaussianDistribution[x] = (float)( 1.0f / sqrt(2 * 3.14 * sigma * sigma)) * pow(2.7, -1 * (val * val) / (2 * sigma * sigma));
+ }
+
+ // alloc a new buffer
+ unsigned char *src = (unsigned char *) stackalloc( rgbaWide * rgbaTall * 4);
+
+ // copy in
+ memcpy(src, rgba, rgbaWide * rgbaTall * 4);
+
+ //make an initial horizontal pass
+ for ( int x = 0; x < rgbaWide; x++ )
+ {
+ const float* dist = pGaussianDistribution;
+ int nValues = nBlur*2 + 1;
+ int nOffset = 0;
+ if ( x < nBlur )
+ {
+ nOffset += nBlur - x;
+ dist += nOffset;
+ nValues -= nOffset;
+ }
+
+ if ( x >= rgbaWide - nBlur )
+ {
+ nValues = rgbaWide - (x - nOffset);
+ }
+
+ for ( int y = 0; y < rgbaTall; y++ )
+ {
+ const unsigned char* read_from = src + (y*rgbaWide + x + nOffset - nBlur)*4 + 3;
+ unsigned char* dst = rgba + (y*rgbaWide + x)*4;
+ unsigned char alpha = CalculatePixelBlur(read_from, 4, dist, nValues);
+ dst[0] = dst[1] = dst[2] = alpha > 0 ? 255 : 0;
+ dst[3] = alpha;
+ }
+ }
+
+ // refresh the source buffer for a second vertical pass
+ memcpy(src, rgba, rgbaWide * rgbaTall * 4);
+
+ for ( int y = 0; y < rgbaTall; y++ )
+ {
+ const float* dist = pGaussianDistribution;
+ int nValues = nBlur*2 + 1;
+ int nOffset = 0;
+ if ( y < nBlur )
+ {
+ nOffset += nBlur - y;
+ dist += nOffset;
+ nValues -= nOffset;
+ }
+
+ if ( y >= rgbaTall - nBlur )
+ {
+ nValues = rgbaTall - (y - nOffset);
+ }
+
+ for ( int x = 0; x < rgbaWide; x++ )
+ {
+ const unsigned char* read_from = src + ((y + nOffset - nBlur)*rgbaWide + x)*4 + 3;
+ unsigned char* dst = rgba + (y*rgbaWide + x)*4;
+ unsigned char alpha = CalculatePixelBlur(read_from, 4*rgbaWide, dist, nValues);
+ dst[0] = dst[1] = dst[2] = alpha > 0 ? 255 : 0;
+ dst[3] = alpha;
+ }
+ }
+}
diff --git a/vgui2/vgui_surfacelib/FontEffects.h b/vgui2/vgui_surfacelib/FontEffects.h
new file mode 100644
index 0000000..16cfbaa
--- /dev/null
+++ b/vgui2/vgui_surfacelib/FontEffects.h
@@ -0,0 +1,20 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Font effects that operate on linear rgba data
+//
+//=====================================================================================//
+
+#ifndef _FONTEFFECTS_H
+#define _FONTEFFECTS_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+void ApplyScanlineEffectToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iScanLines );
+void ApplyGaussianBlurToTexture(int rgbaWide, int rgbaTall, unsigned char *rgba, int iBlur );
+void ApplyDropShadowToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iDropShadowOffset );
+void ApplyOutlineToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iOutlineSize );
+void ApplyRotaryEffectToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, bool bRotary );
+
+#endif \ No newline at end of file
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
+
diff --git a/vgui2/vgui_surfacelib/Win32Font.cpp b/vgui2/vgui_surfacelib/Win32Font.cpp
new file mode 100644
index 0000000..5ec02ac
--- /dev/null
+++ b/vgui2/vgui_surfacelib/Win32Font.cpp
@@ -0,0 +1,638 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=====================================================================================//
+
+#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <malloc.h>
+#include "vgui_surfacelib/Win32Font.h"
+#include <tier0/dbg.h>
+#include <vgui/ISurface.h>
+#include <tier0/mem.h>
+#include <utlbuffer.h>
+#include "FontEffects.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+static OSVERSIONINFO s_OsVersionInfo;
+static bool s_bOsVersionInitialized = false;
+bool s_bSupportsUnicode = false;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWin32Font::CWin32Font() : m_ExtendedABCWidthsCache(256, 0, &ExtendedABCWidthsCacheLessFunc)
+{
+ m_szName = UTL_INVAL_SYMBOL;
+ m_iTall = 0;
+ m_iWeight = 0;
+ m_iHeight = 0;
+ m_iAscent = 0;
+ m_iFlags = 0;
+ m_iMaxCharWidth = 0;
+ m_hFont = NULL;
+ m_hDC = NULL;
+ m_hDIB = NULL;
+ m_bAntiAliased = false;
+ m_bUnderlined = false;
+ m_iBlur = 0;
+ m_iScanLines = 0;
+ m_bRotary = false;
+ m_bAdditive = false;
+ m_rgiBitmapSize[ 0 ] = m_rgiBitmapSize[ 1 ] = 0;
+
+#if defined( _X360 )
+ Q_memset( m_ABCWidthsCache, 0, sizeof( m_ABCWidthsCache ) );
+#endif
+
+ m_ExtendedABCWidthsCache.EnsureCapacity( 128 );
+
+ if ( !s_bOsVersionInitialized )
+ {
+ // get the operating system version
+ s_bOsVersionInitialized = true;
+ memset(&s_OsVersionInfo, 0, sizeof(s_OsVersionInfo));
+ s_OsVersionInfo.dwOSVersionInfoSize = sizeof(s_OsVersionInfo);
+ GetVersionEx(&s_OsVersionInfo);
+
+ if (s_OsVersionInfo.dwMajorVersion >= 5)
+ {
+ s_bSupportsUnicode = true;
+ }
+ else
+ {
+ s_bSupportsUnicode = false;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CWin32Font::~CWin32Font()
+{
+ if ( m_hFont )
+ ::DeleteObject( m_hFont );
+ if ( m_hDC )
+ ::DeleteDC( m_hDC );
+ if ( m_hDIB )
+ ::DeleteObject( m_hDIB );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Font iteration callback function
+// used to determine whether or not a font exists on the system
+//-----------------------------------------------------------------------------
+extern bool g_bFontFound = false;
+int CALLBACK FontEnumProc(
+ const LOGFONT *lpelfe, // logical-font data
+ const TEXTMETRIC *lpntme, // physical-font data
+ DWORD FontType, // type of font
+ LPARAM lParam ) // application-defined data
+{
+ g_bFontFound = true;
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: creates the font from windows. returns false if font does not exist in the OS.
+//-----------------------------------------------------------------------------
+bool CWin32Font::Create(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
+{
+ // setup font properties
+ m_szName = windowsFontName;
+ m_iTall = tall;
+ m_iWeight = weight;
+ m_iFlags = flags;
+ m_bAntiAliased = (flags & vgui::ISurface::FONTFLAG_ANTIALIAS) ? 1 : 0;
+ m_bUnderlined = flags & vgui::ISurface::FONTFLAG_UNDERLINE;
+ m_iDropShadowOffset = (flags & vgui::ISurface::FONTFLAG_DROPSHADOW) ? 1 : 0;
+ m_iOutlineSize = (flags & vgui::ISurface::FONTFLAG_OUTLINE) ? 1 : 0;
+ m_iBlur = blur;
+ m_iScanLines = scanlines;
+ m_bRotary = (flags & vgui::ISurface::FONTFLAG_ROTARY) ? 1 : 0;
+ m_bAdditive = (flags & vgui::ISurface::FONTFLAG_ADDITIVE) ? 1 : 0;
+
+ int charset = (flags & vgui::ISurface::FONTFLAG_SYMBOL) ? SYMBOL_CHARSET : ANSI_CHARSET;
+
+ // hack for japanese win98 support
+ if ( !stricmp( windowsFontName, "win98japanese" ) )
+ {
+ // use any font that contains the japanese charset
+ charset = SHIFTJIS_CHARSET;
+ m_szName = "Tahoma";
+ }
+
+ // create our windows device context
+ m_hDC = ::CreateCompatibleDC(NULL);
+ Assert( m_hDC );
+
+ // see if the font exists on the system
+ LOGFONT logfont;
+ logfont.lfCharSet = DEFAULT_CHARSET;
+ logfont.lfPitchAndFamily = 0;
+ strcpy(logfont.lfFaceName, m_szName.String());
+ g_bFontFound = false;
+ ::EnumFontFamiliesEx(m_hDC, &logfont, &FontEnumProc, 0, 0);
+ if (!g_bFontFound)
+ {
+ // needs to go to a fallback
+ m_szName = UTL_INVAL_SYMBOL;
+ return false;
+ }
+
+ m_hFont = ::CreateFontA(tall, 0, 0, 0,
+ m_iWeight,
+ flags & vgui::ISurface::FONTFLAG_ITALIC,
+ flags & vgui::ISurface::FONTFLAG_UNDERLINE,
+ flags & vgui::ISurface::FONTFLAG_STRIKEOUT,
+ charset,
+ OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ m_bAntiAliased ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY,
+ DEFAULT_PITCH | FF_DONTCARE,
+ windowsFontName);
+ if (!m_hFont)
+ {
+ Error("Couldn't create windows font '%s'\n", windowsFontName);
+ m_szName = UTL_INVAL_SYMBOL;
+ return false;
+ }
+
+ // set as the active font
+ ::SetMapMode(m_hDC, MM_TEXT);
+ ::SelectObject(m_hDC, m_hFont);
+ ::SetTextAlign(m_hDC, TA_LEFT | TA_TOP | TA_UPDATECP);
+
+ // get info about the font
+ ::TEXTMETRIC tm;
+ memset( &tm, 0, sizeof( tm ) );
+ if ( !GetTextMetrics(m_hDC, &tm) )
+ {
+ m_szName = UTL_INVAL_SYMBOL;
+ return false;
+ }
+
+ m_iHeight = tm.tmHeight + m_iDropShadowOffset + 2 * m_iOutlineSize;
+ m_iMaxCharWidth = tm.tmMaxCharWidth;
+ m_iAscent = tm.tmAscent;
+
+ // code for rendering to a bitmap
+ m_rgiBitmapSize[0] = tm.tmMaxCharWidth + m_iOutlineSize * 2;
+ m_rgiBitmapSize[1] = tm.tmHeight + m_iDropShadowOffset + m_iOutlineSize * 2;
+
+ ::BITMAPINFOHEADER header;
+ memset(&header, 0, sizeof(header));
+ header.biSize = sizeof(header);
+ header.biWidth = m_rgiBitmapSize[0];
+ header.biHeight = -m_rgiBitmapSize[1];
+ header.biPlanes = 1;
+ header.biBitCount = 32;
+ header.biCompression = BI_RGB;
+
+ m_hDIB = ::CreateDIBSection(m_hDC, (BITMAPINFO*)&header, DIB_RGB_COLORS, (void**)(&m_pBuf), NULL, 0);
+ ::SelectObject(m_hDC, m_hDIB);
+
+#if defined( _X360 )
+ // get char spacing
+ // a is space before character (can be negative)
+ // b is the width of the character
+ // c is the space after the character
+ memset(m_ABCWidthsCache, 0, sizeof(m_ABCWidthsCache));
+ ABC abc[ABCWIDTHS_CACHE_SIZE];
+ Assert(ABCWIDTHS_CACHE_SIZE <= 256);
+ if (::GetCharABCWidthsW(m_hDC, 0, ABCWIDTHS_CACHE_SIZE - 1, &abc[0]) || ::GetCharABCWidthsA(m_hDC, 0, ABCWIDTHS_CACHE_SIZE - 1, &abc[0]))
+ {
+ // copy out into our formated structure
+ for (int i = 0; i < ABCWIDTHS_CACHE_SIZE; i++)
+ {
+ m_ABCWidthsCache[i].a = abc[i].abcA - m_iBlur - m_iOutlineSize;
+ m_ABCWidthsCache[i].b = abc[i].abcB + ((m_iBlur + m_iOutlineSize) * 2) + m_iDropShadowOffset;
+ m_ABCWidthsCache[i].c = abc[i].abcC - m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
+ }
+ }
+ else
+ {
+ Warning("GetCharABCWidths() failed for windows font '%s'\n", windowsFontName);
+
+ // since that failed, it must be fixed width, zero everything so a and c will be zeros, then
+ // fill b with the value from TEXTMETRIC
+ for (int i = 0; i < ABCWIDTHS_CACHE_SIZE; i++)
+ {
+ // fallback to old method, no underhangs/overhangs (a/c)
+ SIZE size;
+ char mbcs[6] = { 0 };
+ wchar_t wch = (wchar_t)i;
+ ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL);
+ if (::GetTextExtentPoint32(m_hDC, mbcs, strlen(mbcs), &size))
+ {
+ m_ABCWidthsCache[i].b = size.cx;
+ }
+ else
+ {
+ // failed to get width, just use the average width
+ m_ABCWidthsCache[i].b = (char)tm.tmAveCharWidth;
+ }
+ }
+ }
+#endif
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: writes the char into the specified 32bpp texture
+//-----------------------------------------------------------------------------
+void CWin32Font::GetCharRGBA(wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba)
+{
+ int a, b, c;
+ GetCharABCWidths(ch, a, b, c);
+
+ // set us up to render into our dib
+ ::SelectObject(m_hDC, m_hFont);
+
+ int wide = b;
+ if ( m_bUnderlined )
+ {
+ wide += ( a + c );
+ }
+
+ int tall = m_iHeight;
+ GLYPHMETRICS glyphMetrics;
+ MAT2 mat2 = { { 0, 1}, { 0, 0}, { 0, 0}, { 0, 1}};
+ int bytesNeeded = 0;
+
+ bool bShouldAntialias = m_bAntiAliased;
+ // filter out
+ if ( ch > 0x00FF && !(m_iFlags & vgui::ISurface::FONTFLAG_CUSTOM) )
+ {
+ bShouldAntialias = false;
+ }
+ if ( !s_bSupportsUnicode )
+ {
+ // win98 hack, don't antialias some characters that ::GetGlyphOutline() produces bad results for
+ if (ch == 'I' || ch == '1')
+ {
+ bShouldAntialias = false;
+ }
+
+ // don't antialias big fonts at all (since win98 often produces bad results)
+ if (m_iHeight >= 13)
+ {
+ bShouldAntialias = false;
+ }
+ }
+
+
+ // only antialias latin characters, since it essentially always fails for asian characters
+ if (bShouldAntialias)
+ {
+ // try and get the glyph directly
+ ::SelectObject(m_hDC, m_hFont);
+ bytesNeeded = ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, 0, NULL, &mat2);
+ }
+
+ if (bytesNeeded > 0)
+ {
+ // take it
+ unsigned char *lpbuf = (unsigned char *)_alloca(bytesNeeded);
+ ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, bytesNeeded, lpbuf, &mat2);
+
+ // rows are on DWORD boundaries
+ wide = glyphMetrics.gmBlackBoxX;
+ while (wide % 4 != 0)
+ {
+ wide++;
+ }
+
+ // see where we should start rendering
+ int pushDown = m_iAscent - glyphMetrics.gmptGlyphOrigin.y;
+
+ // set where we start copying from
+ int xstart = 0;
+
+ // don't copy the first set of pixels if the antialiased bmp is bigger than the char width
+ if ((int)glyphMetrics.gmBlackBoxX >= b + 2)
+ {
+ xstart = (glyphMetrics.gmBlackBoxX - b) / 2;
+ }
+
+ // iterate through copying the generated dib into the texture
+ for (unsigned int j = 0; j < glyphMetrics.gmBlackBoxY; j++)
+ {
+ for (unsigned int i = xstart; i < glyphMetrics.gmBlackBoxX; i++)
+ {
+ int x = i - xstart + m_iBlur + m_iOutlineSize;
+ int y = j + pushDown;
+ if ((x < rgbaWide) && (y < rgbaTall))
+ {
+ unsigned char grayscale = lpbuf[(j*wide+i)];
+
+ float r, g, b, a;
+ if (grayscale)
+ {
+ r = g = b = 1.0f;
+ a = (grayscale + 0) / 64.0f;
+ if (a > 1.0f) a = 1.0f;
+ }
+ else
+ {
+ r = g = b = a = 0.0f;
+ }
+
+ // Don't want anything drawn for tab characters.
+ if (ch == '\t')
+ {
+ r = g = b = 0;
+ }
+
+ unsigned char *dst = &rgba[(y*rgbaWide+x)*4];
+ dst[0] = (unsigned char)(r * 255.0f);
+ dst[1] = (unsigned char)(g * 255.0f);
+ dst[2] = (unsigned char)(b * 255.0f);
+ dst[3] = (unsigned char)(a * 255.0f);
+ }
+ }
+ }
+ }
+ else
+ {
+ // use render-to-bitmap to get our font texture
+ ::SetBkColor(m_hDC, RGB(0, 0, 0));
+ ::SetTextColor(m_hDC, RGB(255, 255, 255));
+ ::SetBkMode(m_hDC, OPAQUE);
+ if ( m_bUnderlined )
+ {
+ ::MoveToEx(m_hDC, 0, 0, NULL);
+ }
+ else
+ {
+ ::MoveToEx(m_hDC, -a, 0, NULL);
+ }
+
+ // render the character
+ wchar_t wch = (wchar_t)ch;
+
+ if (s_bSupportsUnicode)
+ {
+ // clear the background first
+ RECT rect = { 0, 0, wide, tall};
+ ::ExtTextOutW( m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL );
+
+ // just use the unicode renderer
+ ::ExtTextOutW( m_hDC, 0, 0, 0, NULL, &wch, 1, NULL );
+ }
+ else
+ {
+ // clear the background first (it may not get done automatically in win98/ME
+ RECT rect = { 0, 0, wide, tall};
+ ::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
+
+ // convert the character using the current codepage
+ char mbcs[6] = { 0 };
+ ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL);
+ ::ExtTextOutA(m_hDC, 0, 0, 0, NULL, mbcs, strlen(mbcs), NULL);
+ }
+
+ ::SetBkMode(m_hDC, TRANSPARENT);
+
+ if (wide > m_rgiBitmapSize[0])
+ {
+ wide = m_rgiBitmapSize[0];
+ }
+ if (tall > m_rgiBitmapSize[1])
+ {
+ tall = m_rgiBitmapSize[1];
+ }
+
+ // iterate through copying the generated dib into the texture
+ for (int j = (int)m_iOutlineSize; j < tall - (int)m_iOutlineSize; j++ )
+ {
+ // only copy from within the dib, ignore the outline border we are artificially adding
+ for (int i = (int)m_iOutlineSize; i < wide - (int)m_iDropShadowOffset - (int)m_iOutlineSize; i++)
+ {
+ if ((i < rgbaWide) && (j < rgbaTall))
+ {
+ unsigned char *src = &m_pBuf[(i + j*m_rgiBitmapSize[0])*4];
+ unsigned char *dst = &rgba[(i + j*rgbaWide)*4];
+
+ // Don't want anything drawn for tab characters.
+ unsigned char r, g, b;
+ if ( ch == '\t' )
+ {
+ r = g = b = 0;
+ }
+ else
+ {
+ r = src[0];
+ g = src[1];
+ b = src[2];
+ }
+
+ // generate alpha based on luminance conversion
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+ dst[3] = (unsigned char)((float)r * 0.34f + (float)g * 0.55f + (float)b * 0.11f);
+ }
+ }
+ }
+
+ // if we have a dropshadow, we need to clean off the bottom row of pixels
+ // this is because of a bug in winME that writes noise to them, only on the first time the game is run after a reboot
+ // the bottom row should guaranteed to be empty to fit the dropshadow
+ if ( m_iDropShadowOffset )
+ {
+ unsigned char *dst = &rgba[((m_iHeight - 1) * rgbaWide) * 4];
+ for (int i = 0; i < wide; i++)
+ {
+ dst[0] = 0;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 0;
+ dst += 4;
+ }
+ }
+ }
+
+ // apply requested effects in specified order
+ ApplyDropShadowToTexture( rgbaWide, rgbaTall, rgba, m_iDropShadowOffset );
+ ApplyOutlineToTexture( rgbaWide, rgbaTall, rgba, m_iOutlineSize );
+ ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, rgba, m_iBlur );
+ ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, rgba, m_iScanLines );
+ ApplyRotaryEffectToTexture( rgbaWide, rgbaTall, rgba, m_bRotary );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the font is equivalent to that specified
+//-----------------------------------------------------------------------------
+bool CWin32Font::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
+{
+ if ( !stricmp(windowsFontName, m_szName.String() )
+ && m_iTall == tall
+ && m_iWeight == weight
+ && m_iBlur == blur
+ && m_iFlags == flags)
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true only if this font is valid for use
+//-----------------------------------------------------------------------------
+bool CWin32Font::IsValid()
+{
+ if ( m_szName.IsValid() && m_szName.String()[0] )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the font to be the one to currently draw with in the gdi
+//-----------------------------------------------------------------------------
+void CWin32Font::SetAsActiveFont(HDC hdc)
+{
+ Assert( IsValid() );
+ ::SelectObject( hdc, m_hFont );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the abc widths for a character
+//-----------------------------------------------------------------------------
+void CWin32Font::GetCharABCWidths(int ch, int &a, int &b, int &c)
+{
+ Assert( IsValid() );
+#if defined( _X360 )
+ if (ch < ABCWIDTHS_CACHE_SIZE)
+ {
+ // use the cache entry
+ a = m_ABCWidthsCache[ch].a;
+ b = m_ABCWidthsCache[ch].b;
+ c = m_ABCWidthsCache[ch].c;
+ }
+ else
+#endif
+ {
+
+ // look for it in the cache
+ abc_cache_t finder = { (wchar_t)ch };
+
+ unsigned short i = m_ExtendedABCWidthsCache.Find(finder);
+ if (m_ExtendedABCWidthsCache.IsValidIndex(i))
+ {
+ a = m_ExtendedABCWidthsCache[i].abc.a;
+ b = m_ExtendedABCWidthsCache[i].abc.b;
+ c = m_ExtendedABCWidthsCache[i].abc.c;
+ return;
+ }
+
+ // not in the cache, get from windows (this call is a little slow)
+ ABC abc;
+ if (::GetCharABCWidthsW(m_hDC, ch, ch, &abc) || ::GetCharABCWidthsA(m_hDC, ch, ch, &abc))
+ {
+ a = abc.abcA;
+ b = abc.abcB;
+ c = abc.abcC;
+ }
+ else
+ {
+ // wide character version failed, try the old api function
+ SIZE size;
+ char mbcs[6] = { 0 };
+ wchar_t wch = ch;
+ ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL);
+ if (::GetTextExtentPoint32(m_hDC, mbcs, strlen(mbcs), &size))
+ {
+ a = c = 0;
+ b = size.cx;
+ }
+ else
+ {
+ // failed to get width, just use the max width
+ a = c = 0;
+ b = m_iMaxCharWidth;
+ }
+ }
+
+ // add to the cache
+ finder.abc.a = a - m_iBlur - m_iOutlineSize;
+ finder.abc.b = b + ((m_iBlur + m_iOutlineSize) * 2) + m_iDropShadowOffset;
+ finder.abc.c = c - m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
+ m_ExtendedABCWidthsCache.Insert(finder);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the height of the font, in pixels
+//-----------------------------------------------------------------------------
+int CWin32Font::GetHeight()
+{
+ Assert( IsValid() );
+ return m_iHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the requested height of the font
+//-----------------------------------------------------------------------------
+int CWin32Font::GetHeightRequested()
+{
+ assert(IsValid());
+ return m_iTall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
+//-----------------------------------------------------------------------------
+int CWin32Font::GetAscent()
+{
+ Assert( IsValid() );
+ return m_iAscent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the maximum width of a character, in pixels
+//-----------------------------------------------------------------------------
+int CWin32Font::GetMaxCharWidth()
+{
+ Assert( IsValid() );
+ return m_iMaxCharWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the flags used to make this font, used by the dynamic resizing code
+//-----------------------------------------------------------------------------
+int CWin32Font::GetFlags()
+{
+ return m_iFlags;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Comparison function for abc widths storage
+//-----------------------------------------------------------------------------
+bool CWin32Font::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs)
+{
+ return lhs.wch < rhs.wch;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the kerned size of a char, for win32 just pass thru for now
+//-----------------------------------------------------------------------------
+void CWin32Font::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA )
+{
+ int a,b,c;
+ GetCharABCWidths(ch, a, b, c );
+ wide = ( a + b + c);
+ abcA = a;
+}
+
+
diff --git a/vgui2/vgui_surfacelib/Win32Font_x360.cpp b/vgui2/vgui_surfacelib/Win32Font_x360.cpp
new file mode 100644
index 0000000..1a415b3
--- /dev/null
+++ b/vgui2/vgui_surfacelib/Win32Font_x360.cpp
@@ -0,0 +1,346 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Xbox 360 support for TrueType Fonts. The only cuurent solution is to use XUI
+// to mount the TTF, and rasterize glyph into a render target. XUI does not support
+// rasterization directly to a system memory region.
+//
+//=====================================================================================//
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <malloc.h>
+#include <tier0/dbg.h>
+#include <vgui/ISurface.h>
+#include <tier0/mem.h>
+#include <utlbuffer.h>
+#include "filesystem.h"
+#include "materialsystem/imaterialsystem.h"
+#include "FontEffects.h"
+#include "vgui_surfacelib/Win32Font.h"
+#include "vgui_surfacelib/FontManager.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+bool s_bSupportsUnicode = true;
+
+//-----------------------------------------------------------------------------
+// Determine possible style from parameters.
+//-----------------------------------------------------------------------------
+int GetStyleFromParameters( int iFlags, int iWeight )
+{
+ // Available xbox TTF styles are very restricted.
+ int style = XUI_FONT_STYLE_NORMAL;
+ if ( iFlags & vgui::ISurface::FONTFLAG_ITALIC )
+ style |= XUI_FONT_STYLE_ITALIC;
+ if ( iFlags & vgui::ISurface::FONTFLAG_UNDERLINE )
+ style |= XUI_FONT_STYLE_UNDERLINE;
+ if ( iWeight > 400 )
+ style |= XUI_FONT_STYLE_BOLD;
+ return style;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWin32Font::CWin32Font()
+{
+ m_szName = UTL_INVAL_SYMBOL;
+ m_iTall = 0;
+ m_iWeight = 0;
+ m_iHeight = 0;
+ m_iAscent = 0;
+ m_iFlags = 0;
+ m_iMaxCharWidth = 0;
+ m_hFont = NULL;
+ m_hDC = NULL;
+ m_bAntiAliased = false;
+ m_iBlur = 0;
+ m_iScanLines = 0;
+ m_bRotary = false;
+ m_bAdditive = false;
+ m_rgiBitmapSize[0] = 0;
+ m_rgiBitmapSize[1] = 0;
+
+ Q_memset( m_ABCWidthsCache, 0, sizeof( m_ABCWidthsCache ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CWin32Font::~CWin32Font()
+{
+ CloseResource();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates the font.
+//-----------------------------------------------------------------------------
+bool CWin32Font::Create( const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags )
+{
+ // setup font properties
+ m_iTall = tall;
+ m_iWeight = weight;
+ m_iFlags = flags;
+ m_bAntiAliased = (flags & vgui::ISurface::FONTFLAG_ANTIALIAS) ? 1 : 0;
+ m_iDropShadowOffset = (flags & vgui::ISurface::FONTFLAG_DROPSHADOW) ? 1 : 0;
+ m_iOutlineSize = (flags & vgui::ISurface::FONTFLAG_OUTLINE) ? 1 : 0;
+ m_iBlur = blur;
+ m_iScanLines = scanlines;
+ m_bRotary = (flags & vgui::ISurface::FONTFLAG_ROTARY) ? 1 : 0;
+ m_bAdditive = (flags & vgui::ISurface::FONTFLAG_ADDITIVE) ? 1 : 0;
+
+ int style = GetStyleFromParameters( flags, weight );
+
+ // must support > 128, there are characters in this range in the custom fonts
+ COMPILE_TIME_ASSERT( ABCWIDTHS_CACHE_SIZE == 256 );
+
+ XUIFontMetrics fontMetrics;
+ XUICharMetrics charMetrics[256];
+
+ // many redundant requests are made that are actually the same font metrics
+ // find it in the metric cache first based on the true specific keys
+ if ( !FontManager().GetCachedXUIMetrics( windowsFontName, tall, style, &fontMetrics, charMetrics ) )
+ {
+ m_hFont = FontManager().MaterialSystem()->OpenTrueTypeFont( windowsFontName, tall, style );
+ if ( !m_hFont )
+ {
+ return false;
+ }
+
+ // getting the metrics is an expensive i/o operation, cache results
+ FontManager().MaterialSystem()->GetTrueTypeFontMetrics( m_hFont, &fontMetrics, charMetrics );
+ FontManager().SetCachedXUIMetrics( windowsFontName, tall, style, &fontMetrics, charMetrics );
+ }
+
+ m_szName = windowsFontName;
+
+ m_iHeight = fontMetrics.fMaxHeight + m_iDropShadowOffset + 2 * m_iOutlineSize;
+ m_iMaxCharWidth = fontMetrics.fMaxWidth;
+ m_iAscent = fontMetrics.fMaxAscent;
+
+ // determine cell bounds
+ m_rgiBitmapSize[0] = m_iMaxCharWidth + m_iOutlineSize * 2;
+ m_rgiBitmapSize[1] = m_iHeight;
+
+ // get char spacing
+ // a is space before character (can be negative)
+ // b is the width of the character
+ // c is the space after the character
+ Assert( ABCWIDTHS_CACHE_SIZE <= 256 );
+ Q_memset( m_ABCWidthsCache, 0, sizeof( m_ABCWidthsCache ) );
+
+ for ( int i = 1; i < ABCWIDTHS_CACHE_SIZE; i++ )
+ {
+ int a,b,c;
+
+ // Determine real a,b,c mapping from XUI Character Metrics
+ a = charMetrics[i].fMinX - 1; // Add one column of padding to make up for font rendering blurring into left column (and adjust in b)
+ b = charMetrics[i].fMaxX - charMetrics[i].fMinX + 1;
+ c = charMetrics[i].fAdvance - charMetrics[i].fMaxX; // NOTE: We probably should add a column here, but it's rarely needed in our current fonts so we're opting to save memory instead
+
+ // Widen for blur, outline, and shadow. Need to widen b and reduce a and c.
+ m_ABCWidthsCache[i].a = a - m_iBlur - m_iOutlineSize;
+ m_ABCWidthsCache[i].b = b + ( ( m_iBlur + m_iOutlineSize ) * 2 ) + m_iDropShadowOffset;
+ m_ABCWidthsCache[i].c = c - m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: generates texture data (written into appropriate font page subrects) for multiple chars
+//-----------------------------------------------------------------------------
+void CWin32Font::GetCharsRGBA( newChar_t *newChars, int numNewChars, unsigned char *pRGBA )
+{
+ if ( !m_hFont )
+ {
+ // demand request for font glyph, re-create font
+ int style = GetStyleFromParameters( m_iFlags, m_iWeight );
+ m_hFont = FontManager().MaterialSystem()->OpenTrueTypeFont( GetName(), m_iTall, style );
+ }
+
+ wchar_t *pWch = (wchar_t *)_alloca( numNewChars*sizeof(wchar_t) );
+ int *pOffsetX = (int *)_alloca( numNewChars*sizeof(int) );
+ int *pOffsetY = (int *)_alloca( numNewChars*sizeof(int) );
+ int *pWidth = (int *)_alloca( numNewChars*sizeof(int) );
+ int *pHeight = (int *)_alloca( numNewChars*sizeof(int) );
+ int *pRGBAOffset = (int *)_alloca( numNewChars*sizeof(int) );
+ for ( int i = 0; i < numNewChars; i++ )
+ {
+ int a, c, wide;
+ GetCharABCWidths( newChars[i].wch, a, wide, c );
+ pWch[i] = newChars[i].wch;
+ pOffsetX[i] = -a;
+ pOffsetY[i] = 0;
+ pWidth[i] = newChars[i].fontWide;
+ pHeight[i] = newChars[i].fontTall;
+ pRGBAOffset[i] = newChars[i].offset;
+ }
+ if ( !FontManager().MaterialSystem()->GetTrueTypeGlyphs( m_hFont, numNewChars, pWch, pOffsetX, pOffsetY, pWidth, pHeight, pRGBA, pRGBAOffset ) )
+ {
+ // failure
+ return;
+ }
+
+ for ( int i = 0; i < numNewChars; i++ )
+ {
+ // apply requested effects in specified order
+ unsigned char *pCharRGBA = pRGBA + newChars[i].offset;
+ ApplyDropShadowToTexture( newChars[i].fontWide, newChars[i].fontTall, pCharRGBA, m_iDropShadowOffset );
+ ApplyOutlineToTexture( newChars[i].fontWide, newChars[i].fontTall, pCharRGBA, m_iOutlineSize );
+ ApplyGaussianBlurToTexture( newChars[i].fontWide, newChars[i].fontTall, pCharRGBA, m_iBlur );
+ ApplyScanlineEffectToTexture( newChars[i].fontWide, newChars[i].fontTall, pCharRGBA, m_iScanLines );
+ ApplyRotaryEffectToTexture( newChars[i].fontWide, newChars[i].fontTall, pCharRGBA, m_bRotary );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: writes the char into the specified 32bpp texture at specified rect
+//-----------------------------------------------------------------------------
+void CWin32Font::GetCharRGBA( wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *pRGBA )
+{
+ newChar_t newChar;
+ newChar.wch = ch;
+ newChar.fontWide = rgbaWide;
+ newChar.fontTall = rgbaTall;
+ newChar.offset = 0;
+ GetCharsRGBA( &newChar, 1, pRGBA );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the font is equivalent to that specified
+//-----------------------------------------------------------------------------
+bool CWin32Font::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
+{
+ // do an true comparison that accounts for non-supported behaviors that gets remapped
+ // avoids creating fonts that are graphically equivalent, though specified differently
+ if ( !stricmp( windowsFontName, m_szName.String() ) &&
+ m_iTall == tall &&
+ m_iBlur == blur &&
+ m_iScanLines == scanlines )
+ {
+ // only these flags affect the font glyphs
+ int validFlags = vgui::ISurface::FONTFLAG_DROPSHADOW |
+ vgui::ISurface::FONTFLAG_OUTLINE |
+ vgui::ISurface::FONTFLAG_ROTARY |
+ vgui::ISurface::FONTFLAG_ITALIC |
+ vgui::ISurface::FONTFLAG_UNDERLINE;
+ if ( ( m_iFlags & validFlags ) == ( flags & validFlags ) )
+ {
+ if ( GetStyleFromParameters( m_iFlags, m_iWeight ) == GetStyleFromParameters( flags, weight ) )
+ {
+ // the font is equivalent
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true only if this font is valid for use
+//-----------------------------------------------------------------------------
+bool CWin32Font::IsValid()
+{
+ if ( m_szName != UTL_INVAL_SYMBOL )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the font to be the one to currently draw with in the gdi
+//-----------------------------------------------------------------------------
+void CWin32Font::SetAsActiveFont( HDC hdc )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the abc widths for a character
+//-----------------------------------------------------------------------------
+void CWin32Font::GetCharABCWidths( int ch, int &a, int &b, int &c )
+{
+ Assert( IsValid() );
+
+ if ( ch < ABCWIDTHS_CACHE_SIZE )
+ {
+ // use the cache entry
+ a = m_ABCWidthsCache[ch].a;
+ b = m_ABCWidthsCache[ch].b;
+ c = m_ABCWidthsCache[ch].c;
+ }
+ else
+ {
+ // cannot support getting character metrics outside of the font initialization
+ DevMsg( "CWin32Font: Cannot resolve character %d in font %s\n", ch, m_szName.String() );
+ Assert( 0 );
+
+ a = 0;
+ b = 0;
+ c = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the height of the font, in pixels
+//-----------------------------------------------------------------------------
+int CWin32Font::GetHeight()
+{
+ Assert( IsValid() );
+ return m_iHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
+//-----------------------------------------------------------------------------
+int CWin32Font::GetAscent()
+{
+ Assert( IsValid() );
+ return m_iAscent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the maximum width of a character, in pixels
+//-----------------------------------------------------------------------------
+int CWin32Font::GetMaxCharWidth()
+{
+ Assert( IsValid() );
+ return m_iMaxCharWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the flags used to make this font, used by the dynamic resizing code
+//-----------------------------------------------------------------------------
+int CWin32Font::GetFlags()
+{
+ Assert( IsValid() );
+ return m_iFlags;
+}
+
+void CWin32Font::CloseResource()
+{
+ if ( !m_hFont )
+ {
+ return;
+ }
+
+ // many fonts are blindly precached by vgui and never used
+ // save memory and don't hold font open, re-open if glyph actually requested used during draw
+ FontManager().MaterialSystem()->CloseTrueTypeFont( m_hFont );
+ m_hFont = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the kerned size of a char, for win32 just pass thru for now
+//-----------------------------------------------------------------------------
+void CWin32Font::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA )
+{
+ int a,b,c;
+ GetCharABCWidths(ch, a, b, c );
+ wide = ( a + b + c);
+ abcA = a;
+}
diff --git a/vgui2/vgui_surfacelib/linuxfont.cpp b/vgui2/vgui_surfacelib/linuxfont.cpp
new file mode 100644
index 0000000..b80d908
--- /dev/null
+++ b/vgui2/vgui_surfacelib/linuxfont.cpp
@@ -0,0 +1,777 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include "vgui_surfacelib/linuxfont.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <malloc.h>
+#include <tier0/dbg.h>
+#include <vgui/ISurface.h>
+#include <utlbuffer.h>
+#include <fontconfig/fontconfig.h>
+#include <freetype/ftbitmap.h>
+#include "materialsystem/imaterialsystem.h"
+
+#include "vgui_surfacelib/FontManager.h"
+#include "FontEffects.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define FT_LOAD_FLAGS 0 //$ (FT_LOAD_TARGET_LIGHT)
+
+namespace {
+
+// Freetype uses a lot of fixed float values that are 26.6 splits of a 32 bit word.
+// to make it an int, shift down the 6 bits and round up if the high bit of the 6
+// bits was set.
+inline int32_t FIXED6_2INT(int32_t x) { return ( (x>>6) + ( (x&0x20) ? (x<0 ? -1 : 1) : 0) ); }
+inline float FIXED6_2FLOAT(int32_t x) { return (float)x / 64.0f; }
+inline int32_t INT_2FIXED6(int32_t x) { return x << 6; }
+
+}
+
+bool CLinuxFont::ms_bSetFriendlyNameCacheLessFunc = false;
+CUtlRBTree< CLinuxFont::font_name_entry > CLinuxFont::m_FriendlyNameCache;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CLinuxFont::CLinuxFont() :
+ m_ExtendedABCWidthsCache(256, 0, &ExtendedABCWidthsCacheLessFunc),
+ m_ExtendedKernedABCWidthsCache( 256, 0, &ExtendedKernedABCWidthsCacheLessFunc )
+{
+ m_face = NULL;
+ m_faceValid = false;
+ m_iTall = 0;
+ m_iHeight = 0;
+ m_iHeightRequested = 0;
+ m_iWeight = 0;
+ m_iFlags = 0;
+ m_iMaxCharWidth = 0;
+ m_bAntiAliased = false;
+ m_bUnderlined = false;
+ m_iBlur = 0;
+ m_iScanLines = 0;
+ m_bRotary = false;
+ m_bAdditive = false;
+ if ( !ms_bSetFriendlyNameCacheLessFunc )
+ {
+ ms_bSetFriendlyNameCacheLessFunc = true;
+ SetDefLessFunc( m_FriendlyNameCache );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CLinuxFont::~CLinuxFont()
+{
+ if( m_faceValid )
+ {
+ FT_Done_Face( m_face );
+ m_face = NULL;
+ m_faceValid = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: build a map of friendly (char *) name to crazy ATSU bytestream, so we can ask for "Tahoma" and actually load it
+//-----------------------------------------------------------------------------
+void CLinuxFont::CreateFontList()
+{
+ if ( m_FriendlyNameCache.Count() > 0 )
+ return;
+
+ if(!FcInit())
+ return;
+ FcConfig *config;
+ FcPattern *pat;
+ FcObjectSet *os;
+ FcFontSet *fontset;
+ int i;
+ char *file;
+ const char *name;
+
+ config = FcConfigGetCurrent();
+ pat = FcPatternCreate();
+ os = FcObjectSetCreate();
+ FcObjectSetAdd(os, FC_FILE);
+ FcObjectSetAdd(os, FC_FULLNAME);
+ FcObjectSetAdd(os, FC_FAMILY);
+ FcObjectSetAdd(os, FC_SCALABLE);
+ fontset = FcFontList(config, pat, os);
+ if(!fontset)
+ return;
+ for(i = 0; i < fontset->nfont; i++)
+ {
+ FcBool scalable;
+
+ if ( FcPatternGetBool(fontset->fonts[i], FC_SCALABLE, 0, &scalable) == FcResultMatch && !scalable )
+ continue;
+
+ if ( FcPatternGetString(fontset->fonts[i], FC_FAMILY, 0, (FcChar8**)&name) != FcResultMatch )
+ continue;
+ if ( FcPatternGetString(fontset->fonts[i], FC_FILE, 0, (FcChar8**)&file) != FcResultMatch )
+ continue;
+
+ font_name_entry entry;
+ entry.m_pchFile = (char *)malloc( Q_strlen(file) + 1 );
+ entry.m_pchFriendlyName = (char *)malloc( Q_strlen(name) +1);
+ Q_memcpy( entry.m_pchFile, file, Q_strlen(file) + 1 );
+ Q_memcpy( entry.m_pchFriendlyName, name, Q_strlen(name) +1);
+ m_FriendlyNameCache.Insert( entry );
+
+ // substitute Vera Sans for Tahoma on X
+ if ( !V_stricmp( name, "Bitstream Vera Sans" ) )
+ {
+ name = "Tahoma";
+ entry.m_pchFile = (char *)malloc( Q_strlen(file) + 1 );
+ entry.m_pchFriendlyName = (char *)malloc( Q_strlen(name) +1);
+ Q_memcpy( entry.m_pchFile, file, Q_strlen(file) + 1 );
+ Q_memcpy( entry.m_pchFriendlyName, name, Q_strlen(name) +1);
+ m_FriendlyNameCache.Insert( entry );
+
+ name = "Verdana";
+ entry.m_pchFile = (char *)malloc( Q_strlen(file) + 1 );
+ entry.m_pchFriendlyName = (char *)malloc( Q_strlen(name) +1);
+ Q_memcpy( entry.m_pchFile, file, Q_strlen(file) + 1 );
+ Q_memcpy( entry.m_pchFriendlyName, name, Q_strlen(name) +1);
+ m_FriendlyNameCache.Insert( entry );
+
+ name = "Lucidia Console";
+ entry.m_pchFile = (char *)malloc( Q_strlen(file) + 1 );
+ entry.m_pchFriendlyName = (char *)malloc( Q_strlen(name) +1);
+ Q_memcpy( entry.m_pchFile, file, Q_strlen(file) + 1 );
+ Q_memcpy( entry.m_pchFriendlyName, name, Q_strlen(name) +1);
+ m_FriendlyNameCache.Insert( entry );
+ }
+ }
+
+ FcFontSetDestroy(fontset);
+ FcObjectSetDestroy(os);
+ FcPatternDestroy(pat);
+}
+
+static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
+ ...)
+{
+ va_list ap;
+ va_start(ap, value);
+
+ FcPattern* pattern = FcPatternCreate();
+
+ for (;;)
+ {
+ FcValue fcvalue;
+ fcvalue.type = vtype;
+ switch (vtype) {
+ case FcTypeString:
+ fcvalue.u.s = (FcChar8*) value;
+ break;
+ case FcTypeInteger:
+ fcvalue.u.i = (int) value;
+ break;
+ default:
+ Assert(!"FontMatch unhandled type");
+ }
+ FcPatternAdd(pattern, type, fcvalue, 0);
+
+ type = va_arg(ap, const char *);
+ if (!type)
+ break;
+ // FcType is promoted to int when passed through ...
+ vtype = static_cast<FcType>(va_arg(ap, int));
+ value = va_arg(ap, const void *);
+ };
+ va_end(ap);
+
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+
+ FcResult result;
+ FcPattern* match = FcFontMatch(0, pattern, &result);
+ FcPatternDestroy(pattern);
+
+ return match;
+}
+
+bool CLinuxFont::CreateFromMemory(const char *windowsFontName, void *data, int datasize, int tall, int weight, int blur, int scanlines, int flags)
+{
+ // setup font properties
+ m_szName = windowsFontName;
+ m_iTall = tall;
+ m_iWeight = weight;
+ m_iFlags = flags;
+ m_bAntiAliased = flags & vgui::ISurface::FONTFLAG_ANTIALIAS;
+ m_bUnderlined = flags & vgui::ISurface::FONTFLAG_UNDERLINE;
+ m_iDropShadowOffset = (flags & vgui::ISurface::FONTFLAG_DROPSHADOW) ? 1 : 0;
+ m_iOutlineSize = (flags & vgui::ISurface::FONTFLAG_OUTLINE) ? 1 : 0;
+ m_iBlur = blur;
+ m_iScanLines = scanlines;
+ m_bRotary = flags & vgui::ISurface::FONTFLAG_ROTARY;
+ m_bAdditive = flags & vgui::ISurface::FONTFLAG_ADDITIVE;
+
+ if ( !HushAsserts() )
+ {
+ // These flags are NYI in Linux right now.
+ Assert( !m_bAntiAliased );
+ Assert( !m_bUnderlined );
+ Assert( !m_bAdditive );
+ }
+
+ Assert( !m_faceValid );
+ FT_Error error = FT_New_Memory_Face( FontManager().GetFontLibraryHandle(), (FT_Byte *)data, datasize, 0, &m_face );
+ if ( error )
+ {
+ // FT_Err_Unknown_File_Format?
+ Msg( "FT_New_Memory_Face failed. font:%s error:%d\n", windowsFontName, error );
+ return false;
+ }
+
+ if ( m_face->charmap == NULL )
+ {
+ FT_Error error = FT_Select_Charmap( m_face, FT_ENCODING_APPLE_ROMAN );
+ if ( error )
+ {
+ FT_Done_Face( m_face );
+ m_face = NULL;
+
+ Msg( "Font %s has no valid charmap\n", windowsFontName );
+ return false;
+ }
+ }
+
+ m_iHeightRequested = m_iTall;
+
+ // Loop through until we get a height that is less than or equal to the requested height.
+ // We tried using the BBOX ascender / descender, but it was overly large compared to Windows.
+ // We also tried using the size metrics, but the ascender wasn't high enough and accents were cut off.
+ // Descender from size metrics was too low for fonts with no lower case characters.
+ // Compromise: Use ascent from O' and descent from bbox. Diffs on textures indicate this is best choice.
+ // Used these command lines vars and convars to help beyond compare linux and windows:
+ // -precachefontintlchars / -enable_font_bounding_boxes / vgui_spew_fonts / mat_texture_save_fonts
+
+ bool bFirstTimeThrough = true;
+ int IncAmount = -1;
+
+ for ( ;; )
+ {
+ bool SetPixelSizesFailed = false;
+
+ FT_Error error = FT_Set_Pixel_Sizes( m_face, 0, m_iHeightRequested );
+ if ( error )
+ {
+ SetPixelSizesFailed = true;
+
+ // If FT_Set_Pixel_Sizes fails, it should be because we've got a fixed-size font.
+ Assert( m_face->face_flags & FT_FACE_FLAG_FIXED_SIZES );
+ Assert( !( m_face->face_flags & FT_FACE_FLAG_SCALABLE ) );
+
+ if ( m_face->num_fixed_sizes )
+ {
+ // Pick width/height of first size.
+ int width = m_face->available_sizes[ 0 ].width;
+ m_iHeightRequested = m_face->available_sizes[ 0 ].height;
+
+ // Loop through all the other available sizes and find the closest match.
+ for ( int i = 1; i < m_face->num_fixed_sizes; i++ )
+ {
+ if ( ( m_face->available_sizes[ i ].height <= m_iTall ) &&
+ ( m_face->available_sizes[ i ].height > m_iHeightRequested ) )
+ {
+ width = m_face->available_sizes[ i ].width;
+ m_iHeightRequested = m_face->available_sizes[ i ].height;
+ }
+ }
+
+ FT_Size_RequestRec req;
+
+ Q_memset( &req, 0, sizeof( req ) );
+ req.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
+ req.width = INT_2FIXED6( width );
+ req.height = INT_2FIXED6( m_iHeightRequested );
+ req.horiResolution = 0;
+ req.vertResolution = 0;
+
+ error = FT_Request_Size( m_face, &req );
+ if ( error )
+ {
+ Msg( "FT_Request_Size failed on %s / %s\n",
+ m_face->family_name ? m_face->family_name : "??",
+ m_face->style_name ? m_face->style_name : "??" );
+ }
+ }
+ }
+
+ FT_Pos ascender, descender;
+
+ if( SetPixelSizesFailed )
+ {
+ // If SetPixelSizesFailed failed, then we've hopefully got a fixed size
+ // font, and we can just use the metrics.
+ ascender = m_face->size->metrics.ascender;
+ descender = m_face->size->metrics.descender;
+ }
+ else
+ {
+ // Full bounding box ascent and descent:
+ // ( a * b ) / 0x10000. y_scale is 16.16.
+ //$ ascender = FT_MulFix( m_face->bbox.yMax, m_face->size->metrics.y_scale );
+ descender = FT_MulFix( m_face->bbox.yMin, m_face->size->metrics.y_scale );
+
+ // Metrics ascent and descent
+ ascender = m_face->size->metrics.ascender;
+ //$ descender = m_face->size->metrics.descender;
+
+ // While running with Spanish, the m_face->size->metrics.ascender is less
+ // than the bitmap_top for the character. This makes GetCharRGBA() chop off
+ // the top of the O and the accent is skipped. Complete hack here, but we
+ // check for the tallest character we know about (O') and bump up the ascender
+ // value if it is greater than what we've currently got.
+ wchar_t ch = 0xd3;
+ error = FT_Load_Char( m_face, ch, FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL);
+ if ( !error )
+ {
+ int glyph_index = FT_Get_Char_Index( m_face, ch );
+ error = FT_Load_Glyph( m_face, glyph_index, FT_LOAD_RENDER );
+ if ( !error )
+ {
+ FT_GlyphSlot slot = m_face->glyph;
+ FT_Pos ascenderTop = INT_2FIXED6( slot->bitmap_top );
+
+ if( ascenderTop > ascender )
+ {
+ ascender = ascenderTop;
+ }
+ else if ( !slot->bitmap.rows || !slot->bitmap.width )
+ {
+ // We didn't find an O' character in this font: use the full BBox.
+ ascender = FT_MulFix( m_face->bbox.yMax, m_face->size->metrics.y_scale );
+ }
+ }
+ }
+ }
+
+ m_iAscent = FIXED6_2INT( ascender );
+
+ m_iMaxCharWidth = FIXED6_2INT( m_face->size->metrics.max_advance );
+
+ const int fxpHeight = ascender + -descender + INT_2FIXED6( m_iDropShadowOffset + 2 * m_iOutlineSize );
+ m_iHeight = FIXED6_2INT( fxpHeight );
+
+ // If we're exact or we got too small, bail.
+ if ( SetPixelSizesFailed || ( m_iHeight == m_iTall ) || ( m_iHeight < 7 ) || ( m_iHeightRequested <= 1 ) )
+ break;
+
+ if( bFirstTimeThrough )
+ {
+ bFirstTimeThrough = false;
+
+ // If we're smaller than requested, start searching up.
+ if ( m_iHeight < m_iTall )
+ IncAmount = +1;
+ }
+ else if( IncAmount > 0 )
+ {
+ // If we're searching up and went too far, drop IncAmount and run down.
+ if( m_iHeight > m_iTall )
+ IncAmount = -1;
+ }
+ else if ( m_iHeight <= m_iTall )
+ {
+ // If the height is less than tall, we're done.
+ break;
+ }
+
+ m_iHeightRequested += IncAmount;
+ }
+
+ m_faceValid = true;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a font name from windows, match it to the filename and return that.
+//-----------------------------------------------------------------------------
+char *CLinuxFont::GetFontFileName( const char *windowsFontName, int flags )
+{
+ bool bBold = false;
+ const char *pchFontName = windowsFontName;
+
+ if ( !Q_stricmp( pchFontName, "Tahoma" ) )
+ pchFontName = "Bitstream Vera Sans";
+ else if ( !Q_stricmp( pchFontName, "Arial Black" ) || Q_stristr( pchFontName, "bold" ) )
+ bBold = true;
+
+ const int italic = ( flags & vgui::ISurface::FONTFLAG_ITALIC ) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
+ const int nFcWeight = bBold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
+
+ FcPattern *match = FontMatch( FC_FAMILY, FcTypeString, pchFontName,
+ FC_WEIGHT, FcTypeInteger, nFcWeight,
+ FC_SLANT, FcTypeInteger, italic,
+ NULL);
+ if ( !match )
+ {
+ AssertMsg1( false, "Unable to find font named %s\n", windowsFontName );
+ return NULL;
+ }
+ else
+ {
+ char *filenameret = NULL;
+ FcChar8* filename = NULL;
+
+ if ( FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch )
+ {
+ AssertMsg1( false, "Unable to find font named %s\n", windowsFontName );
+ }
+ else
+ {
+ filenameret = strdup( ( char * )filename );
+ }
+
+ FcPatternDestroy( match );
+ return filenameret;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: writes the char into the specified 32bpp texture
+//-----------------------------------------------------------------------------
+void CLinuxFont::GetCharRGBA( wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *prgba )
+{
+ bool bShouldAntialias = m_bAntiAliased;
+
+ // filter out
+ if ( ( ch > 0x00FF ) && !( m_iFlags & vgui::ISurface::FONTFLAG_CUSTOM ) )
+ {
+ bShouldAntialias = false;
+ }
+
+ FT_Error error = FT_Load_Char( m_face, ch, FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL );
+ if ( error )
+ {
+ Msg( "Error in FT_Load_Char: ch:%x error:%x\n", (int)ch, error );
+ return;
+ }
+
+ int glyph_index = FT_Get_Char_Index( m_face, ch );
+ error = FT_Load_Glyph( m_face, glyph_index, FT_LOAD_RENDER | FT_LOAD_FLAGS );
+ if ( error )
+ {
+ Msg( "Error in FL_Load_Glyph: glyph_index:%d error:%x\n", glyph_index, error );
+ return;
+ }
+
+ int yBitmapStart = 0;
+ FT_GlyphSlot slot = m_face->glyph;
+ int nSkipRows = ( m_iAscent - slot->bitmap_top );
+
+ if( nSkipRows < 0 )
+ {
+ yBitmapStart = -nSkipRows;
+ nSkipRows = 0;
+ }
+ if ( nSkipRows >= rgbaTall )
+ {
+ Msg( "nSkipRows(%d) > rgbaTall(%d) ch:%d\n", nSkipRows, rgbaTall, (int)ch );
+ return;
+ }
+
+ if ( m_face->glyph->bitmap.width == 0 )
+ {
+ Msg( "m_face->glyph->bitmap.width is 0 for ch:%d %s\n", (int)ch, m_face->family_name ? m_face->family_name : "??" );
+ return;
+ }
+
+ FT_Bitmap bitmap;
+ FT_Library ftLibrary = FontManager().GetFontLibraryHandle();
+
+ FT_Bitmap_New( &bitmap );
+
+ error = FT_Bitmap_Convert( ftLibrary, &m_face->glyph->bitmap, &bitmap, 1 );
+ if( error == 0 )
+ {
+ uint32 alpha_scale = 1;
+ int Width = min( rgbaWide, bitmap.width );
+ unsigned char *rgba = prgba + ( nSkipRows * rgbaWide * 4 );
+
+ switch( m_face->glyph->bitmap.pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO: // 8-bit per pixel bitmap
+ alpha_scale *= 256;
+ break;
+ case FT_PIXEL_MODE_GRAY2: // 2-bit per pixel bitmap
+ alpha_scale *= 64;
+ break;
+ case FT_PIXEL_MODE_GRAY4: // 4-bit per pixel bitmap
+ alpha_scale *= 16;
+ break;
+ }
+
+ /* now draw to our target surface */
+ for ( int y = yBitmapStart; y < MIN( bitmap.rows, rgbaTall - nSkipRows ); y++ )
+ {
+ for ( int x = 0; x < Width; x++ )
+ {
+ int rgbaOffset = 4 * ( x + m_iBlur ); // +(rgbaTall-y-1)*rgbaWide*4
+ uint32 alpha = Min( 255U, alpha_scale * bitmap.buffer[ x + y * bitmap.pitch ] );
+
+ rgba[ rgbaOffset + 0 ] = 255;
+ rgba[ rgbaOffset + 1 ] = 255;
+ rgba[ rgbaOffset + 2 ] = 255;
+ rgba[ rgbaOffset + 3 ] = alpha;
+ }
+ rgba += ( rgbaWide * 4 );
+ }
+
+ // apply requested effects in specified order
+ ApplyDropShadowToTexture( rgbaWide, rgbaTall, prgba, m_iDropShadowOffset );
+ ApplyOutlineToTexture( rgbaWide, rgbaTall, prgba, m_iOutlineSize );
+ ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, prgba, m_iBlur );
+ ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, prgba, m_iScanLines );
+ ApplyRotaryEffectToTexture( rgbaWide, rgbaTall, prgba, m_bRotary );
+ }
+ else
+ {
+ Msg( "FT_Bitmap_Convert failed: %d on %s\n", error, m_face->family_name ? m_face->family_name : "??" );
+ }
+
+ FT_Bitmap_Done( ftLibrary, &bitmap );
+}
+
+void CLinuxFont::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA, float &abcC )
+{
+ abcA = abcC = wide = 0.0f;
+
+ // look for it in the cache
+ kerned_abc_cache_t finder = { ch, chBefore, chAfter };
+
+ unsigned short iKerned = m_ExtendedKernedABCWidthsCache.Find(finder);
+ if (m_ExtendedKernedABCWidthsCache.IsValidIndex(iKerned))
+ {
+ abcA = 0; //$ NYI. m_ExtendedKernedABCWidthsCache[iKerned].abc.abcA;
+ abcC = 0; //$ NYI. m_ExtendedKernedABCWidthsCache[iKerned].abc.abcC;
+ wide = m_ExtendedKernedABCWidthsCache[iKerned].abc.wide;
+ return;
+ }
+
+ FT_UInt glyph_index;
+ FT_Bool use_kerning;
+ FT_UInt previous;
+ int32_t iFxpPenX;
+
+ iFxpPenX = 0;
+ wide = 0;
+
+ use_kerning = FT_HAS_KERNING( m_face );
+ previous = chBefore;
+
+ /* convert character code to glyph index */
+ glyph_index = FT_Get_Char_Index( m_face, ch );
+
+ /* retrieve kerning distance and move pen position */
+ if ( use_kerning && previous && glyph_index )
+ {
+ FT_Vector delta;
+
+ FT_Get_Kerning( m_face, previous, glyph_index,
+ FT_KERNING_DEFAULT, &delta );
+
+ iFxpPenX += delta.x;
+ }
+
+ /* load glyph image into the slot (erase previous one) */
+ int error = FT_Load_Glyph( m_face, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_FLAGS );
+ if ( error )
+ {
+ Error( "Error in FL_Load_Glyph: glyph_index:%d ch:%x error:%x\n", glyph_index, (int)ch, error );
+ }
+
+ FT_GlyphSlot slot = m_face->glyph;
+ iFxpPenX += slot->advance.x;
+
+ if ( FIXED6_2INT(iFxpPenX) > wide )
+ wide = FIXED6_2INT(iFxpPenX);
+
+ //$ NYI: finder.abc.abcA = abcA;
+ //$ NYI: finder.abc.abcC = abcC;
+ finder.abc.wide = wide;
+ m_ExtendedKernedABCWidthsCache.Insert(finder);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the abc widths for a character
+//-----------------------------------------------------------------------------
+void CLinuxFont::GetCharABCWidths(int ch, int &a, int &b, int &c)
+{
+ Assert(IsValid());
+
+ // look for it in the cache
+ abc_cache_t finder = { (wchar_t)ch };
+
+ unsigned short i = m_ExtendedABCWidthsCache.Find(finder);
+ if (m_ExtendedABCWidthsCache.IsValidIndex(i))
+ {
+ a = m_ExtendedABCWidthsCache[i].abc.a;
+ b = m_ExtendedABCWidthsCache[i].abc.b;
+ c = m_ExtendedABCWidthsCache[i].abc.c;
+ return;
+ }
+
+ a = b = c = 0;
+
+ FT_Error error = FT_Load_Char( m_face, ch, 0 );
+ if ( error )
+ {
+ Msg( "Error in FT_Load_Char: ch:%x error:%x\n", ch, error );
+ return;
+ }
+
+ // width: The glyph's width.
+ // horiBearingX: Left side bearing for horizontal layout.
+ // horiAdvance: Advance width for horizontal layout.
+ FT_Glyph_Metrics metrics = m_face->glyph->metrics;
+
+ finder.abc.a = metrics.horiBearingX / 64 - m_iBlur - m_iOutlineSize;
+ finder.abc.b = metrics.width / 64 + ( ( m_iBlur + m_iOutlineSize ) * 2 ) + m_iDropShadowOffset;
+ finder.abc.c = ( metrics.horiAdvance - metrics.horiBearingX - metrics.width ) / 64 - m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
+
+ m_ExtendedABCWidthsCache.Insert( finder );
+
+ a = finder.abc.a;
+ b = finder.abc.b;
+ c = finder.abc.c;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the font is equivalent to that specified
+//-----------------------------------------------------------------------------
+bool CLinuxFont::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
+{
+ if (!Q_stricmp(windowsFontName, m_szName.String() )
+ && m_iTall == tall
+ && m_iWeight == weight
+ && m_iBlur == blur
+ && m_iFlags == flags)
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true only if this font is valid for use
+//-----------------------------------------------------------------------------
+bool CLinuxFont::IsValid()
+{
+ if ( !m_szName.IsEmpty() )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the height of the font, in pixels
+//-----------------------------------------------------------------------------
+int CLinuxFont::GetHeight()
+{
+ assert(IsValid());
+ return m_iHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the requested height of the font
+//-----------------------------------------------------------------------------
+int CLinuxFont::GetHeightRequested()
+{
+ assert(IsValid());
+ return m_iHeightRequested;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
+//-----------------------------------------------------------------------------
+int CLinuxFont::GetAscent()
+{
+ assert(IsValid());
+ return m_iAscent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the maximum width of a character, in pixels
+//-----------------------------------------------------------------------------
+int CLinuxFont::GetMaxCharWidth()
+{
+ assert(IsValid());
+ return m_iMaxCharWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the flags used to make this font, used by the dynamic resizing code
+//-----------------------------------------------------------------------------
+int CLinuxFont::GetFlags()
+{
+ return m_iFlags;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Comparison function for abc widths storage
+//-----------------------------------------------------------------------------
+bool CLinuxFont::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs)
+{
+ return lhs.wch < rhs.wch;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Comparison function for abc widths storage
+//-----------------------------------------------------------------------------
+bool CLinuxFont::ExtendedKernedABCWidthsCacheLessFunc(const kerned_abc_cache_t &lhs, const kerned_abc_cache_t &rhs)
+{
+ return ( lhs.wch < rhs.wch ) ||
+ ( lhs.wch == rhs.wch && lhs.wchBefore < rhs.wchBefore ) ||
+ ( lhs.wch == rhs.wch && lhs.wchBefore == rhs.wchBefore && lhs.wchAfter < rhs.wchAfter );
+}
+
+void *CLinuxFont::SetAsActiveFont( void *cglContext )
+{
+ Assert( false );
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if this font has a glyph for the code point.
+//-----------------------------------------------------------------------------
+bool CLinuxFont::HasChar(wchar_t wch)
+{
+ return FT_Get_Char_Index( m_face, wch ) != 0;
+}
+
+
+#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 CLinuxFont::Validate( CValidator &validator, const char *pchName )
+{
+ validator.Push( "CLinuxFont", this, pchName );
+
+ m_ExtendedABCWidthsCache.Validate( validator, "m_ExtendedABCWidthsCache" );
+ m_ExtendedKernedABCWidthsCache.Validate( validator, "m_ExtendedKernedABCWidthsCache" );
+
+ validator.Pop();
+}
+#endif // DBGFLAG_VALIDATE
diff --git a/vgui2/vgui_surfacelib/osxfont.cpp b/vgui2/vgui_surfacelib/osxfont.cpp
new file mode 100644
index 0000000..59b1f4a
--- /dev/null
+++ b/vgui2/vgui_surfacelib/osxfont.cpp
@@ -0,0 +1,769 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <math.h>
+#include <tier0/dbg.h>
+#include <vgui/ISurface.h>
+#include <tier0/mem.h>
+#include <utlbuffer.h>
+
+#include "vgui_surfacelib/osxfont.h"
+#include "FontEffects.h"
+
+struct MetricsTweaks_t
+{
+ const char *m_windowsFontName;
+ int m_sizeAdjust;
+ float m_ascentMultiplier;
+ float m_descentMultiplier;
+ float m_leadingMultiplier;
+};
+
+//94: HFont:0x000000b9, TFTypeDeath, TFTypeDeathClientScheme-no, font:tfd, tall:27(28)
+//95: HFont:0x000000ba, TFTypeDeath, TFTypeDeathClientScheme-p, font:tfd, tall:55(59)
+
+static const MetricsTweaks_t g_defaultMetricTweaks = { NULL, 0, 1.0, 1.0, 1.0 };
+
+static MetricsTweaks_t g_FontMetricTweaks[] =
+{
+ { "Helvetica", 0, 1.0, 1.0, 1.05 },
+ { "Helvetica Bold", 0, 1.0, 1.0, 1.0 },
+ { "HL2cross", 0, 0.8, 1.0, 1.1},
+ { "Counter-Strike Logo", 0, 1.0, 1.0, 1.1 },
+ { "TF2", -2, 1.0, 1.0, 1.0 },
+ { "TF2 Professor", -2, 1.0, 2.0, 1.1 },
+ { "TF2 Build", -2, 1.0, 1.0, 1.0 },
+ { "Stubble bold", -6, 1.3, 1.0, 1.0 },
+ { "tfd", 0, 1.5, 1.0, 1.0 }, // "TFTypeDeath"
+ //{ "TF2 Secondary", -2, 1.0, 1.0, 1.0 },
+// { "Verdana", 0, 1.25, 1.0, 1.0 },
+};
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef STAGING_ONLY
+#define CHECK_ATSU_ERR( err ) if ( (err) != noErr ) Msg( "COSXFont::%s (%d) ATSU Error: %d\n", __FUNCTION__, __LINE__, err );
+#else
+#define CHECK_ATSU_ERR( err )
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+COSXFont::COSXFont() : m_ExtendedABCWidthsCache( 256, 0, &ExtendedABCWidthsCacheLessFunc ),
+ m_ExtendedKernedABCWidthsCache( 256, 0, &ExtendedKernedABCWidthsCacheLessFunc )
+{
+ m_iTall = 0;
+ m_iWeight = 0;
+ m_iFlags = 0;
+ m_bAntiAliased = false;
+ m_bRotary = false;
+ m_bAdditive = false;
+ m_iDropShadowOffset;
+ m_bUnderlined = false;
+ m_iOutlineSize = 0;
+
+ m_iHeight = 0;
+ m_iMaxCharWidth = 0;
+ m_iAscent = 0;
+
+ m_iScanLines = 0;
+ m_iBlur = 0;
+ m_pGaussianDistribution = NULL;
+
+ m_ATSUFont = kATSFontRefUnspecified;
+ m_pContextMemory = NULL;
+ m_ContextRef = 0;
+ m_ATSUStyle = NULL;
+ m_ATSUTextLayout = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+COSXFont::~COSXFont()
+{
+ if ( m_ContextRef )
+ CGContextRelease( m_ContextRef );
+
+ if ( m_pContextMemory )
+ delete [] m_pContextMemory;
+
+ if ( m_ATSUStyle )
+ ATSUDisposeStyle( m_ATSUStyle );
+
+ if ( m_ATSUTextLayout )
+ ATSUDisposeTextLayout( m_ATSUTextLayout );
+}
+
+bool COSXFont::CreateStyle( float flFontSize, bool bBold )
+{
+ OSStatus err = ATSUCreateStyle( &m_ATSUStyle );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return false;
+ }
+
+ Boolean isBold = bBold;
+ Boolean isUnderlined = m_bUnderlined;
+ float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ Fixed fontsize = FloatToFixed( flFontSize ); // font size is based on 72dpi and not pixels, so convert to pixels
+ Boolean isItalic = m_iFlags & vgui::ISurface::FONTFLAG_ITALIC;
+ ATSStyleRenderingOptions renderOpt = kATSStyleNoOptions;
+
+ renderOpt |= ( m_bAntiAliased ? kATSStyleApplyAntiAliasing : kATSStyleNoAntiAliasing );
+
+ const ATSUAttributeTag styleTags[] =
+ {
+ kATSUFontTag,
+ kATSUSizeTag,
+ kATSUQDItalicTag,
+ kATSUQDBoldfaceTag,
+ kATSUStyleRenderingOptionsTag,
+ kATSUQDUnderlineTag,
+ kATSURGBAlphaColorTag,
+ };
+
+ const ATSUAttributeValuePtr styleValues[] =
+ {
+ &m_ATSUFont,
+ &fontsize,
+ &isItalic,
+ &isBold,
+ &renderOpt,
+ &isUnderlined,
+ &color,
+ };
+
+ const ByteCount styleSizes[] =
+ {
+ sizeof( ATSUFontID ),
+ sizeof( Fixed ),
+ sizeof( Boolean ),
+ sizeof( Boolean ),
+ sizeof( renderOpt ),
+ sizeof( Boolean ),
+ sizeof( color ),
+ };
+
+ err = ATSUSetAttributes( m_ATSUStyle, Q_ARRAYSIZE( styleTags ), styleTags, styleSizes, styleValues );
+ if (err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return false;
+ }
+
+ return true;
+}
+
+bool COSXFont::CreateTextLayout()
+{
+ OSStatus err = ATSUCreateTextLayout( &m_ATSUTextLayout );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return false;
+ }
+
+ Fract alignment = kATSUStartAlignment;
+ ATSUTextMeasurement lineWidth = IntToFixed( 32000 );
+ ATSLineLayoutOptions layoutOptions = kATSLineUseDeviceMetrics | kATSLineDisableAllLayoutOperations;
+
+ ATSUAttributeTag theTags[] =
+ {
+ kATSUCGContextTag,
+ kATSULineWidthTag,
+ kATSULineFlushFactorTag,
+ kATSULineLayoutOptionsTag,
+ };
+ ByteCount theSizes[] =
+ {
+ sizeof( CGContextRef ),
+ sizeof( ATSUTextMeasurement ),
+ sizeof( Fract ),
+ sizeof( ATSLineLayoutOptions ),
+ };
+ ATSUAttributeValuePtr theValues[] =
+ {
+ &m_ContextRef,
+ &lineWidth,
+ &alignment,
+ &layoutOptions,
+ };
+
+ err = ATSUSetLayoutControls( m_ATSUTextLayout, Q_ARRAYSIZE( theTags ), theTags, theSizes, theValues );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: creates the font from windows. returns false if font does not exist in the OS.
+//-----------------------------------------------------------------------------
+bool COSXFont::Create( const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags )
+{
+ // setup font properties
+ m_iTall = tall;
+ m_iWeight = weight;
+ m_iFlags = flags;
+ m_iBlur = blur;
+ m_iScanLines = scanlines;
+ m_bAntiAliased = flags & vgui::ISurface::FONTFLAG_ANTIALIAS;
+ m_bUnderlined = flags & vgui::ISurface::FONTFLAG_UNDERLINE;
+ m_iDropShadowOffset = ( flags & vgui::ISurface::FONTFLAG_DROPSHADOW ) ? 1 : 0;
+ m_iOutlineSize = ( flags & vgui::ISurface::FONTFLAG_OUTLINE ) ? 1 : 0;
+ m_bRotary = flags & vgui::ISurface::FONTFLAG_ROTARY;
+ m_bAdditive = flags & vgui::ISurface::FONTFLAG_ADDITIVE;
+
+ m_ATSUFont = kATSFontRefUnspecified;
+ CFStringRef fontName = CFStringCreateWithCString( kCFAllocatorDefault, windowsFontName, kCFStringEncodingUTF8 );
+ if ( fontName )
+ {
+ m_ATSUFont = ATSFontFindFromPostScriptName( fontName, kATSOptionFlagsDefault );
+ CFRelease( fontName );
+ }
+
+ if ( m_ATSUFont == kATSFontRefUnspecified )
+ {
+ CFStringRef fontName = CFStringCreateWithCString( kCFAllocatorDefault, windowsFontName, kCFStringEncodingUTF8 );
+ if ( fontName )
+ {
+ m_ATSUFont = ATSFontFindFromName( fontName, kATSOptionFlagsDefault );
+ CFRelease( fontName );
+ }
+ }
+
+ if ( m_ATSUFont == kATSFontRefUnspecified )
+ {
+ CHECK_ATSU_ERR( -1 );
+ return false;
+ }
+
+ ATSFontMetrics aMetrics;
+ OSStatus err = ATSFontGetHorizontalMetrics( m_ATSUFont, kATSOptionFlagsDefault, &aMetrics );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return false;
+ }
+
+ MetricsTweaks_t metricTweaks = g_defaultMetricTweaks;
+ for ( int i = 0; i < Q_ARRAYSIZE( g_FontMetricTweaks ); i++ )
+ {
+ if ( !Q_stricmp( windowsFontName, g_FontMetricTweaks[ i ].m_windowsFontName ) )
+ {
+ metricTweaks = g_FontMetricTweaks[ i ];
+ break;
+ }
+ }
+
+ bool bBold = ( !Q_stricmp( windowsFontName, "Arial Black" ) || Q_stristr( windowsFontName, "bold" ) );
+ float flFontSize = ( (float)( m_iTall + metricTweaks.m_sizeAdjust ) / ( aMetrics.ascent - aMetrics.descent + aMetrics.leading ) );
+
+ m_iAscent = ceil( ( aMetrics.ascent / ( aMetrics.ascent - aMetrics.descent + aMetrics.leading ) ) *
+ ( m_iTall + metricTweaks.m_sizeAdjust ) * ( metricTweaks.m_ascentMultiplier ) );
+ m_iHeight = ceil( ((float)( m_iTall + metricTweaks.m_sizeAdjust ) *
+ ( aMetrics.ascent * metricTweaks.m_ascentMultiplier -
+ aMetrics.descent * metricTweaks.m_descentMultiplier +
+ aMetrics.leading * metricTweaks.m_leadingMultiplier ) +
+ m_iDropShadowOffset + 2 * m_iOutlineSize ) );
+
+ m_iMaxCharWidth = ( metricTweaks.m_leadingMultiplier * aMetrics.maxAdvanceWidth * m_iTall ) + 0.5f;
+
+ unsigned int bytesPerRow = m_iMaxCharWidth * 4;
+
+ m_pContextMemory = new char[ (int)bytesPerRow * m_iHeight ];
+ Q_memset( m_pContextMemory, 0x0, (int)( bytesPerRow * m_iHeight ) );
+
+ const size_t bitsPerComponent = 8;
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ m_ContextRef = CGBitmapContextCreate(
+ m_pContextMemory,
+ m_iMaxCharWidth,
+ m_iHeight,
+ bitsPerComponent,
+ bytesPerRow,
+ colorSpace,
+ kCGImageAlphaPremultipliedLast );
+
+ CGColorSpaceRelease( colorSpace );
+
+ CGContextSetAllowsAntialiasing( m_ContextRef, m_bAntiAliased );
+ CGContextSetShouldAntialias( m_ContextRef, m_bAntiAliased );
+ CGContextSetTextDrawingMode( m_ContextRef, kCGTextFill );
+ CGContextSetRGBStrokeColor( m_ContextRef, 1.0f, 1.0f, 1.0f, 1.0f );
+ CGContextSetLineWidth( m_ContextRef, 1 );
+
+ // Calculate our gaussian distribution for if we're blurred.
+ if ( m_iBlur > 1 )
+ {
+ m_pGaussianDistribution = new float[ m_iBlur * 2 + 1 ];
+ double sigma = 0.683 * m_iBlur;
+
+ for (int x = 0; x <= (m_iBlur * 2); x++)
+ {
+ int val = x - m_iBlur;
+ m_pGaussianDistribution[x] = (float)(1.0f / sqrt(2 * 3.14 * sigma * sigma)) * pow(2.7, -1 * (val * val) / (2 * sigma * sigma));
+ }
+ }
+
+ if ( !CreateStyle( flFontSize, bBold ) )
+ return false;
+
+ // Create our ATSUTextLayout object.
+ if ( !CreateTextLayout() )
+ return false;
+
+ // Set our font name last as we use it to determine success (or not).
+ m_szName = windowsFontName;
+ return true;
+}
+
+void COSXFont::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA )
+{
+ // look for it in the cache
+ kerned_abc_cache_t finder = { ch, chBefore, chAfter };
+
+ wide = abcA = 0.0f;
+
+ unsigned short iKerned = m_ExtendedKernedABCWidthsCache.Find( finder );
+ if ( m_ExtendedKernedABCWidthsCache.IsValidIndex( iKerned ) )
+ {
+ abcA = m_ExtendedKernedABCWidthsCache[iKerned].abc.abcA;
+ wide = m_ExtendedKernedABCWidthsCache[ iKerned ].abc.wide;
+ return;
+ }
+
+ if ( !m_ATSUStyle || ( ch == 0 ) )
+ return;
+
+ CFCharacterSetRef badCharSetRef = CFCharacterSetGetPredefined( kCFCharacterSetIllegal );
+
+ uint32 i = 0;
+ uint32 iTarget = 0;
+ bool bBadChar = false;
+ wchar_t wchString[ 4 ];
+
+ if ( chBefore )
+ {
+ bBadChar = CFCharacterSetIsLongCharacterMember( badCharSetRef, chBefore );
+ Assert( !bBadChar );
+ if ( !bBadChar)
+ wchString[ i++ ] = chBefore;
+ }
+
+ iTarget = i;
+ bBadChar = CFCharacterSetIsLongCharacterMember( badCharSetRef, ch );
+ Assert( !bBadChar );
+ if ( bBadChar )
+ return; // 0.0 width for bad characters.
+
+ wchString[ i++ ] = ch;
+
+ if ( chAfter )
+ {
+ bBadChar = CFCharacterSetIsLongCharacterMember( badCharSetRef, chAfter );
+ Assert( !bBadChar );
+ if ( !bBadChar)
+ wchString[ i++ ] = chAfter;
+ }
+
+ wchString[ i ] = 0;
+
+ CFStringRef convertedKey = CFStringCreateWithBytes( kCFAllocatorDefault, (const UInt8 *)wchString, i * sizeof(wchar_t), kCFStringEncodingUTF32LE, false );
+ if ( !convertedKey )
+ return;
+
+ CFIndex usedBufLen = 0;
+ char chUTF16Text[ Q_ARRAYSIZE( wchString ) * 2 ];
+
+ CFStringGetBytes( convertedKey, CFRangeMake( 0, (int)i ), kCFStringEncodingUnicode, 0, false, (UInt8 *)chUTF16Text, Q_ARRAYSIZE( chUTF16Text ), &usedBufLen );
+ CFRelease( convertedKey );
+
+ OSStatus err = ATSUSetTextPointerLocation( m_ATSUTextLayout, (ConstUniCharArrayPtr)chUTF16Text, kATSUFromTextBeginning, kATSUToTextEnd, i );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+ err = ATSUSetRunStyle( m_ATSUTextLayout, m_ATSUStyle, 0, i );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+ err = ATSUSetTransientFontMatching( m_ATSUTextLayout, false );
+ if ( err != noErr )
+ CHECK_ATSU_ERR( err );
+
+ ATSLayoutRecord *layoutRecords;
+ ItemCount glyphCount;
+
+ layoutRecords = NULL;
+ err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( m_ATSUTextLayout, 0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **) &layoutRecords, &glyphCount );
+ CHECK_ATSU_ERR( err );
+ if( err == noErr )
+ {
+ ATSGlyphIdealMetrics ScreenMetricts[ Q_ARRAYSIZE( wchString ) ];
+ err = ATSUGlyphGetIdealMetrics( m_ATSUStyle, i, &layoutRecords[ 0 ].glyphID, sizeof( ATSLayoutRecord ), ScreenMetricts );
+ CHECK_ATSU_ERR( err );
+ if( err == noErr )
+ {
+ wide = ScreenMetricts[ iTarget ].advance.x;
+ abcA = ScreenMetricts[ iTarget ].sideBearing.x - (float)m_iBlur - (float)m_iOutlineSize;
+ }
+
+ ATSUDirectReleaseLayoutDataArrayPtr( NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**) &layoutRecords );
+ }
+
+ if ( ( wide < 0.001f && abcA < 0.001f ) || ( ch == chAfter ) )
+ {
+ // For some fonts the kerning engine has an issue and considers the second character in a pair
+ // (i.e the ee in bleed) to be zero width and abcA and the first char to be double width,
+ // so in that case fall back to simple 1 char metrics.
+ int a, b, c;
+ GetCharABCWidths( ch, a, b, c );
+
+ wide = a + b + c;
+ abcA = a;
+ }
+
+ // printf( "GetKernedCharWidth: %c (%f, %f) (%c, %c)\n", (char)ch, wide, abcA, chBefore, chAfter );
+
+ finder.abc.abcA = abcA;
+ finder.abc.wide = wide;
+ m_ExtendedKernedABCWidthsCache.Insert( finder );
+}
+
+static UniCharCount WcharToUnichar( wchar_t ch, UniChar (&dest)[ 3 ] )
+{
+ UniCharCount runLength;
+
+ if ( ch <= 0xFFFF )
+ {
+ dest[ 0 ] = (UniChar)ch;
+ dest[ 1 ] = 0;
+ return 1;
+ }
+
+ ch -= 0x010000;
+ dest[ 0 ] = (UniChar)( ch >> 10 ) | 0xD800;
+ dest[ 1 ] = (UniChar)( ch & 0x3FF ) | 0xDC00;
+ dest[ 2 ] = 0;
+ return 2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: writes the char into the specified 32bpp texture
+//-----------------------------------------------------------------------------
+void COSXFont::GetCharRGBA( wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba )
+{
+ if ( !m_ContextRef )
+ {
+ Assert( !"Context ref not setup to allow GetCharRGBA" );
+ return;
+ }
+
+ UniChar buffer[ 3 ];
+ UniCharCount runLength = WcharToUnichar( ch, buffer );
+
+ OSStatus err = ATSUSetTextPointerLocation( m_ATSUTextLayout, buffer, kATSUFromTextBeginning, kATSUToTextEnd, runLength );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+ err = ATSUSetRunStyle( m_ATSUTextLayout, m_ATSUStyle, 0, runLength );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+ err = ATSUSetTransientFontMatching( m_ATSUTextLayout, true );
+ if ( err != noErr )
+ CHECK_ATSU_ERR( err );
+
+ CGRect rect = { { 0, 0 }, { m_iMaxCharWidth, m_iHeight } };
+ CGContextClearRect( m_ContextRef, rect );
+
+ CGContextFlush( m_ContextRef );
+
+ // You are not seeing a bug. We need the background to have a full
+ // alpha channel since it's going to bitblt in the overlay. But osx when it renders
+ // to a full alpha channel likes to 'antialias' the alpha as well as the color blending.
+ // Turning off antialiasing doesn't just make the characters jagged, but when a font
+ // is 1 pixel wide it ends up alpha blending into the background. By rendering it
+ // more than once we dominate this alpha. Looking at worst case, 3 was the magic number.
+ // Messing with the anti-aliasing settings on the bitmap, or the alpha config (including
+ // alpha-skip-last) caused no change, or complete disabling of antialiasing. Glad these
+ // are cached.
+ if ( m_iHeight < 20 )
+ {
+ err = ATSUDrawText( m_ATSUTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix( 0 ), Long2Fix( m_iHeight - m_iAscent ) );
+ CHECK_ATSU_ERR( err );
+ }
+
+ if ( ( m_iHeight < 16 ) || ( m_iFlags & vgui::ISurface::FONTFLAG_CUSTOM ) )
+ {
+ err = ATSUDrawText( m_ATSUTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix( 0 ), Long2Fix( m_iHeight - m_iAscent ) );
+ CHECK_ATSU_ERR( err );
+ }
+
+ err = ATSUDrawText( m_ATSUTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix( 0 ), Long2Fix( m_iHeight - m_iAscent ) );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+
+ char *pContextData = (char *)CGBitmapContextGetData( m_ContextRef );
+
+ unsigned char *pchPixelData = rgba;
+ for ( int y = 0; y < rgbaTall; y++ )
+ {
+ char *row = pContextData + y * m_iMaxCharWidth * 4;
+ Q_memcpy( pchPixelData, row, rgbaWide * 4 );
+ pchPixelData+= ( rgbaWide * 4 );
+ }
+
+ // apply requested effects in specified order
+ ApplyDropShadowToTexture( rgbaWide, rgbaTall, rgba, m_iDropShadowOffset );
+ ApplyOutlineToTexture( rgbaWide, rgbaTall, rgba, m_iOutlineSize );
+ ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, rgba, m_iBlur );
+ ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, rgba, m_iScanLines );
+ ApplyRotaryEffectToTexture( rgbaWide, rgbaTall, rgba, m_bRotary );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the abc widths for a character
+//-----------------------------------------------------------------------------
+void COSXFont::GetCharABCWidths( int ch, int &a, int &b, int &c )
+{
+ Assert( IsValid() );
+
+ // Look for it in the cache.
+ abc_cache_t finder = { (wchar_t)ch };
+
+ unsigned short i = m_ExtendedABCWidthsCache.Find( finder );
+ if ( m_ExtendedABCWidthsCache.IsValidIndex( i ) )
+ {
+ a = m_ExtendedABCWidthsCache[i].abc.a;
+ b = m_ExtendedABCWidthsCache[i].abc.b;
+ c = m_ExtendedABCWidthsCache[i].abc.c;
+ return;
+ }
+
+ UniChar buffer[ 3 ];
+ UniCharCount runLength = WcharToUnichar( ch, buffer );
+
+ OSStatus err = ATSUSetTextPointerLocation( m_ATSUTextLayout, buffer, kATSUFromTextBeginning, kATSUToTextEnd, runLength );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+ err = ATSUSetRunStyle( m_ATSUTextLayout, m_ATSUStyle, 0, runLength );
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+ err = ATSUSetTransientFontMatching( m_ATSUTextLayout, true );
+ if ( err != noErr )
+ CHECK_ATSU_ERR( err );
+
+ ItemCount glyphCount = 0;
+ ATSLayoutRecord *layoutRecords;
+ err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( m_ATSUTextLayout, 0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, ( void ** )&layoutRecords, &glyphCount );
+ if (err != noErr)
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+ ATSGlyphRef glyph = layoutRecords->glyphID;
+
+ ATSGlyphScreenMetrics gm;
+ err = ATSUGlyphGetScreenMetrics( m_ATSUStyle, 1, &glyph, 0, false, false, &gm );
+ ATSUDirectReleaseLayoutDataArrayPtr( NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, ( void ** )&layoutRecords );
+
+ if ( err != noErr )
+ {
+ // ATSUGlyphGetScreenMetrics fails when font matching happens. When this occurs,
+ // grab the full bounding box for the character and try to fake up some abc values.
+ ATSUTextMeasurement fTextBefore, fTextAfter, fAscent, fDescent;
+ err = ATSUGetUnjustifiedBounds( m_ATSUTextLayout,
+ kATSUFromTextBeginning,
+ kATSUToTextEnd,
+ &fTextBefore,
+ &fTextAfter,
+ &fAscent,
+ &fDescent);
+ gm.sideBearing.x = 0.2f;
+ gm.deviceAdvance.x = FixedToFloat( fTextAfter );
+ gm.otherSideBearing.x = 0.2f;
+ }
+
+ if ( err != noErr )
+ {
+ CHECK_ATSU_ERR( err );
+ return;
+ }
+
+ finder.abc.a = gm.sideBearing.x - (float)m_iBlur - m_iOutlineSize;
+ finder.abc.b = gm.deviceAdvance.x + ( ( (float)m_iBlur + m_iOutlineSize ) * 2 ) + m_iDropShadowOffset;
+ finder.abc.c = gm.otherSideBearing.x - (float)m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
+ // finder.abc.a = ceil( finder.abc.a );
+ // finder.abc.b = ceil( finder.abc.b );
+ // finder.abc.c = ceil( finder.abc.c );
+
+ if ( m_iFlags & vgui::ISurface::FONTFLAG_ITALIC )
+ {
+ finder.abc.b += 4.0f;
+ }
+
+ if ( finder.abc.a + finder.abc.b + finder.abc.c == 0 )
+ {
+ if ( finder.abc.b + finder.abc.c == 0 )
+ finder.abc.c = 0;
+ else if ( finder.abc.a + finder.abc.b == 0 )
+ finder.abc.a = 0;
+ else
+ finder.abc.a = finder.abc.c = 0;
+ }
+
+ a = finder.abc.a;
+ b = finder.abc.b;
+ c = finder.abc.c;
+
+ m_ExtendedABCWidthsCache.Insert( finder );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the font is equivalent to that specified
+//-----------------------------------------------------------------------------
+bool COSXFont::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
+{
+ if ( !Q_stricmp( windowsFontName, m_szName.String() ) &&
+ ( m_iTall == tall ) &&
+ ( m_iWeight == weight ) &&
+ ( m_iBlur == blur ) &&
+ ( m_iFlags == flags ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true only if this font is valid for use
+//-----------------------------------------------------------------------------
+bool COSXFont::IsValid()
+{
+ if ( !m_szName.IsEmpty() && m_szName.String()[ 0 ] )
+ return true;
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the height of the font, in pixels
+//-----------------------------------------------------------------------------
+int COSXFont::GetHeight()
+{
+ Assert( IsValid() );
+ return m_iHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the requested height of the font
+//-----------------------------------------------------------------------------
+int COSXFont::GetHeightRequested()
+{
+ Assert( IsValid() );
+ return m_iTall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
+//-----------------------------------------------------------------------------
+int COSXFont::GetAscent()
+{
+ Assert( IsValid() );
+ return m_iAscent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the maximum width of a character, in pixels
+//-----------------------------------------------------------------------------
+int COSXFont::GetMaxCharWidth()
+{
+ Assert( IsValid() );
+ return m_iMaxCharWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the flags used to make this font, used by the dynamic resizing code
+//-----------------------------------------------------------------------------
+int COSXFont::GetFlags()
+{
+ return m_iFlags;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Comparison function for abc widths storage
+//-----------------------------------------------------------------------------
+bool COSXFont::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs)
+{
+ return lhs.wch < rhs.wch;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Comparison function for abc widths storage
+//-----------------------------------------------------------------------------
+bool COSXFont::ExtendedKernedABCWidthsCacheLessFunc(const kerned_abc_cache_t &lhs, const kerned_abc_cache_t &rhs)
+{
+ return ( lhs.wch < rhs.wch ) ||
+ ( lhs.wch == rhs.wch && lhs.wchBefore < rhs.wchBefore ) ||
+ ( lhs.wch == rhs.wch && lhs.wchBefore == rhs.wchBefore && lhs.wchAfter < rhs.wchAfter );
+}
+
+
+ATSUStyle *COSXFont::SetAsActiveFont( CGContextRef cgContext )
+{
+ CGContextSelectFont ( cgContext, m_szName.String(), m_iHeight, kCGEncodingMacRoman );
+ return &m_ATSUStyle;
+}
+
+
+#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 COSXFont::Validate( CValidator &validator, char *pchName )
+{
+ validator.Push( "COSXFont", this, pchName );
+
+ m_ExtendedABCWidthsCache.Validate( validator, "m_ExtendedABCWidthsCache" );
+ m_ExtendedKernedABCWidthsCache.Validate( validator, "m_ExtendedKernedABCWidthsCache" );
+ validator.ClaimMemory( m_pGaussianDistribution );
+
+ validator.Pop();
+}
+
+#endif // DBGFLAG_VALIDATE
diff --git a/vgui2/vgui_surfacelib/vgui_surfacelib.vpc b/vgui2/vgui_surfacelib/vgui_surfacelib.vpc
new file mode 100644
index 0000000..bd413e4
--- /dev/null
+++ b/vgui2/vgui_surfacelib/vgui_surfacelib.vpc
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------------
+// VGUI_SURFACELIB.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$macro SRCDIR "..\.."
+
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+
+$Project "vgui_surfacelib"
+{
+ $Folder "Source Files"
+ {
+ $File "BitmapFont.cpp"
+ $File "FontAmalgam.cpp"
+ $File "FontManager.cpp"
+ $File "FontEffects.cpp"
+ $File "FontEffects.h"
+ $File "Win32Font.cpp" [$WIN32 && !$X360]
+ $File "Win32Font_x360.cpp" [$X360]
+ $File "osxfont.cpp" [$OSXALL]
+ $File "linuxfont.cpp" [$LINUXALL]
+ }
+
+ $Folder "Public Header Files"
+ {
+ $File "$SRCDIR\public\tier0\basetypes.h"
+ $File "$SRCDIR\common\vgui_surfacelib\BitmapFont.h"
+ $File "$SRCDIR\common\vgui_surfacelib\FontAmalgam.h"
+ $File "$SRCDIR\common\vgui_surfacelib\FontManager.h"
+ $File "$SRCDIR\public\appframework\IAppSystem.h"
+ $File "$SRCDIR\public\tier1\interface.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\tier1\utlbuffer.h"
+ $File "$SRCDIR\public\tier1\utlmemory.h"
+ $File "$SRCDIR\public\tier1\utlvector.h"
+ $File "$SRCDIR\public\mathlib\vector2d.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+ $File "$SRCDIR\common\vgui_surfacelib\vguifont.h"
+ $File "$SRCDIR\common\vgui_surfacelib\Win32Font.h" [$WIN32]
+ $File "$SRCDIR\common\vgui_surfacelib\osxfont.h" [$OSXALL]
+ }
+}