Counter Strike : Global Offensive Source Code
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.

533 lines
16 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <assert.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <math.h>
  11. #include <malloc.h>
  12. #include <tier0/dbg.h>
  13. #include <vgui/ISurface.h>
  14. #include <tier0/mem.h>
  15. #include <utlbuffer.h>
  16. #include <vstdlib/vstrtools.h>
  17. #include "filesystem.h"
  18. #include "vgui_surfacelib/osxfont.h"
  19. #include "FontEffects.h"
  20. #define DEBUG_FONT_CREATE 0
  21. struct MetricsTweaks_t
  22. {
  23. const char *m_windowsFontName;
  24. int m_sizeAdjust;
  25. float m_ascentMultiplier;
  26. float m_descentMultiplier;
  27. float m_leadingMultiplier;
  28. };
  29. static const MetricsTweaks_t g_defaultMetricTweaks = { NULL, 0, 1.0, 1.0, 1.0 };// -2, 1.0, 1.0, 1.0 };
  30. static MetricsTweaks_t g_FontMetricTweaks[] =
  31. {
  32. { "Helvetica", 0, 1.0, 1.0, 1.05 },
  33. { "Helvetica Bold", 0, 1.0, 1.0, 1.0 },
  34. { "HL2cross", 0, 0.8, 1.0, 1.1},
  35. { "Counter-Strike Logo", 0, 1.0, 1.0, 1.1},
  36. { "TF2", -2, 1.0, 1.0, 1.0 },
  37. { "TF2 Professor", -2, 1.0, 1.1, 1.1 },
  38. { "TF2 Build", -2, 1.0, 1.0, 1.0 },
  39. { "UniversLTStd-BoldCn", 0, 1.4, 1.0, 0.8 },
  40. { "UniversLTStd-Cn", 0, 1.2, 1.0, 1.0 },
  41. //{ "TF2 Secondary", -2, 1.0, 1.0, 1.0 },
  42. // { "Verdana", 0, 1.25, 1.0, 1.0 },
  43. };
  44. // memdbgon must be the last include file in a .cpp file!!!
  45. #include "tier0/memdbgon.h"
  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_iAscent = 0;
  54. m_iDescent = 0;
  55. m_iWeight = 0;
  56. m_iFlags = 0;
  57. m_iMaxCharWidth = 0;
  58. m_bAntiAliased = false;
  59. m_bUnderlined = false;
  60. m_iBlur = 0;
  61. m_pGaussianDistribution = NULL;
  62. m_iScanLines = 0;
  63. m_bRotary = false;
  64. m_bAdditive = false;
  65. m_ContextRef = 0;
  66. m_pContextMemory = NULL;
  67. }
  68. //-----------------------------------------------------------------------------
  69. // Purpose: Destructor
  70. //-----------------------------------------------------------------------------
  71. COSXFont::~COSXFont()
  72. {
  73. if ( m_ContextRef )
  74. {
  75. CGContextRelease( m_ContextRef );
  76. }
  77. if ( m_pContextMemory )
  78. delete [] m_pContextMemory;
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose: creates the font from windows. returns false if font does not exist in the OS.
  82. //-----------------------------------------------------------------------------
  83. bool COSXFont::Create(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
  84. {
  85. // setup font properties
  86. m_szName = windowsFontName;
  87. m_iTall = tall;
  88. m_iWeight = weight;
  89. m_iFlags = flags;
  90. m_bAntiAliased = flags & FONTFLAG_ANTIALIAS;
  91. #if 0
  92. // the font used in portal2 looks ok (better, in fact) anti-aliased when small,
  93. if ( tall < 20 )
  94. {
  95. m_bAntiAliased = false;
  96. }
  97. #endif
  98. m_bUnderlined = flags & FONTFLAG_UNDERLINE;
  99. m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
  100. m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
  101. m_iBlur = blur;
  102. m_iScanLines = scanlines;
  103. m_bRotary = flags & FONTFLAG_ROTARY;
  104. m_bAdditive = flags & FONTFLAG_ADDITIVE;
  105. char sCustomPath[1024];
  106. Q_snprintf( sCustomPath, sizeof( sCustomPath ), "./platform/vgui/fonts/%s.ttf", windowsFontName );
  107. if ( g_pFullFileSystem->FileExists( sCustomPath ) )
  108. {
  109. CFStringRef path = CFStringCreateWithCString( NULL, windowsFontName, kCFStringEncodingUTF8 );
  110. CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false);
  111. CGDataProviderRef dataProvider = CGDataProviderCreateWithURL( url );
  112. CGFontRef cgFont = CGFontCreateWithDataProvider( dataProvider );
  113. m_hFont = CTFontCreateWithGraphicsFont( cgFont, tall, nullptr, nullptr );
  114. CFRelease( cgFont );
  115. CFRelease( dataProvider );
  116. CFRelease( url );
  117. CFRelease( path );
  118. CTFontCopyCharacterSet(m_hFont);
  119. }
  120. else
  121. {
  122. const void *pKeys[2];
  123. const void *pValues[2];
  124. float fCTWeight = ( (float)( weight - 400 ) / 500.0f );
  125. pKeys[0] = kCTFontWeightTrait;
  126. pValues[0] = CFNumberCreate( NULL, kCFNumberFloatType, &fCTWeight );
  127. float fCTSlant = ( flags & FONTFLAG_ITALIC ) != 0 ? 1.0f : 0.0f;
  128. pKeys[1] = kCTFontSlantTrait;
  129. pValues[1] = CFNumberCreate( NULL, kCFNumberFloatType, &fCTSlant );
  130. CFDictionaryRef pTraitsDict = CFDictionaryCreate( NULL, pKeys, pValues, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
  131. if ( !pTraitsDict )
  132. {
  133. goto Fail;
  134. }
  135. CFRelease( (CFNumberRef)pValues[0] );
  136. CFRelease( (CFNumberRef)pValues[1] );
  137. pKeys[0] = kCTFontNameAttribute;
  138. pValues[0] = CFStringCreateWithCString( NULL, windowsFontName, kCFStringEncodingUTF8 );
  139. pKeys[1] = kCTFontTraitsAttribute;
  140. pValues[1] = pTraitsDict;
  141. CFDictionaryRef pDescDict;
  142. pDescDict = CFDictionaryCreate( NULL, pKeys, pValues, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
  143. CFRelease( (CFStringRef)pValues[0] );
  144. CFRelease( pTraitsDict );
  145. if ( !pDescDict )
  146. {
  147. goto Fail;
  148. }
  149. CTFontDescriptorRef pFontDesc;
  150. pFontDesc = CTFontDescriptorCreateWithAttributes( pDescDict );
  151. CFRelease( pDescDict );
  152. if ( !pFontDesc )
  153. {
  154. goto Fail;
  155. }
  156. // Fudge the size of the font to something reasonable.
  157. m_hFont = CTFontCreateWithFontDescriptor( pFontDesc, int(tall*0.85), NULL );
  158. CFRelease( pFontDesc );
  159. }
  160. if ( !m_hFont )
  161. {
  162. goto Fail;
  163. }
  164. CGRect bbox;
  165. bbox = CTFontGetBoundingBox( m_hFont );
  166. m_iAscent = ceil( CTFontGetAscent( m_hFont ) );
  167. // The bounding box height seems to be overly large so use
  168. // ascent plus descent.
  169. m_iHeight = m_iAscent + ceil( CTFontGetDescent( m_hFont ) ) + m_iDropShadowOffset + 2 * m_iOutlineSize;
  170. m_iMaxCharWidth = ceil( bbox.size.width ) + 2 * m_iOutlineSize;
  171. uint bytesPerRow;
  172. bytesPerRow = m_iMaxCharWidth * 4;
  173. m_pContextMemory = new char[ (int)bytesPerRow * m_iHeight ];
  174. memset( m_pContextMemory, 0x0, (int)( bytesPerRow * m_iHeight) );
  175. CGColorSpaceRef colorSpace;
  176. colorSpace = CGColorSpaceCreateDeviceRGB();
  177. m_ContextRef = CGBitmapContextCreate( m_pContextMemory, m_iMaxCharWidth, m_iHeight,
  178. 8,
  179. bytesPerRow,
  180. colorSpace,
  181. kCGImageAlphaPremultipliedLast );
  182. CGColorSpaceRelease( colorSpace );
  183. if ( !m_ContextRef )
  184. {
  185. goto Fail;
  186. }
  187. CGContextSetAllowsAntialiasing( m_ContextRef, m_bAntiAliased );
  188. CGContextSetShouldAntialias( m_ContextRef, m_bAntiAliased );
  189. CGContextSetTextDrawingMode( m_ContextRef, kCGTextFill );
  190. CGContextSetRGBStrokeColor( m_ContextRef, 1.0, 1.0, 1.0, 1.0 );
  191. CGContextSetLineWidth( m_ContextRef, 1 );
  192. return true;
  193. Fail:
  194. return false;
  195. }
  196. void COSXFont::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA, float &abcC )
  197. {
  198. int a,b,c;
  199. GetCharABCWidths(ch, a, b, c );
  200. wide = ( a + b + c );
  201. abcA = a;
  202. abcC = c;
  203. }
  204. static bool GetGlyphsForCharacter( CTFontRef hFont, wchar_t ch, CGGlyph* pGlyphs )
  205. {
  206. UniChar pUniChars[2];
  207. pUniChars[0] = ch;
  208. pUniChars[1] = 0;
  209. if ( !CTFontGetGlyphsForCharacters( hFont, pUniChars, pGlyphs, 1 ) )
  210. {
  211. char str[2];
  212. str[0] = (char)ch;
  213. str[1] = 0;
  214. CFStringRef s = CFStringCreateWithCString(nullptr, str, kTextEncodingUnicodeDefault);
  215. pGlyphs[0] = CTFontGetGlyphWithName(hFont, s);
  216. CFRelease( s );
  217. if ( !pGlyphs[0] )
  218. {
  219. return false;
  220. }
  221. }
  222. return true;
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: writes the char into the specified 32bpp texture
  226. //-----------------------------------------------------------------------------
  227. void COSXFont::GetCharRGBA( wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba )
  228. {
  229. wchar_t pWchars[1];
  230. pWchars[0] = (wchar_t)ch;
  231. CGGlyph pGlyphs[1];
  232. if ( !GetGlyphsForCharacter( m_hFont, ch, pGlyphs ) )
  233. {
  234. AssertMsg( false, "CTFontGetGlyphsForCharacters failed" );
  235. return;
  236. }
  237. CGRect rect = { { 0, 0 }, { m_iMaxCharWidth, m_iHeight } };
  238. CGContextClearRect( m_ContextRef, rect );
  239. CGRect pBounds[1];
  240. CTFontGetBoundingRectsForGlyphs( m_hFont, kCTFontDefaultOrientation, pGlyphs, pBounds, 1 );
  241. CGPoint pPositions[1];
  242. // The character will be drawn offset by the 'A' distance so adjust
  243. // it back as this routine only wants the core bits.
  244. pPositions[0].x = m_iOutlineSize;
  245. // The DrawGlyphs coordinate system puts zero Y at the bottom of
  246. // the bitmap and puts the text baseline at zero Y so push
  247. // it up to place characters where we expect them.
  248. pPositions[0].y = ( m_iHeight - m_iAscent ) - m_iOutlineSize;
  249. CTFontDrawGlyphs( m_hFont, pGlyphs, pPositions, 1, m_ContextRef );
  250. CGContextFlush( m_ContextRef );
  251. char *pContextData = (char *)CGBitmapContextGetData( m_ContextRef );
  252. uint8 *pchPixelData = rgba;
  253. for ( int y = 0; y < rgbaTall; y++ )
  254. {
  255. char *row = pContextData + y * m_iMaxCharWidth * 4;
  256. for ( int x = 0; x < rgbaWide; x++ )
  257. {
  258. if ( row[0] || row[1] || row[2] || row[3] )
  259. {
  260. pchPixelData[0] = 0xff;
  261. pchPixelData[1] = 0xff;
  262. pchPixelData[2] = 0xff;
  263. pchPixelData[3] = row[3];
  264. }
  265. else
  266. {
  267. pchPixelData[0] = 0;
  268. pchPixelData[1] = 0;
  269. pchPixelData[2] = 0;
  270. pchPixelData[3] = 0;
  271. }
  272. row += 4;
  273. pchPixelData += 4;
  274. }
  275. }
  276. // Draw top and bottom bars for character placement debugging.
  277. #if FORCE_CHAR_BOX_BOUNDS
  278. pchPixelData = rgba;
  279. for ( int x = 0; x < rgbaWide; x++ )
  280. {
  281. pchPixelData[0] = 0;
  282. pchPixelData[1] = 0;
  283. pchPixelData[2] = 0;
  284. pchPixelData[3] = 0xff;
  285. pchPixelData += 4;
  286. }
  287. pchPixelData = rgba + ( rgbaTall - 1 ) * rgbaWide * 4;
  288. for ( int x = 0; x < rgbaWide; x++ )
  289. {
  290. pchPixelData[0] = 0;
  291. pchPixelData[1] = 0;
  292. pchPixelData[2] = 0;
  293. pchPixelData[3] = 0xff;
  294. pchPixelData += 4;
  295. }
  296. #endif
  297. // apply requested effects in specified order
  298. ApplyDropShadowToTexture( rgbaWide, rgbaTall, rgba, m_iDropShadowOffset );
  299. ApplyOutlineToTexture( rgbaWide, rgbaTall, rgba, m_iOutlineSize );
  300. ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, rgba, m_iBlur );
  301. ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, rgba, m_iScanLines );
  302. ApplyRotaryEffectToTexture( rgbaWide, rgbaTall, rgba, m_bRotary );
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose: gets the abc widths for a character
  306. //-----------------------------------------------------------------------------
  307. void COSXFont::GetCharABCWidths(int ch, int &a, int &b, int &c)
  308. {
  309. Assert(IsValid());
  310. // look for it in the cache
  311. abc_cache_t finder = { (wchar_t)ch };
  312. uint16 i = m_ExtendedABCWidthsCache.Find(finder);
  313. if (m_ExtendedABCWidthsCache.IsValidIndex(i))
  314. {
  315. a = m_ExtendedABCWidthsCache[i].abc.a;
  316. b = m_ExtendedABCWidthsCache[i].abc.b;
  317. c = m_ExtendedABCWidthsCache[i].abc.c;
  318. return;
  319. }
  320. a = 0;
  321. b = 0;
  322. c = 0;
  323. wchar_t pWchars[1];
  324. pWchars[0] = (wchar_t)ch;
  325. CGGlyph pGlyphs[1];
  326. if ( !GetGlyphsForCharacter( m_hFont, ch, pGlyphs ) )
  327. {
  328. AssertMsg( false, "CTFontGetGlyphsForCharacters failed" );
  329. return;
  330. }
  331. CGSize pAdvances[1];
  332. CTFontGetAdvancesForGlyphs( m_hFont, kCTFontDefaultOrientation, pGlyphs, pAdvances, 1 );
  333. CGRect pBounds[1];
  334. CTFontGetBoundingRectsForGlyphs( m_hFont, kCTFontDefaultOrientation, pGlyphs, pBounds, 1 );
  335. a = 0;
  336. b = ceil(pAdvances->width);
  337. c = 0;
  338. finder.abc.a = a;
  339. finder.abc.b = b;
  340. finder.abc.c = c;
  341. m_ExtendedABCWidthsCache.Insert( finder );
  342. }
  343. //-----------------------------------------------------------------------------
  344. // Purpose: returns true if the font is equivalent to that specified
  345. //-----------------------------------------------------------------------------
  346. bool COSXFont::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
  347. {
  348. if (!Q_stricmp(windowsFontName, m_szName.String() )
  349. && m_iTall == tall
  350. && m_iWeight == weight
  351. && m_iBlur == blur
  352. && m_iFlags == flags)
  353. return true;
  354. return false;
  355. }
  356. //-----------------------------------------------------------------------------
  357. // Purpose: returns true only if this font is valid for use
  358. //-----------------------------------------------------------------------------
  359. bool COSXFont::IsValid()
  360. {
  361. if ( !m_szName.IsEmpty() && m_szName.String()[0] )
  362. return true;
  363. return false;
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose: returns the height of the font, in pixels
  367. //-----------------------------------------------------------------------------
  368. int COSXFont::GetHeight()
  369. {
  370. assert(IsValid());
  371. return m_iHeight;
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
  375. //-----------------------------------------------------------------------------
  376. int COSXFont::GetAscent()
  377. {
  378. assert(IsValid());
  379. return m_iAscent;
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
  383. //-----------------------------------------------------------------------------
  384. int COSXFont::GetDescent()
  385. {
  386. assert(IsValid());
  387. return m_iDescent;
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: returns the maximum width of a character, in pixels
  391. //-----------------------------------------------------------------------------
  392. int COSXFont::GetMaxCharWidth()
  393. {
  394. assert(IsValid());
  395. return m_iMaxCharWidth;
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose: returns the flags used to make this font, used by the dynamic resizing code
  399. //-----------------------------------------------------------------------------
  400. int COSXFont::GetFlags()
  401. {
  402. return m_iFlags;
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose: Comparison function for abc widths storage
  406. //-----------------------------------------------------------------------------
  407. bool COSXFont::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs)
  408. {
  409. return lhs.wch < rhs.wch;
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose: Comparison function for abc widths storage
  413. //-----------------------------------------------------------------------------
  414. bool COSXFont::ExtendedKernedABCWidthsCacheLessFunc(const kerned_abc_cache_t &lhs, const kerned_abc_cache_t &rhs)
  415. {
  416. return lhs.wch < rhs.wch || ( lhs.wch == rhs.wch && lhs.wchBefore < rhs.wchBefore )
  417. || ( lhs.wch == rhs.wch && lhs.wchBefore == rhs.wchBefore && lhs.wchAfter < rhs.wchAfter );
  418. }
  419. void *COSXFont::SetAsActiveFont( CGContextRef cgContext )
  420. {
  421. CGContextSelectFont ( cgContext, m_szName.String(), m_iHeight, kCGEncodingMacRoman);
  422. return NULL;
  423. }
  424. #ifdef DBGFLAG_VALIDATE
  425. //-----------------------------------------------------------------------------
  426. // Purpose: Ensure that all of our internal structures are consistent, and
  427. // account for all memory that we've allocated.
  428. // Input: validator - Our global validator object
  429. // pchName - Our name (typically a member var in our container)
  430. //-----------------------------------------------------------------------------
  431. void COSXFont::Validate( CValidator &validator, char *pchName )
  432. {
  433. validator.Push( "COSXFont", this, pchName );
  434. m_ExtendedABCWidthsCache.Validate( validator, "m_ExtendedABCWidthsCache" );
  435. m_ExtendedKernedABCWidthsCache.Validate( validator, "m_ExtendedKernedABCWidthsCache" );
  436. validator.ClaimMemory( m_pGaussianDistribution );
  437. validator.Pop();
  438. }
  439. #endif // DBGFLAG_VALIDATE