aboutsummaryrefslogtreecommitdiff
path: root/src/treegen.cpp
Commit message (Collapse)AuthorAge
* Fix seg fault if popping from empty stack (L-system trees)Craig Robbins2014-08-23
| | | | | | | | | | | | | | | | | | See: https://github.com/minetest/minetest/issues/1525 Background Wuzzy2: If you attempt to spawn a L-system tree with minetest.spawn_tree, you can make Minetest crash if it is attempted to pop an empty stack. ShadowNinja: This shouldn't cause a segmentation fault, but it should throw a Lua error Commit Description This commit throws a Lua error instead of causing a segmentation fault. The server will still "crash" but will include a Lua backtrace. L-Systems fix randomness Unless a random seed is provided (via Lua treedef) seed the PRNG with a different seed for each tree Resolves: https://github.com/minetest/minetest/issues/1469 Fix l-system crash when treedef random_level not set by Lua
* L-systems treegen code tweaks.RealBadAngel2014-06-17
|
* Move the sapling growing and grass adding/removing ABMs to LuaNovatux2013-11-02
|
* Omnicleanup: header cleanup, add ModApiUtil shared between game and mainmenuKahrl2013-08-14
|
* Make saplings only grow on dirt or grass, make jungle tree trunks only ↵kwolekr2013-03-16
| | | | replace air
* Re-add jungles, apple treeskwolekr2013-03-16
|
* Clean up Mapgenkwolekr2013-03-11
|
* Migrate to STL containers/algorithms.Ilya Zhuravlev2013-03-11
|
* Update Copyright YearsSfan52013-02-24
|
* Change Minetest-c55 to MinetestPilzAdam2013-02-24
|
* Add seed parameter for default and L-system treesRealBadAngel2013-02-24
|
* Treegen update. Some new symbols. Speed up code a bit.RealBadAngel2013-01-23
|
* L-Systems treegen update.RealBadAngel2013-01-07
|
* Fixed compile issues on windowsdannydark2012-12-30
| | | | With latest upstream L-Systems treegen.
* L-System treegenRealBadAngel2012-12-30
a id='n265' href='#n265'>265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
/*
Minetest
Copyright (C) 2010-2014 sapier <sapier at gmx dot net>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "fontengine.h"
#include "client/renderingengine.h"
#include "config.h"
#include "porting.h"
#include "filesys.h"

#if USE_FREETYPE
#include "gettext.h"
#include "irrlicht_changes/CGUITTFont.h"
#endif

/** maximum size distance for getting a "similar" font size */
#define MAX_FONT_SIZE_OFFSET 10

/** reference to access font engine, has to be initialized by main */
FontEngine* g_fontengine = NULL;

/** callback to be used on change of font size setting */
static void font_setting_changed(const std::string &name, void *userdata)
{
	g_fontengine->readSettings();
}

/******************************************************************************/
FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
	m_settings(main_settings),
	m_env(env)
{

	for (unsigned int i = 0; i < FM_MaxMode; i++) {
		m_default_size[i] = (FontMode) FONT_SIZE_UNSPECIFIED;
	}

	assert(m_settings != NULL); // pre-condition
	assert(m_env != NULL); // pre-condition
	assert(m_env->getSkin() != NULL); // pre-condition

	m_currentMode = FM_Simple;

#if USE_FREETYPE
	if (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");

		if (is_yes(gettext("needs_fallback_font"))) {
			m_currentMode = FM_Fallback;
		}
		else {
			m_currentMode = FM_Standard;
		}
	}

	// having freetype but not using it is quite a strange case so we need to do
	// special handling for it
	if (m_currentMode == FM_Simple) {
		std::stringstream fontsize;
		fontsize << DEFAULT_FONT_SIZE;
		m_settings->setDefault("font_size", fontsize.str());
		m_settings->setDefault("mono_font_size", fontsize.str());
	}
#endif

	m_default_size[FM_Simple]       = m_settings->getU16("font_size");
	m_default_size[FM_SimpleMono]   = m_settings->getU16("mono_font_size");

	updateSkin();

	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);
	}

	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);
}

/******************************************************************************/
FontEngine::~FontEngine()
{
	cleanCache();
}

/******************************************************************************/
void FontEngine::cleanCache()
{
	for ( unsigned int i = 0; i < FM_MaxMode; i++) {

		for (std::map<unsigned int, irr::gui::IGUIFont*>::iterator iter
				= m_font_cache[i].begin();
				iter != m_font_cache[i].end(); ++iter) {
			iter->second->drop();
			iter->second = NULL;
		}
		m_font_cache[i].clear();
	}
}

/******************************************************************************/
irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode)
{
	if (mode == FM_Unspecified) {
		mode = m_currentMode;
	}
	else if ((mode == FM_Mono) && (m_currentMode == FM_Simple)) {
		mode = FM_SimpleMono;
	}

	if (font_size == FONT_SIZE_UNSPECIFIED) {
		font_size = m_default_size[mode];
	}

	if ((font_size == m_lastSize) && (mode == m_lastMode)) {
		return m_lastFont;
	}

	if (m_font_cache[mode].find(font_size) == m_font_cache[mode].end()) {
		initFont(font_size, mode);
	}

	if (m_font_cache[mode].find(font_size) == m_font_cache[mode].end()) {
		return NULL;
	}

	m_lastSize = font_size;
	m_lastMode = mode;
	m_lastFont = m_font_cache[mode][font_size];

	return m_font_cache[mode][font_size];
}

/******************************************************************************/
unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode)
{
	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");

	return font->getDimension(L"Some unimportant example String").Height;
}

/******************************************************************************/
unsigned int FontEngine::getTextWidth(const std::wstring& text,
		unsigned int font_size, FontMode mode)
{
	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");

	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)
{
	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");

	return font->getDimension(L"Some unimportant example String").Height
			+ font->getKerningHeight();
}

/******************************************************************************/
unsigned int FontEngine::getDefaultFontSize()
{
	return m_default_size[m_currentMode];
}

/******************************************************************************/
void FontEngine::readSettings()
{
#if USE_FREETYPE
	if (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");

		if (is_yes(gettext("needs_fallback_font"))) {
			m_currentMode = FM_Fallback;
		}
		else {
			m_currentMode = FM_Standard;
		}
	}
#endif
	m_default_size[FM_Simple]       = m_settings->getU16("font_size");
	m_default_size[FM_SimpleMono]   = m_settings->getU16("mono_font_size");

	cleanCache();
	updateFontCache();
	updateSkin();
}

/******************************************************************************/
void FontEngine::updateSkin()
{
	gui::IGUIFont *font = getFont();

	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;
}

/******************************************************************************/
void FontEngine::updateFontCache()
{
	/* the only font to be initialized is default one,
	 * all others are re-initialized on demand */
	initFont(m_default_size[m_currentMode], m_currentMode);

	/* reset font quick access */
	m_lastMode = FM_Unspecified;
	m_lastSize = 0;
	m_lastFont = NULL;
}

/******************************************************************************/
void FontEngine::initFont(unsigned int basesize, FontMode mode)
{

	std::string font_config_prefix;

	if (mode == FM_Unspecified) {
		mode = m_currentMode;
	}

	switch (mode) {

		case FM_Standard:
			font_config_prefix = "";
			break;

		case FM_Fallback:
			font_config_prefix = "fallback_";
			break;

		case FM_Mono:
			font_config_prefix = "mono_";
			if (m_currentMode == FM_Simple)
				mode = FM_SimpleMono;
			break;

		case FM_Simple: /* Fallthrough */
		case FM_SimpleMono: /* Fallthrough */
		default:
			font_config_prefix = "";

	}

	if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end())
		return;

	if ((mode == FM_Simple) || (mode == FM_SimpleMono)) {
		initSimpleFont(basesize, mode);
		return;
	}
#if USE_FREETYPE
	else {
		if (! is_yes(m_settings->get("freetype"))) {
			return;
		}
		unsigned int size = floor(RenderingEngine::getDisplayDensity() *
				m_settings->getFloat("gui_scaling") * basesize);
		u32 font_shadow       = 0;
		u32 font_shadow_alpha = 0;

		try {
			font_shadow =
					g_settings->getU16(font_config_prefix + "font_shadow");
		} catch (SettingNotFoundException&) {}
		try {
			font_shadow_alpha =
					g_settings->getU16(font_config_prefix + "font_shadow_alpha");
		} catch (SettingNotFoundException&) {}

		std::string font_path = g_settings->get(font_config_prefix + "font_path");

		irr::gui::IGUIFont* font = gui::CGUITTFont::createTTFont(m_env,
				font_path.c_str(), size, true, true, font_shadow,
				font_shadow_alpha);

		if (font != NULL) {
			m_font_cache[mode][basesize] = font;
			return;
		}

		// try fallback font
		errorstream << "FontEngine: failed to load: " << font_path << ", trying to fall back "
				"to fallback font" << std::endl;

		font_path = g_settings->get(font_config_prefix + "fallback_font_path");

		font = gui::CGUITTFont::createTTFont(m_env,
			font_path.c_str(), size, true, true, font_shadow,
			font_shadow_alpha);

		if (font != NULL) {
			m_font_cache[mode][basesize] = font;
			return;
		}

		// give up
		errorstream << "FontEngine: failed to load freetype font: "
				<< font_path << std::endl;
		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;
		abort();
	}
#endif
}

/** initialize a font without freetype */
void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
{
	assert(mode == FM_Simple || mode == FM_SimpleMono); // pre-condition

	std::string font_path = "";
	if (mode == FM_Simple) {
		font_path = m_settings->get("font_path");
	} else {
		font_path = m_settings->get("mono_font_path");
	}
	std::string basename = font_path;
	std::string ending = font_path.substr(font_path.length() -4);

	if (ending == ".ttf") {
		errorstream << "FontEngine: Not trying to open \"" << font_path
				<< "\" which seems to be a truetype font." << std::endl;
		return;
	}

	if ((ending == ".xml") || (ending == ".png")) {
		basename = font_path.substr(0,font_path.length()-4);
	}

	if (basesize == FONT_SIZE_UNSPECIFIED)
		basesize = DEFAULT_FONT_SIZE;

	unsigned int size = floor(
			RenderingEngine::getDisplayDensity() *
			m_settings->getFloat("gui_scaling") *
			basesize);

	irr::gui::IGUIFont* font = NULL;

	for(unsigned int offset = 0; offset < MAX_FONT_SIZE_OFFSET; offset++) {

		// try opening positive offset
		std::stringstream fontsize_plus_png;
		fontsize_plus_png << basename << "_" << (size + offset) << ".png";

		if (fs::PathExists(fontsize_plus_png.str())) {
			font = m_env->getFont(fontsize_plus_png.str().c_str());

			if (font) {
				verbosestream << "FontEngine: found font: " << fontsize_plus_png.str() << std::endl;
				break;
			}
		}

		std::stringstream fontsize_plus_xml;
		fontsize_plus_xml << basename << "_" << (size + offset) << ".xml";

		if (fs::PathExists(fontsize_plus_xml.str())) {
			font = m_env->getFont(fontsize_plus_xml.str().c_str());

			if (font) {
				verbosestream << "FontEngine: found font: " << fontsize_plus_xml.str() << std::endl;
				break;
			}
		}

		// try negative offset
		std::stringstream fontsize_minus_png;
		fontsize_minus_png << basename << "_" << (size - offset) << ".png";

		if (fs::PathExists(fontsize_minus_png.str())) {
			font = m_env->getFont(fontsize_minus_png.str().c_str());

			if (font) {
				verbosestream << "FontEngine: found font: " << fontsize_minus_png.str() << std::endl;
				break;
			}
		}

		std::stringstream fontsize_minus_xml;
		fontsize_minus_xml << basename << "_" << (size - offset) << ".xml";

		if (fs::PathExists(fontsize_minus_xml.str())) {
			font = m_env->getFont(fontsize_minus_xml.str().c_str());

			if (font) {
				verbosestream << "FontEngine: found font: " << fontsize_minus_xml.str() << std::endl;
				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 (font != NULL) {
		font->grab();
		m_font_cache[mode][basesize] = font;
	}
}