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 Weak = std::weak_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 "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)
|
||||
|
||||
@@ -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); }
|
||||
|
||||
Reference in New Issue
Block a user