diff --git a/3rdParty/msdf/CMakeLists.txt b/3rdParty/msdf/CMakeLists.txt index 6de537b..3142662 100644 --- a/3rdParty/msdf/CMakeLists.txt +++ b/3rdParty/msdf/CMakeLists.txt @@ -85,6 +85,7 @@ function(LinkMsdf TARGET) set(STATIC_LIB_EXT "a") set(freetype_lib_name "libfreetype") endif() + target_include_directories(${TARGET} PUBLIC "${VCPKG_SRC_DIR}/installed/${TRIPLET}/include") # link freetype first to fix linkage issues on linux target_link_libraries(${TARGET} PUBLIC "${VCPKG_SRC_DIR}/installed/${TRIPLET}/lib/${freetype_lib_name}.${STATIC_LIB_EXT}") file(GLOB installed_libs "${VCPKG_SRC_DIR}/installed/${TRIPLET}/lib/*.${STATIC_LIB_EXT}") diff --git a/examples/ExampleApps/TextExampleApp.cpp b/examples/ExampleApps/TextExampleApp.cpp index abc6b96..f30e1c5 100644 --- a/examples/ExampleApps/TextExampleApp.cpp +++ b/examples/ExampleApps/TextExampleApp.cpp @@ -67,13 +67,15 @@ namespace OpenVulkano m_drawablesPool.resize(N); #ifdef MSDFGEN_AVAILABLE - Charset charset = Charset::ASCII; - for (unicode_t c = 0x0410; c <= 0x041F; c++) - { - // some unicode values - charset.add(c); - } - m_atlasGenerator.GenerateAtlas(fontPath, charset, "roboto-regular-atlas.png"); + Charset charset = FontAtlasGenerator::LoadAllGlyphs(fontPath); + //Charset charset = Charset::ASCII; + //for (unicode_t c = 0x0410; c <= 0x041F; c++) + //{ + // // some unicode values + // charset.add(c); + //} + m_atlasGenerator.GenerateAtlas(fontPath, charset); + m_atlasGenerator.SaveAtlasMetadataInfo("atlas_metadata", true); #endif for (int i = 0; i < texts.size(); i++) @@ -81,16 +83,19 @@ namespace OpenVulkano #ifdef MSDFGEN_AVAILABLE TextDrawable* t = new TextDrawable(&m_atlasGenerator, texts[i].second); #else - auto metadataInfo = resourceLoader.GetResource("atlas_metadata"); - auto data = resourceLoader.GetResource("roboto-regular-atlas.png"); - Image::ImageLoaderPng loader; - static auto image = loader.loadData(reinterpret_cast(data.Data()), data.Size()); - static Texture tex; - tex.resolution = image->resolution; - tex.textureBuffer = image->data.Data(); - tex.format = image->dataFormat; - tex.size = image->data.Size(); // 1 channel - TextDrawable* t = new TextDrawable(metadataInfo, &tex, texts[i].second); + auto metadataInfo = resourceLoader.GetResource("full_atlas_metadata_packed"); + TextDrawable* t = new TextDrawable(metadataInfo, texts[i].second); + // OR use separate texture + metadata file + //auto metadataInfo = resourceLoader.GetResource("atlas_metadata"); + //auto data = resourceLoader.GetResource("roboto-regular-atlas.png"); + //Image::ImageLoaderPng loader; + //static auto image = loader.loadData(reinterpret_cast(data.Data()), data.Size()); + //static Texture tex; + //tex.resolution = image->resolution; + //tex.textureBuffer = image->data.Data(); + //tex.format = image->dataFormat; + //tex.size = image->data.Size(); // 1 channel + //TextDrawable* t = new TextDrawable(metadataInfo, &tex, texts[i].second); #endif // MSDFGEN_AVAILABLE t->GenerateText(texts[i].first); m_drawablesPool[i] = t; diff --git a/examples/ExampleSources/atlas_metadata b/examples/ExampleSources/atlas_metadata index 8f6ea29..ec51427 100644 Binary files a/examples/ExampleSources/atlas_metadata and b/examples/ExampleSources/atlas_metadata differ diff --git a/examples/ExampleSources/full_atlas_metadata_packed b/examples/ExampleSources/full_atlas_metadata_packed new file mode 100644 index 0000000..a1d4b1d Binary files /dev/null and b/examples/ExampleSources/full_atlas_metadata_packed differ diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp b/openVulkanoCpp/Scene/FontAtlasGenerator.cpp index 659ef88..d398a73 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/FontAtlasGenerator.cpp @@ -7,6 +7,8 @@ #include "FontAtlasGenerator.hpp" #include "Base/Logger.hpp" #include "Scene/AtlasMetadata.hpp" +#include +#include FT_FREETYPE_H #include namespace OpenVulkano::Scene @@ -14,7 +16,41 @@ namespace OpenVulkano::Scene using namespace msdfgen; using namespace msdf_atlas; - void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, const std::optional& pngOutput) + Charset FontAtlasGenerator::LoadAllGlyphs(const std::variant>& data) + { + FT_Library library; + auto error = FT_Init_FreeType(&library); + if (error) { throw std::runtime_error("Could not initalize freetype library\n"); } + FT_Face face; + if (std::holds_alternative(data)) + { + error = FT_New_Face(library, std::get<0>(data).c_str(), 0, &face); + } + else + { + auto& arr = std::get<1>(data); + error = FT_New_Memory_Face(library, (const FT_Byte*)(arr.Data()), arr.Size(), 0, &face); + } + if (error == FT_Err_Unknown_File_Format) { throw std::runtime_error("Unknown font file format\n"); } + else if (error) { throw std::runtime_error("Font file could not be opened or read or it's corrupted\n"); } + + // some fancy font without unicode charmap + if (face->charmap == nullptr) { throw std::runtime_error("Selected font doesn't contain unicode charmap"); } + Charset s; + FT_UInt glyphIndex; + FT_ULong unicode = FT_Get_First_Char(face, &glyphIndex); + while (glyphIndex != 0) + { + s.add(unicode); + unicode = FT_Get_Next_Char(face, unicode, &glyphIndex); + } + FT_Done_Face(face); + FT_Done_FreeType(library); + return s; + } + + void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, + const std::optional& pngOutput) { if (charset.empty()) { @@ -47,20 +83,43 @@ namespace OpenVulkano::Scene Generate(ft, font, charset, pngOutput); } - void FontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile) const + void FontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const { if (m_symbols.empty()) - { + { Logger::DATA->info("No glyphs loaded. Nothing to save."); return; } - std::fstream fs(outputFile.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + std::string fileName = outputFile; + int extraMode = std::ios_base::trunc; + uint32_t packedFlag = 0; + if (packIntoSingleFile) + { + size_t ext = outputFile.find_last_of('.'); + if (ext == std::string::npos) + { + fileName += "_packed"; + } + else + { + fileName.insert(ext - 1, "_packed"); + } + savePng(m_generator.atlasStorage(), fileName.c_str()); + extraMode = std::ios_base::app; + packedFlag = 1; + } + std::fstream fs(fileName.c_str(), std::ios_base::out | std::ios_base::binary | extraMode); fs.write(reinterpret_cast(&m_meta), sizeof(AtlasMetadata)); + uint64_t metadataBytes = sizeof(AtlasMetadata); for (const auto& [key, val] : m_symbols) - { + { fs.write(reinterpret_cast(&key), sizeof(uint32_t)); fs.write(reinterpret_cast(&val), sizeof(GlyphInfo)); + metadataBytes += sizeof(uint32_t); + metadataBytes += sizeof(GlyphInfo); } + fs.write(reinterpret_cast(&metadataBytes), sizeof(uint64_t)); + fs.write(reinterpret_cast(&packedFlag), sizeof(uint32_t)); } void FontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, @@ -77,7 +136,10 @@ namespace OpenVulkano::Scene int width = 1024, height = 1024; packer.setDimensions(width, height); // more value - more sdf impact - packer.setPixelRange(26.0); + // this setup is tricky. with low value and large amount of characters visible artifacts (extra lines) may appear. + // with high value and large amount of characters sdf deals huge impact and characters are not readable anymore. + const double pixelRange = std::min((width / (double)chset.size()) * 3, 26.0); + packer.setPixelRange(pixelRange); packer.setMiterLimit(1.0); packer.pack(glyphsGeometry.data(), glyphsGeometry.size()); diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp index 484b1c9..064f20e 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp +++ b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "Scene/AtlasMetadata.hpp" #include "Scene/Texture.hpp" #include "msdfgen.h" @@ -24,11 +25,12 @@ namespace OpenVulkano::Scene class FontAtlasGenerator { public: + 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) const; + 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; } diff --git a/openVulkanoCpp/Scene/TextDrawable.cpp b/openVulkanoCpp/Scene/TextDrawable.cpp index d2debdb..e8630ab 100644 --- a/openVulkanoCpp/Scene/TextDrawable.cpp +++ b/openVulkanoCpp/Scene/TextDrawable.cpp @@ -14,6 +14,7 @@ #include "Host/ResourceLoader.hpp" #include #include +#include "Image/ImageLoader.hpp" namespace OpenVulkano::Scene { @@ -40,6 +41,16 @@ namespace OpenVulkano::Scene return textDefaultShader; } + TextDrawable::TextDrawable(const Array& atlasMetadata, const TextConfig& config) + : TextDrawable(atlasMetadata, nullptr, config) + { + } + + TextDrawable::TextDrawable(const std::string& atlasMetadataFile, const TextConfig& config) + : TextDrawable(OpenVulkano::Utils::ReadFile(atlasMetadataFile), nullptr, config) + { + } + TextDrawable::TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config) : TextDrawable(OpenVulkano::Utils::ReadFile(atlasMetadataFile), atlasTex, config) { @@ -47,20 +58,46 @@ namespace OpenVulkano::Scene TextDrawable::TextDrawable(const Array& atlasMetadata, Texture* atlasTex, const TextConfig& config) { - size_t len = atlasMetadata.Size(); - size_t read_bytes = 0; + uint32_t isPacked; + std::memcpy(&isPacked, atlasMetadata.Data() + (atlasMetadata.Size() - sizeof(uint32_t)), sizeof(uint32_t)); + uint64_t metadataBytes; + std::memcpy(&metadataBytes, atlasMetadata.Data() + (atlasMetadata.Size() - sizeof(uint32_t) - sizeof(uint64_t)), + sizeof(uint64_t)); + uint64_t offsetToMetadata = atlasMetadata.Size() - metadataBytes - sizeof(uint32_t) - sizeof(uint64_t); + if (isPacked) + { + m_material.texture = new Texture(); + m_img = Image::IImageLoader::loadData((const uint8_t*) atlasMetadata.Data(), + offsetToMetadata); + m_material.texture->format = m_img->dataFormat; + m_material.texture->resolution = m_img->resolution; + m_material.texture->size = m_img->data.Size(); // 1 channel + m_material.texture->textureBuffer = m_img->data.Data(); + } + else + { + if (atlasTex == nullptr) { throw std::runtime_error("Atlas texture cannot be null with non-packed atlas metadata"); } + m_material.texture = atlasTex; + } + + // metadata info + size_t read_bytes = offsetToMetadata; + size_t readMetadataBytes = 0; uint32_t unicode = 0; - std::memcpy(&m_meta, atlasMetadata.Data(), sizeof(AtlasMetadata)); + + std::memcpy(&m_meta, atlasMetadata.Data() + read_bytes, sizeof(AtlasMetadata)); read_bytes += sizeof(AtlasMetadata); - while (read_bytes < len) + readMetadataBytes += sizeof(AtlasMetadata); + while (readMetadataBytes < metadataBytes) { std::memcpy(&unicode, atlasMetadata.Data() + read_bytes, sizeof(uint32_t)); read_bytes += sizeof(uint32_t); + readMetadataBytes += sizeof(uint32_t); GlyphInfo& info = m_glyphs[unicode]; std::memcpy(&info, atlasMetadata.Data() + read_bytes, sizeof(GlyphInfo)); read_bytes += sizeof(GlyphInfo); + readMetadataBytes += sizeof(GlyphInfo); } - m_material.texture = atlasTex; m_cfg = config; m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); } diff --git a/openVulkanoCpp/Scene/TextDrawable.hpp b/openVulkanoCpp/Scene/TextDrawable.hpp index 1d30d70..974d192 100644 --- a/openVulkanoCpp/Scene/TextDrawable.hpp +++ b/openVulkanoCpp/Scene/TextDrawable.hpp @@ -18,6 +18,7 @@ #include "UniformBuffer.hpp" #include "Base/Logger.hpp" #include "AtlasMetadata.hpp" +#include "Image/Image.hpp" #if __has_include("msdfgen.h") #include "msdfgen.h" #include "msdfgen-ext.h" @@ -48,6 +49,8 @@ namespace OpenVulkano::Scene { public: static Shader& GetDefaultShader(); + TextDrawable(const Array& atlasMetadata, const TextConfig& config = TextConfig()); + TextDrawable(const std::string& atlasMetadataFile, const TextConfig& config = TextConfig()); 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()); @@ -77,6 +80,7 @@ namespace OpenVulkano::Scene UniformBuffer m_uniBuffer; std::map m_glyphs; AtlasMetadata m_meta; + std::unique_ptr m_img; #ifdef MSDFGEN_AVAILABLE FontAtlasGenerator* m_fontAtlasGenerator = nullptr; #endif