Add skeleton of TextRenderer

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-10-05 06:30:24 +03:00
parent 59acba3264
commit 6ec78cf752
8 changed files with 226 additions and 37 deletions

View File

@@ -14,6 +14,7 @@ pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(LIBPORTAL REQUIRED IMPORTED_TARGET libportal)
pkg_check_modules(XKBCOMMON REQUIRED IMPORTED_TARGET xkbcommon)
pkg_check_modules(FONTCONFIG REQUIRED IMPORTED_TARGET fontconfig)
pkg_check_modules(HARFBUZZ REQUIRED IMPORTED_TARGET harfbuzz)
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols)
pkg_check_modules(WLR_PROTOCOLS REQUIRED wlr-protocols)
@@ -30,6 +31,18 @@ set(PLATFORM DRM)
set(BUILD_EXAMPLES OFF)
FetchContent_MakeAvailable(raylib)
FetchContent_Declare(
msdfgen
GIT_REPOSITORY https://github.com/Chlumsky/msdfgen.git
GIT_SHALLOW 1
)
set(MSDFGEN_BUILD_STANDALONE OFF)
set(MSDFGEN_USE_VCPKG OFF)
set(MSDFGEN_USE_SKIA OFF)
set(MSDFGEN_DISABLE_SVG ON)
set(MSDFGEN_DISABLE_PNG ON)
FetchContent_MakeAvailable(msdfgen)
find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
@@ -118,6 +131,7 @@ add_executable(waylight
${GEN_C_PRIVATES}
${CMAKE_CURRENT_SOURCE_DIR}/src/App.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/TextRenderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Tick.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
)
@@ -136,8 +150,10 @@ target_link_libraries(waylight PRIVATE
PkgConfig::LIBPORTAL
PkgConfig::XKBCOMMON
PkgConfig::FONTCONFIG
PkgConfig::HARFBUZZ
raylib
msdfgen::msdfgen-core
m
dl

View File

@@ -47,6 +47,7 @@
glib
libxkbcommon
fontconfig
harfbuzz
]
++ buildInputs
++ nativeBuildInputs

View File

@@ -1,11 +1,11 @@
#include "App.hpp"
#include <cassert>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <poll.h>
#include <print>
#include <pthread.h>
#include <signal.h>
#include <sys/mman.h>
@@ -27,35 +27,6 @@
#include "blur-client-protocol.h"
#include "ext-background-effect-v1-client-protocol.h"
auto find_font_path(std::string_view path = "sans-serif:style=Regular")
-> std::optional<std::string>
{
if (!FcInit())
return std::nullopt;
std::string query(path);
FcPattern *pattern
= FcNameParse(reinterpret_cast<FcChar8 const *>(query.c_str()));
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
FcPattern *font = FcFontMatch(nullptr, pattern, &result);
std::optional<std::string> final_path;
if (font) {
FcChar8 *file;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)
final_path = reinterpret_cast<char *>(file);
FcPatternDestroy(font);
}
FcPatternDestroy(pattern);
FcFini();
return final_path;
}
auto TypingBuffer::push_utf8(char const *s) -> void
{
for (unsigned char const *p = reinterpret_cast<unsigned char const *>(s);
@@ -83,11 +54,6 @@ auto TypingBuffer::push_utf8(char const *s) -> void
App::App()
{
{
auto const path = find_font_path();
if (path)
std::println("font path = {}", *path);
}
init_wayland();
init_egl();
init_signal();
@@ -330,6 +296,13 @@ auto App::init_egl() -> void
ensure_egl_surface();
InitWindow(m_win_w, m_win_h, "");
m_tr = TextRenderer();
auto const font = find_font_path();
assert(font && "Could not find font");
auto const font_handle = m_tr->load_font(*font);
assert(font_handle && "Could not load font");
m_font = *font_handle;
}
auto App::init_signal() -> void

View File

@@ -1,6 +1,5 @@
#pragma once
#include <memory_resource>
#include <unordered_set>
#include <vector>
@@ -18,6 +17,7 @@ extern "C" {
#include <wayland-egl.h>
#include <xkbcommon/xkbcommon.h>
#include "TextRenderer.hpp"
#include "Theme.hpp"
#include "common.hpp"
@@ -139,6 +139,9 @@ private:
}
} m_kbd;
std::optional<TextRenderer> m_tr { std::nullopt };
FontHandle m_font;
enum_array<Theme, ColorScheme> m_themes { make_default_themes() };
Theme m_active_theme { Theme::Light };

104
src/TextRenderer.cpp Normal file
View File

@@ -0,0 +1,104 @@
#include "TextRenderer.hpp"
#include <cassert>
#include <cstdlib>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <fontconfig/fontconfig.h>
#include <raylib.h>
TextRenderer::TextRenderer()
{
static char const msdf_fs_data[] {
#embed "msdf.fs"
, 0
};
m_msdf_shader = LoadShaderFromMemory(nullptr, msdf_fs_data);
assert(IsShaderValid(m_msdf_shader));
}
TextRenderer::~TextRenderer() { UnloadShader(m_msdf_shader); }
auto TextRenderer::measure_text(FontHandle const font,
std::string_view const text, int const size) -> Vector2
{
// FIXME: Implement.
return {};
}
auto TextRenderer::draw_text(FontHandle const font, std::string_view const text,
Vector2 const pos, int const size, Color const color) -> void
{
int const pos_x = pos.x;
int const pos_y = pos.y;
// Don't use pos from here on out!
// FIXME: Implement.
(void)pos_x, (void)pos_y;
}
auto TextRenderer::load_font(std::filesystem::path const &path)
-> std::optional<FontHandle>
{
// FIXME: Implement.
return std::nullopt;
}
auto TextRenderer::unload_font(FontHandle const font)
{
// FIXME: Implement.
}
auto find_font_path(std::string_view path)
-> std::optional<std::filesystem::path>
{
static std::once_flag fc_once;
std::call_once(fc_once, []() {
if (FcInit())
std::atexit([] { FcFini(); });
});
static std::mutex m;
static std::unordered_map<std::string, std::optional<std::string>> cache;
std::string const key(path);
{
std::scoped_lock lock(m);
if (auto it = cache.find(key); it != cache.end())
return it->second;
}
FcPattern *pattern
= FcNameParse(reinterpret_cast<FcChar8 const *>(key.c_str()));
if (!pattern) {
std::scoped_lock lock(m);
return cache[key] = std::nullopt;
}
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
FcPattern *font = FcFontMatch(nullptr, pattern, &result);
std::optional<std::string> final_path;
if (font) {
FcChar8 *file;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)
final_path = reinterpret_cast<char *>(file);
FcPatternDestroy(font);
}
FcPatternDestroy(pattern);
{
std::scoped_lock lock(m);
cache[key] = final_path;
}
return final_path;
}

56
src/TextRenderer.hpp Normal file
View File

@@ -0,0 +1,56 @@
#pragma once
#include <filesystem>
#include <unordered_map>
#include <vector>
#include <raylib.h>
#include "common.hpp"
struct FontHandle {
auto operator()() const -> auto const & { return id; }
private:
usize id;
};
struct TextRenderer {
TextRenderer(); // Requires raylib to be initialized!
~TextRenderer();
auto measure_text(FontHandle const font, std::string_view const text,
int const size = 16) -> Vector2;
auto draw_text(FontHandle const font, std::string_view const text,
Vector2 const pos, int const size = 16, Color const color = WHITE)
-> void;
auto load_font(std::filesystem::path const &path)
-> std::optional<FontHandle>;
auto unload_font(FontHandle const font);
private:
struct FontData {
struct Glyph {
struct Rect {
float top, left, right, bottom;
};
float advance;
Rect plane_bounds;
Rect glyph_bounds;
};
Texture2D atlas;
Image atlas_img;
std::filesystem::path font_path;
std::unordered_map<u32, Glyph> glyphs;
};
Shader m_msdf_shader;
std::vector<FontData> m_font_data;
};
auto find_font_path(std::string_view path = "sans-serif:style=Regular")
-> std::optional<std::filesystem::path>;

View File

@@ -1,3 +1,5 @@
#pragma once
#include <cstdint>
using u8 = std::uint8_t;
@@ -11,7 +13,7 @@ using i64 = std::int64_t;
using usize = std::uintptr_t;
using isize = std::intptr_t;
inline auto rune_to_string(uint32_t cp) -> char const *
[[maybe_unused]] static inline auto rune_to_string(uint32_t cp) -> char const *
{
static char utf8[5] = { 0 };
for (auto &c : utf8)

34
src/msdf.fs Normal file
View File

@@ -0,0 +1,34 @@
#version 100
#extension GL_OES_standard_derivatives : enable
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 fragTexCoord;
varying vec4 fragColor;
uniform sampler2D texture0;
uniform vec4 colDiffuse;
uniform float pxRange;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
float screenPxRange(vec2 uv) {
vec2 duv_dx = dFdx(uv);
vec2 duv_dy = dFdy(uv);
float scale = 0.5 * (length(duv_dx) + length(duv_dy));
return max(scale * pxRange, 1.0);
}
void main() {
vec3 msd = texture2D(texture0, fragTexCoord).rgb;
float sd = median(msd.r, msd.g, msd.b);
float spx = screenPxRange(fragTexCoord);
float dist = spx * (sd - 0.5);
float opacity = clamp(dist + 0.5, 0.0, 1.0);
gl_FragColor = vec4(fragColor.rgb, fragColor.a * opacity) * colDiffuse;
}