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/Win32Font.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/Win32Font.cpp')
| -rw-r--r-- | vgui2/vgui_surfacelib/Win32Font.cpp | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/vgui2/vgui_surfacelib/Win32Font.cpp b/vgui2/vgui_surfacelib/Win32Font.cpp new file mode 100644 index 0000000..5ec02ac --- /dev/null +++ b/vgui2/vgui_surfacelib/Win32Font.cpp @@ -0,0 +1,638 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <malloc.h> +#include "vgui_surfacelib/Win32Font.h" +#include <tier0/dbg.h> +#include <vgui/ISurface.h> +#include <tier0/mem.h> +#include <utlbuffer.h> +#include "FontEffects.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static OSVERSIONINFO s_OsVersionInfo; +static bool s_bOsVersionInitialized = false; +bool s_bSupportsUnicode = false; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CWin32Font::CWin32Font() : m_ExtendedABCWidthsCache(256, 0, &ExtendedABCWidthsCacheLessFunc) +{ + m_szName = UTL_INVAL_SYMBOL; + m_iTall = 0; + m_iWeight = 0; + m_iHeight = 0; + m_iAscent = 0; + m_iFlags = 0; + m_iMaxCharWidth = 0; + m_hFont = NULL; + m_hDC = NULL; + m_hDIB = NULL; + m_bAntiAliased = false; + m_bUnderlined = false; + m_iBlur = 0; + m_iScanLines = 0; + m_bRotary = false; + m_bAdditive = false; + m_rgiBitmapSize[ 0 ] = m_rgiBitmapSize[ 1 ] = 0; + +#if defined( _X360 ) + Q_memset( m_ABCWidthsCache, 0, sizeof( m_ABCWidthsCache ) ); +#endif + + m_ExtendedABCWidthsCache.EnsureCapacity( 128 ); + + if ( !s_bOsVersionInitialized ) + { + // get the operating system version + s_bOsVersionInitialized = true; + memset(&s_OsVersionInfo, 0, sizeof(s_OsVersionInfo)); + s_OsVersionInfo.dwOSVersionInfoSize = sizeof(s_OsVersionInfo); + GetVersionEx(&s_OsVersionInfo); + + if (s_OsVersionInfo.dwMajorVersion >= 5) + { + s_bSupportsUnicode = true; + } + else + { + s_bSupportsUnicode = false; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CWin32Font::~CWin32Font() +{ + if ( m_hFont ) + ::DeleteObject( m_hFont ); + if ( m_hDC ) + ::DeleteDC( m_hDC ); + if ( m_hDIB ) + ::DeleteObject( m_hDIB ); +} + +//----------------------------------------------------------------------------- +// Purpose: Font iteration callback function +// used to determine whether or not a font exists on the system +//----------------------------------------------------------------------------- +extern bool g_bFontFound = false; +int CALLBACK FontEnumProc( + const LOGFONT *lpelfe, // logical-font data + const TEXTMETRIC *lpntme, // physical-font data + DWORD FontType, // type of font + LPARAM lParam ) // application-defined data +{ + g_bFontFound = true; + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: creates the font from windows. returns false if font does not exist in the OS. +//----------------------------------------------------------------------------- +bool CWin32Font::Create(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags) +{ + // setup font properties + m_szName = windowsFontName; + m_iTall = tall; + m_iWeight = weight; + m_iFlags = flags; + m_bAntiAliased = (flags & vgui::ISurface::FONTFLAG_ANTIALIAS) ? 1 : 0; + 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_iBlur = blur; + m_iScanLines = scanlines; + m_bRotary = (flags & vgui::ISurface::FONTFLAG_ROTARY) ? 1 : 0; + m_bAdditive = (flags & vgui::ISurface::FONTFLAG_ADDITIVE) ? 1 : 0; + + int charset = (flags & vgui::ISurface::FONTFLAG_SYMBOL) ? SYMBOL_CHARSET : ANSI_CHARSET; + + // hack for japanese win98 support + if ( !stricmp( windowsFontName, "win98japanese" ) ) + { + // use any font that contains the japanese charset + charset = SHIFTJIS_CHARSET; + m_szName = "Tahoma"; + } + + // create our windows device context + m_hDC = ::CreateCompatibleDC(NULL); + Assert( m_hDC ); + + // see if the font exists on the system + LOGFONT logfont; + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfPitchAndFamily = 0; + strcpy(logfont.lfFaceName, m_szName.String()); + g_bFontFound = false; + ::EnumFontFamiliesEx(m_hDC, &logfont, &FontEnumProc, 0, 0); + if (!g_bFontFound) + { + // needs to go to a fallback + m_szName = UTL_INVAL_SYMBOL; + return false; + } + + m_hFont = ::CreateFontA(tall, 0, 0, 0, + m_iWeight, + flags & vgui::ISurface::FONTFLAG_ITALIC, + flags & vgui::ISurface::FONTFLAG_UNDERLINE, + flags & vgui::ISurface::FONTFLAG_STRIKEOUT, + charset, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + m_bAntiAliased ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY, + DEFAULT_PITCH | FF_DONTCARE, + windowsFontName); + if (!m_hFont) + { + Error("Couldn't create windows font '%s'\n", windowsFontName); + m_szName = UTL_INVAL_SYMBOL; + return false; + } + + // set as the active font + ::SetMapMode(m_hDC, MM_TEXT); + ::SelectObject(m_hDC, m_hFont); + ::SetTextAlign(m_hDC, TA_LEFT | TA_TOP | TA_UPDATECP); + + // get info about the font + ::TEXTMETRIC tm; + memset( &tm, 0, sizeof( tm ) ); + if ( !GetTextMetrics(m_hDC, &tm) ) + { + m_szName = UTL_INVAL_SYMBOL; + return false; + } + + m_iHeight = tm.tmHeight + m_iDropShadowOffset + 2 * m_iOutlineSize; + m_iMaxCharWidth = tm.tmMaxCharWidth; + m_iAscent = tm.tmAscent; + + // code for rendering to a bitmap + m_rgiBitmapSize[0] = tm.tmMaxCharWidth + m_iOutlineSize * 2; + m_rgiBitmapSize[1] = tm.tmHeight + m_iDropShadowOffset + m_iOutlineSize * 2; + + ::BITMAPINFOHEADER header; + memset(&header, 0, sizeof(header)); + header.biSize = sizeof(header); + header.biWidth = m_rgiBitmapSize[0]; + header.biHeight = -m_rgiBitmapSize[1]; + header.biPlanes = 1; + header.biBitCount = 32; + header.biCompression = BI_RGB; + + m_hDIB = ::CreateDIBSection(m_hDC, (BITMAPINFO*)&header, DIB_RGB_COLORS, (void**)(&m_pBuf), NULL, 0); + ::SelectObject(m_hDC, m_hDIB); + +#if defined( _X360 ) + // get char spacing + // a is space before character (can be negative) + // b is the width of the character + // c is the space after the character + memset(m_ABCWidthsCache, 0, sizeof(m_ABCWidthsCache)); + ABC abc[ABCWIDTHS_CACHE_SIZE]; + Assert(ABCWIDTHS_CACHE_SIZE <= 256); + if (::GetCharABCWidthsW(m_hDC, 0, ABCWIDTHS_CACHE_SIZE - 1, &abc[0]) || ::GetCharABCWidthsA(m_hDC, 0, ABCWIDTHS_CACHE_SIZE - 1, &abc[0])) + { + // copy out into our formated structure + for (int i = 0; i < ABCWIDTHS_CACHE_SIZE; i++) + { + m_ABCWidthsCache[i].a = abc[i].abcA - m_iBlur - m_iOutlineSize; + m_ABCWidthsCache[i].b = abc[i].abcB + ((m_iBlur + m_iOutlineSize) * 2) + m_iDropShadowOffset; + m_ABCWidthsCache[i].c = abc[i].abcC - m_iBlur - m_iDropShadowOffset - m_iOutlineSize; + } + } + else + { + Warning("GetCharABCWidths() failed for windows font '%s'\n", windowsFontName); + + // since that failed, it must be fixed width, zero everything so a and c will be zeros, then + // fill b with the value from TEXTMETRIC + for (int i = 0; i < ABCWIDTHS_CACHE_SIZE; i++) + { + // fallback to old method, no underhangs/overhangs (a/c) + SIZE size; + char mbcs[6] = { 0 }; + wchar_t wch = (wchar_t)i; + ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL); + if (::GetTextExtentPoint32(m_hDC, mbcs, strlen(mbcs), &size)) + { + m_ABCWidthsCache[i].b = size.cx; + } + else + { + // failed to get width, just use the average width + m_ABCWidthsCache[i].b = (char)tm.tmAveCharWidth; + } + } + } +#endif + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: writes the char into the specified 32bpp texture +//----------------------------------------------------------------------------- +void CWin32Font::GetCharRGBA(wchar_t ch, int rgbaWide, int rgbaTall, unsigned char *rgba) +{ + int a, b, c; + GetCharABCWidths(ch, a, b, c); + + // set us up to render into our dib + ::SelectObject(m_hDC, m_hFont); + + int wide = b; + if ( m_bUnderlined ) + { + wide += ( a + c ); + } + + int tall = m_iHeight; + GLYPHMETRICS glyphMetrics; + MAT2 mat2 = { { 0, 1}, { 0, 0}, { 0, 0}, { 0, 1}}; + int bytesNeeded = 0; + + bool bShouldAntialias = m_bAntiAliased; + // filter out + if ( ch > 0x00FF && !(m_iFlags & vgui::ISurface::FONTFLAG_CUSTOM) ) + { + bShouldAntialias = false; + } + if ( !s_bSupportsUnicode ) + { + // win98 hack, don't antialias some characters that ::GetGlyphOutline() produces bad results for + if (ch == 'I' || ch == '1') + { + bShouldAntialias = false; + } + + // don't antialias big fonts at all (since win98 often produces bad results) + if (m_iHeight >= 13) + { + bShouldAntialias = false; + } + } + + + // only antialias latin characters, since it essentially always fails for asian characters + if (bShouldAntialias) + { + // try and get the glyph directly + ::SelectObject(m_hDC, m_hFont); + bytesNeeded = ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, 0, NULL, &mat2); + } + + if (bytesNeeded > 0) + { + // take it + unsigned char *lpbuf = (unsigned char *)_alloca(bytesNeeded); + ::GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &glyphMetrics, bytesNeeded, lpbuf, &mat2); + + // rows are on DWORD boundaries + wide = glyphMetrics.gmBlackBoxX; + while (wide % 4 != 0) + { + wide++; + } + + // see where we should start rendering + int pushDown = m_iAscent - glyphMetrics.gmptGlyphOrigin.y; + + // set where we start copying from + int xstart = 0; + + // don't copy the first set of pixels if the antialiased bmp is bigger than the char width + if ((int)glyphMetrics.gmBlackBoxX >= b + 2) + { + xstart = (glyphMetrics.gmBlackBoxX - b) / 2; + } + + // iterate through copying the generated dib into the texture + for (unsigned int j = 0; j < glyphMetrics.gmBlackBoxY; j++) + { + for (unsigned int i = xstart; i < glyphMetrics.gmBlackBoxX; i++) + { + int x = i - xstart + m_iBlur + m_iOutlineSize; + int y = j + pushDown; + if ((x < rgbaWide) && (y < rgbaTall)) + { + unsigned char grayscale = lpbuf[(j*wide+i)]; + + float r, g, b, a; + if (grayscale) + { + r = g = b = 1.0f; + a = (grayscale + 0) / 64.0f; + if (a > 1.0f) a = 1.0f; + } + else + { + r = g = b = a = 0.0f; + } + + // Don't want anything drawn for tab characters. + if (ch == '\t') + { + r = g = b = 0; + } + + unsigned char *dst = &rgba[(y*rgbaWide+x)*4]; + dst[0] = (unsigned char)(r * 255.0f); + dst[1] = (unsigned char)(g * 255.0f); + dst[2] = (unsigned char)(b * 255.0f); + dst[3] = (unsigned char)(a * 255.0f); + } + } + } + } + else + { + // use render-to-bitmap to get our font texture + ::SetBkColor(m_hDC, RGB(0, 0, 0)); + ::SetTextColor(m_hDC, RGB(255, 255, 255)); + ::SetBkMode(m_hDC, OPAQUE); + if ( m_bUnderlined ) + { + ::MoveToEx(m_hDC, 0, 0, NULL); + } + else + { + ::MoveToEx(m_hDC, -a, 0, NULL); + } + + // render the character + wchar_t wch = (wchar_t)ch; + + if (s_bSupportsUnicode) + { + // clear the background first + RECT rect = { 0, 0, wide, tall}; + ::ExtTextOutW( m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL ); + + // just use the unicode renderer + ::ExtTextOutW( m_hDC, 0, 0, 0, NULL, &wch, 1, NULL ); + } + else + { + // clear the background first (it may not get done automatically in win98/ME + RECT rect = { 0, 0, wide, tall}; + ::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + + // convert the character using the current codepage + char mbcs[6] = { 0 }; + ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL); + ::ExtTextOutA(m_hDC, 0, 0, 0, NULL, mbcs, strlen(mbcs), NULL); + } + + ::SetBkMode(m_hDC, TRANSPARENT); + + if (wide > m_rgiBitmapSize[0]) + { + wide = m_rgiBitmapSize[0]; + } + if (tall > m_rgiBitmapSize[1]) + { + tall = m_rgiBitmapSize[1]; + } + + // iterate through copying the generated dib into the texture + for (int j = (int)m_iOutlineSize; j < tall - (int)m_iOutlineSize; j++ ) + { + // only copy from within the dib, ignore the outline border we are artificially adding + for (int i = (int)m_iOutlineSize; i < wide - (int)m_iDropShadowOffset - (int)m_iOutlineSize; i++) + { + if ((i < rgbaWide) && (j < rgbaTall)) + { + unsigned char *src = &m_pBuf[(i + j*m_rgiBitmapSize[0])*4]; + unsigned char *dst = &rgba[(i + j*rgbaWide)*4]; + + // Don't want anything drawn for tab characters. + unsigned char r, g, b; + if ( ch == '\t' ) + { + r = g = b = 0; + } + else + { + r = src[0]; + g = src[1]; + b = src[2]; + } + + // generate alpha based on luminance conversion + dst[0] = r; + dst[1] = g; + dst[2] = b; + dst[3] = (unsigned char)((float)r * 0.34f + (float)g * 0.55f + (float)b * 0.11f); + } + } + } + + // if we have a dropshadow, we need to clean off the bottom row of pixels + // 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 + // the bottom row should guaranteed to be empty to fit the dropshadow + if ( m_iDropShadowOffset ) + { + unsigned char *dst = &rgba[((m_iHeight - 1) * rgbaWide) * 4]; + for (int i = 0; i < wide; i++) + { + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; + dst += 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: returns true if the font is equivalent to that specified +//----------------------------------------------------------------------------- +bool CWin32Font::IsEqualTo(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags) +{ + if ( !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 CWin32Font::IsValid() +{ + if ( m_szName.IsValid() && m_szName.String()[0] ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: set the font to be the one to currently draw with in the gdi +//----------------------------------------------------------------------------- +void CWin32Font::SetAsActiveFont(HDC hdc) +{ + Assert( IsValid() ); + ::SelectObject( hdc, m_hFont ); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the abc widths for a character +//----------------------------------------------------------------------------- +void CWin32Font::GetCharABCWidths(int ch, int &a, int &b, int &c) +{ + Assert( IsValid() ); +#if defined( _X360 ) + if (ch < ABCWIDTHS_CACHE_SIZE) + { + // use the cache entry + a = m_ABCWidthsCache[ch].a; + b = m_ABCWidthsCache[ch].b; + c = m_ABCWidthsCache[ch].c; + } + else +#endif + { + + // 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; + } + + // not in the cache, get from windows (this call is a little slow) + ABC abc; + if (::GetCharABCWidthsW(m_hDC, ch, ch, &abc) || ::GetCharABCWidthsA(m_hDC, ch, ch, &abc)) + { + a = abc.abcA; + b = abc.abcB; + c = abc.abcC; + } + else + { + // wide character version failed, try the old api function + SIZE size; + char mbcs[6] = { 0 }; + wchar_t wch = ch; + ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL); + if (::GetTextExtentPoint32(m_hDC, mbcs, strlen(mbcs), &size)) + { + a = c = 0; + b = size.cx; + } + else + { + // failed to get width, just use the max width + a = c = 0; + b = m_iMaxCharWidth; + } + } + + // add to the cache + finder.abc.a = a - m_iBlur - m_iOutlineSize; + finder.abc.b = b + ((m_iBlur + m_iOutlineSize) * 2) + m_iDropShadowOffset; + finder.abc.c = c - m_iBlur - m_iDropShadowOffset - m_iOutlineSize; + m_ExtendedABCWidthsCache.Insert(finder); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the height of the font, in pixels +//----------------------------------------------------------------------------- +int CWin32Font::GetHeight() +{ + Assert( IsValid() ); + return m_iHeight; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the requested height of the font +//----------------------------------------------------------------------------- +int CWin32Font::GetHeightRequested() +{ + assert(IsValid()); + return m_iTall; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the ascent of the font, in pixels (ascent=units above the base line) +//----------------------------------------------------------------------------- +int CWin32Font::GetAscent() +{ + Assert( IsValid() ); + return m_iAscent; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the maximum width of a character, in pixels +//----------------------------------------------------------------------------- +int CWin32Font::GetMaxCharWidth() +{ + Assert( IsValid() ); + return m_iMaxCharWidth; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the flags used to make this font, used by the dynamic resizing code +//----------------------------------------------------------------------------- +int CWin32Font::GetFlags() +{ + return m_iFlags; +} + +//----------------------------------------------------------------------------- +// Purpose: Comparison function for abc widths storage +//----------------------------------------------------------------------------- +bool CWin32Font::ExtendedABCWidthsCacheLessFunc(const abc_cache_t &lhs, const abc_cache_t &rhs) +{ + return lhs.wch < rhs.wch; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the kerned size of a char, for win32 just pass thru for now +//----------------------------------------------------------------------------- +void CWin32Font::GetKernedCharWidth( wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &abcA ) +{ + int a,b,c; + GetCharABCWidths(ch, a, b, c ); + wide = ( a + b + c); + abcA = a; +} + + |