Rework some text related functions

This commit is contained in:
Georg Hagen
2025-03-01 20:00:27 +01:00
parent bfff861673
commit 0a027b8bb7
8 changed files with 43 additions and 43 deletions

View File

@@ -20,6 +20,7 @@
#include "Controller/FreeCamCameraController.hpp" #include "Controller/FreeCamCameraController.hpp"
#include "Scene/Text/SdfFontAtlasGenerator.hpp" #include "Scene/Text/SdfFontAtlasGenerator.hpp"
#include "Scene/Text/BitmapFontAtlasGenerator.hpp" #include "Scene/Text/BitmapFontAtlasGenerator.hpp"
#include "Scene/Text/FontAtlasFactory.hpp"
#include <filesystem> #include <filesystem>
#ifdef _WIN32 #ifdef _WIN32
@@ -63,18 +64,13 @@ namespace OpenVulkano
constexpr int atlasesCount = 4; constexpr int atlasesCount = 4;
const int textsCount = texts.size(); const int textsCount = texts.size();
auto& resourceLoader = ResourceLoader::GetInstance();
const std::string fontPath = resourceLoader.GetResourcePath("Roboto-Regular.ttf");
m_nodesPool.resize(textsCount * atlasesCount); m_nodesPool.resize(textsCount * atlasesCount);
m_drawablesPool.resize(textsCount * atlasesCount); m_drawablesPool.resize(textsCount * atlasesCount);
if constexpr (CREATE_BITMAP_ATLAS) if constexpr (CREATE_BITMAP_ATLAS)
{ {
// ReSharper disable once CppDFAUnreachableCode // ReSharper disable once CppDFAUnreachableCode
std::set<uint32_t> s = BitmapFontAtlasGenerator::LoadAllGlyphs(fontPath); FontAtlasFactory().GetFontAtlas("Roboto-Regular", 14.0f, SubpixelLayout::RGB)->Save("bitmap_atlas_rgb.ovfont");
BitmapFontAtlasGenerator generator(FontPixelSizeConfig(), SubpixelLayout::RGB);
generator.GenerateAtlas(fontPath, s);
generator.GetAtlas()->Save("bitmap_atlas_packed.png");
} }
#if defined(MSDFGEN_AVAILABLE) && defined(CREATE_NEW_ATLAS) #if defined(MSDFGEN_AVAILABLE) && defined(CREATE_NEW_ATLAS)
@@ -83,7 +79,8 @@ namespace OpenVulkano
m_msdfAtlasGenerator.GenerateAtlas(fontPath, s); m_msdfAtlasGenerator.GenerateAtlas(fontPath, s);
m_atlasGenerator.GetAtlas()->Save("sdf_atlas_packed.png"); m_atlasGenerator.GetAtlas()->Save("sdf_atlas_packed.png");
m_msdfAtlasGenerator.GetAtlas()->Save("msdf_atlas_packed.png"); m_msdfAtlasGenerator.GetAtlas()->Save("msdf_atlas_packed.png");
#else #else
auto& resourceLoader = ResourceLoader::GetInstance();
auto sdfMetadataInfo = resourceLoader.GetResource("sdf_atlas_packed.png"); auto sdfMetadataInfo = resourceLoader.GetResource("sdf_atlas_packed.png");
auto msdfMetadataInfo = resourceLoader.GetResource("msdf_atlas_packed.png"); auto msdfMetadataInfo = resourceLoader.GetResource("msdf_atlas_packed.png");
auto bitmapMetadataInfo = resourceLoader.GetResource("bitmap_atlas_packed.png"); auto bitmapMetadataInfo = resourceLoader.GetResource("bitmap_atlas_packed.png");

View File

@@ -10,6 +10,7 @@
#include <iterator> #include <iterator>
#include <initializer_list> #include <initializer_list>
#include <cstddef> #include <cstddef>
#include <cinttypes>
#include <vector> #include <vector>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
@@ -324,6 +325,10 @@ namespace OpenVulkano
std::swap(size, other.size); std::swap(size, other.size);
} }
std::span<const uint8_t> AsBytes() const noexcept
{
return { reinterpret_cast<const uint8_t*>(data), size * sizeof(T) };
}
private: private:
void Resize(size_t newSize) void Resize(size_t newSize)
{ {

View File

@@ -14,16 +14,16 @@ namespace OpenVulkano::Scene
void BitmapFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset, void BitmapFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput) const std::optional<std::string>& pngOutput)
{ {
Generate(fontFile, charset, pngOutput); GenerateAtlas(Utils::ReadFile(fontFile), charset, pngOutput);
} }
void BitmapFontAtlasGenerator::GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset, void BitmapFontAtlasGenerator::GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput) const std::optional<std::string>& pngOutput)
{ {
Generate(fontData, charset, pngOutput); Generate({ reinterpret_cast<const uint8_t*>(fontData.Data()), fontData.Size() }, charset, pngOutput);
} }
void BitmapFontAtlasGenerator::Generate(const std::variant<std::string, Array<char>>& source, void BitmapFontAtlasGenerator::Generate(const std::span<const uint8_t>& fontData,
const std::set<uint32_t>& chset, const std::set<uint32_t>& chset,
const std::optional<std::string>& pngOutput) const std::optional<std::string>& pngOutput)
{ {
@@ -33,7 +33,7 @@ namespace OpenVulkano::Scene
return; return;
} }
const auto& [lib, face] = FontAtlasGeneratorBase::InitFreetype(source); const auto& [lib, face] = FontAtlasGeneratorBase::InitFreetype(fontData);
FT_Set_Pixel_Sizes(face.get(), 0, m_pixelSizeConfig.CalculatePixelSize()); FT_Set_Pixel_Sizes(face.get(), 0, m_pixelSizeConfig.CalculatePixelSize());
if (m_subpixelLayout != SubpixelLayout::UNKNOWN) if (m_subpixelLayout != SubpixelLayout::UNKNOWN)
{ {

View File

@@ -47,7 +47,7 @@ namespace OpenVulkano::Scene
void GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset, void GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
const std::optional<std::string>& pngOutput = std::nullopt) override; const std::optional<std::string>& pngOutput = std::nullopt) override;
private: private:
void Generate(const std::variant<std::string, Array<char>>& source, const std::set<uint32_t>& chset, const std::optional<std::string>& pngOutput); void Generate(const std::span<const uint8_t>& fontData, const std::set<uint32_t>& chset, const std::optional<std::string>& pngOutput);
void FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor); void FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor);
void FillSubpixelData(const FT_Bitmap& bitmap, const GlyphForPacking& glyph); void FillSubpixelData(const FT_Bitmap& bitmap, const GlyphForPacking& glyph);
FT_Int32 GetGlyphRenderMode() const; FT_Int32 GetGlyphRenderMode() const;

View File

@@ -40,7 +40,9 @@ namespace OpenVulkano::Scene
return nullptr; return nullptr;
} }
const std::set<uint32_t>& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontData) : charset); std::set<uint32_t> fallback;
if (charset.empty()) FontAtlasGeneratorBase::LoadAllGlyphs(fallback, fontData.AsBytes());
const std::set<uint32_t>& setRef = (charset.empty() ? fallback : charset);
FontIdentifier id(fontIdentifier, setRef, SubpixelLayout::UNKNOWN, 0, FontIdentifier id(fontIdentifier, setRef, SubpixelLayout::UNKNOWN, 0,
msdf ? FontAtlasType::MSDF : FontAtlasType::SDF); msdf ? FontAtlasType::MSDF : FontAtlasType::SDF);
@@ -53,11 +55,11 @@ namespace OpenVulkano::Scene
if (msdf) if (msdf)
{ {
MsdfFontAtlasGenerator msdfGen; MsdfFontAtlasGenerator msdfGen;
msdfGen.GenerateAtlas(fontData, setRef); msdfGen.GenerateAtlas(fontData, charset);
return m_atlasesCache.insert({ id, msdfGen.GetAtlas() }).first->second; return m_atlasesCache.insert({ id, msdfGen.GetAtlas() }).first->second;
} }
SdfFontAtlasGenerator sdfGen; SdfFontAtlasGenerator sdfGen;
sdfGen.GenerateAtlas(fontData, setRef); sdfGen.GenerateAtlas(fontData, charset);
return m_atlasesCache.insert({ id, sdfGen.GetAtlas() }).first->second; return m_atlasesCache.insert({ id, sdfGen.GetAtlas() }).first->second;
} }
@@ -72,7 +74,9 @@ namespace OpenVulkano::Scene
return nullptr; return nullptr;
} }
const std::set<uint32_t>& setRef = (charset.empty() ? FontAtlasGeneratorBase::LoadAllGlyphs(fontData) : charset); std::set<uint32_t> fallback;
if (charset.empty()) FontAtlasGeneratorBase::LoadAllGlyphs(fallback, fontData.AsBytes());
const std::set<uint32_t>& setRef = (charset.empty() ? fallback : charset);
FontIdentifier id(fontIdentifier, setRef, subpixelLayout, ptSize, FontIdentifier id(fontIdentifier, setRef, subpixelLayout, ptSize,
subpixelLayout ? FontAtlasType::BITMAP_SUBPIXEL : FontAtlasType::BITMAP); subpixelLayout ? FontAtlasType::BITMAP_SUBPIXEL : FontAtlasType::BITMAP);

View File

@@ -12,39 +12,32 @@
namespace OpenVulkano::Scene namespace OpenVulkano::Scene
{ {
std::pair<FtLibraryRecPtr, FtFaceRecPtr> std::pair<FtLibraryRecPtr, FtFaceRecPtr>
FontAtlasGeneratorBase::InitFreetype(const std::variant<std::string, Array<char>>& source) FontAtlasGeneratorBase::InitFreetype(const std::span<const uint8_t>& data)
{ {
std::pair<FtLibraryRecPtr, FtFaceRecPtr> result;
FT_Library library; FT_Library library;
auto error = FT_Init_FreeType(&library); auto error = FT_Init_FreeType(&library);
if (error) if (error)
{ {
throw std::runtime_error(fmt::format("Could not initalize freetype library. {}", GetFreetypeErrorDescription(error))); throw std::runtime_error(fmt::format("Could not initialize freetype library. Error: {}", GetFreetypeErrorDescription(error)));
} }
result.first = FtLibraryRecPtr(library);
FT_Face face; FT_Face face;
if (std::holds_alternative<std::string>(source)) error = FT_New_Memory_Face(library, data.data(), data.size(), 0, &face);
if (error)
{ {
error = FT_New_Face(library, std::get<0>(source).c_str(), 0, &face); if (error == FT_Err_Unknown_File_Format) throw std::runtime_error("Unknown font file format");
} throw std::runtime_error(fmt::format("Font file could not be read or is corrupted. Error: {}", GetFreetypeErrorDescription(error)));
else
{
auto& arr = std::get<1>(source);
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(fmt::format("Font file could not be opened or read or it's corrupted. {}", GetFreetypeErrorDescription(error)));
} }
result.second = FtFaceRecPtr(face);
// some fancy font without unicode charmap // some fancy font without unicode charmap
if (face->charmap == nullptr) if (face->charmap == nullptr)
{ {
throw std::runtime_error("Selected font doesn't contain unicode charmap"); throw std::runtime_error("Selected font doesn't contain unicode charmap");
} }
return { FtLibraryRecPtr(library), FtFaceRecPtr(face) }; return result;
} }
std::string FontAtlasGeneratorBase::GetFreetypeErrorDescription(FT_Error error) std::string FontAtlasGeneratorBase::GetFreetypeErrorDescription(FT_Error error)
@@ -88,18 +81,16 @@ namespace OpenVulkano::Scene
info.advance = advance; info.advance = advance;
} }
std::set<uint32_t> FontAtlasGeneratorBase::LoadAllGlyphs(const std::variant<std::string, Array<char>>& data) size_t FontAtlasGeneratorBase::LoadAllGlyphs(std::set<uint32_t>& chars, const FtFaceRecPtr& face)
{ {
const auto& [lib, face] = InitFreetype(data); chars.clear();
std::set<uint32_t> s;
FT_UInt glyphIndex; FT_UInt glyphIndex;
FT_ULong unicode = FT_Get_First_Char(face.get(), &glyphIndex); FT_ULong unicode = FT_Get_First_Char(face.get(), &glyphIndex);
while (glyphIndex != 0) while (glyphIndex != 0)
{ {
s.insert(unicode); chars.insert(unicode);
unicode = FT_Get_Next_Char(face.get(), unicode, &glyphIndex); unicode = FT_Get_Next_Char(face.get(), unicode, &glyphIndex);
} }
return s; return chars.size();
} }
} }

View File

@@ -9,7 +9,6 @@
#include "IFontAtlasGenerator.hpp" #include "IFontAtlasGenerator.hpp"
#include "Math/AABB.hpp" #include "Math/AABB.hpp"
#include "Extensions/FreetypeHelper.hpp" #include "Extensions/FreetypeHelper.hpp"
#include <variant>
namespace OpenVulkano::Scene namespace OpenVulkano::Scene
{ {
@@ -25,11 +24,12 @@ namespace OpenVulkano::Scene
FontAtlasGeneratorBase(const int channelsCount) : m_channelsCount(channelsCount) {} FontAtlasGeneratorBase(const int channelsCount) : m_channelsCount(channelsCount) {}
[[nodiscard]] const std::shared_ptr<FontAtlas>& GetAtlas() const final { return m_atlasData; } [[nodiscard]] const std::shared_ptr<FontAtlas>& GetAtlas() const final { return m_atlasData; }
[[nodiscard]] int GetAtlasChannelsCount() const { return m_channelsCount; } [[nodiscard]] int GetAtlasChannelsCount() const { return m_channelsCount; }
[[nodiscard]] static std::set<uint32_t> LoadAllGlyphs(const std::variant<std::string, Array<char>>& data); static size_t LoadAllGlyphs(std::set<uint32_t>& chars, const std::span<const uint8_t>& fontData) { auto [lib, face] = InitFreetype(fontData); return LoadAllGlyphs(chars, face); }
static size_t LoadAllGlyphs(std::set<uint32_t>& chars, const FtFaceRecPtr& face);
protected: protected:
void SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size, const Math::AABB& aabb, double advance); void SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size, const Math::AABB& aabb, double advance);
[[nodiscard]] static std::string GetFreetypeErrorDescription(FT_Error error); [[nodiscard]] static std::string GetFreetypeErrorDescription(FT_Error error);
[[nodiscard]] static std::pair<FtLibraryRecPtr, FtFaceRecPtr> InitFreetype(const std::variant<std::string, Array<char>>& source); [[nodiscard]] static std::pair<FtLibraryRecPtr, FtFaceRecPtr> InitFreetype(const std::span<const uint8_t>& data);
}; };
} }

View File

@@ -120,7 +120,9 @@ TEST_CASE("Timestamp Formatter", "[fmt]")
TEST_CASE("Numeric Type Formatters", "[fmt]") TEST_CASE("Numeric Type Formatters", "[fmt]")
{ {
//TODO figure this out
#ifndef __APPLE__ #ifndef __APPLE__
#ifndef __clang__
SECTION("float16") SECTION("float16")
{ {
float16 f(1.5f); float16 f(1.5f);
@@ -128,6 +130,7 @@ TEST_CASE("Numeric Type Formatters", "[fmt]")
CHECK((formatted == "1.5" || formatted == "1.500000")); CHECK((formatted == "1.5" || formatted == "1.500000"));
CHECK(fmt::format("{:.1f}", f) == "1.5"); CHECK(fmt::format("{:.1f}", f) == "1.5");
} }
#endif
#endif #endif
SECTION("int24") SECTION("int24")