]> git.lizzy.rs Git - minetest.git/blob - src/client/fontengine.cpp
Initialize wield mesh colors when changing item. (#12254)
[minetest.git] / src / client / fontengine.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2014 sapier <sapier at gmx dot net>
4
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.
9
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.
14
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.
18 */
19
20 #include "fontengine.h"
21 #include <cmath>
22 #include "client/renderingengine.h"
23 #include "config.h"
24 #include "porting.h"
25 #include "filesys.h"
26 #include "gettext.h"
27 #include "irrlicht_changes/CGUITTFont.h"
28
29 /** maximum size distance for getting a "similar" font size */
30 #define MAX_FONT_SIZE_OFFSET 10
31
32 /** reference to access font engine, has to be initialized by main */
33 FontEngine* g_fontengine = NULL;
34
35 /** callback to be used on change of font size setting */
36 static void font_setting_changed(const std::string &name, void *userdata)
37 {
38         g_fontengine->readSettings();
39 }
40
41 /******************************************************************************/
42 FontEngine::FontEngine(gui::IGUIEnvironment* env) :
43         m_env(env)
44 {
45         for (u32 &i : m_default_size) {
46                 i = FONT_SIZE_UNSPECIFIED;
47         }
48
49         assert(g_settings != NULL); // pre-condition
50         assert(m_env != NULL); // pre-condition
51         assert(m_env->getSkin() != NULL); // pre-condition
52
53         readSettings();
54
55         const char *settings[] = {
56                 "font_size", "font_bold", "font_italic", "font_size_divisible_by",
57                 "mono_font_size", "mono_font_size_divisible_by",
58                 "font_shadow", "font_shadow_alpha",
59                 "font_path", "font_path_bold", "font_path_italic", "font_path_bold_italic",
60                 "mono_font_path", "mono_font_path_bold", "mono_font_path_italic",
61                 "mono_font_path_bold_italic",
62                 "fallback_font_path",
63                 "screen_dpi", "gui_scaling",
64         };
65
66         for (auto name : settings)
67                 g_settings->registerChangedCallback(name, font_setting_changed, NULL);
68 }
69
70 /******************************************************************************/
71 FontEngine::~FontEngine()
72 {
73         cleanCache();
74 }
75
76 /******************************************************************************/
77 void FontEngine::cleanCache()
78 {
79         RecursiveMutexAutoLock l(m_font_mutex);
80
81         for (auto &font_cache_it : m_font_cache) {
82
83                 for (auto &font_it : font_cache_it) {
84                         font_it.second->drop();
85                         font_it.second = nullptr;
86                 }
87                 font_cache_it.clear();
88         }
89 }
90
91 /******************************************************************************/
92 irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec)
93 {
94         return getFont(spec, false);
95 }
96
97 irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec, bool may_fail)
98 {
99         if (spec.mode == FM_Unspecified) {
100                 spec.mode = m_currentMode;
101         } else if (spec.mode == _FM_Fallback) {
102                 // Fallback font doesn't support these
103                 spec.bold = false;
104                 spec.italic = false;
105         }
106
107         // Fallback to default size
108         if (spec.size == FONT_SIZE_UNSPECIFIED)
109                 spec.size = m_default_size[spec.mode];
110
111         RecursiveMutexAutoLock l(m_font_mutex);
112
113         const auto &cache = m_font_cache[spec.getHash()];
114         auto it = cache.find(spec.size);
115         if (it != cache.end())
116                 return it->second;
117
118         // Font does not yet exist
119         gui::IGUIFont *font = initFont(spec);
120
121         if (!font && !may_fail) {
122                 errorstream << "Minetest cannot continue without a valid font. "
123                         "Please correct the 'font_path' setting or install the font "
124                         "file in the proper location." << std::endl;
125                 abort();
126         }
127
128         m_font_cache[spec.getHash()][spec.size] = font;
129
130         return font;
131 }
132
133 /******************************************************************************/
134 unsigned int FontEngine::getTextHeight(const FontSpec &spec)
135 {
136         gui::IGUIFont *font = getFont(spec);
137
138         return font->getDimension(L"Some unimportant example String").Height;
139 }
140
141 /******************************************************************************/
142 unsigned int FontEngine::getTextWidth(const std::wstring &text, const FontSpec &spec)
143 {
144         gui::IGUIFont *font = getFont(spec);
145
146         return font->getDimension(text.c_str()).Width;
147 }
148
149 /** get line height for a specific font (including empty room between lines) */
150 unsigned int FontEngine::getLineHeight(const FontSpec &spec)
151 {
152         gui::IGUIFont *font = getFont(spec);
153
154         return font->getDimension(L"Some unimportant example String").Height
155                         + font->getKerningHeight();
156 }
157
158 /******************************************************************************/
159 unsigned int FontEngine::getDefaultFontSize()
160 {
161         return m_default_size[m_currentMode];
162 }
163
164 unsigned int FontEngine::getFontSize(FontMode mode)
165 {
166         if (mode == FM_Unspecified)
167                 return m_default_size[FM_Standard];
168
169         return m_default_size[mode];
170 }
171
172 /******************************************************************************/
173 void FontEngine::readSettings()
174 {
175         m_default_size[FM_Standard]  = g_settings->getU16("font_size");
176         m_default_size[_FM_Fallback] = g_settings->getU16("font_size");
177         m_default_size[FM_Mono]      = g_settings->getU16("mono_font_size");
178
179         m_default_bold = g_settings->getBool("font_bold");
180         m_default_italic = g_settings->getBool("font_italic");
181
182         cleanCache();
183         updateFontCache();
184         updateSkin();
185 }
186
187 /******************************************************************************/
188 void FontEngine::updateSkin()
189 {
190         gui::IGUIFont *font = getFont();
191         assert(font);
192
193         m_env->getSkin()->setFont(font);
194 }
195
196 /******************************************************************************/
197 void FontEngine::updateFontCache()
198 {
199         /* the only font to be initialized is default one,
200          * all others are re-initialized on demand */
201         getFont(FONT_SIZE_UNSPECIFIED, FM_Unspecified);
202 }
203
204 /******************************************************************************/
205 gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
206 {
207         assert(spec.mode != FM_Unspecified);
208         assert(spec.size != FONT_SIZE_UNSPECIFIED);
209
210         std::string setting_prefix = "";
211         if (spec.mode == FM_Mono)
212                 setting_prefix = "mono_";
213
214         std::string setting_suffix = "";
215         if (spec.bold)
216                 setting_suffix.append("_bold");
217         if (spec.italic)
218                 setting_suffix.append("_italic");
219
220         u32 size = std::max<u32>(spec.size * RenderingEngine::getDisplayDensity() *
221                         g_settings->getFloat("gui_scaling"), 1);
222
223         // Constrain the font size to a certain multiple, if necessary
224         u16 divisible_by = g_settings->getU16(setting_prefix + "font_size_divisible_by");
225         if (divisible_by > 1) {
226                 size = std::max<u32>(
227                                 std::round((double)size / divisible_by) * divisible_by, divisible_by);
228         }
229
230         sanity_check(size != 0);
231
232         u16 font_shadow       = 0;
233         u16 font_shadow_alpha = 0;
234         g_settings->getU16NoEx(setting_prefix + "font_shadow", font_shadow);
235         g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha",
236                         font_shadow_alpha);
237
238         std::string path_setting;
239         if (spec.mode == _FM_Fallback)
240                 path_setting = "fallback_font_path";
241         else
242                 path_setting = setting_prefix + "font_path" + setting_suffix;
243
244         std::string fallback_settings[] = {
245                 g_settings->get(path_setting),
246                 Settings::getLayer(SL_DEFAULTS)->get(path_setting)
247         };
248
249         for (const std::string &font_path : fallback_settings) {
250                 gui::CGUITTFont *font = gui::CGUITTFont::createTTFont(m_env,
251                                 font_path.c_str(), size, true, true, font_shadow,
252                                 font_shadow_alpha);
253
254                 if (!font) {
255                         errorstream << "FontEngine: Cannot load '" << font_path <<
256                                 "'. Trying to fall back to another path." << std::endl;
257                         continue;
258                 }
259
260                 if (spec.mode != _FM_Fallback) {
261                         FontSpec spec2(spec);
262                         spec2.mode = _FM_Fallback;
263                         font->setFallback(getFont(spec2, true));
264                 }
265                 return font;
266         }
267         return nullptr;
268 }