diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index 980752149..65a49cbfb 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -31,7 +31,11 @@ john@suckerfreegames.com */ +#include +#include #include +#include +#include "irr_ptr.h" #include "log.h" #include "filesys.h" #include "debug.h" @@ -45,29 +49,79 @@ namespace irr namespace gui { -// Manages the FT_Face cache. -struct SGUITTFace : public irr::IReferenceCounted +std::map SGUITTFace::faces; +std::optional SGUITTFace::freetype_library; + +std::optional SGUITTFace::getFreeTypeLibrary() { - SGUITTFace() - { - memset((void*)&face, 0, sizeof(FT_Face)); + if (freetype_library) return *freetype_library; + FT_Library ft; + if (FT_Init_FreeType(&ft)) + return std::nullopt; // TODO can't we just bail out entirely if this fails? + return freetype_library = ft; +} + +SGUITTFace::SGUITTFace(std::string &&buffer) : face_buffer(std::move(buffer)) +{ + memset((void*)&face, 0, sizeof(FT_Face)); +} + +SGUITTFace::~SGUITTFace() +{ + FT_Done_Face(face); +} + +SGUITTFace* SGUITTFace::createFace(std::string &&buffer) +{ + irr_ptr face(new SGUITTFace(std::move(buffer))); + auto ft = getFreeTypeLibrary(); + if (!ft) return nullptr; + return (FT_New_Memory_Face(*ft, + reinterpret_cast(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 CGUITTFont::c_faces; -bool CGUITTFont::c_libraryLoaded = false; +void SGUITTFace::dropFilename(const io::path &filename) +{ + 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); + + // If there are no more faces referenced by FreeType, clean up. + if (faces.empty()) { + assert(freetype_library); + FT_Done_FreeType(*freetype_library); + freetype_library = std::nullopt; + } +} video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const { @@ -203,15 +257,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) -{ - if (!c_libraryLoaded) - { - if (FT_Init_FreeType(&c_library)) - return 0; - c_libraryLoaded = true; - } +// TODO constructor which takes bogus filename and data +CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, + const io::path& filename, u32 size, bool antialias, + bool transparency, u32 shadow, u32 shadow_alpha) +{ CGUITTFont* font = new CGUITTFont(env); bool ret = font->load(filename, size, antialias, transparency); if (!ret) @@ -266,32 +317,9 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia << (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(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(); - } + auto *face = SGUITTFace::loadFace(filename); + if (!face) + return false; // Store our face. tt_face = face->face; @@ -323,22 +351,7 @@ CGUITTFont::~CGUITTFont() 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; - } - } + SGUITTFace::dropFilename(filename); // Drop our driver now. if (Driver) diff --git a/src/irrlicht_changes/CGUITTFont.h b/src/irrlicht_changes/CGUITTFont.h index c010cf181..11a918045 100644 --- a/src/irrlicht_changes/CGUITTFont.h +++ b/src/irrlicht_changes/CGUITTFont.h @@ -33,6 +33,7 @@ #pragma once #include + #include #include FT_FREETYPE_H @@ -44,11 +45,42 @@ #include "util/enriched_string.h" #include "util/basic_macros.h" +// TODO these should be in the C++ when the struct is properly split into C++ & header +#include +#include "irr_ptr.h" +#include "log.h" +#include "filesys.h" + namespace irr { namespace gui { - struct SGUITTFace; + // Manages the FT_Face cache. + struct SGUITTFace : public irr::IReferenceCounted + { + private: + + static std::map faces; + static std::optional freetype_library; + + static std::optional getFreeTypeLibrary(); + + public: + + SGUITTFace(std::string &&buffer); + + ~SGUITTFace(); + + 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); + + static void dropFilename(const io::path &filename); + }; class CGUITTFont; //! Structure representing a single TrueType glyph. @@ -220,7 +252,9 @@ namespace gui //! \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, + const io::path& filename, u32 size, bool antialias = true, + bool transparency = true, u32 shadow = 0, u32 shadow_alpha = 255); //! Destructor virtual ~CGUITTFont(); @@ -329,11 +363,6 @@ namespace gui core::dimension2du max_page_texture_size; private: - // Manages the FreeType library. - static FT_Library c_library; - static std::map 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 getDimension(const std::u32string& text) const;