split font atlas generator to interface and implementation

This commit is contained in:
ohyzha
2024-08-07 20:05:06 +03:00
parent 634b4431b3
commit 38d97f4c28
6 changed files with 160 additions and 101 deletions

View File

@@ -24,6 +24,7 @@
#include "Controller/FreeCamCameraController.hpp"
#include "Image/ImageLoaderPng.hpp"
#include "Scene/FontAtlasGenerator.hpp"
#include "Scene/MsdfFontAtlasGenerator.hpp"
#include <filesystem>
#ifdef _WIN32
@@ -67,7 +68,7 @@ namespace OpenVulkano
m_drawablesPool.resize(N);
#ifdef MSDFGEN_AVAILABLE
Charset charset = FontAtlasGenerator::LoadAllGlyphs(fontPath);
Charset charset = MsdfFontAtlasGenerator::LoadAllGlyphs(fontPath);
//Charset charset = Charset::ASCII;
//for (unicode_t c = 0x0410; c <= 0x041F; c++)
//{
@@ -133,7 +134,7 @@ namespace OpenVulkano
PerspectiveCamera m_cam;
OpenVulkano::FreeCamCameraController m_camController;
#ifdef MSDFGEN_AVAILABLE
FontAtlasGenerator m_atlasGenerator;
MsdfFontAtlasGenerator m_atlasGenerator;
#endif
std::vector<SimpleDrawable*> m_drawablesPool;
std::vector<Node> m_nodesPool;

View File

@@ -6,47 +6,27 @@
#pragma once
#if __has_include("msdfgen.h")
#include <string>
#include <optional>
#include <map>
#include <variant>
#include "Scene/AtlasMetadata.hpp"
#include "Scene/Texture.hpp"
#include "msdfgen.h"
#include "msdfgen-ext.h"
#include "msdf-atlas-gen/msdf-atlas-gen.h"
#include <set>
#include <memory>
namespace OpenVulkano::Scene
{
using namespace msdfgen;
using namespace msdf_atlas;
using namespace OpenVulkano::Scene;
class FontAtlasGenerator
{
public:
using SdfGenerator = ImmediateAtlasGenerator<float, 1, sdfGenerator, BitmapAtlasStorage<msdfgen::byte, 1>>;
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, 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; }
SdfGenerator& GetFontAtlsGenerator() { return m_generator; }
private:
void Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset,
const std::optional<std::string>& pngOutput);
void SavePng(const BitmapConstRef<byte, 1>& storage, const std::string& output, int channels) const;
private:
SdfGenerator m_generator;
Texture m_atlasTex;
AtlasMetadata m_meta;
std::map<uint32_t, GlyphInfo> m_symbols;
virtual void GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput = std::nullopt) = 0;
virtual void GenerateAtlas(const Array<char>& fontData, int length, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput = std::nullopt) = 0;
virtual void SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile = true) const = 0;
virtual const Texture& GetAtlas() const = 0;
virtual std::map<uint32_t, GlyphInfo>& GetGlyphsInfo() = 0;
virtual AtlasMetadata& GetAtlasMetadata() = 0;
};
}
#endif

View File

@@ -6,7 +6,7 @@
#if __has_include("msdfgen.h")
#include "FontAtlasGenerator.hpp"
#include "MsdfFontAtlasGenerator.hpp"
#include "Base/Logger.hpp"
#include "Scene/AtlasMetadata.hpp"
#define STBI_MSC_SECURE_CRT
@@ -22,7 +22,7 @@ namespace OpenVulkano::Scene
using namespace msdfgen;
using namespace msdf_atlas;
Charset FontAtlasGenerator::LoadAllGlyphs(const std::variant<std::string, Array<char>>& data)
Charset MsdfFontAtlasGenerator::LoadAllGlyphs(const std::variant<std::string, Array<char>>& data)
{
FT_Library library;
auto error = FT_Init_FreeType(&library);
@@ -55,41 +55,49 @@ namespace OpenVulkano::Scene
return s;
}
void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset,
void MsdfFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput)
{
if (charset.empty())
{
Logger::RENDER->info("Provided charset is empty. Atlas will not be generated");
return;
FreetypeHandle* ft;
FontHandle* font;
InitFreetypeFromFile(ft, font, fontFile);
Charset s;
std::for_each(s.begin(), s.end(), [&](uint32_t unicode) { s.add(unicode); });
Generate(ft, font, s, pngOutput);
}
void MsdfFontAtlasGenerator::GenerateAtlas(const Array<char>& fontData, int length,
const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput)
{
FreetypeHandle* ft;
FontHandle* font;
InitFreetypeFromBuffer(ft, font, (const msdfgen::byte*)(fontData.Data()), length);
Charset s;
std::for_each(s.begin(), s.end(), [&](uint32_t unicode) { s.add(unicode); });
Generate(ft, font, s, pngOutput);
}
void MsdfFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset,
const std::optional<std::string>& pngOutput)
{
// TODO: dynamic atlas and add only those symbols which are not present yet in current atlas
FreetypeHandle* ft = initializeFreetype();
if (!ft) { throw std::runtime_error("Failed to initialize freetype"); }
FontHandle* font = loadFont(ft, fontFile.data());
if (!font)
{
deinitializeFreetype(ft);
throw std::runtime_error(fmt::format("Failed to load font from file {0}", fontFile.data()));
}
FreetypeHandle* ft;
FontHandle* font;
InitFreetypeFromFile(ft, font, fontFile);
Generate(ft, font, charset, pngOutput);
}
void FontAtlasGenerator::GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset,
void MsdfFontAtlasGenerator::GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset,
const std::optional<std::string>& pngOutput)
{
FreetypeHandle* ft = initializeFreetype();
if (!ft) { throw std::runtime_error("Failed to initialize freetype"); }
FontHandle* font = loadFontData(ft, fontData, length);
if (!font)
{
deinitializeFreetype(ft);
throw std::runtime_error("Failed to load font data from given buffer");
}
FreetypeHandle* ft;
FontHandle* font;
InitFreetypeFromBuffer(ft, font, fontData, length);
Generate(ft, font, charset, pngOutput);
}
void FontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const
void MsdfFontAtlasGenerator::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const
{
if (m_symbols.empty())
{
@@ -126,7 +134,34 @@ namespace OpenVulkano::Scene
fs.write(reinterpret_cast<const char*>(&packedFlag), sizeof(uint32_t));
}
void FontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset,
void MsdfFontAtlasGenerator::InitFreetypeFromFile(FreetypeHandle*& ft, FontHandle*& font, const std::string& fontFile)
{
ft = initializeFreetype();
if (!ft) { throw std::runtime_error("Failed to initialize freetype"); }
font = loadFont(ft, fontFile.data());
if (!font)
{
deinitializeFreetype(ft);
ft = nullptr;
throw std::runtime_error(fmt::format("Failed to load font from file {0}", fontFile.data()));
}
}
void MsdfFontAtlasGenerator::InitFreetypeFromBuffer(FreetypeHandle*& ft, FontHandle*& font,
const msdfgen::byte* fontData, int length)
{
ft = initializeFreetype();
if (!ft) { throw std::runtime_error("Failed to initialize freetype"); }
font = loadFontData(ft, fontData, length);
if (!font)
{
deinitializeFreetype(ft);
ft = nullptr;
throw std::runtime_error("Failed to load font data from given buffer");
}
}
void MsdfFontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset,
const std::optional<std::string>& pngOutput)
{
m_symbols.clear();
@@ -220,7 +255,8 @@ namespace OpenVulkano::Scene
deinitializeFreetype(ft);
}
void FontAtlasGenerator::SavePng(const BitmapConstRef<byte, 1>& storage, const std::string& output, int channels) const
void MsdfFontAtlasGenerator::SavePng(const BitmapConstRef<byte, 1>& storage, const std::string& output,
int channels) const
{
stbi_flip_vertically_on_write(1);
if (std::filesystem::path(output).extension() == ".png")

View File

@@ -0,0 +1,60 @@
/*
* 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/.
*/
#pragma once
#if __has_include("msdfgen.h")
#include <string>
#include <optional>
#include <map>
#include <variant>
#include "Scene/AtlasMetadata.hpp"
#include "FontAtlasGenerator.hpp"
#include "Scene/Texture.hpp"
#include "msdfgen.h"
#include "msdfgen-ext.h"
#include "msdf-atlas-gen/msdf-atlas-gen.h"
#define MSDFGEN_AVAILABLE
namespace OpenVulkano::Scene
{
using namespace msdfgen;
using namespace msdf_atlas;
using namespace OpenVulkano::Scene;
class MsdfFontAtlasGenerator : public FontAtlasGenerator
{
public:
using SdfGenerator = ImmediateAtlasGenerator<float, 1, sdfGenerator, BitmapAtlasStorage<msdfgen::byte, 1>>;
static Charset LoadAllGlyphs(const std::variant<std::string, Array<char>>& data);
void GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput = std::nullopt) override;
void GenerateAtlas(const Array<char>& fontData, int length, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput = std::nullopt) override;
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, bool packIntoSingleFile = true) const override;
const Texture& GetAtlas() const override { return m_atlasTex; }
std::map<uint32_t, GlyphInfo>& GetGlyphsInfo() override { return m_symbols; }
AtlasMetadata& GetAtlasMetadata() override { return m_meta; }
SdfGenerator& GetFontAtlasGenerator() { return m_generator; }
private:
void InitFreetypeFromFile(FreetypeHandle*& ft, FontHandle*& font, const std::string& file);
void InitFreetypeFromBuffer(FreetypeHandle*& ft, FontHandle*& font, const msdfgen::byte* fontData, int length);
void Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset,
const std::optional<std::string>& pngOutput);
void SavePng(const BitmapConstRef<byte, 1>& storage, const std::string& output, int channels) const;
private:
SdfGenerator m_generator;
Texture m_atlasTex;
AtlasMetadata m_meta;
std::map<uint32_t, GlyphInfo> m_symbols;
};
}
#endif

View File

@@ -18,11 +18,6 @@
namespace OpenVulkano::Scene
{
#ifdef MSDFGEN_AVAILABLE
using namespace msdfgen;
using namespace msdf_atlas;
#endif
Shader& TextDrawable::GetDefaultShader()
{
static bool once = true;
@@ -113,7 +108,6 @@ namespace OpenVulkano::Scene
m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3);
}
#ifdef MSDFGEN_AVAILABLE
TextDrawable::TextDrawable(FontAtlasGenerator* fontAtlasGenerator, const TextConfig& config)
{
if (!fontAtlasGenerator) { throw std::runtime_error("FontAtlasGenerator is nullptr"); }
@@ -123,7 +117,6 @@ namespace OpenVulkano::Scene
m_material.texture = const_cast<Texture*>(&m_fontAtlasGenerator->GetAtlas());
m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3);
}
#endif
void TextDrawable::GenerateText(const std::string& text, const Math::Vector3f& pos)
{
@@ -135,32 +128,36 @@ namespace OpenVulkano::Scene
m_geometry.Close();
m_geometry.Init(text.size() * 4, text.size() * 6);
#ifdef MSDFGEN_AVAILABLE
// TODO: better implementation to decide what to use: data from atlas generator or data read from file
// we have msdf but loaded glyphs metadata from file before
std::optional<std::reference_wrapper<std::map<unicode_t, GlyphInfo>>> glyphData;
AtlasMetadata* meta;
std::map<uint32_t, GlyphInfo>* symbols;
if (m_fontAtlasGenerator)
{
if (m_fontAtlasGenerator->GetGlyphsInfo().empty() && !m_glyphs.empty())
{
// texture is set in ctor
glyphData = m_glyphs;
symbols = &m_glyphs;
}
else
{
// just in case if FontAtlasGenerator changed it's atlas
m_material.texture = const_cast<Texture*>(&m_fontAtlasGenerator->GetAtlas());
glyphData = m_fontAtlasGenerator->GetGlyphsInfo();
symbols = &m_fontAtlasGenerator->GetGlyphsInfo();
}
std::map<unicode_t, GlyphInfo>& symbols = glyphData.value();
AtlasMetadata& meta = m_fontAtlasGenerator->GetAtlasMetadata();
#else
std::map<unicode_t, GlyphInfo>& symbols = m_glyphs;
AtlasMetadata& meta = m_meta;
#endif
meta = &m_fontAtlasGenerator->GetAtlasMetadata();
}
else
{
symbols = &m_glyphs;
meta = &m_meta;
}
const Texture& atlasTex = *m_material.texture;
double cursorX = pos.x;
auto begin = text.begin();
auto end = text.end();
const double lineHeight = meta.lineHeight;
const double lineHeight = meta->lineHeight;
double posY = pos.y;
for (size_t i = 0; begin != end; i++)
{
@@ -171,11 +168,11 @@ namespace OpenVulkano::Scene
cursorX = pos.x;
continue;
}
if (symbols.find(c) != symbols.end())
if (symbols->find(c) != symbols->end())
{
uint32_t vIdx = i * 4;
uint32_t indices[] = { 1 + vIdx, 2 + vIdx, 3 + vIdx, 1 + vIdx, 3 + vIdx, 0 + vIdx };
GlyphInfo& info = symbols.at(c);
GlyphInfo& info = symbols->at(c);
// left bottom
m_geometry.vertices[vIdx].position.x = info.xyz[0].x + cursorX;

View File

@@ -19,20 +19,11 @@
#include "Base/Logger.hpp"
#include "AtlasMetadata.hpp"
#include "Image/Image.hpp"
#if __has_include("msdfgen.h")
#include "msdfgen.h"
#include "msdfgen-ext.h"
#include "msdf-atlas-gen/msdf-atlas-gen.h"
#define MSDFGEN_AVAILABLE 1
#endif
#include <map>
#include "FontAtlasGenerator.hpp"
namespace OpenVulkano::Scene
{
#ifdef MSDFGEN_AVAILABLE
using namespace msdfgen;
using namespace msdf_atlas;
#endif // MSDFGEN_AVAILABLE
struct TextConfig
{
Math::Vector4f textColor = { 1, 1, 1, 1 };
@@ -54,15 +45,12 @@ namespace OpenVulkano::Scene
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());
#ifdef MSDFGEN_AVAILABLE
TextDrawable(FontAtlasGenerator* fontAtlasGenerator, const TextConfig& config = TextConfig());
#endif
void GenerateText(const std::string& text, const Math::Vector3f& pos = Math::Vector3f(0.f));
void SetConfig(const TextConfig& cfg) { m_cfg = cfg; }
void SetShader(Shader* shader) { m_shader = shader; }
TextConfig& GetConfig() { return m_cfg; }
Shader* GetShader() { return m_shader; }
#ifdef MSDFGEN_AVAILABLE
void SetFontAtlasGenerator(FontAtlasGenerator* fontAtlasGenerator)
{
if (!fontAtlasGenerator || fontAtlasGenerator->GetGlyphsInfo().empty())
@@ -73,7 +61,6 @@ namespace OpenVulkano::Scene
m_fontAtlasGenerator = fontAtlasGenerator;
}
FontAtlasGenerator* GetFontAtlasGenerator() { return m_fontAtlasGenerator; }
#endif
private:
Geometry m_geometry;
Material m_material;
@@ -81,9 +68,7 @@ namespace OpenVulkano::Scene
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
Shader* m_shader = &GetDefaultShader();
TextConfig m_cfg;
};