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.

583 lines
17 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data
  7. #define SUPPORT_CUSTOM_FONT_FORMAT
  8. #include <assert.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <math.h>
  12. #if !defined( _PS3 )
  13. #include <malloc.h>
  14. #endif // ! _PS3
  15. #include "vgui_surfacelib/Win32Font.h"
  16. #include "tier0/dbg.h"
  17. #include "vgui_surfacelib/IFontSurface.h"
  18. #include "tier0/mem.h"
  19. #include "utlbuffer.h"
  20. #include "FontEffects.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. static OSVERSIONINFO s_OsVersionInfo;
  24. static bool s_bOsVersionInitialized = false;
  25. bool s_bSupportsUnicode = false;
  26. //-----------------------------------------------------------------------------
  27. // Purpose: Constructor
  28. //-----------------------------------------------------------------------------
  29. CWin32Font::CWin32Font() : m_ExtendedABCWidthsCache(256, 0, &ExtendedABCWidthsCacheLessFunc)
  30. {
  31. m_szName = UTL_INVAL_SYMBOL;
  32. m_iTall = 0;
  33. m_iWeight = 0;
  34. m_iHeight = 0;
  35. m_iAscent = 0;
  36. m_iFlags = 0;
  37. m_iMaxCharWidth = 0;
  38. m_hFont = NULL;
  39. m_hDC = NULL;
  40. m_hDIB = NULL;
  41. m_bAntiAliased = false;
  42. m_bUnderlined = false;
  43. m_iBlur = 0;
  44. m_iScanLines = 0;
  45. m_bRotary = false;
  46. m_bAdditive = false;
  47. m_rgiBitmapSize[ 0 ] = m_rgiBitmapSize[ 1 ] = 0;
  48. m_ExtendedABCWidthsCache.EnsureCapacity( 128 );
  49. if ( !s_bOsVersionInitialized )
  50. {
  51. // get the operating system version
  52. s_bOsVersionInitialized = true;
  53. memset(&s_OsVersionInfo, 0, sizeof(s_OsVersionInfo));
  54. s_OsVersionInfo.dwOSVersionInfoSize = sizeof(s_OsVersionInfo);
  55. GetVersionEx(&s_OsVersionInfo);
  56. if (s_OsVersionInfo.dwMajorVersion >= 5)
  57. {
  58. s_bSupportsUnicode = true;
  59. }
  60. else
  61. {
  62. s_bSupportsUnicode = false;
  63. }
  64. }
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose: Destructor
  68. //-----------------------------------------------------------------------------
  69. CWin32Font::~CWin32Font()
  70. {
  71. if ( m_hFont )
  72. ::DeleteObject( m_hFont );
  73. if ( m_hDC )
  74. ::DeleteDC( m_hDC );
  75. if ( m_hDIB )
  76. ::DeleteObject( m_hDIB );
  77. }
  78. #ifndef SUPPORT_CUSTOM_FONT_FORMAT
  79. //-----------------------------------------------------------------------------
  80. // Purpose: Font iteration callback function
  81. // used to determine whether or not a font exists on the system
  82. //-----------------------------------------------------------------------------
  83. extern bool g_bFontFound = false;
  84. int CALLBACK FontEnumProc(
  85. const LOGFONT *lpelfe, // logical-font data
  86. const TEXTMETRIC *lpntme, // physical-font data
  87. DWORD FontType, // type of font
  88. LPARAM lParam ) // application-defined data
  89. {
  90. g_bFontFound = true;
  91. return 0;
  92. }
  93. #endif // SUPPORT_CUSTOM_FONT_FORMAT
  94. //-----------------------------------------------------------------------------
  95. // Purpose: creates the font from windows. returns false if font does not exist in the OS.
  96. //-----------------------------------------------------------------------------
  97. bool CWin32Font::Create(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
  98. {
  99. // setup font properties
  100. m_szName = windowsFontName;
  101. m_iTall = tall;
  102. m_iWeight = weight;
  103. m_iFlags = flags;
  104. m_bAntiAliased = (flags & FONTFLAG_ANTIALIAS) ? 1 : 0;
  105. m_bUnderlined = (flags & FONTFLAG_UNDERLINE) ? 1 : 0;
  106. m_iDropShadowOffset = (flags & FONTFLAG_DROPSHADOW) ? 1 : 0;
  107. m_iOutlineSize = (flags & FONTFLAG_OUTLINE) ? 1 : 0;
  108. m_iBlur = blur;
  109. m_iScanLines = scanlines;
  110. m_bRotary = (flags & FONTFLAG_ROTARY) ? 1 : 0;
  111. m_bAdditive = (flags & FONTFLAG_ADDITIVE) ? 1 : 0;
  112. int charset = (flags & FONTFLAG_SYMBOL) ? SYMBOL_CHARSET : ANSI_CHARSET;
  113. // hack for japanese win98 support
  114. if ( !stricmp( windowsFontName, "win98japanese" ) )
  115. {
  116. // use any font that contains the japanese charset
  117. charset = SHIFTJIS_CHARSET;
  118. m_szName = "Tahoma";
  119. }
  120. // create our windows device context
  121. m_hDC = ::CreateCompatibleDC(NULL);
  122. Assert( m_hDC );
  123. #ifndef SUPPORT_CUSTOM_FONT_FORMAT
  124. // Vitaliy: fonts registered using custom font format are
  125. // not enumerated. Font creation will fail below for a font that
  126. // cannot be instantiated.
  127. {
  128. // see if the font exists on the system
  129. LOGFONT logfont;
  130. logfont.lfCharSet = DEFAULT_CHARSET;
  131. logfont.lfPitchAndFamily = 0;
  132. strcpy(logfont.lfFaceName, m_szName.String());
  133. g_bFontFound = false;
  134. ::EnumFontFamiliesEx(m_hDC, &logfont, &FontEnumProc, 0, 0);
  135. if (!g_bFontFound)
  136. {
  137. // needs to go to a fallback
  138. m_szName = UTL_INVAL_SYMBOL;
  139. return false;
  140. }
  141. }
  142. #endif
  143. m_hFont = ::CreateFontA(tall, 0, 0, 0,
  144. m_iWeight,
  145. flags & FONTFLAG_ITALIC,
  146. flags & FONTFLAG_UNDERLINE,
  147. flags & FONTFLAG_STRIKEOUT,
  148. charset,
  149. OUT_DEFAULT_PRECIS,
  150. CLIP_DEFAULT_PRECIS,
  151. m_bAntiAliased ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY,
  152. DEFAULT_PITCH | FF_DONTCARE,
  153. windowsFontName);
  154. if (!m_hFont)
  155. {
  156. Error("Couldn't create windows font '%s'\n", windowsFontName);
  157. m_szName = UTL_INVAL_SYMBOL;
  158. return false;
  159. }
  160. // set as the active font
  161. ::SetMapMode(m_hDC, MM_TEXT);
  162. ::SelectObject(m_hDC, m_hFont);
  163. ::SetTextAlign(m_hDC, TA_LEFT | TA_TOP | TA_UPDATECP);
  164. // get info about the font
  165. ::TEXTMETRIC tm;
  166. memset( &tm, 0, sizeof( tm ) );
  167. if ( !GetTextMetrics(m_hDC, &tm) )
  168. {
  169. m_szName = UTL_INVAL_SYMBOL;
  170. return false;
  171. }
  172. m_iHeight = tm.tmHeight + m_iDropShadowOffset + 2 * m_iOutlineSize;
  173. m_iMaxCharWidth = tm.tmMaxCharWidth;
  174. m_iAscent = tm.tmAscent;
  175. // code for rendering to a bitmap
  176. m_rgiBitmapSize[0] = tm.tmMaxCharWidth + m_iOutlineSize * 2;
  177. m_rgiBitmapSize[1] = tm.tmHeight + m_iDropShadowOffset + m_iOutlineSize * 2;
  178. ::BITMAPINFOHEADER header;
  179. memset(&header, 0, sizeof(header));
  180. header.biSize = sizeof(header);
  181. header.biWidth = m_rgiBitmapSize[0];
  182. header.biHeight = -m_rgiBitmapSize[1];
  183. header.biPlanes = 1;
  184. header.biBitCount = 32;
  185. header.biCompression = BI_RGB;
  186. m_hDIB = ::CreateDIBSection(m_hDC, (BITMAPINFO*)&header, DIB_RGB_COLORS, (void**)(&m_pBuf), NULL, 0);
  187. ::SelectObject(m_hDC, m_hDIB);
  188. return true;
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Purpose: writes the char into the specified 32bpp texture
  192. //-----------------------------------------------------------------------------
  193. void CWin32Font::GetCharRGBA(wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba)
  194. {
  195. int a, b, c;
  196. GetCharABCWidths(ch, a, b, c);
  197. // set us up to render into our dib
  198. ::SelectObject(m_hDC, m_hFont);
  199. int wide = b;
  200. if ( m_bUnderlined )
  201. {
  202. wide += ( a + c );
  203. }
  204. int tall = m_iHeight;
  205. GLYPHMETRICS glyphMetrics;
  206. MAT2 mat2 = { { 0, 1}, { 0, 0}, { 0, 0}, { 0, 1}};
  207. int bytesNeeded = 0;
  208. bool bShouldAntialias = m_bAntiAliased;
  209. // filter out
  210. if ( ch > 0x00FF && !(m_iFlags & FONTFLAG_CUSTOM) )
  211. {
  212. bShouldAntialias = false;
  213. }
  214. if ( !s_bSupportsUnicode )
  215. {
  216. // win98 hack, don't antialias some characters that ::GetGlyphOutline() produces bad results for
  217. if (ch == 'I' || ch == '1')
  218. {
  219. bShouldAntialias = false;
  220. }
  221. // don't antialias big fonts at all (since win98 often produces bad results)
  222. if (m_iHeight >= 13)
  223. {
  224. bShouldAntialias = false;
  225. }
  226. }
  227. // only antialias latin characters, since it essentially always fails for asian characters
  228. if (bShouldAntialias)
  229. {
  230. // try and get the glyph directly
  231. ::SelectObject(m_hDC, m_hFont);
  232. bytesNeeded = ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, 0, NULL, &mat2);
  233. }
  234. if (bytesNeeded > 0)
  235. {
  236. // take it
  237. unsigned char *lpbuf = (unsigned char *)_alloca(bytesNeeded);
  238. ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, bytesNeeded, lpbuf, &mat2);
  239. // rows are on DWORD boundaries
  240. wide = glyphMetrics.gmBlackBoxX;
  241. while (wide % 4 != 0)
  242. {
  243. wide++;
  244. }
  245. // see where we should start rendering
  246. int pushDown = m_iAscent - glyphMetrics.gmptGlyphOrigin.y;
  247. // set where we start copying from
  248. int xstart = 0;
  249. // don't copy the first set of pixels if the antialiased bmp is bigger than the char width
  250. if ((int)glyphMetrics.gmBlackBoxX >= b + 2)
  251. {
  252. xstart = (glyphMetrics.gmBlackBoxX - b) / 2;
  253. }
  254. // iterate through copying the generated dib into the texture
  255. for (unsigned int j = 0; j < glyphMetrics.gmBlackBoxY; j++)
  256. {
  257. for (unsigned int i = xstart; i < glyphMetrics.gmBlackBoxX; i++)
  258. {
  259. int x = i - xstart + m_iBlur + m_iOutlineSize;
  260. int y = j + pushDown;
  261. if ((x < rgbaWide) && (y < rgbaTall))
  262. {
  263. unsigned char grayscale = lpbuf[(j*wide+i)];
  264. float r, g, b, a;
  265. if (grayscale)
  266. {
  267. r = g = b = 1.0f;
  268. a = (grayscale + 0) / 64.0f;
  269. if (a > 1.0f) a = 1.0f;
  270. }
  271. else
  272. {
  273. r = g = b = a = 0.0f;
  274. }
  275. // Don't want anything drawn for tab characters.
  276. if (ch == '\t')
  277. {
  278. r = g = b = 0;
  279. }
  280. unsigned char *dst = &rgba[(y*rgbaWide+x)*4];
  281. dst[0] = (unsigned char)(r * 255.0f);
  282. dst[1] = (unsigned char)(g * 255.0f);
  283. dst[2] = (unsigned char)(b * 255.0f);
  284. dst[3] = (unsigned char)(a * 255.0f);
  285. }
  286. }
  287. }
  288. }
  289. else
  290. {
  291. // use render-to-bitmap to get our font texture
  292. ::SetBkColor(m_hDC, RGB(0, 0, 0));
  293. ::SetTextColor(m_hDC, RGB(255, 255, 255));
  294. ::SetBkMode(m_hDC, OPAQUE);
  295. if ( m_bUnderlined )
  296. {
  297. ::MoveToEx(m_hDC, 0, 0, NULL);
  298. }
  299. else
  300. {
  301. ::MoveToEx(m_hDC, -a, 0, NULL);
  302. }
  303. // render the character
  304. wchar_t wch = (wchar_t)ch;
  305. if (s_bSupportsUnicode)
  306. {
  307. // clear the background first
  308. RECT rect = { 0, 0, wide, tall};
  309. ::ExtTextOutW( m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL );
  310. // just use the unicode renderer
  311. ::ExtTextOutW( m_hDC, 0, 0, 0, NULL, &wch, 1, NULL );
  312. }
  313. else
  314. {
  315. // clear the background first (it may not get done automatically in win98/ME
  316. RECT rect = { 0, 0, wide, tall};
  317. ::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
  318. // convert the character using the current codepage
  319. char mbcs[6] = { 0 };
  320. ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL);
  321. ::ExtTextOutA(m_hDC, 0, 0, 0, NULL, mbcs, strlen(mbcs), NULL);
  322. }
  323. ::SetBkMode(m_hDC, TRANSPARENT);
  324. if (wide > m_rgiBitmapSize[0])
  325. {
  326. wide = m_rgiBitmapSize[0];
  327. }
  328. if (tall > m_rgiBitmapSize[1])
  329. {
  330. tall = m_rgiBitmapSize[1];
  331. }
  332. // iterate through copying the generated dib into the texture
  333. for (int j = (int)m_iOutlineSize; j < tall - (int)m_iOutlineSize; j++ )
  334. {
  335. // only copy from within the dib, ignore the outline border we are artificially adding
  336. for (int i = (int)m_iOutlineSize; i < wide - (int)m_iDropShadowOffset - (int)m_iOutlineSize; i++)
  337. {
  338. if ((i < rgbaWide) && (j < rgbaTall))
  339. {
  340. unsigned char *src = &m_pBuf[(i + j*m_rgiBitmapSize[0])*4];
  341. unsigned char *dst = &rgba[(i + j*rgbaWide)*4];
  342. // Don't want anything drawn for tab characters.
  343. unsigned char r, g, b;
  344. if ( ch == '\t' )
  345. {
  346. r = g = b = 0;
  347. }
  348. else
  349. {
  350. r = src[0];
  351. g = src[1];
  352. b = src[2];
  353. }
  354. // generate alpha based on luminance conversion
  355. dst[0] = r;
  356. dst[1] = g;
  357. dst[2] = b;
  358. dst[3] = (unsigned char)((float)r * 0.34f + (float)g * 0.55f + (float)b * 0.11f);
  359. }
  360. }
  361. }
  362. // if we have a dropshadow, we need to clean off the bottom row of pixels
  363. // this is because of a bug in winME that writes noise to them, only on the first time the game is run after a reboot
  364. // the bottom row should guaranteed to be empty to fit the dropshadow
  365. if ( m_iDropShadowOffset )
  366. {
  367. unsigned char *dst = &rgba[((m_iHeight - 1) * rgbaWide) * 4];
  368. for (int i = 0; i < wide; i++)
  369. {
  370. dst[0] = 0;
  371. dst[1] = 0;
  372. dst[2] = 0;
  373. dst[3] = 0;
  374. dst += 4;
  375. }
  376. }
  377. }
  378. // apply requested effects in specified order
  379. ApplyDropShadowToTexture( rgbaWide, rgbaTall, rgba, m_iDropShadowOffset );
  380. ApplyOutlineToTexture( rgbaWide, rgbaTall, rgba, m_iOutlineSize );
  381. ApplyGaussianBlurToTexture( rgbaWide, rgbaTall, rgba, m_iBlur );
  382. ApplyScanlineEffectToTexture( rgbaWide, rgbaTall, rgba, m_iScanLines );
  383. ApplyRotaryEffectToTexture( rgbaWide, rgbaTall, rgba, m_bRotary );
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: returns true if the font is equivalent to that specified
  387. //-----------------------------------------------------------------------------
  388. bool CWin32Font::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags)
  389. {
  390. if ( !stricmp(windowsFontName, m_szName.String() )
  391. && m_iTall == tall
  392. && m_iWeight == weight
  393. && m_iBlur == blur
  394. && m_iFlags == flags)
  395. return true;
  396. return false;
  397. }
  398. //-----------------------------------------------------------------------------
  399. // Purpose: returns true only if this font is valid for use
  400. //-----------------------------------------------------------------------------
  401. bool CWin32Font::IsValid()
  402. {
  403. if ( m_szName.IsValid() && m_szName.String()[0] )
  404. return true;
  405. return false;
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose: set the font to be the one to currently draw with in the gdi
  409. //-----------------------------------------------------------------------------
  410. void CWin32Font::SetAsActiveFont(HDC hdc)
  411. {
  412. Assert( IsValid() );
  413. ::SelectObject( hdc, m_hFont );
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Purpose: gets the abc widths for a character
  417. //-----------------------------------------------------------------------------
  418. void CWin32Font::GetCharABCWidths(int ch, int &a, int &b, int &c)
  419. {
  420. Assert( IsValid() );
  421. // look for it in the cache
  422. abc_cache_t finder = { (wchar_t)ch };
  423. unsigned short i = m_ExtendedABCWidthsCache.Find(finder);
  424. if (m_ExtendedABCWidthsCache.IsValidIndex(i))
  425. {
  426. a = m_ExtendedABCWidthsCache[i].abc.a;
  427. b = m_ExtendedABCWidthsCache[i].abc.b;
  428. c = m_ExtendedABCWidthsCache[i].abc.c;
  429. return;
  430. }
  431. // not in the cache, get from windows (this call is a little slow)
  432. ABC abc;
  433. if (::GetCharABCWidthsW(m_hDC, ch, ch, &abc) || ::GetCharABCWidthsA(m_hDC, ch, ch, &abc))
  434. {
  435. a = abc.abcA;
  436. b = abc.abcB;
  437. c = abc.abcC;
  438. }
  439. else
  440. {
  441. // wide character version failed, try the old api function
  442. SIZE size;
  443. char mbcs[6] = { 0 };
  444. wchar_t wch = ch;
  445. ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL);
  446. if (::GetTextExtentPoint32(m_hDC, mbcs, strlen(mbcs), &size))
  447. {
  448. a = c = 0;
  449. b = size.cx;
  450. }
  451. else
  452. {
  453. // failed to get width, just use the max width
  454. a = c = 0;
  455. b = m_iMaxCharWidth;
  456. }
  457. }
  458. // add to the cache
  459. finder.abc.a = a - m_iBlur - m_iOutlineSize;
  460. finder.abc.b = b + ((m_iBlur + m_iOutlineSize) * 2) + m_iDropShadowOffset;
  461. finder.abc.c = c - m_iBlur - m_iDropShadowOffset - m_iOutlineSize;
  462. m_ExtendedABCWidthsCache.Insert(finder);
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose: returns the height of the font, in pixels
  466. //-----------------------------------------------------------------------------
  467. int CWin32Font::GetHeight()
  468. {
  469. Assert( IsValid() );
  470. return m_iHeight;
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Purpose: returns the ascent of the font, in pixels (ascent=units above the base line)
  474. //-----------------------------------------------------------------------------
  475. int CWin32Font::GetAscent()
  476. {
  477. Assert( IsValid() );
  478. return m_iAscent;
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Purpose: returns the maximum width of a character, in pixels
  482. //-----------------------------------------------------------------------------
  483. int CWin32Font::GetMaxCharWidth()
  484. {
  485. Assert( IsValid() );
  486. return m_iMaxCharWidth;
  487. }
  488. //-----------------------------------------------------------------------------
  489. // Purpose: returns the flags used to make this font, used by the dynamic resizing code
  490. //-----------------------------------------------------------------------------
  491. int CWin32Font::GetFlags()
  492. {
  493. return m_iFlags;
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose: Comparison function for abc widths storage
  497. //-----------------------------------------------------------------------------
  498. bool CWin32Font::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs)
  499. {
  500. return lhs.wch < rhs.wch;
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: Get the kerned size of a char, for win32 just pass thru for now
  504. //-----------------------------------------------------------------------------
  505. void CWin32Font::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA, float &abcC )
  506. {
  507. int a,b,c;
  508. GetCharABCWidths(ch, a, b, c );
  509. wide = ( a + b + c);
  510. abcA = a;
  511. abcC = c;
  512. }