mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-07 17:29:36 +00:00
1
This commit is contained in:
@@ -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()
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user