diff --git a/CMakeLists.txt b/CMakeLists.txt index 9da614e69..777475395 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1009,8 +1009,9 @@ set(EXPECTED_DATA editor/winter_main.rules emoticons.png file_icons.png - fonts/DejavuWenQuanYiMicroHei.ttf + fonts/DejaVuSans.ttf fonts/Icons.ttf + fonts/SourceHanSansSC-Regular.otf game.png gui_buttons.png gui_cursor.png @@ -1254,7 +1255,7 @@ set(EXPECTED_DATA wordlist.txt ) -set_glob(DATA GLOB_RECURSE "frag;json;map;pem;png;rules;ttf;txt;vert;wv" data ${EXPECTED_DATA}) +set_glob(DATA GLOB_RECURSE "frag;json;map;otf;png;rules;ttf;txt;vert;wv" data ${EXPECTED_DATA}) ######################################################################## # COPY DATA AND DLLS diff --git a/data/fonts/DejaVuSans.ttf b/data/fonts/DejaVuSans.ttf new file mode 100644 index 000000000..444c22d6b Binary files /dev/null and b/data/fonts/DejaVuSans.ttf differ diff --git a/data/fonts/DejavuWenQuanYiMicroHei.ttf b/data/fonts/DejavuWenQuanYiMicroHei.ttf deleted file mode 100644 index 1b80f13f6..000000000 Binary files a/data/fonts/DejavuWenQuanYiMicroHei.ttf and /dev/null differ diff --git a/data/fonts/SourceHanSansSC-Regular.otf b/data/fonts/SourceHanSansSC-Regular.otf new file mode 100644 index 000000000..88e3a3546 Binary files /dev/null and b/data/fonts/SourceHanSansSC-Regular.otf differ diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 373a44427..1495b1931 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -3837,8 +3837,10 @@ void CClient::ToggleWindowVSync() void CClient::LoadFont() { static CFont *pDefaultFont = 0; + static bool LoadedFallbackFont = false; char aFilename[512]; - const char *pFontFile = "fonts/DejavuWenQuanYiMicroHei.ttf"; + const char *pFontFile = "fonts/DejaVuSans.ttf"; + const char *pFallbackFontFile = "fonts/SourceHanSansSC-Regular.otf"; IOHANDLE File = Storage()->OpenFile(pFontFile, IOFLAG_READ, IStorage::TYPE_ALL, aFilename, sizeof(aFilename)); if(File) { @@ -3847,10 +3849,22 @@ void CClient::LoadFont() pDefaultFont = pTextRender->GetFont(aFilename); if(pDefaultFont == NULL) pDefaultFont = pTextRender->LoadFont(aFilename); + + File = Storage()->OpenFile(pFallbackFontFile, IOFLAG_READ, IStorage::TYPE_ALL, aFilename, sizeof(aFilename)); + if(File) + { + io_close(File); + IEngineTextRender *pTextRender = Kernel()->RequestInterface(); + LoadedFallbackFont = pTextRender->LoadFallbackFont(pDefaultFont, aFilename); + } + Kernel()->RequestInterface()->SetDefaultFont(pDefaultFont); } if(!pDefaultFont) m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", "failed to load font. filename='%s'", pFontFile); + + if(!LoadedFallbackFont) + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", "failed to load the fallback font. filename='%s'", pFallbackFontFile); } void CClient::Notify(const char *pTitle, const char *pMessage) diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp index 80f8e7ad2..ca98ef31f 100644 --- a/src/engine/client/text.cpp +++ b/src/engine/client/text.cpp @@ -100,6 +100,15 @@ public: char m_aFilename[512]; FT_Face m_FtFace; + + struct SFontFallBack + { + char m_aFilename[512]; + FT_Face m_FtFace; + }; + + std::vector m_FtFallbackFonts; + CFontSizeData m_aFontSizes[NUM_FONT_SIZES]; IGraphics::CTextureHandle m_aTextures[2]; @@ -433,31 +442,49 @@ class CTextRender : public IEngineTextRender int y = 1; unsigned int px, py; - FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, pSizeData->m_FontSize); + FT_Face FtFace = pFont->m_FtFace; + + FT_Set_Pixel_Sizes(FtFace, 0, pSizeData->m_FontSize); FT_UInt GlyphIndex = 0; - if(pFont->m_FtFace->charmap) - GlyphIndex = FT_Get_Char_Index(pFont->m_FtFace, (FT_ULong)Chr); + if(FtFace->charmap) + GlyphIndex = FT_Get_Char_Index(FtFace, (FT_ULong)Chr); if(GlyphIndex == 0) { - const int ReplacementChr = 0x25a1; // White square to indicate missing glyph - GlyphIndex = FT_Get_Char_Index(pFont->m_FtFace, (FT_ULong)ReplacementChr); + for(CFont::SFontFallBack& FallbackFont : pFont->m_FtFallbackFonts) + { + FtFace = FallbackFont.m_FtFace; + FT_Set_Pixel_Sizes(FtFace, 0, pSizeData->m_FontSize); + + if(FtFace->charmap) + GlyphIndex = FT_Get_Char_Index(FtFace, (FT_ULong)Chr); + + if(GlyphIndex != 0) + break; + } if(GlyphIndex == 0) { - dbg_msg("pFont", "font has no glyph for either %d or replacement char %d", Chr, ReplacementChr); - return; + const int ReplacementChr = 0x25a1; // White square to indicate missing glyph + FtFace = pFont->m_FtFace; + GlyphIndex = FT_Get_Char_Index(FtFace, (FT_ULong)ReplacementChr); + + if(GlyphIndex == 0) + { + dbg_msg("pFont", "font has no glyph for either %d or replacement char %d", Chr, ReplacementChr); + return; + } } } - if(FT_Load_Glyph(pFont->m_FtFace, GlyphIndex, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP)) + if(FT_Load_Glyph(FtFace, GlyphIndex, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP)) { dbg_msg("pFont", "error loading glyph %d", Chr); return; } - pBitmap = &pFont->m_FtFace->glyph->bitmap; // ignore_convention + pBitmap = &FtFace->glyph->bitmap; // ignore_convention // adjust spacing int OutlineThickness = AdjustOutlineThicknessToFontSize(1, pSizeData->m_FontSize); @@ -515,9 +542,9 @@ class CTextRender : public IEngineTextRender pFontchr->m_ID = Chr; pFontchr->m_Height = Height; pFontchr->m_Width = Width; - pFontchr->m_OffsetX = (pFont->m_FtFace->glyph->metrics.horiBearingX >> 6); // ignore_convention - pFontchr->m_OffsetY = -((pFont->m_FtFace->glyph->metrics.height >> 6) - (pFont->m_FtFace->glyph->metrics.horiBearingY >> 6)); - pFontchr->m_AdvanceX = (pFont->m_FtFace->glyph->advance.x>>6); // ignore_convention + pFontchr->m_OffsetX = (FtFace->glyph->metrics.horiBearingX >> 6); // ignore_convention + pFontchr->m_OffsetY = -((FtFace->glyph->metrics.height >> 6) - (FtFace->glyph->metrics.horiBearingY >> 6)); + pFontchr->m_AdvanceX = (FtFace->glyph->advance.x>>6); // ignore_convention pFontchr->m_aUVs[0] = X; pFontchr->m_aUVs[1] = Y; @@ -545,12 +572,6 @@ class CTextRender : public IEngineTextRender } } - // must only be called from the rendering function as the pFont must be set to the correct size - void RenderSetup(CFont *pFont, int size) - { - FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, size); - } - float Kerning(CFont *pFont, FT_UInt GlyphIndexLeft, FT_UInt GlyphIndexRight) { FT_Vector Kerning = {0,0}; @@ -672,7 +693,23 @@ public: m_Fonts.push_back(pFont); return pFont; - }; + } + + virtual bool LoadFallbackFont(CFont* pFont, const char *pFilename) + { + CFont::SFontFallBack FallbackFont; + str_copy(FallbackFont.m_aFilename, pFilename, sizeof(FallbackFont.m_aFilename)); + + if(FT_New_Face(m_FTLibrary, pFilename, 0, &FallbackFont.m_FtFace) == 0) + { + dbg_msg("textrender", "loaded fallback font from '%s'", pFilename); + pFont->m_FtFallbackFonts.emplace_back(std::move(FallbackFont)); + + return true; + } + + return false; + } virtual CFont *GetFont(int FontIndex) { @@ -703,6 +740,12 @@ public: m_Fonts.pop_back(); FT_Done_Face(pFont->m_FtFace); + + for(CFont::SFontFallBack& FallbackFont : pFont->m_FtFallbackFonts) + { + FT_Done_Face(FallbackFont.m_FtFace); + } + delete pFont; } } diff --git a/src/engine/textrender.h b/src/engine/textrender.h index 144de8697..3212648e0 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -77,6 +77,7 @@ public: virtual void SetCursor(CTextCursor *pCursor, float x, float y, float FontSize, int Flags) = 0; virtual CFont *LoadFont(const char *pFilename) = 0; + virtual bool LoadFallbackFont(CFont* pFont, const char *pFilename) = 0; virtual CFont *GetFont(int FontIndex) = 0; virtual CFont *GetFont(const char *pFilename) = 0; virtual void DestroyFont(CFont *pFont) = 0; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 807cd7873..823a8755e 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -1329,7 +1329,6 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) { str_copy(g_Config.m_ClLanguagefile, s_Languages[s_SelectedLanguage].m_FileName, sizeof(g_Config.m_ClLanguagefile)); g_Localization.Load(s_Languages[s_SelectedLanguage].m_FileName, Storage(), Console()); - Client()->LoadFont(); } }