summaryrefslogtreecommitdiff
path: root/vgui2/vgui_surfacelib/osxfont.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vgui2/vgui_surfacelib/osxfont.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'vgui2/vgui_surfacelib/osxfont.cpp')
-rw-r--r--vgui2/vgui_surfacelib/osxfont.cpp769
1 files changed, 769 insertions, 0 deletions
diff --git a/vgui2/vgui_surfacelib/osxfont.cpp b/vgui2/vgui_surfacelib/osxfont.cpp
new file mode 100644
index 0000000..59b1f4a
--- /dev/null
+++ b/vgui2/vgui_surfacelib/osxfont.cpp
@@ -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