From 933265edbbeadbeee6b25341e70c7794b83bf634 Mon Sep 17 00:00:00 2001 From: nieznanysprawiciel Date: Sun, 17 Nov 2024 13:00:35 +0100 Subject: [PATCH] FontLoader can choose proper font file; Fix choosing eqivalent FontWeights --- .clang-format | 4 +- .../TextAsset/Loader/FontAssetInitData.cpp | 10 ++++- .../TextAsset/Loader/FontAssetInitData.h | 24 +++++++++++ .../Assets/TextAsset/Loader/FontLoader.cpp | 42 +++++++++++++++---- .../Assets/TextAsset/Loader/FontLoader.h | 9 +++- .../Assets/TextAsset/Loader/FontPicker.cpp | 2 +- .../Assets/TextAsset/Loader/FontPicker.h | 12 ++++++ .../ResourceManager/Loaders/RMLoaderAPI.h | 1 + .../Tests/TestText/TestFontLoader.cpp | 26 ++++++++++++ .../Tests/TestText/TestFontPicker.cpp | 6 ++- 10 files changed, 120 insertions(+), 16 deletions(-) diff --git a/.clang-format b/.clang-format index 0fc5f5b3..d7b56281 100644 --- a/.clang-format +++ b/.clang-format @@ -94,9 +94,7 @@ EmptyLineAfterAccessModifier: Leave # Describe the regular expression of the comment with special meaning, it should not be divided into multiple lines or changed in other ways # CommentPragmas:'^ IWYU pragma:' # The initialization list of the constructor is either all on the same line or on their own line -ConstructorInitializerAllOnOneLineOrOnePerLine: true -# The comfort list of constructors all wrap -AllowAllConstructorInitializersOnNextLine: false +AllowAllConstructorInitializersOnNextLine: true # The indentation width of the initialization list of the constructor ConstructorInitializerIndentWidth: 4 # The indentation width of the continued line diff --git a/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.cpp b/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.cpp index d70f87f9..84958ef0 100644 --- a/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.cpp +++ b/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.cpp @@ -132,4 +132,12 @@ float FontLayout::SpaceWidth() const { return (float)Glyphs.at( L'0' ) float FontLayout::NewLineSize() const { return (float)Glyphs.at( L'0' ).Height; } -} // sw +// ================================ // + +std::string ChooseFontLoadData::ResourceKey() const +{ + return fmt::format( "/Font?fontSize={},family={},weight={},style={}", this->FontSize, this->FontFamily, + this->FontWeight, this->FontStyle ); +} + +} // namespace sw diff --git a/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.h b/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.h index 0a6f3d06..0ea009d8 100644 --- a/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.h +++ b/swGraphicAPI/Assets/TextAsset/Loader/FontAssetInitData.h @@ -117,6 +117,30 @@ struct FontLoaderData : public IAssetLoadInfo }; +/**@brief Load font by providing parameters which will be resolved by FontPicker. +@ingroup Text*/ +struct ChooseFontLoadData : public FontLoaderData +{ + RTTR_ENABLE( FontLoaderData ); +public: + + std::string FontFamily; + FontWeight FontWeight; + FontStyle FontStyle; + bool MatchExact; + +public: + explicit ChooseFontLoadData( const std::string& fontFamuly, FontSizeType fontSize ) + : FontLoaderData( fontSize ) + , FontFamily( fontFamuly ) + , FontWeight( FontWeight::Normal ) + , FontStyle( FontStyle::Normal ) + , MatchExact( true ) + {} + +public: + virtual std::string ResourceKey() const override; +}; diff --git a/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.cpp b/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.cpp index 66eeb13a..c31d3e73 100644 --- a/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.cpp +++ b/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.cpp @@ -60,6 +60,9 @@ Nullable< std::map > BuildKerning( const FreeTypeLibr return collector.Return( kerning ); } +// ================================ // + +FreeTypeLoader::FreeTypeLoader( FontPicker&& picker ) : m_fontPicker( std::move( picker ) ) {} // ================================ // // @@ -77,7 +80,11 @@ bool FreeTypeLoader::CanLoad( const AssetPath& filePath, TypeID r TypeID::get< Resource >() }; - return DefaultCanLoad( filePath, resourceType, allowedExtensions, allowedTypes ); + // Variant for ChooseFontLoadData descriptor. + if( filePath.GetFile().IsEmpty() ) + return CanLoadType( resourceType, allowedTypes ); + else + return DefaultCanLoad( filePath, resourceType, allowedExtensions, allowedTypes ); } // ================================ // @@ -87,13 +94,32 @@ LoadingResult FreeTypeLoader::Load( const LoadPath& filePath, TypeID resou if( assetDesc == nullptr ) return LoaderException::Create( "FreeTypeLoader", "Asset descriptor is null.", filePath, resourceType ); - if( assetDesc->get_type() != TypeID::get< FontLoaderData >() ) - return LoaderException::Create( "FreeTypeLoader", "Unsupported descriptor type [ " + assetDesc->get_type().get_name().to_string() + " ].", filePath, resourceType ); + if( assetDesc->get_type() != TypeID::get< FontLoaderData >() + && assetDesc->get_type() != TypeID::get< ChooseFontLoadData >() ) + { + return LoaderException::Create( + "FreeTypeLoader", "Unsupported descriptor type [ " + assetDesc->get_type().get_name().to_string() + " ].", + filePath, resourceType ); + } + + LoadPath fontPath = filePath; + if( assetDesc->get_type() == TypeID::get< ChooseFontLoadData >() ) + { + auto loadInfo = static_cast< const ChooseFontLoadData* >( assetDesc ); + auto chosen = + m_fontPicker.ChooseFontFile( context.GetPathsManager(), loadInfo->FontFamily, loadInfo->FontWeight, loadInfo->FontStyle, loadInfo->MatchExact ); + ReturnIfInvalid( chosen ); + + fontPath = chosen.Get().Path; + } auto loadInfo = static_cast< const FontLoaderData* >( assetDesc ); - AssetPath atlasPath = AssetPath( filePath.GetFileTranslated(), fmt::format( "/Atlas{}", loadInfo->ResourceKey() ) ); - AssetPath fontAssetPath = AssetPath( filePath.GetFileTranslated(), loadInfo->ResourceKey() ); + // Note: ResourceKey is taken from parent descriptor, because since we have exact file path, we don't need to distinguish + // between different styles and weights. The goal of havinf internal path is to avoid duplicate loading of the same font. + AssetPath atlasPath = + AssetPath( fontPath.GetFileTranslated(), fmt::format( "/Atlas{}", loadInfo->FontLoaderData::ResourceKey() ) ); + AssetPath fontAssetPath = AssetPath( fontPath.GetFileTranslated(), loadInfo->FontLoaderData::ResourceKey() ); auto cached = context.GetCachedGeneric( fontAssetPath, resourceType ); if( cached ) @@ -101,7 +127,7 @@ LoadingResult FreeTypeLoader::Load( const LoadPath& filePath, TypeID resou auto freeType = FreeTypeLibrary::Create(); ReturnIfInvalid( freeType ); - ReturnIfInvalid( freeType.Get().CreateFace( filePath, loadInfo->FontSize ) ); + ReturnIfInvalid( freeType.Get().CreateFace( fontPath, loadInfo->FontSize ) ); FontInitData fontDesc( loadInfo->FontSize ); @@ -118,7 +144,7 @@ LoadingResult FreeTypeLoader::Load( const LoadPath& filePath, TypeID resou } catch( const RuntimeException& ex ) { - return LoaderException::Create( "FreeTypeLoader", ex.ErrorMessage(), filePath, resourceType ); + return LoaderException::Create( "FreeTypeLoader", ex.ErrorMessage(), fontPath, resourceType ); } } @@ -154,7 +180,7 @@ Nullable< TexturePtr > FreeTypeLoader::RenderAtlas( const FreeTypeLibrary& //SoilTextureLoader::Save( filePath.GetFile().ChangeExtension( ".png" ), image ); TextureInitData texInfo( std::move( image ) ); - texInfo.MipMaps = MipMapsInfo( MipMapFilter::Lanczos3 ); + texInfo.MipMaps = MipMapsInfo(); texInfo.TextureUsage = TextureUsageInfo(); texInfo.Format = ResourceFormat::RESOURCE_FORMAT_R8G8B8A8_UNORM; diff --git a/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.h b/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.h index 18cc1561..9b7c37df 100644 --- a/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.h +++ b/swGraphicAPI/Assets/TextAsset/Loader/FontLoader.h @@ -10,6 +10,7 @@ #include "swGraphicAPI/ResourceManager/Loaders/IAssetLoadInfo.h" #include "swGraphicAPI/ResourceManager/Loaders/IAssetLoader.h" +#include "swGraphicAPI/Assets/TextAsset/Loader/FontPicker.h" namespace sw @@ -26,9 +27,13 @@ class FreeTypeLibrary; class FreeTypeLoader : public IAssetLoader { protected: + + FontPicker m_fontPicker; + public: - explicit FreeTypeLoader () = default; - virtual ~FreeTypeLoader () = default; + explicit FreeTypeLoader () = default; + explicit FreeTypeLoader ( FontPicker&& picker ); + virtual ~FreeTypeLoader () = default; virtual bool CanLoad ( const AssetPath& filePath, TypeID resourceType ) override; virtual LoadingResult Load ( const LoadPath& filePath, TypeID resourceType, const IAssetLoadInfo* assetDesc, RMLoaderAPI context ) override; diff --git a/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.cpp b/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.cpp index 12dd3afe..27017dea 100644 --- a/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.cpp +++ b/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.cpp @@ -69,7 +69,7 @@ Nullable< FontSearchEntry > FontPicker::ChooseFontFile( PathsManager* pm, co for( auto& variant : variants.Get() ) { - if( variant.Metadata.Weight == weight && variant.Metadata.Style == style ) + if( Eqivalent( variant.Metadata.Weight, weight ) && Eqivalent( variant.Metadata.Style, style ) ) return variant; metric[ &variant ] = diff --git a/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.h b/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.h index 3309fd86..0a2fafe1 100644 --- a/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.h +++ b/swGraphicAPI/Assets/TextAsset/Loader/FontPicker.h @@ -161,6 +161,18 @@ inline bool operator>( const FontStyle& left, const FontStyle& right ) return static_cast< u8 >( left ) > static_cast< u8 >( right ); } +/**@brief Checks if enum variants can be treated as equal.*/ +inline bool Eqivalent( const FontStyle& left, const FontStyle& right ) +{ + return FontPicker::ClosenessMetric( left ) == FontPicker::ClosenessMetric( right ); +} + +/**@brief Checks if enum variants can be treated as equal.*/ +inline bool Eqivalent( const FontWeight& left, const FontWeight& right ) +{ + return FontPicker::ClosenessMetric( left ) == FontPicker::ClosenessMetric( right ); +} + } // sw DEFINE_FMT_FORMATTER_ENUM( sw::FontWeight ); diff --git a/swGraphicAPI/ResourceManager/Loaders/RMLoaderAPI.h b/swGraphicAPI/ResourceManager/Loaders/RMLoaderAPI.h index 12a3b18f..62c3e85c 100644 --- a/swGraphicAPI/ResourceManager/Loaders/RMLoaderAPI.h +++ b/swGraphicAPI/ResourceManager/Loaders/RMLoaderAPI.h @@ -63,6 +63,7 @@ class RMLoaderAPI : protected ResourceManagerAPI using ResourceManagerAPI::LoadComputeShader; using ResourceManagerAPI::LoadShader; + using ResourceManagerAPI::GetPathsManager; }; diff --git a/swGraphicAPI/Tests/TestText/TestFontLoader.cpp b/swGraphicAPI/Tests/TestText/TestFontLoader.cpp index 1ae4a732..1e121495 100644 --- a/swGraphicAPI/Tests/TestText/TestFontLoader.cpp +++ b/swGraphicAPI/Tests/TestText/TestFontLoader.cpp @@ -210,3 +210,29 @@ TEST_CASE( "GraphicAPI.Loaders.Correctness.ExitingAtlas", "[GraphicAPI][FontLoad REQUIRE_IS_VALID( font2 ); } +// ================================ // +// +TEST_CASE( "GraphicAPI.Loaders.PickFont", "[GraphicAPI][FontLoader][FreeTypeLoader]" ) +{ + auto rm = CreateResourceManagerWithMocksAndDefaults(); + auto pm = rm->GetPathsManager(); + + FontPicker picker; + picker.RegisterSearchPath( "$(FontAssets)" ); + pm->RegisterAlias( "$(FontAssets)", "$(TestAssets)/fonts/" ); + + rm->RegisterLoader( std::make_shared< FreeTypeLoader >( std::move( picker ) ) ); + rm->RegisterAssetCreator( FontCreator::CreateCreator() ); + + ChooseFontLoadData init( "Arial", 16 ); + init.FontWeight = FontWeight::Normal; + init.FontStyle = FontStyle::Normal; + + auto api = ResourceManagerAPI( rm.get() ); + auto font = api.Load< FontAsset >( "", &init ); + REQUIRE_IS_VALID( font ); + + CHECK( font.Get()->GetMetadata().Family == "Arial" ); + CHECK( font.Get()->GetMetadata().Style == FontStyle::Normal ); + CHECK( font.Get()->GetMetadata().Weight == FontWeight::Regular ); // Regular is eqivalent to Normal +} diff --git a/swGraphicAPI/Tests/TestText/TestFontPicker.cpp b/swGraphicAPI/Tests/TestText/TestFontPicker.cpp index 7340c9cf..ac43a684 100644 --- a/swGraphicAPI/Tests/TestText/TestFontPicker.cpp +++ b/swGraphicAPI/Tests/TestText/TestFontPicker.cpp @@ -120,6 +120,10 @@ TEST_CASE( "GraphicAPI.Loaders.Font.FontPicker.ChooseFont", "[GraphicAPI][FontLo chosen = picker.ChooseFontFile( rm->GetPathsManager(), "Source Sans Pro", FontWeight::Light, FontStyle::Italic, true ); REQUIRE_INVALID( chosen ); + // DemiBold is eqivalent to SemiBold so exact match should be found. + chosen = picker.ChooseFontFile( rm->GetPathsManager(), "Source Sans Pro", FontWeight::DemiBold, FontStyle::Normal, true ); + REQUIRE_IS_VALID( chosen ); + chosen = picker.ChooseFontFile( rm->GetPathsManager(), "Source Sans Pro", FontWeight::Light, FontStyle::Italic, false ); REQUIRE_IS_VALID( chosen ); CHECK( chosen.Get().Metadata.Weight == FontWeight::ExtraLight ); @@ -191,7 +195,7 @@ TEST_CASE( "GraphicAPI.Loaders.Font.FontPicker.ParseStyle", "[GraphicAPI][FontLo result = FontPicker::ParseFontStyle( "Condensed Bold" ); REQUIRE_IS_VALID( result ); - CHECK( result.Get() == FontStyle::Normal ); + CHECK( result.Get() == FontStyle::Condensed ); result = FontPicker::ParseFontStyle( "Condensed Bold Italic" ); REQUIRE_IS_VALID( result );