mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-06 17:41:04 +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
|
@ -31,43 +31,105 @@
|
|||
john@suckerfreegames.com
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "CGUITTFont.h"
|
||||
|
||||
#include "irr_ptr.h"
|
||||
#include "log.h"
|
||||
#include "filesys.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "CGUITTFont.h"
|
||||
#include "IFileSystem.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
// Manages the FT_Face cache.
|
||||
struct SGUITTFace : public irr::IReferenceCounted
|
||||
std::map<io::path, SGUITTFace*> SGUITTFace::faces;
|
||||
FT_Library SGUITTFace::freetype_library = nullptr;
|
||||
std::size_t SGUITTFace::n_faces = 0;
|
||||
|
||||
FT_Library SGUITTFace::getFreeTypeLibrary()
|
||||
{
|
||||
SGUITTFace()
|
||||
{
|
||||
memset((void*)&face, 0, sizeof(FT_Face));
|
||||
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));
|
||||
n_faces++;
|
||||
}
|
||||
|
||||
SGUITTFace::~SGUITTFace()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
SGUITTFace* SGUITTFace::createFace(std::string &&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();
|
||||
}
|
||||
|
||||
SGUITTFace* SGUITTFace::loadFace(const io::path &filename)
|
||||
{
|
||||
auto it = faces.find(filename);
|
||||
if (it != faces.end()) {
|
||||
it->second->grab();
|
||||
return it->second;
|
||||
}
|
||||
|
||||
~SGUITTFace()
|
||||
{
|
||||
FT_Done_Face(face);
|
||||
std::string buffer;
|
||||
if (!fs::ReadFile(filename.c_str(), buffer, true)) {
|
||||
errorstream << "CGUITTFont: Reading file " << filename.c_str() << " failed." << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FT_Face face;
|
||||
std::string face_buffer;
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
// Static variables.
|
||||
FT_Library CGUITTFont::c_library;
|
||||
std::map<io::path, SGUITTFace*> CGUITTFont::c_faces;
|
||||
bool CGUITTFont::c_libraryLoaded = false;
|
||||
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
|
||||
{
|
||||
|
@ -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);
|
||||
bool ret = font->load(filename, size, antialias, transparency);
|
||||
bool ret = font->load(face, size, antialias, transparency);
|
||||
if (!ret)
|
||||
{
|
||||
font->drop();
|
||||
|
@ -246,54 +303,20 @@ shadow_offset(0), shadow_alpha(0), fallback(0)
|
|||
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) return false;
|
||||
if (size == 0) return false;
|
||||
if (filename.empty()) return false;
|
||||
if (!Driver || size == 0 || !face)
|
||||
return false;
|
||||
|
||||
this->size = size;
|
||||
this->filename = filename;
|
||||
|
||||
// Update the font loading flags when the font is first loaded.
|
||||
this->use_monochrome = !antialias;
|
||||
this->use_transparency = transparency;
|
||||
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.
|
||||
face->grab();
|
||||
tt_face = face->face;
|
||||
|
||||
// Store font metrics.
|
||||
|
@ -322,24 +345,6 @@ CGUITTFont::~CGUITTFont()
|
|||
reset_images();
|
||||
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.
|
||||
if (Driver)
|
||||
Driver->drop();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
Copyright (c) 2009-2010 John Norman
|
||||
Copyright (c) 2016 Nathanaëlle Courant
|
||||
Copyright (c) 2023 Caleb Butler
|
||||
Copyright (c) 2024 Luanti contributors
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
|
@ -32,23 +33,52 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include <freetype/freetype.h>
|
||||
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IrrlichtDevice.h"
|
||||
#include "SMesh.h"
|
||||
#include "util/enriched_string.h"
|
||||
#include "util/basic_macros.h"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
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;
|
||||
|
||||
//! Structure representing a single TrueType glyph.
|
||||
|
@ -215,12 +245,13 @@ namespace gui
|
|||
public:
|
||||
//! 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 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 antialias set the use_monochrome (opposite to antialias) flag
|
||||
//! \param transparency set the use_transparency flag
|
||||
//! \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
|
||||
virtual ~CGUITTFont();
|
||||
|
@ -329,11 +360,6 @@ namespace gui
|
|||
core::dimension2du max_page_texture_size;
|
||||
|
||||
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
|
||||
// (Since std::u32string is nicer to work with than wchar_t *)
|
||||
core::dimension2d<u32> getDimension(const std::u32string& text) const;
|
||||
|
@ -343,7 +369,7 @@ namespace gui
|
|||
std::u32string convertWCharToU32String(const wchar_t* const) const;
|
||||
|
||||
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 update_glyph_pages() const;
|
||||
void update_load_flags()
|
||||
|
@ -361,7 +387,7 @@ namespace gui
|
|||
core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const;
|
||||
|
||||
video::IVideoDriver* Driver;
|
||||
io::path filename;
|
||||
std::optional<io::path> filename;
|
||||
FT_Face tt_face;
|
||||
FT_Size_Metrics font_metrics;
|
||||
FT_Int32 load_flags;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue