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