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