mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Allow overriding fonts via media files (#15606)
Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
parent
eeb6cab4c4
commit
547e1476bb
8 changed files with 270 additions and 138 deletions
|
@ -194,7 +194,8 @@ Mod directory structure
|
||||||
│ │ │ └── another_subfolder
|
│ │ │ └── another_subfolder
|
||||||
│ │ └── bar_subfolder
|
│ │ └── bar_subfolder
|
||||||
│ ├── sounds
|
│ ├── sounds
|
||||||
│ ├── media
|
│ ├── fonts
|
||||||
|
│ ├── media
|
||||||
│ ├── locale
|
│ ├── locale
|
||||||
│ └── <custom data>
|
│ └── <custom data>
|
||||||
└── another
|
└── another
|
||||||
|
@ -265,7 +266,7 @@ The main Lua script. Running this script should register everything it
|
||||||
wants to register. Subsequent execution depends on Luanti calling the
|
wants to register. Subsequent execution depends on Luanti calling the
|
||||||
registered callbacks.
|
registered callbacks.
|
||||||
|
|
||||||
### `textures`, `sounds`, `media`, `models`, `locale`
|
### `textures`, `sounds`, `media`, `models`, `locale`, `fonts`
|
||||||
|
|
||||||
Media files (textures, sounds, whatever) that will be transferred to the
|
Media files (textures, sounds, whatever) that will be transferred to the
|
||||||
client and will be available for use by the mod and translation files for
|
client and will be available for use by the mod and translation files for
|
||||||
|
@ -278,6 +279,7 @@ Accepted formats are:
|
||||||
images: .png, .jpg, .tga
|
images: .png, .jpg, .tga
|
||||||
sounds: .ogg vorbis
|
sounds: .ogg vorbis
|
||||||
models: .x, .b3d, .obj, (since version 5.10:) .gltf, .glb
|
models: .x, .b3d, .obj, (since version 5.10:) .gltf, .glb
|
||||||
|
fonts: .ttf, .woff (both since version 5.11, see notes below)
|
||||||
|
|
||||||
Other formats won't be sent to the client (e.g. you can store .blend files
|
Other formats won't be sent to the client (e.g. you can store .blend files
|
||||||
in a folder for convenience, without the risk that such files are transferred)
|
in a folder for convenience, without the risk that such files are transferred)
|
||||||
|
@ -342,6 +344,28 @@ For example, if your model used an emissive material,
|
||||||
you should expect that a future version of Luanti may respect this,
|
you should expect that a future version of Luanti may respect this,
|
||||||
and thus cause your model to render differently there.
|
and thus cause your model to render differently there.
|
||||||
|
|
||||||
|
#### Custom fonts
|
||||||
|
|
||||||
|
You can supply custom fonts in TrueType Font (`.ttf`) or Web Open Font Format (`.woff`) format.
|
||||||
|
The former is supported primarily for convenience. The latter is preferred due to its compression.
|
||||||
|
|
||||||
|
In the future, having multiple custom fonts and the ability to switch between them is planned,
|
||||||
|
but for now this feature is limited to the ability to override Luanti's default fonts via mods.
|
||||||
|
It is recommended that this only be used by game mods to set a look and feel.
|
||||||
|
|
||||||
|
The stems (file names without extension) are self-explanatory:
|
||||||
|
|
||||||
|
* Regular variants:
|
||||||
|
* `regular`
|
||||||
|
* `bold`
|
||||||
|
* `italic`
|
||||||
|
* `bold_italic`
|
||||||
|
* Monospaced variants:
|
||||||
|
* `mono`
|
||||||
|
* `mono_bold`
|
||||||
|
* `mono_italic`
|
||||||
|
* `mono_bold_italic`
|
||||||
|
|
||||||
Naming conventions
|
Naming conventions
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <IFileSystem.h>
|
#include <IFileSystem.h>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "client/fontengine.h"
|
||||||
#include "network/clientopcodes.h"
|
#include "network/clientopcodes.h"
|
||||||
#include "network/connection.h"
|
#include "network/connection.h"
|
||||||
#include "network/networkpacket.h"
|
#include "network/networkpacket.h"
|
||||||
|
@ -361,6 +362,9 @@ Client::~Client()
|
||||||
for (auto &csp : m_sounds_client_to_server)
|
for (auto &csp : m_sounds_client_to_server)
|
||||||
m_sound->freeId(csp.first);
|
m_sound->freeId(csp.first);
|
||||||
m_sounds_client_to_server.clear();
|
m_sounds_client_to_server.clear();
|
||||||
|
|
||||||
|
// Go back to our mainmenu fonts
|
||||||
|
g_fontengine->clearMediaFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::connect(const Address &address, const std::string &address_name,
|
void Client::connect(const Address &address, const std::string &address_name,
|
||||||
|
@ -837,6 +841,13 @@ bool Client::loadMedia(const std::string &data, const std::string &filename,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *font_ext[] = {".ttf", ".woff", NULL};
|
||||||
|
name = removeStringEnd(filename, font_ext);
|
||||||
|
if (!name.empty()) {
|
||||||
|
g_fontengine->setMediaFont(name, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
errorstream << "Client: Don't know how to load file \""
|
errorstream << "Client: Don't know how to load file \""
|
||||||
<< filename << "\"" << std::endl;
|
<< filename << "\"" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
// Copyright (C) 2010-2014 sapier <sapier at gmx dot net>
|
// Copyright (C) 2010-2014 sapier <sapier at gmx dot net>
|
||||||
|
|
||||||
#include "fontengine.h"
|
#include "fontengine.h"
|
||||||
#include <cmath>
|
|
||||||
#include "client/renderingengine.h"
|
#include "client/renderingengine.h"
|
||||||
#include "config.h"
|
|
||||||
#include "porting.h"
|
|
||||||
#include "filesys.h"
|
|
||||||
#include "gettext.h"
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "irrlicht_changes/CGUITTFont.h"
|
#include "irrlicht_changes/CGUITTFont.h"
|
||||||
#include "util/numeric.h" // rangelim
|
#include "util/numeric.h" // rangelim
|
||||||
#include <IGUIEnvironment.h>
|
#include <IGUIEnvironment.h>
|
||||||
#include <IGUIFont.h>
|
#include <IGUIFont.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
/** reference to access font engine, has to be initialized by main */
|
/** reference to access font engine, has to be initialized by main */
|
||||||
FontEngine *g_fontengine = nullptr;
|
FontEngine *g_fontengine = nullptr;
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ static const char *settings[] = {
|
||||||
"dpi_change_notifier", "display_density_factor", "gui_scaling",
|
"dpi_change_notifier", "display_density_factor", "gui_scaling",
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
FontEngine::FontEngine(gui::IGUIEnvironment* env) :
|
FontEngine::FontEngine(gui::IGUIEnvironment* env) :
|
||||||
m_env(env)
|
m_env(env)
|
||||||
{
|
{
|
||||||
|
@ -53,16 +52,14 @@ FontEngine::FontEngine(gui::IGUIEnvironment* env) :
|
||||||
g_settings->registerChangedCallback(name, font_setting_changed, this);
|
g_settings->registerChangedCallback(name, font_setting_changed, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
FontEngine::~FontEngine()
|
FontEngine::~FontEngine()
|
||||||
{
|
{
|
||||||
g_settings->deregisterAllChangedCallbacks(this);
|
g_settings->deregisterAllChangedCallbacks(this);
|
||||||
|
|
||||||
cleanCache();
|
clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
void FontEngine::clearCache()
|
||||||
void FontEngine::cleanCache()
|
|
||||||
{
|
{
|
||||||
RecursiveMutexAutoLock l(m_font_mutex);
|
RecursiveMutexAutoLock l(m_font_mutex);
|
||||||
|
|
||||||
|
@ -76,7 +73,6 @@ void FontEngine::cleanCache()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec)
|
irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec)
|
||||||
{
|
{
|
||||||
return getFont(spec, false);
|
return getFont(spec, false);
|
||||||
|
@ -118,7 +114,6 @@ irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec, bool may_fail)
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
unsigned int FontEngine::getTextHeight(const FontSpec &spec)
|
unsigned int FontEngine::getTextHeight(const FontSpec &spec)
|
||||||
{
|
{
|
||||||
gui::IGUIFont *font = getFont(spec);
|
gui::IGUIFont *font = getFont(spec);
|
||||||
|
@ -126,7 +121,6 @@ unsigned int FontEngine::getTextHeight(const FontSpec &spec)
|
||||||
return font->getDimension(L"Some unimportant example String").Height;
|
return font->getDimension(L"Some unimportant example String").Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
unsigned int FontEngine::getTextWidth(const std::wstring &text, const FontSpec &spec)
|
unsigned int FontEngine::getTextWidth(const std::wstring &text, const FontSpec &spec)
|
||||||
{
|
{
|
||||||
gui::IGUIFont *font = getFont(spec);
|
gui::IGUIFont *font = getFont(spec);
|
||||||
|
@ -143,7 +137,6 @@ unsigned int FontEngine::getLineHeight(const FontSpec &spec)
|
||||||
+ font->getKerning(L'S').Y;
|
+ font->getKerning(L'S').Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
unsigned int FontEngine::getDefaultFontSize()
|
unsigned int FontEngine::getDefaultFontSize()
|
||||||
{
|
{
|
||||||
return m_default_size[m_currentMode];
|
return m_default_size[m_currentMode];
|
||||||
|
@ -157,7 +150,6 @@ unsigned int FontEngine::getFontSize(FontMode mode)
|
||||||
return m_default_size[mode];
|
return m_default_size[mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
void FontEngine::readSettings()
|
void FontEngine::readSettings()
|
||||||
{
|
{
|
||||||
m_default_size[FM_Standard] = rangelim(g_settings->getU16("font_size"), 5, 72);
|
m_default_size[FM_Standard] = rangelim(g_settings->getU16("font_size"), 5, 72);
|
||||||
|
@ -167,12 +159,9 @@ void FontEngine::readSettings()
|
||||||
m_default_bold = g_settings->getBool("font_bold");
|
m_default_bold = g_settings->getBool("font_bold");
|
||||||
m_default_italic = g_settings->getBool("font_italic");
|
m_default_italic = g_settings->getBool("font_italic");
|
||||||
|
|
||||||
cleanCache();
|
refresh();
|
||||||
updateFontCache();
|
|
||||||
updateSkin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
void FontEngine::updateSkin()
|
void FontEngine::updateSkin()
|
||||||
{
|
{
|
||||||
gui::IGUIFont *font = getFont();
|
gui::IGUIFont *font = getFont();
|
||||||
|
@ -181,15 +170,50 @@ void FontEngine::updateSkin()
|
||||||
m_env->getSkin()->setFont(font);
|
m_env->getSkin()->setFont(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
void FontEngine::updateCache()
|
||||||
void FontEngine::updateFontCache()
|
|
||||||
{
|
{
|
||||||
/* the only font to be initialized is default one,
|
/* the only font to be initialized is default one,
|
||||||
* all others are re-initialized on demand */
|
* all others are re-initialized on demand */
|
||||||
getFont(FONT_SIZE_UNSPECIFIED, FM_Unspecified);
|
getFont(FONT_SIZE_UNSPECIFIED, FM_Unspecified);
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
void FontEngine::refresh() {
|
||||||
|
clearCache();
|
||||||
|
updateCache();
|
||||||
|
updateSkin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontEngine::setMediaFont(const std::string &name, const std::string &data)
|
||||||
|
{
|
||||||
|
static std::unordered_set<std::string> valid_names {
|
||||||
|
"regular", "bold", "italic", "bold_italic",
|
||||||
|
"mono", "mono_bold", "mono_italic", "mono_bold_italic",
|
||||||
|
};
|
||||||
|
if (!valid_names.count(name)) {
|
||||||
|
warningstream << "Ignoring unrecognized media font: " << name << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char TTF_MAGIC[5] = {0, 1, 0, 0, 0};
|
||||||
|
if (data.size() < 5 || (memcmp(data.data(), "wOFF", 4) &&
|
||||||
|
memcmp(data.data(), TTF_MAGIC, 5))) {
|
||||||
|
warningstream << "Rejecting media font with unrecognized magic" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string copy = data;
|
||||||
|
irr_ptr<gui::SGUITTFace> face(gui::SGUITTFace::createFace(std::move(copy)));
|
||||||
|
m_media_faces.emplace(name, face);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontEngine::clearMediaFonts()
|
||||||
|
{
|
||||||
|
RecursiveMutexAutoLock l(m_font_mutex);
|
||||||
|
m_media_faces.clear();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
|
gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
|
||||||
{
|
{
|
||||||
assert(spec.mode != FM_Unspecified);
|
assert(spec.mode != FM_Unspecified);
|
||||||
|
@ -230,28 +254,55 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
|
||||||
else
|
else
|
||||||
path_setting = setting_prefix + "font_path" + setting_suffix;
|
path_setting = setting_prefix + "font_path" + setting_suffix;
|
||||||
|
|
||||||
std::string fallback_settings[] = {
|
std::string media_name = spec.mode == FM_Mono
|
||||||
g_settings->get(path_setting),
|
? "mono" + setting_suffix
|
||||||
Settings::getLayer(SL_DEFAULTS)->get(path_setting)
|
: (setting_suffix.empty() ? "" : setting_suffix.substr(1));
|
||||||
};
|
if (media_name.empty())
|
||||||
|
media_name = "regular";
|
||||||
|
|
||||||
for (const std::string &font_path : fallback_settings) {
|
auto createFont = [&](gui::SGUITTFace *face) -> gui::CGUITTFont* {
|
||||||
gui::CGUITTFont *font = gui::CGUITTFont::createTTFont(m_env,
|
auto *font = gui::CGUITTFont::createTTFont(m_env,
|
||||||
font_path.c_str(), size, true, true, font_shadow,
|
face, size, true, true, font_shadow,
|
||||||
font_shadow_alpha);
|
font_shadow_alpha);
|
||||||
|
|
||||||
if (!font) {
|
if (!font)
|
||||||
errorstream << "FontEngine: Cannot load '" << font_path <<
|
return nullptr;
|
||||||
"'. Trying to fall back to another path." << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spec.mode != _FM_Fallback) {
|
if (spec.mode != _FM_Fallback) {
|
||||||
FontSpec spec2(spec);
|
FontSpec spec2(spec);
|
||||||
spec2.mode = _FM_Fallback;
|
spec2.mode = _FM_Fallback;
|
||||||
font->setFallback(getFont(spec2, true));
|
font->setFallback(getFont(spec2, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = m_media_faces.find(media_name);
|
||||||
|
if (it != m_media_faces.end()) {
|
||||||
|
auto *face = it->second.get();
|
||||||
|
if (auto *font = createFont(face))
|
||||||
|
return font;
|
||||||
|
errorstream << "FontEngine: Cannot load media font '" << media_name <<
|
||||||
|
"'. Falling back to client settings." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fallback_settings[] = {
|
||||||
|
g_settings->get(path_setting),
|
||||||
|
Settings::getLayer(SL_DEFAULTS)->get(path_setting)
|
||||||
|
};
|
||||||
|
for (const std::string &font_path : fallback_settings) {
|
||||||
|
infostream << "Creating new font: " << font_path.c_str()
|
||||||
|
<< " " << size << "pt" << std::endl;
|
||||||
|
|
||||||
|
// Grab the face.
|
||||||
|
if (auto *face = irr::gui::SGUITTFace::loadFace(font_path)) {
|
||||||
|
auto *font = createFont(face);
|
||||||
|
face->drop();
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorstream << "FontEngine: Cannot load '" << font_path <<
|
||||||
|
"'. Trying to fall back to another path." << std::endl;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "irr_ptr.h"
|
||||||
|
#include "irrlicht_changes/CGUITTFont.h"
|
||||||
#include "util/basic_macros.h"
|
#include "util/basic_macros.h"
|
||||||
#include "irrlichttypes.h"
|
#include "irrlichttypes.h"
|
||||||
#include "irrString.h" // utf8_to_wide
|
#include "irrString.h" // utf8_to_wide
|
||||||
|
@ -66,7 +69,7 @@ public:
|
||||||
/** get text height for a specific font */
|
/** get text height for a specific font */
|
||||||
unsigned int getTextHeight(const FontSpec &spec);
|
unsigned int getTextHeight(const FontSpec &spec);
|
||||||
|
|
||||||
/** get text width if a text for a specific font */
|
/** get text width of a text for a specific font */
|
||||||
unsigned int getTextHeight(
|
unsigned int getTextHeight(
|
||||||
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
|
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
|
||||||
FontMode mode=FM_Unspecified)
|
FontMode mode=FM_Unspecified)
|
||||||
|
@ -77,7 +80,7 @@ public:
|
||||||
|
|
||||||
unsigned int getTextWidth(const std::wstring &text, const FontSpec &spec);
|
unsigned int getTextWidth(const std::wstring &text, const FontSpec &spec);
|
||||||
|
|
||||||
/** get text width if a text for a specific font */
|
/** get text width of a text for a specific font */
|
||||||
unsigned int getTextWidth(const std::wstring& text,
|
unsigned int getTextWidth(const std::wstring& text,
|
||||||
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
|
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
|
||||||
FontMode mode=FM_Unspecified)
|
FontMode mode=FM_Unspecified)
|
||||||
|
@ -118,11 +121,15 @@ public:
|
||||||
/** update internal parameters from settings */
|
/** update internal parameters from settings */
|
||||||
void readSettings();
|
void readSettings();
|
||||||
|
|
||||||
|
void setMediaFont(const std::string &name, const std::string &data);
|
||||||
|
|
||||||
|
void clearMediaFonts();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
irr::gui::IGUIFont *getFont(FontSpec spec, bool may_fail);
|
irr::gui::IGUIFont *getFont(FontSpec spec, bool may_fail);
|
||||||
|
|
||||||
/** update content of font cache in case of a setting change made it invalid */
|
/** update content of font cache in case of a setting change made it invalid */
|
||||||
void updateFontCache();
|
void updateCache();
|
||||||
|
|
||||||
/** initialize a new TTF font */
|
/** initialize a new TTF font */
|
||||||
gui::IGUIFont *initFont(const FontSpec &spec);
|
gui::IGUIFont *initFont(const FontSpec &spec);
|
||||||
|
@ -130,8 +137,10 @@ private:
|
||||||
/** update current minetest skin with font changes */
|
/** update current minetest skin with font changes */
|
||||||
void updateSkin();
|
void updateSkin();
|
||||||
|
|
||||||
/** clean cache */
|
void clearCache();
|
||||||
void cleanCache();
|
|
||||||
|
/** refresh after fonts have been changed */
|
||||||
|
void refresh();
|
||||||
|
|
||||||
/** pointer to irrlicht gui environment */
|
/** pointer to irrlicht gui environment */
|
||||||
gui::IGUIEnvironment* m_env = nullptr;
|
gui::IGUIEnvironment* m_env = nullptr;
|
||||||
|
@ -142,6 +151,9 @@ private:
|
||||||
/** internal storage for caching fonts of different size */
|
/** internal storage for caching fonts of different size */
|
||||||
std::map<unsigned int, irr::gui::IGUIFont*> m_font_cache[FM_MaxMode << 2];
|
std::map<unsigned int, irr::gui::IGUIFont*> m_font_cache[FM_MaxMode << 2];
|
||||||
|
|
||||||
|
/** media-provided faces, indexed by filename (without extension) */
|
||||||
|
std::unordered_map<std::string, irr_ptr<gui::SGUITTFace>> m_media_faces;
|
||||||
|
|
||||||
/** default font size to use */
|
/** default font size to use */
|
||||||
unsigned int m_default_size[FM_MaxMode];
|
unsigned int m_default_size[FM_MaxMode];
|
||||||
|
|
||||||
|
|
|
@ -31,43 +31,105 @@
|
||||||
john@suckerfreegames.com
|
john@suckerfreegames.com
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include "CGUITTFont.h"
|
||||||
|
|
||||||
|
#include "irr_ptr.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "filesys.h"
|
#include "filesys.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
#include "CGUITTFont.h"
|
|
||||||
#include "IFileSystem.h"
|
#include "IFileSystem.h"
|
||||||
#include "IGUIEnvironment.h"
|
#include "IGUIEnvironment.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace irr
|
namespace irr
|
||||||
{
|
{
|
||||||
namespace gui
|
namespace gui
|
||||||
{
|
{
|
||||||
|
|
||||||
// Manages the FT_Face cache.
|
std::map<io::path, SGUITTFace*> SGUITTFace::faces;
|
||||||
struct SGUITTFace : public irr::IReferenceCounted
|
FT_Library SGUITTFace::freetype_library = nullptr;
|
||||||
|
std::size_t SGUITTFace::n_faces = 0;
|
||||||
|
|
||||||
|
FT_Library SGUITTFace::getFreeTypeLibrary()
|
||||||
{
|
{
|
||||||
SGUITTFace()
|
if (freetype_library)
|
||||||
|
return freetype_library;
|
||||||
|
FT_Library ft;
|
||||||
|
if (FT_Init_FreeType(&ft))
|
||||||
|
FATAL_ERROR("initializing freetype failed");
|
||||||
|
freetype_library = ft;
|
||||||
|
return freetype_library;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGUITTFace::SGUITTFace(std::string &&buffer) : face_buffer(std::move(buffer))
|
||||||
{
|
{
|
||||||
memset((void*)&face, 0, sizeof(FT_Face));
|
memset((void*)&face, 0, sizeof(FT_Face));
|
||||||
|
n_faces++;
|
||||||
}
|
}
|
||||||
|
|
||||||
~SGUITTFace()
|
SGUITTFace::~SGUITTFace()
|
||||||
{
|
{
|
||||||
FT_Done_Face(face);
|
FT_Done_Face(face);
|
||||||
|
n_faces--;
|
||||||
|
// If there are no more faces referenced by FreeType, clean up.
|
||||||
|
if (n_faces == 0) {
|
||||||
|
assert(freetype_library);
|
||||||
|
FT_Done_FreeType(freetype_library);
|
||||||
|
freetype_library = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FT_Face face;
|
SGUITTFace* SGUITTFace::createFace(std::string &&buffer)
|
||||||
std::string face_buffer;
|
{
|
||||||
};
|
irr_ptr<SGUITTFace> face(new SGUITTFace(std::move(buffer)));
|
||||||
|
auto ft = getFreeTypeLibrary();
|
||||||
|
if (!ft)
|
||||||
|
return nullptr;
|
||||||
|
return (FT_New_Memory_Face(ft,
|
||||||
|
reinterpret_cast<const FT_Byte*>(face->face_buffer.data()),
|
||||||
|
face->face_buffer.size(), 0, &face->face))
|
||||||
|
? nullptr : face.release();
|
||||||
|
}
|
||||||
|
|
||||||
// Static variables.
|
SGUITTFace* SGUITTFace::loadFace(const io::path &filename)
|
||||||
FT_Library CGUITTFont::c_library;
|
{
|
||||||
std::map<io::path, SGUITTFace*> CGUITTFont::c_faces;
|
auto it = faces.find(filename);
|
||||||
bool CGUITTFont::c_libraryLoaded = false;
|
if (it != faces.end()) {
|
||||||
|
it->second->grab();
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
std::string buffer;
|
||||||
|
if (!fs::ReadFile(filename.c_str(), buffer, true)) {
|
||||||
|
errorstream << "CGUITTFont: Reading file " << filename.c_str() << " failed." << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *face = SGUITTFace::createFace(std::move(buffer));
|
||||||
|
if (!face) {
|
||||||
|
errorstream << "CGUITTFont: FT_New_Memory_Face failed." << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
faces.emplace(filename, face);
|
||||||
|
return face;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SGUITTFace::dropFilename()
|
||||||
|
{
|
||||||
|
if (!filename.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = faces.find(*filename);
|
||||||
|
if (it == faces.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SGUITTFace* f = it->second;
|
||||||
|
// Drop our face. If this was the last face, the destructor will clean up.
|
||||||
|
if (f->drop())
|
||||||
|
faces.erase(*filename);
|
||||||
|
}
|
||||||
|
|
||||||
video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
|
video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
|
||||||
{
|
{
|
||||||
|
@ -203,17 +265,12 @@ void SGUITTGlyph::unload()
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
|
|
||||||
CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency, const u32 shadow, const u32 shadow_alpha)
|
CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env,
|
||||||
|
SGUITTFace *face, u32 size, bool antialias,
|
||||||
|
bool transparency, u32 shadow, u32 shadow_alpha)
|
||||||
{
|
{
|
||||||
if (!c_libraryLoaded)
|
|
||||||
{
|
|
||||||
if (FT_Init_FreeType(&c_library))
|
|
||||||
return 0;
|
|
||||||
c_libraryLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CGUITTFont* font = new CGUITTFont(env);
|
CGUITTFont* font = new CGUITTFont(env);
|
||||||
bool ret = font->load(filename, size, antialias, transparency);
|
bool ret = font->load(face, size, antialias, transparency);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
font->drop();
|
font->drop();
|
||||||
|
@ -246,54 +303,20 @@ shadow_offset(0), shadow_alpha(0), fallback(0)
|
||||||
setInvisibleCharacters(L" ");
|
setInvisibleCharacters(L" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
|
bool CGUITTFont::load(SGUITTFace *face, const u32 size, const bool antialias, const bool transparency)
|
||||||
{
|
{
|
||||||
// Some sanity checks.
|
if (!Driver || size == 0 || !face)
|
||||||
if (!Driver) return false;
|
return false;
|
||||||
if (size == 0) return false;
|
|
||||||
if (filename.empty()) return false;
|
|
||||||
|
|
||||||
this->size = size;
|
this->size = size;
|
||||||
this->filename = filename;
|
|
||||||
|
|
||||||
// Update the font loading flags when the font is first loaded.
|
// Update the font loading flags when the font is first loaded.
|
||||||
this->use_monochrome = !antialias;
|
this->use_monochrome = !antialias;
|
||||||
this->use_transparency = transparency;
|
this->use_transparency = transparency;
|
||||||
update_load_flags();
|
update_load_flags();
|
||||||
|
|
||||||
infostream << "CGUITTFont: Creating new font: " << filename.c_str() << " "
|
|
||||||
<< size << "pt " << (antialias ? "+antialias " : "-antialias ")
|
|
||||||
<< (transparency ? "+transparency" : "-transparency") << std::endl;
|
|
||||||
|
|
||||||
// Grab the face.
|
|
||||||
SGUITTFace* face = nullptr;
|
|
||||||
auto node = c_faces.find(filename);
|
|
||||||
if (node == c_faces.end()) {
|
|
||||||
face = new SGUITTFace();
|
|
||||||
|
|
||||||
if (!fs::ReadFile(filename.c_str(), face->face_buffer, true)) {
|
|
||||||
delete face;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the face.
|
|
||||||
if (FT_New_Memory_Face(c_library,
|
|
||||||
reinterpret_cast<const FT_Byte*>(face->face_buffer.data()),
|
|
||||||
face->face_buffer.size(), 0, &face->face))
|
|
||||||
{
|
|
||||||
errorstream << "CGUITTFont: FT_New_Memory_Face failed." << std::endl;
|
|
||||||
delete face;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
c_faces.emplace(filename, face);
|
|
||||||
} else {
|
|
||||||
// Using another instance of this face.
|
|
||||||
face = node->second;
|
|
||||||
face->grab();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store our face.
|
// Store our face.
|
||||||
|
face->grab();
|
||||||
tt_face = face->face;
|
tt_face = face->face;
|
||||||
|
|
||||||
// Store font metrics.
|
// Store font metrics.
|
||||||
|
@ -322,24 +345,6 @@ CGUITTFont::~CGUITTFont()
|
||||||
reset_images();
|
reset_images();
|
||||||
Glyphs.clear();
|
Glyphs.clear();
|
||||||
|
|
||||||
// We aren't using this face anymore.
|
|
||||||
auto n = c_faces.find(filename);
|
|
||||||
if (n != c_faces.end())
|
|
||||||
{
|
|
||||||
SGUITTFace* f = n->second;
|
|
||||||
|
|
||||||
// Drop our face. If this was the last face, the destructor will clean up.
|
|
||||||
if (f->drop())
|
|
||||||
c_faces.erase(filename);
|
|
||||||
|
|
||||||
// If there are no more faces referenced by FreeType, clean up.
|
|
||||||
if (c_faces.empty())
|
|
||||||
{
|
|
||||||
FT_Done_FreeType(c_library);
|
|
||||||
c_libraryLoaded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop our driver now.
|
// Drop our driver now.
|
||||||
if (Driver)
|
if (Driver)
|
||||||
Driver->drop();
|
Driver->drop();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
Copyright (c) 2009-2010 John Norman
|
Copyright (c) 2009-2010 John Norman
|
||||||
Copyright (c) 2016 Nathanaëlle Courant
|
Copyright (c) 2016 Nathanaëlle Courant
|
||||||
Copyright (c) 2023 Caleb Butler
|
Copyright (c) 2023 Caleb Butler
|
||||||
|
Copyright (c) 2024 Luanti contributors
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
This software is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the authors be held liable for any
|
warranty. In no event will the authors be held liable for any
|
||||||
|
@ -32,23 +33,52 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#include FT_FREETYPE_H
|
#include <freetype/freetype.h>
|
||||||
|
|
||||||
#include "IGUIEnvironment.h"
|
#include "IGUIEnvironment.h"
|
||||||
#include "IGUIFont.h"
|
#include "IGUIFont.h"
|
||||||
#include "IVideoDriver.h"
|
#include "IVideoDriver.h"
|
||||||
#include "IrrlichtDevice.h"
|
#include "IrrlichtDevice.h"
|
||||||
#include "SMesh.h"
|
|
||||||
#include "util/enriched_string.h"
|
#include "util/enriched_string.h"
|
||||||
#include "util/basic_macros.h"
|
#include "util/basic_macros.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace irr
|
namespace irr
|
||||||
{
|
{
|
||||||
namespace gui
|
namespace gui
|
||||||
{
|
{
|
||||||
struct SGUITTFace;
|
// Manages the FT_Face cache.
|
||||||
|
struct SGUITTFace : public irr::IReferenceCounted
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
static std::map<io::path, SGUITTFace*> faces;
|
||||||
|
static FT_Library freetype_library;
|
||||||
|
static std::size_t n_faces;
|
||||||
|
|
||||||
|
static FT_Library getFreeTypeLibrary();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SGUITTFace(std::string &&buffer);
|
||||||
|
|
||||||
|
~SGUITTFace();
|
||||||
|
|
||||||
|
std::optional<std::string> filename;
|
||||||
|
|
||||||
|
FT_Face face;
|
||||||
|
/// Must not be deallocated until we are done with the face!
|
||||||
|
std::string face_buffer;
|
||||||
|
|
||||||
|
static SGUITTFace* createFace(std::string &&buffer);
|
||||||
|
|
||||||
|
static SGUITTFace* loadFace(const io::path &filename);
|
||||||
|
|
||||||
|
void dropFilename();
|
||||||
|
};
|
||||||
class CGUITTFont;
|
class CGUITTFont;
|
||||||
|
|
||||||
//! Structure representing a single TrueType glyph.
|
//! Structure representing a single TrueType glyph.
|
||||||
|
@ -215,12 +245,13 @@ namespace gui
|
||||||
public:
|
public:
|
||||||
//! Creates a new TrueType font and returns a pointer to it. The pointer must be drop()'ed when finished.
|
//! Creates a new TrueType font and returns a pointer to it. The pointer must be drop()'ed when finished.
|
||||||
//! \param env The IGUIEnvironment the font loads out of.
|
//! \param env The IGUIEnvironment the font loads out of.
|
||||||
//! \param filename The filename of the font.
|
|
||||||
//! \param size The size of the font glyphs in pixels. Since this is the size of the individual glyphs, the true height of the font may change depending on the characters used.
|
//! \param size The size of the font glyphs in pixels. Since this is the size of the individual glyphs, the true height of the font may change depending on the characters used.
|
||||||
//! \param antialias set the use_monochrome (opposite to antialias) flag
|
//! \param antialias set the use_monochrome (opposite to antialias) flag
|
||||||
//! \param transparency set the use_transparency flag
|
//! \param transparency set the use_transparency flag
|
||||||
//! \return Returns a pointer to a CGUITTFont. Will return 0 if the font failed to load.
|
//! \return Returns a pointer to a CGUITTFont. Will return 0 if the font failed to load.
|
||||||
static CGUITTFont* createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true, const u32 shadow = 0, const u32 shadow_alpha = 255);
|
static CGUITTFont* createTTFont(IGUIEnvironment *env,
|
||||||
|
SGUITTFace *face, u32 size, bool antialias = true,
|
||||||
|
bool transparency = true, u32 shadow = 0, u32 shadow_alpha = 255);
|
||||||
|
|
||||||
//! Destructor
|
//! Destructor
|
||||||
virtual ~CGUITTFont();
|
virtual ~CGUITTFont();
|
||||||
|
@ -329,11 +360,6 @@ namespace gui
|
||||||
core::dimension2du max_page_texture_size;
|
core::dimension2du max_page_texture_size;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Manages the FreeType library.
|
|
||||||
static FT_Library c_library;
|
|
||||||
static std::map<io::path, SGUITTFace*> c_faces;
|
|
||||||
static bool c_libraryLoaded;
|
|
||||||
|
|
||||||
// Helper functions for the same-named public member functions above
|
// Helper functions for the same-named public member functions above
|
||||||
// (Since std::u32string is nicer to work with than wchar_t *)
|
// (Since std::u32string is nicer to work with than wchar_t *)
|
||||||
core::dimension2d<u32> getDimension(const std::u32string& text) const;
|
core::dimension2d<u32> getDimension(const std::u32string& text) const;
|
||||||
|
@ -343,7 +369,7 @@ namespace gui
|
||||||
std::u32string convertWCharToU32String(const wchar_t* const) const;
|
std::u32string convertWCharToU32String(const wchar_t* const) const;
|
||||||
|
|
||||||
CGUITTFont(IGUIEnvironment *env);
|
CGUITTFont(IGUIEnvironment *env);
|
||||||
bool load(const io::path& filename, const u32 size, const bool antialias, const bool transparency);
|
bool load(SGUITTFace *face, const u32 size, const bool antialias, const bool transparency);
|
||||||
void reset_images();
|
void reset_images();
|
||||||
void update_glyph_pages() const;
|
void update_glyph_pages() const;
|
||||||
void update_load_flags()
|
void update_load_flags()
|
||||||
|
@ -361,7 +387,7 @@ namespace gui
|
||||||
core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const;
|
core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const;
|
||||||
|
|
||||||
video::IVideoDriver* Driver;
|
video::IVideoDriver* Driver;
|
||||||
io::path filename;
|
std::optional<io::path> filename;
|
||||||
FT_Face tt_face;
|
FT_Face tt_face;
|
||||||
FT_Size_Metrics font_metrics;
|
FT_Size_Metrics font_metrics;
|
||||||
FT_Int32 load_flags;
|
FT_Int32 load_flags;
|
||||||
|
|
|
@ -2548,6 +2548,8 @@ bool Server::addMediaFile(const std::string &filename,
|
||||||
".x", ".b3d", ".obj", ".gltf", ".glb",
|
".x", ".b3d", ".obj", ".gltf", ".glb",
|
||||||
// Translation file formats
|
// Translation file formats
|
||||||
".tr", ".po", ".mo",
|
".tr", ".po", ".mo",
|
||||||
|
// Fonts
|
||||||
|
".ttf", ".woff",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
if (removeStringEnd(filename, supported_ext).empty()) {
|
if (removeStringEnd(filename, supported_ext).empty()) {
|
||||||
|
|
|
@ -87,5 +87,6 @@ void ServerModManager::getModsMediaPaths(std::vector<std::string> &paths) const
|
||||||
fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "media");
|
fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "media");
|
||||||
fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "models");
|
fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "models");
|
||||||
fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "locale");
|
fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "locale");
|
||||||
|
fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "fonts");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue