diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 92c9d9fd6..f43254bd3 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -31,7 +31,7 @@ jobs: sudo apt-get update sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev libglew2.2 \ libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev \ - libcurl4-openssl-dev libgtest-dev cmake squashfs-tools valgrind + libcurl4-openssl-dev libgtest-dev libfreetype6-dev cmake squashfs-tools valgrind # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 652cc26f9..f41e940e9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -39,7 +39,7 @@ jobs: # make && make install INSTALL_INC=/usr/include/lua run: | sudo apt-get update - sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libgtest-dev libcurl4-openssl-dev + sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libgtest-dev libcurl4-openssl-dev libfreetype6-dev # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua-5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9fdd8d0b3..c3f5156f9 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -23,7 +23,7 @@ jobs: - name: Install dependencies from brew run: | - brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest glm + brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest glm freetype - name: Configure run: | diff --git a/Dockerfile b/Dockerfile index e067b92d1..da90324fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ libluajit-5.1-dev \ libvorbis-dev \ libcurl4-openssl-dev \ + libfreetype6-dev \ ca-certificates \ wget \ && rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index 1221148c3..93e597fb6 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,13 @@ sudo make install ```sh su - -apt-get install entt-devel libglfw3-devel libGLEW-devel libglm-devel libpng-devel libvorbis-devel libopenal-devel libluajit-devel libstdc++13-devel-static libcurl-devel +apt-get install entt-devel libglfw3-devel libGLEW-devel libglm-devel libpng-devel libvorbis-devel libopenal-devel libluajit-devel libstdc++13-devel-static libcurl-devel libfreetype-devel ``` #### Debian based distros ```sh -sudo apt install libglfw3 libglfw3-dev libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev +sudo apt install libglfw3 libglfw3-dev libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libfreetype6-dev ``` > [!TIP] @@ -50,7 +50,7 @@ sudo apt install libglfw3 libglfw3-dev libglew-dev libglm-dev libpng-dev libopen #### RHEL based distros ```sh -sudo dnf install glfw-devel glew-devel glm-devel libpng-devel libvorbis-devel openal-soft-devel luajit-devel libcurl-devel +sudo dnf install glfw-devel glew-devel glm-devel libpng-devel libvorbis-devel openal-soft-devel luajit-devel libcurl-devel libfreetype-devel ``` #### Arch based distros @@ -58,13 +58,13 @@ sudo dnf install glfw-devel glew-devel glm-devel libpng-devel libvorbis-devel op If you use X11: ```sh -sudo pacman -S glfw-x11 glew glm libpng libvorbis openal luajit libcurl +sudo pacman -S glfw-x11 glew glm libpng libvorbis openal luajit libcurl freetype2 ``` If you use Wayland: ```sh -sudo pacman -S glfw-wayland glew glm libpng libvorbis openal luajit libcurl +sudo pacman -S glfw-wayland glew glm libpng libvorbis openal luajit libcurl freetype2 ``` And install EnTT: @@ -94,7 +94,7 @@ cmake --build . --parallel ### Install libraries ```sh -brew install glfw3 glew glm libpng libvorbis lua luajit libcurl openal-soft skypjack/entt/entt +brew install glfw3 glew glm libpng libvorbis lua luajit libcurl openal-soft skypjack/entt/entt freetype ``` > [!TIP] diff --git a/dev/AppImageBuilder.yml b/dev/AppImageBuilder.yml index 16fcf57a7..b2c7f09ec 100644 --- a/dev/AppImageBuilder.yml +++ b/dev/AppImageBuilder.yml @@ -31,6 +31,7 @@ AppDir: - libvorbis0a - libvorbisfile3 - libluajit-5.1-2 + - libfreetype6 exclude: - hicolor-icon-theme - sound-theme-freedesktop @@ -45,6 +46,9 @@ AppDir: - kwin-style-breeze - kde-style-breeze - plasma-integration + - libllvm15 + - libicu70 + - libxml2 files: exclude: - usr/share/man diff --git a/res/layouts/templates/binding.xml b/res/layouts/templates/binding.xml index 928437498..f9646a9f1 100644 --- a/res/layouts/templates/binding.xml +++ b/res/layouts/templates/binding.xml @@ -1,5 +1,5 @@ - + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 226ac8c25..d6f4c5b74 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) find_package(CURL REQUIRED) find_package(glfw3 REQUIRED) +find_package(Freetype REQUIRED) if(NOT APPLE) find_package(EnTT REQUIRED) endif() @@ -75,6 +76,7 @@ target_link_libraries( Vorbis::vorbis Vorbis::vorbisfile luajit::luajit + Freetype::Freetype PUBLIC glm::glm # Need public for src/delegates.hpp, which including to # main.cpp ) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 4d943ae95..774fa1a00 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -187,6 +187,12 @@ void AssetsLoader::processPreload( config = std::make_shared(squashed); break; } + case AssetType::FONT: { + int size = DEFAULT_FONT_SIZE; + map.at("size").get(size); + config = std::make_unique(size); + break; + } default: break; } diff --git a/src/assets/AssetsLoader.hpp b/src/assets/AssetsLoader.hpp index 3d1704545..e237e01e9 100644 --- a/src/assets/AssetsLoader.hpp +++ b/src/assets/AssetsLoader.hpp @@ -33,8 +33,7 @@ struct LayoutCfg : AssetCfg { gui::GUI* gui; scriptenv env; - LayoutCfg(gui::GUI* gui, scriptenv env) : gui(gui), env(std::move(env)) { - } + LayoutCfg(gui::GUI* gui, scriptenv env) : gui(gui), env(std::move(env)) {} }; struct SoundCfg : AssetCfg { @@ -43,6 +42,12 @@ struct SoundCfg : AssetCfg { SoundCfg(bool keepPCM) : keepPCM(keepPCM) {} }; +struct FontCfg : AssetCfg { + int size; + + FontCfg(int size) : size(size) {} +}; + enum class AtlasType { ATLAS, SEPARATE }; diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 4bd32d5d0..6a301ec62 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -13,6 +13,7 @@ #include "coders/obj.hpp" #include "coders/vcm.hpp" #include "coders/vec3.hpp" +#include "coders/vector_fonts.hpp" #include "constants.hpp" #include "debug/Logger.hpp" #include "engine/EnginePaths.hpp" @@ -199,8 +200,24 @@ assetload::postfunc assetload::font( const ResPaths& paths, const std::string& filename, const std::string& name, - const std::shared_ptr& + const std::shared_ptr& config ) { + auto cfg = std::dynamic_pointer_cast(config); + auto ext = fs::path(filename).extension().string(); + if (ext == ".ttf" || ext == ".otf") { + logger.info() << "loading vector font " << util::quote(filename); + return [=](Assets* assets) { + using FontFile = vector_fonts::FontFile; + FontFile* fontFile; + if ((fontFile = assets->get(filename)) == nullptr) { + auto fontFilePtr = vector_fonts::load_font(paths.find(filename).string()); + fontFile = fontFilePtr.get(); + assets->store(fontFilePtr, filename); + } + assets->store(fontFile->createInstance(cfg ? cfg->size : 16), name); + }; + } + auto pages = std::make_shared>>(); for (size_t i = 0; i <= 1024; i++) { std::string pagefile = filename + "_" + std::to_string(i) + ".png"; @@ -214,20 +231,7 @@ assetload::postfunc assetload::font( } } return [=](auto assets) { - int res = pages->at(0)->getHeight() / 16; - std::vector> textures; - for (auto& page : *pages) { - if (page == nullptr) { - textures.emplace_back(nullptr); - } else { - auto texture = Texture::from(page.get()); - texture->setMipMapping(false, true); - textures.emplace_back(std::move(texture)); - } - } - assets->store( - std::make_unique(std::move(textures), res, 4), name - ); + assets->store(Font::createBitmapFont(std::move(*pages)), name); }; } diff --git a/src/coders/vector_fonts.cpp b/src/coders/vector_fonts.cpp new file mode 100644 index 000000000..ad7706e6b --- /dev/null +++ b/src/coders/vector_fonts.cpp @@ -0,0 +1,180 @@ +#include "vector_fonts.hpp" + +#include "io/io.hpp" +#include "debug/Logger.hpp" +#include "graphics/core/ImageData.hpp" +#include "graphics/core/Texture.hpp" +#include "graphics/core/Font.hpp" +#include "constants.hpp" + +#include +#include +#include FT_FREETYPE_H + +static debug::Logger logger("vector_fonts"); + +static FT_Library library; + +static const char* get_ft_error_message(FT_Error error) { + #undef FTERRORS_H_ + #define FT_ERRORDEF( e, v, s ) case v: return s; + #define FT_ERROR_START_LIST switch (error) { + #define FT_ERROR_END_LIST } + #include FT_ERRORS_H + return "unknown error"; +} + +namespace { + class FTFontFile : public vector_fonts::FontFile, + public std::enable_shared_from_this { + public: + FTFontFile(FT_Face face, util::Buffer buffer) + : face(std::move(face)), buffer(std::move(buffer)) { + } + + ~FTFontFile() { + if (FT_Error error = FT_Done_Face(face)) { + logger.error() + << "error on FT_Done_Face: " << get_ft_error_message(error); + } + } + + std::unique_ptr createInstance(int size) override { + if (FT_Error error = FT_Set_Pixel_Sizes(face, 0, size)) { + throw std::runtime_error( + "FT_Set_Pixel_Sizes error: " + + std::string(get_ft_error_message(error)) + ); + } + + std::vector> pages; + std::vector glyphs; + + int dstSize = size * 16; + ImageData canvas(ImageFormat::RGBA8888, dstSize, dstSize); + ImageData bitmapDst(ImageFormat::RGBA8888, size, size); + + for (int pageid = 0; pageid < DEFAULT_PRE_RENDER_FONT_PAGES; pageid++) { + renderPage(pageid, canvas, bitmapDst, glyphs); + pages.push_back(Texture::from(&canvas)); + } + return std::make_unique( + std::move(pages), + std::move(glyphs), + size, + size / 2, + shared_from_this() + ); + } + + std::unique_ptr renderPage( + int pageid, std::vector& glyphs, int size + ) override { + if (FT_Error error = FT_Set_Pixel_Sizes(face, 0, size)) { + throw std::runtime_error( + "FT_Set_Pixel_Sizes error: " + + std::string(get_ft_error_message(error)) + ); + } + + int dstSize = size * 16; + ImageData canvas(ImageFormat::RGBA8888, dstSize, dstSize); + ImageData bitmapDst(ImageFormat::RGBA8888, size, size); + + renderPage(pageid, canvas, bitmapDst, glyphs); + return Texture::from(&canvas); + } + private: + FT_Face face; + util::Buffer buffer; + + bool renderGlyph(int codepoint, ImageData& bitmapDst) { + int width = bitmapDst.getWidth(); + int height = bitmapDst.getHeight(); + + if (FT_Error error = FT_Load_Char(face, codepoint, FT_LOAD_RENDER)) { + logger.warning() << get_ft_error_message(error); + return false; + } + auto dstData = bitmapDst.getData(); + std::memset(dstData, 0, bitmapDst.getDataSize()); + if (face->glyph->bitmap.width == 0) { + return false; + } + const FT_Bitmap& bitmap = face->glyph->bitmap; + for (int row = 0; row < std::min(bitmap.rows, height); row++) { + for (int col = 0; col < std::min(bitmap.width, width); col++) { + uint8_t value = bitmap.buffer[row * bitmap.pitch + col]; + dstData[(row * width + col) * 4 + 0] = 255; + dstData[(row * width + col) * 4 + 1] = 255; + dstData[(row * width + col) * 4 + 2] = 255; + dstData[(row * width + col) * 4 + 3] = value; + } + } + return true; + } + + void renderPage( + int pageid, + ImageData& canvas, + ImageData& bitmapDst, + std::vector& glyphs + ) { + int size = canvas.getWidth() / 16; + for (int c = 0; c < 256; c++) { + int codepoint = pageid << 8 | c; + if (!renderGlyph(codepoint, bitmapDst)) { + glyphs.push_back(Glyph {0, size / 2}); + continue; + } + + canvas.blit(bitmapDst, (c % 16) * size, (c / 16) * size); + + Glyph glyph { + face->glyph->bitmap_top - size, + static_cast(face->glyph->advance.x >> 6) + }; + if (codepoint < glyphs.size()) { + glyphs[codepoint] = std::move(glyph); + } else { + glyphs.push_back(std::move(glyph)); + } + } + canvas.flipY(); + } + }; +} + +void vector_fonts::initialize() { + if (FT_Error error = FT_Init_FreeType(&library)) { + throw std::runtime_error( + "could not initialize freetype: " + + std::string(get_ft_error_message(error)) + ); + } +} + +void vector_fonts::finalize() { + if (FT_Error error = FT_Done_FreeType(library)) { + logger.error() << "error on FT_Done_FreeType: " + << get_ft_error_message(error); + } +} + +std::shared_ptr vector_fonts::load_font( + const std::string& filename +) { + auto bytes = io::read_bytes_buffer(io::path(filename)); + + FT_Face face; + + if (FT_Error error = + FT_New_Memory_Face(library, bytes.data(), bytes.size(), 0, &face)) { + throw std::runtime_error( + "could not load font '" + filename + + "': " + std::string(get_ft_error_message(error)) + ); + } + + return std::make_shared(std::move(face), std::move(bytes)); +} diff --git a/src/coders/vector_fonts.hpp b/src/coders/vector_fonts.hpp new file mode 100644 index 000000000..a28f3c5bf --- /dev/null +++ b/src/coders/vector_fonts.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +class Font; +struct Glyph; + +class Texture; + +namespace vector_fonts { + class FontFile { + public: + virtual ~FontFile() {} + + virtual std::unique_ptr createInstance(int size) = 0; + + virtual std::unique_ptr renderPage( + int pageid, std::vector& glyphs, int size + ) = 0; + }; + + void initialize(); + void finalize(); + + std::shared_ptr load_font(const std::string& filename); +} diff --git a/src/constants.hpp b/src/constants.hpp index 1ae27a545..501c5f62e 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -63,6 +63,10 @@ inline constexpr int TRANSLUCENT_BLOCKS_SORT_INTERVAL = 8; inline constexpr int ATLAS_EXTRUSION = 2; +inline constexpr int DEFAULT_FONT_SIZE = 16; + +inline constexpr int DEFAULT_PRE_RENDER_FONT_PAGES = 1; // must be at least 1 + inline const std::string SHADERS_FOLDER = "shaders"; inline const std::string TEXTURES_FOLDER = "textures"; inline const std::string FONTS_FOLDER = "fonts"; diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index e793224a9..89a345ff6 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -9,6 +9,7 @@ #include "coders/commons.hpp" #include "coders/GLSLExtension.hpp" #include "coders/toml.hpp" +#include "coders/vector_fonts.hpp" #include "content/ContentControl.hpp" #include "core_defs.hpp" #include "debug/Logger.hpp" @@ -167,6 +168,8 @@ void Engine::initialize(CoreParameters coreParameters) { settings.audio ); + vector_fonts::initialize(); + if (settings.ui.language.get() == "auto") { settings.ui.language.set( langs::locale_by_envlocale(platform::detect_locale()) @@ -342,6 +345,9 @@ void Engine::close() { gui.reset(); logger.info() << "gui finished"; } + if (!isHeadless()) { + vector_fonts::finalize(); + } audio::close(); debuggingServer.reset(); network.reset(); diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index 73718b4fa..3f0b4b69b 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -1,18 +1,31 @@ #include "Font.hpp" +#include "Batch2D.hpp" +#include "Batch3D.hpp" +#include "coders/vector_fonts.hpp" +#include "Texture.hpp" +#include "window/Camera.hpp" + #include #include -#include "Texture.hpp" -#include "Batch2D.hpp" -#include "Batch3D.hpp" -#include "window/Camera.hpp" inline constexpr uint GLYPH_SIZE = 16; inline constexpr uint MAX_CODEPAGES = 10000; // idk ho many codepages unicode has inline constexpr glm::vec4 SHADOW_TINT(0.0f, 0.0f, 0.0f, 1.0f); -Font::Font(std::vector> pages, int lineHeight, int yoffset) - : lineHeight(lineHeight), yoffset(yoffset), pages(std::move(pages)) { +Font::Font( + std::vector> pages, + std::vector glyphs, + int lineHeight, + int yoffset, + std::optional> fontFile +) + : lineHeight(lineHeight), + yoffset(yoffset), + glyphInterval(lineHeight / 2), + pages(std::move(pages)), + glyphs(std::move(glyphs)), + fontFile(std::move(fontFile)) { } Font::~Font() = default; @@ -39,7 +52,22 @@ bool Font::isPrintableChar(uint codepoint) const { } int FontMetrics::calcWidth(std::wstring_view text, size_t offset, size_t length) const { - return std::min(text.length() - offset, length) * glyphInterval; + auto font = this->font.has_value() ? this->font->lock() : nullptr; + if (font == nullptr) { + return std::min(text.length() - offset, length) * _glyphInterval; + } + int totalWidth = 0; + for (int i = offset; i < offset + length && i < text.length(); i++) { + auto codepoint = text[i]; + if (!font->isPrintableChar(codepoint)) { + totalWidth += _glyphInterval; + } else if (auto glyph = font->getGlyph(codepoint)) { + totalWidth += glyph->xAdvance; + } else { + totalWidth += _glyphInterval; + } + } + return totalWidth; } int Font::calcWidth(std::wstring_view text, size_t length) const { @@ -71,7 +99,7 @@ static inline void draw_glyph( batch.sprite( pos.x + (offset.x + i / (right.x/glyphInterval/2.0f)) * right.x, - pos.y + offset.y * right.y, + pos.y + offset.y * up.y, right.x / glyphInterval, up.y, -0.15f * style.italic, @@ -100,7 +128,6 @@ static inline void draw_glyph( } else { color = style.color; } - batch.sprite( pos + right * (offset.x + i) + up * offset.y, @@ -116,7 +143,7 @@ static inline void draw_glyph( template static inline void draw_text( - const Font& font, + Font& font, Batch& batch, std::wstring_view text, const glm::vec3& pos, @@ -134,10 +161,12 @@ static inline void draw_text( uint page = 0; uint next = MAX_CODEPAGES; - int x = 0; + float x = 0; int y = 0; bool hasLines = false; + float baseAdvance = glm::length(right); + do { for (size_t i = 0; i < text.length(); i++) { uint c = text[i]; @@ -152,17 +181,23 @@ static inline void draw_text( x++; continue; } + int yOffset = 0; + float advance = baseAdvance; + if (auto glyph = font.getGlyph(c)) { + yOffset = glyph->yOffset; + advance = glyph->xAdvance; + } uint charpage = c >> 8; if (charpage == page){ batch.texture(font.getPage(charpage)); draw_glyph( - batch, pos, glm::vec2(x, y), c, right, up, interval, style + batch, pos, glm::vec2(x, y - yOffset / static_cast(font.getLineHeight())), c, right, up, interval, style ); } else if (charpage > page && charpage < next){ next = charpage; } - x++; + x += advance / baseAdvance; } page = next; next = MAX_CODEPAGES; @@ -213,7 +248,7 @@ void Font::draw( const FontStylesScheme* styles, size_t styleMapOffset, float scale -) const { +) { draw_text( *this, batch, text, glm::vec3(x, y, 0), @@ -233,7 +268,7 @@ void Font::draw( const glm::vec3& pos, const glm::vec3& right, const glm::vec3& up -) const { +) { draw_text( *this, batch, text, pos, right * static_cast(glyphInterval), @@ -243,3 +278,50 @@ void Font::draw( styleMapOffset ); } + +std::unique_ptr Font::createBitmapFont( + std::vector> pages +) { + int res = pages.at(0)->getHeight() / 16; + std::vector> textures; + std::vector glyphs(textures.size() * 256); + for (auto& glyph : glyphs) { + glyph.yOffset = 0; + glyph.xAdvance = res / 2; + } + for (auto& page : pages) { + if (page == nullptr) { + textures.emplace_back(nullptr); + } else { + auto texture = Texture::from(page.get()); + texture->setMipMapping(false, true); + textures.emplace_back(std::move(texture)); + } + } + return std::make_unique(std::move(textures), std::move(glyphs), res, 4); +} + +const Glyph* Font::getGlyph(int codepoint) { + if (codepoint < 0) { + return nullptr; + } + if (codepoint < glyphs.size()) { + return &glyphs.at(codepoint); + } + if (!this->fontFile.has_value() || this->fontFile->expired()) { + return nullptr; + } + int codepage = codepoint >> 8; + if (codepage >= 1024) { + return nullptr; + } + if (glyphs.size() < (codepage << 8)) { + glyphs.resize(codepage << 8); + } + auto fontFile = this->fontFile->lock(); + if (pages.size() <= codepage) { + pages.resize(codepage); + } + pages.push_back(fontFile->renderPage(codepage, glyphs, lineHeight)); + return &glyphs.at(codepoint); +} diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index f30335be3..e23c45ba8 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "typedefs.hpp" @@ -14,14 +15,28 @@ class Texture; class Batch2D; class Batch3D; class Camera; +class ImageData; + +class Font; + +namespace vector_fonts { + class FontFile; +} + +struct Glyph { + int yOffset; + int xAdvance; +}; class Font { - int lineHeight; - int yoffset; - int glyphInterval = 8; - std::vector> pages; public: - Font(std::vector> pages, int lineHeight, int yoffset); + Font( + std::vector> pages, + std::vector glyphs, + int lineHeight, + int yoffset, + std::optional> fontFile = std::nullopt + ); ~Font(); int getLineHeight() const; @@ -52,7 +67,7 @@ public: const FontStylesScheme* styles, size_t styleMapOffset, float scale = 1 - ) const; + ); void draw( Batch3D& batch, @@ -62,11 +77,24 @@ public: const glm::vec3& pos, const glm::vec3& right={1, 0, 0}, const glm::vec3& up={0, 1, 0} - ) const; + ); const Texture* getPage(int page) const; FontMetrics getMetrics() const { - return {lineHeight, yoffset, glyphInterval}; + return {std::nullopt, lineHeight, yoffset, glyphInterval}; } + + const Glyph* getGlyph(int codepoint); + + static std::unique_ptr createBitmapFont( + std::vector> pages + ); +private: + int lineHeight; + int yoffset; + int glyphInterval; + std::vector> pages; + std::vector glyphs; + std::optional> fontFile; }; diff --git a/src/graphics/core/FontMetics.hpp b/src/graphics/core/FontMetics.hpp index d58a4fd98..a835a495b 100644 --- a/src/graphics/core/FontMetics.hpp +++ b/src/graphics/core/FontMetics.hpp @@ -1,11 +1,19 @@ #pragma once +#include #include +#include + +class Font; struct FontMetrics { + std::optional> font; + int lineHeight; int yoffset; - int glyphInterval = 8; + int _glyphInterval = 8; + + ~FontMetrics(); int calcWidth(std::wstring_view text, size_t offset=0, size_t length=-1) const; }; diff --git a/src/graphics/core/FontMetrics.cpp b/src/graphics/core/FontMetrics.cpp new file mode 100644 index 000000000..4727faed0 --- /dev/null +++ b/src/graphics/core/FontMetrics.cpp @@ -0,0 +1,5 @@ +#include "FontMetics.hpp" + +#include "Font.hpp" + +FontMetrics::~FontMetrics() = default; diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp index 4b905e138..0f9d32285 100644 --- a/src/graphics/render/TextsRenderer.cpp +++ b/src/graphics/render/TextsRenderer.cpp @@ -45,7 +45,7 @@ void TextsRenderer::renderNote( } opacity = preset.xrayOpacity; } - const auto& font = assets.require(FONT_DEFAULT); + auto& font = assets.require(FONT_DEFAULT); glm::vec3 xvec = note.getAxisX(); glm::vec3 yvec = note.getAxisY(); diff --git a/src/graphics/ui/elements/Button.cpp b/src/graphics/ui/elements/Button.cpp index c951e6c3d..96ca620b9 100644 --- a/src/graphics/ui/elements/Button.cpp +++ b/src/graphics/ui/elements/Button.cpp @@ -91,6 +91,10 @@ void Button::setTextAlign(Align align) { } } +std::shared_ptr