diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vgui2/vgui_surfacelib | |
| download | archived-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.cpp | 324 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/FontAmalgam.cpp | 227 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/FontEffects.cpp | 241 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/FontEffects.h | 20 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/FontManager.cpp | 787 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/Win32Font.cpp | 638 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/Win32Font_x360.cpp | 346 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/linuxfont.cpp | 777 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/osxfont.cpp | 769 | ||||
| -rw-r--r-- | vgui2/vgui_surfacelib/vgui_surfacelib.vpc | 45 |
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] + } +} |