Update atlas metadata encoding, now with compression

This commit is contained in:
Georg Hagen
2025-01-11 13:22:16 +01:00
parent 98db9f79fe
commit 9167bb82d0
3 changed files with 74 additions and 12 deletions

View File

@@ -6,9 +6,11 @@
#include "FontAtlas.hpp"
#include "Base/Logger.hpp"
#include "Base/Wrapper.hpp"
#include "Image/ImageLoader.hpp"
#include "Extensions/STBZlibCompressor.hpp"
#include <fstream>
#include <zstd.h>
#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<const char*>(&m_metadata), sizeof(Metadata));
uint64_t metadataBytes = sizeof(Metadata);
for (const auto& [key, val] : m_glyphs)
// Save image
{
fs.write(reinterpret_cast<const char*>(&key), sizeof(uint32_t));
fs.write(reinterpret_cast<const char*>(&val), sizeof(GlyphInfo));
metadataBytes += sizeof(uint32_t) + sizeof(GlyphInfo);
stbi_flip_vertically_on_write(1);
int pngSize;
UniqueMalloc<uint8_t> png(stbi_write_png_to_mem(static_cast<const uint8_t*>(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<const std::ostream::char_type*>(png.get()), pngSize);
}
fs.write(reinterpret_cast<const char*>(&metadataBytes), sizeof(uint64_t));
// Save metadata
{
const Array<char> metadata = SerializeMetadata();
Array<char> 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<const char*>(&compressedSize), sizeof(uint64_t));
}
FontAtlasFileFlags flags;
fs.write(reinterpret_cast<const char*>(&flags), sizeof(FontAtlasFileFlags));
fs.close();
}
Array<char> FontAtlas::SerializeMetadata() const
{
Array<char> 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<char>& 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<char> 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<char> 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<char> 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<char> data)

View File

@@ -41,6 +41,9 @@ namespace OpenVulkano::Scene
void LoadNew(std::span<char> data);
void LoadImage(std::span<char> data);
Array<char> SerializeMetadata() const;
void DeserializeMetadata(const std::span<char>& data);
public:
FontAtlas() = default;
FontAtlas(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType) { Init(textureResolution, lineHeight, atlasType); }