diff --git a/openVulkanoCpp/Base/Wrapper.hpp b/openVulkanoCpp/Base/Wrapper.hpp index b37939f..dff0be9 100644 --- a/openVulkanoCpp/Base/Wrapper.hpp +++ b/openVulkanoCpp/Base/Wrapper.hpp @@ -13,4 +13,12 @@ namespace OpenVulkano template using Ptr = std::shared_ptr; template using Weak = std::weak_ptr; template using Unique = std::unique_ptr; + + template + struct FreeDelete + { + void operator()(T* x) const { free(x); } + }; + + template using UniqueMalloc = std::unique_ptr>; } diff --git a/openVulkanoCpp/Scene/Text/FontAtlas.cpp b/openVulkanoCpp/Scene/Text/FontAtlas.cpp index 5432244..a946f29 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlas.cpp +++ b/openVulkanoCpp/Scene/Text/FontAtlas.cpp @@ -6,9 +6,11 @@ #include "FontAtlas.hpp" #include "Base/Logger.hpp" +#include "Base/Wrapper.hpp" #include "Image/ImageLoader.hpp" #include "Extensions/STBZlibCompressor.hpp" #include +#include #define STBI_MSC_SECURE_CRT #define STB_IMAGE_WRITE_IMPLEMENTATION #define STBIW_ZLIB_COMPRESS Extensions::STBZlibCompressor @@ -36,26 +38,66 @@ namespace OpenVulkano::Scene { if (!*this) { Logger::DATA->warn("Can't save empty font atlas!"); return; }; - stbi_flip_vertically_on_write(1); - std::string p = path.string(); - stbi_write_png(p.c_str(), m_texture.resolution.x, m_texture.resolution.y, m_texture.format.GetBytesPerPixel(), - m_texture.textureBuffer, m_texture.format.GetBytesPerPixel() * m_texture.resolution.x); + std::fstream fs(path, std::ios_base::out | std::ios_base::binary); - std::fstream fs(path, std::ios_base::out | std::ios_base::binary | std::ios_base::app); - fs.write(reinterpret_cast(&m_metadata), sizeof(Metadata)); - uint64_t metadataBytes = sizeof(Metadata); - for (const auto& [key, val] : m_glyphs) + // Save image { - fs.write(reinterpret_cast(&key), sizeof(uint32_t)); - fs.write(reinterpret_cast(&val), sizeof(GlyphInfo)); - metadataBytes += sizeof(uint32_t) + sizeof(GlyphInfo); + stbi_flip_vertically_on_write(1); + int pngSize; + UniqueMalloc png(stbi_write_png_to_mem(static_cast(m_texture.textureBuffer), m_texture.format.GetBytesPerPixel() * m_texture.resolution.x, m_texture.resolution.x, m_texture.resolution.y, m_texture.format.GetBytesPerPixel(), &pngSize)); + fs.write(reinterpret_cast(png.get()), pngSize); } - fs.write(reinterpret_cast(&metadataBytes), sizeof(uint64_t)); + + // Save metadata + { + const Array metadata = SerializeMetadata(); + Array compressed(ZSTD_compressBound(metadata.Size())); + size_t compressedSize = ZSTD_compress(compressed.Data(), compressed.Size(), metadata.Data(), metadata.Size(), 5); + if (ZSTD_isError(compressedSize)) throw std::runtime_error(std::string("Compression failed: ") + ZSTD_getErrorName(compressedSize)); + fs.write(compressed.Data(), compressedSize); + fs.write(reinterpret_cast(&compressedSize), sizeof(uint64_t)); + } + FontAtlasFileFlags flags; fs.write(reinterpret_cast(&flags), sizeof(FontAtlasFileFlags)); fs.close(); } + Array FontAtlas::SerializeMetadata() const + { + Array data(sizeof(Metadata) + m_glyphs.size() * (sizeof(GlyphInfo) + sizeof(uint32_t))); + char* ptr = data.Data(); + memcpy(ptr, &m_metadata, sizeof(Metadata)); + ptr += sizeof(Metadata); + for (const auto& [key, val] : m_glyphs) + { + memcpy(ptr, &key, sizeof(uint32_t)); + ptr += sizeof(uint32_t); + memcpy(ptr, &val, sizeof(GlyphInfo)); + ptr += sizeof(GlyphInfo); + } + return data; + } + + void FontAtlas::DeserializeMetadata(const std::span& data) + { + const char* d = data.data(); + const char* end = data.data() + data.size(); + uint32_t unicode = 0; + + std::memcpy(&m_metadata, d, sizeof(Metadata)); + d += sizeof(Metadata); + while (d < end) + { + std::memcpy(&unicode, d, sizeof(uint32_t)); + d += sizeof(uint32_t); + GlyphInfo& info = m_glyphs[unicode]; + std::memcpy(&info, d, sizeof(GlyphInfo)); + d += sizeof(GlyphInfo); + } + } + + void FontAtlas::Load(const std::span data) { if (data.size() < 16) { Logger::DATA->warn("Font atlas file is invalid!"); return; }; @@ -101,6 +143,15 @@ namespace OpenVulkano::Scene void FontAtlas::LoadNew(const std::span data) { + size_t originalSize = ZSTD_getFrameContentSize(data.data(), data.size()); + if (originalSize == ZSTD_CONTENTSIZE_ERROR) throw std::runtime_error("Not compressed by zstd"); + if (originalSize == ZSTD_CONTENTSIZE_UNKNOWN) throw std::runtime_error("Original size unknown"); + + Array metadata(originalSize); + size_t decompressedBytes = ZSTD_decompress(metadata.Data(), metadata.Size(), data.data(), data.size()); + if (ZSTD_isError(decompressedBytes)) throw std::runtime_error(std::string("Decompression failed: ") + ZSTD_getErrorName(decompressedBytes)); + if (decompressedBytes != originalSize) throw std::runtime_error("Decompressed size does not match expected size"); + DeserializeMetadata(metadata); } void FontAtlas::LoadImage(const std::span data) diff --git a/openVulkanoCpp/Scene/Text/FontAtlas.hpp b/openVulkanoCpp/Scene/Text/FontAtlas.hpp index 1cd1020..9249eb7 100644 --- a/openVulkanoCpp/Scene/Text/FontAtlas.hpp +++ b/openVulkanoCpp/Scene/Text/FontAtlas.hpp @@ -41,6 +41,9 @@ namespace OpenVulkano::Scene void LoadNew(std::span data); void LoadImage(std::span data); + Array SerializeMetadata() const; + void DeserializeMetadata(const std::span& data); + public: FontAtlas() = default; FontAtlas(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType) { Init(textureResolution, lineHeight, atlasType); }