Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <math.h>
  8. #include <tier0/dbg.h>
  9. #include <vgui/ISurface.h>
  10. #include <tier0/mem.h>
  11. #include <utlbuffer.h>
  12. #include "vgui_surfacelib/osxfont.h"
  13. #include "FontEffects.h"
  14. struct MetricsTweaks_t
  15. {
  16. const char *m_windowsFontName;
  17. int m_sizeAdjust;
  18. float m_ascentMultiplier;
  19. float m_descentMultiplier;
  20. float m_leadingMultiplier;
  21. };
  22. //94: HFont:0x000000b9, TFTypeDeath, TFTypeDeathClientScheme-no, font:tfd, tall:27(28)
  23. //95: HFont:0x000000ba, TFTypeDeath, TFTypeDeathClientScheme-p, font:tfd, tall:55(59)
  24. static const MetricsTweaks_t g_defaultMetricTweaks = { NULL, 0, 1.0, 1.0, 1.0 };
  25. static MetricsTweaks_t g_FontMetricTweaks[] =
  26. {
  27. { "Helvetica", 0, 1.0, 1.0, 1.05 },
  28. { "Helvetica Bold", 0, 1.0, 1.0, 1.0 },
  29. { "HL2cross", 0, 0.8, 1.0, 1.1},
  30. { "Counter-Strike Logo", 0, 1.0, 1.0, 1.1 },
  31. { "TF2", -2, 1.0, 1.0, 1.0 },
  32. { "TF2 Professor", -2, 1.0, 2.0, 1.1 },
  33. { "TF2 Build", -2, 1.0, 1.0, 1.0 },
  34. { "Stubble bold", -6, 1.3, 1.0, 1.0 },
  35. { "tfd", 0, 1.5, 1.0, 1.0 }, // "TFTypeDeath"
  36. //{ "TF2 Secondary", -2, 1.0, 1.0, 1.0 },
  37. // { "Verdana", 0, 1.25, 1.0, 1.0 },
  38. };
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. #ifdef STAGING_ONLY
  42. #define CHECK_ATSU_ERR( err ) if ( (err) != noErr ) Msg( "COSXFont::%s (%d) ATSU Error: %d\n", __FUNCTION__, __LINE__, err );
  43. #else
  44. #define CHECK_ATSU_ERR( err )
  45. #endif
  46. //-----------------------------------------------------------------------------
  47. // Purpose: Constructor
  48. //-----------------------------------------------------------------------------
  49. COSXFont::COSXFont() : m_ExtendedABCWidthsCache( 256, 0, &ExtendedABCWidthsCacheLessFunc ),
  50. m_ExtendedKernedABCWidthsCache( 256, 0, &ExtendedKernedABCWidthsCacheLessFunc )
  51. {
  52. m_iTall = 0;
  53. m_iWeight = 0;
  54. m_iFlags = 0;
  55. m_bAntiAliased = false;
  56. m_bRotary = false;
  57. m_bAdditive = false;
  58. m_iDropShadowOffset;
  59. m_bUnderlined = false;
  60. m_iOutlineSize = 0;
  61. m_iHeight = 0;
  62. m_iMaxCharWidth = 0;
  63. m_iAscent = 0;
  64. m_iScanLines = 0;
  65. m_iBlur = 0;
  66. m_pGaussianDistribution = NULL;
  67. m_ATSUFont = kATSFontRefUnspecified;
  68. m_pContextMemory = NULL;
  69. m_ContextRef = 0;
  70. m_ATSUStyle = NULL;
  71. m_ATSUTextLayout = NULL;
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose: Destructor
  75. //-----------------------------------------------------------------------------
  76. COSXFont::~COSXFont()
  77. {
  78. if ( m_ContextRef )
  79. CGContextRelease( m_ContextRef );
  80. if ( m_pContextMemory )
  81. delete [] m_pContextMemory;
  82. if ( m_ATSUStyle )
  83. ATSUDisposeStyle( m_ATSUStyle );
  84. if ( m_ATSUTextLayout )
  85. ATSUDisposeTextLayout( m_ATSUTextLayout );
  86. }
  87. bool COSXFont::CreateStyle( float flFontSize, bool bBold )
  88. {
  89. OSStatus err = ATSUCreateStyle( &m_ATSUStyle );
  90. if ( err != noErr )
  91. {
  92. CHECK_ATSU_ERR( err );
  93. return false;
  94. }
  95. Boolean isBold = bBold;
  96. Boolean isUnderlined = m_bUnderlined;
  97. float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  98. Fixed fontsize = FloatToFixed( flFontSize ); // font size is based on 72dpi and not pixels, so convert to pixels
  99. Boolean isItalic = m_iFlags & vgui::ISurface::FONTFLAG_ITALIC;
  100. ATSStyleRenderingOptions renderOpt = kATSStyleNoOptions;
  101. renderOpt |= ( m_bAntiAliased ? kATSStyleApplyAntiAliasing : kATSStyleNoAntiAliasing );
  102. const ATSUAttributeTag styleTags[] =
  103. {
  104. kATSUFontTag,
  105. kATSUSizeTag,
  106. kATSUQDItalicTag,
  107. kATSUQDBoldfaceTag,
  108. kATSUStyleRenderingOptionsTag,
  109. kATSUQDUnderlineTag,
  110. kATSURGBAlphaColorTag,
  111. };
  112. const ATSUAttributeValuePtr styleValues[] =
  113. {
  114. &m_ATSUFont,
  115. &fontsize,
  116. &isItalic,
  117. &isBold,
  118. &renderOpt,
  119. &isUnderlined,
  120. &color,
  121. };
  122. const ByteCount styleSizes[] =
  123. {
  124. sizeof( ATSUFontID ),
  125. sizeof( Fixed ),
  126. sizeof( Boolean ),
  127. sizeof( Boolean ),
  128. sizeof( renderOpt ),
  129. sizeof( Boolean ),
  130. sizeof( color ),
  131. };
  132. err = ATSUSetAttributes( m_ATSUStyle, Q_ARRAYSIZE( styleTags ), styleTags, styleSizes, styleValues );
  133. if (err != noErr )
  134. {
  135. CHECK_ATSU_ERR( err );
  136. return false;
  137. }
  138. return true;
  139. }
  140. bool COSXFont::CreateTextLayout()
  141. {
  142. OSStatus err = ATSUCreateTextLayout( &m_ATSUTextLayout );
  143. if ( err != noErr )
  144. {
  145. CHECK_ATSU_ERR( err );
  146. return false;
  147. }
  148. Fract alignment = kATSUStartAlignment;
  149. ATSUTextMeasurement lineWidth = IntToFixed( 32000 );
  150. ATSLineLayoutOptions layoutOptions = kATSLineUseDeviceMetrics | kATSLineDisableAllLayoutOperations;
  151. ATSUAttributeTag theTags[] =
  152. {
  153. kATSUCGContextTag,
  154. kATSULineWidthTag,
  155. kATSULineFlushFactorTag,
  156. kATSULineLayoutOptionsTag,
  157. };
  158. ByteCount theSizes[] =
  159. {
  160. sizeof( CGContextRef ),
  161. sizeof( ATSUTextMeasurement ),
  162. sizeof( Fract ),
  163. sizeof( ATSLineLayoutOptions ),
  164. };
  165. ATSUAttributeValuePtr theValues[] =
  166. {
  167. &m_ContextRef,
  168. &lineWidth,
  169. &alignment,
  170. &layoutOptions,
  171. };
  172. err = ATSUSetLayoutControls( m_ATSUTextLayout, Q_ARRAYSIZE( theTags ), theTags, theSizes, theValues );
  173. if ( err != noErr )
  174. {
  175. CHECK_ATSU_ERR( err );
  176. return false;
  177. }
  178. return true;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose: creates the font from windows. returns false if font does not exist in the OS.
  182. //-----------------------------------------------------------------------------
  183. bool COSXFont::Create( const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags )
  184. {
  185. // setup font properties
  186. m_iTall = tall;
  187. m_iWeight = weight;
  188. m_iFlags = flags;
  189. m_iBlur = blur;
  190. m_iScanLines = scanlines;
  191. m_bAntiAliased = flags & vgui::ISurface::FONTFLAG_ANTIALIAS;
  192. m_bUnderlined = flags & vgui::ISurface::FONTFLAG_UNDERLINE;
  193. m_iDropShadowOffset = ( flags & vgui::ISurface::FONTFLAG_DROPSHADOW ) ? 1 : 0;
  194. m_iOutlineSize = ( flags & vgui::ISurface::FONTFLAG_OUTLINE ) ? 1 : 0;
  195. m_bRotary = flags & vgui::ISurface::FONTFLAG_ROTARY;
  196. m_bAdditive = flags & vgui::ISurface::FONTFLAG_ADDITIVE;
  197. m_ATSUFont = kATSFontRefUnspecified;
  198. CFStringRef fontName = CFStringCreateWithCString( kCFAllocatorDefault, windowsFontName, kCFStringEncodingUTF8 );
  199. if ( fontName )
  200. {
  201. m_ATSUFont = ATSFontFindFromPostScriptName( fontName, kATSOptionFlagsDefault );
  202. CFRelease( fontName );
  203. }
  204. if ( m_ATSUFont == kATSFontRefUnspecified )
  205. {
  206. CFStringRef fontName = CFStringCreateWithCString( kCFAllocatorDefault, windowsFontName, kCFStringEncodingUTF8 );
  207. if ( fontName )
  208. {
  209. m_ATSUFont = ATSFontFindFromName( fontName, kATSOptionFlagsDefault );
  210. CFRelease( fontName );
  211. }
  212. }
  213. if ( m_ATSUFont == kATSFontRefUnspecified )
  214. {
  215. CHECK_ATSU_ERR( -1 );
  216. return false;
  217. }
  218. ATSFontMetrics aMetrics;
  219. OSStatus err = ATSFontGetHorizontalMetrics( m_ATSUFont, kATSOptionFlagsDefault, &aMetrics );
  220. if ( err != noErr )
  221. {
  222. CHECK_ATSU_ERR( err );
  223. return false;
  224. }
  225. MetricsTweaks_t metricTweaks = g_defaultMetricTweaks;
  226. for ( int i = 0; i < Q_ARRAYSIZE( g_FontMetricTweaks ); i++ )
  227. {
  228. if ( !Q_stricmp( windowsFontName, g_FontMetricTweaks[ i ].m_windowsFontName ) )
  229. {
  230. metricTweaks = g_FontMetricTweaks[ i ];
  231. break;
  232. }
  233. }
  234. bool bBold = ( !Q_stricmp( windowsFontName, "Arial Black" ) || Q_stristr( windowsFontName, "bold" ) );
  235. float flFontSize = ( (float)( m_iTall + metricTweaks.m_sizeAdjust ) / ( aMetrics.ascent - aMetrics.descent + aMetrics.leading ) );
  236. m_iAscent = ceil( ( aMetrics.ascent / ( aMetrics.ascent - aMetrics.descent + aMetrics.leading ) ) *
  237. ( m_iTall + metricTweaks.m_sizeAdjust ) * ( metricTweaks.m_ascentMultiplier ) );
  238. m_iHeight = ceil( ((float)( m_iTall + metricTweaks.m_sizeAdjust ) *
  239. ( aMetrics.ascent * metricTweaks.m_ascentMultiplier -
  240. aMetrics.descent * metricTweaks.m_descentMultiplier +
  241. aMetrics.leading * metricTweaks.m_leadingMultiplier ) +
  242. m_iDropShadowOffset + 2 * m_iOutlineSize ) );
  243. m_iMaxCharWidth = ( metricTweaks.m_leadingMultiplier * aMetrics.maxAdvanceWidth * m_iTall ) + 0.5f;
  244. unsigned int bytesPerRow = m_iMaxCharWidth * 4;
  245. m_pContextMemory = new char[ (int)bytesPerRow * m_iHeight ];
  246. Q_memset( m_pContextMemory, 0x0, (int)( bytesPerRow * m_iHeight ) );
  247. const size_t bitsPerComponent = 8;
  248. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  249. m_ContextRef = CGBitmapContextCreate(
  250. m_pContextMemory,
  251. m_iMaxCharWidth,
  252. m_iHeight,
  253. bitsPerComponent,
  254. bytesPerRow,
  255. colorSpace,
  256. kCGImageAlphaPremultipliedLast );
  257. CGColorSpaceRelease( colorSpace );
  258. CGContextSetAllowsAntialiasing( m_ContextRef, m_bAntiAliased );
  259. CGContextSetShouldAntialias( m_ContextRef, m_bAntiAliased );
  260. CGContextSetTextDrawingMode( m_ContextRef, kCGTextFill );
  261. CGContextSetRGBStrokeColor( m_ContextRef, 1.0f, 1.0f, 1.0f, 1.0f );
  262. CGContextSetLineWidth( m_ContextRef, 1 );
  263. // Calculate our gaussian distribution for if we're blurred.
  264. if ( m_iBlur > 1 )
  265. {
  266. m_pGaussianDistribution = new float[ m_iBlur * 2 + 1 ];
  267. double sigma = 0.683 * m_iBlur;
  268. for (int x = 0; x <= (m_iBlur * 2); x++)
  269. {
  270. int val = x - m_iBlur;
  271. m_pGaussianDistribution[x] = (float)(1.0f / sqrt(2 * 3.14 * sigma * sigma)) * pow(2.7, -1 * (val * val) / (2 * sigma * sigma));
  272. }
  273. }
  274. if ( !CreateStyle( flFontSize, bBold ) )
  275. return false;
  276. // Create our ATSUTextLayout object.
  277. if ( !CreateTextLayout() )
  278. return false;
  279. // Set our font name last as we use it to determine success (or not).
  280. m_szName = windowsFontName;
  281. return true;
  282. }
  283. void COSXFont::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA )
  284. {
  285. // look for it in the cache
  286. kerned_abc_cache_t finder = { ch, chBefore, chAfter };
  287. wide = abcA = 0.0f;
  288. unsigned short iKerned = m_ExtendedKernedABCWidthsCache.Find( finder );
  289. if ( m_ExtendedKernedABCWidthsCache.IsValidIndex( iKerned ) )
  290. {
  291. abcA = m_ExtendedKernedABCWidthsCache[iKerned].abc.abcA;
  292. wide = m_ExtendedKernedABCWidthsCache[ iKerned ].abc.wide;
  293. return;
  294. }
  295. if ( !m_ATSUStyle || ( ch == 0 ) )
  296. return;
  297. CFCharacterSetRef badCharSetRef = CFCharacterSetGetPredefined( kCFCharacterSetIllegal );
  298. uint32 i = 0;
  299. uint32 iTarget = 0;
  300. bool bBadChar = false;
  301. wchar_t wchString[ 4 ];
  302. if ( chBefore )
  303. {
  304. bBadChar = CFCharacterSetIsLongCharacterMember( badCharSetRef, chBefore );
  305. Assert( !bBadChar );
  306. if ( !bBadChar)
  307. wchString[ i++ ] = chBefore;
  308. }
  309. iTarget = i;
  310. bBadChar = CFCharacterSetIsLongCharacterMember( badCharSetRef, ch );
  311. Assert( !bBadChar );
  312. if ( bBadChar )
  313. return; // 0.0 width for bad characters.
  314. wchString[ i++ ] = ch;
  315. if ( chAfter )
  316. {
  317. bBadChar = CFCharacterSetIsLongCharacterMember( badCharSetRef, chAfter );
  318. Assert( !bBadChar );
  319. if ( !bBadChar)
  320. wchString[ i++ ] = chAfter;
  321. }
  322. wchString[ i ] = 0;
  323. CFStringRef convertedKey = CFStringCreateWithBytes( kCFAllocatorDefault, (const UInt8 *)wchString, i * sizeof(wchar_t), kCFStringEncodingUTF32LE, false );
  324. if ( !convertedKey )
  325. return;
  326. CFIndex usedBufLen = 0;
  327. char chUTF16Text[ Q_ARRAYSIZE( wchString ) * 2 ];
  328. CFStringGetBytes( convertedKey, CFRangeMake( 0, (int)i ), kCFStringEncodingUnicode, 0, false, (UInt8 *)chUTF16Text, Q_ARRAYSIZE( chUTF16Text ), &usedBufLen );
  329. CFRelease( convertedKey );
  330. OSStatus err = ATSUSetTextPointerLocation( m_ATSUTextLayout, (ConstUniCharArrayPtr)chUTF16Text, kATSUFromTextBeginning, kATSUToTextEnd, i );
  331. if ( err != noErr )
  332. {
  333. CHECK_ATSU_ERR( err );
  334. return;
  335. }
  336. err = ATSUSetRunStyle( m_ATSUTextLayout, m_ATSUStyle, 0, i );
  337. if ( err != noErr )
  338. {
  339. CHECK_ATSU_ERR( err );
  340. return;
  341. }
  342. err = ATSUSetTransientFontMatching( m_ATSUTextLayout, false );
  343. if ( err != noErr )
  344. CHECK_ATSU_ERR( err );
  345. ATSLayoutRecord *layoutRecords;
  346. ItemCount glyphCount;
  347. layoutRecords = NULL;
  348. err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( m_ATSUTextLayout, 0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **) &layoutRecords, &glyphCount );
  349. CHECK_ATSU_ERR( err );
  350. if( err == noErr )
  351. {
  352. ATSGlyphIdealMetrics ScreenMetricts[ Q_ARRAYSIZE( wchString ) ];
  353. err = ATSUGlyphGetIdealMetrics( m_ATSUStyle, i, &layoutRecords[ 0 ].glyphID, sizeof( ATSLayoutRecord ), ScreenMetricts );
  354. CHECK_ATSU_ERR( err );
  355. if( err == noErr )
  356. {
  357. wide = ScreenMetricts[ iTarget ].advance.x;
  358. abcA = ScreenMetricts[ iTarget ].sideBearing.x - (float)m_iBlur - (float)m_iOutlineSize;
  359. }
  360. ATSUDirectReleaseLayoutDataArrayPtr( NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**) &layoutRecords );
  361. }
  362. if ( ( wide < 0.001f && abcA < 0.001f ) || ( ch == chAfter ) )
  363. {
  364. // For some fonts the kerning engine has an issue and considers the second character in a pair
  365. // (i.e the ee in bleed) to be zero width and abcA and the first char to be double width,
  366. // so in that case fall back to simple 1 char metrics.
  367. int a, b, c;
  368. GetCharABCWidths( ch, a, b, c );
  369. wide = a + b + c;
  370. abcA = a;
  371. }
  372. // printf( "GetKernedCharWidth: %c (%f, %f) (%c, %c)\n", (char)ch, wide, abcA, chBefore, chAfter );
  373. finder.abc.abcA = abcA;
  374. finder.abc.wide = wide;
  375. m_ExtendedKernedABCWidthsCache.Insert( finder );
  376. }
  377. static UniCharCount WcharToUnichar( wchar_t ch, UniChar (&dest)[ 3 ] )
  378. {
  379. UniCharCount runLength;
  380. if ( ch <= 0xFFFF )
  381. {
  382. dest[ 0 ] = (UniChar)ch;
  383. dest[ 1 ] = 0;
  384. return 1;
  385. }
  386. ch -= 0x010000;
  387. dest[ 0 ] = (UniChar)( ch >> 10 ) | 0xD800;
  388. dest[ 1 ] = (UniChar)( ch & 0x3FF ) | 0xDC00;
  389. dest[ 2 ] = 0;
  390. return 2;
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Purpose: writes the char into the specified 32bpp texture
  394. //-----------------------------------------------------------------------------
  395. void COSXFont::GetCharRGBA( wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba )
  396. {
  397. if ( !m_ContextRef )
  398. {
  399. Assert( !"Context ref not setup to allow GetCharRGBA" );
  400. return;
  401. }
  402. UniChar buffer[ 3 ];
  403. UniCharCount runLength = WcharToUnichar( ch, buffer );
  404. OSStatus err = ATSUSetTextPointerLocation( m_ATSUTextLayout, buffer, kATSUFromTextBeginning, kATSUToTextEnd, runLength );
  405. if ( err != noErr )
  406. {
  407. CHECK_ATSU_ERR( err );
  408. return;
  409. }
  410. err = ATSUSetRunStyle( m_ATSUTextLayout, m_ATSUStyle, 0, runLength );
  411. if ( err != noErr )
  412. {
  413. CHECK_ATSU_ERR( err );
  414. return;
  415. }
  416. err = ATSUSetTransientFontMatching( m_ATSUTextLayout, true );
  417. if ( err != noErr )
  418. CHECK_ATSU_ERR( err );
  419. CGRect rect = { { 0, 0 }, { m_iMaxCharWidth, m_iHeight } };
  420. CGContextClearRect( m_ContextRef, rect );
  421. CGContextFlush( m_ContextRef );
  422. // You are not seeing a bug. We need the background to have a full
  423. // alpha channel since it's going to bitblt in the overlay. But osx when it renders
  424. // to a full alpha channel likes to 'antialias' the alpha as well as the color blending.
  425. // Turning off antialiasing doesn't just make the characters jagged, but when a font
  426. // is 1 pixel wide it ends up alpha blending into the background. By rendering it
  427. // more than once we dominate this alpha. Looking at worst case, 3 was the magic number.
  428. // Messing with the anti-aliasing settings on the bitmap, or the alpha config (including
  429. // alpha-skip-last) caused no change, or complete disabling of antialiasing. Glad these
  430. // are cached.
  431. if ( m_iHeight < 20 )
  432. {
  433. err = ATSUDrawText( m_ATSUTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix( 0 ), Long2Fix( m_iHeight - m_iAscent ) );
  434. CHECK_ATSU_ERR( err );
  435. }
  436. if ( ( m_iHeight < 16 ) || ( m_iFlags & vgui::ISurface::FONTFLAG_CUSTOM ) )
  437. {
  438. err = ATSUDrawText( m_ATSUTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix( 0 ), Long2Fix( m_iHeight - m_iAscent ) );
  439. CHECK_ATSU_ERR( err );
  440. }
  441. err = ATSUDrawText( m_ATSUTextLayout, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix( 0 ), Long2Fix( m_iHeight - m_iAscent ) );
  442. if ( err != noErr )
  443. {
  444. CHECK_ATSU_ERR( err );
  445. return;
  446. }
  447. char *pContextData = (char *)CGBitmapContextGetData( m_ContextRef );
  448. unsigned char *pchPixelData = rgba;
  449. for ( int y = 0; y < rgbaTall; y++ )
  450. {
  451. char *row = pContextData + y * m_iMaxCharWidth * 4;
  452. Q_memcpy( pchPixelData, row, rgbaWide * 4 );
  453. pchPixelData+= ( rgbaWide * 4 );
  454. }
  455. // apply requested effects in specified order
  456. ApplyDropShadowToTexture( rgbaWide, rgbaTall, rgba, m_iDropShadowOffset );
  457. ApplyOutlineToTexture( rgbaWide, rgbaTall, rgba, m_iOutlineSize );
  458. ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, rgba, m_iBlur );
  459. ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, rgba, m_iScanLines );
  460. ApplyRotaryEffectToTexture( rgbaWide, rgbaTall, rgba, m_bRotary );
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Purpose: gets the abc widths for a character
  464. //-----------------------------------------------------------------------------
  465. void COSXFont::GetCharABCWidths( int ch, int &a, int &b, int &c )
  466. {
  467. Assert( IsValid() );
  468. // Look for it in the cache.
  469. abc_cache_t finder = { (wchar_t)ch };
  470. unsigned short i = m_ExtendedABCWidthsCache.Find( finder );
  471. if ( m_ExtendedABCWidthsCache.IsValidIndex( i ) )
  472. {
  473. a = m_ExtendedABCWidthsCache[i].abc.a;
  474. b = m_ExtendedABCWidthsCache[i].abc.b;
  475. c = m_ExtendedABCWidthsCache[i].abc.c;
  476. return;
  477. }
  478. UniChar buffer[ 3 ];
  479. UniCharCount runLength = WcharToUnichar( ch, buffer );
  480. OSStatus err = ATSUSetTextPointerLocation( m_ATSUTextLayout, buffer, kATSUFromTextBeginning, kATSUToTextEnd, runLength );
  481. if ( err != noErr )
  482. {
  483. CHECK_ATSU_ERR( err );
  484. return;
  485. }
  486. err = ATSUSetRunStyle( m_ATSUTextLayout, m_ATSUStyle, 0, runLength );
  487. if ( err != noErr )
  488. {
  489. CHECK_ATSU_ERR( err );
  490. return;
  491. }
  492. err = ATSUSetTransientFontMatching( m_ATSUTextLayout, true );
  493. if ( err != noErr )
  494. CHECK_ATSU_ERR( err );
  495. ItemCount glyphCount = 0;
  496. ATSLayoutRecord *layoutRecords;
  497. err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( m_ATSUTextLayout, 0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, ( void ** )&layoutRecords, &glyphCount );
  498. if (err != noErr)
  499. {
  500. CHECK_ATSU_ERR( err );
  501. return;
  502. }
  503. ATSGlyphRef glyph = layoutRecords->glyphID;
  504. ATSGlyphScreenMetrics gm;
  505. err = ATSUGlyphGetScreenMetrics( m_ATSUStyle, 1, &glyph, 0, false, false, &gm );
  506. ATSUDirectReleaseLayoutDataArrayPtr( NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, ( void ** )&layoutRecords );
  507. if ( err != noErr )
  508. {
  509. // ATSUGlyphGetScreenMetrics fails when font matching happens. When this occurs,
  510. // grab the full bounding box for the character and try to fake up some abc values.
  511. ATSUTextMeasurement fTextBefore, fTextAfter, fAscent, fDescent;
  512. err = ATSUGetUnjustifiedBounds( m_ATSUTextLayout,
  513. kATSUFromTextBeginning,
  514. kATSUToTextEnd,
  515. &fTextBefore,
  516. &fTextAfter,
  517. &fAscent,
  518. &fDescent);
  519. gm.sideBearing.x = 0.2f;
  520. gm.deviceAdvance.x = FixedToFloat( fTextAfter );
  521. gm.otherSideBearing.x = 0.2f;
  522. }
  523. if ( err != noErr )
  524. {
  525. CHECK_ATSU_ERR( err );
  526. return;
  527. }
  528. finder.abc.a = gm.sideBearing.x - (float)m_iBlur - m_iOutlineSize;
  529. finder.abc.b = gm.deviceAdvance.x + ( ( (float)m_iBlur + m_iOutlineSize ) * 2 ) + m_iDropShadowOffset;
  530. finder.abc.c = gm.otherSideBearing.x - (float)m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
  531. // finder.abc.a = ceil( finder.abc.a );
  532. // finder.abc.b = ceil( finder.abc.b );
  533. // finder.abc.c = ceil( finder.abc.c );
  534. if ( m_iFlags & vgui::ISurface::FONTFLAG_ITALIC )
  535. {
  536. finder.abc.b += 4.0f;
  537. }
  538. if ( finder.abc.a + finder.abc.b + finder.abc.c == 0 )
  539. {
  540. if ( finder.abc.b + finder.abc.c == 0 )
  541. finder.abc.c = 0;
  542. else if ( finder.abc.a + finder.abc.b == 0 )
  543. finder.abc.a = 0;
  544. else
  545. finder.abc.a = finder.abc.c = 0;
  546. }
  547. a = finder.abc.a;
  548. b = finder.abc.b;
  549. c = finder.abc.c;
  550. m_ExtendedABCWidthsCache.Insert( finder );
  551. }
  552. //-----------------------------------------------------------------------------
  553. // Purpose: returns true if the font is equivalent to that specified
  554. //-----------------------------------------------------------------------------
  555. bool COSXFont::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
  556. {
  557. if ( !Q_stricmp( windowsFontName, m_szName.String() ) &&
  558. ( m_iTall == tall ) &&
  559. ( m_iWeight == weight ) &&
  560. ( m_iBlur == blur ) &&
  561. ( m_iFlags == flags ) )
  562. {
  563. return true;
  564. }
  565. return false;
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose: returns true only if this font is valid for use
  569. //-----------------------------------------------------------------------------
  570. bool COSXFont::IsValid()
  571. {
  572. if ( !m_szName.IsEmpty() && m_szName.String()[ 0 ] )
  573. return true;
  574. return false;
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose: returns the height of the font, in pixels
  578. //-----------------------------------------------------------------------------
  579. int COSXFont::GetHeight()
  580. {
  581. Assert( IsValid() );
  582. return m_iHeight;
  583. }
  584. //-----------------------------------------------------------------------------
  585. // Purpose: returns the requested height of the font
  586. //-----------------------------------------------------------------------------
  587. int COSXFont::GetHeightRequested()
  588. {
  589. Assert( IsValid() );
  590. return m_iTall;
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
  594. //-----------------------------------------------------------------------------
  595. int COSXFont::GetAscent()
  596. {
  597. Assert( IsValid() );
  598. return m_iAscent;
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose: returns the maximum width of a character, in pixels
  602. //-----------------------------------------------------------------------------
  603. int COSXFont::GetMaxCharWidth()
  604. {
  605. Assert( IsValid() );
  606. return m_iMaxCharWidth;
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose: returns the flags used to make this font, used by the dynamic resizing code
  610. //-----------------------------------------------------------------------------
  611. int COSXFont::GetFlags()
  612. {
  613. return m_iFlags;
  614. }
  615. //-----------------------------------------------------------------------------
  616. // Purpose: Comparison function for abc widths storage
  617. //-----------------------------------------------------------------------------
  618. bool COSXFont::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs)
  619. {
  620. return lhs.wch < rhs.wch;
  621. }
  622. //-----------------------------------------------------------------------------
  623. // Purpose: Comparison function for abc widths storage
  624. //-----------------------------------------------------------------------------
  625. bool COSXFont::ExtendedKernedABCWidthsCacheLessFunc(const kerned_abc_cache_t &lhs, const kerned_abc_cache_t &rhs)
  626. {
  627. return ( lhs.wch < rhs.wch ) ||
  628. ( lhs.wch == rhs.wch && lhs.wchBefore < rhs.wchBefore ) ||
  629. ( lhs.wch == rhs.wch && lhs.wchBefore == rhs.wchBefore && lhs.wchAfter < rhs.wchAfter );
  630. }
  631. ATSUStyle *COSXFont::SetAsActiveFont( CGContextRef cgContext )
  632. {
  633. CGContextSelectFont ( cgContext, m_szName.String(), m_iHeight, kCGEncodingMacRoman );
  634. return &m_ATSUStyle;
  635. }
  636. #ifdef DBGFLAG_VALIDATE
  637. //-----------------------------------------------------------------------------
  638. // Purpose: Ensure that all of our internal structures are consistent, and
  639. // account for all memory that we've allocated.
  640. // Input: validator - Our global validator object
  641. // pchName - Our name (typically a member var in our container)
  642. //-----------------------------------------------------------------------------
  643. void COSXFont::Validate( CValidator &validator, char *pchName )
  644. {
  645. validator.Push( "COSXFont", this, pchName );
  646. m_ExtendedABCWidthsCache.Validate( validator, "m_ExtendedABCWidthsCache" );
  647. m_ExtendedKernedABCWidthsCache.Validate( validator, "m_ExtendedKernedABCWidthsCache" );
  648. validator.ClaimMemory( m_pGaussianDistribution );
  649. validator.Pop();
  650. }
  651. #endif // DBGFLAG_VALIDATE