From 38d97f4c28e9c356fe326536252d48ef37cee4e6 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Wed, 7 Aug 2024 20:05:06 +0300 Subject: [PATCH] split font atlas generator to interface and implementation --- examples/ExampleApps/TextExampleApp.cpp | 5 +- openVulkanoCpp/Scene/FontAtlasGenerator.hpp | 40 ++------ ...nerator.cpp => MsdfFontAtlasGenerator.cpp} | 92 +++++++++++++------ .../Scene/MsdfFontAtlasGenerator.hpp | 60 ++++++++++++ openVulkanoCpp/Scene/TextDrawable.cpp | 45 +++++---- openVulkanoCpp/Scene/TextDrawable.hpp | 19 +--- 6 files changed, 160 insertions(+), 101 deletions(-) rename openVulkanoCpp/Scene/{FontAtlasGenerator.cpp => MsdfFontAtlasGenerator.cpp} (76%) create mode 100644 openVulkanoCpp/Scene/MsdfFontAtlasGenerator.hpp diff --git a/examples/ExampleApps/TextExampleApp.cpp b/examples/ExampleApps/TextExampleApp.cpp index 4dd619b..acde656 100644 --- a/examples/ExampleApps/TextExampleApp.cpp +++ b/examples/ExampleApps/TextExampleApp.cpp @@ -24,6 +24,7 @@ #include "Controller/FreeCamCameraController.hpp" #include "Image/ImageLoaderPng.hpp" #include "Scene/FontAtlasGenerator.hpp" +#include "Scene/MsdfFontAtlasGenerator.hpp" #include #ifdef _WIN32 @@ -67,7 +68,7 @@ namespace OpenVulkano m_drawablesPool.resize(N); #ifdef MSDFGEN_AVAILABLE - Charset charset = FontAtlasGenerator::LoadAllGlyphs(fontPath); + Charset charset = MsdfFontAtlasGenerator::LoadAllGlyphs(fontPath); //Charset charset = Charset::ASCII; //for (unicode_t c = 0x0410; c <= 0x041F; c++) //{ @@ -133,7 +134,7 @@ namespace OpenVulkano PerspectiveCamera m_cam; OpenVulkano::FreeCamCameraController m_camController; #ifdef MSDFGEN_AVAILABLE - FontAtlasGenerator m_atlasGenerator; + MsdfFontAtlasGenerator m_atlasGenerator; #endif std::vector m_drawablesPool; std::vector m_nodesPool; diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp index c4ebf6d..cc05992 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp +++ b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp @@ -6,47 +6,27 @@ #pragma once -#if __has_include("msdfgen.h") - #include #include #include #include #include "Scene/AtlasMetadata.hpp" #include "Scene/Texture.hpp" -#include "msdfgen.h" -#include "msdfgen-ext.h" -#include "msdf-atlas-gen/msdf-atlas-gen.h" +#include +#include namespace OpenVulkano::Scene { - using namespace msdfgen; - using namespace msdf_atlas; - using namespace OpenVulkano::Scene; - class FontAtlasGenerator { public: - using SdfGenerator = ImmediateAtlasGenerator>; - static Charset LoadAllGlyphs(const std::variant>& data); - void GenerateAtlas(const std::string& fontFile, const Charset& charset = Charset::ASCII, - const std::optional& pngOutput = std::nullopt); - void GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset = Charset::ASCII, - const std::optional& pngOutput = std::nullopt); - void SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile = true) const; - const Texture& GetAtlas() const { return m_atlasTex; } - std::map& GetGlyphsInfo() { return m_symbols; } - AtlasMetadata& GetAtlasMetadata() { return m_meta; } - SdfGenerator& GetFontAtlsGenerator() { return m_generator; } - private: - void Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, - const std::optional& pngOutput); - void SavePng(const BitmapConstRef& storage, const std::string& output, int channels) const; - private: - SdfGenerator m_generator; - Texture m_atlasTex; - AtlasMetadata m_meta; - std::map m_symbols; + virtual void GenerateAtlas(const std::string& fontFile, const std::set& charset, + const std::optional& pngOutput = std::nullopt) = 0; + virtual void GenerateAtlas(const Array& fontData, int length, const std::set& charset, + const std::optional& pngOutput = std::nullopt) = 0; + virtual void SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile = true) const = 0; + virtual const Texture& GetAtlas() const = 0; + virtual std::map& GetGlyphsInfo() = 0; + virtual AtlasMetadata& GetAtlasMetadata() = 0; }; } -#endif diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp b/openVulkanoCpp/Scene/MsdfFontAtlasGenerator.cpp similarity index 76% rename from openVulkanoCpp/Scene/FontAtlasGenerator.cpp rename to openVulkanoCpp/Scene/MsdfFontAtlasGenerator.cpp index c86884e..f4fbeea 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/MsdfFontAtlasGenerator.cpp @@ -6,7 +6,7 @@ #if __has_include("msdfgen.h") -#include "FontAtlasGenerator.hpp" +#include "MsdfFontAtlasGenerator.hpp" #include "Base/Logger.hpp" #include "Scene/AtlasMetadata.hpp" #define STBI_MSC_SECURE_CRT @@ -22,7 +22,7 @@ namespace OpenVulkano::Scene using namespace msdfgen; using namespace msdf_atlas; - Charset FontAtlasGenerator::LoadAllGlyphs(const std::variant>& data) + Charset MsdfFontAtlasGenerator::LoadAllGlyphs(const std::variant>& data) { FT_Library library; auto error = FT_Init_FreeType(&library); @@ -55,41 +55,49 @@ namespace OpenVulkano::Scene return s; } - void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, + void MsdfFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set& charset, + const std::optional& pngOutput) + { + FreetypeHandle* ft; + FontHandle* font; + InitFreetypeFromFile(ft, font, fontFile); + Charset s; + std::for_each(s.begin(), s.end(), [&](uint32_t unicode) { s.add(unicode); }); + Generate(ft, font, s, pngOutput); + } + + void MsdfFontAtlasGenerator::GenerateAtlas(const Array& fontData, int length, + const std::set& charset, + const std::optional& pngOutput) + { + FreetypeHandle* ft; + FontHandle* font; + InitFreetypeFromBuffer(ft, font, (const msdfgen::byte*)(fontData.Data()), length); + Charset s; + std::for_each(s.begin(), s.end(), [&](uint32_t unicode) { s.add(unicode); }); + Generate(ft, font, s, pngOutput); + } + + void MsdfFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, const std::optional& pngOutput) { - if (charset.empty()) - { - Logger::RENDER->info("Provided charset is empty. Atlas will not be generated"); - return; - } // TODO: dynamic atlas and add only those symbols which are not present yet in current atlas - FreetypeHandle* ft = initializeFreetype(); - if (!ft) { throw std::runtime_error("Failed to initialize freetype"); } - FontHandle* font = loadFont(ft, fontFile.data()); - if (!font) - { - deinitializeFreetype(ft); - throw std::runtime_error(fmt::format("Failed to load font from file {0}", fontFile.data())); - } + FreetypeHandle* ft; + FontHandle* font; + InitFreetypeFromFile(ft, font, fontFile); Generate(ft, font, charset, pngOutput); } - void FontAtlasGenerator::GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset, + void MsdfFontAtlasGenerator::GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset, const std::optional& pngOutput) { - FreetypeHandle* ft = initializeFreetype(); - if (!ft) { throw std::runtime_error("Failed to initialize freetype"); } - FontHandle* font = loadFontData(ft, fontData, length); - if (!font) - { - deinitializeFreetype(ft); - throw std::runtime_error("Failed to load font data from given buffer"); - } + FreetypeHandle* ft; + FontHandle* font; + InitFreetypeFromBuffer(ft, font, fontData, length); Generate(ft, font, charset, pngOutput); } - void FontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const + void MsdfFontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const { if (m_symbols.empty()) { @@ -126,7 +134,34 @@ namespace OpenVulkano::Scene fs.write(reinterpret_cast(&packedFlag), sizeof(uint32_t)); } - void FontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, + void MsdfFontAtlasGenerator::InitFreetypeFromFile(FreetypeHandle*& ft, FontHandle*& font, const std::string& fontFile) + { + ft = initializeFreetype(); + if (!ft) { throw std::runtime_error("Failed to initialize freetype"); } + font = loadFont(ft, fontFile.data()); + if (!font) + { + deinitializeFreetype(ft); + ft = nullptr; + throw std::runtime_error(fmt::format("Failed to load font from file {0}", fontFile.data())); + } + } + + void MsdfFontAtlasGenerator::InitFreetypeFromBuffer(FreetypeHandle*& ft, FontHandle*& font, + const msdfgen::byte* fontData, int length) + { + ft = initializeFreetype(); + if (!ft) { throw std::runtime_error("Failed to initialize freetype"); } + font = loadFontData(ft, fontData, length); + if (!font) + { + deinitializeFreetype(ft); + ft = nullptr; + throw std::runtime_error("Failed to load font data from given buffer"); + } + } + + void MsdfFontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, const std::optional& pngOutput) { m_symbols.clear(); @@ -220,7 +255,8 @@ namespace OpenVulkano::Scene deinitializeFreetype(ft); } - void FontAtlasGenerator::SavePng(const BitmapConstRef& storage, const std::string& output, int channels) const + void MsdfFontAtlasGenerator::SavePng(const BitmapConstRef& storage, const std::string& output, + int channels) const { stbi_flip_vertically_on_write(1); if (std::filesystem::path(output).extension() == ".png") diff --git a/openVulkanoCpp/Scene/MsdfFontAtlasGenerator.hpp b/openVulkanoCpp/Scene/MsdfFontAtlasGenerator.hpp new file mode 100644 index 0000000..106f6a8 --- /dev/null +++ b/openVulkanoCpp/Scene/MsdfFontAtlasGenerator.hpp @@ -0,0 +1,60 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#if __has_include("msdfgen.h") + +#include +#include +#include +#include +#include "Scene/AtlasMetadata.hpp" +#include "FontAtlasGenerator.hpp" +#include "Scene/Texture.hpp" +#include "msdfgen.h" +#include "msdfgen-ext.h" +#include "msdf-atlas-gen/msdf-atlas-gen.h" +#define MSDFGEN_AVAILABLE + +namespace OpenVulkano::Scene +{ + using namespace msdfgen; + using namespace msdf_atlas; + using namespace OpenVulkano::Scene; + + class MsdfFontAtlasGenerator : public FontAtlasGenerator + { + public: + using SdfGenerator = ImmediateAtlasGenerator>; + static Charset LoadAllGlyphs(const std::variant>& data); + void GenerateAtlas(const std::string& fontFile, const std::set& charset, + const std::optional& pngOutput = std::nullopt) override; + void GenerateAtlas(const Array& fontData, int length, const std::set& charset, + const std::optional& pngOutput = std::nullopt) override; + void GenerateAtlas(const std::string& fontFile, const Charset& charset = Charset::ASCII, + const std::optional& pngOutput = std::nullopt); + void GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset = Charset::ASCII, + const std::optional& pngOutput = std::nullopt); + void SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile = true) const override; + const Texture& GetAtlas() const override { return m_atlasTex; } + std::map& GetGlyphsInfo() override { return m_symbols; } + AtlasMetadata& GetAtlasMetadata() override { return m_meta; } + SdfGenerator& GetFontAtlasGenerator() { return m_generator; } + private: + void InitFreetypeFromFile(FreetypeHandle*& ft, FontHandle*& font, const std::string& file); + void InitFreetypeFromBuffer(FreetypeHandle*& ft, FontHandle*& font, const msdfgen::byte* fontData, int length); + void Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, + const std::optional& pngOutput); + void SavePng(const BitmapConstRef& storage, const std::string& output, int channels) const; + private: + SdfGenerator m_generator; + Texture m_atlasTex; + AtlasMetadata m_meta; + std::map m_symbols; + }; +} +#endif diff --git a/openVulkanoCpp/Scene/TextDrawable.cpp b/openVulkanoCpp/Scene/TextDrawable.cpp index e8630ab..c235a4c 100644 --- a/openVulkanoCpp/Scene/TextDrawable.cpp +++ b/openVulkanoCpp/Scene/TextDrawable.cpp @@ -18,11 +18,6 @@ namespace OpenVulkano::Scene { -#ifdef MSDFGEN_AVAILABLE - using namespace msdfgen; - using namespace msdf_atlas; -#endif - Shader& TextDrawable::GetDefaultShader() { static bool once = true; @@ -113,7 +108,6 @@ namespace OpenVulkano::Scene m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); } -#ifdef MSDFGEN_AVAILABLE TextDrawable::TextDrawable(FontAtlasGenerator* fontAtlasGenerator, const TextConfig& config) { if (!fontAtlasGenerator) { throw std::runtime_error("FontAtlasGenerator is nullptr"); } @@ -123,7 +117,6 @@ namespace OpenVulkano::Scene m_material.texture = const_cast(&m_fontAtlasGenerator->GetAtlas()); m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); } -#endif void TextDrawable::GenerateText(const std::string& text, const Math::Vector3f& pos) { @@ -135,32 +128,36 @@ namespace OpenVulkano::Scene m_geometry.Close(); m_geometry.Init(text.size() * 4, text.size() * 6); -#ifdef MSDFGEN_AVAILABLE // TODO: better implementation to decide what to use: data from atlas generator or data read from file // we have msdf but loaded glyphs metadata from file before - std::optional>> glyphData; - if (m_fontAtlasGenerator->GetGlyphsInfo().empty() && !m_glyphs.empty()) + AtlasMetadata* meta; + std::map* symbols; + if (m_fontAtlasGenerator) { - // texture is set in ctor - glyphData = m_glyphs; + if (m_fontAtlasGenerator->GetGlyphsInfo().empty() && !m_glyphs.empty()) + { + // texture is set in ctor + symbols = &m_glyphs; + } + else + { + // just in case if FontAtlasGenerator changed it's atlas + m_material.texture = const_cast(&m_fontAtlasGenerator->GetAtlas()); + symbols = &m_fontAtlasGenerator->GetGlyphsInfo(); + } + meta = &m_fontAtlasGenerator->GetAtlasMetadata(); } else { - // just in case if FontAtlasGenerator changed it's atlas - m_material.texture = const_cast(&m_fontAtlasGenerator->GetAtlas()); - glyphData = m_fontAtlasGenerator->GetGlyphsInfo(); + symbols = &m_glyphs; + meta = &m_meta; } - std::map& symbols = glyphData.value(); - AtlasMetadata& meta = m_fontAtlasGenerator->GetAtlasMetadata(); -#else - std::map& symbols = m_glyphs; - AtlasMetadata& meta = m_meta; -#endif + const Texture& atlasTex = *m_material.texture; double cursorX = pos.x; auto begin = text.begin(); auto end = text.end(); - const double lineHeight = meta.lineHeight; + const double lineHeight = meta->lineHeight; double posY = pos.y; for (size_t i = 0; begin != end; i++) { @@ -171,11 +168,11 @@ namespace OpenVulkano::Scene cursorX = pos.x; continue; } - if (symbols.find(c) != symbols.end()) + if (symbols->find(c) != symbols->end()) { uint32_t vIdx = i * 4; uint32_t indices[] = { 1 + vIdx, 2 + vIdx, 3 + vIdx, 1 + vIdx, 3 + vIdx, 0 + vIdx }; - GlyphInfo& info = symbols.at(c); + GlyphInfo& info = symbols->at(c); // left bottom m_geometry.vertices[vIdx].position.x = info.xyz[0].x + cursorX; diff --git a/openVulkanoCpp/Scene/TextDrawable.hpp b/openVulkanoCpp/Scene/TextDrawable.hpp index 974d192..3575768 100644 --- a/openVulkanoCpp/Scene/TextDrawable.hpp +++ b/openVulkanoCpp/Scene/TextDrawable.hpp @@ -19,20 +19,11 @@ #include "Base/Logger.hpp" #include "AtlasMetadata.hpp" #include "Image/Image.hpp" -#if __has_include("msdfgen.h") - #include "msdfgen.h" - #include "msdfgen-ext.h" - #include "msdf-atlas-gen/msdf-atlas-gen.h" - #define MSDFGEN_AVAILABLE 1 -#endif +#include +#include "FontAtlasGenerator.hpp" namespace OpenVulkano::Scene { -#ifdef MSDFGEN_AVAILABLE - using namespace msdfgen; - using namespace msdf_atlas; -#endif // MSDFGEN_AVAILABLE - struct TextConfig { Math::Vector4f textColor = { 1, 1, 1, 1 }; @@ -54,15 +45,12 @@ namespace OpenVulkano::Scene TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config = TextConfig()); TextDrawable(const Array& atlasMetadata, Texture* atlasTex, const TextConfig& config = TextConfig()); TextDrawable(const std::map& glyphData, Texture* atlasTex, const TextConfig& config = TextConfig()); -#ifdef MSDFGEN_AVAILABLE TextDrawable(FontAtlasGenerator* fontAtlasGenerator, const TextConfig& config = TextConfig()); -#endif void GenerateText(const std::string& text, const Math::Vector3f& pos = Math::Vector3f(0.f)); void SetConfig(const TextConfig& cfg) { m_cfg = cfg; } void SetShader(Shader* shader) { m_shader = shader; } TextConfig& GetConfig() { return m_cfg; } Shader* GetShader() { return m_shader; } -#ifdef MSDFGEN_AVAILABLE void SetFontAtlasGenerator(FontAtlasGenerator* fontAtlasGenerator) { if (!fontAtlasGenerator || fontAtlasGenerator->GetGlyphsInfo().empty()) @@ -73,7 +61,6 @@ namespace OpenVulkano::Scene m_fontAtlasGenerator = fontAtlasGenerator; } FontAtlasGenerator* GetFontAtlasGenerator() { return m_fontAtlasGenerator; } -#endif private: Geometry m_geometry; Material m_material; @@ -81,9 +68,7 @@ namespace OpenVulkano::Scene std::map m_glyphs; AtlasMetadata m_meta; std::unique_ptr m_img; -#ifdef MSDFGEN_AVAILABLE FontAtlasGenerator* m_fontAtlasGenerator = nullptr; -#endif Shader* m_shader = &GetDefaultShader(); TextConfig m_cfg; };