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.
769 lines
24 KiB
769 lines
24 KiB
//========= 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
|
|
|