From 6b277e508b024125227a1b55fb46fea4f026e692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 24 Jul 2026 01:54:51 -0300 Subject: [PATCH] migrate fonts to scoped resource ownership - replace FontManager with per-ResourceScope FontService instances - publish font factories into explicit or default resource scopes - import default resources into UI scene scopes unless explicitly disabled - keep related font faces strongly owned without hot-path weak_ptr locking - add cached glyph advance queries without creating texture pages - fix font picker preview ownership, cleanup, and rapid switching - selectively evict TextLayout cache entries when referenced fonts unload - document FontService, ResourceCatalog, and cache invalidation semantics - migrate examples, tools, benchmarks, and tests to the scoped font API - expand coverage for font ownership, texture release, and layout caching --- .../resource_shared_ownership_architecture.md | 31 +- include/eepp/core/lrucache.hpp | 27 ++ include/eepp/graphics.hpp | 6 +- include/eepp/graphics/font.hpp | 6 + include/eepp/graphics/fontbmfont.hpp | 15 +- include/eepp/graphics/fontfamily.hpp | 4 +- include/eepp/graphics/fontmanager.hpp | 70 ---- include/eepp/graphics/fontservice.hpp | 135 +++++++ include/eepp/graphics/fontsprite.hpp | 12 +- include/eepp/graphics/fonttruetype.hpp | 59 +-- include/eepp/graphics/resource.hpp | 11 + include/eepp/graphics/resourcecatalog.hpp | 94 ++++- include/eepp/graphics/resourcescope.hpp | 52 ++- include/eepp/graphics/textlayout.hpp | 11 +- include/eepp/ui/tools/uifontpickerdialog.hpp | 7 +- include/eepp/ui/uiscenenode.hpp | 21 +- projects/linux/ee.files | 4 +- projects/macos/ee.files | 4 +- projects/windows/ee.files | 4 +- src/benchmarks/inline_layout_benchmark.cpp | 6 +- src/eepp/graphics/font.cpp | 13 +- src/eepp/graphics/fontbmfont.cpp | 29 +- src/eepp/graphics/fontfamily.cpp | 14 +- src/eepp/graphics/fontmanager.cpp | 131 ------- src/eepp/graphics/fontservice.cpp | 162 +++++++++ src/eepp/graphics/fontsprite.cpp | 24 +- src/eepp/graphics/fonttruetype.cpp | 339 +++++++++++------- src/eepp/graphics/resourcecatalog.cpp | 102 +++++- src/eepp/graphics/resourcescope.cpp | 107 +++++- src/eepp/graphics/richtext.cpp | 2 +- src/eepp/graphics/text.cpp | 32 +- src/eepp/graphics/textlayout.cpp | 38 +- src/eepp/graphics/texturefactory.cpp | 1 - src/eepp/ui/css/drawableimageparser.cpp | 39 +- src/eepp/ui/tools/uifontpickerdialog.cpp | 67 +++- src/eepp/ui/uiapplication.cpp | 17 +- src/eepp/ui/uicodeeditor.cpp | 4 +- src/eepp/ui/uiconsole.cpp | 4 +- src/eepp/ui/uilistbox.cpp | 1 - src/eepp/ui/uirichtext.cpp | 1 - src/eepp/ui/uiscenenode.cpp | 84 +++-- src/eepp/ui/uistyle.cpp | 1 - src/eepp/ui/uitabwidget.cpp | 1 - src/eepp/ui/uitextinput.cpp | 7 +- src/eepp/ui/uitextspan.cpp | 1 - src/eepp/ui/uitextview.cpp | 1 - src/eepp/ui/uitooltip.cpp | 1 - src/eepp/window/backend/SDL2/windowsdl2.cpp | 2 - src/eepp/window/backend/SDL3/windowsdl3.cpp | 2 - src/eepp/window/engine.cpp | 3 - src/examples/fonts/fonts.cpp | 15 +- src/examples/richtext/richtext.cpp | 2 +- .../ui_font_picker/ui_font_picker.cpp | 11 +- .../ui_hello_world/ui_hello_world.cpp | 9 +- src/examples/ui_html/ui_html.cpp | 12 +- .../src/eterm/terminal/terminaldisplay.cpp | 9 +- src/tests/test_all/test.cpp | 4 +- src/tests/ui_perf_test/ui_perf_test.cpp | 10 +- .../unit_tests/drawableimageparser_tests.cpp | 40 +-- src/tests/unit_tests/fontrendering_tests.cpp | 198 +++++++--- src/tests/unit_tests/htmlsvg_tests.cpp | 2 +- .../resource_prerequisite_tests.cpp | 123 ++++++- src/tests/unit_tests/richtext_tests.cpp | 36 +- .../unit_tests/systemfontresolver_tests.cpp | 14 +- .../unit_tests/uicss_inheritance_tests.cpp | 2 +- .../unit_tests/uifontpickerdialog_tests.cpp | 163 ++++++++- src/tests/unit_tests/uihtml_flex_test.cpp | 2 +- src/tests/unit_tests/uihtml_float_tests.cpp | 4 +- src/tests/unit_tests/uihtml_grid_test.cpp | 2 +- .../unit_tests/uihtml_position_tests.cpp | 2 +- src/tests/unit_tests/uihtml_tests.cpp | 57 ++- src/tests/unit_tests/uihtmlform_tests.cpp | 2 +- src/tests/unit_tests/uiscenenode_tests.cpp | 5 +- src/tests/unit_tests/uitextnode_tests.cpp | 4 +- src/tests/unit_tests/uiwebview_tests.cpp | 91 ++--- src/tools/ecode/ecode.cpp | 18 +- src/tools/ecode/fontpickercontroller.cpp | 55 +-- src/tools/ecode/settingsmenu.cpp | 5 +- src/tools/eterm/eterm.cpp | 8 +- src/tools/mapeditor/mapeditor.cpp | 9 +- .../textureatlaseditor/textureatlaseditor.cpp | 11 +- src/tools/uieditor/uieditor.cpp | 26 +- src/tools/uieditor/uieditor.hpp | 8 +- 83 files changed, 1940 insertions(+), 828 deletions(-) delete mode 100644 include/eepp/graphics/fontmanager.hpp create mode 100644 include/eepp/graphics/fontservice.hpp delete mode 100644 src/eepp/graphics/fontmanager.cpp create mode 100644 src/eepp/graphics/fontservice.cpp diff --git a/.agent/plans/resource_shared_ownership_architecture.md b/.agent/plans/resource_shared_ownership_architecture.md index 37a745b0b..a052197c0 100644 --- a/.agent/plans/resource_shared_ownership_architecture.md +++ b/.agent/plans/resource_shared_ownership_architecture.md @@ -233,9 +233,11 @@ Frozen lookup rules: - Never search the live registry. - Never implicitly search a parent, host scene, sibling scene, or every live resource. - The default Graphics scope imports the global catalog explicitly. -- A UI/application scene receives only the catalogs deliberately imported into it. -- A Web document does not inherit host/global resources unless the host exports and imports them - intentionally. +- A UI/application scene imports the default resource catalog automatically for the common case; + callers can disable this at construction for strict isolation and then import only the catalogs + they deliberately expose. +- A Web document receives the same default-catalog baseline unless created with automatic import + disabled. It never implicitly inherits host, sibling, or other document-local catalogs. - Scopes import catalogs, not arbitrary scopes. This avoids recursive lookup and import cycles. Pure `EE::Graphics` users may use TextureFactory for unpinned creation or Engine's default Graphics @@ -566,7 +568,7 @@ focused regression coverage while preserving current raw factory ownership: - Externally executed HTTP tasks cannot retain a dangling raw Http after Pool destruction. - TextureAtlasLoader joins/stops ResourceLoader work before callback-visible loader state is destroyed. -- Engine destroys ShaderProgramManager before Renderer and clears TextLayout before FontManager. +- Engine clears TextLayout before destroying the default ResourceScope and its FontService. - Engine stops asynchronous resource producers before resource consumers and GPU managers. - UISceneNode's static async delivery queue has an explicit shutdown purge/rejection boundary. - Obsolete FrameBuffer context-loss reload APIs were removed. @@ -808,6 +810,17 @@ raw-owning ResourceManager subclass one family at a time. Their self-contained G the established graphics-thread destruction contract unless a concrete migration requires otherwise. +For fonts, ownership is separate from rendering policy. Font handles and semantic lookup live in +naturally owned catalogs: application defaults use the default scope, while author `@font-face` +resources are owned by their document scene. Every `ResourceScope` owns an inline `FontService` for +its rendering configuration, emoji fonts, configured fallbacks, and system fallback cache. Fonts +retain only a borrowed service pointer while published in that scope and are detached when removed +or when the scope is destroyed. Raw `Font*` values in text/layout/style structures remain borrowed +views whose enclosing application, scene, theme, or fallback service retains the corresponding +handle. The former process-wide `FontManager` singleton and compatibility namespace were removed. +Publishing a font with an existing local key replaces that catalog binding; the legacy manager +behavior that silently suffixed duplicate font names is intentionally not preserved. + Remove raw-owning `ResourceManager` only when no subclass or consumer depends on it. ## 11. Required validation matrix @@ -873,8 +886,8 @@ Remove raw-owning `ResourceManager` only when no subclass or consumer depends ## 12. Next implementation deliverable -Stage 7 continues with the remaining ResourceManager families one at a time: fonts and font caches, -themes/icons, shaders/programs, and any remaining raw-owning manager. Each singleton semantic -namespace is replaced by naturally owned catalogs plus explicit scope imports, following the -completed nine-patch and texture-atlas migrations. The raw-owning ResourceManager template is -removed only after its final consumer is migrated. +Stage 7 continues with the remaining ResourceManager families one at a time. Fonts and font caches +now use scope catalogs plus `FontService`; the next families are themes/icons, shaders/programs, and +any remaining raw-owning manager. Each singleton semantic namespace is replaced by naturally owned +catalogs plus explicit scope imports. The raw-owning ResourceManager template is removed only after +its final consumer is migrated. diff --git a/include/eepp/core/lrucache.hpp b/include/eepp/core/lrucache.hpp index 8f0153bff..64bbd1ea0 100644 --- a/include/eepp/core/lrucache.hpp +++ b/include/eepp/core/lrucache.hpp @@ -68,6 +68,17 @@ class DynamicLRU { mCacheMap.clear(); } + template void eraseIf( Predicate predicate ) { + for ( auto it = mCacheList.begin(); it != mCacheList.end(); ) { + if ( predicate( it->first, it->second ) ) { + mCacheMap.erase( it->first ); + it = mCacheList.erase( it ); + } else { + ++it; + } + } + } + [[nodiscard]] std::size_t size() const { return mCacheList.size(); } }; @@ -166,6 +177,19 @@ class StaticLRU { used_ = 0; } + template void eraseIf( Predicate predicate ) { + std::vector> retained; + retained.reserve( used_ ); + for ( std::uint16_t idx = head_; idx < N; idx = next_[idx] ) { + if ( !predicate( keys_[idx], vals_[idx] ) ) + retained.emplace_back( std::move( keys_[idx] ), std::move( vals_[idx] ) ); + } + + clear(); + for ( auto it = retained.rbegin(); it != retained.rend(); ++it ) + put( std::move( it->first ), std::move( it->second ) ); + } + [[nodiscard]] std::size_t size() const noexcept { return used_; } private: @@ -283,6 +307,9 @@ class LRUCache { std::optional get( KeyParamT key ) { return impl_.get( key ); } void put( KeyT key, ValueT value ) { impl_.put( std::move( key ), std::move( value ) ); } void clear() { impl_.clear(); } + template void eraseIf( Predicate predicate ) { + impl_.eraseIf( std::move( predicate ) ); + } static constexpr std::size_t capacity() { return Capacity; } [[nodiscard]] std::size_t size() const { return impl_.size(); } diff --git a/include/eepp/graphics.hpp b/include/eepp/graphics.hpp index 9e57caa56..9558f5751 100644 --- a/include/eepp/graphics.hpp +++ b/include/eepp/graphics.hpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -43,9 +43,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -62,9 +62,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/include/eepp/graphics/font.hpp b/include/eepp/graphics/font.hpp index 934c710c3..4283e1a21 100644 --- a/include/eepp/graphics/font.hpp +++ b/include/eepp/graphics/font.hpp @@ -11,6 +11,8 @@ using namespace std::literals; namespace EE { namespace Graphics { class Font; +using FontPtr = ResourcePtr; +using FontWeakPtr = ResourceWeakPtr; struct EE_API Glyph { Float advance{ 0 }; ///< Offset to move horizontally to the next character @@ -135,6 +137,10 @@ class EE_API Font { virtual Glyph getGlyph( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, Float outlineThickness = 0 ) const = 0; + /** Returns the horizontal advance without requiring a renderable glyph texture. */ + virtual Float getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold = false, + bool italic = false, Float outlineThickness = 0 ) const; + /** @return The glyph drawable that represents the glyph in a texture. The glyph drawable * allocation is managed by the font. */ virtual GlyphDrawable* getGlyphDrawable( Uint32 codePoint, unsigned int characterSize, diff --git a/include/eepp/graphics/fontbmfont.hpp b/include/eepp/graphics/fontbmfont.hpp index 1ebe751bb..b2e5c355a 100644 --- a/include/eepp/graphics/fontbmfont.hpp +++ b/include/eepp/graphics/fontbmfont.hpp @@ -12,12 +12,20 @@ class IOStream; namespace EE { namespace Graphics { +class FontBMFont; +class ResourceScope; +using FontBMFontPtr = ResourcePtr; +using FontBMFontWeakPtr = ResourceWeakPtr; + /** @brief Implementation of AngelCode BMFont fonts. */ class EE_API FontBMFont : public Font { public: - static FontBMFont* New( const std::string fontName ); + static FontBMFontPtr New( const std::string fontName ); + static FontBMFontPtr New( const std::string fontName, ResourceScope& resourceScope ); - static FontBMFont* New( const std::string fontName, const std::string& filename ); + static FontBMFontPtr New( const std::string fontName, const std::string& filename ); + static FontBMFontPtr New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ); ~FontBMFont(); @@ -39,6 +47,9 @@ class EE_API FontBMFont : public Font { Glyph getGlyph( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, Float outlineThickness = 0 ) const; + Float getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold = false, + bool italic = false, Float outlineThickness = 0 ) const; + GlyphDrawable* getGlyphDrawable( Uint32 codePoint, unsigned int characterSize, bool bold = false, bool italic = false, Float outlineThickness = 0 ) const; diff --git a/include/eepp/graphics/fontfamily.hpp b/include/eepp/graphics/fontfamily.hpp index 734296210..9539d37b5 100644 --- a/include/eepp/graphics/fontfamily.hpp +++ b/include/eepp/graphics/fontfamily.hpp @@ -14,8 +14,8 @@ class EE_API FontFamily { const std::string& ext, const std::vector& names ); - static FontTrueType* setFont( FontTrueType* font, const std::string& fontpath, - const std::string_view& fontType ); + static void setFont( FontTrueType* font, const std::string& fontpath, + const std::string_view& fontType ); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/fontmanager.hpp b/include/eepp/graphics/fontmanager.hpp deleted file mode 100644 index 59624f305..000000000 --- a/include/eepp/graphics/fontmanager.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef EE_GRAPHICSCFONTMANAGER_HPP -#define EE_GRAPHICSCFONTMANAGER_HPP - -#include -#include - -#include -#include -using namespace EE::System; - -namespace EE { namespace Graphics { - -class FontTrueType; -struct FontDesc; - -/** @brief The Font Manager is a singleton class that manages all the instance of fonts - instantiated. And releases the font instances automatically. So the user doesn't need to release - any font instance. -*/ -class EE_API FontManager : public ResourceManager { - SINGLETON_DECLARE_HEADERS( FontManager ) - - public: - virtual ~FontManager(); - - /** @brief Adds a new font to the manager */ - Graphics::Font* add( Graphics::Font* Font ); - - Font* getColorEmojiFont() const; - - void setColorEmojiFont( Graphics::Font* font ); - - Font* getEmojiFont() const; - - void setEmojiFont( Font* newEmojiFont ); - - const std::vector& getFallbackFonts() const; - - bool hasFallbackFonts() const; - - bool addFallbackFont( Font* fallbackFont ); - - bool removeFallbackFont( Font* fallbackFont ); - - FontHinting getHinting() const; - - void setHinting( FontHinting hinting ); - - FontAntialiasing getAntialiasing() const; - - void setAntialiasing( FontAntialiasing antialiasing ); - - Font* getByInternalId( Uint32 internalId ) const; - - FontTrueType* getOrLoadSystemFallbackFont( const FontDesc& desc ); - - protected: - Font* mColorEmojiFont{ nullptr }; - Font* mEmojiFont{ nullptr }; - std::vector mFallbackFonts; - std::vector mSystemFallbackFonts; - FontHinting mHinting{ FontHinting::Full }; - FontAntialiasing mAntialiasing{ FontAntialiasing::Grayscale }; - - FontManager(); -}; - -}} // namespace EE::Graphics - -#endif diff --git a/include/eepp/graphics/fontservice.hpp b/include/eepp/graphics/fontservice.hpp new file mode 100644 index 000000000..1e7c81673 --- /dev/null +++ b/include/eepp/graphics/fontservice.hpp @@ -0,0 +1,135 @@ +#ifndef EE_GRAPHICS_FONTSERVICE_HPP +#define EE_GRAPHICS_FONTSERVICE_HPP + +#include +#include + +namespace EE { namespace Graphics { + +class FontTrueType; +class ResourceScope; +struct FontDesc; + +/** + * @brief Owns font fallback state and rendering policy for a ResourceScope. + * + * Every ResourceScope has one FontService. Fonts published locally by that scope are associated + * with its service, allowing glyph lookup to resolve configured emoji, explicit fallback, and + * system fallback fonts without consulting global state. + * + * The service strongly owns configured emoji and fallback fonts. Raw-pointer setters only accept + * fonts that can be resolved through the associated scope; passing an unrelated pointer clears or + * ignores the corresponding configuration. Removing a locally published font from the scope also + * removes any service references to it. + * + * Rendering policy changes are applied to TrueType fonts associated with this service. Imported + * fonts remain associated with the service of their owning scope and are therefore not mutated. + * + * System fonts can be loaded with two different lifetime contracts: + * - loadSystemFont() returns an independently owned, uncached font. + * - getOrLoadSystemFallbackFont() retains the font in this service for repeated fallback lookup. + */ +class EE_API FontService { + friend class ResourceScope; + + public: + /** Creates a service associated with @p resourceScope. ResourceScope owns its service. */ + explicit FontService( ResourceScope& resourceScope ); + + /** @return The resource scope whose fonts and policy are managed by this service. */ + ResourceScope& getResourceScope() const; + + /** @return The configured color emoji font, or nullptr when none is configured. */ + Font* getColorEmojiFont() const; + + /** + * Sets the color emoji font. The font must be resolvable through the associated scope. Passing + * nullptr or an unrelated font clears the current value. + */ + void setColorEmojiFont( Font* font ); + + /** @return The configured monochrome emoji font, or nullptr when none is configured. */ + Font* getEmojiFont() const; + + /** + * Sets the monochrome emoji font. The font must be resolvable through the associated scope. + * Passing nullptr or an unrelated font clears the current value. + */ + void setEmojiFont( Font* font ); + + /** @return The strongly owned explicit fallback fonts in lookup order. */ + const std::vector& getFallbackFonts() const; + + /** @return Whether at least one explicit fallback font is configured. */ + bool hasFallbackFonts() const; + + /** Adds an owning fallback handle unless the same font is already configured. */ + bool addFallbackFont( FontPtr fallbackFont ); + + /** + * Adds a fallback font resolved through the associated scope. + * @return True when the font was found and added; false otherwise. + */ + bool addFallbackFont( Font* fallbackFont ); + + /** Removes the matching explicit fallback font without removing it from its resource scope. */ + bool removeFallbackFont( Font* fallbackFont ); + + /** @return The policy used when loading and updating associated TrueType fonts. */ + FontHinting getHinting() const; + + /** Updates the hinting policy and applies it to associated non-emoji TrueType fonts. */ + void setHinting( FontHinting hinting ); + + /** @return The antialiasing policy used by associated TrueType fonts. */ + FontAntialiasing getAntialiasing() const; + + /** Updates the antialiasing policy and applies it to associated non-emoji TrueType fonts. */ + void setAntialiasing( FontAntialiasing antialiasing ); + + /** + * Finds a TrueType font visible through the associated scope by its runtime internal ID. + * @return A borrowed pointer, or nullptr when no matching font is visible. + */ + Font* getByInternalId( Uint32 internalId ) const; + + /** + * Loads a standalone system font described by @p desc. + * + * The font is not published into the associated scope and is not retained by this service. The + * returned handle is its sole owner and can be released independently, immediately freeing its + * glyph pages when no other handle exists. Current hinting and antialiasing policy is applied + * at load time. The standalone font does not use this service for subsequent fallback + * resolution. + * + * @return An owning handle, or an empty handle when the descriptor cannot be loaded. + */ + ResourcePtr loadSystemFont( const FontDesc& desc ); + + /** + * Finds or loads a system fallback font described by @p desc. + * + * Successfully loaded fonts are published into the associated scope and strongly retained by + * the service for future glyph fallback requests. The returned pointer is borrowed and remains + * valid until the font is removed from the scope or the service is destroyed. + * + * @return A borrowed pointer to the cached font, or nullptr when loading fails. + */ + FontTrueType* getOrLoadSystemFallbackFont( const FontDesc& desc ); + + private: + FontPtr findHandle( Font* font ) const; + void onFontRemoved( Font* font ); + + ResourceScope& mResourceScope; + FontPtr mColorEmojiFont; + FontPtr mEmojiFont; + std::vector mFallbackFonts; + std::vector mSystemFallbackFonts; + FontHinting mHinting{ FontHinting::Full }; + FontAntialiasing mAntialiasing{ FontAntialiasing::Grayscale }; +}; + +}} // namespace EE::Graphics + +#endif diff --git a/include/eepp/graphics/fontsprite.hpp b/include/eepp/graphics/fontsprite.hpp index 70cc1e53c..4200b9098 100644 --- a/include/eepp/graphics/fontsprite.hpp +++ b/include/eepp/graphics/fontsprite.hpp @@ -13,12 +13,20 @@ class IOStream; namespace EE { namespace Graphics { +class FontSprite; +class ResourceScope; +using FontSpritePtr = ResourcePtr; +using FontSpriteWeakPtr = ResourceWeakPtr; + /** @brief Implementation of XNA Font Sprites */ class EE_API FontSprite : public Font { public: - static FontSprite* New( const std::string fontName ); + static FontSpritePtr New( const std::string fontName ); + static FontSpritePtr New( const std::string fontName, ResourceScope& resourceScope ); - static FontSprite* New( const std::string fontName, const std::string& filename ); + static FontSpritePtr New( const std::string fontName, const std::string& filename ); + static FontSpritePtr New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ); ~FontSprite(); diff --git a/include/eepp/graphics/fonttruetype.hpp b/include/eepp/graphics/fonttruetype.hpp index 518fc3442..028c908f6 100644 --- a/include/eepp/graphics/fonttruetype.hpp +++ b/include/eepp/graphics/fonttruetype.hpp @@ -15,15 +15,26 @@ namespace EE { namespace Graphics { enum class FontWeight : Uint16; struct FontDesc; +class ResourceScope; + +class FontTrueType; +class FontService; +using FontTrueTypePtr = ResourcePtr; +using FontTrueTypeWeakPtr = ResourceWeakPtr; class EE_API FontTrueType : public Font { public: - static FontTrueType* New( const std::string& FontName ); + static FontTrueTypePtr New( const std::string& FontName ); + static FontTrueTypePtr New( const std::string& FontName, ResourceScope& resourceScope ); - static FontTrueType* New( const std::string& FontName, const std::string& filename ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename, + ResourceScope& resourceScope ); - static FontTrueType* New( const std::string& FontName, const std::string& filename, - Uint32 faceIndex ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex ); + static FontTrueTypePtr New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex, ResourceScope& resourceScope ); ~FontTrueType(); @@ -48,6 +59,9 @@ class EE_API FontTrueType : public Font { Glyph getGlyph( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, Float outlineThickness = 0 ) const; + Float getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold = false, + bool italic = false, Float outlineThickness = 0 ) const; + Glyph getGlyphByIndex( Uint32 index, unsigned int characterSize, bool bold, bool italic, Float outlineThickness = 0 ) const; @@ -80,6 +94,7 @@ class EE_API FontTrueType : public Font { const TexturePtr& getTexture( unsigned int characterSize ) const; bool loaded() const; + FontService* getFontService() const; FontTrueType( const FontTrueType& ) = delete; FontTrueType& operator=( const FontTrueType& ) = delete; @@ -151,19 +166,19 @@ class EE_API FontTrueType : public Font { virtual bool hasItalic() const { return mIsItalic || mFontItalic != nullptr; } - virtual bool hasBoldItalic() const { return isBoldItalic() || mFontBoldItalic; } + virtual bool hasBoldItalic() const { return isBoldItalic() || mFontBoldItalic != nullptr; } - FontTrueType* getBoldFont() const { return mFontBold; } + const FontTrueTypePtr& getBoldFont() const { return mFontBold; } - FontTrueType* getItalicFont() const { return mFontItalic; } + const FontTrueTypePtr& getItalicFont() const { return mFontItalic; } - FontTrueType* getBoldItalicFont() const { return mFontBoldItalic; } + const FontTrueTypePtr& getBoldItalicFont() const { return mFontBoldItalic; } - void setBoldFont( FontTrueType* fontBold ); + void setBoldFont( const FontTrueTypePtr& fontBold ); - void setItalicFont( FontTrueType* fontItalic ); + void setItalicFont( const FontTrueTypePtr& fontItalic ); - void setBoldItalicFont( FontTrueType* fontBoldItalic ); + void setBoldItalicFont( const FontTrueTypePtr& fontBoldItalic ); void* face() const { return mFace; } @@ -178,8 +193,11 @@ class EE_API FontTrueType : public Font { protected: friend class Text; friend class TextLayout; + friend class FontService; + friend class ResourceScope; - explicit FontTrueType( const std::string& FontName ); + explicit FontTrueType( const std::string& FontName, FontService& fontService ); + void setFontService( FontService* fontService ); struct Row { Row( unsigned int rowTop, unsigned int rowHeight ) : @@ -267,15 +285,14 @@ class EE_API FontTrueType : public Font { mutable UnorderedMap> mKeyCache; mutable UnorderedMap mKerningCache; // For codepoints (getKerning) mutable UnorderedMap mKerningGlyphCache; // For glyph indices + mutable UnorderedMap> mGlyphAdvanceCache; FontHinting mHinting{ FontHinting::Full }; FontAntialiasing mAntialiasing{ FontAntialiasing::Grayscale }; + FontService* mFontService{ nullptr }; Uint32 mFaceIndex{ 0 }; - FontTrueType* mFontBold{ nullptr }; - FontTrueType* mFontItalic{ nullptr }; - FontTrueType* mFontBoldItalic{ nullptr }; - Uint32 mFontBoldCb{ 0 }; - Uint32 mFontItalicCb{ 0 }; - Uint32 mFontBoldItalicCb{ 0 }; + FontTrueTypePtr mFontBold; + FontTrueTypePtr mFontItalic; + FontTrueTypePtr mFontBoldItalic; Float getGlyphTopOffset( unsigned int characterSize ) const; @@ -284,12 +301,6 @@ class EE_API FontTrueType : public Font { bool setFontFace( void* face ); void updateMonospaceState() const; - - void disconnectBoldFont(); - - void disconnectItalicFont(); - - void disconnectBoldItalicFont(); }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/resource.hpp b/include/eepp/graphics/resource.hpp index 8473e92cd..0083331dc 100644 --- a/include/eepp/graphics/resource.hpp +++ b/include/eepp/graphics/resource.hpp @@ -1,6 +1,7 @@ #ifndef EE_GRAPHICS_RESOURCE_HPP #define EE_GRAPHICS_RESOURCE_HPP +#include #include #include #include @@ -57,4 +58,14 @@ template ResourcePtr makeResource( Args&&... a }} // namespace EE::Graphics +namespace std { + +template <> struct hash { + std::size_t operator()( const EE::Graphics::ResourceId& id ) const noexcept { + return std::hash{}( id.value() ); + } +}; + +} // namespace std + #endif diff --git a/include/eepp/graphics/resourcecatalog.hpp b/include/eepp/graphics/resourcecatalog.hpp index 7419dc870..ef1543cc5 100644 --- a/include/eepp/graphics/resourcecatalog.hpp +++ b/include/eepp/graphics/resourcecatalog.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -13,34 +14,123 @@ namespace EE { namespace Graphics { class ResourceCatalog; using ResourceCatalogPtr = ResourcePtr; -/** Strong, named ownership for Graphics resources. The live registry is never searched here. */ +/** + * @brief Thread-safe, strongly owning collection of named graphics resources. + * + * A catalog maps semantic string keys to textures, drawable sources, texture atlases, and fonts. + * Publishing a resource gives the catalog shared ownership of it. Publishing another resource of + * the same kind under the same key replaces that binding; the key and resource name are never + * changed automatically. Publishing a null handle is equivalent to erasing the corresponding key, + * while an empty key is ignored. + * + * Lookups return owning handles. A resource obtained from a catalog therefore remains alive even + * if its binding is subsequently replaced, erased, or the catalog is cleared. Final handles are + * released after dropping the catalog mutex so resource destruction and callbacks never execute + * while the catalog is locked. + * + * Drawable and font hash lookups use String::hash() of the semantic key as a weak secondary index. + * They are compatibility/convenience lookups and do not replace full-key lookup when collision-safe + * identity is required. Textures use ResourceId for process-wide object identity elsewhere; the + * hash accepted by findDrawable() and findFont() is not a ResourceId. + * + * ResourceCatalog performs no parent, scene, live-registry, filesystem, or fallback search. A + * ResourceScope defines lookup precedence by searching its local catalog and then explicitly + * imported catalogs. Importing a catalog shares this object and its live contents; it does not copy + * any resource. + * + * All public operations are safe to call concurrently. Enumeration methods return snapshots and + * allocate vectors containing owning handles. + */ class EE_API ResourceCatalog { public: + /** @return A new empty catalog using eepp resource allocation and deletion. */ static ResourceCatalogPtr New(); + /** @brief Publishes or replaces a texture binding. A null texture erases @p key. */ void publish( ResourceKey key, TexturePtr texture ); + /** @copydoc publish(ResourceKey,TexturePtr) */ void publish( std::string key, TexturePtr texture ); + + /** @brief Publishes or replaces a drawable-source binding. A null drawable erases @p key. */ void publishDrawable( ResourceKey key, DrawablePtr drawable ); + /** @copydoc publishDrawable(ResourceKey,DrawablePtr) */ void publishDrawable( std::string key, DrawablePtr drawable ); + + /** @brief Publishes or replaces a texture-atlas binding. A null atlas erases @p key. */ void publishAtlas( ResourceKey key, TextureAtlasPtr atlas ); + /** @copydoc publishAtlas(ResourceKey,TextureAtlasPtr) */ void publishAtlas( std::string key, TextureAtlasPtr atlas ); + /** @brief Publishes or replaces a font binding. A null font erases @p key. */ + void publishFont( ResourceKey key, FontPtr font ); + /** @copydoc publishFont(ResourceKey,FontPtr) */ + void publishFont( std::string key, FontPtr font ); + + /** @return The texture bound to @p key, or an empty handle when it is not present. */ TexturePtr findTexture( const ResourceKey& key ) const; + /** @copydoc findTexture(const ResourceKey&)const */ TexturePtr findTexture( const std::string& key ) const; + + /** @return The drawable source bound to @p key, or an empty handle when it is not present. */ DrawablePtr findDrawable( const ResourceKey& key ) const; + /** @copydoc findDrawable(const ResourceKey&)const */ DrawablePtr findDrawable( const std::string& key ) const; + /** + * @brief Looks up a drawable through the weak String::hash(key) secondary index. + * @return An owning handle when the indexed drawable is still present, otherwise an empty + * handle. + */ DrawablePtr findDrawable( const String::HashType& id ) const; + + /** @return The texture atlas bound to @p key, or an empty handle when it is not present. */ TextureAtlasPtr findAtlas( const ResourceKey& key ) const; + /** @copydoc findAtlas(const ResourceKey&)const */ TextureAtlasPtr findAtlas( const std::string& key ) const; + /** @return An owning snapshot of all texture atlases currently published in this catalog. */ std::vector getAtlases() const; + /** @return The font bound to @p key, or an empty handle when it is not present. */ + FontPtr findFont( const ResourceKey& key ) const; + /** @copydoc findFont(const ResourceKey&)const */ + FontPtr findFont( const std::string& key ) const; + /** + * @brief Looks up a font through the weak String::hash(key) secondary index. + * @return An owning handle when the indexed font is still present, otherwise an empty handle. + */ + FontPtr findFont( const String::HashType& id ) const; + /** @return An owning snapshot of all fonts currently published in this catalog. */ + std::vector getFonts() const; + + /** @brief Removes the texture binding for @p key. @return Whether a binding was removed. */ bool erase( const ResourceKey& key ); + /** @copydoc erase(const ResourceKey&) */ bool erase( const std::string& key ); + + /** @brief Removes the drawable binding for @p key. @return Whether a binding was removed. */ bool eraseDrawable( const ResourceKey& key ); + /** @copydoc eraseDrawable(const ResourceKey&) */ bool eraseDrawable( const std::string& key ); + + /** @brief Removes the texture-atlas binding for @p key. @return Whether a binding was removed. + */ bool eraseAtlas( const ResourceKey& key ); + /** @copydoc eraseAtlas(const ResourceKey&) */ bool eraseAtlas( const std::string& key ); + + /** @brief Removes the font binding for @p key. @return Whether a binding was removed. */ + bool eraseFont( const ResourceKey& key ); + /** @copydoc eraseFont(const ResourceKey&) */ + bool eraseFont( const std::string& key ); + /** + * @brief Removes @p font only when its current name maps to that exact font in this catalog. + * @return Whether the matching binding was removed. + */ + bool eraseFont( Font* font ); + + /** @brief Removes every binding while allowing previously returned handles to remain valid. */ void clear(); + + /** @return The total number of texture, drawable, atlas, and font bindings. */ std::size_t size() const; private: @@ -49,6 +139,8 @@ class EE_API ResourceCatalog { UnorderedMap mDrawables; UnorderedMap mDrawablesById; UnorderedMap mAtlases; + UnorderedMap mFonts; + UnorderedMap mFontsById; }; }} // namespace EE::Graphics diff --git a/include/eepp/graphics/resourcescope.hpp b/include/eepp/graphics/resourcescope.hpp index 0f60fd457..bb5e5e985 100644 --- a/include/eepp/graphics/resourcescope.hpp +++ b/include/eepp/graphics/resourcescope.hpp @@ -2,6 +2,7 @@ #define EE_GRAPHICS_RESOURCESCOPE_HPP #include +#include #include #include @@ -10,12 +11,30 @@ namespace EE { namespace Graphics { class ResourceScope; using ResourceScopePtr = ResourcePtr; -/** Graphics-only semantic lookup boundary with a local catalog and explicit catalog imports. */ +/** + * @brief Graphics resource lookup boundary with local ownership and explicit catalog visibility. + * + * A scope owns resources published into its local catalog. Lookups search that local catalog first, + * followed by imported catalogs in import order. Importing a catalog retains the catalog and makes + * its resources visible; it does not copy resources and does not transfer them into the local + * catalog. Consequently, removing an import only removes lookup visibility and releases the + * scope's catalog reference. + * + * Use importCatalog() when resources owned by another lifetime boundary must be visible in this + * scope, such as application assets shared with a scene or a theme catalog used by UI widgets. + * Avoid importing unrelated scene or document catalogs: keeping imports explicit prevents name + * collisions and resource leakage between independent documents. UISceneNode imports the default + * resource catalog automatically unless that behavior is disabled at construction time. + */ class EE_API ResourceScope { public: static ResourceScopePtr New(); ResourceScope(); + ~ResourceScope(); + + FontService& getFontService(); + const FontService& getFontService() const; TexturePtr findTexture( const ResourceKey& key ) const; TexturePtr findTexture( const std::string& key ) const; @@ -26,6 +45,10 @@ class EE_API ResourceScope { TextureAtlasPtr findAtlas( const ResourceKey& key ) const; TextureAtlasPtr findAtlas( const std::string& key ) const; std::vector getAtlases() const; + FontPtr findFont( const ResourceKey& key ) const; + FontPtr findFont( const std::string& key ) const; + FontPtr findFont( const String::HashType& id ) const; + std::vector getFonts() const; std::vector findTextureRegionsByPattern( const std::string& name, const std::string& extension = "", TextureAtlas* searchInTextureAtlas = nullptr ) const; @@ -39,27 +62,52 @@ class EE_API ResourceScope { void publishLocalDrawable( std::string key, DrawablePtr drawable ); void publishLocalAtlas( ResourceKey key, TextureAtlasPtr atlas ); void publishLocalAtlas( std::string key, TextureAtlasPtr atlas ); + /** + * @brief Publishes a font under @p key, replacing any existing local binding for that key. + * + * The requested semantic key is preserved. Fonts are never renamed to avoid a collision. + */ + void publishLocalFont( ResourceKey key, FontPtr font ); + void publishLocalFont( std::string key, FontPtr font ); bool eraseLocal( const ResourceKey& key ); bool eraseLocal( const std::string& key ); bool eraseLocalDrawable( const ResourceKey& key ); bool eraseLocalDrawable( const std::string& key ); bool eraseLocalAtlas( const ResourceKey& key ); bool eraseLocalAtlas( const std::string& key ); + bool eraseLocalFont( const ResourceKey& key ); + bool eraseLocalFont( const std::string& key ); + bool eraseLocalFont( Font* font ); void clearLocal(); + /** + * @brief Makes resources from @p catalog visible after this scope's local resources. + * + * Importing the same catalog more than once has no effect. The catalog is retained strongly for + * as long as it remains imported. Catalog contents stay live: resources published after this + * call become visible without importing the catalog again. + */ void importCatalog( ResourceCatalogPtr catalog ); + + /** @brief Removes an imported catalog without modifying the catalog or its resources. */ bool removeCatalog( const ResourceCatalogPtr& catalog ); + + /** @brief Removes every imported catalog while preserving this scope's local resources. */ void clearImports(); ResourceCatalogPtr getLocalCatalog() const; private: + void attachFontService( const FontPtr& font ); + void detachFontService( const FontPtr& font ); + ResourceCatalogPtr mLocalCatalog; + FontService mFontService; std::vector mImports; mutable System::Mutex mMutex; }; -/** Engine-owned process defaults for pure Graphics and legacy application-wide resolution. */ +/** Engine-owned process defaults for pure Graphics and application-wide resolution. */ EE_API ResourceCatalog& globalResourceCatalog(); EE_API ResourceScope& defaultResourceScope(); diff --git a/include/eepp/graphics/textlayout.hpp b/include/eepp/graphics/textlayout.hpp index d8802d3e4..796421a13 100644 --- a/include/eepp/graphics/textlayout.hpp +++ b/include/eepp/graphics/textlayout.hpp @@ -48,7 +48,16 @@ class EE_API TextLayout { LineWrapMode lineWrapMode = LineWrapMode::NoWrap, Uint32 wrapWidth = 0, bool keepIndentation = false, Float initialXOffset = 0 ); - static void clearLayoutCache(); + /** + * Removes entries from the shared text-layout LRU cache. + * + * @param font Optional borrowed font identity. When provided, only layouts requested with this + * font or containing shaped glyphs produced by this font are evicted. Passing nullptr clears + * the entire cache. Font destruction uses selective eviction before its borrowed pointers + * become invalid. + */ + static void clearLayoutCache( Font* font = nullptr ); + protected: static void wrapLayout( const String::View& string, TextLayout&, LineWrapMode lineWrapMode, Float wrapWidth, Float vspace, bool keepIndentation, Font* font, diff --git a/include/eepp/ui/tools/uifontpickerdialog.hpp b/include/eepp/ui/tools/uifontpickerdialog.hpp index 58d1858ed..1c70dd623 100644 --- a/include/eepp/ui/tools/uifontpickerdialog.hpp +++ b/include/eepp/ui/tools/uifontpickerdialog.hpp @@ -118,6 +118,9 @@ class EE_API UIFontPickerDialog : public UIWindow { std::vector mSizes; UnorderedSet mLoadedFontKeys; UnorderedMap mFontTags; + Graphics::FontTrueTypePtr mPreviewFont; + Graphics::Font* mPreviewTextDefaultFont{ nullptr }; + Graphics::Font* mPreviewInputDefaultFont{ nullptr }; std::shared_ptr mFamilyModel; std::shared_ptr mStyleModel; std::shared_ptr mSizeModel; @@ -171,7 +174,7 @@ class EE_API UIFontPickerDialog : public UIWindow { void sortFonts(); - void mergeFontManagerFonts( std::vector& fonts ); + void mergeLoadedFonts( std::vector& fonts ); void updateFontTags(); @@ -189,6 +192,8 @@ class EE_API UIFontPickerDialog : public UIWindow { void updatePreview(); + void clearPreviewFont(); + void selectInitialRows(); void selectFamily( const std::string& family ); diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp index 78de61ebf..637ffbe7f 100644 --- a/include/eepp/ui/uiscenenode.hpp +++ b/include/eepp/ui/uiscenenode.hpp @@ -23,6 +23,7 @@ using namespace EE::Network; namespace EE { namespace Graphics { class Font; +using FontPtr = ResourcePtr; }} // namespace EE::Graphics namespace EE { namespace Window { @@ -57,9 +58,14 @@ class EE_API UISceneNode : public SceneNode { * * @param window Pointer to the window to associate with this UI scene node. * If NULL, uses the current window from Engine. + * @param importDefaultResources Whether the scene scope automatically imports the catalog from + * Graphics::defaultResourceScope(). Keep this enabled for normal + * application scenes. Disable it for intentionally isolated + * scenes that must only resolve local or explicitly imported resources. * @return Pointer to the newly created UISceneNode instance. */ - static UISceneNode* New( EE::Window::Window* window = NULL ); + static UISceneNode* New( EE::Window::Window* window = NULL, + bool importDefaultResources = true ); /** * @brief Destroys the UISceneNode and cleans up resources. @@ -782,7 +788,12 @@ class EE_API UISceneNode : public SceneNode { /** @return The Graphics resource lookup and ownership boundary of this scene. */ const Graphics::ResourceScopePtr& getResourceScope() const; - /** Replaces this scene's resource boundary, allowing intentional sharing between scenes. */ + /** + * @brief Replaces this scene's resource boundary, allowing intentional sharing between scenes. + * + * Scenes created with default-resource importing enabled also import the default catalog into + * the replacement scope. Scenes created with it disabled leave the replacement scope unchanged. + */ UISceneNode* setResourceScope( Graphics::ResourceScopePtr resourceScope ); /** @@ -928,10 +939,11 @@ class EE_API UISceneNode : public SceneNode { bool mStyleDuringLoad{ false }; UIThemeManager* mUIThemeManager{ nullptr }; UIIconThemeManager* mUIIconThemeManager{ nullptr }; - std::vector mFontFaces; + std::vector mFontFaces; UnorderedMap mFontFaceAliases; UnorderedMap mFontFaceFamilies; std::shared_ptr mAsyncResourceLoadState; + bool mImportDefaultResources{ true }; Graphics::ResourceScopePtr mResourceScope; DrawableResolver mDrawableResolver; WebResourceCachePtr mWebResourceCache; @@ -971,8 +983,9 @@ class EE_API UISceneNode : public SceneNode { * Creates a UISceneNode with optional window association. * * @param window Pointer to the window, or NULL for default. + * @param importDefaultResources Whether the scene scope imports the default resource catalog. */ - explicit UISceneNode( EE::Window::Window* window = NULL ); + explicit UISceneNode( EE::Window::Window* window = NULL, bool importDefaultResources = true ); /** * @brief Handles node resize. diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 5cf2d6393..2e85401ed 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -62,7 +62,7 @@ ../../include/eepp/graphics/fontbmfont.hpp ../../include/eepp/graphics/font.hpp ../../include/eepp/graphics/fontfamily.hpp -../../include/eepp/graphics/fontmanager.hpp +../../include/eepp/graphics/fontservice.hpp ../../include/eepp/graphics/fontsprite.hpp ../../include/eepp/graphics/fontstyleconfig.hpp ../../include/eepp/graphics/fonttruetype.hpp @@ -560,7 +560,7 @@ ../../src/eepp/graphics/fontbmfont.cpp ../../src/eepp/graphics/font.cpp ../../src/eepp/graphics/fontfamily.cpp -../../src/eepp/graphics/fontmanager.cpp +../../src/eepp/graphics/fontservice.cpp ../../src/eepp/graphics/fontsprite.cpp ../../src/eepp/graphics/fonttruetype.cpp ../../src/eepp/graphics/framebuffer.cpp diff --git a/projects/macos/ee.files b/projects/macos/ee.files index 040f1f3f1..69aa2bb6e 100644 --- a/projects/macos/ee.files +++ b/projects/macos/ee.files @@ -62,7 +62,7 @@ ../../include/eepp/graphics/fontbmfont.hpp ../../include/eepp/graphics/font.hpp ../../include/eepp/graphics/fontfamily.hpp -../../include/eepp/graphics/fontmanager.hpp +../../include/eepp/graphics/fontservice.hpp ../../include/eepp/graphics/fontsprite.hpp ../../include/eepp/graphics/fontstyleconfig.hpp ../../include/eepp/graphics/fonttruetype.hpp @@ -551,7 +551,7 @@ ../../src/eepp/graphics/fontbmfont.cpp ../../src/eepp/graphics/font.cpp ../../src/eepp/graphics/fontfamily.cpp -../../src/eepp/graphics/fontmanager.cpp +../../src/eepp/graphics/fontservice.cpp ../../src/eepp/graphics/fontsprite.cpp ../../src/eepp/graphics/fonttruetype.cpp ../../src/eepp/graphics/framebuffer.cpp diff --git a/projects/windows/ee.files b/projects/windows/ee.files index a829bd017..d7359ae4f 100644 --- a/projects/windows/ee.files +++ b/projects/windows/ee.files @@ -60,7 +60,7 @@ ../../include/eepp/ui/webresourcecache.hpp ../../include/eepp/graphics/fontbmfont.hpp ../../include/eepp/graphics/font.hpp -../../include/eepp/graphics/fontmanager.hpp +../../include/eepp/graphics/fontservice.hpp ../../include/eepp/graphics/fontsprite.hpp ../../include/eepp/graphics/fontstyleconfig.hpp ../../include/eepp/graphics/fonttruetype.hpp @@ -542,7 +542,7 @@ ../../src/eepp/ui/webresourcecache.cpp ../../src/eepp/graphics/fontbmfont.cpp ../../src/eepp/graphics/font.cpp -../../src/eepp/graphics/fontmanager.cpp +../../src/eepp/graphics/fontservice.cpp ../../src/eepp/graphics/fontsprite.cpp ../../src/eepp/graphics/fonttruetype.cpp ../../src/eepp/graphics/framebuffer.cpp diff --git a/src/benchmarks/inline_layout_benchmark.cpp b/src/benchmarks/inline_layout_benchmark.cpp index 97952c155..5bd292442 100644 --- a/src/benchmarks/inline_layout_benchmark.cpp +++ b/src/benchmarks/inline_layout_benchmark.cpp @@ -236,7 +236,7 @@ UTEST( Benchmark, InlineLayout ) { 800, 600, "bench", WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); if ( !font->loaded() ) { Engine::destroySingleton(); @@ -323,9 +323,9 @@ UTEST( Benchmark, MarkdownReadme ) { UISceneNode* ui = UISceneNode::New( window ); SceneManager::instance()->add( ui ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); - FontTrueType* monoFont = FontTrueType::New( "monospace" ); + FontTrueType* monoFont = FontTrueType::New( "monospace" ).get(); monoFont->loadFromFile( "../assets/fonts/DejaVuSansMono.ttf" ); if ( !font->loaded() || !monoFont->loaded() ) { Engine::destroySingleton(); diff --git a/src/eepp/graphics/font.cpp b/src/eepp/graphics/font.cpp index d99a017c3..e58c2918c 100644 --- a/src/eepp/graphics/font.cpp +++ b/src/eepp/graphics/font.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -80,19 +79,19 @@ std::vector Font::emojiCodePointsPositions( const String& string ) Font::Font( const FontType& Type, const std::string& Name ) : mType( Type ), mNumCallBacks( 0 ) { this->setName( Name ); - FontManager::instance()->add( this ); } -Font::~Font() { - if ( !FontManager::instance()->isDestroying() ) { - FontManager::instance()->remove( this, false ); - } -} +Font::~Font() {} const FontType& Font::getType() const { return mType; } +Float Font::getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, + Float outlineThickness ) const { + return getGlyph( codePoint, characterSize, bold, italic, outlineThickness ).advance; +} + const std::string& Font::getName() const { return mFontName; } diff --git a/src/eepp/graphics/fontbmfont.cpp b/src/eepp/graphics/fontbmfont.cpp index 40e503fc1..625d3d52f 100644 --- a/src/eepp/graphics/fontbmfont.cpp +++ b/src/eepp/graphics/fontbmfont.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,12 +11,27 @@ namespace EE { namespace Graphics { -FontBMFont* FontBMFont::New( const std::string fontName ) { - return eeNew( FontBMFont, ( fontName ) ); +FontBMFontPtr FontBMFont::New( const std::string fontName ) { + FontBMFontPtr font( eeNew( FontBMFont, ( fontName ) ), ResourceDeleter() ); + defaultResourceScope().publishLocalFont( fontName, font ); + return font; } -FontBMFont* FontBMFont::New( const std::string fontName, const std::string& filename ) { - FontBMFont* fontBMFont = New( fontName ); +FontBMFontPtr FontBMFont::New( const std::string fontName, ResourceScope& resourceScope ) { + FontBMFontPtr font( eeNew( FontBMFont, ( fontName ) ), ResourceDeleter() ); + resourceScope.publishLocalFont( fontName, font ); + return font; +} + +FontBMFontPtr FontBMFont::New( const std::string fontName, const std::string& filename ) { + FontBMFontPtr fontBMFont = New( fontName ); + fontBMFont->loadFromFile( filename ); + return fontBMFont; +} + +FontBMFontPtr FontBMFont::New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ) { + FontBMFontPtr fontBMFont = New( fontName, resourceScope ); fontBMFont->loadFromFile( filename ); return fontBMFont; } @@ -194,6 +210,11 @@ Glyph FontBMFont::getGlyph( Uint32 codePoint, unsigned int characterSize, bool b } } +Float FontBMFont::getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold, + bool italic, Float outlineThickness ) const { + return getGlyph( codePoint, characterSize, bold, italic, outlineThickness ).advance; +} + GlyphDrawable* FontBMFont::getGlyphDrawable( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic, Float outlineThickness ) const { diff --git a/src/eepp/graphics/fontfamily.cpp b/src/eepp/graphics/fontfamily.cpp index c8ca1ea79..e07f79aaf 100644 --- a/src/eepp/graphics/fontfamily.cpp +++ b/src/eepp/graphics/fontfamily.cpp @@ -1,4 +1,5 @@ #include +#include #include using namespace std::literals; @@ -53,11 +54,15 @@ std::string FontFamily::findType( const std::string& fontpath, const std::string return ""; } -FontTrueType* FontFamily::setFont( FontTrueType* font, const std::string& fontpath, - const std::string_view& fontType ) { +void FontFamily::setFont( FontTrueType* font, const std::string& fontpath, + const std::string_view& fontType ) { if ( fontpath.empty() ) - return nullptr; - FontTrueType* loadedFont = FontTrueType::New( font->getName() + "-" + fontType, fontpath ); + return; + FontService* fontService = font->getFontService(); + FontTrueTypePtr loadedFont = + fontService ? FontTrueType::New( font->getName() + "-" + fontType, fontpath, + fontService->getResourceScope() ) + : FontTrueType::New( font->getName() + "-" + fontType, fontpath ); if ( fontType == "bold"sv ) font->setBoldFont( loadedFont ); else if ( fontType == "italic"sv ) @@ -66,7 +71,6 @@ FontTrueType* FontFamily::setFont( FontTrueType* font, const std::string& fontpa font->setBoldItalicFont( loadedFont ); loadedFont->setBoldAdvanceSameAsRegular( font->getBoldAdvanceSameAsRegular() ); loadedFont->setEnableDynamicMonospace( font->getEnableDynamicMonospace() ); - return loadedFont; } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontmanager.cpp b/src/eepp/graphics/fontmanager.cpp deleted file mode 100644 index 9d785ec6d..000000000 --- a/src/eepp/graphics/fontmanager.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include - -namespace EE { namespace Graphics { - -SINGLETON_DECLARE_IMPLEMENTATION( FontManager ) - -FontManager::FontManager() {} - -FontManager::~FontManager() { - mEmojiFont = nullptr; - mColorEmojiFont = nullptr; - mFallbackFonts.clear(); - mSystemFallbackFonts.clear(); -} - -Graphics::Font* FontManager::add( Graphics::Font* font ) { - eeASSERT( NULL != font ); - return ResourceManager::add( font ); -} - -void FontManager::setColorEmojiFont( Font* font ) { - mColorEmojiFont = font; -} - -Graphics::Font* FontManager::getColorEmojiFont() const { - return mColorEmojiFont; -} - -Graphics::Font* FontManager::getEmojiFont() const { - return mEmojiFont; -} - -void FontManager::setEmojiFont( Graphics::Font* newEmojiFont ) { - mEmojiFont = newEmojiFont; -} - -const std::vector& FontManager::getFallbackFonts() const { - return mFallbackFonts; -} - -bool FontManager::hasFallbackFonts() const { - return !mFallbackFonts.empty(); -} - -bool FontManager::addFallbackFont( Font* fallbackFont ) { - if ( fallbackFont && std::find( mFallbackFonts.begin(), mFallbackFonts.end(), fallbackFont ) == - mFallbackFonts.end() ) { - mFallbackFonts.emplace_back( fallbackFont ); - return true; - } - return false; -} - -bool FontManager::removeFallbackFont( Font* fallbackFont ) { - auto fallbackFontIt = std::find( mFallbackFonts.begin(), mFallbackFonts.end(), fallbackFont ); - if ( fallbackFontIt != mFallbackFonts.end() ) { - mFallbackFonts.erase( fallbackFontIt ); - return true; - } - return false; -} - -FontHinting FontManager::getHinting() const { - return mHinting; -} - -void FontManager::setHinting( FontHinting hinting ) { - mHinting = hinting; - - for ( auto [_, font] : mResources ) { - if ( font->getType() == FontType::TTF ) { - auto ttf = static_cast( font ); - if ( !ttf->isEmojiFont() ) - ttf->setHinting( hinting ); - } - } -} - -FontAntialiasing FontManager::getAntialiasing() const { - return mAntialiasing; -} - -void FontManager::setAntialiasing( FontAntialiasing antialiasing ) { - mAntialiasing = antialiasing; - - for ( auto [_, font] : mResources ) { - if ( font->getType() == FontType::TTF ) { - auto ttf = static_cast( font ); - if ( !ttf->isEmojiFont() ) - ttf->setAntialiasing( antialiasing ); - } - } -} - -Font* FontManager::getByInternalId( Uint32 internalId ) const { - for ( auto [_, font] : mResources ) { - if ( font->getType() == FontType::TTF && - static_cast( font )->getFontInternalId() == internalId ) - return font; - } - return nullptr; -} - -FontTrueType* FontManager::getOrLoadSystemFallbackFont( const FontDesc& desc ) { - if ( desc.path.empty() ) - return nullptr; - - for ( auto* font : mSystemFallbackFonts ) { - if ( font->getType() == FontType::TTF ) { - auto* ttf = static_cast( font ); - if ( ttf->getInfo().fontpath + ttf->getInfo().filename == desc.path && - ttf->getFaceIndex() == desc.faceIndex ) - return ttf; - } - } - - FontTrueType* ttf = FontTrueType::New( desc.family, desc.path, desc.faceIndex ); - if ( !ttf || !ttf->loaded() ) { - eeSAFE_DELETE( ttf ); - return nullptr; - } - - mSystemFallbackFonts.push_back( ttf ); - - return ttf; -} - -}} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontservice.cpp b/src/eepp/graphics/fontservice.cpp new file mode 100644 index 000000000..43c3ea539 --- /dev/null +++ b/src/eepp/graphics/fontservice.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +namespace EE { namespace Graphics { + +FontService::FontService( ResourceScope& resourceScope ) : mResourceScope( resourceScope ) {} + +ResourceScope& FontService::getResourceScope() const { + return mResourceScope; +} + +FontPtr FontService::findHandle( Font* font ) const { + if ( !font ) + return {}; + FontPtr handle = mResourceScope.findFont( font->getId() ); + return handle.get() == font ? handle : FontPtr{}; +} + +void FontService::onFontRemoved( Font* font ) { + if ( mColorEmojiFont.get() == font ) + mColorEmojiFont.reset(); + if ( mEmojiFont.get() == font ) + mEmojiFont.reset(); + removeFallbackFont( font ); + mSystemFallbackFonts.erase( + std::remove_if( mSystemFallbackFonts.begin(), mSystemFallbackFonts.end(), + [font]( const FontPtr& systemFont ) { return systemFont.get() == font; } ), + mSystemFallbackFonts.end() ); +} + +void FontService::setColorEmojiFont( Font* font ) { + mColorEmojiFont = findHandle( font ); +} + +Font* FontService::getColorEmojiFont() const { + return mColorEmojiFont.get(); +} + +Font* FontService::getEmojiFont() const { + return mEmojiFont.get(); +} + +void FontService::setEmojiFont( Font* font ) { + mEmojiFont = findHandle( font ); +} + +const std::vector& FontService::getFallbackFonts() const { + return mFallbackFonts; +} + +bool FontService::hasFallbackFonts() const { + return !mFallbackFonts.empty(); +} + +bool FontService::addFallbackFont( FontPtr fallbackFont ) { + if ( fallbackFont && std::find( mFallbackFonts.begin(), mFallbackFonts.end(), fallbackFont ) == + mFallbackFonts.end() ) { + mFallbackFonts.emplace_back( std::move( fallbackFont ) ); + return true; + } + return false; +} + +bool FontService::addFallbackFont( Font* fallbackFont ) { + return addFallbackFont( findHandle( fallbackFont ) ); +} + +bool FontService::removeFallbackFont( Font* fallbackFont ) { + auto it = std::find_if( + mFallbackFonts.begin(), mFallbackFonts.end(), + [fallbackFont]( const FontPtr& font ) { return font.get() == fallbackFont; } ); + if ( it == mFallbackFonts.end() ) + return false; + mFallbackFonts.erase( it ); + return true; +} + +FontHinting FontService::getHinting() const { + return mHinting; +} + +void FontService::setHinting( FontHinting hinting ) { + mHinting = hinting; + for ( const FontPtr& fontHandle : mResourceScope.getFonts() ) { + Font* font = fontHandle.get(); + if ( font->getType() == FontType::TTF ) { + auto ttf = static_cast( font ); + if ( ttf->getFontService() == this && !ttf->isEmojiFont() ) + ttf->setHinting( hinting ); + } + } +} + +FontAntialiasing FontService::getAntialiasing() const { + return mAntialiasing; +} + +void FontService::setAntialiasing( FontAntialiasing antialiasing ) { + mAntialiasing = antialiasing; + for ( const FontPtr& fontHandle : mResourceScope.getFonts() ) { + Font* font = fontHandle.get(); + if ( font->getType() == FontType::TTF ) { + auto ttf = static_cast( font ); + if ( ttf->getFontService() == this && !ttf->isEmojiFont() ) + ttf->setAntialiasing( antialiasing ); + } + } +} + +Font* FontService::getByInternalId( Uint32 internalId ) const { + for ( const FontPtr& fontHandle : mResourceScope.getFonts() ) { + Font* font = fontHandle.get(); + if ( font->getType() == FontType::TTF && + static_cast( font )->getFontInternalId() == internalId ) + return font; + } + return nullptr; +} + +ResourcePtr FontService::loadSystemFont( const FontDesc& desc ) { + if ( desc.path.empty() ) + return {}; + + FontTrueTypePtr ttf( eeNew( FontTrueType, ( desc.family, *this ) ), + ResourceDeleter() ); + if ( !ttf->loadFromFile( desc.path, desc.faceIndex ) ) + return {}; + + ttf->setHinting( mHinting ); + ttf->setAntialiasing( mAntialiasing ); + // A standalone font can outlive this service, so it must not retain a borrowed service pointer. + ttf->setFontService( nullptr ); + return ttf; +} + +FontTrueType* FontService::getOrLoadSystemFallbackFont( const FontDesc& desc ) { + if ( desc.path.empty() ) + return nullptr; + + for ( const FontPtr& fontHandle : mSystemFallbackFonts ) { + auto* ttf = static_cast( fontHandle.get() ); + if ( ttf->getInfo().fontpath + ttf->getInfo().filename == desc.path && + ttf->getFaceIndex() == desc.faceIndex ) + return ttf; + } + + FontTrueTypePtr ttf = + FontTrueType::New( desc.family, desc.path, desc.faceIndex, mResourceScope ); + if ( !ttf || !ttf->loaded() ) { + if ( ttf ) + mResourceScope.eraseLocalFont( ttf.get() ); + return nullptr; + } + + mSystemFallbackFonts.emplace_back( ttf ); + return ttf.get(); +} + +}} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontsprite.cpp b/src/eepp/graphics/fontsprite.cpp index 45d2e5337..211b7debb 100644 --- a/src/eepp/graphics/fontsprite.cpp +++ b/src/eepp/graphics/fontsprite.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,12 +11,27 @@ namespace EE { namespace Graphics { -FontSprite* FontSprite::New( const std::string fontName ) { - return eeNew( FontSprite, ( fontName ) ); +FontSpritePtr FontSprite::New( const std::string fontName ) { + FontSpritePtr font( eeNew( FontSprite, ( fontName ) ), ResourceDeleter() ); + defaultResourceScope().publishLocalFont( fontName, font ); + return font; } -FontSprite* FontSprite::New( const std::string fontName, const std::string& filename ) { - FontSprite* fontSprite = New( fontName ); +FontSpritePtr FontSprite::New( const std::string fontName, ResourceScope& resourceScope ) { + FontSpritePtr font( eeNew( FontSprite, ( fontName ) ), ResourceDeleter() ); + resourceScope.publishLocalFont( fontName, font ); + return font; +} + +FontSpritePtr FontSprite::New( const std::string fontName, const std::string& filename ) { + FontSpritePtr fontSprite = New( fontName ); + fontSprite->loadFromFile( filename ); + return fontSprite; +} + +FontSpritePtr FontSprite::New( const std::string fontName, const std::string& filename, + ResourceScope& resourceScope ) { + FontSpritePtr fontSprite = New( fontName, resourceScope ); fontSprite->loadFromFile( filename ); return fontSprite; } diff --git a/src/eepp/graphics/fonttruetype.cpp b/src/eepp/graphics/fonttruetype.cpp index 9f0d55703..d27d8dead 100644 --- a/src/eepp/graphics/fonttruetype.cpp +++ b/src/eepp/graphics/fonttruetype.cpp @@ -1,8 +1,10 @@ #include -#include +#include #include +#include #include #include +#include #include #include #include @@ -266,24 +268,49 @@ static inline Uint64 getKerningKey( Uint32 first, Uint32 second, unsigned int ch ( static_cast( static_cast( outlineThickness * 100.f ) & 0xFF ) ); } -FontTrueType* FontTrueType::New( const std::string& FontName ) { - return eeNew( FontTrueType, ( FontName ) ); +FontTrueTypePtr FontTrueType::New( const std::string& FontName ) { + FontTrueTypePtr font( + eeNew( FontTrueType, ( FontName, defaultResourceScope().getFontService() ) ), + ResourceDeleter() ); + defaultResourceScope().publishLocalFont( FontName, font ); + return font; } -FontTrueType* FontTrueType::New( const std::string& FontName, const std::string& filename ) { - FontTrueType* fontTrueType = New( FontName ); +FontTrueTypePtr FontTrueType::New( const std::string& FontName, ResourceScope& resourceScope ) { + FontTrueTypePtr font( eeNew( FontTrueType, ( FontName, resourceScope.getFontService() ) ), + ResourceDeleter() ); + resourceScope.publishLocalFont( FontName, font ); + return font; +} + +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename ) { + FontTrueTypePtr fontTrueType = New( FontName ); fontTrueType->loadFromFile( filename ); return fontTrueType; } -FontTrueType* FontTrueType::New( const std::string& FontName, const std::string& filename, - Uint32 faceIndex ) { - FontTrueType* fontTrueType = New( FontName ); +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename, + ResourceScope& resourceScope ) { + FontTrueTypePtr fontTrueType = New( FontName, resourceScope ); + fontTrueType->loadFromFile( filename ); + return fontTrueType; +} + +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex ) { + FontTrueTypePtr fontTrueType = New( FontName ); fontTrueType->loadFromFile( filename, faceIndex ); return fontTrueType; } -FontTrueType::FontTrueType( const std::string& FontName ) : +FontTrueTypePtr FontTrueType::New( const std::string& FontName, const std::string& filename, + Uint32 faceIndex, ResourceScope& resourceScope ) { + FontTrueTypePtr fontTrueType = New( FontName, resourceScope ); + fontTrueType->loadFromFile( filename, faceIndex ); + return fontTrueType; +} + +FontTrueType::FontTrueType( const std::string& FontName, FontService& fontService ) : Font( FontType::TTF, FontName ), mLibrary( NULL ), mFace( NULL ), @@ -307,17 +334,19 @@ FontTrueType::FontTrueType( const std::string& FontName ) : mIsBold( false ), mIsItalic( false ), mIsMonospaceCompletePending( false ), - mHinting( FontManager::instance()->getHinting() ), - mAntialiasing( FontManager::instance()->getAntialiasing() ), - mFaceIndex( 0 ), - mFontBold( nullptr ), - mFontItalic( nullptr ), - mFontBoldItalic( nullptr ), - mFontBoldCb( 0 ), - mFontItalicCb( 0 ), - mFontBoldItalicCb( 0 ) {} + mHinting( fontService.getHinting() ), + mAntialiasing( fontService.getAntialiasing() ), + mFontService( &fontService ), + mFaceIndex( 0 ) {} + +void FontTrueType::setFontService( FontService* fontService ) { + mFontService = fontService; +} FontTrueType::~FontTrueType() { + // Cached shaped glyphs borrow FontTrueType pointers. Drop layouts referencing this font before + // it dies, while preserving unrelated entries in the shared cache. + TextLayout::clearLayoutCache( this ); cleanup(); } @@ -516,12 +545,12 @@ bool FontTrueType::setFontFace( void* _face ) { #endif } - if ( ( mIsColorEmojiFont || mHasColrGlyphs ) && - FontManager::instance()->getColorEmojiFont() == nullptr ) - FontManager::instance()->setColorEmojiFont( this ); + if ( mFontService && ( mIsColorEmojiFont || mHasColrGlyphs ) && + mFontService->getColorEmojiFont() == nullptr ) + mFontService->setColorEmojiFont( this ); - if ( mIsEmojiFont && !mHasSvgGlyphs && FontManager::instance()->getEmojiFont() == nullptr ) - FontManager::instance()->setEmojiFont( this ); + if ( mFontService && mIsEmojiFont && !mHasSvgGlyphs && mFontService->getEmojiFont() == nullptr ) + mFontService->setEmojiFont( this ); // Load the stroker that will be used to outline the font FT_Stroker stroker = nullptr; @@ -611,6 +640,7 @@ bool FontTrueType::setVariableFontWeight( FontWeight weight ) { mKeyCache.clear(); mKerningCache.clear(); mKerningGlyphCache.clear(); + mGlyphAdvanceCache.clear(); #ifdef EE_TEXT_SHAPER_ENABLED if ( mHBFont ) hb_ft_font_changed( static_cast( mHBFont ) ); @@ -649,11 +679,11 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool Uint32 idx = 0; if ( mEnableEmojiFallback && !mIsColorEmojiFont && !mHasSvgGlyphs && !mHasColrGlyphs && !mIsEmojiFont && Font::isEmojiCodePoint( codePoint ) ) { - if ( !mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() != nullptr && - FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF ) { + if ( mFontService && !mIsColorEmojiFont && mFontService->getColorEmojiFont() != nullptr && + mFontService->getColorEmojiFont()->getType() == FontType::TTF ) { FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getColorEmojiFont() ); + static_cast( mFontService->getColorEmojiFont() ); if ( ( idx = fontEmoji->getGlyphIndex( codePoint ) ) ) { if ( mIsMonospace && mEnableDynamicMonospace ) { mIsMonospaceComplete = false; @@ -662,11 +692,10 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool return fontEmoji->getGlyphByIndex( idx, characterSize, bold, italic, outlineThickness, getPage( characterSize ) ); } - } else if ( !mIsEmojiFont && FontManager::instance()->getEmojiFont() != nullptr && - FontManager::instance()->getEmojiFont()->getType() == FontType::TTF ) { + } else if ( mFontService && !mIsEmojiFont && mFontService->getEmojiFont() != nullptr && + mFontService->getEmojiFont()->getType() == FontType::TTF ) { - FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getEmojiFont() ); + FontTrueType* fontEmoji = static_cast( mFontService->getEmojiFont() ); if ( ( idx = fontEmoji->getGlyphIndex( codePoint ) ) ) { if ( mIsMonospace && mEnableDynamicMonospace ) { mIsMonospaceComplete = false; @@ -678,27 +707,26 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool } } - if ( bold && italic && mFontBoldItalic != nullptr && + if ( bold && italic && mFontBoldItalic && ( idx = mFontBoldItalic->getGlyphIndex( codePoint ) ) ) { return mFontBoldItalic->getGlyphByIndex( idx, characterSize, true, true, outlineThickness, getPage( characterSize ) ); } - if ( bold && !italic && mFontBold != nullptr && - ( idx = mFontBold->getGlyphIndex( codePoint ) ) ) { + if ( bold && !italic && mFontBold && ( idx = mFontBold->getGlyphIndex( codePoint ) ) ) { return mFontBold->getGlyphByIndex( idx, characterSize, true, false, outlineThickness, getPage( characterSize ) ); } - if ( italic && !bold && mFontItalic != nullptr && - ( idx = mFontItalic->getGlyphIndex( codePoint ) ) ) { + if ( italic && !bold && mFontItalic && ( idx = mFontItalic->getGlyphIndex( codePoint ) ) ) { return mFontItalic->getGlyphByIndex( idx, characterSize, false, true, outlineThickness, getPage( characterSize ) ); } idx = getGlyphIndex( codePoint ); - if ( 0 == idx && mEnableFallbackFont && FontManager::instance()->hasFallbackFonts() ) { - for ( Font* fallbackFontPtr : FontManager::instance()->getFallbackFonts() ) { + if ( 0 == idx && mEnableFallbackFont && mFontService && mFontService->hasFallbackFonts() ) { + for ( const FontPtr& fallbackFontHandle : mFontService->getFallbackFonts() ) { + Font* fallbackFontPtr = fallbackFontHandle.get(); if ( fallbackFontPtr->getType() != FontType::TTF ) continue; FontTrueType* fallbackFont = static_cast( fallbackFontPtr ); @@ -718,7 +746,7 @@ Glyph FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSize, bool codePoint, FontWeight::Normal, false ); if ( !fallbackDesc.path.empty() ) { FontTrueType* systemFallback = - FontManager::instance()->getOrLoadSystemFallbackFont( fallbackDesc ); + mFontService ? mFontService->getOrLoadSystemFallbackFont( fallbackDesc ) : nullptr; if ( systemFallback && ( idx = systemFallback->getGlyphIndex( codePoint ) ) ) { if ( mIsMonospace && mEnableDynamicMonospace ) { mIsMonospaceComplete = false; @@ -791,10 +819,11 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch if ( mEnableEmojiFallback && Font::isEmojiCodePoint( codePoint ) && !mIsColorEmojiFont && !mIsEmojiFont ) { - if ( !mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() != nullptr && - FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF ) { + if ( mFontService && !mIsColorEmojiFont && + mFontService->getColorEmojiFont() != nullptr && + mFontService->getColorEmojiFont()->getType() == FontType::TTF ) { FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getColorEmojiFont() ); + static_cast( mFontService->getColorEmojiFont() ); tGlyphIndex = fontEmoji->getGlyphIndex( codePoint ); if ( 0 != tGlyphIndex ) { glyphIndex = tGlyphIndex; @@ -802,10 +831,10 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch } else { glyphIndex = getGlyphIndex( codePoint ); } - } else if ( !mIsEmojiFont && FontManager::instance()->getEmojiFont() != nullptr && - FontManager::instance()->getEmojiFont()->getType() == FontType::TTF ) { + } else if ( mFontService && !mIsEmojiFont && mFontService->getEmojiFont() != nullptr && + mFontService->getEmojiFont()->getType() == FontType::TTF ) { FontTrueType* fontEmoji = - static_cast( FontManager::instance()->getEmojiFont() ); + static_cast( mFontService->getEmojiFont() ); tGlyphIndex = fontEmoji->getGlyphIndex( codePoint ); if ( 0 != tGlyphIndex ) { glyphIndex = tGlyphIndex; @@ -820,29 +849,30 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch glyphIndex = getGlyphIndex( codePoint ); } - if ( bold && italic && mFontBoldItalic != nullptr && + if ( bold && italic && mFontBoldItalic && ( tGlyphIndex = mFontBoldItalic->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; fontInternalId = mFontBoldItalic->getFontInternalId(); isItalic = true; } - if ( bold && !italic && mFontBold != nullptr && + if ( bold && !italic && mFontBold && ( tGlyphIndex = mFontBold->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; fontInternalId = mFontBold->getFontInternalId(); } - if ( italic && !bold && mFontItalic != nullptr && + if ( italic && !bold && mFontItalic && ( tGlyphIndex = mFontItalic->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; fontInternalId = mFontItalic->getFontInternalId(); isItalic = true; } - if ( 0 == glyphIndex && mEnableFallbackFont && - FontManager::instance()->hasFallbackFonts() ) { - for ( Font* fontFallbackPtr : FontManager::instance()->getFallbackFonts() ) { + if ( 0 == glyphIndex && mEnableFallbackFont && mFontService && + mFontService->hasFallbackFonts() ) { + for ( const FontPtr& fontFallbackHandle : mFontService->getFallbackFonts() ) { + Font* fontFallbackPtr = fontFallbackHandle.get(); if ( fontFallbackPtr->getType() != FontType::TTF ) continue; FontTrueType* fontFallback = static_cast( fontFallbackPtr ); @@ -866,7 +896,8 @@ GlyphDrawable* FontTrueType::getGlyphDrawable( Uint32 codePoint, unsigned int ch codePoint, FontWeight::Normal, false ); if ( !fallbackDesc.path.empty() ) { FontTrueType* systemFallback = - FontManager::instance()->getOrLoadSystemFallbackFont( fallbackDesc ); + mFontService ? mFontService->getOrLoadSystemFallbackFont( fallbackDesc ) + : nullptr; if ( systemFallback && ( tGlyphIndex = systemFallback->getGlyphIndex( codePoint ) ) ) { glyphIndex = tGlyphIndex; @@ -1143,15 +1174,15 @@ bool FontTrueType::loaded() const { return NULL != mFace; } +FontService* FontTrueType::getFontService() const { + return mFontService; +} + void FontTrueType::cleanup() { sendEvent( Event::Unload ); - if ( FontManager::existsSingleton() && FontManager::instance()->getColorEmojiFont() == this ) - FontManager::instance()->setColorEmojiFont( nullptr ); - - disconnectBoldItalicFont(); - disconnectBoldFont(); - disconnectItalicFont(); + if ( mFontService && mFontService->getColorEmojiFont() == this ) + mFontService->setColorEmojiFont( nullptr ); mCallbacks.clear(); mNumCallBacks = 0; @@ -1202,16 +1233,14 @@ void FontTrueType::cleanup() { mIsBold = false; mIsItalic = false; mIsMonospaceCompletePending = false; - mFontBold = nullptr; - mFontItalic = nullptr; - mFontBoldItalic = nullptr; - mFontBoldCb = 0; - mFontItalicCb = 0; - mFontBoldItalicCb = 0; + mFontBold.reset(); + mFontItalic.reset(); + mFontBoldItalic.reset(); mPages.clear(); std::vector().swap( mPixelBuffer ); mKerningCache.clear(); mKerningGlyphCache.clear(); + mGlyphAdvanceCache.clear(); mCodePointIndexCache.clear(); mKeyCache.clear(); mClosestCharacterSize.clear(); @@ -1226,6 +1255,81 @@ static int fontSetLoadOptions( FontAntialiasing antialiasing, FontHinting hintin return load_target | hint; } +Float FontTrueType::getGlyphAdvance( Uint32 codePoint, unsigned int characterSize, bool bold, + bool italic, Float outlineThickness ) const { + Uint32 index = 0; + if ( mEnableEmojiFallback && !mIsColorEmojiFont && !mHasSvgGlyphs && !mHasColrGlyphs && + !mIsEmojiFont && mFontService && Font::isEmojiCodePoint( codePoint ) ) { + Font* emojiFont = mFontService->getColorEmojiFont(); + if ( !emojiFont ) + emojiFont = mFontService->getEmojiFont(); + if ( emojiFont && emojiFont->getType() == FontType::TTF ) { + auto emojiTrueType = static_cast( emojiFont ); + if ( emojiTrueType->getGlyphIndex( codePoint ) ) + return emojiTrueType->getGlyphAdvance( codePoint, characterSize, bold, italic, + outlineThickness ); + } + } + + if ( bold && italic ) { + if ( mFontBoldItalic && ( index = mFontBoldItalic->getGlyphIndex( codePoint ) ) ) + return mFontBoldItalic->getGlyphAdvance( codePoint, characterSize, true, true, + outlineThickness ); + } else if ( bold ) { + if ( mFontBold && ( index = mFontBold->getGlyphIndex( codePoint ) ) ) + return mFontBold->getGlyphAdvance( codePoint, characterSize, true, false, + outlineThickness ); + } else if ( italic ) { + if ( mFontItalic && ( index = mFontItalic->getGlyphIndex( codePoint ) ) ) + return mFontItalic->getGlyphAdvance( codePoint, characterSize, false, true, + outlineThickness ); + } + + const FontTrueType* advanceFont = this; + index = getGlyphIndex( codePoint ); + if ( index == 0 && mEnableFallbackFont && mFontService && mFontService->hasFallbackFonts() ) { + for ( const FontPtr& fallbackHandle : mFontService->getFallbackFonts() ) { + if ( !fallbackHandle || fallbackHandle->getType() != FontType::TTF ) + continue; + auto fallbackFont = static_cast( fallbackHandle.get() ); + if ( ( index = fallbackFont->getGlyphIndex( codePoint ) ) ) { + advanceFont = fallbackFont; + break; + } + } + } + if ( index == 0 && mEnableSystemFallback && mFontService && + SystemFontResolver::existsSingleton() ) { + FontDesc fallbackDesc = SystemFontResolver::instance()->getFallbackForCodepoint( + codePoint, FontWeight::Normal, false ); + FontTrueType* fallbackFont = + fallbackDesc.path.empty() ? nullptr + : mFontService->getOrLoadSystemFallbackFont( fallbackDesc ); + if ( fallbackFont && ( index = fallbackFont->getGlyphIndex( codePoint ) ) ) + advanceFont = fallbackFont; + } + + Uint64 key = getIndexKey( advanceFont->mFontInternalId, index, bold, italic, outlineThickness ); + auto& advances = advanceFont->mGlyphAdvanceCache[characterSize]; + auto advanceIt = advances.find( key ); + if ( advanceIt != advances.end() ) + return advanceIt->second; + FT_Face face = static_cast( advanceFont->mFace ); + if ( !face || !advanceFont->setCurrentSize( characterSize ) ) + return 0.f; + FT_Int32 flags = + fontSetLoadOptions( advanceFont->mAntialiasing, advanceFont->mHinting ) | FT_LOAD_COLOR; + if ( FT_Load_Glyph( face, index, flags ) != 0 ) + return 0.f; + + Float advance = + static_cast( face->glyph->metrics.horiAdvance ) / static_cast( 1 << 6 ); + if ( bold && !advanceFont->mIsBold && !advanceFont->mBoldAdvanceSameAsRegular ) + advance += 1.f; + advances[key] = advance; + return advance; +} + static constexpr FT_Render_Mode fontSetRenderOptions( FT_Library library, FontAntialiasing antialiasing, FontHinting hinting, @@ -1786,7 +1890,10 @@ bool FontTrueType::isFallbackFontEnabled() const { } void FontTrueType::setEnableFallbackFont( bool enableFallbackFont ) { - mEnableFallbackFont = enableFallbackFont; + if ( mEnableFallbackFont != enableFallbackFont ) { + mEnableFallbackFont = enableFallbackFont; + mGlyphAdvanceCache.clear(); + } } bool FontTrueType::isSystemFallbackEnabled() const { @@ -1794,7 +1901,10 @@ bool FontTrueType::isSystemFallbackEnabled() const { } void FontTrueType::setEnableSystemFallback( bool enableSystemFallback ) { - mEnableSystemFallback = enableSystemFallback; + if ( mEnableSystemFallback != enableSystemFallback ) { + mEnableSystemFallback = enableSystemFallback; + mGlyphAdvanceCache.clear(); + } } bool FontTrueType::isEmojiFallbackEnabled() const { @@ -1802,7 +1912,10 @@ bool FontTrueType::isEmojiFallbackEnabled() const { } void FontTrueType::setEnableEmojiFallback( bool enableEmojiFallback ) { - mEnableEmojiFallback = enableEmojiFallback; + if ( mEnableEmojiFallback != enableEmojiFallback ) { + mEnableEmojiFallback = enableEmojiFallback; + mGlyphAdvanceCache.clear(); + } } const Uint32& FontTrueType::getFontInternalId() const { @@ -1848,7 +1961,10 @@ bool FontTrueType::getBoldAdvanceSameAsRegular() const { } void FontTrueType::setBoldAdvanceSameAsRegular( bool boldAdvanceSameAsRegular ) { - mBoldAdvanceSameAsRegular = boldAdvanceSameAsRegular; + if ( mBoldAdvanceSameAsRegular != boldAdvanceSameAsRegular ) { + mBoldAdvanceSameAsRegular = boldAdvanceSameAsRegular; + mGlyphAdvanceCache.clear(); + } } void FontTrueType::updateMonospaceState() const { @@ -1858,96 +1974,40 @@ void FontTrueType::updateMonospaceState() const { return; } mIsMonospaceCompletePending = false; - if ( mIsMonospaceComplete && mFontBold != nullptr ) { + if ( mIsMonospaceComplete && mFontBold ) { mIsMonospaceComplete = mIsMonospaceComplete && mFontBold->isMonospace() && - getGlyph( ' ', 10, false, false ).advance == - mFontBold->getGlyph( ' ', 10, false, false ).advance; + getGlyphAdvance( ' ', 10 ) == mFontBold->getGlyphAdvance( ' ', 10 ); } - if ( mIsMonospaceComplete && mFontItalic != nullptr ) { - mIsMonospaceComplete = mIsMonospaceComplete && mFontItalic->isMonospace() && - getGlyph( ' ', 10, false, false ).advance == - mFontItalic->getGlyph( ' ', 10, false, false ).advance; + if ( mIsMonospaceComplete && mFontItalic ) { + mIsMonospaceComplete = + mIsMonospaceComplete && mFontItalic->isMonospace() && + getGlyphAdvance( ' ', 10 ) == mFontItalic->getGlyphAdvance( ' ', 10 ); } - if ( mIsMonospaceComplete && mFontBoldItalic != nullptr ) { - mIsMonospaceComplete = mIsMonospaceComplete && mFontBoldItalic->isMonospace() && - getGlyph( ' ', 10, false, false ).advance == - mFontBoldItalic->getGlyph( ' ', 10, false, false ).advance; + if ( mIsMonospaceComplete && mFontBoldItalic ) { + mIsMonospaceComplete = + mIsMonospaceComplete && mFontBoldItalic->isMonospace() && + getGlyphAdvance( ' ', 10 ) == mFontBoldItalic->getGlyphAdvance( ' ', 10 ); } } -void FontTrueType::setBoldFont( FontTrueType* fontBold ) { - if ( fontBold == mFontBold ) - return; - disconnectBoldFont(); +void FontTrueType::setBoldFont( const FontTrueTypePtr& fontBold ) { mFontBold = fontBold; - if ( mFontBold != nullptr ) { - mFontBoldCb = mFontBold->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { - if ( event == Font::Event::Unload ) { - // Maybe we should recreate the page table - mFontBold = nullptr; - mFontBoldCb = 0; - } - } ); - } + mGlyphAdvanceCache.clear(); updateMonospaceState(); } -void FontTrueType::setItalicFont( FontTrueType* fontItalic ) { - if ( fontItalic == mFontItalic ) - return; - disconnectItalicFont(); +void FontTrueType::setItalicFont( const FontTrueTypePtr& fontItalic ) { mFontItalic = fontItalic; - if ( mFontItalic != nullptr ) { - mFontItalicCb = mFontItalic->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { - if ( event == Font::Event::Unload ) { - // Maybe we should recreate the page table - mFontItalic = nullptr; - mFontItalicCb = 0; - } - } ); - } + mGlyphAdvanceCache.clear(); updateMonospaceState(); } -void FontTrueType::setBoldItalicFont( FontTrueType* fontBoldItalic ) { - if ( fontBoldItalic == mFontBoldItalic ) - return; - disconnectBoldItalicFont(); +void FontTrueType::setBoldItalicFont( const FontTrueTypePtr& fontBoldItalic ) { mFontBoldItalic = fontBoldItalic; - if ( mFontBoldItalic != nullptr ) { - mFontBoldItalicCb = - mFontBoldItalic->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { - if ( event == Font::Event::Unload ) { - // Maybe we should recreate the page table - mFontBoldItalic = nullptr; - mFontBoldItalicCb = 0; - } - } ); - } + mGlyphAdvanceCache.clear(); updateMonospaceState(); } -void FontTrueType::disconnectBoldFont() { - if ( mFontBoldCb != 0 && mFontBold != nullptr ) - mFontBold->popFontEventCallback( mFontBoldCb ); - mFontBold = nullptr; - mFontBoldCb = 0; -} - -void FontTrueType::disconnectItalicFont() { - if ( mFontItalicCb != 0 && mFontItalic != nullptr ) - mFontItalic->popFontEventCallback( mFontItalicCb ); - mFontItalic = nullptr; - mFontItalicCb = 0; -} - -void FontTrueType::disconnectBoldItalicFont() { - if ( mFontBoldItalicCb != 0 && mFontBoldItalic != nullptr ) - mFontBoldItalic->popFontEventCallback( mFontBoldItalicCb ); - mFontBoldItalic = nullptr; - mFontBoldItalicCb = 0; -} - bool FontTrueType::hasSvgGlyphs() const { return mHasSvgGlyphs; } @@ -1988,6 +2048,7 @@ void FontTrueType::clearCache() { mKeyCache.clear(); mKerningCache.clear(); mKerningGlyphCache.clear(); + mGlyphAdvanceCache.clear(); Text::GlobalInvalidationId++; } diff --git a/src/eepp/graphics/resourcecatalog.cpp b/src/eepp/graphics/resourcecatalog.cpp index 1868aa2cf..bc4339ef0 100644 --- a/src/eepp/graphics/resourcecatalog.cpp +++ b/src/eepp/graphics/resourcecatalog.cpp @@ -98,6 +98,35 @@ void ResourceCatalog::publishAtlas( std::string key, TextureAtlasPtr atlas ) { previous.reset(); } +void ResourceCatalog::publishFont( ResourceKey key, FontPtr font ) { + publishFont( key.value(), std::move( font ) ); +} + +void ResourceCatalog::publishFont( std::string key, FontPtr font ) { + if ( key.empty() ) + return; + if ( !font ) { + eraseFont( key ); + return; + } + + FontPtr previous; + String::HashType id = String::hash( key ); + { + Lock lock( mMutex ); + auto it = mFonts.find( key ); + if ( it == mFonts.end() ) { + mFonts.emplace( std::move( key ), font ); + mFontsById[id] = font; + return; + } + previous = std::move( it->second ); + it->second = font; + mFontsById[id] = font; + } + previous.reset(); +} + TexturePtr ResourceCatalog::findTexture( const ResourceKey& key ) const { return findTexture( key.value() ); } @@ -143,6 +172,31 @@ std::vector ResourceCatalog::getAtlases() const { return atlases; } +FontPtr ResourceCatalog::findFont( const ResourceKey& key ) const { + return findFont( key.value() ); +} + +FontPtr ResourceCatalog::findFont( const std::string& key ) const { + Lock lock( mMutex ); + auto it = mFonts.find( key ); + return it != mFonts.end() ? it->second : FontPtr{}; +} + +FontPtr ResourceCatalog::findFont( const String::HashType& id ) const { + Lock lock( mMutex ); + auto it = mFontsById.find( id ); + return it != mFontsById.end() ? it->second.lock() : FontPtr{}; +} + +std::vector ResourceCatalog::getFonts() const { + std::vector fonts; + Lock lock( mMutex ); + fonts.reserve( mFonts.size() ); + for ( const auto& font : mFonts ) + fonts.emplace_back( font.second ); + return fonts; +} + bool ResourceCatalog::erase( const ResourceKey& key ) { return erase( key.value() ); } @@ -204,28 +258,74 @@ bool ResourceCatalog::eraseAtlas( const std::string& key ) { return true; } +bool ResourceCatalog::eraseFont( const ResourceKey& key ) { + return eraseFont( key.value() ); +} + +bool ResourceCatalog::eraseFont( const std::string& key ) { + FontPtr font; + { + Lock lock( mMutex ); + auto it = mFonts.find( key ); + if ( it == mFonts.end() ) + return false; + font = std::move( it->second ); + mFonts.erase( it ); + auto idIt = mFontsById.find( String::hash( key ) ); + if ( idIt != mFontsById.end() ) + mFontsById.erase( idIt ); + } + font.reset(); + return true; +} + +bool ResourceCatalog::eraseFont( Font* font ) { + if ( !font ) + return false; + FontPtr removed; + { + Lock lock( mMutex ); + auto it = mFonts.find( font->getName() ); + if ( it == mFonts.end() || it->second.get() != font ) + return false; + removed = std::move( it->second ); + mFonts.erase( it ); + auto idIt = mFontsById.find( font->getId() ); + if ( idIt != mFontsById.end() ) + mFontsById.erase( idIt ); + } + removed.reset(); + return true; +} + void ResourceCatalog::clear() { UnorderedMap textures; UnorderedMap drawables; UnorderedMap drawablesById; UnorderedMap atlases; + UnorderedMap fonts; + UnorderedMap fontsById; { Lock lock( mMutex ); textures = std::move( mTextures ); drawables = std::move( mDrawables ); drawablesById = std::move( mDrawablesById ); atlases = std::move( mAtlases ); + fonts = std::move( mFonts ); + fontsById = std::move( mFontsById ); } textures.clear(); drawables.clear(); drawablesById.clear(); atlases.clear(); + fonts.clear(); + fontsById.clear(); } std::size_t ResourceCatalog::size() const { Lock lock( mMutex ); - return mTextures.size() + mDrawables.size() + mAtlases.size(); + return mTextures.size() + mDrawables.size() + mAtlases.size() + mFonts.size(); } }} // namespace EE::Graphics diff --git a/src/eepp/graphics/resourcescope.cpp b/src/eepp/graphics/resourcescope.cpp index 2501b8c8b..674473f69 100644 --- a/src/eepp/graphics/resourcescope.cpp +++ b/src/eepp/graphics/resourcescope.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -15,7 +16,37 @@ ResourceScopePtr ResourceScope::New() { return ResourceScopePtr( eeNew( ResourceScope, () ), ResourceDeleter() ); } -ResourceScope::ResourceScope() : mLocalCatalog( ResourceCatalog::New() ) {} +ResourceScope::ResourceScope() : mLocalCatalog( ResourceCatalog::New() ), mFontService( *this ) {} + +ResourceScope::~ResourceScope() { + for ( const FontPtr& font : mLocalCatalog->getFonts() ) + detachFontService( font ); +} + +void ResourceScope::attachFontService( const FontPtr& font ) { + if ( font && font->getType() == FontType::TTF ) + static_cast( font.get() )->setFontService( &mFontService ); +} + +void ResourceScope::detachFontService( const FontPtr& font ) { + if ( font ) { + mFontService.onFontRemoved( font.get() ); + if ( font->getType() != FontType::TTF ) + return; + auto* ttf = static_cast( font.get() ); + if ( ttf->getFontService() == &mFontService ) { + ttf->setFontService( nullptr ); + } + } +} + +FontService& ResourceScope::getFontService() { + return mFontService; +} + +const FontService& ResourceScope::getFontService() const { + return mFontService; +} TexturePtr ResourceScope::findTexture( const ResourceKey& key ) const { return findTexture( key.value() ); @@ -75,6 +106,42 @@ std::vector ResourceScope::getAtlases() const { return atlases; } +FontPtr ResourceScope::findFont( const ResourceKey& key ) const { + return findFont( key.value() ); +} + +FontPtr ResourceScope::findFont( const std::string& key ) const { + if ( FontPtr font = mLocalCatalog->findFont( key ) ) + return font; + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( FontPtr font = catalog->findFont( key ) ) + return font; + } + return {}; +} + +FontPtr ResourceScope::findFont( const String::HashType& id ) const { + if ( FontPtr font = mLocalCatalog->findFont( id ) ) + return font; + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + if ( FontPtr font = catalog->findFont( id ) ) + return font; + } + return {}; +} + +std::vector ResourceScope::getFonts() const { + std::vector fonts = mLocalCatalog->getFonts(); + Lock lock( mMutex ); + for ( const ResourceCatalogPtr& catalog : mImports ) { + std::vector imported = catalog->getFonts(); + fonts.insert( fonts.end(), imported.begin(), imported.end() ); + } + return fonts; +} + std::vector ResourceScope::findTextureRegionsByPattern( const std::string& name, const std::string& extension, TextureAtlas* searchInTextureAtlas ) const { @@ -232,6 +299,18 @@ void ResourceScope::publishLocalAtlas( std::string key, TextureAtlasPtr atlas ) mLocalCatalog->publishAtlas( std::move( key ), std::move( atlas ) ); } +void ResourceScope::publishLocalFont( ResourceKey key, FontPtr font ) { + publishLocalFont( key.value(), std::move( font ) ); +} + +void ResourceScope::publishLocalFont( std::string key, FontPtr font ) { + FontPtr replaced = mLocalCatalog->findFont( key ); + if ( replaced && replaced != font ) + detachFontService( replaced ); + attachFontService( font ); + mLocalCatalog->publishFont( std::move( key ), std::move( font ) ); +} + bool ResourceScope::eraseLocal( const ResourceKey& key ) { return mLocalCatalog->erase( key ); } @@ -256,7 +335,33 @@ bool ResourceScope::eraseLocalAtlas( const std::string& key ) { return mLocalCatalog->eraseAtlas( key ); } +bool ResourceScope::eraseLocalFont( const ResourceKey& key ) { + return eraseLocalFont( key.value() ); +} + +bool ResourceScope::eraseLocalFont( const std::string& key ) { + FontPtr font = mLocalCatalog->findFont( key ); + if ( !font ) + return false; + detachFontService( font ); + return mLocalCatalog->eraseFont( key ); +} + +bool ResourceScope::eraseLocalFont( Font* font ) { + if ( !font ) + return false; + FontPtr handle = mLocalCatalog->findFont( font->getId() ); + if ( handle.get() != font ) + return false; + if ( !mLocalCatalog->eraseFont( font ) ) + return false; + detachFontService( handle ); + return true; +} + void ResourceScope::clearLocal() { + for ( const FontPtr& font : mLocalCatalog->getFonts() ) + detachFontService( font ); mLocalCatalog->clear(); } diff --git a/src/eepp/graphics/richtext.cpp b/src/eepp/graphics/richtext.cpp index f9f81c3ee..6fc0c5740 100644 --- a/src/eepp/graphics/richtext.cpp +++ b/src/eepp/graphics/richtext.cpp @@ -1,8 +1,8 @@ #include #include -#include #include #include +#include #include namespace EE { namespace Graphics { diff --git a/src/eepp/graphics/text.cpp b/src/eepp/graphics/text.cpp index 3cd21972e..c0bfaab91 100644 --- a/src/eepp/graphics/text.cpp +++ b/src/eepp/graphics/text.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -341,11 +341,12 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo String::StringBaseType prevChar = 0; bool isBold = ( style & Text::Bold ) != 0; bool isItalic = ( style & Text::Italic ) != 0; - bool fallbacksToColorEmoji = - font && font->getType() == FontType::TTF && - !static_cast( font )->isColorEmojiFont() && - FontManager::instance()->getColorEmojiFont() != nullptr && - FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF; + FontTrueType* trueTypeFont = + font && font->getType() == FontType::TTF ? static_cast( font ) : nullptr; + FontService* fontService = trueTypeFont ? trueTypeFont->getFontService() : nullptr; + bool fallbacksToColorEmoji = trueTypeFont && !trueTypeFont->isColorEmojiFont() && fontService && + fontService->getColorEmojiFont() != nullptr && + fontService->getColorEmojiFont()->getType() == FontType::TTF; bool isMonospace = font && ( font->isMonospace() || ( font->getType() == FontType::TTF && static_cast( font )->isIdentifiedAsMonospace() && @@ -355,12 +356,14 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo Float height = font->getLineSpacing( fontSize ); Sizef size{ 0, height }; size_t ssize = string.size(); + if ( ssize == 0 ) + return size; BatchRenderer* BR = GlobalBatchRenderer::instance(); const TexturePtr& fontTexture = font->getTexture( fontSize ); Float tabAlign = 0; GlyphDrawable* spaceGlyph = nullptr; GlyphDrawable* tabGlyph = nullptr; - Float hspace = font->getGlyph( ' ', fontSize, isBold, isItalic ).advance; + Float hspace = font->getGlyphAdvance( ' ', fontSize, isBold, isItalic ); std::optional tabOffset{ whitespaceDisplayConfig.tabOffset }; if ( whitespaceDisplayConfig.tabDisplayCharacter ) tabGlyph = font->getGlyphDrawable( whitespaceDisplayConfig.tabDisplayCharacter, fontSize ); @@ -640,9 +643,10 @@ void Text::create( Font* font, const String& text, Color FontColor, Color FontSh void Text::checkColorEmojis() { mContainsColorEmoji = false; - if ( mFontStyleConfig.Font && FontManager::instance()->getColorEmojiFont() != nullptr ) { - if ( mFontStyleConfig.Font->getType() == FontType::TTF ) { - FontTrueType* fontTrueType = static_cast( mFontStyleConfig.Font ); + if ( mFontStyleConfig.Font && mFontStyleConfig.Font->getType() == FontType::TTF ) { + FontTrueType* fontTrueType = static_cast( mFontStyleConfig.Font ); + FontService* fontService = fontTrueType->getFontService(); + if ( fontService && fontService->getColorEmojiFont() != nullptr ) { if ( fontTrueType->isColorEmojiFont() || !fontTrueType->isEmojiFont() ) mContainsColorEmoji = Font::containsEmojiCodePoint( mString ); } @@ -2963,11 +2967,9 @@ SmallVector Text::getSelectionRects( TextSelectionRange range ) { size_t startLine = findVisualLineFromCharIndex( range.start ); size_t endLine = findVisualLineFromCharIndex( range.end ); - Float hspace = - mFontStyleConfig.Font - ->getGlyph( ' ', mFontStyleConfig.CharacterSize, mFontStyleConfig.Style & Text::Bold, - mFontStyleConfig.Style & Text::Italic ) - .advance; + Float hspace = mFontStyleConfig.Font->getGlyphAdvance( ' ', mFontStyleConfig.CharacterSize, + mFontStyleConfig.Style & Text::Bold, + mFontStyleConfig.Style & Text::Italic ); Float vspace = getLineSpacing(); for ( size_t i = startLine; i <= endLine; ++i ) { diff --git a/src/eepp/graphics/textlayout.cpp b/src/eepp/graphics/textlayout.cpp index 70324a882..547bf5b16 100644 --- a/src/eepp/graphics/textlayout.cpp +++ b/src/eepp/graphics/textlayout.cpp @@ -13,7 +13,12 @@ namespace EE::Graphics { -using LRULayoutCache = LRUCache<8192, Uint64, TextLayout::Cache>; +struct LayoutCacheEntry { + Font* sourceFont{ nullptr }; + TextLayout::Cache layout; +}; + +using LRULayoutCache = LRUCache<8192, Uint64, LayoutCacheEntry>; #ifdef EE_TEXT_SHAPER_ENABLED @@ -236,15 +241,30 @@ static inline Uint64 textLayoutHash( const String::View& string, Font* font, std::hash()( initialXOffset ) ); } -static LRULayoutCache& getLayoutCache( bool invalidate = false ) { +static LRULayoutCache& getLayoutCache( bool invalidate = false, Font* font = nullptr ) { static LRULayoutCache sLayoutCache; - if ( invalidate ) - sLayoutCache.clear(); + if ( invalidate ) { + if ( !font ) { + sLayoutCache.clear(); + } else { + sLayoutCache.eraseIf( [font]( Uint64, const LayoutCacheEntry& entry ) { + if ( !entry.layout || entry.sourceFont == font ) + return true; + for ( const ShapedTextParagraph& paragraph : entry.layout->paragraphs ) { + for ( const ShapedGlyph& glyph : paragraph.shapedGlyphs ) { + if ( glyph.font == font ) + return true; + } + } + return false; + } ); + } + } return sLayoutCache; } -void TextLayout::clearLayoutCache() { - getLayoutCache( true ); +void TextLayout::clearLayoutCache( Font* font ) { + getLayoutCache( true, font ); } TextLayout::Cache TextLayout::layout( const String::View& string, Font* font, @@ -270,13 +290,13 @@ TextLayout::Cache TextLayout::layout( const String::View& string, Font* font, auto cacheHit = getLayoutCache().get( hash ); if ( cacheHit.has_value() ) - return *cacheHit; + return cacheHit->layout; } bool bold = ( style & Text::Bold ) != 0; bool italic = ( style & Text::Italic ) != 0; Uint32 spaceGlyphIndex = 0; - Float hspace = font->getGlyph( ' ', characterSize, bold, italic, outlineThickness ).advance; + Float hspace = font->getGlyphAdvance( ' ', characterSize, bold, italic, outlineThickness ); Float vspace = font->getLineSpacing( characterSize ); Vector2f pen{ initialXOffset, 0 }; Float maxWidth = 0; @@ -523,7 +543,7 @@ TextLayout::Cache TextLayout::layout( const String::View& string, Font* font, characterSize, style, tabWidth, outlineThickness, hspace ); } - getLayoutCache().put( hash, resultPtr ); + getLayoutCache().put( hash, { font, resultPtr } ); return resultPtr; } diff --git a/src/eepp/graphics/texturefactory.cpp b/src/eepp/graphics/texturefactory.cpp index 20044115f..33cc5b4d8 100644 --- a/src/eepp/graphics/texturefactory.cpp +++ b/src/eepp/graphics/texturefactory.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/src/eepp/ui/css/drawableimageparser.cpp b/src/eepp/ui/css/drawableimageparser.cpp index 2d3771db2..e6d75dc8c 100644 --- a/src/eepp/ui/css/drawableimageparser.cpp +++ b/src/eepp/ui/css/drawableimageparser.cpp @@ -1,8 +1,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -151,7 +151,7 @@ bool DrawableImageParser::exists( const std::string& name ) const { } DrawablePtr DrawableImageParser::createDrawable( const std::string& value, const Sizef& size, - UINode* node ) { + UINode* node ) { FunctionString functionType = FunctionString::parse( value ); if ( "none" == value ) @@ -183,7 +183,7 @@ void DrawableImageParser::addParser( const std::string& name, void DrawableImageParser::registerBaseParsers() { // Shared parsing logic for linear-gradient and repeating-linear-gradient auto parseGradient = []( const FunctionString& functionType, UINode* node, - bool repeating ) -> DrawablePtr { + bool repeating ) -> DrawablePtr { const auto& params( functionType.getParameters() ); if ( params.size() < 2 ) return {}; @@ -459,14 +459,14 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["repeating-linear-gradient"] = [parseGradient]( const FunctionString& functionType, - const Sizef& /*size*/, - UINode* node ) -> DrawablePtr { + const Sizef& /*size*/, + UINode* node ) -> DrawablePtr { return parseGradient( functionType, node, true ); }; // Shared parsing logic for radial-gradient and repeating-radial-gradient auto parseRadialGradient = []( const FunctionString& functionType, UINode* node, - bool repeating ) -> DrawablePtr { + bool repeating ) -> DrawablePtr { const auto& params( functionType.getParameters() ); if ( params.size() < 2 ) return {}; @@ -681,13 +681,13 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["repeating-radial-gradient"] = [parseRadialGradient]( const FunctionString& functionType, - const Sizef& /*size*/, - UINode* node ) -> DrawablePtr { + const Sizef& /*size*/, + UINode* node ) -> DrawablePtr { return parseRadialGradient( functionType, node, true ); }; mFuncs["circle"] = []( const FunctionString& functionType, const Sizef& size, - UINode* node ) -> DrawablePtr { + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 1 ) { return {}; } @@ -717,7 +717,7 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["rectangle"] = []( const FunctionString& functionType, const Sizef& size, - UINode* node ) -> DrawablePtr { + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 1 ) { return {}; } @@ -776,7 +776,7 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["triangle"] = []( const FunctionString& functionType, const Sizef& size, - UINode* node ) -> DrawablePtr { + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 2 ) { return {}; } @@ -843,7 +843,7 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["poly"] = []( const FunctionString& functionType, const Sizef& size, - UINode* node ) -> DrawablePtr { + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 2 ) { return {}; } @@ -899,7 +899,7 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["url"] = []( const FunctionString& functionType, const Sizef& /*size*/, - UINode* node ) -> DrawablePtr { + UINode* node ) -> DrawablePtr { if ( functionType.getParameters().size() < 1 ) return {}; const auto& param = functionType.getParameters().at( 0 ); @@ -915,8 +915,7 @@ void DrawableImageParser::registerBaseParsers() { cparam += ','; cparam += functionType.getParameters().at( i ); } - DrawablePtr drawable = - node->getUISceneNode()->getDrawableResolver().resolve( cparam ); + DrawablePtr drawable = node->getUISceneNode()->getDrawableResolver().resolve( cparam ); return drawable; } DrawablePtr drawable = node->getUISceneNode()->getDrawableResolver().resolve( param ); @@ -924,7 +923,7 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["icon"] = []( const FunctionString& functionType, const Sizef& size, - UINode* node ) -> DrawablePtr { + UINode* node ) -> DrawablePtr { auto* uiScene = node->getUISceneNode(); const auto& params = functionType.getParameters(); if ( params.size() < 2 ) @@ -935,11 +934,11 @@ void DrawableImageParser::registerBaseParsers() { }; mFuncs["glyph"] = []( const FunctionString& functionType, const Sizef& size, - UINode* node ) -> DrawablePtr { + UINode* node ) -> DrawablePtr { const auto& params = functionType.getParameters(); if ( params.size() < 3 ) return nullptr; - Font* font = FontManager::instance()->getByName( params[0] ); + Font* font = node->getUISceneNode()->getResourceScope()->findFont( params[0] ).get(); if ( font == nullptr ) return nullptr; Uint32 codePoint = 0; @@ -955,8 +954,8 @@ void DrawableImageParser::registerBaseParsers() { } else if ( String::fromString( value, buffer ) ) { codePoint = value; } - Drawable* drawable = font->getGlyphDrawable( - codePoint, node->convertLength( params[1], size.getWidth() ) ); + Drawable* drawable = + font->getGlyphDrawable( codePoint, node->convertLength( params[1], size.getWidth() ) ); return drawable ? drawable->clone() : DrawablePtr{}; }; } diff --git a/src/eepp/ui/tools/uifontpickerdialog.cpp b/src/eepp/ui/tools/uifontpickerdialog.cpp index 72bdc158f..73dbefd2c 100644 --- a/src/eepp/ui/tools/uifontpickerdialog.cpp +++ b/src/eepp/ui/tools/uifontpickerdialog.cpp @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include #include @@ -267,6 +267,7 @@ UIFontPickerDialog::~UIFontPickerDialog() { mColorPicker = nullptr; mColorPickerCloseCb = 0; clearBrowseDialog(); + clearPreviewFont(); } Uint32 UIFontPickerDialog::getType() const { @@ -294,7 +295,7 @@ void UIFontPickerDialog::setTheme( UITheme* theme ) { if ( mButtonBrowse ) { if ( DrawablePtr icon = getUISceneNode()->findIconDrawable( "document-open", - PixelDensity::dpToPxI( 16 ) ) ) + PixelDensity::dpToPxI( 16 ) ) ) mButtonBrowse->setIcon( std::move( icon ) ); } @@ -438,7 +439,7 @@ void UIFontPickerDialog::loadFonts() { void UIFontPickerDialog::setFonts( std::vector fonts ) { FontDesc selectedFont = mSelection.font; - mergeFontManagerFonts( fonts ); + mergeLoadedFonts( fonts ); for ( const auto& font : mFonts ) { if ( std::find_if( fonts.begin(), fonts.end(), [&]( const FontDesc& desc ) { return desc.sameFile( font ); @@ -471,21 +472,23 @@ void UIFontPickerDialog::sortFonts() { } ); } -void UIFontPickerDialog::mergeFontManagerFonts( std::vector& fonts ) { - FontManager::instance()->each( [&]( const auto& res ) { - if ( res.second == nullptr || res.second->getType() != FontType::TTF ) - return; +void UIFontPickerDialog::mergeLoadedFonts( std::vector& fonts ) { + if ( !getUISceneNode() ) + return; + for ( const FontPtr& font : getUISceneNode()->getResourceScope()->getFonts() ) { + if ( font == nullptr || font->getType() != FontType::TTF ) + continue; FontDesc desc; - if ( !static_cast( res.second )->getFontDesc( desc ) ) - return; + if ( !static_cast( font.get() )->getFontDesc( desc ) ) + continue; mLoadedFontKeys.insert( desc.getFileKey() ); if ( std::find_if( fonts.begin(), fonts.end(), [&]( const FontDesc& font ) { return font.sameFile( desc ); } ) == fonts.end() ) fonts.push_back( desc ); - } ); + } } void UIFontPickerDialog::updateFontTags() { @@ -628,10 +631,24 @@ void UIFontPickerDialog::updatePreview() { if ( !mPreviewText ) return; - FontTrueType* font = FontManager::instance()->getOrLoadSystemFallbackFont( mSelection.font ); - if ( font ) { - mPreviewText->setFont( font ); - mPreviewInput->setFont( font ); + if ( !mPreviewTextDefaultFont ) + mPreviewTextDefaultFont = mPreviewText->getFont(); + if ( !mPreviewInputDefaultFont ) + mPreviewInputDefaultFont = mPreviewInput->getFont(); + + FontDesc previewDesc; + const bool previewMatchesSelection = mPreviewFont && mPreviewFont->getFontDesc( previewDesc ) && + previewDesc.sameFile( mSelection.font ); + if ( !previewMatchesSelection && !mSelection.font.path.empty() && getUISceneNode() ) { + FontTrueTypePtr font = + getUISceneNode()->getResourceScope()->getFontService().loadSystemFont( + mSelection.font ); + if ( font ) { + mPreviewText->setFont( font.get() ); + mPreviewInput->setFont( font.get() ); + clearPreviewFont(); + mPreviewFont = std::move( font ); + } } mPreviewText->setFontSize( PixelDensity::dpToPxI( mSelection.size * 2 ) ); @@ -657,6 +674,17 @@ void UIFontPickerDialog::updatePreview() { } } +void UIFontPickerDialog::clearPreviewFont() { + if ( !mPreviewFont ) + return; + if ( mPreviewText && mPreviewTextDefaultFont && mPreviewText->getFont() == mPreviewFont.get() ) + mPreviewText->setFont( mPreviewTextDefaultFont ); + if ( mPreviewInput && mPreviewInputDefaultFont && + mPreviewInput->getFont() == mPreviewFont.get() ) + mPreviewInput->setFont( mPreviewInputDefaultFont ); + mPreviewFont.reset(); +} + void UIFontPickerDialog::selectInitialRows() { mSizeModel = ItemListModel::create( mSizes ); mSizeList->setModel( mSizeModel ); @@ -782,18 +810,21 @@ bool UIFontPickerDialog::addExternalFont( const std::string& path, Uint32 faceIn const std::string fontName( FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ) ); - FontTrueType* font = FontTrueType::New( fontName ); + if ( !getUISceneNode() ) + return false; + ResourceScope& resourceScope = *getUISceneNode()->getResourceScope(); + FontTrueTypePtr font = FontTrueType::New( fontName, resourceScope ); if ( !font || !font->loadFromFile( path, faceIndex ) ) { - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); return false; } FontDesc desc; if ( !font->getFontDesc( desc ) ) { - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); return false; } - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); mLoadedFontKeys.insert( desc.getFileKey() ); mFonts.push_back( desc ); diff --git a/src/eepp/ui/uiapplication.cpp b/src/eepp/ui/uiapplication.cpp index 0fe03f806..bfbda4ff8 100644 --- a/src/eepp/ui/uiapplication.cpp +++ b/src/eepp/ui/uiapplication.cpp @@ -59,17 +59,20 @@ UIApplication::UIApplication( const WindowSettings& windowSettings, const Settin if ( !appSettings.loadBaseResources ) return; - - Font* font = appSettings.baseFont - ? appSettings.baseFont - : FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); + FontTrueTypePtr loadedBaseFont; + if ( !appSettings.baseFont ) + loadedBaseFont = + FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); + Font* font = appSettings.baseFont ? appSettings.baseFont : loadedBaseFont.get(); if ( font && font->getType() == FontType::TTF ) FontFamily::loadFromRegular( static_cast( font ) ); - Font* monospaceFont = appSettings.monospaceFont - ? appSettings.monospaceFont - : FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" ); + FontTrueTypePtr loadedMonospaceFont; + if ( !appSettings.monospaceFont ) + loadedMonospaceFont = FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" ); + Font* monospaceFont = + appSettings.monospaceFont ? appSettings.monospaceFont : loadedMonospaceFont.get(); if ( monospaceFont && monospaceFont->getType() == FontType::TTF ) { static_cast( monospaceFont )->setEnableDynamicMonospace( true ); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index a1ab4161f..cde9a7694 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -1,10 +1,10 @@ #include "eepp/ui/uistyle.hpp" #include -#include #include #include #include #include +#include #include #include #include @@ -138,7 +138,7 @@ const MouseBindings::ShortcutMap UICodeEditor::getDefaultMousebindings() { UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands, const bool& autoRegisterBaseKeybindings ) : UIWidget( elementTag ), - mFont( FontManager::instance()->getByName( "monospace" ) ), + mFont( getUISceneNode()->getResourceScope()->findFont( "monospace" ).get() ), mDoc( std::make_shared() ), mDocView( mDoc, mFontStyleConfig, { .tabStops = mTabStops } ), mBlinkTime( Seconds( 0.5f ) ), diff --git a/src/eepp/ui/uiconsole.cpp b/src/eepp/ui/uiconsole.cpp index 568053334..e235688a5 100644 --- a/src/eepp/ui/uiconsole.cpp +++ b/src/eepp/ui/uiconsole.cpp @@ -1,9 +1,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -52,7 +52,7 @@ UIConsole::UIConsole( Font* font, const bool& makeDefaultCommands, const bool& a mFontStyleConfig.Font = font; if ( nullptr == font ) - mFontStyleConfig.Font = FontManager::instance()->getByName( "monospace" ); + mFontStyleConfig.Font = getUISceneNode()->getResourceScope()->findFont( "monospace" ).get(); mMaxLogLines = maxLogLines; diff --git a/src/eepp/ui/uilistbox.cpp b/src/eepp/ui/uilistbox.cpp index 0e8a42063..a9c67ee17 100644 --- a/src/eepp/ui/uilistbox.cpp +++ b/src/eepp/ui/uilistbox.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp index 244d59448..a8b2e3320 100644 --- a/src/eepp/ui/uirichtext.cpp +++ b/src/eepp/ui/uirichtext.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 6c17cbf45..174afbc90 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -129,11 +129,11 @@ static void refreshWebViewDocumentLayoutAfterStyleChange( UIWidget* root ) { } } -UISceneNode* UISceneNode::New( EE::Window::Window* window ) { - return eeNew( UISceneNode, ( window ) ); +UISceneNode* UISceneNode::New( EE::Window::Window* window, bool importDefaultResources ) { + return eeNew( UISceneNode, ( window, importDefaultResources ) ); } -UISceneNode::UISceneNode( EE::Window::Window* window ) : +UISceneNode::UISceneNode( EE::Window::Window* window, bool importDefaultResources ) : SceneNode( window ), mRoot( NULL ), mIsLoading( false ), @@ -141,10 +141,14 @@ UISceneNode::UISceneNode( EE::Window::Window* window ) : mUIThemeManager( UIThemeManager::New() ), mUIIconThemeManager( UIIconThemeManager::New()->setFallbackThemeManager( mUIThemeManager ) ), mAsyncResourceLoadState( std::make_shared() ), + mImportDefaultResources( importDefaultResources ), mResourceScope( ResourceScope::New() ), mDrawableResolver( *this ), mWebResourceCache( WebResourceCache::New() ), mKeyBindings( mWindow->getInput() ) { + if ( mImportDefaultResources ) + mResourceScope->importCatalog( defaultResourceScope().getLocalCatalog() ); + // Reset size since the SceneNode already set it but needs to set the size from zero to emit // the required events to its children. mSize = Sizef(); @@ -764,6 +768,8 @@ const ResourceScopePtr& UISceneNode::getResourceScope() const { UISceneNode* UISceneNode::setResourceScope( ResourceScopePtr resourceScope ) { mResourceScope = resourceScope ? std::move( resourceScope ) : ResourceScope::New(); + if ( mImportDefaultResources ) + mResourceScope->importCatalog( defaultResourceScope().getLocalCatalog() ); mUIThemeManager->setResourceScope( mResourceScope ); return this; } @@ -1455,7 +1461,7 @@ UIIcon* UISceneNode::findIcon( const std::string& iconName ) { } DrawablePtr UISceneNode::findIconDrawable( const std::string& iconName, - const size_t& drawableSize ) { + const size_t& drawableSize ) { UIIcon* icon = findIcon( iconName ); return icon ? icon->createDrawable( drawableSize ) : DrawablePtr{}; } @@ -1530,7 +1536,7 @@ void UISceneNode::loadGlyphIcon( const StyleSheetStyleVector& styles ) { CSS::StyleSheetProperty glyphProp( *glyph ); if ( !familyProp.isEmpty() && !nameProp.isEmpty() && !glyphProp.isEmpty() ) { - Font* fontSearch = FontManager::instance()->getByName( familyProp.getValue() ); + Font* fontSearch = mResourceScope->findFont( familyProp.getValue() ).get(); if ( nullptr == fontSearch ) continue; @@ -1675,11 +1681,11 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR fontStyle, static_cast( fontWeight ) ); }; auto registerLoadedFont = [this, authorFamily, fontStyle, - fontWeight]( FontTrueType* font ) { + fontWeight]( FontTrueTypePtr font ) { if ( font == nullptr || !font->loaded() ) return false; font->setVariableFontWeight( fontWeight ); - registerFontFaceAlias( authorFamily, fontStyle, fontWeight, font ); + registerFontFaceAlias( authorFamily, fontStyle, fontWeight, font.get() ); mFontFaces.push_back( font ); mRoot->reloadFontFamily(); return true; @@ -1706,12 +1712,13 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR if ( isBase64 && !data.empty() ) { std::string decoded; Base64::decode( data, decoded ); - FontTrueType* font = FontTrueType::New( - makeInternalFontName( authorFamily, fontStyle, fontWeight ) ); + FontTrueTypePtr font = FontTrueType::New( + makeInternalFontName( authorFamily, fontStyle, fontWeight ), + *mResourceScope ); if ( font->loadFromMemory( &decoded[0], decoded.size() ) ) { registerLoadedFont( font ); } else - eeSAFE_DELETE( font ); + mResourceScope->eraseLocalFont( font.get() ); } } return; @@ -1723,14 +1730,14 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR if ( String::startsWith( path, "file://" ) ) { std::string filePath( resolvedURI.getFSPath() ); - FontTrueType* font = - FontTrueType::New( makeInternalFontName( authorFamily, fontStyle, fontWeight ) ); + FontTrueTypePtr font = FontTrueType::New( + makeInternalFontName( authorFamily, fontStyle, fontWeight ), *mResourceScope ); if ( font->loadFromFile( filePath ) ) { registerLoadedFont( font ); runOnMainThread( [this] { mRoot->reloadFontFamily(); } ); } else - eeSAFE_DELETE( font ); + mResourceScope->eraseLocalFont( font.get() ); } else if ( String::startsWith( path, "http://" ) || String::startsWith( path, "https://" ) ) { std::string internalFontName( @@ -1755,17 +1762,18 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR resourceState, resourceGeneration, [fontData = std::move( fontData ), internalFontName, authorFamily, fontStyle, fontWeight]( UISceneNode* scene ) mutable { - FontTrueType* font = FontTrueType::New( internalFontName ); + FontTrueTypePtr font = + FontTrueType::New( internalFontName, *scene->mResourceScope ); if ( font->loadFromMemory( &fontData[0], fontData.size() ) && font->loaded() ) { font->setVariableFontWeight( fontWeight ); scene->registerFontFaceAlias( authorFamily, fontStyle, fontWeight, - font ); + font.get() ); scene->mFontFaces.push_back( font ); if ( scene->mRoot ) scene->mRoot->reloadFontFamily(); } else { - eeSAFE_DELETE( font ); + scene->mResourceScope->eraseLocalFont( font.get() ); } } ); } else { @@ -1781,14 +1789,14 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR } } ); } else if ( VFS::instance()->fileExists( path ) ) { - FontTrueType* font = - FontTrueType::New( makeInternalFontName( authorFamily, fontStyle, fontWeight ) ); + FontTrueTypePtr font = FontTrueType::New( + makeInternalFontName( authorFamily, fontStyle, fontWeight ), *mResourceScope ); IOStream* stream = VFS::instance()->getFileFromPath( path ); if ( font->loadFromStream( *stream ) ) { registerLoadedFont( font ); } else - eeSAFE_DELETE( font ); + mResourceScope->eraseLocalFont( font.get() ); } }; @@ -2204,7 +2212,6 @@ void UISceneNode::invalidate( Node* invalidator ) { Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyle, FontWeight weight ) const { - FontManager* fm = FontManager::instance(); Font* font = nullptr; String::readBySeparatorStoppable( names, @@ -2222,7 +2229,7 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl if ( fontStyle ) fontFamily += "#" + Text::styleFlagToString( fontStyle ); - font = fm->getByName( fontFamily ); + font = mResourceScope->findFont( fontFamily ).get(); // Remove the font style part (ex: `Arial#bold` to `Arial`) // We need this for SystemFontResolver::genericFamilyFromName @@ -2243,7 +2250,7 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl if ( fontStyle ) fontFamily += "#" + Text::styleFlagToString( fontStyle ); - font = fm->getByName( fontFamily ); + font = mResourceScope->findFont( fontFamily ).get(); } if ( font == nullptr && SystemFontResolver::isEnabled() ) { @@ -2258,15 +2265,16 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl if ( fontStyle ) family += "#" + Text::styleFlagToString( fontStyle ); - if ( ( font = fm->getByName( family ) ) ) + if ( ( font = mResourceScope->findFont( family ).get() ) ) return true; - FontTrueType* ttf = FontTrueType::New( family, desc.path, desc.faceIndex ); + FontTrueTypePtr ttf = + FontTrueType::New( family, desc.path, desc.faceIndex, *mResourceScope ); if ( ttf && ttf->loaded() ) { - font = ttf; + font = ttf.get(); Uint32 weightStyle = fontStyle & ( Text::Bold | Text::Italic ); if ( weightStyle ) { - Font* regular = fm->getByName( desc.family ); + Font* regular = mResourceScope->findFont( desc.family ).get(); if ( regular && regular != font && regular->getType() == FontType::TTF ) { auto* regularFT = static_cast( regular ); @@ -2309,8 +2317,8 @@ void UISceneNode::clearFontFaces() { if ( mRoot ) mRoot->reloadFontFamily(); - for ( auto& font : mFontFaces ) - FontManager::instance()->remove( font ); + for ( const FontPtr& font : mFontFaces ) + mResourceScope->eraseLocalFont( font.get() ); mFontFaces.clear(); } @@ -2348,7 +2356,7 @@ void UISceneNode::loadFontStyleVariants( Font* font, const std::string& family ) return; auto* ft = static_cast( font ); - auto loadVariant = [family]( FontWeight weight, bool italic ) -> FontTrueType* { + auto loadVariant = [this, family]( FontWeight weight, bool italic ) -> FontTrueTypePtr { Uint32 style = 0; if ( italic ) style |= Text::Italic; @@ -2357,9 +2365,10 @@ void UISceneNode::loadFontStyleVariants( Font* font, const std::string& family ) std::string queryFamily = family; if ( style ) queryFamily += "#" + Text::styleFlagToString( style ); - Font* existing = FontManager::instance()->getByName( queryFamily ); + FontPtr existingHandle = mResourceScope->findFont( queryFamily ); + Font* existing = existingHandle.get(); if ( existing && existing->getType() == FontType::TTF ) - return static_cast( existing ); + return std::static_pointer_cast( existingHandle ); FontDesc desc = SystemFontResolver::instance()->resolveGeneric( SystemFontResolver::genericFamilyFromName( family ), weight, italic ); @@ -2373,23 +2382,24 @@ void UISceneNode::loadFontStyleVariants( Font* font, const std::string& family ) if ( desc.path.empty() ) return nullptr; - auto* ttf = FontTrueType::New( queryFamily, desc.path, desc.faceIndex ); + FontTrueTypePtr ttf = + FontTrueType::New( queryFamily, desc.path, desc.faceIndex, *mResourceScope ); if ( !ttf || !ttf->loaded() ) { - eeSAFE_DELETE( ttf ); + mResourceScope->eraseLocalFont( ttf.get() ); return nullptr; } return ttf; }; - FontTrueType* boldFont = loadVariant( FontWeight::Bold, false ); + FontTrueTypePtr boldFont = loadVariant( FontWeight::Bold, false ); if ( boldFont ) ft->setBoldFont( boldFont ); - FontTrueType* italicFont = loadVariant( FontWeight::Normal, true ); + FontTrueTypePtr italicFont = loadVariant( FontWeight::Normal, true ); if ( italicFont ) ft->setItalicFont( italicFont ); - FontTrueType* boldItalicFont = loadVariant( FontWeight::Bold, true ); + FontTrueTypePtr boldItalicFont = loadVariant( FontWeight::Bold, true ); if ( boldItalicFont ) ft->setBoldItalicFont( boldItalicFont ); } diff --git a/src/eepp/ui/uistyle.cpp b/src/eepp/ui/uistyle.cpp index 0fed69841..4f2500914 100644 --- a/src/eepp/ui/uistyle.cpp +++ b/src/eepp/ui/uistyle.cpp @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/src/eepp/ui/uitabwidget.cpp b/src/eepp/ui/uitabwidget.cpp index 35a4745c4..3ea06fde8 100644 --- a/src/eepp/ui/uitabwidget.cpp +++ b/src/eepp/ui/uitabwidget.cpp @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/src/eepp/ui/uitextinput.cpp b/src/eepp/ui/uitextinput.cpp index 8d103c349..5f4167422 100644 --- a/src/eepp/ui/uitextinput.cpp +++ b/src/eepp/ui/uitextinput.cpp @@ -1,8 +1,8 @@ #include -#include #include #include #include +#include #include #include #include @@ -649,7 +649,10 @@ bool UITextInput::applyProperty( const StyleSheetProperty& attribute ) { setHintFontSize( lengthFromValue( attribute ) ); break; case PropertyId::HintFontFamily: - setHintFont( FontManager::instance()->getByName( attribute.value() ) ); + setHintFont( + getUISceneNode() + ? getUISceneNode()->getResourceScope()->findFont( attribute.value() ).get() + : nullptr ); break; case PropertyId::HintFontStyle: setHintFontStyle( attribute.asFontStyle() ); diff --git a/src/eepp/ui/uitextspan.cpp b/src/eepp/ui/uitextspan.cpp index ececcd91a..af244ef38 100644 --- a/src/eepp/ui/uitextspan.cpp +++ b/src/eepp/ui/uitextspan.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/eepp/ui/uitextview.cpp b/src/eepp/ui/uitextview.cpp index 09e020015..72e8597a4 100644 --- a/src/eepp/ui/uitextview.cpp +++ b/src/eepp/ui/uitextview.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/eepp/ui/uitooltip.cpp b/src/eepp/ui/uitooltip.cpp index 9d1aa5b18..f852ac766 100644 --- a/src/eepp/ui/uitooltip.cpp +++ b/src/eepp/ui/uitooltip.cpp @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/src/eepp/window/backend/SDL2/windowsdl2.cpp b/src/eepp/window/backend/SDL2/windowsdl2.cpp index 086483b25..bf36e2587 100644 --- a/src/eepp/window/backend/SDL2/windowsdl2.cpp +++ b/src/eepp/window/backend/SDL2/windowsdl2.cpp @@ -2,12 +2,10 @@ #ifdef EE_BACKEND_SDL2 -#include #include #include #include #include -#include #include #include #include diff --git a/src/eepp/window/backend/SDL3/windowsdl3.cpp b/src/eepp/window/backend/SDL3/windowsdl3.cpp index 180e458c9..7866d11fb 100644 --- a/src/eepp/window/backend/SDL3/windowsdl3.cpp +++ b/src/eepp/window/backend/SDL3/windowsdl3.cpp @@ -2,12 +2,10 @@ #ifdef EE_BACKEND_SDL3 -#include #include #include #include #include -#include #include #include #include diff --git a/src/eepp/window/engine.cpp b/src/eepp/window/engine.cpp index 4a8956ad1..2701144fb 100644 --- a/src/eepp/window/engine.cpp +++ b/src/eepp/window/engine.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -105,8 +104,6 @@ Engine::~Engine() { Doc::SyntaxDefinitionManager::destroySingleton(); - FontManager::destroySingleton(); - Graphics::Private::FrameBufferManager::destroySingleton(); Graphics::Private::VertexBufferManager::destroySingleton(); diff --git a/src/examples/fonts/fonts.cpp b/src/examples/fonts/fonts.cpp index 4858da0ab..3b4963097 100644 --- a/src/examples/fonts/fonts.cpp +++ b/src/examples/fonts/fonts.cpp @@ -25,23 +25,24 @@ EE_MAIN_FUNC int main( int, char*[] ) { "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " "proident, sunt in culpa qui officia deserunt mollit anim id est laborum." ); - FontTrueType* fontTest = FontTrueType::New( "DejaVuSansMono" ); + FontTrueType* fontTest = FontTrueType::New( "DejaVuSansMono" ).get(); fontTest->loadFromFile( "assets/fonts/DejaVuSansMono.ttf" ); - FontTrueType* fontTest2 = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* fontTest2 = FontTrueType::New( "NotoSans-Regular" ).get(); fontTest2->loadFromFile( "assets/fonts/NotoSans-Regular.ttf" ); - FontTrueType* fontEmoji = FontTrueType::New( "NotoEmoji-Regular" ); + FontTrueType* fontEmoji = FontTrueType::New( "NotoEmoji-Regular" ).get(); fontEmoji->loadFromFile( "assets/fonts/NotoEmoji-Regular.ttf" ); - FontTrueType* fontEmojiColor = FontTrueType::New( "NotoColorEmoji" ); + FontTrueType* fontEmojiColor = FontTrueType::New( "NotoColorEmoji" ).get(); fontEmojiColor->loadFromFile( "assets/fonts/NotoColorEmoji.ttf" ); - FontBMFont* fontBMFont = FontBMFont::New( "bmfont" ); + FontBMFont* fontBMFont = FontBMFont::New( "bmfont" ).get(); fontBMFont->loadFromFile( "assets/fonts/bmfont.fnt" ); - FontSprite* fontSprite = FontSprite::New( - "alagard" ); // Alagard - Hewett Tsoi ( https://www.dafont.com/alagard.font ) + FontSprite* fontSprite = + FontSprite::New( "alagard" ) + .get(); // Alagard - Hewett Tsoi ( https://www.dafont.com/alagard.font ) fontSprite->loadFromFile( "assets/fonts/custom_alagard.png", Color::Fuchsia, 32, -4 ); Text text; diff --git a/src/examples/richtext/richtext.cpp b/src/examples/richtext/richtext.cpp index ccc0852c4..98dedec42 100644 --- a/src/examples/richtext/richtext.cpp +++ b/src/examples/richtext/richtext.cpp @@ -9,7 +9,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = - FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); + FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ).get(); if ( !font || !font->loaded() ) return EXIT_FAILURE; diff --git a/src/examples/ui_font_picker/ui_font_picker.cpp b/src/examples/ui_font_picker/ui_font_picker.cpp index 112343102..1715349a0 100644 --- a/src/examples/ui_font_picker/ui_font_picker.cpp +++ b/src/examples/ui_font_picker/ui_font_picker.cpp @@ -10,10 +10,15 @@ EE_MAIN_FUNC int main( int, char** ) { return EXIT_FAILURE; app.getUI()->setThreadPool( threadPool ); + ResourceScope& resourceScope = *app.getUI()->getResourceScope(); + FontTrueTypePtr remixIconFont = + FontTrueType::New( "icon", "assets/fonts/remixicon.ttf", resourceScope ); + FontTrueTypePtr noniconsFont = + FontTrueType::New( "nonicons", "assets/fonts/nonicons.ttf", resourceScope ); + FontTrueTypePtr codIconFont = + FontTrueType::New( "codicon", "assets/fonts/codicon.ttf", resourceScope ); app.getUI()->getUIIconThemeManager()->setCurrentTheme( - IconManager::init( "icons", FontTrueType::New( "icon", "assets/fonts/remixicon.ttf" ), - FontTrueType::New( "nonicons", "assets/fonts/nonicons.ttf" ), - FontTrueType::New( "codicon", "assets/fonts/codicon.ttf" ) ) ); + IconManager::init( "icons", remixIconFont.get(), noniconsFont.get(), codIconFont.get() ) ); UIFontPickerDialog* dialog = UIFontPickerDialog::New(); dialog->setCloseShortcut( KEY_ESCAPE ); diff --git a/src/examples/ui_hello_world/ui_hello_world.cpp b/src/examples/ui_hello_world/ui_hello_world.cpp index 37d40b4cd..a1e030a00 100644 --- a/src/examples/ui_hello_world/ui_hello_world.cpp +++ b/src/examples/ui_hello_world/ui_hello_world.cpp @@ -13,13 +13,12 @@ EE_MAIN_FUNC int main( int, char** ) { // is always correct even if we load the application from other directory than the binary // path. FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - - // Load a font to use as the default font in our UI. - FontTrueType* font = - FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); - // Create a new scene node to add our widgets. UISceneNode* uiSceneNode = UISceneNode::New(); + // Load a font to use as the default font in our UI. + FontTrueType* font = FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf", + *uiSceneNode->getResourceScope() ) + .get(); // Set the default font used in the scene node (otherwise we won't have any font to create // text views. diff --git a/src/examples/ui_html/ui_html.cpp b/src/examples/ui_html/ui_html.cpp index 9b5a635fb..a33d8319d 100644 --- a/src/examples/ui_html/ui_html.cpp +++ b/src/examples/ui_html/ui_html.cpp @@ -63,11 +63,15 @@ EE_MAIN_FUNC int main( int argc, char** argv ) { auto ui = app.getUI(); ui->setThreadPool( threadPool ); - FontTrueType* remixIconFont = FontTrueType::New( "icon", "assets/fonts/remixicon.ttf" ); - FontTrueType* noniconsFont = FontTrueType::New( "nonicons", "assets/fonts/nonicons.ttf" ); - FontTrueType* codIconFont = FontTrueType::New( "codicon", "assets/fonts/codicon.ttf" ); + ResourceScope& resourceScope = *ui->getResourceScope(); + FontTrueTypePtr remixIconFont = + FontTrueType::New( "icon", "assets/fonts/remixicon.ttf", resourceScope ); + FontTrueTypePtr noniconsFont = + FontTrueType::New( "nonicons", "assets/fonts/nonicons.ttf", resourceScope ); + FontTrueTypePtr codIconFont = + FontTrueType::New( "codicon", "assets/fonts/codicon.ttf", resourceScope ); ui->getUIIconThemeManager()->setCurrentTheme( - IconManager::init( "icons", remixIconFont, noniconsFont, codIconFont ) ); + IconManager::init( "icons", remixIconFont.get(), noniconsFont.get(), codIconFont.get() ) ); ui->setColorSchemePreference( !prefersColorScheme.Get().empty() diff --git a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp index 0337a902d..88558790d 100644 --- a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -1391,7 +1391,12 @@ void TerminalDisplay::drawGrid( const Vector2f& pos ) { auto* gd = mFont->getGlyphDrawable( glyph.u, mFontSize, glyph.mode & ATTR_BOLD, glyph.mode & ATTR_ITALIC, 0 ); - if ( ( glyph.mode & ATTR_EMOJI ) && FontManager::instance()->getColorEmojiFont() ) { + FontService* fontService = + mFont->getType() == FontType::TTF + ? static_cast( mFont )->getFontService() + : nullptr; + if ( ( glyph.mode & ATTR_EMOJI ) && fontService && + fontService->getColorEmojiFont() ) { gd->setColor( Color::White ); } else { gd->setColor( fg ); diff --git a/src/tests/test_all/test.cpp b/src/tests/test_all/test.cpp index eda2b1474..2fd731771 100644 --- a/src/tests/test_all/test.cpp +++ b/src/tests/test_all/test.cpp @@ -256,8 +256,8 @@ void EETest::loadFonts() { } void EETest::onFontLoaded() { - TTF = FontManager::instance()->getByName( "NotoSans-Regular" ); - Font* monospace = FontManager::instance()->getByName( "monospace" ); + TTF = defaultResourceScope().findFont( "NotoSans-Regular" ).get(); + Font* monospace = defaultResourceScope().findFont( "monospace" ).get(); Log::info( "Fonts loading time: %4.3f ms.", mFTE.getElapsedTimeAndReset().asMilliseconds() ); diff --git a/src/tests/ui_perf_test/ui_perf_test.cpp b/src/tests/ui_perf_test/ui_perf_test.cpp index 8790d7d93..870fc0b79 100644 --- a/src/tests/ui_perf_test/ui_perf_test.cpp +++ b/src/tests/ui_perf_test/ui_perf_test.cpp @@ -168,13 +168,13 @@ int testTextRendering() { editor->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); editor->setParent( ll ); // editor->setFontSize( PixelDensity::dpToPx( 12 ) ); - FontManager::instance()->addFallbackFont( + defaultResourceScope().getFontService().addFallbackFont( FontTrueType::New( "arabic", "unit_tests/assets/fonts/NotoNaskhArabic-Regular.ttf" ) ); - FontManager::instance()->addFallbackFont( FontTrueType::New( + defaultResourceScope().getFontService().addFallbackFont( FontTrueType::New( "NotoSerifBengali-Regular", "unit_tests/assets/fonts/NotoSansBengali-Regular.ttf" ) ); editor->setWordWrap( true ); // editor->setLineWrapMode( LineWrapMode::Word ); - // editor->setFont( FontManager::instance()->getByName( "monospace" ) ); + // editor->setFont( defaultResourceScope().findFont( "monospace" ).get() ); // editor->loadFromFile( "unit_tests/assets/textfiles/test-arabic-simple.uext" ); // editor->loadFromFile( "unit_tests/assets/textfiles/test-arabic.uext" ); // editor->loadFromFile( "unit_tests/assets/textfiles/test-bengali.uext" ); @@ -212,8 +212,8 @@ EE_MAIN_FUNC int main( int, char*[] ) { PixelDensity::setPixelDensity( Engine::instance()->getDisplayManager()->getDisplayIndex( 0 )->getPixelDensity() ); FontTrueType* font = - FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); - FontTrueType* iconFont = FontTrueType::New( "icon", "assets/fonts/remixicon.ttf" ); + FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ).get(); + FontTrueType* iconFont = FontTrueType::New( "icon", "assets/fonts/remixicon.ttf" ).get(); UIIconTheme* iconTheme = UIIconTheme::New( "remixicon" ); auto addIcon = [iconTheme, iconFont]( const std::string& name, const Uint32& codePoint ) -> UIIcon* { diff --git a/src/tests/unit_tests/drawableimageparser_tests.cpp b/src/tests/unit_tests/drawableimageparser_tests.cpp index 7416bbf5b..720aed65d 100644 --- a/src/tests/unit_tests/drawableimageparser_tests.cpp +++ b/src/tests/unit_tests/drawableimageparser_tests.cpp @@ -25,7 +25,7 @@ using namespace EE::UI::CSS; static UISceneNode* initDrawableParserTest() { FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); @@ -54,7 +54,7 @@ UTEST( DrawableImageParser, TwoStopsWithPosition ) { auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); DrawablePtr drawable = parser.createDrawable( "linear-gradient(#f5eedd 0%, #ebe0c2 100%)", - Sizef( 100, 100 ), node ); + Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); ASSERT_EQ( drawable->getDrawableType(), Drawable::LINEARGRADIENT ); @@ -82,8 +82,8 @@ UTEST( DrawableImageParser, DirectionToRight ) { UINode* node = createTestNode( sceneNode ); auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); - DrawablePtr drawable = parser.createDrawable( "linear-gradient(to right, red, blue)", - Sizef( 100, 100 ), node ); + DrawablePtr drawable = + parser.createDrawable( "linear-gradient(to right, red, blue)", Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -105,7 +105,7 @@ UTEST( DrawableImageParser, AngleDegrees ) { auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); DrawablePtr drawable = parser.createDrawable( "linear-gradient(45deg, red 0%, blue 100%)", - Sizef( 100, 100 ), node ); + Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -124,8 +124,8 @@ UTEST( DrawableImageParser, AngleTurn ) { UINode* node = createTestNode( sceneNode ); auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); - DrawablePtr drawable = parser.createDrawable( "linear-gradient(0.25turn, red, blue)", - Sizef( 100, 100 ), node ); + DrawablePtr drawable = + parser.createDrawable( "linear-gradient(0.25turn, red, blue)", Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -145,7 +145,7 @@ UTEST( DrawableImageParser, ThreeStops ) { auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); DrawablePtr drawable = parser.createDrawable( "linear-gradient(red 0%, green 50%, blue 100%)", - Sizef( 100, 100 ), node ); + Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -168,8 +168,8 @@ UTEST( DrawableImageParser, ColorHint ) { UINode* node = createTestNode( sceneNode ); auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); - DrawablePtr drawable = parser.createDrawable( "linear-gradient(red 0%, 25%, blue 100%)", - Sizef( 100, 100 ), node ); + DrawablePtr drawable = + parser.createDrawable( "linear-gradient(red 0%, 25%, blue 100%)", Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -209,8 +209,8 @@ UTEST( DrawableImageParser, StopsWithoutPositions ) { UINode* node = createTestNode( sceneNode ); auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); - DrawablePtr drawable = parser.createDrawable( "linear-gradient(red, green, blue)", - Sizef( 100, 100 ), node ); + DrawablePtr drawable = + parser.createDrawable( "linear-gradient(red, green, blue)", Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -254,8 +254,8 @@ UTEST( DrawableImageParser, RepeatingTwoStops ) { UINode* node = createTestNode( sceneNode ); auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); - DrawablePtr drawable = parser.createDrawable( "repeating-linear-gradient(red, blue)", - Sizef( 100, 100 ), node ); + DrawablePtr drawable = + parser.createDrawable( "repeating-linear-gradient(red, blue)", Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); ASSERT_TRUE( drawable->getDrawableType() == Drawable::REPEATINGLINEARGRADIENT ); @@ -301,7 +301,7 @@ UTEST( DrawableImageParser, RepeatingWithPositions ) { auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); DrawablePtr drawable = parser.createDrawable( "repeating-linear-gradient(red 10%, blue 40%)", - Sizef( 100, 100 ), node ); + Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -347,8 +347,8 @@ UTEST( DrawableImageParser, RadialCircleKeyword ) { UINode* node = createTestNode( sceneNode ); auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); - DrawablePtr drawable = parser.createDrawable( "radial-gradient(circle, #f00, #00f)", - Sizef( 100, 100 ), node ); + DrawablePtr drawable = + parser.createDrawable( "radial-gradient(circle, #f00, #00f)", Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -367,8 +367,8 @@ UTEST( DrawableImageParser, RadialWithPositions ) { UINode* node = createTestNode( sceneNode ); auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); - DrawablePtr drawable = parser.createDrawable( "radial-gradient(red 10%, blue 80%)", - Sizef( 100, 100 ), node ); + DrawablePtr drawable = + parser.createDrawable( "radial-gradient(red 10%, blue 80%)", Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); auto* grad = static_cast( drawable.get() ); @@ -390,7 +390,7 @@ UTEST( DrawableImageParser, RepeatingRadial ) { auto& parser = StyleSheetSpecification::instance()->getDrawableImageParser(); DrawablePtr drawable = parser.createDrawable( "repeating-radial-gradient(red 10%, blue 40%)", - Sizef( 100, 100 ), node ); + Sizef( 100, 100 ), node ); ASSERT_TRUE( drawable != NULL ); ASSERT_TRUE( drawable->getDrawableType() == Drawable::REPEATINGRADIALGRADIENT ); diff --git a/src/tests/unit_tests/fontrendering_tests.cpp b/src/tests/unit_tests/fontrendering_tests.cpp index eea02bcaf..b5a4053c8 100644 --- a/src/tests/unit_tests/fontrendering_tests.cpp +++ b/src/tests/unit_tests/fontrendering_tests.cpp @@ -4,15 +4,16 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include @@ -35,34 +36,127 @@ using namespace EE::Window; using namespace EE::UI; using namespace EE::UI::CSS; -UTEST( FontRendering, relatedFontsDisconnectReplacedCallbacks ) { - FontTrueType* font = FontTrueType::New( "RelatedFontsDisconnect-Regular" ); - FontTrueType* oldBold = FontTrueType::New( "RelatedFontsDisconnect-OldBold" ); - FontTrueType* newBold = FontTrueType::New( "RelatedFontsDisconnect-NewBold" ); - FontTrueType* oldItalic = FontTrueType::New( "RelatedFontsDisconnect-OldItalic" ); - FontTrueType* newItalic = FontTrueType::New( "RelatedFontsDisconnect-NewItalic" ); - FontTrueType* oldBoldItalic = FontTrueType::New( "RelatedFontsDisconnect-OldBoldItalic" ); - FontTrueType* newBoldItalic = FontTrueType::New( "RelatedFontsDisconnect-NewBoldItalic" ); +UTEST( FontRendering, drawingEmptyTextDoesNotCreateFontPage ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - Empty Text Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); - font->setBoldFont( oldBold ); - font->setBoldFont( newBold ); - eeDelete( oldBold ); - EXPECT_EQ( newBold, font->getBoldFont() ); + FontDesc desc; + desc.family = "Empty Text Test"; + desc.path = Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf"; + FontTrueTypePtr font = app.getUI()->getResourceScope()->getFontService().loadSystemFont( desc ); + ASSERT_TRUE( font ); - font->setItalicFont( oldItalic ); - font->setItalicFont( newItalic ); - eeDelete( oldItalic ); - EXPECT_EQ( newItalic, font->getItalicFont() ); + TextureFactory* textureFactory = TextureFactory::instance(); + const Uint32 textureCount = textureFactory->getTextureCount(); + Text::draw( String{}, Vector2f::Zero, font.get(), 10, Color::White ); - font->setBoldItalicFont( oldBoldItalic ); - font->setBoldItalicFont( newBoldItalic ); - eeDelete( oldBoldItalic ); - EXPECT_EQ( newBoldItalic, font->getBoldItalicFont() ); + EXPECT_EQ( textureCount, textureFactory->getTextureCount() ); +} - eeDelete( font ); - eeDelete( newBold ); - eeDelete( newItalic ); - eeDelete( newBoldItalic ); +UTEST( FontRendering, glyphAdvanceDoesNotCreateTexturePages ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - Glyph Advance Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); + ResourceScope& scope = *app.getUI()->getResourceScope(); + FontTrueTypePtr font = FontTrueType::New( "GlyphAdvance-Regular", scope ); + ASSERT_TRUE( + font->loadFromFile( Sys::getProcessPath() + "../assets/fonts/NotoSans-Regular.ttf" ) ); + + TextureFactory* textureFactory = TextureFactory::instance(); + const Uint32 textureCount = textureFactory->getTextureCount(); + const Float advance = font->getGlyphAdvance( ' ', 10 ); + const Float outlinedAdvance = font->getGlyphAdvance( ' ', 10, false, false, 2.f ); + EXPECT_TRUE( advance > 0 ); + EXPECT_EQ( advance, outlinedAdvance ); + EXPECT_EQ( textureCount, textureFactory->getTextureCount() ); +} + +UTEST( FontRendering, loadingFontFamilyDoesNotCreateTexturePages ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - Font Family Metrics Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); + ResourceScope& scope = *app.getUI()->getResourceScope(); + FontTrueTypePtr font = FontTrueType::New( "FontFamilyMetrics-Regular", scope ); + ASSERT_TRUE( + font->loadFromFile( Sys::getProcessPath() + "../assets/fonts/NotoSans-Regular.ttf" ) ); + + TextureFactory* textureFactory = TextureFactory::instance(); + const Uint32 textureCount = textureFactory->getTextureCount(); + + FontFamily::loadFromRegular( font.get() ); + EXPECT_TRUE( font->hasBold() ); + EXPECT_TRUE( font->hasItalic() ); + EXPECT_EQ( textureCount, textureFactory->getTextureCount() ); +} + +UTEST( FontRendering, regularFontOwnsRelatedFonts ) { + FontTrueTypePtr font = FontTrueType::New( "RelatedFonts-Regular" ); + FontTrueTypePtr bold = FontTrueType::New( "RelatedFonts-Bold" ); + FontTrueTypePtr italic = FontTrueType::New( "RelatedFonts-Italic" ); + FontTrueTypePtr boldItalic = FontTrueType::New( "RelatedFonts-BoldItalic" ); + FontTrueTypeWeakPtr weakBold = bold; + FontTrueTypeWeakPtr weakItalic = italic; + FontTrueTypeWeakPtr weakBoldItalic = boldItalic; + + font->setBoldFont( bold ); + font->setItalicFont( italic ); + font->setBoldItalicFont( boldItalic ); + + defaultResourceScope().eraseLocalFont( bold.get() ); + defaultResourceScope().eraseLocalFont( italic.get() ); + defaultResourceScope().eraseLocalFont( boldItalic.get() ); + bold.reset(); + italic.reset(); + boldItalic.reset(); + + EXPECT_TRUE( font->getBoldFont() ); + EXPECT_TRUE( font->getItalicFont() ); + EXPECT_TRUE( font->getBoldItalicFont() ); + EXPECT_FALSE( weakBold.expired() ); + EXPECT_FALSE( weakItalic.expired() ); + EXPECT_FALSE( weakBoldItalic.expired() ); + + defaultResourceScope().eraseLocalFont( font.get() ); + font.reset(); + + EXPECT_TRUE( weakBold.expired() ); + EXPECT_TRUE( weakItalic.expired() ); + EXPECT_TRUE( weakBoldItalic.expired() ); +} + +UTEST( FontRendering, destroyingFontInvalidatesTextLayoutCache ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - Text Layout Cache Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); + ResourceScope& scope = *app.getUI()->getResourceScope(); + FontTrueTypePtr font = FontTrueType::New( "TextLayoutCache-Regular", scope ); + FontTrueTypePtr retainedFont = FontTrueType::New( "TextLayoutCache-Retained", scope ); + ASSERT_TRUE( + font->loadFromFile( Sys::getProcessPath() + "../assets/fonts/NotoSans-Regular.ttf" ) ); + ASSERT_TRUE( retainedFont->loadFromFile( Sys::getProcessPath() + + "../assets/fonts/NotoSans-Regular.ttf" ) ); + + TextLayout::Cache layout = + TextLayout::layout( String( "cached shaped text" ), font.get(), 14, Text::Regular ); + TextLayout::Cache retainedLayout = TextLayout::layout( String( "unrelated cached text" ), + retainedFont.get(), 14, Text::Regular ); + std::weak_ptr layoutWeak = layout; + std::weak_ptr retainedLayoutWeak = retainedLayout; + layout.reset(); + retainedLayout.reset(); + EXPECT_FALSE( layoutWeak.expired() ); + EXPECT_FALSE( retainedLayoutWeak.expired() ); + + EXPECT_TRUE( scope.eraseLocalFont( font.get() ) ); + font.reset(); + + EXPECT_TRUE( layoutWeak.expired() ); + EXPECT_FALSE( retainedLayoutWeak.expired() ); } UTEST( FontRendering, fontsTest ) { @@ -85,22 +179,22 @@ UTEST( FontRendering, fontsTest ) { "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " "proident, sunt in culpa qui officia deserunt mollit anim id est laborum." ); - FontTrueType* fontTest = FontTrueType::New( "DejaVuSansMono" ); + FontTrueType* fontTest = FontTrueType::New( "DejaVuSansMono" ).get(); fontTest->loadFromFile( "../assets/fonts/DejaVuSansMono.ttf" ); - FontTrueType* fontTest2 = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* fontTest2 = FontTrueType::New( "NotoSans-Regular" ).get(); fontTest2->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); - FontTrueType* fontEmoji = FontTrueType::New( "NotoEmoji-Regular" ); + FontTrueType* fontEmoji = FontTrueType::New( "NotoEmoji-Regular" ).get(); fontEmoji->loadFromFile( "../assets/fonts/NotoEmoji-Regular.ttf" ); - FontTrueType* fontEmojiColor = FontTrueType::New( "NotoColorEmoji" ); + FontTrueType* fontEmojiColor = FontTrueType::New( "NotoColorEmoji" ).get(); fontEmojiColor->loadFromFile( "../assets/fonts/NotoColorEmoji.ttf" ); - FontBMFont* fontBMFont = FontBMFont::New( "bmfont" ); + FontBMFont* fontBMFont = FontBMFont::New( "bmfont" ).get(); fontBMFont->loadFromFile( "../assets/fonts/bmfont.fnt" ); - FontSprite* fontSprite = FontSprite::New( "alagard" ); + FontSprite* fontSprite = FontSprite::New( "alagard" ).get(); fontSprite->loadFromFile( "../assets/fonts/custom_alagard.png", Color::Fuchsia, 32, -4 ); Text text; @@ -288,7 +382,7 @@ UTEST( FontRendering, loadFontFaceDataURI ) { SceneManager::instance()->add( sceneNode ); UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager(); - FontTrueType* baseFont = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* baseFont = FontTrueType::New( "NotoSans-Regular" ).get(); baseFont->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); themeManager->setDefaultFont( baseFont ); @@ -315,7 +409,7 @@ UTEST( FontRendering, loadFontFaceDataURI ) { Font* loadedFont = sceneNode->getFontFromNamesList( "DataURIFont" ); ASSERT_NE( loadedFont, nullptr ); ASSERT_TRUE_MSG( loadedFont->loaded(), "Font loaded via data URI is not loaded" ); - EXPECT_EQ( nullptr, FontManager::instance()->getByName( "DataURIFont" ) ); + EXPECT_EQ( nullptr, defaultResourceScope().findFont( "DataURIFont" ).get() ); Engine::destroySingleton(); } @@ -353,7 +447,7 @@ UTEST( FontRendering, fontFaceAuthorFamilyIsSceneScoped ) { EXPECT_TRUE( fontB->loaded() ); EXPECT_NE( fontA, fontB ); EXPECT_TRUE( fontA->getName() != fontB->getName() ); - EXPECT_EQ( nullptr, FontManager::instance()->getByName( "ScopedAuthorFace" ) ); + EXPECT_EQ( nullptr, defaultResourceScope().findFont( "ScopedAuthorFace" ).get() ); Engine::destroySingleton(); } @@ -631,8 +725,9 @@ UTEST( FontRendering, textEditBengaliTest ) { UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1.5f ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* bengaliFont = - FontTrueType::New( "NotoSansBengali-Regular", "assets/fonts/NotoSansBengali-Regular.ttf" ); - FontManager::instance()->addFallbackFont( bengaliFont ); + FontTrueType::New( "NotoSansBengali-Regular", "assets/fonts/NotoSansBengali-Regular.ttf" ) + .get(); + defaultResourceScope().getFontService().addFallbackFont( bengaliFont ); UTEST_PRINT_STEP( "Text Shaper enabled" ); auto* editor = UITextEdit::New(); // editor->setFontSize( PixelDensity::dpToPx( 12 ) ); @@ -651,8 +746,9 @@ UTEST( FontRendering, textEditArabicTest ) { UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1.5f ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* arabicFont = - FontTrueType::New( "NotoNaskhArabic-Regular", "assets/fonts/NotoNaskhArabic-Regular.ttf" ); - FontManager::instance()->addFallbackFont( arabicFont ); + FontTrueType::New( "NotoNaskhArabic-Regular", "assets/fonts/NotoNaskhArabic-Regular.ttf" ) + .get(); + defaultResourceScope().getFontService().addFallbackFont( arabicFont ); UTEST_PRINT_STEP( "Text Shaper enabled" ); auto* editor = UITextEdit::New(); // editor->setFontSize( PixelDensity::dpToPx( 12 ) ); @@ -671,8 +767,9 @@ UTEST( FontRendering, textEditHebrewTest ) { UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1.5f ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* hebrewFont = - FontTrueType::New( "NotoSansHebrew-Regular", "assets/fonts/NotoSansHebrew-Regular.ttf" ); - FontManager::instance()->addFallbackFont( hebrewFont ); + FontTrueType::New( "NotoSansHebrew-Regular", "assets/fonts/NotoSansHebrew-Regular.ttf" ) + .get(); + defaultResourceScope().getFontService().addFallbackFont( hebrewFont ); UTEST_PRINT_STEP( "Text Shaper enabled" ); auto* editor = UITextEdit::New(); // editor->setFontSize( PixelDensity::dpToPx( 12 ) ); @@ -692,7 +789,7 @@ UTEST( FontRendering, textSizes ) { Text::TextShaperEnabled = false; - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontStyleConfig config; @@ -793,7 +890,7 @@ UTEST( FontRendering, textStyles ) { Text::TextShaperEnabled = false; - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); @@ -876,11 +973,11 @@ UTEST( FontRendering, emojisWithText ) { Text::TextShaperEnabled = false; - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); - FontTrueType* fontEmojiColor = FontTrueType::New( "NotoColorEmoji" ); + FontTrueType* fontEmojiColor = FontTrueType::New( "NotoColorEmoji" ).get(); fontEmojiColor->loadFromFile( "../assets/fonts/NotoColorEmoji.ttf" ); win->setClearColor( RGB( 255, 255, 255 ) ); @@ -934,7 +1031,8 @@ UTEST( FontRendering, textSetFillColor ) { win->setClearColor( RGB( 230, 230, 230 ) ); FontTrueType* arabicFont = - FontTrueType::New( "NotoNaskhArabic-Regular", "assets/fonts/NotoNaskhArabic-Regular.ttf" ); + FontTrueType::New( "NotoNaskhArabic-Regular", "assets/fonts/NotoNaskhArabic-Regular.ttf" ) + .get(); Text text; text.setFont( arabicFont ); @@ -1364,7 +1462,7 @@ UTEST( FontRendering, TextSoftWrapPos ) { UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "DejaVuSansMono" ); + FontTrueType* font = FontTrueType::New( "DejaVuSansMono" ).get(); font->loadFromFile( "../assets/fonts/DejaVuSansMono.ttf" ); Text text; @@ -1414,7 +1512,7 @@ UTEST( FontRendering, TextSelection ) { Text::TextShaperEnabled = false; - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); bool loaded = font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( loaded ); FontFamily::loadFromRegular( font ); @@ -1501,7 +1599,7 @@ UTEST( FontRendering, TextInitialOffset ) { win->setClearColor( RGB( 255, 255, 255 ) ); win->clear(); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); Primitives p; @@ -1568,7 +1666,7 @@ UTEST( FontRendering, TextContiguousOffset ) { win->setClearColor( RGB( 255, 255, 255 ) ); win->clear(); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); Float maxWidth = 450.f; @@ -1681,7 +1779,7 @@ UTEST( FontRendering, TextBackgroundColor ) { win->setClearColor( RGB( 255, 255, 255 ) ); win->clear(); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); Vector2f pos{ 20, 20 }; diff --git a/src/tests/unit_tests/htmlsvg_tests.cpp b/src/tests/unit_tests/htmlsvg_tests.cpp index 5dde0ee3b..99d244d12 100644 --- a/src/tests/unit_tests/htmlsvg_tests.cpp +++ b/src/tests/unit_tests/htmlsvg_tests.cpp @@ -26,7 +26,7 @@ static UISceneNode* createScene() { true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); if ( !font->loaded() ) { Engine::destroySingleton(); diff --git a/src/tests/unit_tests/resource_prerequisite_tests.cpp b/src/tests/unit_tests/resource_prerequisite_tests.cpp index 30d3e315f..58187cec4 100644 --- a/src/tests/unit_tests/resource_prerequisite_tests.cpp +++ b/src/tests/unit_tests/resource_prerequisite_tests.cpp @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -199,6 +200,18 @@ EE::Window::Window* createLifecycleTestWindow( const std::string& title ) { static_assert( !std::is_copy_constructible::value, "Texture must not be copyable" ); static_assert( !std::is_copy_assignable::value, "Texture must not be copy-assignable" ); +static_assert( std::is_invocable_r_v, const ResourceId&>, + "ResourceId must support standard unordered containers" ); + +UTEST( ResourcePrerequisites, resourceIdSupportsUnorderedContainers ) { + UnorderedMap resources; + resources[ResourceId( 1 )] = 10; + resources[ResourceId( 2 )] = 20; + + EXPECT_EQ( 10, resources[ResourceId( 1 )] ); + EXPECT_EQ( 20, resources[ResourceId( 2 )] ); + EXPECT_EQ( (size_t)2, resources.size() ); +} UTEST( ResourcePrerequisites, lruCacheEvictsOnlyLeastRecentlyUsedAndReleasesKeys ) { LRUCache<2, int, bool> recencyCache; @@ -221,6 +234,21 @@ UTEST( ResourcePrerequisites, lruCacheEvictsOnlyLeastRecentlyUsedAndReleasesKeys owningKeyCache.clear(); EXPECT_TRUE( weakKey.expired() ); + + LRUCache<3, int, int> selectiveCache; + selectiveCache.put( 1, 10 ); + selectiveCache.put( 2, 20 ); + selectiveCache.put( 3, 30 ); + ASSERT_TRUE( selectiveCache.get( 1 ).has_value() ); + selectiveCache.eraseIf( []( int key, int ) { return key == 2; } ); + EXPECT_TRUE( !selectiveCache.get( 2 ).has_value() ); + EXPECT_EQ( (size_t)2, selectiveCache.size() ); + selectiveCache.put( 4, 40 ); + selectiveCache.put( 5, 50 ); + EXPECT_TRUE( !selectiveCache.get( 3 ).has_value() ); + EXPECT_TRUE( selectiveCache.get( 1 ).has_value() ); + EXPECT_TRUE( selectiveCache.get( 4 ).has_value() ); + EXPECT_TRUE( selectiveCache.get( 5 ).has_value() ); } UTEST( ResourcePrerequisites, textureAtlasLoaderAppliesFilterToEveryTexture ) { @@ -495,6 +523,92 @@ UTEST( ResourcePrerequisites, defaultResourceScopeImportsGlobalCatalog ) { Engine::destroySingleton(); } +UTEST( ResourcePrerequisites, uiScenesImportDefaultResourcesUnlessDisabled ) { + EE::Window::Window* window = createLifecycleTestWindow( "UI scene default resources test" ); + TexturePtr texture = TextureFactory::instance()->createEmptyTexture( 1, 1 ); + ASSERT_TRUE( texture != nullptr ); + defaultResourceScope().publishLocal( "ui-scene-default-texture", texture ); + + UISceneNode* defaultScene = UISceneNode::New( window ); + UISceneNode* isolatedScene = UISceneNode::New( window, false ); + EXPECT_EQ( texture.get(), + defaultScene->getResourceScope()->findTexture( "ui-scene-default-texture" ).get() ); + EXPECT_TRUE( isolatedScene->getResourceScope()->findTexture( "ui-scene-default-texture" ) == + nullptr ); + + defaultScene->setResourceScope( ResourceScope::New() ); + EXPECT_EQ( texture.get(), + defaultScene->getResourceScope()->findTexture( "ui-scene-default-texture" ).get() ); + + eeDelete( isolatedScene ); + eeDelete( defaultScene ); + defaultResourceScope().eraseLocal( "ui-scene-default-texture" ); + texture.reset(); + window->display( false ); + Engine::destroySingleton(); +} + +UTEST( ResourcePrerequisites, fontServiceFollowsScopeOwnershipAndDetachesRetainedFonts ) { + EE::Window::Window* window = createLifecycleTestWindow( "Font service scope test" ); + ResourceScopePtr scope = ResourceScope::New(); + FontTrueTypePtr font = FontTrueType::New( "scoped-font-service", *scope ); + ASSERT_TRUE( font != nullptr ); + + EXPECT_EQ( &scope->getFontService(), font->getFontService() ); + EXPECT_EQ( font.get(), scope->findFont( "scoped-font-service" ).get() ); + EXPECT_TRUE( scope->getFontService().addFallbackFont( font ) ); + EXPECT_TRUE( scope->getFontService().hasFallbackFonts() ); + + EXPECT_TRUE( scope->eraseLocalFont( font.get() ) ); + EXPECT_EQ( nullptr, font->getFontService() ); + EXPECT_FALSE( scope->getFontService().hasFallbackFonts() ); + EXPECT_TRUE( scope->findFont( "scoped-font-service" ) == nullptr ); + + font.reset(); + scope.reset(); + window->display( false ); + Engine::destroySingleton(); +} + +UTEST( ResourcePrerequisites, fontFactoriesPublishIntoExplicitScope ) { + EE::Window::Window* window = createLifecycleTestWindow( "Scoped font factories test" ); + auto scope = ResourceScope::New(); + auto bmFont = FontBMFont::New( "scoped-bm-font", *scope ); + auto spriteFont = FontSprite::New( "scoped-sprite-font", *scope ); + + EXPECT_EQ( bmFont.get(), scope->findFont( "scoped-bm-font" ).get() ); + EXPECT_EQ( spriteFont.get(), scope->findFont( "scoped-sprite-font" ).get() ); + EXPECT_EQ( nullptr, defaultResourceScope().findFont( "scoped-bm-font" ).get() ); + EXPECT_EQ( nullptr, defaultResourceScope().findFont( "scoped-sprite-font" ).get() ); + + bmFont.reset(); + spriteFont.reset(); + scope.reset(); + window->display( false ); + Engine::destroySingleton(); +} + +UTEST( ResourcePrerequisites, duplicateFontNamesReplaceCatalogBinding ) { + EE::Window::Window* window = createLifecycleTestWindow( "Font replacement semantics test" ); + auto scope = ResourceScope::New(); + FontTrueTypePtr first = FontTrueType::New( "replaceable-font", *scope ); + FontTrueTypePtr second = FontTrueType::New( "replaceable-font", *scope ); + FontTrueTypeWeakPtr firstWeak = first; + + EXPECT_STRINGEQ( "replaceable-font", first->getName() ); + EXPECT_STRINGEQ( "replaceable-font", second->getName() ); + EXPECT_EQ( second.get(), scope->findFont( "replaceable-font" ).get() ); + EXPECT_EQ( nullptr, first->getFontService() ); + EXPECT_EQ( &scope->getFontService(), second->getFontService() ); + + first.reset(); + EXPECT_TRUE( firstWeak.expired() ); + second.reset(); + scope.reset(); + window->display( false ); + Engine::destroySingleton(); +} + UTEST( ResourcePrerequisites, uiScenesOwnIsolatedScopesThatCanBeSharedExplicitly ) { EE::Window::Window* window = createLifecycleTestWindow( "UI scene resource scope test" ); UISceneNode* firstScene = UISceneNode::New( window ); @@ -1049,10 +1163,11 @@ UTEST( ResourcePrerequisites, engineTeardownReleasesGraphicsBeforeContextsAcross scene->enableFrameBuffer(); UIImage::New()->setDrawable( ninePatch->clone() )->setParent( scene->getRoot() ); - auto* font = FontTrueType::New( "engine-teardown-font" ); + FontTrueTypePtr font = FontTrueType::New( "engine-teardown-font" ); ASSERT_TRUE( font->loadFromFile( Sys::getProcessPath() + "../assets/fonts/NotoSans-Regular.ttf" ) ); - auto layout = TextLayout::layout( String( "cached before Engine teardown" ), font, 14, 0 ); + auto layout = + TextLayout::layout( String( "cached before Engine teardown" ), font.get(), 14, 0 ); std::weak_ptr layoutWeak = layout; layout.reset(); @@ -1061,6 +1176,7 @@ UTEST( ResourcePrerequisites, engineTeardownReleasesGraphicsBeforeContextsAcross batch->batchQuad( 0, 0, 4, 4 ); ninePatch.reset(); texture.reset(); + font.reset(); Engine::destroySingleton(); @@ -1068,7 +1184,6 @@ UTEST( ResourcePrerequisites, engineTeardownReleasesGraphicsBeforeContextsAcross EXPECT_TRUE( Engine::existsSingleton() == nullptr ); EXPECT_TRUE( SceneManager::existsSingleton() == nullptr ); EXPECT_TRUE( GlobalBatchRenderer::existsSingleton() == nullptr ); - EXPECT_TRUE( FontManager::existsSingleton() == nullptr ); EXPECT_TRUE( TextureFactory::existsSingleton() == nullptr ); EXPECT_TRUE( ShaderProgramManager::existsSingleton() == nullptr ); EXPECT_TRUE( Graphics::Private::FrameBufferManager::existsSingleton() == nullptr ); diff --git a/src/tests/unit_tests/richtext_tests.cpp b/src/tests/unit_tests/richtext_tests.cpp index b3f032561..fc6a7bef9 100644 --- a/src/tests/unit_tests/richtext_tests.cpp +++ b/src/tests/unit_tests/richtext_tests.cpp @@ -40,7 +40,7 @@ static UISceneNode* createRichTextScene() { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); if ( !font->loaded() ) { Engine::destroySingleton(); @@ -94,7 +94,7 @@ UTEST( RichText, basicFunctionality ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -141,7 +141,7 @@ UTEST( RichText, selection ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -222,7 +222,7 @@ UTEST( RichText, BaselineAlignment ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -260,7 +260,7 @@ UTEST( RichText, VerticalAlignAtomicBoxes ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -355,7 +355,7 @@ UTEST( RichText, InlineTextUsesActiveInlineBoxAlignment ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -403,7 +403,7 @@ UTEST( RichText, InlineAncestorLineHeightContributesToLineHeight ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -452,7 +452,7 @@ UTEST( RichText, FloatAwareInlineLayoutUsesTreeOrder ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -487,7 +487,7 @@ UTEST( RichText, RenderSpanPayloadSupportsDrawableAndAtomicSelection ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -545,7 +545,7 @@ UTEST( RichText, InlineBoxHorizontalEdgesContributeToAdvance ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -607,7 +607,7 @@ UTEST( RichText, HitTestingSnapsAcrossInlineBoxSpacing ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -653,7 +653,7 @@ UTEST( RichText, SelectionRectsUseInlineFragments ) { WindowBackend::Default, 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -699,7 +699,7 @@ UTEST( RichText, InlineParentTextDecorationReachesFragments ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -748,7 +748,7 @@ UTEST( RichText, AtomicInlineBoxBaselineAlignment ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -778,7 +778,7 @@ UTEST( LineWrap, SoftWrapPreventsWordSplitWithOffset ) { WindowStyle::Default, WindowBackend::Default, 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); @@ -798,8 +798,6 @@ UTEST( LineWrap, SoftWrapPreventsWordSplitWithOffset ) { ASSERT_GE( info.wraps.size(), (size_t)2 ); EXPECT_EQ( info.wraps[1], 1 ); - delete font; - Engine::destroySingleton(); } @@ -840,7 +838,7 @@ UTEST( RichText, RichTextTest ) { FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); FontTrueType* font = - FontTrueType::New( "NotoSans-Regular", "../assets/fonts/NotoSans-Regular.ttf" ); + FontTrueType::New( "NotoSans-Regular", "../assets/fonts/NotoSans-Regular.ttf" ).get(); ASSERT_TRUE( font && font->loaded() ); @@ -1738,7 +1736,7 @@ UTEST( RichText, BreakSpacesWrapsAfterPreservedSpacesAndTabs ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font->loaded() ); FontFamily::loadFromRegular( font ); diff --git a/src/tests/unit_tests/systemfontresolver_tests.cpp b/src/tests/unit_tests/systemfontresolver_tests.cpp index 154afe636..f3473003d 100644 --- a/src/tests/unit_tests/systemfontresolver_tests.cpp +++ b/src/tests/unit_tests/systemfontresolver_tests.cpp @@ -1,7 +1,7 @@ #include "utest.hpp" -#include #include +#include #include #include #include @@ -548,12 +548,12 @@ UTEST( FontTrueType_faceIndex, loadWithDefaultFaceIndex ) { std::string fontPath = getFontsDir() + "DejaVuSansMono.ttf"; ASSERT_TRUE( FileSystem::fileExists( fontPath ) ); - FontTrueType* font = FontTrueType::New( "Test-faceIndex-default" ); + FontTrueType* font = FontTrueType::New( "Test-faceIndex-default" ).get(); bool loaded = font->loadFromFile( fontPath ); ASSERT_TRUE( loaded ); EXPECT_TRUE( font->loaded() ); - eeDelete( font ); + defaultResourceScope().eraseLocalFont( font ); } UTEST( FontTrueType_faceIndex, newWithFaceIndex ) { @@ -562,11 +562,11 @@ UTEST( FontTrueType_faceIndex, newWithFaceIndex ) { std::string fontPath = getFontsDir() + "DejaVuSansMono.ttf"; ASSERT_TRUE( FileSystem::fileExists( fontPath ) ); - FontTrueType* font = FontTrueType::New( "Test-faceIndex-explicit", fontPath, 0 ); + FontTrueType* font = FontTrueType::New( "Test-faceIndex-explicit", fontPath, 0 ).get(); ASSERT_TRUE( font != nullptr ); EXPECT_TRUE( font->loaded() ); - eeDelete( font ); + defaultResourceScope().eraseLocalFont( font ); } UTEST( FontTrueType_faceIndex, loadFromMemoryFaceIndex ) { @@ -578,10 +578,10 @@ UTEST( FontTrueType_faceIndex, loadFromMemoryFaceIndex ) { ScopedBuffer buf; FileSystem::fileGet( fontPath, buf ); - FontTrueType* font = FontTrueType::New( "Test-faceIndex-memory" ); + FontTrueType* font = FontTrueType::New( "Test-faceIndex-memory" ).get(); bool loaded = font->loadFromMemory( buf.get(), buf.length(), true, 0 ); ASSERT_TRUE( loaded ); EXPECT_TRUE( font->loaded() ); - eeDelete( font ); + defaultResourceScope().eraseLocalFont( font ); } diff --git a/src/tests/unit_tests/uicss_inheritance_tests.cpp b/src/tests/unit_tests/uicss_inheritance_tests.cpp index 1bd5bd109..f6141424b 100644 --- a/src/tests/unit_tests/uicss_inheritance_tests.cpp +++ b/src/tests/unit_tests/uicss_inheritance_tests.cpp @@ -1,7 +1,7 @@ #include "utest.hpp" #include -#include #include +#include #include #include #include diff --git a/src/tests/unit_tests/uifontpickerdialog_tests.cpp b/src/tests/unit_tests/uifontpickerdialog_tests.cpp index 006c7ff87..c90ae6504 100644 --- a/src/tests/unit_tests/uifontpickerdialog_tests.cpp +++ b/src/tests/unit_tests/uifontpickerdialog_tests.cpp @@ -1,7 +1,9 @@ #include "utest.hpp" #include #include +#include #include +#include #include #include #include @@ -47,6 +49,16 @@ template static void pumpUntil( UISceneNode* sceneNode, Pre } } +class TestFontPickerDialog : public UIFontPickerDialog { + public: + static TestFontPickerDialog* New() { return eeNew( TestFontPickerDialog, () ); } + + FontTrueTypeWeakPtr getPreviewFontHandle() const { return mPreviewFont; } + + protected: + TestFontPickerDialog() : UIFontPickerDialog() {} +}; + UTEST( UIFontPickerDialog, PreselectsExternalFontPath ) { UIApplication app( WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default, @@ -55,7 +67,6 @@ UTEST( UIFontPickerDialog, PreselectsExternalFontPath ) { const std::string fontPath = Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf"; ASSERT_TRUE( FileSystem::fileExists( fontPath ) ); - UIFontPickerDialog* dialog = UIFontPickerDialog::New(); dialog->setSelectedFont( fontPath ); @@ -73,6 +84,150 @@ UTEST( UIFontPickerDialog, PreselectsExternalFontPath ) { EXPECT_FALSE( selectionDialog->getSelection().font.family.empty() ); } +UTEST( UIFontPickerDialog, PreviewFontsDoNotPopulateSceneFontCatalog ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); + + const std::string koreanFontPath = + Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf"; + const std::string hebrewFontPath = + Sys::getProcessPath() + "assets/fonts/NotoSansHebrew-Regular.ttf"; + ASSERT_TRUE( FileSystem::fileExists( koreanFontPath ) ); + ASSERT_TRUE( FileSystem::fileExists( hebrewFontPath ) ); + + UIFontPickerDialog* dialog = UIFontPickerDialog::New(); + ResourceScope& sceneScope = *app.getUI()->getResourceScope(); + const size_t sceneFontCount = sceneScope.getFonts().size(); + + dialog->setSelectedFont( koreanFontPath ); + dialog->setSelectedFont( hebrewFontPath ); + + EXPECT_EQ( sceneFontCount, sceneScope.getFonts().size() ); +} + +UTEST( UIFontPickerDialog, SelectingFontDoesNotCreateMetricOnlyPage ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); + + const std::string fontPath = Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf"; + ASSERT_TRUE( FileSystem::fileExists( fontPath ) ); + UIFontPickerDialog* dialog = UIFontPickerDialog::New(); + dialog->setSelectedFont( fontPath ); + app.getUI()->draw(); + GlobalBatchRenderer::instance()->draw(); + + bool foundPreviewPage = false; + for ( const auto& texture : TextureFactory::instance()->snapshotTextures() ) { + if ( String::startsWith( texture.displayName, "@font:TrueType:Noto Sans KR:" ) ) { + foundPreviewPage = true; + EXPECT_FALSE( String::endsWith( texture.displayName, ":10" ) ); + } + } + EXPECT_TRUE( foundPreviewPage ); +} + +UTEST( UIFontPickerDialog, ReleasesPreviewFontTexturesOnClose ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); + + const std::string koreanFontPath = + Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf"; + const std::string hebrewFontPath = + Sys::getProcessPath() + "assets/fonts/NotoSansHebrew-Regular.ttf"; + ASSERT_TRUE( FileSystem::fileExists( koreanFontPath ) ); + ASSERT_TRUE( FileSystem::fileExists( hebrewFontPath ) ); + + TextureFactory* textureFactory = TextureFactory::instance(); + UnorderedSet baselineIds; + for ( const auto& texture : textureFactory->snapshotTextures() ) + baselineIds.insert( texture.id ); + + UIFontPickerDialog* dialog = UIFontPickerDialog::New(); + dialog->setSelectedFont( koreanFontPath ); + app.getUI()->draw(); + dialog->setSelectedFont( hebrewFontPath ); + app.getUI()->draw(); + GlobalBatchRenderer::instance()->draw(); + + std::vector> previewTextures; + for ( const auto& texture : textureFactory->snapshotTextures() ) { + if ( baselineIds.find( texture.id ) == baselineIds.end() && + ( String::contains( texture.displayName, "Noto Sans KR" ) || + String::contains( texture.displayName, "Noto Sans Hebrew" ) ) ) + previewTextures.emplace_back( texture.displayName, texture.texture ); + } + ASSERT_FALSE( previewTextures.empty() ); + + dialog->close(); + app.getUI()->update( Time::Zero ); + GlobalBatchRenderer::instance()->draw(); + textureFactory->collectReleasedTextures(); + + for ( const auto& texture : previewTextures ) + EXPECT_TRUE( texture.second.expired() ); +} + +UTEST( UIFontPickerDialog, ReleasesEnumeratedSystemFontTexturesOnClose ) { + UIApplication app( + WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default, + WindowBackend::Default, 32 ), + UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) ); + app.getUI()->getResourceScope()->clearImports(); + + std::vector systemFonts; + UnorderedSet families; + for ( const FontDesc& font : SystemFontResolver::instance()->enumerate() ) { + if ( font.path.empty() || font.family.empty() || !families.insert( font.family ).second ) + continue; + systemFonts.emplace_back( font ); + if ( systemFonts.size() == 3 ) + break; + } + if ( systemFonts.size() < 2 ) + UTEST_SKIP( "fewer than two distinct system font families available" ); + + TextureFactory* textureFactory = TextureFactory::instance(); + TestFontPickerDialog* dialog = TestFontPickerDialog::New(); + app.getUI()->draw(); + GlobalBatchRenderer::instance()->draw(); + std::vector previewFonts; + std::vector previewTextures; + + for ( const FontDesc& font : systemFonts ) { + dialog->setSelectedFont( font ); + app.getUI()->draw(); + GlobalBatchRenderer::instance()->draw(); + FontTrueTypePtr previewFont = dialog->getPreviewFontHandle().lock(); + ASSERT_TRUE( previewFont ); + previewFonts.emplace_back( previewFont ); + previewTextures.emplace_back( + previewFont->getTexture( PixelDensity::dpToPxI( dialog->getSelection().size * 2 ) ) ); + previewTextures.emplace_back( previewFont->getTexture( PixelDensity::dpToPxI( 12 ) ) ); + } + + dialog->close(); + GlobalBatchRenderer::instance()->draw(); + pumpUntil( app.getUI(), [&] { + textureFactory->collectReleasedTextures(); + textureFactory->purgeExpiredTextures(); + return std::all_of( previewFonts.begin(), previewFonts.end(), + []( const FontTrueTypeWeakPtr& font ) { return font.expired(); } ) && + std::all_of( previewTextures.begin(), previewTextures.end(), + []( const TextureWeakPtr& texture ) { return texture.expired(); } ); + } ); + + for ( const auto& font : previewFonts ) + EXPECT_TRUE( font.expired() ); + for ( const auto& texture : previewTextures ) + EXPECT_TRUE( texture.expired() ); +} + UTEST( UIFontPickerDialog, AsyncLoadPreservesExternalFontPreselection ) { UIApplication app( WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default, @@ -108,7 +263,7 @@ UTEST( UIFontPickerDialog, DefaultColorComesFromTheme ) { EXPECT_TRUE( dialog->getSelection().color.a != 0 ); } -UTEST( UIFontPickerDialog, IncludesDiskFontsLoadedInFontManager ) { +UTEST( UIFontPickerDialog, IncludesDiskFontsLoadedInDefaultScope ) { UIApplication app( WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default, WindowBackend::Default, 32 ), @@ -117,7 +272,9 @@ UTEST( UIFontPickerDialog, IncludesDiskFontsLoadedInFontManager ) { const std::string fontPath = Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf"; ASSERT_TRUE( FileSystem::fileExists( fontPath ) ); - FontTrueType* font = FontTrueType::New( "UIFontPickerDialogManagedNotoSansKR", fontPath ); + FontTrueTypePtr fontHandle = + FontTrueType::New( "UIFontPickerDialogManagedNotoSansKR", fontPath ); + FontTrueType* font = fontHandle.get(); ASSERT_TRUE( font != nullptr ); ASSERT_TRUE( font->loaded() ); ASSERT_FALSE( font->getInfo().family.empty() ); diff --git a/src/tests/unit_tests/uihtml_flex_test.cpp b/src/tests/unit_tests/uihtml_flex_test.cpp index aecc5dfe4..d59ca740a 100644 --- a/src/tests/unit_tests/uihtml_flex_test.cpp +++ b/src/tests/unit_tests/uihtml_flex_test.cpp @@ -24,7 +24,7 @@ using namespace EE::Graphics; static void init_flex_test() { FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); UISceneNode* sceneNode = UISceneNode::New(); diff --git a/src/tests/unit_tests/uihtml_float_tests.cpp b/src/tests/unit_tests/uihtml_float_tests.cpp index eac1bcbd2..783a322f1 100644 --- a/src/tests/unit_tests/uihtml_float_tests.cpp +++ b/src/tests/unit_tests/uihtml_float_tests.cpp @@ -26,7 +26,7 @@ static void init_float_test() { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); @@ -1161,7 +1161,7 @@ UTEST( UIHTMLFloat, floatNotAffectedByTextAlignCenter ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); diff --git a/src/tests/unit_tests/uihtml_grid_test.cpp b/src/tests/unit_tests/uihtml_grid_test.cpp index 01832d7cb..29ded78a0 100644 --- a/src/tests/unit_tests/uihtml_grid_test.cpp +++ b/src/tests/unit_tests/uihtml_grid_test.cpp @@ -26,7 +26,7 @@ using namespace EE::Graphics; static void init_grid_test() { FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); UISceneNode* sceneNode = UISceneNode::New(); diff --git a/src/tests/unit_tests/uihtml_position_tests.cpp b/src/tests/unit_tests/uihtml_position_tests.cpp index 6577923ae..3630f7d84 100644 --- a/src/tests/unit_tests/uihtml_position_tests.cpp +++ b/src/tests/unit_tests/uihtml_position_tests.cpp @@ -25,7 +25,7 @@ static void init_ui_test() { WindowBackend::Default, 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp index 5dfac9521..480d1ef21 100644 --- a/src/tests/unit_tests/uihtml_tests.cpp +++ b/src/tests/unit_tests/uihtml_tests.cpp @@ -2,10 +2,10 @@ #include "utest.hpp" #include -#include #include #include #include +#include #include #include #include @@ -80,10 +80,10 @@ static void init_ui_test() { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); - FontTrueType* monospace = FontTrueType::New( "monospace" ); + FontTrueType* monospace = FontTrueType::New( "monospace" ).get(); monospace->loadFromFile( "../assets/fonts/DejaVuSansMono.ttf" ); FontFamily::loadFromRegular( monospace ); @@ -131,7 +131,7 @@ UTEST( UIHTMLTable, complexLayout ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -192,7 +192,7 @@ UTEST( UIHTMLTable, complexLayout2 ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -251,7 +251,7 @@ UTEST( UIHTML, redditOldThreadWebViewSmoke ) { UTEST_SKIP( "old Reddit fixture CSS asset is not readable" ); } - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1048,7 +1048,7 @@ UTEST( UIRichText, anchorMargins ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1090,7 +1090,7 @@ UTEST( UIRichText, spanPadding ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1124,7 +1124,7 @@ UTEST( UIRichText, anchorPadding ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1164,7 +1164,7 @@ UTEST( UIRichText, anchorPaddingLineHeight ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1204,7 +1204,7 @@ UTEST( UIHTML, InlineBaselineAlignmentProperties ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1257,7 +1257,7 @@ UTEST( UIHTML, InlineBlockVerticalAlignDoesNotInflateOwnTextLine ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1306,7 +1306,7 @@ UTEST( UIHTMLTable, complexLayout3 ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1339,7 +1339,7 @@ UTEST( UIHTMLTable, nestedPerformance ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1390,7 +1390,7 @@ UTEST( UIHTMLTable, specifiedWidth ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1430,7 +1430,7 @@ UTEST( UIHTMLTable, nestedSpecifiedWidth ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1975,7 +1975,7 @@ UTEST( UIHTMLTable, tableLayoutFixed ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2023,7 +2023,7 @@ UTEST( UIHTMLBody, backgroundColorPropagation ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2100,7 +2100,7 @@ UTEST( UILayout, marginAuto ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2368,7 +2368,7 @@ UTEST( UIHTMLDetails, lobstersInlineBlockCachesWidth ) { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); @@ -2548,7 +2548,7 @@ UTEST( UIBorder, renderingVariations ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2582,7 +2582,7 @@ UTEST( UIBorder, renderingVariations2 ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2610,12 +2610,11 @@ UTEST( UIBorder, renderingVariations2 ) { } static UISceneNode* init_test_inline_block() { - FontTrueType* font = nullptr; FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); - FontTrueType* monoFont = FontTrueType::New( "monospace" ); + FontTrueType* monoFont = FontTrueType::New( "monospace" ).get(); monoFont->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); UISceneNode* sceneNode = UISceneNode::New(); SceneManager::instance()->add( sceneNode ); @@ -4379,7 +4378,7 @@ static UISceneNode* createWinAndLoadHTML( std::string winName, std::string htmlP ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); if ( font == nullptr || !font->loaded() ) return nullptr; @@ -4447,7 +4446,7 @@ UTEST( FontTrueType, glyphScaleZeroDimensionsNoCrash ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -5326,7 +5325,7 @@ UTEST( UIHTML, RonStonerDeferredImagesUpdateDocumentHeight ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -5416,7 +5415,7 @@ UTEST( UIHTML, NewsBlurStoryArchiveLayoutStabilizes ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); diff --git a/src/tests/unit_tests/uihtmlform_tests.cpp b/src/tests/unit_tests/uihtmlform_tests.cpp index d6641d147..6ab300a72 100644 --- a/src/tests/unit_tests/uihtmlform_tests.cpp +++ b/src/tests/unit_tests/uihtmlform_tests.cpp @@ -31,7 +31,7 @@ static UISceneNode* initFormTest( const std::string& title ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); diff --git a/src/tests/unit_tests/uiscenenode_tests.cpp b/src/tests/unit_tests/uiscenenode_tests.cpp index 326e87ee1..3c688fd91 100644 --- a/src/tests/unit_tests/uiscenenode_tests.cpp +++ b/src/tests/unit_tests/uiscenenode_tests.cpp @@ -20,12 +20,11 @@ using namespace EE::Scene; using namespace EE::UI; static UISceneNode* init_test_scene_node() { - FontTrueType* font = nullptr; FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); FontFamily::loadFromRegular( font ); - FontTrueType* monoFont = FontTrueType::New( "monospace" ); + FontTrueType* monoFont = FontTrueType::New( "monospace" ).get(); monoFont->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); UISceneNode* sceneNode = UISceneNode::New(); SceneManager::instance()->add( sceneNode ); diff --git a/src/tests/unit_tests/uitextnode_tests.cpp b/src/tests/unit_tests/uitextnode_tests.cpp index b22949e01..76a12fe51 100644 --- a/src/tests/unit_tests/uitextnode_tests.cpp +++ b/src/tests/unit_tests/uitextnode_tests.cpp @@ -1,9 +1,9 @@ #include "utest.hpp" #include -#include #include #include +#include #include #include #include @@ -39,7 +39,7 @@ static UISceneNode* createRichTextScene() { 32, {}, 1, false, true ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); if ( !font->loaded() ) { Engine::destroySingleton(); diff --git a/src/tests/unit_tests/uiwebview_tests.cpp b/src/tests/unit_tests/uiwebview_tests.cpp index 6373e45a5..77cf21aed 100644 --- a/src/tests/unit_tests/uiwebview_tests.cpp +++ b/src/tests/unit_tests/uiwebview_tests.cpp @@ -9,9 +9,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -65,7 +65,7 @@ UTEST( UIWebView, OwnedDocumentSceneScrollTarget ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -136,7 +136,7 @@ UTEST( UIWebView, DocumentRootHitTestingTraversesScrollableExtent ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -215,7 +215,7 @@ UTEST( UIWebView, FontSizeEmDoesNotCompoundOnViewportRelayout ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -287,7 +287,7 @@ UTEST( UIWebView, FontFaceWeightSurvivesViewportRelayout ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -369,7 +369,7 @@ UTEST( UIWebView, AbsoluteTextareaPercentageHeightDoesNotRecurse ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -436,7 +436,7 @@ UTEST( UIWebView, RedditLoggedOutCommentSignupTextareaDoesNotGrowWithDocument ) ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -575,7 +575,7 @@ UTEST( UIWebView, VerticalScrollbarViewportDoesNotCreateHorizontalScroll ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -660,7 +660,7 @@ UTEST( UIWebView, ExplicitWideDocumentStillCreatesHorizontalScroll ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -713,7 +713,7 @@ UTEST( UIWebView, ExplicitWideDocumentHorizontalScrollReachesRightEdgeAtPixelDen ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -777,7 +777,7 @@ UTEST( UIWebView, ResponsiveDocumentShrinksAfterGrowResize ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -867,7 +867,7 @@ UTEST( UIWebView, HorizontalScrollDisappearsAfterResponsiveShrink ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -951,7 +951,7 @@ UTEST( UIWebView, HiddenAndClippedWideDescendantsDoNotCreateHorizontalScroll ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1019,7 +1019,7 @@ UTEST( UIWebView, VerticalScrollbarDisappearsWithoutViewportWidthOscillation ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1090,7 +1090,7 @@ UTEST( UIWebView, HackerNewsGrowUntilVerticalScrollbarDisappearsSettlesViewport ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1147,7 +1147,7 @@ UTEST( UIWebView, HackerNewsFrontPageBottomIsReachable ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1282,7 +1282,7 @@ UTEST( UIWebView, NavigationAfterGrowDoesNotKeepMaximizedWidthOnShrink ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1350,7 +1350,7 @@ UTEST( UIWebView, NavigationFromTallToShortShrinksDocumentExtent ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1414,7 +1414,7 @@ UTEST( UIWebView, AsyncCSSCanShrinkDocumentExtent ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1470,7 +1470,7 @@ UTEST( UIWebView, FixedPositionStaysPinnedToViewportWhenScrolled ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1537,7 +1537,7 @@ UTEST( UIWebView, StickyPositionUsesWebViewViewportWhenScrolled ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1607,7 +1607,7 @@ UTEST( UIWebView, DeferredLocalCSSIgnoredAfterNavigation ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1686,7 +1686,7 @@ UTEST( UIWebView, RemoteCSSIgnoredAfterNavigation ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1793,7 +1793,7 @@ UTEST( UIWebView, RemoteImageIgnoredAfterNavigation ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -1903,7 +1903,7 @@ UTEST( UIWebView, RemoteBackgroundImageIgnoredAfterNavigation ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2025,7 +2025,7 @@ UTEST( UIWebView, LinearGradientRendersToFramebuffer ) { FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); win->setClearColor( Color::Black ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2101,7 +2101,7 @@ UTEST( UIWebView, CoalescesViewportResizeDocumentMetrics ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2152,7 +2152,7 @@ UTEST( UIWebView, HackerNewsSingleStepRestoreSettlesViewportInOneFrame ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2228,7 +2228,7 @@ UTEST( UIWebView, LayoutDrivenResizeKeepsDocumentRootAtViewport ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2468,7 +2468,7 @@ UTEST( UIWebView, DocumentScenesIsolateStylesUriAndLookup ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2574,7 +2574,7 @@ UTEST( UIWebView, DocumentScenesIsolateAuthorFontFaces ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2647,9 +2647,9 @@ UTEST( UIWebView, DocumentScenesIsolateAuthorFontFaces ) { EXPECT_TRUE( fontA->loaded() ); EXPECT_TRUE( fontB->loaded() ); EXPECT_NE( fontA, fontB ); - EXPECT_EQ( nullptr, FontManager::instance()->getByName( "SharedDocFace" ) ); + EXPECT_EQ( nullptr, defaultResourceScope().findFont( "SharedDocFace" ).get() ); const std::string fontAResourceName = fontA->getName(); - EXPECT_EQ( fontA, FontManager::instance()->getByName( fontAResourceName ) ); + EXPECT_EQ( fontA, docA->getResourceScope()->findFont( fontAResourceName ).get() ); webViewA->loadURI( URI( "file://" + pathAWithoutFont ) ); pump(); @@ -2657,7 +2657,7 @@ UTEST( UIWebView, DocumentScenesIsolateAuthorFontFaces ) { auto targetAWithoutFont = docA->getRoot()->find( "target-a-empty" ); ASSERT_TRUE( targetAWithoutFont != nullptr ); EXPECT_EQ( nullptr, docA->getFontFromNamesList( "SharedDocFace" ) ); - EXPECT_EQ( nullptr, FontManager::instance()->getByName( fontAResourceName ) ); + EXPECT_EQ( nullptr, docA->getResourceScope()->findFont( fontAResourceName ).get() ); webViewA->loadURI( URI( "file://" + pathA2 ) ); pump(); @@ -2679,7 +2679,7 @@ UTEST( UIWebView, DocumentSceneAuthorFontFacesCleanUpOnDestruction ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2739,14 +2739,15 @@ UTEST( UIWebView, DocumentSceneAuthorFontFacesCleanUpOnDestruction ) { EXPECT_NE( loadedFontA, loadedFontB ); const std::string loadedFontAName = loadedFontA->getName(); const std::string loadedFontBName = loadedFontB->getName(); - EXPECT_EQ( loadedFontA, FontManager::instance()->getByName( loadedFontAName ) ); - EXPECT_EQ( loadedFontB, FontManager::instance()->getByName( loadedFontBName ) ); + FontWeakPtr loadedFontAWeak = docA->getResourceScope()->findFont( loadedFontAName ); + EXPECT_EQ( loadedFontA, docA->getResourceScope()->findFont( loadedFontAName ).get() ); + EXPECT_EQ( loadedFontB, docB->getResourceScope()->findFont( loadedFontBName ).get() ); webViewA->close(); pump(); - EXPECT_EQ( nullptr, FontManager::instance()->getByName( loadedFontAName ) ); - EXPECT_EQ( loadedFontB, FontManager::instance()->getByName( loadedFontBName ) ); + EXPECT_TRUE( loadedFontAWeak.expired() ); + EXPECT_EQ( loadedFontB, docB->getResourceScope()->findFont( loadedFontBName ).get() ); EXPECT_EQ( loadedFontB, docB->getFontFromNamesList( "DestroyDocFace" ) ); Engine::destroySingleton(); @@ -2759,7 +2760,7 @@ UTEST( UIWebView, RemoteFontFaceIgnoredAfterNavigation ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); const std::string fontPath = Sys::getProcessPath() + "../assets/fonts/NotoSans-Regular.ttf"; font->loadFromFile( fontPath ); ASSERT_TRUE( font != nullptr && font->loaded() ); @@ -2873,7 +2874,7 @@ UTEST( UIWebView, StaleRedirectCookieIgnoredAfterNavigation ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -2965,7 +2966,7 @@ UTEST( UIWebView, DestroyWithPendingSubresourcesIsSafe ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); const std::string fontPath = Sys::getProcessPath() + "../assets/fonts/NotoSans-Regular.ttf"; font->loadFromFile( fontPath ); ASSERT_TRUE( font != nullptr && font->loaded() ); @@ -3065,7 +3066,7 @@ UTEST( UIWebView, NewerNavigationSupersedesStartedLoad ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -3114,7 +3115,7 @@ UTEST( UIWebView, CacheGenerationAdvancesWhenReplacementDocumentIsInstalled ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); @@ -3194,7 +3195,7 @@ UTEST( UIWebView, RepeatedRemoteNavigationHandlesSubresourceFanOut ) { ContextSettings( false, 0, 0, GLv_default, true, false ) ); FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); - FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ); + FontTrueType* font = FontTrueType::New( "NotoSans-Regular" ).get(); font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" ); ASSERT_TRUE( font != nullptr && font->loaded() ); FontFamily::loadFromRegular( font ); diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index e96c71cbb..66fde0b27 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -4261,24 +4261,25 @@ FontTrueType* App::loadFont( const std::string& name, std::string fontPath, } if ( fontPath.empty() ) return nullptr; - FontTrueType* font = FontTrueType::New( name ); + ResourceScope& resourceScope = defaultResourceScope(); + FontTrueTypePtr font = FontTrueType::New( name, resourceScope ); if ( font->loadFromFile( fontPath ) ) { font->setHinting( mConfig.ui.fontHinting ); font->setAntialiasing( mConfig.ui.fontAntialiasing ); - return font; + return font.get(); } - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); // Failed to load original font? Try to fallback if ( !fallback.empty() && !wasFallback ) { if ( !fontPath.empty() && FileSystem::isRelativePath( fontPath ) ) fontPath = mResPath + fontPath; - font = FontTrueType::New( name ); + font = FontTrueType::New( name, resourceScope ); if ( font->loadFromFile( fontPath ) ) { font->setHinting( mConfig.ui.fontHinting ); font->setAntialiasing( mConfig.ui.fontAntialiasing ); - return font; + return font.get(); } - eeSAFE_DELETE( font ); + resourceScope.eraseLocalFont( font.get() ); } return nullptr; } @@ -4533,12 +4534,12 @@ void App::init( InitParameters& params ) { mFallbackFont = loadFont( "fallback-font", "fonts/DroidSansFallbackFull.ttf" ); if ( mFallbackFont ) - FontManager::instance()->addFallbackFont( mFallbackFont ); + defaultResourceScope().getFontService().addFallbackFont( mFallbackFont ); if ( mConfig.ui.fallbackFont != "fonts/DroidSansFallbackFull.ttf" ) { mUserFallbackFont = loadFont( "fallback-font", mConfig.ui.fallbackFont ); if ( mUserFallbackFont ) - FontManager::instance()->addFallbackFont( mUserFallbackFont ); + defaultResourceScope().getFontService().addFallbackFont( mUserFallbackFont ); } Log::info( "Fonts loaded in: %s", fontsClock.getElapsedTime().toString() ); @@ -4812,7 +4813,6 @@ void App::init( InitParameters& params ) { mAsyncResourcesLoadCond.wait( syntaxLanguagesLock, [this]() { return mAsyncResourcesLoaded; } ); } - if ( !mFont || !mFontMono || !mRemixIconFont || !mNoniconsFont || !mCodIconFont ) { printf( "Font not found!" ); Log::error( "Font not found!" ); diff --git a/src/tools/ecode/fontpickercontroller.cpp b/src/tools/ecode/fontpickercontroller.cpp index 81a5a8ed8..8c17414c3 100644 --- a/src/tools/ecode/fontpickercontroller.cpp +++ b/src/tools/ecode/fontpickercontroller.cpp @@ -14,7 +14,7 @@ struct MonospaceFontPreview { std::string previewPath; FontTrueType* originalFont{ nullptr }; bool confirmed{ false }; - UnorderedMap loadedFonts; + UnorderedMap loadedFonts; }; void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMonoFont, @@ -29,7 +29,8 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo return path; }; - const auto applyMonospaceFont = [this, terminalFont]( FontTrueType* fontMono ) { + const auto applyMonospaceFont = [this, terminalFont]( FontTrueType* fontMono, + bool loadFamily = false ) { if ( !fontMono ) return; @@ -40,7 +41,10 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo fontMono->setEnableDynamicMonospace( true ); fontMono->setBoldAdvanceSameAsRegular( true ); - FontFamily::loadFromRegular( fontMono ); + // Related faces are published by FontFamily. Keep transient previews isolated and only + // publish/load their family after the user confirms the selection. + if ( loadFamily ) + FontFamily::loadFromRegular( fontMono ); if ( !mApp->getSplitter() ) return; @@ -75,21 +79,30 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo : mApp->getFontMono() ); } - const auto loadPreviewFont = [this, preview]( const std::string& newPath ) -> FontTrueType* { + const auto loadPreviewFont = [this, preview]( const FontDesc& desc ) -> FontTrueTypePtr { if ( !preview ) - return nullptr; + return {}; - auto found = preview->loadedFonts.find( newPath ); + auto found = preview->loadedFonts.find( desc.path ); if ( found != preview->loadedFonts.end() ) return found->second; - auto fontName = FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( newPath ) ); - FontTrueType* fontMono = mApp->loadFont( fontName, newPath ); - if ( fontMono ) - preview->loadedFonts[newPath] = fontMono; + FontTrueTypePtr fontMono = defaultResourceScope().getFontService().loadSystemFont( desc ); + if ( fontMono ) { + fontMono->setHinting( mApp->getConfig().ui.fontHinting ); + fontMono->setAntialiasing( mApp->getConfig().ui.fontAntialiasing ); + preview->loadedFonts[desc.path] = fontMono; + } return fontMono; }; + const auto publishPreviewFont = []( const std::string& path, const FontTrueTypePtr& font ) { + if ( !font ) + return; + auto fontName = FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ); + defaultResourceScope().publishLocalFont( std::move( fontName ), font ); + }; + const Uint32 flags = UIFontPickerDialog::DefaultFlags | ( loadingMonoFont ? UIFontPickerDialog::MonospaceOnly : 0 ); UIFontPickerDialog* dialog = UIFontPickerDialog::New( flags ); @@ -97,7 +110,7 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo dialog->setCloseShortcut( KEY_ESCAPE ); dialog->on( Event::OnWindowClose, [this, preview, applyMonospaceFont]( const Event* ) { if ( preview && !preview->confirmed ) - applyMonospaceFont( preview->originalFont ); + applyMonospaceFont( preview->originalFont, false ); if ( App::instance() && mApp->getSplitter() && mApp->getSplitter()->getCurWidget() && !SceneManager::instance()->isShuttingDown() ) { @@ -113,20 +126,20 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo return; if ( newPath == preview->originalPath ) { - applyMonospaceFont( preview->originalFont ); + applyMonospaceFont( preview->originalFont, false ); preview->previewPath = newPath; return; } - FontTrueType* fontMono = loadPreviewFont( newPath ); + FontTrueTypePtr fontMono = loadPreviewFont( selection.font ); if ( fontMono ) { - applyMonospaceFont( fontMono ); + applyMonospaceFont( fontMono.get(), false ); preview->previewPath = newPath; } } ); } dialog->setOnFontPicked( [&fontPath, loadingMonoFont, onFinish, preview, normalizedFontPath, - loadPreviewFont, + loadPreviewFont, publishPreviewFont, applyMonospaceFont]( const UIFontSelection& selection ) { auto newPath = normalizedFontPath( selection.font.path ); if ( newPath.empty() ) @@ -139,18 +152,20 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo return; } - FontTrueType* fontMono = preview && newPath == preview->originalPath - ? preview->originalFont - : loadPreviewFont( newPath ); + FontTrueTypePtr previewFont = preview && newPath != preview->originalPath + ? loadPreviewFont( selection.font ) + : FontTrueTypePtr{}; + FontTrueType* fontMono = previewFont ? previewFont.get() : preview->originalFont; if ( fontMono ) { fontPath = newPath; if ( preview ) preview->confirmed = true; - applyMonospaceFont( fontMono ); + publishPreviewFont( newPath, previewFont ); + applyMonospaceFont( fontMono, previewFont != nullptr ); } } else if ( preview ) { preview->confirmed = true; - applyMonospaceFont( preview->originalFont ); + applyMonospaceFont( preview->originalFont, false ); } } ); dialog->setSelectedFont( absoluteFontPath ); diff --git a/src/tools/ecode/settingsmenu.cpp b/src/tools/ecode/settingsmenu.cpp index 8d83c5bd6..5c92f9121 100644 --- a/src/tools/ecode/settingsmenu.cpp +++ b/src/tools/ecode/settingsmenu.cpp @@ -3163,7 +3163,7 @@ UIMenu* SettingsMenu::createFontHintMenu() { if ( String::startsWith( id, "hint_" ) ) { auto hint = id.substr( 5 ).toUtf8(); mApp->getConfig().ui.fontHinting = FontTrueType::fontHintingFromString( hint ); - FontManager::instance()->setHinting( mApp->getConfig().ui.fontHinting ); + defaultResourceScope().getFontService().setHinting( mApp->getConfig().ui.fontHinting ); } } ); @@ -3204,7 +3204,8 @@ UIMenu* SettingsMenu::createFontAntiAliasingMenu() { auto hint = id.substr( 3 ).toUtf8(); mApp->getConfig().ui.fontAntialiasing = FontTrueType::fontAntialiasingFromString( hint ); - FontManager::instance()->setAntialiasing( mApp->getConfig().ui.fontAntialiasing ); + defaultResourceScope().getFontService().setAntialiasing( + mApp->getConfig().ui.fontAntialiasing ); } } ); diff --git a/src/tools/eterm/eterm.cpp b/src/tools/eterm/eterm.cpp index b4c5565f0..6b4f6fb11 100644 --- a/src/tools/eterm/eterm.cpp +++ b/src/tools/eterm/eterm.cpp @@ -276,7 +276,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { FontTrueType* fontMono = nullptr; if ( fontPath && FileSystem::fileExists( fontPath.Get() ) ) { FileInfo file( fontPath.Get() ); - fontMono = FontTrueType::New( "monospace" ); + fontMono = FontTrueType::New( "monospace" ).get(); if ( fontMono->loadFromFile( file.getFilepath() ) ) { FontFamily::loadFromRegular( fontMono ); } else { @@ -284,7 +284,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { } } if ( fontMono == nullptr ) { - fontMono = FontTrueType::New( "monospace" ); + fontMono = FontTrueType::New( "monospace" ).get(); fontMono->loadFromFile( resPath + "fonts/DejaVuSansMonoNerdFontComplete.ttf" ); FontFamily::loadFromRegular( fontMono, "DejaVuSansMono" ); } @@ -301,9 +301,9 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { ? fallbackFontPathF.Get() : resPath + "fonts/DroidSansFallbackFull.ttf" ); if ( FileSystem::fileExists( fallbackFontPath ) ) { - FontTrueType* fallbackFont = FontTrueType::New( "fallback-font" ); + FontTrueType* fallbackFont = FontTrueType::New( "fallback-font" ).get(); if ( fallbackFont->loadFromFile( fallbackFontPath ) ) - FontManager::instance()->addFallbackFont( fallbackFont ); + defaultResourceScope().getFontService().addFallbackFont( fallbackFont ); } Float realMaxFPS = maxFPS.Get() ? maxFPS.Get() : currentDisplay->getRefreshRate(); diff --git a/src/tools/mapeditor/mapeditor.cpp b/src/tools/mapeditor/mapeditor.cpp index fd83342ee..53b323eb9 100644 --- a/src/tools/mapeditor/mapeditor.cpp +++ b/src/tools/mapeditor/mapeditor.cpp @@ -105,11 +105,12 @@ EE_MAIN_FUNC int main( int, char*[] ) { else if ( PixelDensity::getPixelDensity() >= 2.f ) pd = "2x"; - FontTrueType* font = FontTrueType::New( "NotoSans-Regular", - resPath + "assets/fonts/NotoSans-Regular.ttf" ); + FontTrueTypePtr font = FontTrueType::New( "NotoSans-Regular", + resPath + "assets/fonts/NotoSans-Regular.ttf", + *uiSceneNode->getResourceScope() ); UITheme* theme = UITheme::load( "uitheme" + pd, "uitheme" + pd, - resPath + "assets/ui/uitheme" + pd + ".eta", font, + resPath + "assets/ui/uitheme" + pd + ".eta", font.get(), resPath + "assets/ui/uitheme.css" ); /*UITheme* theme = @@ -120,7 +121,7 @@ EE_MAIN_FUNC int main( int, char*[] ) { uiSceneNode->getUIThemeManager() ->setDefaultEffectsEnabled( true ) ->setDefaultTheme( theme ) - ->setDefaultFont( font ) + ->setDefaultFont( font.get() ) ->add( theme ); } diff --git a/src/tools/textureatlaseditor/textureatlaseditor.cpp b/src/tools/textureatlaseditor/textureatlaseditor.cpp index 68a21c13a..db0e61b85 100644 --- a/src/tools/textureatlaseditor/textureatlaseditor.cpp +++ b/src/tools/textureatlaseditor/textureatlaseditor.cpp @@ -109,21 +109,22 @@ EE_MAIN_FUNC int main( int, char*[] ) { else if ( PixelDensity::getPixelDensity() >= 2.f ) pd = "2x"; - FontTrueType* font = FontTrueType::New( "NotoSans-Regular", - resPath + "assets/fonts/NotoSans-Regular.ttf" ); + FontTrueTypePtr font = FontTrueType::New( "NotoSans-Regular", + resPath + "assets/fonts/NotoSans-Regular.ttf", + *uiSceneNode->getResourceScope() ); /*UITheme* theme = UITheme::load( "uitheme" + pd, "uitheme" + pd, resPath + * "assets/ui/uitheme" + pd + ".eta", font, resPath + "assets/ui/uitheme.css" );*/ - UITheme* theme = - UITheme::load( "uitheme", "uitheme", "", font, resPath + "assets/ui/breeze.css" ); + UITheme* theme = UITheme::load( "uitheme", "uitheme", "", font.get(), + resPath + "assets/ui/breeze.css" ); uiSceneNode->combineStyleSheet( theme->getStyleSheet() ); uiSceneNode->getUIThemeManager() ->setDefaultEffectsEnabled( true ) ->setDefaultTheme( theme ) - ->setDefaultFont( font ) + ->setDefaultFont( font.get() ) ->add( theme ); } diff --git a/src/tools/uieditor/uieditor.cpp b/src/tools/uieditor/uieditor.cpp index 5c1431cb2..f82c804f5 100644 --- a/src/tools/uieditor/uieditor.cpp +++ b/src/tools/uieditor/uieditor.cpp @@ -142,7 +142,7 @@ void App::unloadImages() { void App::unloadFonts() { for ( auto it = mFontsLoaded.begin(); it != mFontsLoaded.end(); ++it ) - FontManager::instance()->remove( it->first ); + mUISceneNode->getResourceScope()->eraseLocalFont( it->first.get() ); mFontsLoaded.clear(); } @@ -168,7 +168,9 @@ FontTrueType* App::loadFont( const std::string& name, std::string fontPath, } if ( fontPath.empty() ) return nullptr; - return FontTrueType::New( name, fontPath ); + FontTrueTypePtr font = FontTrueType::New( name, fontPath, *mUISceneNode->getResourceScope() ); + mFontsLoaded[font] = name; + return font.get(); } void App::createWidgetInspector() { @@ -179,7 +181,7 @@ void App::createWidgetInspector() { void App::loadFont( std::string path ) { std::string filename( FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ) ); - FontTrueType* font = FontTrueType::New( filename ); + FontTrueTypePtr font = FontTrueType::New( filename, *mUISceneNode->getResourceScope() ); font->loadFromFile( path ); mFontsLoaded[font] = filename; } @@ -614,7 +616,7 @@ void App::onLayoutSelected( const Event* event ) { UIMenuCheckBox* chk = static_cast( event->getNode() ); chk->setActive( true ); - std::map::iterator it; + UnorderedMap::iterator it; if ( ( it = mLayouts.find( txt.toUtf8() ) ) != mLayouts.end() ) loadLayout( it->second ); @@ -1271,16 +1273,16 @@ void App::init( const Float& pixelDensityConf, const bool& useAppTheme, const st mResPath += "assets"; FileSystem::dirAddSlashAtEnd( mResPath ); - FontTrueType* font = + FontTrueTypePtr font = FontTrueType::New( "NotoSans-Regular", mResPath + "fonts/NotoSans-Regular.ttf" ); - FontTrueType* fontMono = + FontTrueTypePtr fontMono = FontTrueType::New( "monospace", mResPath + "fonts/DejaVuSansMono.ttf" ); - FontFamily::loadFromRegular( font ); - FontFamily::loadFromRegular( fontMono ); + FontFamily::loadFromRegular( font.get() ); + FontFamily::loadFromRegular( fontMono.get() ); mBaseStyleSheet = mResPath + "ui/breeze.css"; - mTheme = UITheme::load( "uitheme", "uitheme", "", font, mBaseStyleSheet ); + mTheme = UITheme::load( "uitheme", "uitheme", "", font.get(), mBaseStyleSheet ); mUISceneNode = UISceneNode::New(); mUISceneNode->setId( "uiSceneNode" ); @@ -1311,10 +1313,12 @@ void App::init( const Float& pixelDensityConf, const bool& useAppTheme, const st mAppUISceneNode->getUIThemeManager() ->setDefaultEffectsEnabled( true ) ->setDefaultTheme( mTheme ) - ->setDefaultFont( font ) + ->setDefaultFont( font.get() ) ->add( mTheme ); - mUISceneNode->getUIThemeManager()->setDefaultFont( font )->setDefaultEffectsEnabled( true ); + mUISceneNode->getUIThemeManager() + ->setDefaultFont( font.get() ) + ->setDefaultEffectsEnabled( true ); mAppUISceneNode->getUIIconThemeManager()->setCurrentTheme( iconTheme ); diff --git a/src/tools/uieditor/uieditor.hpp b/src/tools/uieditor/uieditor.hpp index 08514074b..41e3383c1 100644 --- a/src/tools/uieditor/uieditor.hpp +++ b/src/tools/uieditor/uieditor.hpp @@ -184,18 +184,18 @@ class App : public UICodeEditorSplitter::Client { efsw::WatchID mWatch{ 0 }; efsw::WatchID mStyleSheetWatch{ 0 }; efsw::WatchID mBaseStyleSheetWatch{ 0 }; - std::map mWidgetRegistered; + UnorderedMap mWidgetRegistered; std::string mResPath; std::string mBasePath; UIConsole* mConsole{ nullptr }; - std::map mLayouts; + UnorderedMap mLayouts; std::vector mRecentProjects; std::vector mRecentFiles; IniFile mIni; Uint32 mRecentProjectEventClickId{ 0xFFFFFFFF }; Uint32 mRecentFilesEventClickId{ 0xFFFFFFFF }; - std::map mImagesLoaded; - std::map mFontsLoaded; + UnorderedMap mImagesLoaded; + UnorderedMap mFontsLoaded; UpdateListener* mListener{ nullptr }; std::string mConfigPath; std::string mColorSchemesPath;