Update atlas metadata encoding, now with compression
This commit is contained in:
@@ -13,4 +13,12 @@ namespace OpenVulkano
|
|||||||
template<typename T> using Ptr = std::shared_ptr<T>;
|
template<typename T> using Ptr = std::shared_ptr<T>;
|
||||||
template<typename T> using Weak = std::weak_ptr<T>;
|
template<typename T> using Weak = std::weak_ptr<T>;
|
||||||
template<typename T> using Unique = std::unique_ptr<T>;
|
template<typename T> using Unique = std::unique_ptr<T>;
|
||||||
|
|
||||||
|
template<typename T = void>
|
||||||
|
struct FreeDelete
|
||||||
|
{
|
||||||
|
void operator()(T* x) const { free(x); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> using UniqueMalloc = std::unique_ptr<T, FreeDelete<T>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
#include "FontAtlas.hpp"
|
#include "FontAtlas.hpp"
|
||||||
#include "Base/Logger.hpp"
|
#include "Base/Logger.hpp"
|
||||||
|
#include "Base/Wrapper.hpp"
|
||||||
#include "Image/ImageLoader.hpp"
|
#include "Image/ImageLoader.hpp"
|
||||||
#include "Extensions/STBZlibCompressor.hpp"
|
#include "Extensions/STBZlibCompressor.hpp"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <zstd.h>
|
||||||
#define STBI_MSC_SECURE_CRT
|
#define STBI_MSC_SECURE_CRT
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#define STBIW_ZLIB_COMPRESS Extensions::STBZlibCompressor
|
#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; };
|
if (!*this) { Logger::DATA->warn("Can't save empty font atlas!"); return; };
|
||||||
|
|
||||||
stbi_flip_vertically_on_write(1);
|
std::fstream fs(path, std::ios_base::out | std::ios_base::binary);
|
||||||
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::ios_base::app);
|
// Save image
|
||||||
fs.write(reinterpret_cast<const char*>(&m_metadata), sizeof(Metadata));
|
|
||||||
uint64_t metadataBytes = sizeof(Metadata);
|
|
||||||
for (const auto& [key, val] : m_glyphs)
|
|
||||||
{
|
{
|
||||||
fs.write(reinterpret_cast<const char*>(&key), sizeof(uint32_t));
|
stbi_flip_vertically_on_write(1);
|
||||||
fs.write(reinterpret_cast<const char*>(&val), sizeof(GlyphInfo));
|
int pngSize;
|
||||||
metadataBytes += sizeof(uint32_t) + sizeof(GlyphInfo);
|
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;
|
FontAtlasFileFlags flags;
|
||||||
fs.write(reinterpret_cast<const char*>(&flags), sizeof(FontAtlasFileFlags));
|
fs.write(reinterpret_cast<const char*>(&flags), sizeof(FontAtlasFileFlags));
|
||||||
fs.close();
|
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)
|
void FontAtlas::Load(const std::span<char> data)
|
||||||
{
|
{
|
||||||
if (data.size() < 16) { Logger::DATA->warn("Font atlas file is invalid!"); return; };
|
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)
|
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)
|
void FontAtlas::LoadImage(const std::span<char> data)
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ namespace OpenVulkano::Scene
|
|||||||
void LoadNew(std::span<char> data);
|
void LoadNew(std::span<char> data);
|
||||||
void LoadImage(std::span<char> data);
|
void LoadImage(std::span<char> data);
|
||||||
|
|
||||||
|
Array<char> SerializeMetadata() const;
|
||||||
|
void DeserializeMetadata(const std::span<char>& data);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FontAtlas() = default;
|
FontAtlas() = default;
|
||||||
FontAtlas(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType) { Init(textureResolution, lineHeight, atlasType); }
|
FontAtlas(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType) { Init(textureResolution, lineHeight, atlasType); }
|
||||||
|
|||||||
Reference in New Issue
Block a user