3 Copyright (C) 2010-2014 sapier <sapier at gmx dot net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "fontengine.h"
22 #include "client/renderingengine.h"
29 #include "irrlicht_changes/CGUITTFont.h"
32 /** maximum size distance for getting a "similar" font size */
33 #define MAX_FONT_SIZE_OFFSET 10
35 /** reference to access font engine, has to be initialized by main */
36 FontEngine* g_fontengine = NULL;
38 /** callback to be used on change of font size setting */
39 static void font_setting_changed(const std::string &name, void *userdata)
41 g_fontengine->readSettings();
44 /******************************************************************************/
45 FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
46 m_settings(main_settings),
50 for (u32 &i : m_default_size) {
51 i = (FontMode) FONT_SIZE_UNSPECIFIED;
54 assert(m_settings != NULL); // pre-condition
55 assert(m_env != NULL); // pre-condition
56 assert(m_env->getSkin() != NULL); // pre-condition
60 if (m_currentMode == FM_Standard) {
61 m_settings->registerChangedCallback("font_size", font_setting_changed, NULL);
62 m_settings->registerChangedCallback("font_path", font_setting_changed, NULL);
63 m_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL);
64 m_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL);
66 else if (m_currentMode == FM_Fallback) {
67 m_settings->registerChangedCallback("fallback_font_size", font_setting_changed, NULL);
68 m_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL);
69 m_settings->registerChangedCallback("fallback_font_shadow", font_setting_changed, NULL);
70 m_settings->registerChangedCallback("fallback_font_shadow_alpha", font_setting_changed, NULL);
73 m_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL);
74 m_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL);
75 m_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL);
76 m_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL);
79 /******************************************************************************/
80 FontEngine::~FontEngine()
85 /******************************************************************************/
86 void FontEngine::cleanCache()
88 for (auto &font_cache_it : m_font_cache) {
90 for (auto &font_it : font_cache_it) {
91 font_it.second->drop();
92 font_it.second = NULL;
94 font_cache_it.clear();
98 /******************************************************************************/
99 irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode)
101 if (mode == FM_Unspecified) {
102 mode = m_currentMode;
103 } else if (m_currentMode == FM_Simple) {
104 // Freetype disabled -> Force simple mode
105 mode = (mode == FM_Mono || mode == FM_SimpleMono) ?
106 FM_SimpleMono : FM_Simple;
109 // Fallback to default size
110 if (font_size == FONT_SIZE_UNSPECIFIED)
111 font_size = m_default_size[mode];
113 const auto &cache = m_font_cache[mode];
114 if (cache.find(font_size) == cache.end()) {
115 if (mode == FM_Simple || mode == FM_SimpleMono)
116 initSimpleFont(font_size, mode);
118 initFont(font_size, mode);
121 const auto &font = cache.find(font_size);
122 return font != cache.end() ? font->second : nullptr;
125 /******************************************************************************/
126 unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode)
128 irr::gui::IGUIFont* font = getFont(font_size, mode);
130 // use current skin font as fallback
132 font = m_env->getSkin()->getFont();
134 FATAL_ERROR_IF(font == NULL, "Could not get skin font");
136 return font->getDimension(L"Some unimportant example String").Height;
139 /******************************************************************************/
140 unsigned int FontEngine::getTextWidth(const std::wstring& text,
141 unsigned int font_size, FontMode mode)
143 irr::gui::IGUIFont* font = getFont(font_size, mode);
145 // use current skin font as fallback
147 font = m_env->getSkin()->getFont();
149 FATAL_ERROR_IF(font == NULL, "Could not get font");
151 return font->getDimension(text.c_str()).Width;
155 /** get line height for a specific font (including empty room between lines) */
156 unsigned int FontEngine::getLineHeight(unsigned int font_size, FontMode mode)
158 irr::gui::IGUIFont* font = getFont(font_size, mode);
160 // use current skin font as fallback
162 font = m_env->getSkin()->getFont();
164 FATAL_ERROR_IF(font == NULL, "Could not get font");
166 return font->getDimension(L"Some unimportant example String").Height
167 + font->getKerningHeight();
170 /******************************************************************************/
171 unsigned int FontEngine::getDefaultFontSize()
173 return m_default_size[m_currentMode];
176 /******************************************************************************/
177 void FontEngine::readSettings()
179 if (USE_FREETYPE && g_settings->getBool("freetype")) {
180 m_default_size[FM_Standard] = m_settings->getU16("font_size");
181 m_default_size[FM_Fallback] = m_settings->getU16("fallback_font_size");
182 m_default_size[FM_Mono] = m_settings->getU16("mono_font_size");
184 m_currentMode = is_yes(gettext("needs_fallback_font")) ?
185 FM_Fallback : FM_Standard;
187 m_currentMode = FM_Simple;
190 m_default_size[FM_Simple] = m_settings->getU16("font_size");
191 m_default_size[FM_SimpleMono] = m_settings->getU16("mono_font_size");
198 /******************************************************************************/
199 void FontEngine::updateSkin()
201 gui::IGUIFont *font = getFont();
204 m_env->getSkin()->setFont(font);
206 errorstream << "FontEngine: Default font file: " <<
207 "\n\t\"" << m_settings->get("font_path") << "\"" <<
208 "\n\trequired for current screen configuration was not found" <<
209 " or was invalid file format." <<
210 "\n\tUsing irrlicht default font." << std::endl;
212 // If we did fail to create a font our own make irrlicht find a default one
213 font = m_env->getSkin()->getFont();
214 FATAL_ERROR_IF(font == NULL, "Could not create/get font");
216 u32 text_height = font->getDimension(L"Hello, world!").Height;
217 infostream << "text_height=" << text_height << std::endl;
220 /******************************************************************************/
221 void FontEngine::updateFontCache()
223 /* the only font to be initialized is default one,
224 * all others are re-initialized on demand */
225 getFont(FONT_SIZE_UNSPECIFIED, FM_Unspecified);
228 /******************************************************************************/
229 void FontEngine::initFont(unsigned int basesize, FontMode mode)
231 assert(mode != FM_Unspecified);
232 assert(basesize != FONT_SIZE_UNSPECIFIED);
234 if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end())
238 std::string setting_prefix = "";
242 setting_prefix = "fallback_";
246 setting_prefix = "mono_";
252 u32 size = std::floor(RenderingEngine::getDisplayDensity() *
253 m_settings->getFloat("gui_scaling") * basesize);
255 errorstream << "FontEngine: attempt to use font size 0" << std::endl;
256 errorstream << " display density: " << RenderingEngine::getDisplayDensity() << std::endl;
261 u16 font_shadow_alpha = 0;
262 g_settings->getU16NoEx(setting_prefix + "font_shadow", font_shadow);
263 g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha", font_shadow_alpha);
265 std::string fallback_settings[] = {
266 m_settings->get(setting_prefix + "font_path"),
267 m_settings->get("fallback_font_path"),
268 m_settings->getDefault(setting_prefix + "font_path")
272 for (const std::string &font_path : fallback_settings) {
273 irr::gui::IGUIFont *font = gui::CGUITTFont::createTTFont(m_env,
274 font_path.c_str(), size, true, true, font_shadow,
279 m_font_cache[mode][basesize] = font;
283 errorstream << "FontEngine: Cannot load '" << font_path <<
284 "'. Trying to fall back to another path." << std::endl;
289 errorstream << "minetest can not continue without a valid font. "
290 "Please correct the 'font_path' setting or install the font "
291 "file in the proper location" << std::endl;
293 errorstream << "FontEngine: Tried to load freetype fonts but Minetest was"
294 " not compiled with that library." << std::endl;
299 /** initialize a font without freetype */
300 void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
302 assert(mode == FM_Simple || mode == FM_SimpleMono);
304 const std::string &font_path = m_settings->get(
305 (mode == FM_SimpleMono) ? "mono_font_path" : "font_path");
307 size_t pos_dot = font_path.find_last_of('.');
308 std::string basename = font_path;
309 std::string ending = lowercase(font_path.substr(pos_dot));
311 if (ending == ".ttf") {
312 errorstream << "FontEngine: Found font \"" << font_path
313 << "\" but freetype is not available." << std::endl;
317 if (ending == ".xml" || ending == ".png")
318 basename = font_path.substr(0, pos_dot);
320 if (basesize == FONT_SIZE_UNSPECIFIED)
321 basesize = DEFAULT_FONT_SIZE;
323 u32 size = std::floor(
324 RenderingEngine::getDisplayDensity() *
325 m_settings->getFloat("gui_scaling") *
328 irr::gui::IGUIFont *font = nullptr;
329 std::string font_extensions[] = { ".png", ".xml" };
331 // Find nearest matching font scale
332 // Does a "zig-zag motion" (positibe/negative), from 0 to MAX_FONT_SIZE_OFFSET
333 for (s32 zoffset = 0; zoffset < MAX_FONT_SIZE_OFFSET * 2; zoffset++) {
334 std::stringstream path;
337 s32 sign = (zoffset & 1) ? -1 : 1;
338 s32 offset = zoffset >> 1;
340 for (const std::string &ext : font_extensions) {
341 path.str(""); // Clear
342 path << basename << "_" << (size + offset * sign) << ext;
344 if (!fs::PathExists(path.str()))
347 font = m_env->getFont(path.str().c_str());
350 verbosestream << "FontEngine: found font: " << path.str() << std::endl;
361 if (fs::PathExists(font_path)) {
362 font = m_env->getFont(font_path.c_str());
364 verbosestream << "FontEngine: found font: " << font_path << std::endl;
370 m_font_cache[mode][basesize] = font;