Simplify 'FontHandles' management

This commit is contained in:
vittorioromeo 2023-04-17 23:39:36 +02:00 committed by Vittorio Romeo
parent 31c4b9472a
commit 230c6a4d57
2 changed files with 63 additions and 69 deletions

View File

@ -415,7 +415,7 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Types // Types
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
class FontHandles; struct FontHandles;
using PageTable = std::unordered_map<unsigned int, Page>; //!< Table mapping a character size to its page (texture) using PageTable = std::unordered_map<unsigned int, Page>; //!< Table mapping a character size to its page (texture)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -91,31 +91,34 @@ std::uint64_t combine(float outlineThickness, bool bold, std::uint32_t index)
namespace sf namespace sf
{ {
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
class Font::FontHandles struct Font::FontHandles
{ {
private: FontHandles() = default;
// Default constructible deleter functor
struct Deleter
{
void operator()(FT_Library theLibrary)
{
FT_Done_FreeType(theLibrary);
}
void operator()(FT_Face theFace)
{
FT_Done_Face(theFace);
}
void operator()(FT_Stroker theStroker)
{
FT_Stroker_Done(theStroker);
}
};
public: ~FontHandles()
std::unique_ptr<std::remove_pointer_t<FT_Library>, Deleter> library; //< Pointer to the internal library interface {
std::unique_ptr<FT_StreamRec> streamRec; //< Pointer to the stream rec instance // All the function below are safe to call with null pointer arguments.
std::unique_ptr<std::remove_pointer_t<FT_Face>, Deleter> face; //< Pointer to the internal font face // The documentation of FreeType isn't clear on the matter, but the
std::unique_ptr<std::remove_pointer_t<FT_Stroker>, Deleter> stroker; //< Pointer to the stroker // implementation does explictly check for null.
FT_Stroker_Done(stroker);
FT_Done_Face(face);
// `streamRec` doesn't need to be explicitly freed.
FT_Done_FreeType(library);
}
// clang-format off
FontHandles(const FontHandles&) = delete;
FontHandles& operator=(const FontHandles&) = delete;
FontHandles(FontHandles&&) = delete;
FontHandles& operator=(FontHandles&&) = delete;
// clang-format on
FT_Library library{}; //< Pointer to the internal library interface
FT_StreamRec streamRec{}; //< Stream rec object describing an input stream
FT_Face face{}; //< Pointer to the internal font face
FT_Stroker stroker{}; //< Pointer to the stroker
}; };
@ -155,36 +158,32 @@ bool Font::loadFromFile(const std::filesystem::path& filename)
// Cleanup the previous resources // Cleanup the previous resources
cleanup(); cleanup();
auto fontHandles = std::make_unique<FontHandles>(); auto fontHandles = std::make_shared<FontHandles>();
// Initialize FreeType // Initialize FreeType
// Note: we initialize FreeType for every font instance in order to avoid having a single // Note: we initialize FreeType for every font instance in order to avoid having a single
// global manager that would create a lot of issues regarding creation and destruction order. // global manager that would create a lot of issues regarding creation and destruction order.
FT_Library library; if (FT_Init_FreeType(&fontHandles->library) != 0)
if (FT_Init_FreeType(&library) != 0)
{ {
err() << "Failed to load font (failed to initialize FreeType)\n" << formatDebugPathInfo(filename) << std::endl; err() << "Failed to load font (failed to initialize FreeType)\n" << formatDebugPathInfo(filename) << std::endl;
return false; return false;
} }
fontHandles->library.reset(library);
// Load the new font face from the specified file // Load the new font face from the specified file
FT_Face face; FT_Face face;
if (FT_New_Face(library, filename.string().c_str(), 0, &face) != 0) if (FT_New_Face(fontHandles->library, filename.string().c_str(), 0, &face) != 0)
{ {
err() << "Failed to load font (failed to create the font face)\n" << formatDebugPathInfo(filename) << std::endl; err() << "Failed to load font (failed to create the font face)\n" << formatDebugPathInfo(filename) << std::endl;
return false; return false;
} }
fontHandles->face.reset(face); fontHandles->face = face;
// Load the stroker that will be used to outline the font // Load the stroker that will be used to outline the font
FT_Stroker stroker; if (FT_Stroker_New(fontHandles->library, &fontHandles->stroker) != 0)
if (FT_Stroker_New(library, &stroker) != 0)
{ {
err() << "Failed to load font (failed to create the stroker)\n" << formatDebugPathInfo(filename) << std::endl; err() << "Failed to load font (failed to create the stroker)\n" << formatDebugPathInfo(filename) << std::endl;
return false; return false;
} }
fontHandles->stroker.reset(stroker);
// Select the unicode character map // Select the unicode character map
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0) if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
@ -217,36 +216,36 @@ bool Font::loadFromMemory(const void* data, std::size_t sizeInBytes)
// Cleanup the previous resources // Cleanup the previous resources
cleanup(); cleanup();
auto fontHandles = std::make_unique<FontHandles>(); auto fontHandles = std::make_shared<FontHandles>();
// Initialize FreeType // Initialize FreeType
// Note: we initialize FreeType for every font instance in order to avoid having a single // Note: we initialize FreeType for every font instance in order to avoid having a single
// global manager that would create a lot of issues regarding creation and destruction order. // global manager that would create a lot of issues regarding creation and destruction order.
FT_Library library; if (FT_Init_FreeType(&fontHandles->library) != 0)
if (FT_Init_FreeType(&library) != 0)
{ {
err() << "Failed to load font from memory (failed to initialize FreeType)" << std::endl; err() << "Failed to load font from memory (failed to initialize FreeType)" << std::endl;
return false; return false;
} }
fontHandles->library.reset(library);
// Load the new font face from the specified file // Load the new font face from the specified file
FT_Face face; FT_Face face;
if (FT_New_Memory_Face(library, reinterpret_cast<const FT_Byte*>(data), static_cast<FT_Long>(sizeInBytes), 0, &face) != 0) if (FT_New_Memory_Face(fontHandles->library,
reinterpret_cast<const FT_Byte*>(data),
static_cast<FT_Long>(sizeInBytes),
0,
&face) != 0)
{ {
err() << "Failed to load font from memory (failed to create the font face)" << std::endl; err() << "Failed to load font from memory (failed to create the font face)" << std::endl;
return false; return false;
} }
fontHandles->face.reset(face); fontHandles->face = face;
// Load the stroker that will be used to outline the font // Load the stroker that will be used to outline the font
FT_Stroker stroker; if (FT_Stroker_New(fontHandles->library, &fontHandles->stroker) != 0)
if (FT_Stroker_New(library, &stroker) != 0)
{ {
err() << "Failed to load font from memory (failed to create the stroker)" << std::endl; err() << "Failed to load font from memory (failed to create the stroker)" << std::endl;
return false; return false;
} }
fontHandles->stroker.reset(stroker);
// Select the Unicode character map // Select the Unicode character map
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0) if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
@ -271,18 +270,16 @@ bool Font::loadFromStream(InputStream& stream)
// Cleanup the previous resources // Cleanup the previous resources
cleanup(); cleanup();
auto fontHandles = std::make_unique<FontHandles>(); auto fontHandles = std::make_shared<FontHandles>();
// Initialize FreeType // Initialize FreeType
// Note: we initialize FreeType for every font instance in order to avoid having a single // Note: we initialize FreeType for every font instance in order to avoid having a single
// global manager that would create a lot of issues regarding creation and destruction order. // global manager that would create a lot of issues regarding creation and destruction order.
FT_Library library; if (FT_Init_FreeType(&fontHandles->library) != 0)
if (FT_Init_FreeType(&library) != 0)
{ {
err() << "Failed to load font from stream (failed to initialize FreeType)" << std::endl; err() << "Failed to load font from stream (failed to initialize FreeType)" << std::endl;
return false; return false;
} }
fontHandles->library.reset(library);
// Make sure that the stream's reading position is at the beginning // Make sure that the stream's reading position is at the beginning
if (stream.seek(0) == -1) if (stream.seek(0) == -1)
@ -292,37 +289,34 @@ bool Font::loadFromStream(InputStream& stream)
} }
// Prepare a wrapper for our stream, that we'll pass to FreeType callbacks // Prepare a wrapper for our stream, that we'll pass to FreeType callbacks
fontHandles->streamRec = std::make_unique<FT_StreamRec>(); fontHandles->streamRec.base = nullptr;
fontHandles->streamRec->base = nullptr; fontHandles->streamRec.size = static_cast<unsigned long>(stream.getSize());
fontHandles->streamRec->size = static_cast<unsigned long>(stream.getSize()); fontHandles->streamRec.pos = 0;
fontHandles->streamRec->pos = 0; fontHandles->streamRec.descriptor.pointer = &stream;
fontHandles->streamRec->descriptor.pointer = &stream; fontHandles->streamRec.read = &read;
fontHandles->streamRec->read = &read; fontHandles->streamRec.close = &close;
fontHandles->streamRec->close = &close;
// Setup the FreeType callbacks that will read our stream // Setup the FreeType callbacks that will read our stream
FT_Open_Args args; FT_Open_Args args;
args.flags = FT_OPEN_STREAM; args.flags = FT_OPEN_STREAM;
args.stream = fontHandles->streamRec.get(); args.stream = &fontHandles->streamRec;
args.driver = nullptr; args.driver = nullptr;
// Load the new font face from the specified stream // Load the new font face from the specified stream
FT_Face face; FT_Face face;
if (FT_Open_Face(library, &args, 0, &face) != 0) if (FT_Open_Face(fontHandles->library, &args, 0, &face) != 0)
{ {
err() << "Failed to load font from stream (failed to create the font face)" << std::endl; err() << "Failed to load font from stream (failed to create the font face)" << std::endl;
return false; return false;
} }
fontHandles->face.reset(face); fontHandles->face = face;
// Load the stroker that will be used to outline the font // Load the stroker that will be used to outline the font
FT_Stroker stroker; if (FT_Stroker_New(fontHandles->library, &fontHandles->stroker) != 0)
if (FT_Stroker_New(library, &stroker) != 0)
{ {
err() << "Failed to load font from stream (failed to create the stroker)" << std::endl; err() << "Failed to load font from stream (failed to create the stroker)" << std::endl;
return false; return false;
} }
fontHandles->stroker.reset(stroker);
// Select the Unicode character map // Select the Unicode character map
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0) if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
@ -357,7 +351,7 @@ const Glyph& Font::getGlyph(std::uint32_t codePoint, unsigned int characterSize,
// Build the key by combining the glyph index (based on code point), bold flag, and outline thickness // Build the key by combining the glyph index (based on code point), bold flag, and outline thickness
std::uint64_t key = combine(outlineThickness, std::uint64_t key = combine(outlineThickness,
bold, bold,
FT_Get_Char_Index(m_fontHandles ? m_fontHandles->face.get() : nullptr, codePoint)); FT_Get_Char_Index(m_fontHandles ? m_fontHandles->face : nullptr, codePoint));
// Search the glyph into the cache // Search the glyph into the cache
if (auto it = glyphs.find(key); it != glyphs.end()) if (auto it = glyphs.find(key); it != glyphs.end())
@ -377,7 +371,7 @@ const Glyph& Font::getGlyph(std::uint32_t codePoint, unsigned int characterSize,
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Font::hasGlyph(std::uint32_t codePoint) const bool Font::hasGlyph(std::uint32_t codePoint) const
{ {
return FT_Get_Char_Index(m_fontHandles ? m_fontHandles->face.get() : nullptr, codePoint) != 0; return FT_Get_Char_Index(m_fontHandles ? m_fontHandles->face : nullptr, codePoint) != 0;
} }
@ -388,7 +382,7 @@ float Font::getKerning(std::uint32_t first, std::uint32_t second, unsigned int c
if (first == 0 || second == 0) if (first == 0 || second == 0)
return 0.f; return 0.f;
auto* face = m_fontHandles ? m_fontHandles->face.get() : nullptr; FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
if (face && setCurrentSize(characterSize)) if (face && setCurrentSize(characterSize))
{ {
@ -426,7 +420,7 @@ float Font::getKerning(std::uint32_t first, std::uint32_t second, unsigned int c
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
float Font::getLineSpacing(unsigned int characterSize) const float Font::getLineSpacing(unsigned int characterSize) const
{ {
auto* face = m_fontHandles ? m_fontHandles->face.get() : nullptr; FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
if (face && setCurrentSize(characterSize)) if (face && setCurrentSize(characterSize))
{ {
@ -442,7 +436,7 @@ float Font::getLineSpacing(unsigned int characterSize) const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
float Font::getUnderlinePosition(unsigned int characterSize) const float Font::getUnderlinePosition(unsigned int characterSize) const
{ {
auto* face = m_fontHandles ? m_fontHandles->face.get() : nullptr; FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
if (face && setCurrentSize(characterSize)) if (face && setCurrentSize(characterSize))
{ {
@ -463,7 +457,7 @@ float Font::getUnderlinePosition(unsigned int characterSize) const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
float Font::getUnderlineThickness(unsigned int characterSize) const float Font::getUnderlineThickness(unsigned int characterSize) const
{ {
auto* face = m_fontHandles ? m_fontHandles->face.get() : nullptr; FT_Face face = m_fontHandles ? m_fontHandles->face : nullptr;
if (face && setCurrentSize(characterSize)) if (face && setCurrentSize(characterSize))
{ {
@ -557,7 +551,7 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
return glyph; return glyph;
// Get our FT_Face // Get our FT_Face
auto* face = m_fontHandles->face.get(); FT_Face face = m_fontHandles->face;
if (!face) if (!face)
return glyph; return glyph;
@ -590,7 +584,7 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
if (outlineThickness != 0) if (outlineThickness != 0)
{ {
auto* stroker = m_fontHandles->stroker.get(); FT_Stroker stroker = m_fontHandles->stroker;
FT_Stroker_Set(stroker, FT_Stroker_Set(stroker,
static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)), static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)),
@ -612,7 +606,7 @@ Glyph Font::loadGlyph(std::uint32_t codePoint, unsigned int characterSize, bool
if (!outline) if (!outline)
{ {
if (bold) if (bold)
FT_Bitmap_Embolden(m_fontHandles->library.get(), &bitmap, weight, weight); FT_Bitmap_Embolden(m_fontHandles->library, &bitmap, weight, weight);
if (outlineThickness != 0) if (outlineThickness != 0)
err() << "Failed to outline glyph (no fallback available)" << std::endl; err() << "Failed to outline glyph (no fallback available)" << std::endl;
@ -799,7 +793,7 @@ bool Font::setCurrentSize(unsigned int characterSize) const
// only when necessary to avoid killing performances // only when necessary to avoid killing performances
// m_fontHandles and m_fontHandles->face are checked to be non-null before calling this method // m_fontHandles and m_fontHandles->face are checked to be non-null before calling this method
auto* face = m_fontHandles->face.get(); FT_Face face = m_fontHandles->face;
FT_UShort currentSize = face->size->metrics.x_ppem; FT_UShort currentSize = face->size->metrics.x_ppem;
if (currentSize != characterSize) if (currentSize != characterSize)