Line data Source code
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 : #include "fontengine.h"
20 : #include "log.h"
21 : #include "config.h"
22 : #include "porting.h"
23 : #include "constants.h"
24 : #include "filesys.h"
25 :
26 : #if USE_FREETYPE
27 : #include "gettext.h"
28 : #include "xCGUITTFont.h"
29 : #endif
30 :
31 : /** maximum size distance for getting a "similar" font size */
32 : #define MAX_FONT_SIZE_OFFSET 10
33 :
34 : /** reference to access font engine, has to be initialized by main */
35 : FontEngine* g_fontengine = NULL;
36 :
37 : /** callback to be used on change of font size setting */
38 0 : static void font_setting_changed(const std::string, void *userdata) {
39 0 : g_fontengine->readSettings();
40 0 : }
41 :
42 : /******************************************************************************/
43 1 : FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
44 : m_settings(main_settings),
45 : m_env(env),
46 : m_font_cache(),
47 : m_currentMode(FM_Standard),
48 : m_lastMode(),
49 : m_lastSize(0),
50 1 : m_lastFont(NULL)
51 : {
52 :
53 6 : for (unsigned int i = 0; i < FM_MaxMode; i++) {
54 5 : m_default_size[i] = (FontMode) FONT_SIZE_UNSPECIFIED;
55 : }
56 :
57 : assert(m_settings != NULL); // pre-condition
58 : assert(m_env != NULL); // pre-condition
59 : assert(m_env->getSkin() != NULL); // pre-condition
60 :
61 1 : m_currentMode = FM_Simple;
62 :
63 : #if USE_FREETYPE
64 1 : if (g_settings->getBool("freetype")) {
65 1 : m_default_size[FM_Standard] = m_settings->getU16("font_size");
66 1 : m_default_size[FM_Fallback] = m_settings->getU16("fallback_font_size");
67 1 : m_default_size[FM_Mono] = m_settings->getU16("mono_font_size");
68 :
69 1 : if (is_yes(gettext("needs_fallback_font"))) {
70 0 : m_currentMode = FM_Fallback;
71 : }
72 : else {
73 1 : m_currentMode = FM_Standard;
74 : }
75 : }
76 :
77 : // having freetype but not using it is quite a strange case so we need to do
78 : // special handling for it
79 1 : if (m_currentMode == FM_Simple) {
80 0 : std::stringstream fontsize;
81 0 : fontsize << DEFAULT_FONT_SIZE;
82 0 : m_settings->setDefault("font_size", fontsize.str());
83 0 : m_settings->setDefault("mono_font_size", fontsize.str());
84 : }
85 : #endif
86 :
87 1 : m_default_size[FM_Simple] = m_settings->getU16("font_size");
88 1 : m_default_size[FM_SimpleMono] = m_settings->getU16("mono_font_size");
89 :
90 1 : updateSkin();
91 :
92 1 : if (m_currentMode == FM_Standard) {
93 1 : m_settings->registerChangedCallback("font_size", font_setting_changed, NULL);
94 1 : m_settings->registerChangedCallback("font_path", font_setting_changed, NULL);
95 1 : m_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL);
96 1 : m_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL);
97 : }
98 0 : else if (m_currentMode == FM_Fallback) {
99 0 : m_settings->registerChangedCallback("fallback_font_size", font_setting_changed, NULL);
100 0 : m_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL);
101 0 : m_settings->registerChangedCallback("fallback_font_shadow", font_setting_changed, NULL);
102 0 : m_settings->registerChangedCallback("fallback_font_shadow_alpha", font_setting_changed, NULL);
103 : }
104 :
105 1 : m_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL);
106 1 : m_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL);
107 1 : m_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL);
108 1 : m_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL);
109 1 : }
110 :
111 : /******************************************************************************/
112 2 : FontEngine::~FontEngine()
113 : {
114 1 : cleanCache();
115 1 : }
116 :
117 : /******************************************************************************/
118 1 : void FontEngine::cleanCache()
119 : {
120 6 : for ( unsigned int i = 0; i < FM_MaxMode; i++) {
121 :
122 16 : for (std::map<unsigned int, irr::gui::IGUIFont*>::iterator iter
123 5 : = m_font_cache[i].begin();
124 14 : iter != m_font_cache[i].end(); iter++) {
125 2 : iter->second->drop();
126 2 : iter->second = NULL;
127 : }
128 5 : m_font_cache[i].clear();
129 : }
130 1 : }
131 :
132 : /******************************************************************************/
133 15487 : irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode)
134 : {
135 15487 : if (mode == FM_Unspecified) {
136 15486 : mode = m_currentMode;
137 : }
138 1 : else if ((mode == FM_Mono) && (m_currentMode == FM_Simple)) {
139 0 : mode = FM_SimpleMono;
140 : }
141 :
142 15487 : if (font_size == FONT_SIZE_UNSPECIFIED) {
143 15487 : font_size = m_default_size[mode];
144 : }
145 :
146 15487 : if ((font_size == m_lastSize) && (mode == m_lastMode)) {
147 15484 : return m_lastFont;
148 : }
149 :
150 3 : if (m_font_cache[mode].find(font_size) == m_font_cache[mode].end()) {
151 2 : initFont(font_size, mode);
152 : }
153 :
154 3 : if (m_font_cache[mode].find(font_size) == m_font_cache[mode].end()) {
155 0 : return NULL;
156 : }
157 :
158 3 : m_lastSize = font_size;
159 3 : m_lastMode = mode;
160 3 : m_lastFont = m_font_cache[mode][font_size];
161 :
162 3 : return m_font_cache[mode][font_size];
163 : }
164 :
165 : /******************************************************************************/
166 2502 : unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode)
167 : {
168 2502 : irr::gui::IGUIFont* font = getFont(font_size, mode);
169 :
170 : // use current skin font as fallback
171 2502 : if (font == NULL) {
172 0 : font = m_env->getSkin()->getFont();
173 : }
174 2502 : FATAL_ERROR_IF(font == NULL, "Could not get skin font");
175 :
176 2502 : return font->getDimension(L"Some unimportant example String").Height;
177 : }
178 :
179 : /******************************************************************************/
180 1243 : unsigned int FontEngine::getTextWidth(const std::wstring& text,
181 : unsigned int font_size, FontMode mode)
182 : {
183 1243 : irr::gui::IGUIFont* font = getFont(font_size, mode);
184 :
185 : // use current skin font as fallback
186 1243 : if (font == NULL) {
187 0 : font = m_env->getSkin()->getFont();
188 : }
189 1243 : FATAL_ERROR_IF(font == NULL, "Could not get font");
190 :
191 1243 : return font->getDimension(text.c_str()).Width;
192 : }
193 :
194 :
195 : /** get line height for a specific font (including empty room between lines) */
196 1241 : unsigned int FontEngine::getLineHeight(unsigned int font_size, FontMode mode)
197 : {
198 1241 : irr::gui::IGUIFont* font = getFont(font_size, mode);
199 :
200 : // use current skin font as fallback
201 1241 : if (font == NULL) {
202 0 : font = m_env->getSkin()->getFont();
203 : }
204 1241 : FATAL_ERROR_IF(font == NULL, "Could not get font");
205 :
206 1241 : return font->getDimension(L"Some unimportant example String").Height
207 1241 : + font->getKerningHeight();
208 : }
209 :
210 : /******************************************************************************/
211 0 : unsigned int FontEngine::getDefaultFontSize()
212 : {
213 0 : return m_default_size[m_currentMode];
214 : }
215 :
216 : /******************************************************************************/
217 0 : void FontEngine::readSettings()
218 : {
219 : #if USE_FREETYPE
220 0 : if (g_settings->getBool("freetype")) {
221 0 : m_default_size[FM_Standard] = m_settings->getU16("font_size");
222 0 : m_default_size[FM_Fallback] = m_settings->getU16("fallback_font_size");
223 0 : m_default_size[FM_Mono] = m_settings->getU16("mono_font_size");
224 :
225 0 : if (is_yes(gettext("needs_fallback_font"))) {
226 0 : m_currentMode = FM_Fallback;
227 : }
228 : else {
229 0 : m_currentMode = FM_Standard;
230 : }
231 : }
232 : #endif
233 0 : m_default_size[FM_Simple] = m_settings->getU16("font_size");
234 0 : m_default_size[FM_SimpleMono] = m_settings->getU16("mono_font_size");
235 :
236 0 : cleanCache();
237 0 : updateFontCache();
238 0 : updateSkin();
239 0 : }
240 :
241 : /******************************************************************************/
242 1 : void FontEngine::updateSkin()
243 : {
244 1 : gui::IGUIFont *font = getFont();
245 :
246 1 : if (font)
247 1 : m_env->getSkin()->setFont(font);
248 : else
249 0 : errorstream << "FontEngine: Default font file: " <<
250 0 : "\n\t\"" << m_settings->get("font_path") << "\"" <<
251 0 : "\n\trequired for current screen configuration was not found" <<
252 0 : " or was invalid file format." <<
253 0 : "\n\tUsing irrlicht default font." << std::endl;
254 :
255 : // If we did fail to create a font our own make irrlicht find a default one
256 1 : font = m_env->getSkin()->getFont();
257 1 : FATAL_ERROR_IF(font == NULL, "Could not create/get font");
258 :
259 1 : u32 text_height = font->getDimension(L"Hello, world!").Height;
260 1 : infostream << "text_height=" << text_height << std::endl;
261 1 : }
262 :
263 : /******************************************************************************/
264 0 : void FontEngine::updateFontCache()
265 : {
266 : /* the only font to be initialized is default one,
267 : * all others are re-initialized on demand */
268 0 : initFont(m_default_size[m_currentMode], m_currentMode);
269 :
270 : /* reset font quick access */
271 0 : m_lastMode = FM_Unspecified;
272 0 : m_lastSize = 0;
273 0 : m_lastFont = NULL;
274 0 : }
275 :
276 : /******************************************************************************/
277 2 : void FontEngine::initFont(unsigned int basesize, FontMode mode)
278 : {
279 :
280 4 : std::string font_config_prefix;
281 :
282 2 : if (mode == FM_Unspecified) {
283 0 : mode = m_currentMode;
284 : }
285 :
286 2 : switch (mode) {
287 :
288 : case FM_Standard:
289 1 : font_config_prefix = "";
290 1 : break;
291 :
292 : case FM_Fallback:
293 0 : font_config_prefix = "fallback_";
294 0 : break;
295 :
296 : case FM_Mono:
297 1 : font_config_prefix = "mono_";
298 1 : if (m_currentMode == FM_Simple)
299 0 : mode = FM_SimpleMono;
300 1 : break;
301 :
302 : case FM_Simple: /* Fallthrough */
303 : case FM_SimpleMono: /* Fallthrough */
304 : default:
305 0 : font_config_prefix = "";
306 :
307 : }
308 :
309 2 : if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end())
310 0 : return;
311 :
312 2 : if ((mode == FM_Simple) || (mode == FM_SimpleMono)) {
313 0 : initSimpleFont(basesize, mode);
314 0 : return;
315 : }
316 : #if USE_FREETYPE
317 : else {
318 2 : if (! is_yes(m_settings->get("freetype"))) {
319 0 : return;
320 : }
321 2 : unsigned int size = floor(
322 4 : porting::getDisplayDensity() *
323 6 : m_settings->getFloat("gui_scaling") *
324 2 : basesize);
325 2 : u32 font_shadow = 0;
326 2 : u32 font_shadow_alpha = 0;
327 :
328 : try {
329 : font_shadow =
330 3 : g_settings->getU16(font_config_prefix + "font_shadow");
331 1 : } catch (SettingNotFoundException&) {}
332 : try {
333 : font_shadow_alpha =
334 3 : g_settings->getU16(font_config_prefix + "font_shadow_alpha");
335 1 : } catch (SettingNotFoundException&) {}
336 :
337 4 : std::string font_path = g_settings->get(font_config_prefix + "font_path");
338 :
339 4 : irr::gui::IGUIFont* font = gui::CGUITTFont::createTTFont(m_env,
340 : font_path.c_str(), size, true, true, font_shadow,
341 2 : font_shadow_alpha);
342 :
343 2 : if (font != NULL) {
344 2 : m_font_cache[mode][basesize] = font;
345 : }
346 : else {
347 0 : errorstream << "FontEngine: failed to load freetype font: "
348 0 : << font_path << std::endl;
349 : }
350 : }
351 : #endif
352 : }
353 :
354 : /** initialize a font without freetype */
355 0 : void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
356 : {
357 : assert(mode == FM_Simple || mode == FM_SimpleMono); // pre-condition
358 :
359 0 : std::string font_path = "";
360 0 : if (mode == FM_Simple) {
361 0 : font_path = m_settings->get("font_path");
362 : } else {
363 0 : font_path = m_settings->get("mono_font_path");
364 : }
365 0 : std::string basename = font_path;
366 0 : std::string ending = font_path.substr(font_path.length() -4);
367 :
368 0 : if (ending == ".ttf") {
369 0 : errorstream << "FontEngine: Not trying to open \"" << font_path
370 0 : << "\" which seems to be a truetype font." << std::endl;
371 0 : return;
372 : }
373 :
374 0 : if ((ending == ".xml") || (ending == ".png")) {
375 0 : basename = font_path.substr(0,font_path.length()-4);
376 : }
377 :
378 0 : if (basesize == FONT_SIZE_UNSPECIFIED)
379 0 : basesize = DEFAULT_FONT_SIZE;
380 :
381 0 : unsigned int size = floor(
382 0 : porting::getDisplayDensity() *
383 0 : m_settings->getFloat("gui_scaling") *
384 0 : basesize);
385 :
386 0 : irr::gui::IGUIFont* font = NULL;
387 :
388 0 : for(unsigned int offset = 0; offset < MAX_FONT_SIZE_OFFSET; offset++) {
389 :
390 : // try opening positive offset
391 0 : std::stringstream fontsize_plus_png;
392 0 : fontsize_plus_png << basename << "_" << (size + offset) << ".png";
393 :
394 0 : if (fs::PathExists(fontsize_plus_png.str())) {
395 0 : font = m_env->getFont(fontsize_plus_png.str().c_str());
396 :
397 0 : if (font) {
398 0 : verbosestream << "FontEngine: found font: " << fontsize_plus_png.str() << std::endl;
399 0 : break;
400 : }
401 : }
402 :
403 0 : std::stringstream fontsize_plus_xml;
404 0 : fontsize_plus_xml << basename << "_" << (size + offset) << ".xml";
405 :
406 0 : if (fs::PathExists(fontsize_plus_xml.str())) {
407 0 : font = m_env->getFont(fontsize_plus_xml.str().c_str());
408 :
409 0 : if (font) {
410 0 : verbosestream << "FontEngine: found font: " << fontsize_plus_xml.str() << std::endl;
411 0 : break;
412 : }
413 : }
414 :
415 : // try negative offset
416 0 : std::stringstream fontsize_minus_png;
417 0 : fontsize_minus_png << basename << "_" << (size - offset) << ".png";
418 :
419 0 : if (fs::PathExists(fontsize_minus_png.str())) {
420 0 : font = m_env->getFont(fontsize_minus_png.str().c_str());
421 :
422 0 : if (font) {
423 0 : verbosestream << "FontEngine: found font: " << fontsize_minus_png.str() << std::endl;
424 0 : break;
425 : }
426 : }
427 :
428 0 : std::stringstream fontsize_minus_xml;
429 0 : fontsize_minus_xml << basename << "_" << (size - offset) << ".xml";
430 :
431 0 : if (fs::PathExists(fontsize_minus_xml.str())) {
432 0 : font = m_env->getFont(fontsize_minus_xml.str().c_str());
433 :
434 0 : if (font) {
435 0 : verbosestream << "FontEngine: found font: " << fontsize_minus_xml.str() << std::endl;
436 0 : break;
437 : }
438 : }
439 : }
440 :
441 : // try name direct
442 0 : if (font == NULL) {
443 0 : if (fs::PathExists(font_path)) {
444 0 : font = m_env->getFont(font_path.c_str());
445 0 : if (font)
446 0 : verbosestream << "FontEngine: found font: " << font_path << std::endl;
447 : }
448 : }
449 :
450 0 : if (font != NULL) {
451 0 : font->grab();
452 0 : m_font_cache[mode][basesize] = font;
453 : }
454 3 : }
|