add opportunity to pack atlas and meta data in same file

This commit is contained in:
ohyzha
2024-08-06 17:02:24 +03:00
parent 9e9a76e459
commit c7c2a96b9c
8 changed files with 140 additions and 29 deletions

View File

@@ -85,6 +85,7 @@ function(LinkMsdf TARGET)
set(STATIC_LIB_EXT "a")
set(freetype_lib_name "libfreetype")
endif()
target_include_directories(${TARGET} PUBLIC "${VCPKG_SRC_DIR}/installed/${TRIPLET}/include")
# link freetype first to fix linkage issues on linux
target_link_libraries(${TARGET} PUBLIC "${VCPKG_SRC_DIR}/installed/${TRIPLET}/lib/${freetype_lib_name}.${STATIC_LIB_EXT}")
file(GLOB installed_libs "${VCPKG_SRC_DIR}/installed/${TRIPLET}/lib/*.${STATIC_LIB_EXT}")

View File

@@ -67,13 +67,15 @@ namespace OpenVulkano
m_drawablesPool.resize(N);
#ifdef MSDFGEN_AVAILABLE
Charset charset = Charset::ASCII;
for (unicode_t c = 0x0410; c <= 0x041F; c++)
{
// some unicode values
charset.add(c);
}
m_atlasGenerator.GenerateAtlas(fontPath, charset, "roboto-regular-atlas.png");
Charset charset = FontAtlasGenerator::LoadAllGlyphs(fontPath);
//Charset charset = Charset::ASCII;
//for (unicode_t c = 0x0410; c <= 0x041F; c++)
//{
// // some unicode values
// charset.add(c);
//}
m_atlasGenerator.GenerateAtlas(fontPath, charset);
m_atlasGenerator.SaveAtlasMetadataInfo("atlas_metadata", true);
#endif
for (int i = 0; i < texts.size(); i++)
@@ -81,16 +83,19 @@ namespace OpenVulkano
#ifdef MSDFGEN_AVAILABLE
TextDrawable* t = new TextDrawable(&m_atlasGenerator, texts[i].second);
#else
auto metadataInfo = resourceLoader.GetResource("atlas_metadata");
auto data = resourceLoader.GetResource("roboto-regular-atlas.png");
Image::ImageLoaderPng loader;
static auto image = loader.loadData(reinterpret_cast<uint8_t*>(data.Data()), data.Size());
static Texture tex;
tex.resolution = image->resolution;
tex.textureBuffer = image->data.Data();
tex.format = image->dataFormat;
tex.size = image->data.Size(); // 1 channel
TextDrawable* t = new TextDrawable(metadataInfo, &tex, texts[i].second);
auto metadataInfo = resourceLoader.GetResource("full_atlas_metadata_packed");
TextDrawable* t = new TextDrawable(metadataInfo, texts[i].second);
// OR use separate texture + metadata file
//auto metadataInfo = resourceLoader.GetResource("atlas_metadata");
//auto data = resourceLoader.GetResource("roboto-regular-atlas.png");
//Image::ImageLoaderPng loader;
//static auto image = loader.loadData(reinterpret_cast<uint8_t*>(data.Data()), data.Size());
//static Texture tex;
//tex.resolution = image->resolution;
//tex.textureBuffer = image->data.Data();
//tex.format = image->dataFormat;
//tex.size = image->data.Size(); // 1 channel
//TextDrawable* t = new TextDrawable(metadataInfo, &tex, texts[i].second);
#endif // MSDFGEN_AVAILABLE
t->GenerateText(texts[i].first);
m_drawablesPool[i] = t;

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

View File

@@ -7,6 +7,8 @@
#include "FontAtlasGenerator.hpp"
#include "Base/Logger.hpp"
#include "Scene/AtlasMetadata.hpp"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <fstream>
namespace OpenVulkano::Scene
@@ -14,7 +16,41 @@ namespace OpenVulkano::Scene
using namespace msdfgen;
using namespace msdf_atlas;
void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, const std::optional<std::string>& pngOutput)
Charset FontAtlasGenerator::LoadAllGlyphs(const std::variant<std::string, Array<char>>& data)
{
FT_Library library;
auto error = FT_Init_FreeType(&library);
if (error) { throw std::runtime_error("Could not initalize freetype library\n"); }
FT_Face face;
if (std::holds_alternative<std::string>(data))
{
error = FT_New_Face(library, std::get<0>(data).c_str(), 0, &face);
}
else
{
auto& arr = std::get<1>(data);
error = FT_New_Memory_Face(library, (const FT_Byte*)(arr.Data()), arr.Size(), 0, &face);
}
if (error == FT_Err_Unknown_File_Format) { throw std::runtime_error("Unknown font file format\n"); }
else if (error) { throw std::runtime_error("Font file could not be opened or read or it's corrupted\n"); }
// some fancy font without unicode charmap
if (face->charmap == nullptr) { throw std::runtime_error("Selected font doesn't contain unicode charmap"); }
Charset s;
FT_UInt glyphIndex;
FT_ULong unicode = FT_Get_First_Char(face, &glyphIndex);
while (glyphIndex != 0)
{
s.add(unicode);
unicode = FT_Get_Next_Char(face, unicode, &glyphIndex);
}
FT_Done_Face(face);
FT_Done_FreeType(library);
return s;
}
void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset,
const std::optional<std::string>& pngOutput)
{
if (charset.empty())
{
@@ -47,20 +83,43 @@ namespace OpenVulkano::Scene
Generate(ft, font, charset, pngOutput);
}
void FontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile) const
void FontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const
{
if (m_symbols.empty())
{
Logger::DATA->info("No glyphs loaded. Nothing to save.");
return;
}
std::fstream fs(outputFile.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
std::string fileName = outputFile;
int extraMode = std::ios_base::trunc;
uint32_t packedFlag = 0;
if (packIntoSingleFile)
{
size_t ext = outputFile.find_last_of('.');
if (ext == std::string::npos)
{
fileName += "_packed";
}
else
{
fileName.insert(ext - 1, "_packed");
}
savePng(m_generator.atlasStorage(), fileName.c_str());
extraMode = std::ios_base::app;
packedFlag = 1;
}
std::fstream fs(fileName.c_str(), std::ios_base::out | std::ios_base::binary | extraMode);
fs.write(reinterpret_cast<const char*>(&m_meta), sizeof(AtlasMetadata));
uint64_t metadataBytes = sizeof(AtlasMetadata);
for (const auto& [key, val] : m_symbols)
{
fs.write(reinterpret_cast<const char*>(&key), sizeof(uint32_t));
fs.write(reinterpret_cast<const char*>(&val), sizeof(GlyphInfo));
metadataBytes += sizeof(uint32_t);
metadataBytes += sizeof(GlyphInfo);
}
fs.write(reinterpret_cast<const char*>(&metadataBytes), sizeof(uint64_t));
fs.write(reinterpret_cast<const char*>(&packedFlag), sizeof(uint32_t));
}
void FontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset,
@@ -77,7 +136,10 @@ namespace OpenVulkano::Scene
int width = 1024, height = 1024;
packer.setDimensions(width, height);
// more value - more sdf impact
packer.setPixelRange(26.0);
// this setup is tricky. with low value and large amount of characters visible artifacts (extra lines) may appear.
// with high value and large amount of characters sdf deals huge impact and characters are not readable anymore.
const double pixelRange = std::min((width / (double)chset.size()) * 3, 26.0);
packer.setPixelRange(pixelRange);
packer.setMiterLimit(1.0);
packer.pack(glyphsGeometry.data(), glyphsGeometry.size());

View File

@@ -9,6 +9,7 @@
#include <string>
#include <optional>
#include <map>
#include <variant>
#include "Scene/AtlasMetadata.hpp"
#include "Scene/Texture.hpp"
#include "msdfgen.h"
@@ -24,11 +25,12 @@ namespace OpenVulkano::Scene
class FontAtlasGenerator
{
public:
static Charset LoadAllGlyphs(const std::variant<std::string, Array<char>>& data);
void GenerateAtlas(const std::string& fontFile, const Charset& charset = Charset::ASCII,
const std::optional<std::string>& pngOutput = std::nullopt);
void GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset = Charset::ASCII,
const std::optional<std::string>& pngOutput = std::nullopt);
void SaveAtlasMetadataInfo(const std::string& outputFile) const;
void SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile = true) const;
const Texture& GetAtlas() const { return m_atlasTex; }
std::map<uint32_t, GlyphInfo>& GetGlyphsInfo() { return m_symbols; }
AtlasMetadata& GetAtlasMetadata() { return m_meta; }

View File

@@ -14,6 +14,7 @@
#include "Host/ResourceLoader.hpp"
#include <fstream>
#include <utf8.h>
#include "Image/ImageLoader.hpp"
namespace OpenVulkano::Scene
{
@@ -40,6 +41,16 @@ namespace OpenVulkano::Scene
return textDefaultShader;
}
TextDrawable::TextDrawable(const Array<char>& atlasMetadata, const TextConfig& config)
: TextDrawable(atlasMetadata, nullptr, config)
{
}
TextDrawable::TextDrawable(const std::string& atlasMetadataFile, const TextConfig& config)
: TextDrawable(OpenVulkano::Utils::ReadFile(atlasMetadataFile), nullptr, config)
{
}
TextDrawable::TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config)
: TextDrawable(OpenVulkano::Utils::ReadFile(atlasMetadataFile), atlasTex, config)
{
@@ -47,20 +58,46 @@ namespace OpenVulkano::Scene
TextDrawable::TextDrawable(const Array<char>& atlasMetadata, Texture* atlasTex, const TextConfig& config)
{
size_t len = atlasMetadata.Size();
size_t read_bytes = 0;
uint32_t isPacked;
std::memcpy(&isPacked, atlasMetadata.Data() + (atlasMetadata.Size() - sizeof(uint32_t)), sizeof(uint32_t));
uint64_t metadataBytes;
std::memcpy(&metadataBytes, atlasMetadata.Data() + (atlasMetadata.Size() - sizeof(uint32_t) - sizeof(uint64_t)),
sizeof(uint64_t));
uint64_t offsetToMetadata = atlasMetadata.Size() - metadataBytes - sizeof(uint32_t) - sizeof(uint64_t);
if (isPacked)
{
m_material.texture = new Texture();
m_img = Image::IImageLoader::loadData((const uint8_t*) atlasMetadata.Data(),
offsetToMetadata);
m_material.texture->format = m_img->dataFormat;
m_material.texture->resolution = m_img->resolution;
m_material.texture->size = m_img->data.Size(); // 1 channel
m_material.texture->textureBuffer = m_img->data.Data();
}
else
{
if (atlasTex == nullptr) { throw std::runtime_error("Atlas texture cannot be null with non-packed atlas metadata"); }
m_material.texture = atlasTex;
}
// metadata info
size_t read_bytes = offsetToMetadata;
size_t readMetadataBytes = 0;
uint32_t unicode = 0;
std::memcpy(&m_meta, atlasMetadata.Data(), sizeof(AtlasMetadata));
std::memcpy(&m_meta, atlasMetadata.Data() + read_bytes, sizeof(AtlasMetadata));
read_bytes += sizeof(AtlasMetadata);
while (read_bytes < len)
readMetadataBytes += sizeof(AtlasMetadata);
while (readMetadataBytes < metadataBytes)
{
std::memcpy(&unicode, atlasMetadata.Data() + read_bytes, sizeof(uint32_t));
read_bytes += sizeof(uint32_t);
readMetadataBytes += sizeof(uint32_t);
GlyphInfo& info = m_glyphs[unicode];
std::memcpy(&info, atlasMetadata.Data() + read_bytes, sizeof(GlyphInfo));
read_bytes += sizeof(GlyphInfo);
readMetadataBytes += sizeof(GlyphInfo);
}
m_material.texture = atlasTex;
m_cfg = config;
m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3);
}

View File

@@ -18,6 +18,7 @@
#include "UniformBuffer.hpp"
#include "Base/Logger.hpp"
#include "AtlasMetadata.hpp"
#include "Image/Image.hpp"
#if __has_include("msdfgen.h")
#include "msdfgen.h"
#include "msdfgen-ext.h"
@@ -48,6 +49,8 @@ namespace OpenVulkano::Scene
{
public:
static Shader& GetDefaultShader();
TextDrawable(const Array<char>& atlasMetadata, const TextConfig& config = TextConfig());
TextDrawable(const std::string& atlasMetadataFile, const TextConfig& config = TextConfig());
TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config = TextConfig());
TextDrawable(const Array<char>& atlasMetadata, Texture* atlasTex, const TextConfig& config = TextConfig());
TextDrawable(const std::map<uint32_t, GlyphInfo>& glyphData, Texture* atlasTex, const TextConfig& config = TextConfig());
@@ -77,6 +80,7 @@ namespace OpenVulkano::Scene
UniformBuffer m_uniBuffer;
std::map<uint32_t, GlyphInfo> m_glyphs;
AtlasMetadata m_meta;
std::unique_ptr<Image::Image> m_img;
#ifdef MSDFGEN_AVAILABLE
FontAtlasGenerator* m_fontAtlasGenerator = nullptr;
#endif