This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View File

@@ -0,0 +1,340 @@
//========= Copyright 1996-2005, 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>
#if !defined( _PS3 )
#include <malloc.h>
#endif // ! _PS3
#include "vgui_surfacelib/BitmapFont.h"
#include "vgui_surfacelib/fontmanager.h"
#include "tier0/dbg.h"
#include "vgui_surfacelib/ifontsurface.h"
#include "tier0/mem.h"
#include "utlbuffer.h"
#include "filesystem.h"
#include "materialsystem/itexture.h"
#include "rendersystem/irenderdevice.h"
#include "resourcesystem/stronghandle.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;
HRenderTextureStrong m_pTexture2;
};
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 ( IsGameConsole() )
{
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 );
if ( g_pMaterialSystem )
{
pFontTable->m_pTexture = FontManager().MaterialSystem()->FindTexture( textureName, TEXTURE_GROUP_VGUI );
#if defined( DEVELOPMENT_ONLY ) || defined( ALLOW_TEXT_MODE )
static bool s_bTextMode = CommandLine()->HasParm( "-textmode" );
#else
const bool s_bTextMode = false;
#endif
#if defined( _DEBUG ) && !defined( POSIX )
if ( ( pFontTable->m_pBitmapFont->m_PageWidth != pFontTable->m_pTexture->GetActualWidth() ||
pFontTable->m_pBitmapFont->m_PageHeight != pFontTable->m_pTexture->GetActualHeight() ) && !s_bTextMode )
{
// 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();
}
else
{
Assert(0); // TODO add support for materialsystem2
}
}
// setup font properties
m_scalex = scalex;
m_scaley = scaley;
// flags are derived from the baked font
m_iFlags = FONTFLAG_BITMAP;
int bitmapFlags = pFontTable->m_pBitmapFont->m_Flags;
if ( bitmapFlags & BF_ANTIALIASED )
{
m_iFlags |= FONTFLAG_ANTIALIAS;
}
if ( bitmapFlags & BF_ITALIC )
{
m_iFlags |= FONTFLAG_ITALIC;
}
if ( bitmapFlags & BF_BLURRED )
{
m_iFlags |= FONTFLAG_GAUSSIANBLUR;
m_iBlur = 1;
}
if ( bitmapFlags & BF_SCANLINES )
{
m_iScanLines = 1;
}
if ( bitmapFlags & BF_OUTLINED )
{
m_iFlags |= FONTFLAG_OUTLINE;
m_iOutlineSize = 1;
}
if ( bitmapFlags & BF_DROPSHADOW )
{
m_iFlags |= FONTFLAG_DROPSHADOW;
m_iDropShadowOffset = 1;
}
if ( flags & FONTFLAG_ADDITIVE )
{
m_bAdditive = true;
m_iFlags |= 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 & 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;
}
//-----------------------------------------------------------------------------
// Purpose: gets the abc widths for a character
//-----------------------------------------------------------------------------
void CBitmapFont::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA, float &abcC )
{
int a, b, c;
GetCharABCWidths( ch, a, b, c );
wide = a+b+c;
abcA = a;
abcC = c;
}
//-----------------------------------------------------------------------------
// 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()
{
if ( g_pMaterialSystem )
{
Assert( IsValid() );
return g_BitmapFontTable[m_bitmapFontHandle].m_pTexture;
}
else
{
return NULL;
}
}
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()

View File

@@ -0,0 +1,183 @@
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "vgui_surfacelib/FontAmalgam.h"
#include "vgui_surfacelib/ifontsurface.h"
#include "tier0/dbg.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CFontAmalgam::CFontAmalgam()
{
m_Fonts.EnsureCapacity( 4 );
m_iMaxHeight = 0;
m_iMaxWidth = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CFontAmalgam::~CFontAmalgam()
{
}
//-----------------------------------------------------------------------------
// Purpose: adds a font to the amalgam
//-----------------------------------------------------------------------------
void CFontAmalgam::AddFont(font_t *pFont, int lowRange, int highRange)
{
int i = m_Fonts.AddToTail();
m_Fonts[i].pWin32Font = pFont;
m_Fonts[i].lowRange = lowRange;
m_Fonts[i].highRange = highRange;
m_iMaxHeight = MAX(pFont->GetHeight(), m_iMaxHeight);
m_iMaxWidth = MAX(pFont->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 ( ch >= m_Fonts[i].lowRange && ch <= m_Fonts[i].highRange )
{
Assert( m_Fonts[i].pWin32Font->IsValid() );
return m_Fonts[i].pWin32Font;
}
}
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 ) & FONTFLAG_BITMAP )
{
reinterpret_cast< CBitmapFont* >( m_Fonts[0].pWin32Font )->SetScale( sx, sy );
}
else
{
Warning( "%s: Can't set font scale on a non-bitmap font!\n", m_Fonts[0].pWin32Font->GetName() );
}
}
//-----------------------------------------------------------------------------
// Purpose: returns the max height of the font set
//-----------------------------------------------------------------------------
int CFontAmalgam::GetFontHeight()
{
if (!m_Fonts.Count())
{
return m_iMaxHeight;
}
return m_Fonts[0].pWin32Font->GetHeight();
}
//-----------------------------------------------------------------------------
// 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.Count() && m_Fonts[i].pWin32Font && m_Fonts[i].pWin32Font->IsValid() )
{
return m_Fonts[i].pWin32Font->GetName();
}
return "";
}
//-----------------------------------------------------------------------------
// Purpose: returns the name of the font that is loaded
//-----------------------------------------------------------------------------
int CFontAmalgam::GetFlags(int i)
{
if ( m_Fonts.Count() && m_Fonts[i].pWin32Font )
{
return m_Fonts[i].pWin32Font->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].pWin32Font->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

View File

@@ -0,0 +1,213 @@
//========= Copyright 1996-2005, 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;
}
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Gets the blur value for a single pixel
//-----------------------------------------------------------------------------
FORCEINLINE void GetBlurValueForPixel(unsigned char *src, int blur, float *gaussianDistribution, int srcX, int srcY, int rgbaWide, int rgbaTall, unsigned char *dest)
{
float accum = 0.0f;
// scan the positive x direction
int maxX = MIN(srcX + blur, rgbaWide - 1);
int minX = MAX(srcX - blur, 0);
for (int x = minX; x <= maxX; x++)
{
int maxY = MIN(srcY + blur, rgbaTall - 1);
int minY = MAX(srcY - blur, 0);
for (int y = minY; y <= maxY; y++)
{
unsigned char *srcPos = src + ((x + (y * rgbaWide)) * 4);
// muliply by the value matrix
float weight = gaussianDistribution[x - srcX + blur];
float weight2 = gaussianDistribution[y - srcY + blur];
accum += (srcPos[0] * (weight * weight2));
}
}
dest[0] = dest[1] = dest[2] = 255; //leave ALL pixels white or we get black backgrounds mixed in
dest[3] = MIN( (int)accum, 255); //blur occurs entirely in the alpha
}
//-----------------------------------------------------------------------------
// Purpose: blurs the texture
//-----------------------------------------------------------------------------
void ApplyGaussianBlurToTexture( int rgbaWide, int rgbaTall, unsigned char *rgba, int iBlur )
{
float *pGaussianDistribution;
if ( !iBlur )
return;
// generate the gaussian field
pGaussianDistribution = (float*) stackalloc( (iBlur*2+1) * sizeof(float) );
double sigma = 0.683 * iBlur;
for (int x = 0; x <= (iBlur * 2); x++)
{
int val = x - iBlur;
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);
// incrementing destination pointer
unsigned char *dest = rgba;
for (int y = 0; y < rgbaTall; y++)
{
for (int x = 0; x < rgbaWide; x++)
{
// scan the source pixel
GetBlurValueForPixel(src, iBlur, pGaussianDistribution, x, y, rgbaWide, rgbaTall, dest);
// move to the next
dest += 4;
}
}
}

View File

@@ -0,0 +1,20 @@
//========= Copyright © 1996-2005, 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

View File

@@ -0,0 +1,583 @@
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=====================================================================================//
#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data
#define SUPPORT_CUSTOM_FONT_FORMAT
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#if !defined( _PS3 )
#include <malloc.h>
#endif // ! _PS3
#include "vgui_surfacelib/Win32Font.h"
#include "tier0/dbg.h"
#include "vgui_surfacelib/IFontSurface.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;
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 );
}
#ifndef SUPPORT_CUSTOM_FONT_FORMAT
//-----------------------------------------------------------------------------
// 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;
}
#endif // SUPPORT_CUSTOM_FONT_FORMAT
//-----------------------------------------------------------------------------
// 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 & FONTFLAG_ANTIALIAS) ? 1 : 0;
m_bUnderlined = (flags & FONTFLAG_UNDERLINE) ? 1 : 0;
m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
m_iBlur = blur;
m_iScanLines = scanlines;
m_bRotary = (flags & FONTFLAG_ROTARY) ? 1 : 0;
m_bAdditive = (flags & FONTFLAG_ADDITIVE) ? 1 : 0;
int charset = (flags & 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 );
#ifndef SUPPORT_CUSTOM_FONT_FORMAT
// Vitaliy: fonts registered using custom font format are
// not enumerated. Font creation will fail below for a font that
// cannot be instantiated.
{
// 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;
}
}
#endif
m_hFont = ::CreateFontA(tall, 0, 0, 0,
m_iWeight,
flags & FONTFLAG_ITALIC,
flags & FONTFLAG_UNDERLINE,
flags & 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);
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 & 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() );
// 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 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, float &abcC )
{
int a,b,c;
GetCharABCWidths(ch, a, b, c );
wide = ( a + b + c);
abcA = a;
abcC = c;
}

View File

@@ -0,0 +1,404 @@
//========= Copyright <20> 1996-2005, 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/vguifont.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 & FONTFLAG_ITALIC )
style |= XUI_FONT_STYLE_ITALIC;
if ( iFlags & FONTFLAG_UNDERLINE )
style |= XUI_FONT_STYLE_UNDERLINE;
if ( iWeight > 400 )
style |= XUI_FONT_STYLE_BOLD;
return style;
}
//-----------------------------------------------------------------------------
// 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_bAntiAliased = false;
m_bUnderlined = false;
m_iBlur = 0;
m_iScanLines = 0;
m_bRotary = false;
m_bAdditive = false;
m_rgiBitmapSize[0] = 0;
m_rgiBitmapSize[1] = 0;
s_bSupportsUnicode = true;
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 & FONTFLAG_ANTIALIAS) ? 1 : 0;
m_bUnderlined = (flags & FONTFLAG_UNDERLINE) ? 1 : 0;
m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
m_iBlur = blur;
m_iScanLines = scanlines;
m_bRotary = (flags & FONTFLAG_ROTARY) ? 1 : 0;
m_bAdditive = (flags & 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;
}
// get the predominant font metrics now [1-255], the extended set [256-65535] is on-demand
FontManager().MaterialSystem()->GetTrueTypeFontMetrics( m_hFont, 1, 255, &fontMetrics, &charMetrics[1] );
// getting the metrics is an expensive i/o operation, cache results
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 = FONTFLAG_DROPSHADOW |
FONTFLAG_OUTLINE |
FONTFLAG_ROTARY |
FONTFLAG_ITALIC |
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
{
// look for it in the extended 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 system
// getting the metrics is an expensive i/o operation
if ( !m_hFont )
{
// demand request for font metrics, re-open font
int style = GetStyleFromParameters( m_iFlags, m_iWeight );
m_hFont = FontManager().MaterialSystem()->OpenTrueTypeFont( GetName(), m_iTall, style );
}
if ( m_hFont )
{
XUIFontMetrics fontMetrics;
XUICharMetrics charMetrics;
FontManager().MaterialSystem()->GetTrueTypeFontMetrics( m_hFont, ch, ch, &fontMetrics, &charMetrics );
// Determine real a,b,c mapping from XUI Character Metrics
a = charMetrics.fMinX - 1; // Add one column of padding to make up for font rendering blurring into left column (and adjust in b)
b = charMetrics.fMaxX - charMetrics.fMinX + 1;
c = charMetrics.fAdvance - charMetrics.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.
a = a - m_iBlur - m_iOutlineSize;
b = b + ( ( m_iBlur + m_iOutlineSize ) * 2 ) + m_iDropShadowOffset;
c = c - m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
}
else
{
a = 0;
b = 0;
c = 0;
}
// add to the cache
finder.abc.a = a;
finder.abc.b = b;
finder.abc.c = c;
m_ExtendedABCWidthsCache.Insert( finder );
}
}
//-----------------------------------------------------------------------------
// 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: 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, float &abcC )
{
int a,b,c;
GetCharABCWidths(ch, a, b, c );
wide = ( a + b + c);
abcA = a;
abcC = c;
}

View File

@@ -0,0 +1,52 @@
// ----------------------------------------- //
// File generated by VPC //
// ----------------------------------------- //
Source file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\BitmapFont.cpp
Debug output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\BitmapFont.cpp
Release output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\BitmapFont.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Debug output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Release output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\FontAmalgam.cpp
Debug output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\FontAmalgam.cpp
Release output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\FontAmalgam.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\FontEffects.cpp
Debug output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\FontEffects.cpp
Release output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\FontEffects.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\fontmanager.cpp
Debug output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\fontmanager.cpp
Release output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\fontmanager.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\fonttexturecache.cpp
Debug output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\fonttexturecache.cpp
Release output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\fonttexturecache.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\texturedictionary.cpp
Debug output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\texturedictionary.cpp
Release output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\texturedictionary.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\Win32Font.cpp
Debug output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\Win32Font.cpp
Release output file: F:\csgo_64\cstrike15_src\vgui2\vgui_surfacelib\Win32Font.cpp
Containing unity file:
PCH file:

View File

@@ -0,0 +1,847 @@
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=====================================================================================//
#include <locale.h>
#include "vgui_surfacelib/BitmapFont.h"
#include "vgui_surfacelib/fontmanager.h"
#include "convar.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?" );
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));
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CFontManager::GetLanguage()
{
return m_szLanguage;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CFontManager::~CFontManager()
{
ClearAllFonts();
#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();
m_FontAmalgams.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 bForceSingleFont = false;
if ( IsX360() )
{
//-----//
// 360 //
//-----//
// AV - The 360 must use the same size font for 0-255 and 256-0xFFFF regardless of the font since the
// fontAmalgam can only deal with a consistent font height for all fonts in a single amalgam. We
// could change this if we forced all fonts within a single amalgam to have the same height with
// the font baselines aligned, but even then the fonts wouldn't look great, because different
// fonts set to the same size don't necessarily have the same height visually. We need to revisit
// this before shipping l4d2 on the PC!
bForceSingleFont = true;
// 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" ) || !V_stricmp( windowsFontName, "courier new" ) )
{
// toolbox stays as-is
// courier new is an internal debug font, not part of customer UI, need it stay as is
}
else
{
bool bUseFallback = false;
if ( !V_stricmp( m_szLanguage, "portuguese" ) ||
!V_stricmp( m_szLanguage, "polish" ) )
{
static ConVarRef mat_xbox_iswidescreen( "mat_xbox_iswidescreen" );
static ConVarRef mat_xbox_ishidef( "mat_xbox_ishidef" );
// we can support these languages with our desired fonts in hidef/widescreen modes only
// we must fallback to the more legible font in the lowdef or non-widescreen
bUseFallback = !( mat_xbox_iswidescreen.GetBool() && mat_xbox_ishidef.GetBool() );
}
if ( bUseFallback ||
!V_stricmp( m_szLanguage, "japanese" ) ||
!V_stricmp( m_szLanguage, "korean" ) ||
!V_stricmp( m_szLanguage, "schinese" ) ||
!V_stricmp( m_szLanguage, "tchinese" ) ||
!V_stricmp( m_szLanguage, "russian" ) )
{
// these languages must use the font that has their glyphs
// these language require a high degree of legibility
windowsFontName = GetForeignFallbackFontName();
}
}
}
else
{
//----//
// PC //
//----//
// AV - The PC has the same issues caused by multiple fonts in a single amalgam with different font
// heights...see comment above. Given the available languages in Steam, the languages below
// were illegible at 1024x768. Resolutions of 800x600 and 640x480 are a complete mess for all
// languages, including English. We probably need to fallback to Tahoma for all languages when
// the vertical resolution < 720. This will probably be the next check-in, but we need to evaluate
// this further tomorrow.
if ( !V_stricmp( windowsFontName, "toolbox" ) || !V_stricmp( windowsFontName, "courier new" ) )
{
// toolbox stays as-is
// courier new is an internal debug font, not part of customer UI, need it stay as is
}
else
{
// These languages are illegible @ vertical resolutions <= 768
if ( !V_stricmp( m_szLanguage, "korean" ) ||
!V_stricmp( m_szLanguage, "schinese" ) ||
!V_stricmp( m_szLanguage, "tchinese" ) ||
!V_stricmp( m_szLanguage, "russian" ) ||
!V_stricmp( m_szLanguage, "thai" ) ||
!V_stricmp( m_szLanguage, "japanese" ) ||
!V_stricmp( m_szLanguage, "czech" ) )
{
windowsFontName = GetForeignFallbackFontName();
bForceSingleFont = true;
}
}
}
// AV - If we actually want to support multiple fonts within an amalgam, we need a change here. Currently,
// the code will use winFont for 0-255 and pExtendedFont for 256-0xFFFF! But since the functions for
// getting the font height from the amalgam can only return one height, the heights of the two fonts
// need to be identical. This isn't trivial because even if we query both fonts for their height
// first, that won't force their baselines within the font pages to be aligned. So we would have to
// do something much more complicated where we loop over all characters in each font to find the
// absolute ascent and descent above/below the baseline and then when we create the font, align both
// fonts to the shared baseline with a shared height. But even with the font baselines aligned with a
// shared height, the fonts still wouldn't look great, because different fonts set to the same size
// don't necessarily have the same height visually. We need to revisit this before shipping l4d2 on the PC!
// And there are still issues with what I'm suggesting here because when we ask the font API what
// the maxHeight, maxAscent, maxDescent is, we get inconsistent results, so I'm not even sure we could
// successfully align the baseline of two fonts in the font pages.
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 ( bForceSingleFont || 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();
#ifdef LINUX
int memSize = 0;
void *pchFontData = pFontDataHelper( windowsFontName, memSize );
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];
}
else
{
// failed to create, remove
delete m_Win32Fonts[i];
m_Win32Fonts.Remove(i);
return NULL;
}
}
else
{
#endif
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;
}
#ifdef LINUX
}
#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 & 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 );
}
//-----------------------------------------------------------------------------
// 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)
{
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 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 ) & 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 ) & 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;
// AV - Calling GetFontTall() for an amalgam with multiple fonts will return
// the font height of the first font only! We should be doing something like:
//
// tall = FontManager().GetFontForChar( font, text[0] )->GetHeight();
//
// but that's a little hacky since we're only looking at the first character!
// Same goes for the calls to GetFontTall() in the for loop below
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')
{
// AV - See note above about calling this instead: tall += FontManager().GetFontForChar( font, text[0] )->GetHeight();
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;
};
const char *g_szValidAsianFonts[] = {
#ifdef WIN32
"Marlett",
#else
"Helvetica",
#endif
NULL };
// list of how fonts fallback
FallbackFont_t g_FallbackFonts[] =
{
{ "Times New Roman", "Courier New" },
{ "Courier New", "Courier" },
{ "Verdana", "Arial" },
{ "Trebuchet MS", "Arial" },
#ifdef WIN32
{ "Tahoma", NULL },
{ NULL, "Tahoma" }, // every other font falls back to this
#else
{ "Tahoma", "Helvetica" },
{ "Helvetica", NULL },
{ NULL, "Helvetica" } // every other font falls back to this
#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;
}
struct Win98ForeignFallbackFont_t
{
const char *language;
const char *fallbackFont;
};
// list of how fonts fallback
Win98ForeignFallbackFont_t g_Win98ForeignFallbackFonts[] =
{
{ "russian", "system" },
{ "japanese", "win98japanese" },
{ "thai", "system" },
#ifdef WIN32
{ NULL, "Tahoma" }, // every other font falls back to this
#else
{ NULL, "Helvetica" }, // every other font falls back to this
#endif
};
//-----------------------------------------------------------------------------
// Purpose: specialized fonts
//-----------------------------------------------------------------------------
const char *CFontManager::GetForeignFallbackFontName()
{
#ifdef WIN32
if ( s_bSupportsUnicode )
{
if ( IsX360() )
{
return "arial unicode ms";
}
// tahoma has all the necessary characters for asian/russian languages for winXP/2K+
return "Tahoma";
}
#endif
int i;
for (i = 0; g_Win98ForeignFallbackFonts[i].language != NULL; i++)
{
if (!stricmp(g_Win98ForeignFallbackFonts[i].language, m_szLanguage))
return g_Win98ForeignFallbackFonts[i].fallbackFont;
}
// the ultimate fallback
return g_Win98ForeignFallbackFonts[i].fallbackFont;
}
#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;
pFont->GetKernedCharWidth( ch, chBefore, chAfter, wide, flabcA, flabcC );
}
#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

View File

@@ -0,0 +1,585 @@
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#if !defined( _GAMECONSOLE ) && defined( _WIN32 )
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "vgui_surfacelib/fonttexturecache.h"
#include "tier1/keyvalues.h"
#include "materialsystem/itexture.h"
#include "materialsystem/imaterial.h"
#include "tier1/utlbuffer.h"
#include "fmtstr.h"
#include "vgui_surfacelib/texturedictionary.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define TEXTURE_PAGE_WIDTH 256
#define TEXTURE_PAGE_HEIGHT 256
ConVar vgui_show_glyph_miss( "vgui_show_glyph_miss", "0", FCVAR_DEVELOPMENTONLY );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CFontTextureCache::CFontTextureCache() : m_CharCache( 0, 256, CacheEntryLessFunc )
{
V_memset( m_CommonCharCache, 0, sizeof( m_CommonCharCache ) );
Clear();
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CFontTextureCache::~CFontTextureCache()
{
Clear();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFontTextureCache::SetPrefix( const char *pTexturePagePrefix )
{
m_TexturePagePrefix = pTexturePagePrefix;
}
//-----------------------------------------------------------------------------
// Purpose: Resets the cache
//-----------------------------------------------------------------------------
void CFontTextureCache::Clear()
{
// remove all existing data
m_CharCache.RemoveAll();
for ( int i = 0; i < m_PageList.Count(); ++i )
{
if ( m_PageList[i].pPackedFontTextureCache )
{
delete m_PageList[i].pPackedFontTextureCache;
}
}
m_PageList.RemoveAll();
m_CurrPage = -1;
m_FontPages.RemoveAll();
m_FontPages.SetLessFunc( DefLessFunc( FontHandle_t ) );
for ( int i = 0; i < ARRAYSIZE( m_CommonCharCache ); i++ )
{
delete m_CommonCharCache[i];
m_CommonCharCache[i] = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose: comparison function for cache entries
//-----------------------------------------------------------------------------
bool CFontTextureCache::CacheEntryLessFunc( CacheEntry_t const &lhs, CacheEntry_t const &rhs )
{
uint64 lhsLookupID = ( ((uint64)lhs.font) << 32 ) | ((uint64)lhs.wch);
uint64 rhsLookupID = ( ((uint64)rhs.font) << 32 ) | ((uint64)rhs.wch);
return lhsLookupID < rhsLookupID;
}
//-----------------------------------------------------------------------------
// Purpose: returns the texture info for the given char & font
//-----------------------------------------------------------------------------
bool CFontTextureCache::GetTextureForChar( FontHandle_t font, FontDrawType_t type, wchar_t wch, int *textureID, float **texCoords )
{
// Ask for just one character
return GetTextureForChars( font, type, &wch, textureID, texCoords, 1 );
}
//-----------------------------------------------------------------------------
// Purpose: returns the texture info for the given char & font
// This function copies in the texcoords out from the static into a preallocated passed in arg.
//-----------------------------------------------------------------------------
bool CFontTextureCache::GetTextureAndCoordsForChar( FontHandle_t font, FontDrawType_t type, wchar_t wch, int *textureID, float *texCoords )
{
// Ask for just one character
float *textureCoords = NULL;
bool bSuccess = GetTextureForChars( font, type, &wch, textureID, &textureCoords, 1 );
if ( textureCoords )
{
texCoords[0] = textureCoords[0];
texCoords[1] = textureCoords[1];
texCoords[2] = textureCoords[2];
texCoords[3] = textureCoords[3];
}
return bSuccess;
}
//-----------------------------------------------------------------------------
// Purpose: returns the texture info for the given chars & font
//-----------------------------------------------------------------------------
bool CFontTextureCache::GetTextureForChars( FontHandle_t hFont, FontDrawType_t type, wchar_t *wch, int *textureID, float **texCoords, int numChars )
{
Assert( wch && textureID && texCoords );
Assert( numChars >= 1 );
if ( type == FONT_DRAW_DEFAULT )
{
type = FontManager().IsFontAdditive( hFont ) ? FONT_DRAW_ADDITIVE : FONT_DRAW_NONADDITIVE;
}
int typePage = (int)type - 1;
typePage = clamp( typePage, 0, (int)FONT_DRAW_TYPE_COUNT - 1 );
if ( FontManager().IsBitmapFont( hFont ) )
{
const int MAX_BITMAP_CHARS = 256;
if ( numChars > MAX_BITMAP_CHARS )
{
// Increase MAX_BITMAP_CHARS
Assert( 0 );
return false;
}
for ( int i = 0; i < numChars; i++ )
{
static float sTexCoords[ 4*MAX_BITMAP_CHARS ];
CBitmapFont *pWinFont;
float left, top, right, bottom;
int index;
Page_t *pPage;
pWinFont = reinterpret_cast< CBitmapFont* >( FontManager().GetFontForChar( hFont, wch[i] ) );
if ( !pWinFont )
{
// bad font handle
return false;
}
// get the texture coords
pWinFont->GetCharCoords( wch[i], &left, &top, &right, &bottom );
sTexCoords[i*4 + 0] = left;
sTexCoords[i*4 + 1] = top;
sTexCoords[i*4 + 2] = right;
sTexCoords[i*4 + 3] = bottom;
// find font handle in our list of ready pages
index = m_FontPages.Find( hFont );
if ( index == m_FontPages.InvalidIndex() )
{
// not found, create the texture id and its materials
index = m_FontPages.Insert( hFont );
pPage = &m_FontPages.Element( index );
for (int type = 0; type < FONT_DRAW_TYPE_COUNT; ++type )
{
pPage->textureID[type] = TextureDictionary()->CreateTexture( false );
}
CreateFontMaterials( *pPage, pWinFont->GetTexturePage(), true );
}
texCoords[i] = &(sTexCoords[ i*4 ]);
textureID[i] = m_FontPages.Element( index ).textureID[typePage];
}
}
else
{
font_t *pWinFont = FontManager().GetFontForChar( hFont, wch[0] );
if ( !pWinFont )
{
return false;
}
struct newPageEntry_t
{
int page; // The font page a new character will go in
int drawX; // X location within the font page
int drawY; // Y location within the font page
};
// Determine how many characters need to have their texture generated
newChar_t *newChars = (newChar_t *)stackalloc( numChars*sizeof( newChar_t ) );
newPageEntry_t *newEntries = (newPageEntry_t *)stackalloc( numChars*sizeof( newPageEntry_t ) );
int numNewChars = 0;
int maxNewCharTexels = 0;
int totalNewCharTexels = 0;
for ( int i = 0; i < numChars; i++ )
{
wchar_t wideChar = wch[i];
int *pCachePage;
float *pCacheCoords;
// profiling dicatated that avoiding the naive font/char RB lookup was beneficial
// instead waste a little memory to get all the western language chars to be direct
if ( IsGameConsole() && wideChar < MAX_COMMON_CHARS && hFont < ARRAYSIZE( m_CommonCharCache ) )
{
// dominant amount of simple chars are instant direct lookup
CommonChar_t *pCommonChars = m_CommonCharCache[hFont];
if ( !pCommonChars )
{
// missing
if ( pWinFont != FontManager().GetFontForChar( hFont, wideChar ) )
{
// all characters in string must come out of the same font
return false;
}
// init and insert
pCommonChars = new CommonChar_t;
memset( pCommonChars, 0, sizeof( CommonChar_t ) );
m_CommonCharCache[hFont] = pCommonChars;
}
pCachePage = &pCommonChars->details[wideChar].page;
pCacheCoords = pCommonChars->details[wideChar].texCoords;
}
else
{
// for console only, either more fonts than expected (> 256 fonts!) or not a simple integer
// want to keep this a direct lookup and not a search (which defeats the perf gain)
AssertMsgOnce( !IsGameConsole() || hFont < ARRAYSIZE( m_CommonCharCache ), "CFontTextureCache: Unexpected hFont out-of-range\n" );
// extended chars are a costlier lookup
// page and char form a unique key to find in cache
CacheEntry_t cacheItem;
cacheItem.font = hFont;
cacheItem.wch = wideChar;
HCacheEntry cacheHandle = m_CharCache.Find( cacheItem );
if ( !m_CharCache.IsValidIndex( cacheHandle ) )
{
// missing
if ( pWinFont != FontManager().GetFontForChar( hFont, wideChar ) )
{
// all characters in string must come out of the same font
return false;
}
// init and insert
cacheItem.texCoords[0] = 0;
cacheItem.texCoords[1] = 0;
cacheItem.texCoords[2] = 0;
cacheItem.texCoords[3] = 0;
cacheHandle = m_CharCache.Insert( cacheItem );
Assert( m_CharCache.IsValidIndex( cacheHandle ) );
}
pCachePage = &m_CharCache[cacheHandle].page;
pCacheCoords = m_CharCache[cacheHandle].texCoords;
}
if ( pCacheCoords[2] == 0 && pCacheCoords[3] == 0 )
{
// invalid page, setup for page allocation
// get the char details
int a, b, c;
pWinFont->GetCharABCWidths( wideChar, a, b, c );
int fontWide = MAX( b, 1 );
int fontTall = MAX( pWinFont->GetHeight(), 1 );
if ( pWinFont->GetUnderlined() )
{
fontWide += ( a + c );
}
// Get a texture to render into
int page, drawX, drawY, twide, ttall;
if ( !AllocatePageForChar( fontWide, fontTall, page, drawX, drawY, twide, ttall ) )
{
return false;
}
// accumulate data to pass to GetCharsRGBA below
newEntries[numNewChars].page = page;
newEntries[numNewChars].drawX = drawX;
newEntries[numNewChars].drawY = drawY;
newChars[numNewChars].wch = wideChar;
newChars[numNewChars].fontWide = fontWide;
newChars[numNewChars].fontTall = fontTall;
newChars[numNewChars].offset = 4*totalNewCharTexels;
totalNewCharTexels += fontWide*fontTall;
maxNewCharTexels = MAX( maxNewCharTexels, fontWide*fontTall );
numNewChars++;
// the 0.5 texel offset is done in CMatSystemTexture::SetMaterial()
pCacheCoords[0] = (float)( (double)drawX / ((double)twide) );
pCacheCoords[1] = (float)( (double)drawY / ((double)ttall) );
pCacheCoords[2] = (float)( (double)(drawX + fontWide) / (double)twide );
pCacheCoords[3] = (float)( (double)(drawY + fontTall) / (double)ttall );
*pCachePage = page;
}
// give data to caller
textureID[i] = m_PageList[*pCachePage].textureID[typePage];
texCoords[i] = pCacheCoords;
}
// Generate texture data for all newly-encountered characters
if ( numNewChars > 0 )
{
if ( vgui_show_glyph_miss.GetBool() )
{
char *pMissString = (char *)stackalloc( numNewChars * sizeof( char ) );
char *pString = pMissString;
for ( int i = 0; i < numNewChars; i++ )
{
// build a string representative enough for debugging puproses
wchar_t wch = newChars[i].wch;
if ( V_isprint( wch ) )
{
*pString++ = (char)wch;
}
else
{
*pString++ = '?';
}
}
*pString = '\0';
const char *pMsg = CFmtStr( "Glyph Miss: FontHandle_t:0x%8.8x (%s), %s (0x%x)\n", (int)hFont, pWinFont->GetName(), pMissString, pMissString[0] );
if ( IsGameConsole() )
{
// valid on xbox, and really want this spew treated like console spew
Warning( "%s", pMsg );
}
else
{
// debugger output only, to prevent any reentrant glyph miss as a result of spewing
Plat_DebugString( pMsg );
}
}
if ( IsGameConsole() && numNewChars > 1 )
{
MEM_ALLOC_CREDIT();
// Use the 360 fast path that generates multiple characters at once
int newCharDataSize = totalNewCharTexels*4;
CUtlBuffer newCharData( 0, newCharDataSize, CUtlBuffer::READ_ONLY );
unsigned char *pRGBA = (unsigned char *)newCharData.Base();
#if defined( _X360 ) || defined( _PS3 )
pWinFont->GetCharsRGBA( newChars, numNewChars, pRGBA );
#endif
// Copy the data into our font pages
for ( int i = 0; i < numNewChars; i++ )
{
newChar_t &newChar = newChars[i];
newPageEntry_t &newEntry = newEntries[i];
// upload the new sub texture
// NOTE: both textureIDs reference the same ITexture, so we're ok
unsigned char *characterRGBA = pRGBA + newChar.offset;
TextureDictionary()->SetSubTextureRGBA( m_PageList[newEntry.page].textureID[typePage], newEntry.drawX, newEntry.drawY, characterRGBA, newChar.fontWide, newChar.fontTall );
}
}
else
{
// create a buffer for new characters to be rendered into
int nByteCount = maxNewCharTexels * 4;
unsigned char *pRGBA = (unsigned char *)stackalloc( nByteCount * sizeof( unsigned char ) );
// Generate characters individually
for ( int i = 0; i < numNewChars; i++ )
{
newChar_t &newChar = newChars[i];
newPageEntry_t &newEntry = newEntries[i];
// render the character into the buffer
Q_memset( pRGBA, 0, nByteCount );
pWinFont->GetCharRGBA( newChar.wch, newChar.fontWide, newChar.fontTall, pRGBA );
// Make the char white if we are in source 2
if ( !g_pMaterialSystem )
{
for ( int i = 0; i < nByteCount; i += 4 )
{
pRGBA[i+0] = pRGBA[i+1] = pRGBA[i+2] = 255;
}
}
// upload the new sub texture
// NOTE: both textureIDs reference the same ITexture, so we're ok
TextureDictionary()->SetSubTextureRGBA( m_PageList[newEntry.page].textureID[typePage], newEntry.drawX, newEntry.drawY, pRGBA, newChar.fontWide, newChar.fontTall );
}
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Creates font materials
//-----------------------------------------------------------------------------
void CFontTextureCache::CreateFontMaterials( Page_t &page, ITexture *pFontTexture, bool bitmapFont )
{
// The normal material
KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
pVMTKeyValues->SetInt( "$vertexcolor", 1 );
pVMTKeyValues->SetInt( "$vertexalpha", 1 );
pVMTKeyValues->SetInt( "$ignorez", 1 );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$translucent", 1 );
pVMTKeyValues->SetString( "$basetexture", pFontTexture->GetName() );
CUtlString materialName = m_TexturePagePrefix + "__fontpage";
Assert( g_pMaterialSystem );
IMaterial *pMaterial = g_pMaterialSystem->CreateMaterial( materialName, pVMTKeyValues );
pMaterial->Refresh();
int typePageNonAdditive = (int)FONT_DRAW_NONADDITIVE-1;
TextureDictionary()->BindTextureToMaterial( page.textureID[typePageNonAdditive], pMaterial );
pMaterial->DecrementReferenceCount();
// The additive material
pVMTKeyValues = new KeyValues( "UnlitGeneric" );
pVMTKeyValues->SetInt( "$vertexcolor", 1 );
pVMTKeyValues->SetInt( "$vertexalpha", 1 );
pVMTKeyValues->SetInt( "$ignorez", 1 );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$translucent", 1 );
pVMTKeyValues->SetInt( "$additive", 1 );
pVMTKeyValues->SetString( "$basetexture", pFontTexture->GetName() );
CUtlString addmaterialName = m_TexturePagePrefix + "__fontpage_additive";
pMaterial = g_pMaterialSystem->CreateMaterial( addmaterialName.String(), pVMTKeyValues );
pMaterial->Refresh();
int typePageAdditive = (int)FONT_DRAW_ADDITIVE-1;
if ( bitmapFont )
{
TextureDictionary()->BindTextureToMaterial( page.textureID[typePageAdditive], pMaterial );
}
else
{
TextureDictionary()->BindTextureToMaterialReference( page.textureID[typePageAdditive], page.textureID[typePageNonAdditive], pMaterial);
}
pMaterial->DecrementReferenceCount();
}
//-----------------------------------------------------------------------------
// Purpose: allocates a new page for a given character
//-----------------------------------------------------------------------------
bool CFontTextureCache::AllocatePageForChar( int charWide, int charTall, int &pageIndex, int &drawX, int &drawY, int &twide, int &ttall )
{
// Catch the case where the glyph is too tall for the page
if ( charTall > TEXTURE_PAGE_HEIGHT )
return false;
// See if there is room in the last page for this character
pageIndex = m_CurrPage;
bool bNeedsNewPage = true;
int nodeIndex = -1;
Rect_t glpyhRect;
glpyhRect.x = 0;
glpyhRect.y = 0;
glpyhRect.width = charWide;
glpyhRect.height = charTall;
if ( pageIndex > -1 )
{
// Let's use r/b tree to find a good spot.
nodeIndex = m_PageList[pageIndex].pPackedFontTextureCache->InsertRect( glpyhRect );
bNeedsNewPage = ( nodeIndex == -1 );
}
if ( bNeedsNewPage )
{
// allocate a new page
pageIndex = m_PageList.AddToTail();
Page_t &newPage = m_PageList[pageIndex];
m_CurrPage = pageIndex;
for (int i = 0; i < FONT_DRAW_TYPE_COUNT; ++i )
{
newPage.textureID[i] = TextureDictionary()->CreateTexture( true );
}
newPage.pPackedFontTextureCache = new CTexturePacker( TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT, ( IsGameConsole() ? 0 : 1 ) );
nodeIndex = newPage.pPackedFontTextureCache->InsertRect( glpyhRect );
Assert( nodeIndex != -1 );
static int nFontPageId = 0;
char pTextureName[64];
Q_snprintf( pTextureName, 64, "%s__font_page_%d", m_TexturePagePrefix.String(), nFontPageId );
++nFontPageId;
MEM_ALLOC_CREDIT();
if ( g_pMaterialSystem )
{
ITexture *pTexture = AllocateNewPage( pTextureName );
CreateFontMaterials( newPage, pTexture );
pTexture->DecrementReferenceCount();
}
if ( IsPC() || !IsDebug() )
{
// clear the texture from the inital checkerboard to black
// allocate for 32bpp format
int nByteCount = TEXTURE_PAGE_WIDTH * TEXTURE_PAGE_HEIGHT * 4;
CUtlMemory<unsigned char> mRGBA;
mRGBA.EnsureCapacity( nByteCount );
//Q_memset( mRGBA.Base(), 0, nByteCount );
// Clear to white, full alpha.
for ( int i = 0; i < nByteCount; i += 4 )
{
mRGBA[i+0] = mRGBA[i+1] = mRGBA[i+2] = 255;
mRGBA[i+3] = 0;
}
int typePageNonAdditive = (int)(FONT_DRAW_NONADDITIVE)-1;
TextureDictionary()->SetTextureRGBAEx( newPage.textureID[typePageNonAdditive], ( const char* )mRGBA.Base(),
TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT, IMAGE_FORMAT_RGBA8888, k_ETextureScalingPointSample );
// Note, in rendersystem2 we do not have materials, as we actually have 2 diff textures.
if ( !g_pMaterialSystem )
{
int typePageAdditive = (int)(FONT_DRAW_ADDITIVE)-1;
newPage.textureID[typePageAdditive] = newPage.textureID[typePageNonAdditive];
}
}
}
// output the position
Page_t &page = m_PageList[ pageIndex ];
Assert( nodeIndex != -1 );
const CTexturePacker::TreeEntry_t &newEntry = page.pPackedFontTextureCache->GetEntry( nodeIndex );
drawX = newEntry.rc.x;
drawY = newEntry.rc.y;
twide = TEXTURE_PAGE_WIDTH;
ttall = TEXTURE_PAGE_HEIGHT;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: allocates a new page
//-----------------------------------------------------------------------------
ITexture *CFontTextureCache::AllocateNewPage( char *pTextureName )
{
Assert( g_pMaterialSystem );
ITexture *pTexture = g_pMaterialSystem->CreateProceduralTexture(
pTextureName,
TEXTURE_GROUP_VGUI,
TEXTURE_PAGE_WIDTH,
TEXTURE_PAGE_HEIGHT,
IMAGE_FORMAT_RGBA8888,
TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT |
TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY );
return pTexture;
}

View File

@@ -0,0 +1,624 @@
//========= Copyright 1996-2005, 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 "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"
namespace {
//Due to different font rendering approaches on different platforms, we have
//to apply custom tweaks to fonts on Linux to make them render as desired.
struct MetricsTweaks_t
{
const char *m_windowsFontName;
int m_tallAdjust;
};
MetricsTweaks_t GetFontMetricsTweaks(const char* windowsFontName)
{
static const MetricsTweaks_t FontMetricTweaks[] =
{
{ "Stubble bold", -5 },
};
for( int i = 0; i != Q_ARRAYSIZE( FontMetricTweaks ); ++i )
{
if ( !Q_stricmp( windowsFontName, FontMetricTweaks[i].m_windowsFontName ) )
{
return FontMetricTweaks[i];
}
}
static const MetricsTweaks_t DefaultMetricTweaks = { NULL, 0 };
return DefaultMetricTweaks;
}
// 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_iTall = 0;
m_iWeight = 0;
m_iFlags = 0;
m_iMaxCharWidth = 0;
m_bAntiAliased = false;
m_bUnderlined = false;
m_iBlur = 0;
m_pGaussianDistribution = NULL;
m_iScanLines = 0;
m_bRotary = false;
m_bAdditive = false;
if ( !ms_bSetFriendlyNameCacheLessFunc )
{
ms_bSetFriendlyNameCacheLessFunc = true;
SetDefLessFunc( m_FriendlyNameCache );
}
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CLinuxFont::~CLinuxFont()
{
}
//-----------------------------------------------------------------------------
// 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();
FcConfigAppFontAddDir(config, "platform/vgui/fonts");
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 size, 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 & FONTFLAG_ANTIALIAS;
m_bUnderlined = flags & FONTFLAG_UNDERLINE;
m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
m_iBlur = blur;
m_iScanLines = scanlines;
m_bRotary = flags & FONTFLAG_ROTARY;
m_bAdditive = flags & FONTFLAG_ADDITIVE;
FT_Error error = FT_New_Memory_Face( FontManager().GetFontLibraryHandle(), (FT_Byte *)data, size, 0, &face );
if ( error == FT_Err_Unknown_File_Format )
{
return false;
}
else if ( error )
{
return false;
}
InitMetrics();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: creates the font from windows. returns false if font does not exist in the OS.
//-----------------------------------------------------------------------------
bool CLinuxFont::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 & FONTFLAG_ANTIALIAS;
m_bUnderlined = flags & FONTFLAG_UNDERLINE;
m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
m_iBlur = blur;
m_iScanLines = scanlines;
m_bRotary = flags & FONTFLAG_ROTARY;
m_bAdditive = flags & FONTFLAG_ADDITIVE;
CreateFontList();
const char *pchFontName = windowsFontName;
if ( !Q_stricmp( pchFontName, "Tahoma" ) )
pchFontName = "Bitstream Vera Sans";
const int italic = flags & FONTFLAG_ITALIC ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, pchFontName,
FC_WEIGHT, FcTypeInteger, FC_WEIGHT_NORMAL,
FC_SLANT, FcTypeInteger, italic,
NULL);
if (!match)
{
AssertMsg1( false, "Unable to find font named %s\n", windowsFontName );
m_szName = "";
return false;
}
else
{
FcChar8* filename;
if ( FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch )
{
AssertMsg1( false, "Unable to find font named %s\n", windowsFontName );
m_szName = "";
FcPatternDestroy(match);
return false;
}
FT_Error error = FT_New_Face( FontManager().GetFontLibraryHandle(), (const char *)filename, 0, &face );
// Only destroy the pattern at this point so that "filename" is pointing
// to valid memory
FcPatternDestroy(match);
if ( error == FT_Err_Unknown_File_Format )
{
return false;
}
else if ( error )
{
return false;
}
if ( face->charmap == nullptr )
{
FT_Error error = FT_Select_Charmap( face, FT_ENCODING_APPLE_ROMAN );
if ( error )
{
FT_Done_Face( face );
face = NULL;
Msg( "Font %s has no valid charmap\n", windowsFontName );
return false;
}
}
}
InitMetrics();
return true;
}
void CLinuxFont::InitMetrics()
{
const MetricsTweaks_t metricTweaks = GetFontMetricsTweaks( m_szName );
FT_Set_Pixel_Sizes( face, 0, m_iTall + metricTweaks.m_tallAdjust );
m_iAscent = FIXED6_2INT( face->size->metrics.ascender );
m_iMaxCharWidth = FIXED6_2INT( face->size->metrics.max_advance );
const int fxpHeight = face->size->metrics.height + INT_2FIXED6( m_iDropShadowOffset + 2 * m_iOutlineSize );
m_iHeight = FIXED6_2INT( fxpHeight );
// 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));
// brightening factor
m_pGaussianDistribution[x] *= 1;
}
}
}
//-----------------------------------------------------------------------------
// 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 & FONTFLAG_CUSTOM) )
{
bShouldAntialias = false;
}
FT_Error error = FT_Load_Char( face,ch, FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL);
if ( error )
return;
int glyph_index = FT_Get_Char_Index( face, ch );
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER );
if ( error )
{
fprintf( stderr, "Error in FL_Load_Glyph: %x\n", error );
return;
}
FT_GlyphSlot slot = face->glyph;
uint32 nSkipRows = ( m_iAscent - slot->bitmap_top );
if ( nSkipRows )
nSkipRows--;
if ( nSkipRows > rgbaTall )
return;
unsigned char *rgba = prgba + ( nSkipRows * rgbaWide * 4 );
FT_Bitmap bitmap = face->glyph->bitmap;
Assert( bitmap.rows <= rgbaTall );
Assert( rgbaWide >= bitmap.width + m_iBlur );
if ( bitmap.width == 0 )
return;
/* now draw to our target surface */
for ( int y = 0; y < MIN( bitmap.rows, rgbaTall ); y++ )
{
for ( int x = 0; x < bitmap.width; x++ )
{
int rgbaOffset = 4*(x + m_iBlur); // +(rgbaTall-y-1)*rgbaWide*4
rgba[ rgbaOffset] = 255;
rgba[ rgbaOffset+1] = 255;
rgba[ rgbaOffset+2] = 255;
rgba[ rgbaOffset+3] = bitmap.buffer[ x + y*bitmap.width ];
}
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 );
}
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( face );
previous = chBefore;
/* convert character code to glyph index */
glyph_index = FT_Get_Char_Index( face, ch );
/* retrieve kerning distance and move pen position */
if ( use_kerning && previous && glyph_index )
{
FT_Vector delta;
FT_Get_Kerning( 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( face, glyph_index, FT_LOAD_DEFAULT );
if ( error )
{
fprintf( stderr, "Error in FL_Load_Glyph: %x\n", error );
}
FT_GlyphSlot slot = 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( face,ch, 0 );
if ( error )
return;
FT_Glyph_Metrics metrics = 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 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;
}
#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.ClaimMemory( m_pGaussianDistribution );
validator.Pop();
}
#endif // DBGFLAG_VALIDATE

View File

@@ -0,0 +1,533 @@
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <assert.h>
#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 <vstdlib/vstrtools.h>
#include "filesystem.h"
#include "vgui_surfacelib/osxfont.h"
#include "FontEffects.h"
#define DEBUG_FONT_CREATE 0
struct MetricsTweaks_t
{
const char *m_windowsFontName;
int m_sizeAdjust;
float m_ascentMultiplier;
float m_descentMultiplier;
float m_leadingMultiplier;
};
static const MetricsTweaks_t g_defaultMetricTweaks = { NULL, 0, 1.0, 1.0, 1.0 };// -2, 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, 1.1, 1.1 },
{ "TF2 Build", -2, 1.0, 1.0, 1.0 },
{ "UniversLTStd-BoldCn", 0, 1.4, 1.0, 0.8 },
{ "UniversLTStd-Cn", 0, 1.2, 1.0, 1.0 },
//{ "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"
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
COSXFont::COSXFont() : m_ExtendedABCWidthsCache(256, 0, &ExtendedABCWidthsCacheLessFunc),
m_ExtendedKernedABCWidthsCache( 256, 0, &ExtendedKernedABCWidthsCacheLessFunc )
{
m_iTall = 0;
m_iAscent = 0;
m_iDescent = 0;
m_iWeight = 0;
m_iFlags = 0;
m_iMaxCharWidth = 0;
m_bAntiAliased = false;
m_bUnderlined = false;
m_iBlur = 0;
m_pGaussianDistribution = NULL;
m_iScanLines = 0;
m_bRotary = false;
m_bAdditive = false;
m_ContextRef = 0;
m_pContextMemory = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
COSXFont::~COSXFont()
{
if ( m_ContextRef )
{
CGContextRelease( m_ContextRef );
}
if ( m_pContextMemory )
delete [] m_pContextMemory;
}
//-----------------------------------------------------------------------------
// 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_szName = windowsFontName;
m_iTall = tall;
m_iWeight = weight;
m_iFlags = flags;
m_bAntiAliased = flags & FONTFLAG_ANTIALIAS;
#if 0
// the font used in portal2 looks ok (better, in fact) anti-aliased when small,
if ( tall < 20 )
{
m_bAntiAliased = false;
}
#endif
m_bUnderlined = flags & FONTFLAG_UNDERLINE;
m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
m_iBlur = blur;
m_iScanLines = scanlines;
m_bRotary = flags & FONTFLAG_ROTARY;
m_bAdditive = flags & FONTFLAG_ADDITIVE;
char sCustomPath[1024];
Q_snprintf( sCustomPath, sizeof( sCustomPath ), "./platform/vgui/fonts/%s.ttf", windowsFontName );
if ( g_pFullFileSystem->FileExists( sCustomPath ) )
{
CFStringRef path = CFStringCreateWithCString( NULL, windowsFontName, kCFStringEncodingUTF8 );
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false);
CGDataProviderRef dataProvider = CGDataProviderCreateWithURL( url );
CGFontRef cgFont = CGFontCreateWithDataProvider( dataProvider );
m_hFont = CTFontCreateWithGraphicsFont( cgFont, tall, nullptr, nullptr );
CFRelease( cgFont );
CFRelease( dataProvider );
CFRelease( url );
CFRelease( path );
CTFontCopyCharacterSet(m_hFont);
}
else
{
const void *pKeys[2];
const void *pValues[2];
float fCTWeight = ( (float)( weight - 400 ) / 500.0f );
pKeys[0] = kCTFontWeightTrait;
pValues[0] = CFNumberCreate( NULL, kCFNumberFloatType, &fCTWeight );
float fCTSlant = ( flags & FONTFLAG_ITALIC ) != 0 ? 1.0f : 0.0f;
pKeys[1] = kCTFontSlantTrait;
pValues[1] = CFNumberCreate( NULL, kCFNumberFloatType, &fCTSlant );
CFDictionaryRef pTraitsDict = CFDictionaryCreate( NULL, pKeys, pValues, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
if ( !pTraitsDict )
{
goto Fail;
}
CFRelease( (CFNumberRef)pValues[0] );
CFRelease( (CFNumberRef)pValues[1] );
pKeys[0] = kCTFontNameAttribute;
pValues[0] = CFStringCreateWithCString( NULL, windowsFontName, kCFStringEncodingUTF8 );
pKeys[1] = kCTFontTraitsAttribute;
pValues[1] = pTraitsDict;
CFDictionaryRef pDescDict;
pDescDict = CFDictionaryCreate( NULL, pKeys, pValues, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
CFRelease( (CFStringRef)pValues[0] );
CFRelease( pTraitsDict );
if ( !pDescDict )
{
goto Fail;
}
CTFontDescriptorRef pFontDesc;
pFontDesc = CTFontDescriptorCreateWithAttributes( pDescDict );
CFRelease( pDescDict );
if ( !pFontDesc )
{
goto Fail;
}
// Fudge the size of the font to something reasonable.
m_hFont = CTFontCreateWithFontDescriptor( pFontDesc, int(tall*0.85), NULL );
CFRelease( pFontDesc );
}
if ( !m_hFont )
{
goto Fail;
}
CGRect bbox;
bbox = CTFontGetBoundingBox( m_hFont );
m_iAscent = ceil( CTFontGetAscent( m_hFont ) );
// The bounding box height seems to be overly large so use
// ascent plus descent.
m_iHeight = m_iAscent + ceil( CTFontGetDescent( m_hFont ) ) + m_iDropShadowOffset + 2 * m_iOutlineSize;
m_iMaxCharWidth = ceil( bbox.size.width ) + 2 * m_iOutlineSize;
uint bytesPerRow;
bytesPerRow = m_iMaxCharWidth * 4;
m_pContextMemory = new char[ (int)bytesPerRow * m_iHeight ];
memset( m_pContextMemory, 0x0, (int)( bytesPerRow * m_iHeight) );
CGColorSpaceRef colorSpace;
colorSpace = CGColorSpaceCreateDeviceRGB();
m_ContextRef = CGBitmapContextCreate( m_pContextMemory, m_iMaxCharWidth, m_iHeight,
8,
bytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast );
CGColorSpaceRelease( colorSpace );
if ( !m_ContextRef )
{
goto Fail;
}
CGContextSetAllowsAntialiasing( m_ContextRef, m_bAntiAliased );
CGContextSetShouldAntialias( m_ContextRef, m_bAntiAliased );
CGContextSetTextDrawingMode( m_ContextRef, kCGTextFill );
CGContextSetRGBStrokeColor( m_ContextRef, 1.0, 1.0, 1.0, 1.0 );
CGContextSetLineWidth( m_ContextRef, 1 );
return true;
Fail:
return false;
}
void COSXFont::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA, float &abcC )
{
int a,b,c;
GetCharABCWidths(ch, a, b, c );
wide = ( a + b + c );
abcA = a;
abcC = c;
}
static bool GetGlyphsForCharacter( CTFontRef hFont, wchar_t ch, CGGlyph* pGlyphs )
{
UniChar pUniChars[2];
pUniChars[0] = ch;
pUniChars[1] = 0;
if ( !CTFontGetGlyphsForCharacters( hFont, pUniChars, pGlyphs, 1 ) )
{
char str[2];
str[0] = (char)ch;
str[1] = 0;
CFStringRef s = CFStringCreateWithCString(nullptr, str, kTextEncodingUnicodeDefault);
pGlyphs[0] = CTFontGetGlyphWithName(hFont, s);
CFRelease( s );
if ( !pGlyphs[0] )
{
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: writes the char into the specified 32bpp texture
//-----------------------------------------------------------------------------
void COSXFont::GetCharRGBA( wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba )
{
wchar_t pWchars[1];
pWchars[0] = (wchar_t)ch;
CGGlyph pGlyphs[1];
if ( !GetGlyphsForCharacter( m_hFont, ch, pGlyphs ) )
{
AssertMsg( false, "CTFontGetGlyphsForCharacters failed" );
return;
}
CGRect rect = { { 0, 0 }, { m_iMaxCharWidth, m_iHeight } };
CGContextClearRect( m_ContextRef, rect );
CGRect pBounds[1];
CTFontGetBoundingRectsForGlyphs( m_hFont, kCTFontDefaultOrientation, pGlyphs, pBounds, 1 );
CGPoint pPositions[1];
// The character will be drawn offset by the 'A' distance so adjust
// it back as this routine only wants the core bits.
pPositions[0].x = m_iOutlineSize;
// The DrawGlyphs coordinate system puts zero Y at the bottom of
// the bitmap and puts the text baseline at zero Y so push
// it up to place characters where we expect them.
pPositions[0].y = ( m_iHeight - m_iAscent ) - m_iOutlineSize;
CTFontDrawGlyphs( m_hFont, pGlyphs, pPositions, 1, m_ContextRef );
CGContextFlush( m_ContextRef );
char *pContextData = (char *)CGBitmapContextGetData( m_ContextRef );
uint8 *pchPixelData = rgba;
for ( int y = 0; y < rgbaTall; y++ )
{
char *row = pContextData + y * m_iMaxCharWidth * 4;
for ( int x = 0; x < rgbaWide; x++ )
{
if ( row[0] || row[1] || row[2] || row[3] )
{
pchPixelData[0] = 0xff;
pchPixelData[1] = 0xff;
pchPixelData[2] = 0xff;
pchPixelData[3] = row[3];
}
else
{
pchPixelData[0] = 0;
pchPixelData[1] = 0;
pchPixelData[2] = 0;
pchPixelData[3] = 0;
}
row += 4;
pchPixelData += 4;
}
}
// Draw top and bottom bars for character placement debugging.
#if FORCE_CHAR_BOX_BOUNDS
pchPixelData = rgba;
for ( int x = 0; x < rgbaWide; x++ )
{
pchPixelData[0] = 0;
pchPixelData[1] = 0;
pchPixelData[2] = 0;
pchPixelData[3] = 0xff;
pchPixelData += 4;
}
pchPixelData = rgba + ( rgbaTall - 1 ) * rgbaWide * 4;
for ( int x = 0; x < rgbaWide; x++ )
{
pchPixelData[0] = 0;
pchPixelData[1] = 0;
pchPixelData[2] = 0;
pchPixelData[3] = 0xff;
pchPixelData += 4;
}
#endif
// 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 };
uint16 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 = 0;
b = 0;
c = 0;
wchar_t pWchars[1];
pWchars[0] = (wchar_t)ch;
CGGlyph pGlyphs[1];
if ( !GetGlyphsForCharacter( m_hFont, ch, pGlyphs ) )
{
AssertMsg( false, "CTFontGetGlyphsForCharacters failed" );
return;
}
CGSize pAdvances[1];
CTFontGetAdvancesForGlyphs( m_hFont, kCTFontDefaultOrientation, pGlyphs, pAdvances, 1 );
CGRect pBounds[1];
CTFontGetBoundingRectsForGlyphs( m_hFont, kCTFontDefaultOrientation, pGlyphs, pBounds, 1 );
a = 0;
b = ceil(pAdvances->width);
c = 0;
finder.abc.a = a;
finder.abc.b = b;
finder.abc.c = 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 ascent of the font, in pixels (ascent=units above the base line)
//-----------------------------------------------------------------------------
int COSXFont::GetAscent()
{
assert(IsValid());
return m_iAscent;
}
//-----------------------------------------------------------------------------
// Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
//-----------------------------------------------------------------------------
int COSXFont::GetDescent()
{
assert(IsValid());
return m_iDescent;
}
//-----------------------------------------------------------------------------
// 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 );
}
void *COSXFont::SetAsActiveFont( CGContextRef cgContext )
{
CGContextSelectFont ( cgContext, m_szName.String(), m_iHeight, kCGEncodingMacRoman);
return NULL;
}
#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

View File

@@ -0,0 +1,422 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: PS3 support for TrueType Fonts as hastily bastardized from Xbox 360 code.
// On 360, the only 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.
// On PS3 that is not a problem, but rather than reimplement this whole class,
// the minimal-code-change approach is just to patch some functions in shaderapi,
// even though we don't need to do the work in shaderapi. I hang my head in shame
// for this unworthy laziness/haste.
//
//=====================================================================================//
#include <stdio.h>
#include <string.h>
#include <math.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/vguifont.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 = CPS3Font::kFONT_STYLE_NORMAL;
if ( iFlags & FONTFLAG_ITALIC )
style |= CPS3Font::kFONT_STYLE_ITALIC;
if ( iFlags & FONTFLAG_UNDERLINE )
style |= CPS3Font::kFONT_STYLE_UNDERLINE;
if ( iWeight > 400 )
style |= CPS3Font::kFONT_STYLE_BOLD;
return style;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CPS3Font::CPS3Font() : 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_bAntiAliased = false;
m_bUnderlined = false;
m_iBlur = 0;
m_iScanLines = 0;
m_bRotary = false;
m_bAdditive = false;
m_rgiBitmapSize[0] = 0;
m_rgiBitmapSize[1] = 0;
s_bSupportsUnicode = true;
Q_memset( m_ABCWidthsCache, 0, sizeof( m_ABCWidthsCache ) );
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CPS3Font::~CPS3Font()
{
if ( m_hFont != NULL )
{
// 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
if ( IMaterialSystem *pMS = FontManager().MaterialSystem() )
pMS->CloseTrueTypeFont( m_hFont );
}
m_hFont = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Creates the font.
//-----------------------------------------------------------------------------
bool CPS3Font::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 & FONTFLAG_ANTIALIAS) ? 1 : 0;
m_bUnderlined = (flags & FONTFLAG_UNDERLINE) ? 1 : 0;
m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
m_iBlur = blur;
m_iScanLines = scanlines;
m_bRotary = (flags & FONTFLAG_ROTARY) ? 1 : 0;
m_bAdditive = (flags & 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 );
CPS3FontMetrics fontMetrics;
CPS3CharMetrics charMetrics[ABCWIDTHS_CACHE_SIZE];
// 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().GetCachedPS3Metrics( windowsFontName, tall, style, &fontMetrics, charMetrics ) )
{
if ( !m_hFont )
{
m_hFont = FontManager().MaterialSystem()->OpenTrueTypeFont( windowsFontName, tall, style );
// no you're not seeing double
if ( !m_hFont )
{
return false;
}
}
// get the predominant font metrics now [1-255], the extended set [256-65535] is on-demand
FontManager().MaterialSystem()->GetTrueTypeFontMetrics( m_hFont, m_iTall, 1, 255, &fontMetrics, &charMetrics[1] );
// getting the metrics is an expensive i/o operation, cache results
FontManager().SetCachedPS3Metrics( windowsFontName, tall, style, &fontMetrics, charMetrics );
}
m_szName = windowsFontName;
m_iHeight = fontMetrics.lineHeight + fontMetrics.effectHeight + m_iDropShadowOffset + 2 * m_iOutlineSize;
m_iMaxCharWidth = ceilf(fontMetrics.fMaxWidth);
m_iAscent = fontMetrics.baseLineY;
// 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
*/
a = charMetrics[i].A();
b = charMetrics[i].B();
c = charMetrics[i].C();
// Widen for blur, outline, and shadow. Need to widen b and reduce a and c.
m_ABCWidthsCache[i].a = a - m_iOutlineSize;
m_ABCWidthsCache[i].b = b + ( ( m_iBlur + m_iOutlineSize ) * 2 ) + m_iDropShadowOffset;
m_ABCWidthsCache[i].c = c - 2*m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: generates texture data (written into appropriate font page subrects) for multiple chars
//-----------------------------------------------------------------------------
void CPS3Font::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, m_iTall, 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 CPS3Font::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 CPS3Font::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 = FONTFLAG_DROPSHADOW |
FONTFLAG_OUTLINE |
FONTFLAG_ROTARY |
FONTFLAG_ITALIC |
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 CPS3Font::IsValid()
{
if ( m_szName != UTL_INVAL_SYMBOL )
return true;
return false;
}
//-----------------------------------------------------------------------------
// Purpose: gets the abc widths for a character
//-----------------------------------------------------------------------------
void CPS3Font::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
{
// look for it in the extended 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 system
// getting the metrics is an expensive i/o operation
if ( !m_hFont )
{
// demand request for font metrics, re-open font
int style = GetStyleFromParameters( m_iFlags, m_iWeight );
m_hFont = FontManager().MaterialSystem()->OpenTrueTypeFont( GetName(), m_iTall, style );
}
if ( m_hFont )
{
CPS3FontMetrics fontMetrics;
CPS3CharMetrics charMetrics;
FontManager().MaterialSystem()->GetTrueTypeFontMetrics( m_hFont, m_iTall, ch, ch, &fontMetrics, &charMetrics );
/*
// Determine real a,b,c mapping from XUI Character Metrics
a = charMetrics.fMinX - 1; // Add one column of padding to make up for font rendering blurring into left column (and adjust in b)
b = charMetrics.fMaxX - charMetrics.fMinX + 1;
c = charMetrics.fAdvance - charMetrics.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
*/
a = charMetrics.A();
b = charMetrics.B();
c = charMetrics.C();
// Widen for blur, outline, and shadow. Need to widen b and reduce a and c.
a = a - m_iOutlineSize;
b = b + ( ( m_iBlur + m_iOutlineSize ) * 2 ) + m_iDropShadowOffset;
c = c - 2*m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
}
else
{
a = 0;
b = 0;
c = 0;
}
// add to the cache
finder.abc.a = a;
finder.abc.b = b;
finder.abc.c = c;
m_ExtendedABCWidthsCache.Insert( finder );
}
}
void CPS3Font::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA, float &abcC )
{
int a, b, c;
GetCharABCWidths( ch, a, b, c );
wide = a+b+c;
abcA = a;
abcC = c;
}
//-----------------------------------------------------------------------------
// Purpose: returns the height of the font, in pixels
//-----------------------------------------------------------------------------
int CPS3Font::GetHeight()
{
Assert( IsValid() );
return m_iHeight;
}
//-----------------------------------------------------------------------------
// Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
//-----------------------------------------------------------------------------
int CPS3Font::GetAscent()
{
Assert( IsValid() );
return m_iAscent;
}
//-----------------------------------------------------------------------------
// Purpose: returns the maximum width of a character, in pixels
//-----------------------------------------------------------------------------
int CPS3Font::GetMaxCharWidth()
{
Assert( IsValid() );
return m_iMaxCharWidth;
}
//-----------------------------------------------------------------------------
// Purpose: returns the flags used to make this font, used by the dynamic resizing code
//-----------------------------------------------------------------------------
int CPS3Font::GetFlags()
{
Assert( IsValid() );
return m_iFlags;
}
//-----------------------------------------------------------------------------
// Purpose: Comparison function for abc widths storage
//-----------------------------------------------------------------------------
bool CPS3Font::ExtendedABCWidthsCacheLessFunc( const abc_cache_t &lhs, const abc_cache_t &rhs )
{
return lhs.wch < rhs.wch;
}
void CPS3Font::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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Contains all texture state for the material system surface to use
//
// $Revision: $
// $NoKeywords: $
//=============================================================================//
#ifndef TEXTUREDICTIONARY_H
#define TEXTUREDICTIONARY_H
class IMaterial;
enum
{
INVALID_TEXTURE_ID = -1
};
//-----------------------------------------------------------------------------
// A class that manages textures used by the material system surface
//-----------------------------------------------------------------------------
class ITextureDictionary
{
public:
// Create, destroy textures
virtual int CreateTexture( bool procedural = false ) = 0;
virtual void DestroyTexture( int id ) = 0;
virtual void DestroyAllTextures() = 0;
// Is this a valid id?
virtual bool IsValidId( int id ) const = 0;
// Binds a material to a texture
virtual void BindTextureToFile( int id, const char *pFileName ) = 0;
// Binds a material to a texture
virtual void BindTextureToMaterial( int id, IMaterial *pMaterial ) = 0;
// Binds a material to a texture
virtual void BindTextureToMaterialReference( int id, int referenceId, IMaterial *pMaterial ) = 0;
// Texture info
virtual IMaterial *GetTextureMaterial( int id ) = 0;
virtual void GetTextureSize(int id, int& iWide, int& iTall ) = 0;
virtual void GetTextureTexCoords( int id, float &s0, float &t0, float &s1, float &t1 ) = 0;
virtual void SetTextureRGBA( int id, const char* rgba, int wide, int tall ) = 0;
virtual int FindTextureIdForTextureFile( char const *pFileName ) = 0;
virtual void SetSubTextureRGBA( int id, int drawX, int drawY, unsigned const char *rgba, int subTextureWide, int subTextureTall ) = 0;
virtual void SetSubTextureRGBAEx( int id, int drawX, int drawY, unsigned const char *rgba, int subTextureWide, int subTextureTall, ImageFormat format ) = 0;
virtual void SetTextureRGBAEx( int id, const char* rgba, int wide, int tall, ImageFormat format, bool bFixupTextCoordsForDimensions ) = 0;
virtual void UpdateSubTextureRGBA( int id, int drawX, int drawY, unsigned const char *rgba, int subTextureWide, int subTextureTall, ImageFormat imageFormat ) = 0;
};
ITextureDictionary *TextureDictionary();
#endif // TEXTUREDICTIONARY_H

View File

@@ -0,0 +1,60 @@
//-----------------------------------------------------------------------------
// VGUI_SURFACELIB.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$macro SRCDIR "..\.."
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Configuration
{
$Compiler
{
$PreprocessorDefinitions "$BASE;ALLOW_TEXT_MODE=1" [$CSTRIKE_TRUNK_BUILD||$CSTRIKE_STAGING_BUILD]
}
}
$Project "vgui_surfacelib"
{
$Folder "Source Files"
{
$File "BitmapFont.cpp"
$File "FontAmalgam.cpp"
$File "fontmanager.cpp"
$File "FontEffects.cpp"
$File "FontEffects.h"
$File "fonttexturecache.cpp"
$File "texturedictionary.cpp"
$File "Win32Font.cpp" [$WINDOWS]
$File "Win32Font_x360.cpp" [$X360]
$File "ps3font.cpp" [$PS3]
$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\common\vgui_surfacelib\fonttexturecache.h"
$File "$SRCDIR\common\vgui_surfacelib\ifontsurface.h"
$File "$SRCDIR\common\vgui_surfacelib\texturedictionary.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" [$WINDOWS]
$File "$SRCDIR\common\vgui_surfacelib\ps3font.h" [$PS3]
$File "$SRCDIR\common\vgui_surfacelib\osxfont.h" [$OSXALL]
}
}

View File

@@ -0,0 +1,13 @@
"vpc_cache"
{
"CacheVersion" "1"
"win32"
{
"CRCFile" "vgui_surfacelib.vcxproj.vpc_crc"
"OutputFiles"
{
"0" "vgui_surfacelib.vcxproj"
"1" "vgui_surfacelib.vcxproj.filters"
}
}
}

View File

@@ -0,0 +1,2 @@
SN Visual Studio Integration
IMPORTANT: Do not remove the custom build step for this file

View File

@@ -0,0 +1,213 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: PS3 support for true type fonts.
//
//=====================================================================================//
#if 0 // clipped for now
#include "materialsystem/imaterialsystem.h"
#include "vgui_surfacelib/Win32Font.h"
#include "vgui_surfacelib/FontManager.h"
#include "../materialsystem/ifont.h"
#include "FontEffects.h"
#include <vgui/ISurface.h>
CWin32Font::CWin32Font() : m_ExtendedABCWidthsCache(256, 0, &ExtendedABCWidthsCacheLessFunc)
{
m_pFont = NULL;
}
CWin32Font::~CWin32Font()
{
//FontManager().MaterialSystem()->CloseTrueTypeFont(m_pFont);
m_pFont = NULL;
}
// Create 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 & FONTFLAG_ANTIALIAS) ? 1 : 0;
m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
m_iBlur = blur;
m_iScanLines = scanlines;
m_bRotary = (flags & FONTFLAG_ROTARY) ? 1 : 0;
m_bAdditive = (flags & FONTFLAG_ADDITIVE) ? 1 : 0;
m_szName = windowsFontName;
// if the weight is greater that 400, set the style to bold (cf win32font_x360)
// By default use the regular style
m_iWeight = 0x10000 ; // ONE16Dot16 1.0f (F16Dot16 format used by font fusion)
if ( weight > 400 )
{
m_iWeight = 5L << 14; // 1.25 (F16Dot16 format used by font fusion)
}
// Open the font
ExecuteNTimes( 5, Warning( "Fonts dont work on PS3\n" ) );
//m_pFont = FontManager().MaterialSystem()->OpenTrueTypeFont(windowsFontName, tall, m_iWeight);
if(m_pFont == NULL)
{
Warning("Failed to open font %s\n", windowsFontName);
return false;
}
// Store the font parameters
m_iHeight = m_pFont->GetMaxHeight();
m_iAscent = m_pFont->GetAscent();
m_iMaxCharWidth = m_pFont->GetMaxWidth();
// Setup ABC cache
// 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));
for(int i = 0; i < ABCWIDTHS_CACHE_SIZE; i++)
{
int a,b,c;
a = 0;
b = 0;
c = 0;
m_pFont->GetCharABCWidth(i, a, b, c);
m_ABCWidthsCache[i].a = a - m_iBlur;
m_ABCWidthsCache[i].b = b + m_iBlur*2;
m_ABCWidthsCache[i].c = c - m_iBlur;
}
// 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
Assert( 0 );
//FontManager().MaterialSystem()->CloseTrueTypeFont( m_pFont );
m_pFont = NULL;
return true;
}
// Render the font to a buffer
void CWin32Font::GetCharRGBA(wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba)
{
if ( ch == '\t' )
{
// tabs don't draw
return;
}
if ( !m_pFont )
{
// demand request for font glyph, re-create font
Assert( 0 );
//m_pFont = FontManager().MaterialSystem()->OpenTrueTypeFont(GetName(), m_iTall, m_iWeight);
}
int a, c, wide, tall;
GetCharABCWidths( ch, a, wide, c );
tall = m_iHeight;
m_pFont->RenderToBuffer(ch, m_iBlur, rgbaWide, rgbaTall, rgba);
// apply requested effects in specified order
//ApplyDropShadowToTexture( rgbaX, rgbaY, rgbaWide, rgbaTall, wide, tall, rgba, m_iDropShadowOffset );
//ApplyOutlineToTexture( rgbaX, rgbaY, rgbaWide, rgbaTall, wide, tall, rgba, m_iOutlineSize );
ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, rgba, m_iBlur );
ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, rgba, m_iScanLines );
//ApplyRotaryEffectToTexture( rgbaX, rgbaY, rgbaWide, rgbaTall, rgba, m_bRotary );
}
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_iScanLines == scanlines)
{
return true;
}
return false;
}
bool CWin32Font::IsValid()
{
return true;
}
// Font metrics
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
{
// 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;
}
if(!m_pFont)
{
//BRAD: In some instances (e.g. when you try and create an EAOnline account) it tries
// to call GetCharABCWidths before GetCharRGBA so m_pFont is not opened.
// demand request for font glyph, re-create font
Assert( 0 );
//m_pFont = FontManager().MaterialSystem()->OpenTrueTypeFont(GetName(), m_iTall, m_iWeight);
}
m_pFont->GetCharABCWidth(ch, a, b, c);
// add to the cache
finder.abc.a = a;
finder.abc.b = b;
finder.abc.c = c;
m_ExtendedABCWidthsCache.Insert(finder);
}
}
int CWin32Font::GetHeight()
{
return m_iHeight;
}
int CWin32Font::GetAscent()
{
return m_iAscent;
}
int CWin32Font::GetMaxCharWidth()
{
return m_iMaxCharWidth;
}
int CWin32Font::GetFlags()
{
return m_iFlags;
}
bool CWin32Font::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs)
{
return lhs.wch < rhs.wch;
}
#endif