diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vgui2/vgui_surfacelib/osxfont.cpp | |
| download | archived-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.cpp | 769 |
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 |