Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

873 lines
26 KiB

5 years ago
//========= 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>
#ifdef OSX
#include <malloc/malloc.h>
#else
5 years ago
#include <malloc.h>
#endif
5 years ago
#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 HAVE_FC
if(!FcInit())
5 years ago
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);
#endif
5 years ago
}
#if HAVE_FC
static FcPattern* FontMatch(const char* type, ...)
5 years ago
{
FcValue fcvalue;
5 years ago
va_list ap;
va_start(ap, type);
5 years ago
FcPattern* pattern = FcPatternCreate();
for (;;) {
// FcType is promoted to int when passed through ...
fcvalue.type = static_cast<FcType>(va_arg(ap, int));
switch (fcvalue.type) {
5 years ago
case FcTypeString:
fcvalue.u.s = va_arg(ap, const FcChar8 *);
5 years ago
break;
case FcTypeInteger:
fcvalue.u.i = va_arg(ap, int);
5 years ago
break;
default:
Assert(!"FontMatch unhandled type");
}
FcPatternAdd(pattern, type, fcvalue, FcFalse);
5 years ago
type = va_arg(ap, const char *);
if (!type)
break;
};
va_end(ap);
FcConfigSubstitute(NULL, pattern, FcMatchPattern);
5 years ago
FcDefaultSubstitute(pattern);
FcResult result;
FcPattern* match = FcFontMatch(NULL, pattern, &result);
5 years ago
FcPatternDestroy(pattern);
return match;
}
#endif
5 years ago
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;
}
#if !HAVE_FC
char *TryFindFont(const char *winFontName, bool bBold, int italic)
{
static char fontFile[MAX_PATH];
const char *fontName, *fontNamePost;
#ifdef ANDROID
if( strcmp( winFontName, "Courier New") == 0 )
fontName = "LiberationMono-Regular.ttf";
else
fontName = "DroidSansFallback.ttf"; // for chinese/japanese/korean
snprintf( fontFile, sizeof fontFile, "%s/files/%s", getenv("APP_DATA_PATH"), fontName);
#else
// "platform/resource/linux_fonts/";
if( strcmp( winFontName, "Courier New") == 0 )
fontName = "liberationmono";
if( bBold )
{
if( italic )
fontNamePost = "boldoblique";
else
fontNamePost = "bold";
}
else if( italic )
fontNamePost = "oblique";
else
fontNamePost = "regular";
snprintf(fontFile, sizeof fontFile, "platform/resource/linux_fonts/%s-%s.ttf", fontName, fontNamePost);
#endif
return fontFile;
#if 0 // Old detect
const char *fontFileName, *fontFileNamePost = NULL;
fontFileName = "Roboto";
if( bBold )
{
if( italic )
fontFileNamePost = "BoldItalic";
else
fontFileNamePost = "Bold";
}
else if( italic )
fontFileNamePost = "Italic";
else
fontFileNamePost = "Regular";
static char dataFile[MAX_PATH];
if( fontFileNamePost )
snprintf( dataFile, sizeof dataFile, "/system/fonts/%s-%s.ttf", fontFileName, fontFileNamePost );
else
snprintf( dataFile, sizeof dataFile, "/system/fonts/%s.ttf", fontFileName );
if( access( dataFile, R_OK ) != 0 )
{
fontFileNamePost = NULL;
fontFileName = "DroidSans";
if( bBold )
fontFileNamePost = "Bold";
if( fontFileNamePost )
snprintf( dataFile, sizeof dataFile, "/system/fonts/%s-%s.ttf", fontFileName, fontFileNamePost );
else
snprintf( dataFile, sizeof dataFile, "/system/fonts/%s.ttf", fontFileName );
if( access( dataFile, R_OK ) != 0 )
return NULL;
}
return dataFile;
#endif
}
#endif
5 years ago
//-----------------------------------------------------------------------------
// 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;
5 years ago
#if !HAVE_FC
char *filename = TryFindFont( windowsFontName, bBold, italic );
Msg("Found font: %s\n", filename);
if( !filename ) return NULL;
return strdup( filename );
#else
const int nFcWeight = bBold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
FcPattern *match = FontMatch( FC_FAMILY, FcTypeString, pchFontName,
5 years ago
FC_WEIGHT, FcTypeInteger, nFcWeight,
FC_SLANT, FcTypeInteger, italic,
NULL);
if ( !match )
{
5 years ago
AssertMsg1( false, "Unable to find font named %s\n", windowsFontName );
return NULL;
}
5 years ago
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 );
Msg("font fc: %s - %s\n", windowsFontName, filenameret);
5 years ago
return filenameret;
}
#endif
5 years ago
}
//-----------------------------------------------------------------------------
// 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 );
5 years ago
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