]> git.lizzy.rs Git - minetest.git/blobdiff - src/client/fontengine.cpp
Fix '[combine' when EVDF_TEXTURE_NPOT is disabled. (#12187)
[minetest.git] / src / client / fontengine.cpp
index 858d6780e9773849a80c16ccb20a2ebebb346964..ad8305b4583e9d7a39c1649f8103bec08b4bd4be 100644 (file)
@@ -24,10 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "filesys.h"
 #include "gettext.h"
-
-#if USE_FREETYPE
 #include "irrlicht_changes/CGUITTFont.h"
-#endif
 
 /** maximum size distance for getting a "similar" font size */
 #define MAX_FONT_SIZE_OFFSET 10
@@ -42,38 +39,32 @@ static void font_setting_changed(const std::string &name, void *userdata)
 }
 
 /******************************************************************************/
-FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
-       m_settings(main_settings),
+FontEngine::FontEngine(gui::IGUIEnvironment* env) :
        m_env(env)
 {
-
        for (u32 &i : m_default_size) {
-               i = (FontMode) FONT_SIZE_UNSPECIFIED;
+               i = FONT_SIZE_UNSPECIFIED;
        }
 
-       assert(m_settings != NULL); // pre-condition
+       assert(g_settings != NULL); // pre-condition
        assert(m_env != NULL); // pre-condition
        assert(m_env->getSkin() != NULL); // pre-condition
 
        readSettings();
 
-       if (m_currentMode == FM_Standard) {
-               m_settings->registerChangedCallback("font_size", font_setting_changed, NULL);
-               m_settings->registerChangedCallback("font_path", font_setting_changed, NULL);
-               m_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL);
-               m_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL);
-       }
-       else if (m_currentMode == FM_Fallback) {
-               m_settings->registerChangedCallback("fallback_font_size", font_setting_changed, NULL);
-               m_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL);
-               m_settings->registerChangedCallback("fallback_font_shadow", font_setting_changed, NULL);
-               m_settings->registerChangedCallback("fallback_font_shadow_alpha", font_setting_changed, NULL);
-       }
+       const char *settings[] = {
+               "font_size", "font_bold", "font_italic", "font_size_divisible_by",
+               "mono_font_size", "mono_font_size_divisible_by",
+               "font_shadow", "font_shadow_alpha",
+               "font_path", "font_path_bold", "font_path_italic", "font_path_bold_italic",
+               "mono_font_path", "mono_font_path_bold", "mono_font_path_italic",
+               "mono_font_path_bold_italic",
+               "fallback_font_path",
+               "screen_dpi", "gui_scaling",
+       };
 
-       m_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL);
-       m_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL);
-       m_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL);
-       m_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL);
+       for (auto name : settings)
+               g_settings->registerChangedCallback(name, font_setting_changed, NULL);
 }
 
 /******************************************************************************/
@@ -85,83 +76,80 @@ FontEngine::~FontEngine()
 /******************************************************************************/
 void FontEngine::cleanCache()
 {
+       RecursiveMutexAutoLock l(m_font_mutex);
+
        for (auto &font_cache_it : m_font_cache) {
 
                for (auto &font_it : font_cache_it) {
                        font_it.second->drop();
-                       font_it.second = NULL;
+                       font_it.second = nullptr;
                }
                font_cache_it.clear();
        }
 }
 
 /******************************************************************************/
-irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode)
+irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec)
+{
+       return getFont(spec, false);
+}
+
+irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec, bool may_fail)
 {
-       if (mode == FM_Unspecified) {
-               mode = m_currentMode;
-       } else if (m_currentMode == FM_Simple) {
-               // Freetype disabled -> Force simple mode
-               mode = (mode == FM_Mono || mode == FM_SimpleMono) ?
-                                FM_SimpleMono : FM_Simple;
+       if (spec.mode == FM_Unspecified) {
+               spec.mode = m_currentMode;
+       } else if (spec.mode == _FM_Fallback) {
+               // Fallback font doesn't support these
+               spec.bold = false;
+               spec.italic = false;
        }
 
        // Fallback to default size
-       if (font_size == FONT_SIZE_UNSPECIFIED)
-               font_size = m_default_size[mode];
-
-       const auto &cache = m_font_cache[mode];
-       if (cache.find(font_size) == cache.end()) {
-               if (mode == FM_Simple || mode == FM_SimpleMono)
-                       initSimpleFont(font_size, mode);
-               else
-                       initFont(font_size, mode);
+       if (spec.size == FONT_SIZE_UNSPECIFIED)
+               spec.size = m_default_size[spec.mode];
+
+       RecursiveMutexAutoLock l(m_font_mutex);
+
+       const auto &cache = m_font_cache[spec.getHash()];
+       auto it = cache.find(spec.size);
+       if (it != cache.end())
+               return it->second;
+
+       // Font does not yet exist
+       gui::IGUIFont *font = initFont(spec);
+
+       if (!font && !may_fail) {
+               errorstream << "Minetest cannot continue without a valid font. "
+                       "Please correct the 'font_path' setting or install the font "
+                       "file in the proper location." << std::endl;
+               abort();
        }
 
-       const auto &font = cache.find(font_size);
-       return font != cache.end() ? font->second : nullptr;
+       m_font_cache[spec.getHash()][spec.size] = font;
+
+       return font;
 }
 
 /******************************************************************************/
-unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode)
+unsigned int FontEngine::getTextHeight(const FontSpec &spec)
 {
-       irr::gui::IGUIFont* font = getFont(font_size, mode);
-
-       // use current skin font as fallback
-       if (font == NULL) {
-               font = m_env->getSkin()->getFont();
-       }
-       FATAL_ERROR_IF(font == NULL, "Could not get skin font");
+       gui::IGUIFont *font = getFont(spec);
 
        return font->getDimension(L"Some unimportant example String").Height;
 }
 
 /******************************************************************************/
-unsigned int FontEngine::getTextWidth(const std::wstring& text,
-               unsigned int font_size, FontMode mode)
+unsigned int FontEngine::getTextWidth(const std::wstring &text, const FontSpec &spec)
 {
-       irr::gui::IGUIFont* font = getFont(font_size, mode);
-
-       // use current skin font as fallback
-       if (font == NULL) {
-               font = m_env->getSkin()->getFont();
-       }
-       FATAL_ERROR_IF(font == NULL, "Could not get font");
+       gui::IGUIFont *font = getFont(spec);
 
        return font->getDimension(text.c_str()).Width;
 }
 
-
 /** get line height for a specific font (including empty room between lines) */
-unsigned int FontEngine::getLineHeight(unsigned int font_size, FontMode mode)
+unsigned int FontEngine::getLineHeight(const FontSpec &spec)
 {
-       irr::gui::IGUIFont* font = getFont(font_size, mode);
-
-       // use current skin font as fallback
-       if (font == NULL) {
-               font = m_env->getSkin()->getFont();
-       }
-       FATAL_ERROR_IF(font == NULL, "Could not get font");
+       gui::IGUIFont *font = getFont(spec);
 
        return font->getDimension(L"Some unimportant example String").Height
                        + font->getKerningHeight();
@@ -173,22 +161,23 @@ unsigned int FontEngine::getDefaultFontSize()
        return m_default_size[m_currentMode];
 }
 
+unsigned int FontEngine::getFontSize(FontMode mode)
+{
+       if (mode == FM_Unspecified)
+               return m_default_size[FM_Standard];
+
+       return m_default_size[mode];
+}
+
 /******************************************************************************/
 void FontEngine::readSettings()
 {
-       if (USE_FREETYPE && g_settings->getBool("freetype")) {
-               m_default_size[FM_Standard] = m_settings->getU16("font_size");
-               m_default_size[FM_Fallback] = m_settings->getU16("fallback_font_size");
-               m_default_size[FM_Mono]     = m_settings->getU16("mono_font_size");
-
-               m_currentMode = is_yes(gettext("needs_fallback_font")) ?
-                               FM_Fallback : FM_Standard;
-       } else {
-               m_currentMode = FM_Simple;
-       }
+       m_default_size[FM_Standard]  = g_settings->getU16("font_size");
+       m_default_size[_FM_Fallback] = g_settings->getU16("font_size");
+       m_default_size[FM_Mono]      = g_settings->getU16("mono_font_size");
 
-       m_default_size[FM_Simple]       = m_settings->getU16("font_size");
-       m_default_size[FM_SimpleMono]   = m_settings->getU16("mono_font_size");
+       m_default_bold = g_settings->getBool("font_bold");
+       m_default_italic = g_settings->getBool("font_italic");
 
        cleanCache();
        updateFontCache();
@@ -199,22 +188,9 @@ void FontEngine::readSettings()
 void FontEngine::updateSkin()
 {
        gui::IGUIFont *font = getFont();
+       assert(font);
 
-       if (font)
-               m_env->getSkin()->setFont(font);
-       else
-               errorstream << "FontEngine: Default font file: " <<
-                               "\n\t\"" << m_settings->get("font_path") << "\"" <<
-                               "\n\trequired for current screen configuration was not found" <<
-                               " or was invalid file format." <<
-                               "\n\tUsing irrlicht default font." << std::endl;
-
-       // If we did fail to create a font our own make irrlicht find a default one
-       font = m_env->getSkin()->getFont();
-       FATAL_ERROR_IF(font == NULL, "Could not create/get font");
-
-       u32 text_height = font->getDimension(L"Hello, world!").Height;
-       infostream << "text_height=" << text_height << std::endl;
+       m_env->getSkin()->setFont(font);
 }
 
 /******************************************************************************/
@@ -226,144 +202,67 @@ void FontEngine::updateFontCache()
 }
 
 /******************************************************************************/
-void FontEngine::initFont(unsigned int basesize, FontMode mode)
+gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
 {
-       assert(mode != FM_Unspecified);
-       assert(basesize != FONT_SIZE_UNSPECIFIED);
-
-       if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end())
-               return;
-
+       assert(spec.mode != FM_Unspecified);
+       assert(spec.size != FONT_SIZE_UNSPECIFIED);
 
        std::string setting_prefix = "";
-
-       switch (mode) {
-               case FM_Fallback:
-                       setting_prefix = "fallback_";
-                       break;
-               case FM_Mono:
-               case FM_SimpleMono:
-                       setting_prefix = "mono_";
-                       break;
-               default:
-                       break;
+       if (spec.mode == FM_Mono)
+               setting_prefix = "mono_";
+
+       std::string setting_suffix = "";
+       if (spec.bold)
+               setting_suffix.append("_bold");
+       if (spec.italic)
+               setting_suffix.append("_italic");
+
+       u32 size = std::max<u32>(spec.size * RenderingEngine::getDisplayDensity() *
+                       g_settings->getFloat("gui_scaling"), 1);
+
+       // Constrain the font size to a certain multiple, if necessary
+       u16 divisible_by = g_settings->getU16(setting_prefix + "font_size_divisible_by");
+       if (divisible_by > 1) {
+               size = std::max<u32>(
+                               std::round((double)size / divisible_by) * divisible_by, divisible_by);
        }
 
-       u32 size = std::floor(RenderingEngine::getDisplayDensity() *
-                       m_settings->getFloat("gui_scaling") * basesize);
-       if (size == 0) {
-               errorstream << "FontEngine: attempt to use font size 0" << std::endl;
-               errorstream << "  display density: " << RenderingEngine::getDisplayDensity() << std::endl;
-               abort();
-       }
+       sanity_check(size != 0);
 
        u16 font_shadow       = 0;
        u16 font_shadow_alpha = 0;
        g_settings->getU16NoEx(setting_prefix + "font_shadow", font_shadow);
-       g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha", font_shadow_alpha);
+       g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha",
+                       font_shadow_alpha);
+
+       std::string path_setting;
+       if (spec.mode == _FM_Fallback)
+               path_setting = "fallback_font_path";
+       else
+               path_setting = setting_prefix + "font_path" + setting_suffix;
 
        std::string fallback_settings[] = {
-               m_settings->get(setting_prefix + "font_path"),
-               m_settings->get("fallback_font_path"),
-               m_settings->getDefault(setting_prefix + "font_path")
+               g_settings->get(path_setting),
+               Settings::getLayer(SL_DEFAULTS)->get(path_setting)
        };
 
-#if USE_FREETYPE
        for (const std::string &font_path : fallback_settings) {
-               irr::gui::IGUIFont *font = gui::CGUITTFont::createTTFont(m_env,
+               gui::CGUITTFont *font = gui::CGUITTFont::createTTFont(m_env,
                                font_path.c_str(), size, true, true, font_shadow,
                                font_shadow_alpha);
 
-               if (font) {
-                       m_font_cache[mode][basesize] = font;
-                       return;
-               }
-
-               errorstream << "FontEngine: Cannot load '" << font_path <<
+               if (!font) {
+                       errorstream << "FontEngine: Cannot load '" << font_path <<
                                "'. Trying to fall back to another path." << std::endl;
-       }
-
-
-       // give up
-       errorstream << "minetest can not continue without a valid font. "
-                       "Please correct the 'font_path' setting or install the font "
-                       "file in the proper location" << std::endl;
-#else
-       errorstream << "FontEngine: Tried to load freetype fonts but Minetest was"
-                       " not compiled with that library." << std::endl;
-#endif
-       abort();
-}
-
-/** initialize a font without freetype */
-void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
-{
-       assert(mode == FM_Simple || mode == FM_SimpleMono);
-
-       const std::string &font_path = m_settings->get(
-                       (mode == FM_SimpleMono) ? "mono_font_path" : "font_path");
-
-       size_t pos_dot = font_path.find_last_of('.');
-       std::string basename = font_path;
-       std::string ending = lowercase(font_path.substr(pos_dot));
-
-       if (ending == ".ttf") {
-               errorstream << "FontEngine: Found font \"" << font_path
-                               << "\" but freetype is not available." << std::endl;
-               return;
-       }
-
-       if (ending == ".xml" || ending == ".png")
-               basename = font_path.substr(0, pos_dot);
-
-       if (basesize == FONT_SIZE_UNSPECIFIED)
-               basesize = DEFAULT_FONT_SIZE;
-
-       u32 size = std::floor(
-                       RenderingEngine::getDisplayDensity() *
-                       m_settings->getFloat("gui_scaling") *
-                       basesize);
-
-       irr::gui::IGUIFont *font = nullptr;
-       std::string font_extensions[] = { ".png", ".xml" };
-
-       // Find nearest matching font scale
-       // Does a "zig-zag motion" (positibe/negative), from 0 to MAX_FONT_SIZE_OFFSET
-       for (s32 zoffset = 0; zoffset < MAX_FONT_SIZE_OFFSET * 2; zoffset++) {
-               std::stringstream path;
-
-               // LSB to sign
-               s32 sign = (zoffset & 1) ? -1 : 1;
-               s32 offset = zoffset >> 1;
-
-               for (const std::string &ext : font_extensions) {
-                       path.str(""); // Clear
-                       path << basename << "_" << (size + offset * sign) << ext;
-
-                       if (!fs::PathExists(path.str())) 
-                               continue;
-
-                       font = m_env->getFont(path.str().c_str());
-
-                       if (font) {
-                               verbosestream << "FontEngine: found font: " << path.str() << std::endl;
-                               break;
-                       }
+                       continue;
                }
 
-               if (font)
-                       break;
-       }
-
-       // try name direct
-       if (font == NULL) {
-               if (fs::PathExists(font_path)) {
-                       font = m_env->getFont(font_path.c_str());
-                       if (font)
-                               verbosestream << "FontEngine: found font: " << font_path << std::endl;
+               if (spec.mode != _FM_Fallback) {
+                       FontSpec spec2(spec);
+                       spec2.mode = _FM_Fallback;
+                       font->setFallback(getFont(spec2, true));
                }
+               return font;
        }
-
-       if (font)
-               m_font_cache[mode][basesize] = font;
+       return nullptr;
 }