diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ad5637..b9d0952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,6 @@ include(cmake/detect_os.cmake) include(cmake/configure_glfw_native.cmake) include(cmake/compile_shaders.cmake) -find_package(Eigen3 REQUIRED) -find_package(spdlog REQUIRED) -find_package(glfw3 REQUIRED) -find_package(Stb REQUIRED) - # 如果是Debug模式, 添加宏定义 if (CMAKE_BUILD_TYPE STREQUAL "Debug") add_definitions(-DDEBUG=1) diff --git a/example/src/main.cpp b/example/src/main.cpp index cb96b28..a8ec834 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -2,17 +2,19 @@ #include +#include "async/thread_pool.h" #include "core/renderer/renderer.h" #include "core/window/window_manager.h" #include "core/window/renderer_window.h" int main(int argc, char* argv[]) { - aorii::create_renderer(renderer_api::dx11); + aorii::create_renderer(renderer_api::DX11); aorii::create_window_manager(); auto window = aorii::create_window({1280, 1280}, "hello world"); auto glfw_window = window->get_glfw_window(); while (!glfwWindowShouldClose(glfw_window)) { + thread_pool::global().process_main_thread_callbacks(); glfwPollEvents(); aorii::update(); } diff --git a/resource/5a5326c4ce213d25d227b2b3ef16391a3493138886822091.jpg b/resource/5a5326c4ce213d25d227b2b3ef16391a3493138886822091.jpg new file mode 100644 index 0000000..360b76c Binary files /dev/null and b/resource/5a5326c4ce213d25d227b2b3ef16391a3493138886822091.jpg differ diff --git a/resource/61532343_p0.jpg b/resource/61532343_p0.jpg new file mode 100644 index 0000000..1474931 Binary files /dev/null and b/resource/61532343_p0.jpg differ diff --git a/resource/69054578_p0.jpg b/resource/69054578_p0.jpg new file mode 100644 index 0000000..45ed144 Binary files /dev/null and b/resource/69054578_p0.jpg differ diff --git a/HarmonyOS_Sans_SC_Regular.ttf b/resource/HarmonyOS_Sans_SC_Regular.ttf similarity index 100% rename from HarmonyOS_Sans_SC_Regular.ttf rename to resource/HarmonyOS_Sans_SC_Regular.ttf diff --git a/resource/IMG_5687.jpg b/resource/IMG_5687.jpg new file mode 100644 index 0000000..28af24d Binary files /dev/null and b/resource/IMG_5687.jpg differ diff --git a/resource/JetBrainsMono-Light.ttf b/resource/JetBrainsMono-Light.ttf new file mode 100644 index 0000000..15f15a2 Binary files /dev/null and b/resource/JetBrainsMono-Light.ttf differ diff --git a/resource/JetBrainsMono-Regular.ttf b/resource/JetBrainsMono-Regular.ttf new file mode 100644 index 0000000..dff66cc Binary files /dev/null and b/resource/JetBrainsMono-Regular.ttf differ diff --git a/resource/__original_drawn_by_hoji_hooooooooji1029__e14b5348c62587c17c297324af85cb0b.jpg b/resource/__original_drawn_by_hoji_hooooooooji1029__e14b5348c62587c17c297324af85cb0b.jpg new file mode 100644 index 0000000..5348f94 Binary files /dev/null and b/resource/__original_drawn_by_hoji_hooooooooji1029__e14b5348c62587c17c297324af85cb0b.jpg differ diff --git a/src/core/async/thread_pool.h b/src/core/async/thread_pool.h index 10e13aa..d83cbc4 100644 --- a/src/core/async/thread_pool.h +++ b/src/core/async/thread_pool.h @@ -60,6 +60,35 @@ public: return res; } + template + auto submit_with_callback(f&& in_func, callback&& in_callback, args&&... in_args) { + using return_type = std::invoke_result_t; + + if (stop) { throw std::runtime_error("submit on stopped ThreadPool"); } + + auto task = std::make_shared>( + std::bind(std::forward(in_func), std::forward(in_args)...) + ); + + auto res = task->get_future(); + + { + std::lock_guard lock(queue_mutex); + tasks.emplace([task, in_callback] { + std::optional callback_value; + try { + callback_value = (*task)(); + } catch (...) { + callback_value = std::nullopt; + } + in_callback(callback_value); + }); + } + + condition.notify_one(); + return res; + } + /** * @brief 提交一个任务到线程池,并在主线程中执行回调函数 * @tparam f 任务函数类型 diff --git a/src/core/misc/color.cpp b/src/core/misc/color.cpp new file mode 100644 index 0000000..73452be --- /dev/null +++ b/src/core/misc/color.cpp @@ -0,0 +1,2 @@ +#include "color.h" + diff --git a/src/core/misc/color.h b/src/core/misc/color.h new file mode 100644 index 0000000..5020c32 --- /dev/null +++ b/src/core/misc/color.h @@ -0,0 +1,59 @@ +#pragma once +#include + +class linear_color { +public: + linear_color() : r(1), g(1), b(1), a(1) { + } + + linear_color(float in_r, float in_g, float in_b, float in_a = 1.0f) : r(in_r), g(in_g), b(in_b), a(in_a) { + } + + static linear_color from_srgb(float in_r, float in_g, float in_b, float in_a = 1.0f) { + return linear_color( + in_r <= 0.04045f ? in_r / 12.92f : std::pow((in_r + 0.055f) / 1.055f, 2.4f), + in_g <= 0.04045f ? in_g / 12.92f : std::pow((in_g + 0.055f) / 1.055f, 2.4f), + in_b <= 0.04045f ? in_b / 12.92f : std::pow((in_b + 0.055f) / 1.055f, 2.4f), + in_a + ); + } + + static linear_color from_srgb(const linear_color& in_color) { + return from_srgb(in_color.r, in_color.g, in_color.b, in_color.a); + } + + linear_color& operator+=(const linear_color& in_color) { + r += in_color.r; + g += in_color.g; + b += in_color.b; + a += in_color.a; + return *this; + } + + linear_color& operator-=(const linear_color& in_color) { + r -= in_color.r; + g -= in_color.g; + b -= in_color.b; + a -= in_color.a; + return *this; + } + + linear_color operator+(const linear_color& in_color) const { + return { r + in_color.r, g + in_color.g, b + in_color.b, a + in_color.a }; + } + + linear_color operator-(const linear_color& in_color) const { + return { r - in_color.r, g - in_color.g, b - in_color.b, a - in_color.a }; + } + + bool operator==(const linear_color& in_color) const { + return r == in_color.r && g == in_color.g && b == in_color.b && a == in_color.a; + } + + float r, g, b, a; + + + inline static const linear_color white = { 1.0f, 1.0f, 1.0f, 1.0f }; + inline static const linear_color black = { 0.0f, 0.0f, 0.0f, 1.0f }; + inline static const linear_color transparent = { 0.0f, 0.0f, 0.0f, 1.0f }; +}; diff --git a/src/core/misc/intrusive_unset_optional_state.h b/src/core/misc/intrusive_unset_optional_state.h new file mode 100644 index 0000000..0cfb0fb --- /dev/null +++ b/src/core/misc/intrusive_unset_optional_state.h @@ -0,0 +1,17 @@ +#pragma once +#include + +namespace aorii_private { + template + struct optional_storage; +} + +struct intrusive_unset_optional_state { + template + friend class std::optional; + + template + friend struct aorii_private::optional_storage; +private: + explicit intrusive_unset_optional_state() = default; +}; diff --git a/src/core/misc/lazy_singleton.h b/src/core/misc/lazy_singleton.h new file mode 100644 index 0000000..26a2432 --- /dev/null +++ b/src/core/misc/lazy_singleton.h @@ -0,0 +1,63 @@ +#pragma once +#include + +class lazy_singleton_func { +protected: + template static void construct(void* in_place) { new (in_place) T(); } + template static void destruct(T* in_instance) { in_instance->~T(); } +}; + +template +class lazy_singleton : public lazy_singleton_func { +public: + static T& get() { + return *get_lazy(construct).ptr; + } + + static T* try_get() { + return get_lazy(construct).try_get_value(); + } + + static void tear_down() { + get_lazy(nullptr).reset(); + } + +private: + static lazy_singleton& get_lazy(void(*ctor)(void*)) { + static lazy_singleton instance(ctor); + return instance; + } + + explicit lazy_singleton(void(*ctor)(void*)) { + if (ctor) { + ctor(instance_data); + } + + ptr = ctor ? reinterpret_cast(instance_data) : nullptr; + } + + ~lazy_singleton() { + reset(); + } + + T* try_get_value() { + return ptr; + } + + T& get_value() { + if (!ptr) { + throw std::runtime_error("lazy_singleton not initialized"); + } + return *ptr; + } + + void reset() { + if (ptr) { + destruct(ptr); + ptr = nullptr; + } + } + + alignas(T) unsigned char instance_data[sizeof(T)]{}; + T* ptr{}; +}; diff --git a/src/core/misc/test.cpp b/src/core/misc/test.cpp deleted file mode 100644 index 9da63d9..0000000 --- a/src/core/misc/test.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// -// Created by 46944 on 24-10-15. -// diff --git a/src/core/misc/type_hash.h b/src/core/misc/type_hash.h new file mode 100644 index 0000000..0c7c6da --- /dev/null +++ b/src/core/misc/type_hash.h @@ -0,0 +1,170 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once +#include +#include + +inline uint32_t murmur_finalize32(uint32_t hash) { + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; + return hash; +} + +/** + * Combines two hash values to get a third. + * Note - this function is not commutative. + * + * This function cannot change for backward compatibility reasons. + * You may want to choose HashCombineFast for a better in-memory hash combining function. + */ +[[nodiscard]] inline uint32_t hash_combine(uint32_t a, uint32_t c) { + uint32_t b = 0x9e3779b9; + a += b; + + a -= b; a -= c; a ^= (c>>13); + b -= c; b -= a; b ^= (a<<8); + c -= a; c -= b; c ^= (b>>13); + a -= b; a -= c; a ^= (c>>12); + b -= c; b -= a; b ^= (a<<16); + c -= a; c -= b; c ^= (b>>5); + a -= b; a -= c; a ^= (c>>3); + b -= c; b -= a; b ^= (a<<10); + c -= a; c -= b; c ^= (b>>15); + + return c; +} + +/** + * Combines two hash values to get a third. + * Note - this function is not commutative. + * + * WARNING! This function is subject to change and should only be used for creating + * combined hash values which don't leave the running process, + * e.g. GetTypeHash() overloads. + */ +[[nodiscard]] inline uint32_t hash_combine_fast(uint32_t a, uint32_t b) { + return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2)); +} + +[[nodiscard]] inline uint32_t pointer_hash(const void* key) { + // Ignoring the lower 4 bits since they are likely zero anyway. + // Higher bits are more significant in 64 bit builds. + const uintptr_t ptr_int = reinterpret_cast(key) >> 4; + return murmur_finalize32((uint32_t)ptr_int); +} + +[[nodiscard]] inline uint32_t pointer_hash(const void* key, uint32_t c) { + // we can use HashCombineFast here because pointers are non-persistent + return hash_combine_fast(pointer_hash(key), c); +} + +// +// Hash functions for common types. +// +// WARNING! GetTypeHash result values are not expected to leave the running process. +// Do not persist them to disk, send them to another running process or +// expect them to be consistent across multiple runs. +// +template < + typename ScalarType, + std::enable_if_t && !std::is_same_v && !std::is_same_v>* = nullptr +> +[[nodiscard]] uint32_t get_type_hash(ScalarType value) +{ + if constexpr (std::is_integral_v) + { + if constexpr (sizeof(ScalarType) <= 4) + { + return value; + } + else if constexpr (sizeof(ScalarType) == 8) + { + return (uint32_t)value + ((uint32_t)(value >> 32) * 23); + } + else if constexpr (sizeof(ScalarType) == 16) + { + const uint64_t low = (uint64_t)value; + const uint64_t high = (uint64_t)(value >> 64); + return get_type_hash(low) ^ get_type_hash(high); + } + else + { + static_assert(sizeof(ScalarType) == 0, "Unsupported integral type"); + return 0; + } + } + else if constexpr (std::is_floating_point_v) + { + if constexpr (std::is_same_v) + { + return *(uint32_t*)&value; + } + else if constexpr (std::is_same_v) + { + return get_type_hash(*(uint64_t*)&value); + } + else + { + static_assert(sizeof(ScalarType) == 0, "Unsupported floating point type"); + return 0; + } + } + else if constexpr (std::is_enum_v) + { + return get_type_hash((__underlying_type(ScalarType))value); + } + else if constexpr (std::is_pointer_v) + { + // Once the TCHAR* deprecations below are removed, we want to prevent accidental string hashing, so this static_assert should be commented back in + //static_assert(!TIsCharType>::Value, "Pointers to string types should use a PointerHash() or FCrc::Stricmp_DEPRECATED() call depending on requirements"); + + return pointer_hash(value); + } + else + { + static_assert(sizeof(ScalarType) == 0, "Unsupported scalar type"); + return 0; + } +} + +template < + typename T, + uint32_t N, + std::enable_if_t>* = nullptr +> +uint32_t get_type_hash(T (&array)[N]) +{ + return pointer_hash(array); +} + +// template < +// typename T, +// std::enable_if_t>* = nullptr +// > +// uint32_t get_type_hash(T* value) +// { +// // Hashing a TCHAR* array differently from a void* is dangerous and is deprecated. +// // When removing these overloads post-deprecation, comment in the related static_assert in the std::is_pointer_v block of the GetTypeHash overload above. +// return FCrc::Strihash_DEPRECATED(value); +// } + +template +[[nodiscard]] uint32_t get_array_hash(const T* ptr, uint64_t size, uint32_t previous_hash = 0) +{ + uint32_t result = previous_hash; + while (size) + { + result = hash_combine_fast(result, get_type_hash(*ptr)); + ++ptr; + --size; + } + + return result; +} + +// Use this when inside type that has get_type_hash() (no in-parameters) implemented. It makes GetTypeHash dispatch in global namespace +template +[[nodiscard]] uint32_t get_type_hash_helper(const T& V) { return get_type_hash(V); } diff --git a/src/renderer/CMakeLists.txt b/src/renderer/CMakeLists.txt index ed9d065..5b4d8ed 100644 --- a/src/renderer/CMakeLists.txt +++ b/src/renderer/CMakeLists.txt @@ -1,5 +1,15 @@ project(aorii_renderer) +find_package(harfbuzz REQUIRED) +find_package(msdfgen REQUIRED) +find_package(Eigen3 REQUIRED) +find_package(spdlog REQUIRED) +find_package(glfw3 REQUIRED) +find_package(Stb REQUIRED) + +cmake_policy(SET CMP0167 NEW) +find_package(Boost REQUIRED COMPONENTS flyweight icl) + set(GL_BACKEND FALSE CACHE BOOL "OpenGL backend to use") set(DX_BACKEND FALSE CACHE BOOL "DirectX backend to use") set(VK_BACKEND FALSE CACHE BOOL "Vulkan backend to use") @@ -26,7 +36,7 @@ if (METAL_BACKEND) endif () add_library(${PROJECT_NAME} STATIC ${RENDERER_SOURCES}) -target_link_libraries(${PROJECT_NAME} PUBLIC Eigen3::Eigen spdlog::spdlog glfw aorii_core) +target_link_libraries(${PROJECT_NAME} PUBLIC harfbuzz::harfbuzz Eigen3::Eigen spdlog::spdlog glfw aorii_core msdfgen::msdfgen Boost::boost) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${Stb_INCLUDE_DIR}) target_compile_definitions(${PROJECT_NAME} PRIVATE NOMINMAX) add_os_definitions(${PROJECT_NAME}) diff --git a/src/renderer/backend/dx/dx_buffer.h b/src/renderer/backend/dx/dx_buffer.h index 3eca2b7..5bcd65b 100644 --- a/src/renderer/backend/dx/dx_buffer.h +++ b/src/renderer/backend/dx/dx_buffer.h @@ -41,11 +41,11 @@ public: [[nodiscard]] unsigned int get_dx_buffer_type() const { switch (this->get_type()) { - case buffer_type::vertex: + case buffer_type::VERTEX: return D3D11_BIND_VERTEX_BUFFER; - case buffer_type::index: + case buffer_type::INDEX: return D3D11_BIND_INDEX_BUFFER; - case buffer_type::constant: + case buffer_type::CONSTANT: return D3D11_BIND_CONSTANT_BUFFER; default: return 0; diff --git a/src/renderer/backend/dx/dx_texture.cpp b/src/renderer/backend/dx/dx_texture.cpp index 12e5e77..1df548b 100644 --- a/src/renderer/backend/dx/dx_texture.cpp +++ b/src/renderer/backend/dx/dx_texture.cpp @@ -5,11 +5,11 @@ int get_cpu_access_flag(device_memory_type in_memory_type) { switch (in_memory_type) { - case device_memory_type::gpu: + case device_memory_type::GPU: return 0; - case device_memory_type::shared: + case device_memory_type::SHARED: return D3D11_CPU_ACCESS_WRITE; - case device_memory_type::cpu: + case device_memory_type::CPU: return D3D11_CPU_ACCESS_WRITE; default: return D3D11_CPU_ACCESS_WRITE; diff --git a/src/renderer/backend/dx/dx_window.cpp b/src/renderer/backend/dx/dx_window.cpp index 321b35c..b3db0fb 100644 --- a/src/renderer/backend/dx/dx_window.cpp +++ b/src/renderer/backend/dx/dx_window.cpp @@ -114,7 +114,7 @@ void dx_window::begin_frame() { // if (test_texture) context.draw_texture({ 0.f, 0.f }, test_texture->size().cast(), test_texture); // context.draw_string({0, 0}, U"你好,世界!全是水群大师\n测试换行\n测试Unicode: 😀\nТест по русскому языку\nテスト日本語", 32, {0, 0, 0, 1}); float font_height = 64; - context.draw_string({0, 100}, U"你好,世界!\nТест по русскому языку\nテスト", font_height, {0, 0, 0, 1}); + context.draw_string({0, 100}, U"你好,世界!\nТест по русскому языку\nテスト", font_height, {0, 0, 0, 1}); context.draw_rectangle({ 0, 100 }, { 2048, 1 }, { 1, 0, 1, 1 }); context.draw_rectangle({ 0, 100 + font_height }, { 2048, 1 }, { 1, 0, 1, 1 }); context.draw_rectangle({ 0, 100 + font_height * 2 }, { 2048, 1 }, { 1, 0, 1, 1 }); diff --git a/src/renderer/core/fonts/composite_font.cpp b/src/renderer/core/fonts/composite_font.cpp new file mode 100644 index 0000000..01e625a --- /dev/null +++ b/src/renderer/core/fonts/composite_font.cpp @@ -0,0 +1,43 @@ +#include "composite_font.h" + +#include "misc/mapped_file.h" + +struct font_face_data_lazy_load : font_face_data::font_face_data_impl { + explicit font_face_data_lazy_load(const std::wstring& in_filename) { + mapped_file file; + if (!file.map_file(in_filename)) + return; + + font_data.resize(file.get_size()); + std::memcpy(font_data.data(), file.get_data(), file.get_size()); + } + + [[nodiscard]] bool empty() const noexcept override { return font_data.empty(); } + [[nodiscard]] uint8_t const* data() const noexcept override { return font_data.data(); } + [[nodiscard]] size_t size() const noexcept override { return font_data.size(); } + + std::vector font_data; +}; + +struct font_face_data_stream : font_face_data::font_face_data_impl { + explicit font_face_data_stream(const std::wstring& in_filename) { + file.map_file(in_filename); + } + + [[nodiscard]] bool empty() const noexcept override { return file.is_mapped(); } + [[nodiscard]] uint8_t const* data() const noexcept override { return static_cast(file.get_data()); } + [[nodiscard]] size_t size() const noexcept override { return file.get_size(); } + + mapped_file file; +}; + +font_face_data::font_face_data(const std::wstring& in_filename, font_loading_policy in_loading_policy) { + switch (in_loading_policy) { + case font_loading_policy::LAZY_LOAD: + impl = std::make_unique(in_filename); + break; + case font_loading_policy::STREAM: + impl = std::make_unique(in_filename); + break; + } +} diff --git a/src/renderer/core/fonts/composite_font.h b/src/renderer/core/fonts/composite_font.h new file mode 100644 index 0000000..6ac251b --- /dev/null +++ b/src/renderer/core/fonts/composite_font.h @@ -0,0 +1,160 @@ +#pragma once +#include +#include + +#include "font_rasterization_mode.h" +#include +#include +#include +#include +#include + +// 字体渲染的hinting方式 +enum class font_hinting { + // 使用字体的默认hinting方式 + DEFAULT, + // 强制使用FreeType的自动hinting算法 + AUTO, + // 强制针对非单色显示器优化的hinting算法 + AUTO_LIGHT, + // 强制针对单色显示器优化的hinting算法 + MONOCHROME, + // 不使用hinting + NONE +}; + +// 字体加载策略 +enum class font_loading_policy { + // 将内存中的字体数据全部加载到内存中, 适用于字体文件较小的情况 + LAZY_LOAD, + // 流式加载字体数据, 适用于字体文件较大的情况 + STREAM, +}; + +// 字体布局方式 +enum class font_layout_method { + // 使用字体内置的度量信息进行布局, 但某些字体的度量信息可能不准确 + METRICS, + // 使用字体边界值进行布局, 通常会有较大的行高, 但适用于字体度量信息不准确的情况 + BOUNDING_BOX +}; + +struct font_face_data { + struct font_face_data_impl { + virtual ~font_face_data_impl() = default; + [[nodiscard]] virtual bool empty() const noexcept = 0; + [[nodiscard]] virtual uint8_t const* data() const noexcept = 0; + [[nodiscard]] virtual size_t size() const noexcept = 0; + }; +public: + font_face_data() = default; + + explicit font_face_data(const std::wstring& in_filename, font_loading_policy in_loading_policy); + + [[nodiscard]] bool empty() const noexcept { return impl->empty(); } + [[nodiscard]] uint8_t const* data() const noexcept { return impl->data(); } + [[nodiscard]] size_t size() const noexcept { return impl->size(); } +private: + std::unique_ptr impl; +}; + +using font_face_data_ptr = std::shared_ptr; +using font_face_data_const_ptr = std::shared_ptr; + +// 字体渲染设置 +struct font_rasterization_settings { + // 字体栅格化方式 + font_rasterization_mode mode = font_rasterization_mode::BITMAP; + // 距离场px/em分辨率, 如果是位图则不生效 + int32_t distance_field_ppem = 0; +}; + +class font_data { +public: + font_data(); + font_data(const std::wstring& in_font_filename, font_hinting in_hinting, font_loading_policy in_loading_policy, int32_t in_sub_face_index = 0); + [[nodiscard]] bool has_font() const; + + [[nodiscard]] const std::wstring& get_font_filename() const noexcept { return font_filename; } + [[nodiscard]] font_hinting get_hinting() const noexcept { return hinting; } + [[nodiscard]] font_loading_policy get_loading_policy() const noexcept { return loading_policy; } + [[nodiscard]] int32_t get_sub_face_index() const noexcept { return sub_face_index; } + void set_sub_face_index(const int32_t in_sub_face_index) noexcept { sub_face_index = in_sub_face_index; } + [[nodiscard]] font_layout_method get_layout_method() const noexcept { return layout_method; } + void set_font_layout_method(font_layout_method in_layout_method) noexcept { layout_method = in_layout_method; } + + [[nodiscard]] int32_t get_strike_line_height_percentage() const { return 60; } + + [[nodiscard]] font_rasterization_settings get_rasterization_settings() const { return {}; } +private: + std::wstring font_filename; + uint32_t font_filename_hash; + font_hinting hinting; + font_loading_policy loading_policy; + font_layout_method layout_method; + int32_t sub_face_index; +}; + +class typeface_entry { +public: + typeface_entry() = default; + explicit typeface_entry(const boost::flyweight& in_name) : name(std::move(in_name)) {} + + typeface_entry(const boost::flyweight& in_name, const std::wstring& in_font_filename, const font_hinting in_hinting, + const font_loading_policy in_loading_policy) : name(std::move(in_name)), + font(in_font_filename, in_hinting, + in_loading_policy) { + } + + [[nodiscard]] const boost::flyweight& get_name() const noexcept { return name; } + [[nodiscard]] const font_data& get_font() const noexcept { return font; } + // 字体名称 + boost::flyweight name; + // 字体数据 + font_data font; +}; + +class typeface { +public: + typeface() = default; + explicit typeface(const std::wstring& in_font_name, const std::wstring& in_font_filename, const font_hinting in_hinting, const font_loading_policy in_loading_policy) { + append_font(in_font_name, in_font_filename, in_hinting, in_loading_policy); + } + + typeface& append_font(const std::wstring& in_font_name, const std::wstring& in_font_filename, const font_hinting in_hinting, const font_loading_policy in_loading_policy) { + fonts.emplace_back(in_font_name, in_font_filename, in_hinting, in_loading_policy); + return *this; + } + + [[nodiscard]] const std::vector& get_fonts() const noexcept { return fonts; } + + std::vector fonts; +}; + +class composite_fallback_font { +public: + composite_fallback_font() : scaling_factor(1.0f) {} + + typeface fallback_typeface; + float scaling_factor; +}; + +class composite_subfont : public composite_fallback_font { +public: + std::vector character_ranges; + std::wstring cultures; +}; + +class composite_font { +public: + [[nodiscard]] bool is_ascent_descent_override_enabled() const { + return enable_ascent_descent_override; + } + + typeface default_typeface; + composite_fallback_font fallback_typeface; + std::vector sub_typefaces; +private: + bool enable_ascent_descent_override = true; +}; + diff --git a/src/renderer/core/fonts/font_cache.cpp b/src/renderer/core/fonts/font_cache.cpp new file mode 100644 index 0000000..5578804 --- /dev/null +++ b/src/renderer/core/fonts/font_cache.cpp @@ -0,0 +1,2 @@ +#include "font_cache.h" + diff --git a/src/renderer/core/fonts/font_cache.h b/src/renderer/core/fonts/font_cache.h new file mode 100644 index 0000000..ecb44ee --- /dev/null +++ b/src/renderer/core/fonts/font_cache.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include + +#include "text_shaper.h" + +enum class font_cache_atlas_data_type { + REGULAR, + OUTLINE, + NUM +}; + +enum class text_shaping_method { + AUTO, + KERNING_ONLY, + FULL_SHAPING +}; + +struct shaped_glyph_font_atlas_data { + // 从baseline到字形位图最上边框的垂直距离 + int16_t vertical_offset = 0; + // 从原点到字形位图最左边框的水平距离 + int16_t horizontal_offset = 0; + uint16_t start_u = 0; + uint16_t start_v = 0; + uint16_t u_size = 0; + uint16_t v_size = 0; + uint8_t texture_index = 0; + bool supports_outline = false; + bool valid = false; +}; + +struct sdf_glyph_font_atlas_data { + int16_t vertical_offset = 0; + int16_t horizontal_offset = 0; + uint16_t start_u = 0; + uint16_t start_v = 0; + uint16_t u_size = 0; + uint16_t v_size = 0; + float em_outer_spread = 0.f; + float em_inner_spread = 0.f; + + struct metrics { + float bearing_x = 0.f; + float bearing_y = 0.f; + float width = 0.f; + float height = 0.f; + }; + + metrics sdf_metrics; + uint8_t texture_index = 0; + bool supports_sdf = false; + bool pending_respawn = false; + bool valid = false; +}; + +struct shaped_glyph_entry { + friend class font_cache; + + std::shared_ptr font_face_data; + uint32_t glyph_index = 0; + int32_t source_index = 0; + int16_t x_advance = 0; + int16_t y_advance = 0; + int16_t x_offset = 0; + int16_t y_offset = 0; + int8_t kerning = 0; + uint8_t num_characters_in_glyph = 0; + uint8_t num_grapheme_clusters_in_glyph = 0; + +}; + +class character_list { +public: +}; + +class font_cache { + +}; diff --git a/src/renderer/core/fonts/font_cache_composite_font.cpp b/src/renderer/core/fonts/font_cache_composite_font.cpp new file mode 100644 index 0000000..d03f403 --- /dev/null +++ b/src/renderer/core/fonts/font_cache_composite_font.cpp @@ -0,0 +1,92 @@ +#include "font_cache_composite_font.h" + +cached_typeface_data::cached_typeface_data(): typeface_(nullptr), scaling_factor_(1.f) { +} + +cached_typeface_data::cached_typeface_data(const typeface& in_typeface, float in_scaling_factor): typeface_(&in_typeface), + scaling_factor_(in_scaling_factor) { + for (const auto& typeface_entry : typeface_->fonts) { + cached_font_data_.emplace_back(typeface_entry.name, &typeface_entry.font); + } + std::sort(cached_font_data_.begin(), cached_font_data_.end(), [](const auto& lhs, const auto& rhs) { + return lhs.first < rhs.first; + }); + std::ranges::sort(cached_font_data_, &cached_font_data::sort_predicate); +} + +const font_data* cached_typeface_data::get_font_data(const boost::flyweight& in_name) const { + const auto it = std::ranges::lower_bound(cached_font_data_, in_name, &cached_font_data::key_sort_predicate, &cached_font_data::binary_search_key); + if (it != cached_font_data_.end() && *it == in_name) { + const auto index = std::distance(cached_font_data_.begin(), it); + return cached_font_data_[index].font_data; + } + return nullptr; +} + +void cached_typeface_data::get_cached_font_data(std::vector& out_font_data) const { + for (const auto& font_entry : cached_font_data_) { + out_font_data.push_back(font_entry.font_data); + } +} + +cached_composite_font_data::cached_composite_font_data(): composite_font_(nullptr) { +} + +cached_composite_font_data::cached_composite_font_data(const composite_font& in_composite_font): composite_font_(&in_composite_font) { + cached_typefaces_.push_back(std::make_shared(composite_font_->default_typeface)); + cached_typefaces_.push_back(std::make_shared(composite_font_->fallback_typeface.fallback_typeface, composite_font_->fallback_typeface.scaling_factor)); + for (const auto& sub_typeface : composite_font_->sub_typefaces) { + auto cached_typeface = std::make_shared(sub_typeface.fallback_typeface, sub_typeface.scaling_factor); + cached_typefaces_.push_back(cached_typeface); + } + refresh_font_ranges(); +} + +auto cached_composite_font_data::get_typeface_for_codepoint(char32_t in_codepoint) const { + int32_t char_index = in_codepoint; + + auto get_typeface_from_range = [char_index](const std::vector& in_font_ranges) -> const cached_typeface_data* { + auto get_typeface_from_range_index = [char_index, &in_font_ranges](const int32_t in_range_index) -> cached_typeface_data* { + bool is_valid_index = in_range_index >= 0 && in_range_index < static_cast(in_font_ranges.size()); + if (!is_valid_index) + return nullptr; + bool is_contains = boost::icl::contains(in_font_ranges[in_range_index].range, char_index); + if (!is_contains) + return nullptr; + return in_font_ranges[in_range_index].cached_typeface.get(); + }; + + if (in_font_ranges.empty() || char_index < in_font_ranges.front().range.lower() || char_index > in_font_ranges.back().range.upper()) + return nullptr; + + const auto found_range_index = std::ranges::lower_bound(in_font_ranges, char_index, + &cached_font_range::sort_predicate, + &cached_font_range::binary_search_key); + const int32_t range_index = std::distance(in_font_ranges.begin(), found_range_index); + + if (range_index < 0 || range_index >= static_cast(in_font_ranges.size())) + return nullptr; + + if (auto range_typeface = get_typeface_from_range_index(range_index)) + return range_typeface; + if (auto range_typeface = get_typeface_from_range_index(range_index - 1)) + return range_typeface; + return nullptr; + }; + + if (auto typeface = get_typeface_from_range(cached_priority_font_ranges_)) + return typeface; + if (auto typeface = get_typeface_from_range(cached_font_ranges_)) + return typeface; + return cached_typefaces_[cached_typeface_default_index].get(); +} + +void cached_composite_font_data::get_cached_font_data(std::vector& out_font_data) const { + for (const auto& typeface : cached_typefaces_) { + typeface->get_cached_font_data(out_font_data); + } +} + +void cached_composite_font_data::refresh_font_ranges() { + const auto prioritized_culture_names = composite_font_->get_prioritized_culture_names(); +} diff --git a/src/renderer/core/fonts/font_cache_composite_font.h b/src/renderer/core/fonts/font_cache_composite_font.h new file mode 100644 index 0000000..a6150eb --- /dev/null +++ b/src/renderer/core/fonts/font_cache_composite_font.h @@ -0,0 +1,111 @@ +#pragma once +#include "composite_font.h" +#include "boost/flyweight.hpp" +#include + +class freetype_library; + +class cached_typeface_data { +public: + cached_typeface_data(); + cached_typeface_data(const typeface& in_typeface, float in_scaling_factor = 1.f); + + [[nodiscard]] const auto& get_typeface() const { + return typeface_; + } + + [[nodiscard]] auto get_scaling_factor() const { + return scaling_factor_; + } + + [[nodiscard]] auto get_primary_font_data() const { + return typeface_->fonts.size() > 0 ? &typeface_->fonts[0] : nullptr; + } + + [[nodiscard]] const font_data* get_font_data(const boost::flyweight& in_name) const; + + void get_cached_font_data(std::vector& out_font_data) const; +private: + struct cached_font_data { + cached_font_data() : font_data(nullptr) {} + cached_font_data(const boost::flyweight& in_name, const font_data* in_font_data) : name(in_name), font_data(in_font_data) {} + + static auto binary_search_key(const cached_font_data& in_cached_font_data) { + return in_cached_font_data.name; + } + + static bool key_sort_predicate(const boost::flyweight& lhs, const boost::flyweight& rhs) { + return lhs < rhs; + } + + static bool sort_predicate(const cached_font_data& lhs, const cached_font_data& rhs) { + return key_sort_predicate(lhs.name, rhs.name); + } + + boost::flyweight name; + const font_data* font_data; + }; + const typeface* typeface_; + std::vector cached_font_data_; + float scaling_factor_; +}; + +class cached_composite_font_data { +public: + cached_composite_font_data(); + cached_composite_font_data(const composite_font& in_composite_font); + + [[nodiscard]] const auto& get_composite_font() const { + return composite_font_; + } + + [[nodiscard]] auto get_default_typeface() const { + return cached_typefaces_[cached_typeface_default_index].get(); + } + + [[nodiscard]] auto get_fallback_typeface() const { + return cached_typefaces_[cached_typeface_fallback_index].get(); + } + + [[nodiscard]] auto get_typeface_for_codepoint(char32_t in_codepoint) const; + + void get_cached_font_data(std::vector& out_font_data) const; + + void refresh_font_ranges(); +private: + struct cached_font_range { + cached_font_range() : range(), cached_typeface(nullptr) {} + + cached_font_range(const boost::icl::discrete_interval& in_code_points, + const std::shared_ptr& in_cached_typeface) : range(in_code_points), cached_typeface(in_cached_typeface) { + } + + static int32_t binary_search_key(const cached_font_range& in_cached_font_range) { + return in_cached_font_range.range.lower(); + } + + static bool sort_predicate(const int32_t& lhs, const int32_t& rhs) { + return lhs < rhs; + } + + mutable boost::icl::discrete_interval range; + std::shared_ptr cached_typeface; + }; + + static constexpr int32_t cached_typeface_default_index = 0; + static constexpr int32_t cached_typeface_fallback_index = 1; + static constexpr int32_t cached_typeface_first_sub_typeface_index = 2; + + const composite_font* composite_font_; + std::vector> cached_typefaces_; + std::vector cached_priority_font_ranges_; + std::vector cached_font_ranges_; +}; + +class composite_font_cache { +public: + composite_font_cache(const freetype_library* in_library); + ~composite_font_cache(); + + const font_data& get_default_font_data(); +}; diff --git a/src/renderer/core/fonts/font_cache_freetype.cpp b/src/renderer/core/fonts/font_cache_freetype.cpp new file mode 100644 index 0000000..1fcf4ae --- /dev/null +++ b/src/renderer/core/fonts/font_cache_freetype.cpp @@ -0,0 +1,529 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "font_cache_freetype.h" + +#include + +namespace freetype_memory { + static void* alloc(FT_Memory memory, long size) { + return std::malloc(size); + } + + static void* realloc(FT_Memory memory, long cur_size, long new_size, void* block) { + return std::realloc(block, new_size); + } + + static void free(FT_Memory memory, void* block) { + std::free(block); + } +} + +namespace freetype_utils { + bool is_face_eligible_for_sdf(FT_Face in_face) { + return in_face && !FT_IS_TRICKY(in_face) && FT_IS_SCALABLE(in_face) && (!FT_HAS_FIXED_SIZES(in_face) || in_face->num_glyphs > 0); + } + + bool is_glyph_eligible_for_sdf(FT_GlyphSlot in_glyph) { + return in_glyph && in_glyph->format == FT_GLYPH_FORMAT_OUTLINE; + } + + FT_F26Dot6 frac_26dot6(const FT_F26Dot6 in_value) { + return in_value & 63; + } + + FT_F26Dot6 floor_26dot6(const FT_F26Dot6 in_value) { + return in_value & -64; + } + + FT_F26Dot6 ceil_26dot6(const FT_F26Dot6 in_value) { + return floor_26dot6(in_value + 63); + } + + FT_F26Dot6 round_26dot6(const FT_F26Dot6 in_value) { + return floor_26dot6(in_value + 32); + } + + FT_F26Dot6 determine_26dot6_ppem(const float in_font_size, const float in_font_scale, const bool in_round_ppem) { + FT_F26Dot6 ppem = FT_MulFix( + (convert_pixel_to_26dot6(std::max(0.f, in_font_size)) * (FT_Long)font_constants::render_dpi + 36) / 72, + convert_pixel_to_16dot16(std::max(0.f, in_font_scale)) + ); + if (in_round_ppem) { + ppem = round_26dot6(ppem); + } + return ppem; + } + + FT_Fixed determine_em_scale(const uint16_t in_em_size, const FT_F26Dot6 in_ppem) { + return FT_DivFix(in_ppem, std::max(uint16_t{1}, in_em_size)); + } + + FT_Fixed determine_ppem_and_em_scale(const uint16_t in_em_size, const float in_font_size, const float in_font_scale, const bool in_round_ppem) { + return determine_em_scale(in_em_size, determine_26dot6_ppem(in_font_size, in_font_scale, in_round_ppem)); + } + + uint32_t compute_font_pixel_size(float in_font_size, float in_font_scale) { + // 转换为固定比例以获得最大精度 + const FT_F26Dot6 fixed_font_size = convert_pixel_to_26dot6(std::max(0.f, in_font_size)); + const FT_Long fixed_font_scale = convert_pixel_to_16dot16(std::max(0.f, in_font_scale)); + + // 根据我们的渲染DPI和请求的比例,将请求的字体大小转换为像素大小 + + // 将26.6字符大小转换为未缩放的26.6像素大小 + // 注意:此处的逻辑与在使用FT_Set_Char_Size时FT_REQUEST_WIDTH和FT_REQUEST_HEIGHT内部执行的逻辑相同 + FT_F26Dot6 required_fixed_font_pixel_size = (fixed_font_size * FT_Pos{font_constants::render_dpi} + 36) / 72; + + // 将26.6像素大小按照期望的16.16分数缩放值进行缩放。 + required_fixed_font_pixel_size = FT_MulFix(required_fixed_font_pixel_size, fixed_font_scale); + + return convert_26dot6_to_rounded_pixel(required_fixed_font_pixel_size); + } + + void apply_size_and_scale(FT_Face in_face, const float in_font_size, const float in_font_scale) { + apply_size_and_scale(in_face, compute_font_pixel_size(in_font_size, in_font_scale)); + } + + void apply_size_and_scale(FT_Face in_face, const uint32_t required_font_pixel_size) { + if (FT_IS_SCALABLE(in_face)) { + FT_Error error = FT_Set_Pixel_Sizes(in_face, 0, required_font_pixel_size); + if (error) { + spdlog::error("无法设置字体大小:{}", error); + assert(false); + } + } else if (FT_HAS_FIXED_SIZES(in_face)) { + FT_F26Dot6 required_fixed_font_pixel_size = convert_pixel_to_26dot6(required_font_pixel_size); + + int32_t best_strike_index = -1; + { + FT_F26Dot6 running_best_fixed_strike_height = 0; + for (int32_t potential_strike_index = 0; potential_strike_index < in_face->num_fixed_sizes; ++potential_strike_index) { + const FT_F26Dot6 potential_fixed_strike_height = in_face->available_sizes[potential_strike_index].y_ppem; + + // 如果我们找到一个完美的匹配,我们就可以停止了 + if (potential_fixed_strike_height == required_fixed_font_pixel_size) { + best_strike_index = potential_strike_index; + break; + } + + // 第一次迭代时,我们将选择一个最接近的匹配 + if (best_strike_index == -1) { + best_strike_index = potential_strike_index; + running_best_fixed_strike_height = potential_fixed_strike_height; + continue; + } + + // 我们当前的笔画尺寸比所需尺寸小,因此选择此尺寸作为我们的当前笔画尺寸 + if (running_best_fixed_strike_height < required_fixed_font_pixel_size) { + // 但前提是它扩大到我们的所需尺寸 + if (potential_fixed_strike_height > running_best_fixed_strike_height) { + best_strike_index = potential_strike_index; + running_best_fixed_strike_height = potential_fixed_strike_height; + } + continue; + } + + // 我们当前的笔画尺寸比所需尺寸大,因此选择此尺寸作为我们的当前笔画尺寸 + if (running_best_fixed_strike_height > required_fixed_font_pixel_size) { + // 但前提是它缩小到我们的所需尺寸 + if (potential_fixed_strike_height < running_best_fixed_strike_height) { + best_strike_index = potential_strike_index; + running_best_fixed_strike_height = potential_fixed_strike_height; + } + } + } + } + + assert(best_strike_index != -1); + FT_Error error = FT_Select_Size(in_face, best_strike_index); + if (error) { + spdlog::error("无法选择字体大小:{}", error); + assert(false); + } + + FT_Long fixed_strike_scale = 0; + { + // 将26.6的值转换为16.16的空间,以便我们可以使用FT_DivFix + const FT_Long required_fixed_font_pixel_size_16dot16 = required_fixed_font_pixel_size << 10; + const FT_Long beat_fixed_strike_height_16dot16 = in_face->available_sizes[best_strike_index].y_ppem << 10; + fixed_strike_scale = FT_DivFix(required_fixed_font_pixel_size_16dot16, beat_fixed_strike_height_16dot16); + } + + // 固定大小字体不使用 x_scale/y_scale 值,所以我们使用它们来存储我们需要应用于位图的缩放调整(作为16.16分数缩放值),以将其缩放到我们期望的像素大小 + // 注意:从技术上讲,度量应该是只读的,所以如果这造成问题,那么我们将不得不将信息添加到 freetype_face 中,然后通过传递给调用 apply_size_and_scale 的所有内容。 + in_face->size->metrics.x_scale = fixed_strike_scale; + in_face->size->metrics.y_scale = fixed_strike_scale; + } + } + + FT_Error load_glyph(FT_Face in_face, const uint32_t in_glyph_index, const uint32_t in_load_flags, const float in_font_size, const float in_font_scale) { + return load_glyph(in_face, in_glyph_index, in_load_flags, compute_font_pixel_size(in_font_size, in_font_scale)); + } + + FT_Error load_glyph(FT_Face in_face, const uint32_t in_glyph_index, const int32_t in_load_flags, const uint32_t in_required_font_pixel_size) { + assert(!(in_load_flags & FT_LOAD_NO_SCALE)); + apply_size_and_scale(in_face, in_required_font_pixel_size); + return FT_Load_Glyph(in_face, in_glyph_index, in_load_flags); + } + + FT_Pos get_height(FT_Face in_face, const font_layout_method in_layout_method) { + if (FT_IS_SCALABLE(in_face)) { + return in_layout_method == font_layout_method::METRICS + ? FT_Pos{ in_face->height } + : in_face->bbox.yMax - in_face->bbox.yMin; + } + if (FT_HAS_FIXED_SIZES(in_face)) { + return in_face->size->metrics.height; + } + return 0; + } + + FT_Pos get_scaled_height(FT_Face in_face, const font_layout_method in_layout_method) { + if (FT_IS_SCALABLE(in_face)) { + return FT_MulFix( + in_layout_method == font_layout_method::METRICS ? in_face->height : in_face->bbox.yMax - in_face->bbox.yMin, + in_face->size->metrics.y_scale + ); + } + if (FT_HAS_FIXED_SIZES(in_face)) { + return FT_MulFix(in_face->size->metrics.height, in_face->size->metrics.y_scale); + } + return 0; + } + + FT_Pos get_ascender(FT_Face in_face, const font_layout_method in_layout_method) { + if (FT_IS_SCALABLE(in_face)) { + return FT_MulFix( + in_layout_method == font_layout_method::METRICS ? FT_Pos{ in_face->ascender } : in_face->bbox.yMax, + in_face->size->metrics.y_scale + ); + } + if (FT_HAS_FIXED_SIZES(in_face)) { + return FT_MulFix(in_face->size->metrics.ascender, in_face->size->metrics.y_scale); + } + return 0; + } + + FT_Pos get_descender(FT_Face in_face, const font_layout_method in_layout_method) { + if (FT_IS_SCALABLE(in_face)) { + return FT_MulFix( + in_layout_method == font_layout_method::METRICS ? FT_Pos{ in_face->descender } : in_face->bbox.yMin, + in_face->size->metrics.y_scale + ); + } + if (FT_HAS_FIXED_SIZES(in_face)) { + return FT_MulFix(in_face->size->metrics.descender, in_face->size->metrics.y_scale); + } + return 0; + } + + float get_bitmap_atlas_scale(FT_Face in_face) { + if (!FT_IS_SCALABLE(in_face) && FT_HAS_FIXED_SIZES(in_face)) { + // 我们只将图像缩小以适应图集 + // 如果它们小于我们期望的尺寸,我们就让它们在渲染时在 GPU 上进行缩放(请参阅 get_bitmap_render_scale) + if (in_face->size->metrics.x_scale < convert_pixel_to_16dot16(1)) { + // 固定大小字体不支持缩放,但我们在 apply_size_and_scale 中计算了要用于字形的缩放比例 + return static_cast(in_face->size->metrics.x_scale) / 65536.f; // 16.16 -> pixel scale + } + } + return 1.f; + } + + float get_bitmap_render_scale(FT_Face in_face) { + if (!FT_IS_SCALABLE(in_face) && FT_HAS_FIXED_SIZES(in_face)) { + // 我们只在渲染时才放大图像 + // 如果它们大于我们期望的尺寸,我们会在它们进入图集之前对它们进行缩放(请参阅 get_bitmap_atlas_scale) + if (in_face->size->metrics.x_scale < convert_pixel_to_16dot16(1)) { + // 固定大小字体不支持缩放,但我们在 apply_size_and_scale 中计算了用于字形的缩放比例 + return static_cast(in_face->size->metrics.x_scale) / 65536.f; // 16.16 -> pixel scale + } + } + return 1.f; + } +} + +freetype_library::freetype_library() { + custom_memory = static_cast(malloc(sizeof(*custom_memory))); + custom_memory->alloc = freetype_memory::alloc; + custom_memory->realloc = freetype_memory::realloc; + custom_memory->free = freetype_memory::free; + custom_memory->user = nullptr; + + FT_Error error = FT_New_Library(custom_memory, &library); + if (error) { + spdlog::critical("无法创建FreeType库:{}", error); + } + + FT_Add_Default_Modules(library); + + FT_UInt interpreter_version = TT_INTERPRETER_VERSION_40; + error = FT_Property_Set(library, "truetype", "interpreter-version", &interpreter_version); + + static bool logged_version = false; + if (!logged_version) { + FT_Int major = 0; + FT_Int minor = 0; + FT_Int patch = 0; + FT_Library_Version(library, &major, &minor, &patch); + spdlog::info("FreeType版本 {}.{}.{}", major, minor, patch); + logged_version = true; + } +} + +freetype_library::~freetype_library() { + FT_Done_Library(library); + free(custom_memory); +} + +freetype_face::freetype_face(const freetype_library* in_library, font_face_data_const_ptr in_memory, + const int32_t in_face_index, const font_layout_method in_layout_method) { + face = nullptr; + pending_async_load = true; + layout_method = in_layout_method; + complete_async_load(in_library, in_memory, in_face_index); +} + +freetype_face::freetype_face(const font_layout_method in_layout_method) { + face = nullptr; + pending_async_load = true; + layout_method = in_layout_method; +} + +freetype_face::~freetype_face() { + if (!face) + return; + + FT_Done_Face(face); +} + +void freetype_face::fail_async_load() { + pending_async_load = false; +} + +void freetype_face::complete_async_load(const freetype_library* in_library, font_face_data_const_ptr in_memory, const int32_t in_face_index) { + pending_async_load = false; + + face = nullptr; + memory = std::move(in_memory); + + FT_Error error = FT_New_Memory_Face(in_library->get_library(), memory->data(), memory->size(), in_face_index, &face); + if (error) { + spdlog::error("无法加载字体:{}", error); + face = nullptr; + } + + parse_styles(); +} + +std::vector freetype_face::get_available_sub_faces(const freetype_library* in_library, font_face_data_const_ptr in_memory) { + std::vector result; + + FT_Face face = nullptr; + FT_Error error = FT_New_Memory_Face(in_library->get_library(), in_memory->data(), in_memory->size(), -1, &face); + if (!face) + return result; + + const int32_t num_faces = face->num_faces; + FT_Done_Face(face); + face = nullptr; + + result.reserve(num_faces); + for (int i = 0; i < num_faces; ++i) { + FT_New_Memory_Face(in_library->get_library(), in_memory->data(), in_memory->size(), i, &face); + if (!face) + continue; + + result.emplace_back(face->style_name); + FT_Done_Face(face); + face = nullptr; + } + + return result; +} + +void freetype_face::parse_styles() { + if (!face) + return; + + std::string style_name = face->style_name; + if (style_name.empty()) + return; + + // 将样式按照空格分割 + size_t start = 0; + size_t end = 0; + while (end != std::string::npos) { + end = style_name.find(' ', start); + styles.push_back(style_name.substr(start, end - start)); + start = end + 1; + } +} + +freetype_glyph_cache::freetype_glyph_cache(FT_Face in_face, const int32_t in_load_flags, const float in_font_size, + const float in_font_scale) : face(in_face), load_flags(in_load_flags), font_render_size(freetype_utils::compute_font_pixel_size(in_font_size, in_font_scale)) { + +} + +bool freetype_glyph_cache::find_or_cache(const uint32_t in_glyph_index, cached_glyph_data& out) { + if (auto found_cached_glyph_data = glyph_data_map.find(in_glyph_index); found_cached_glyph_data != glyph_data_map.end()) { + out = found_cached_glyph_data->second; + return true; + } + + FT_Error error = freetype_utils::load_glyph(face, in_glyph_index, load_flags, font_render_size); + if (error) { + spdlog::error("无法加载字形:{}", error); + return false; + } + + out = {}; + out.height = face->height; + out.glyph_metrics = face->glyph->metrics; + out.size_metrics = face->size->metrics; + + if (face->glyph->outline.n_points > 0) { + const int32_t num_points = face->glyph->outline.n_points; + out.outline_points.resize(num_points); + for (int32_t i = 0; i < num_points; ++i) { + out.outline_points[i] = face->glyph->outline.points[i]; + } + } + + glyph_data_map[in_glyph_index] = out; + return true; +} + +freetype_advance_cache::freetype_advance_cache() :face(nullptr), load_flags(), font_render_size() { +} + +freetype_advance_cache::freetype_advance_cache(FT_Face in_face, int32_t in_load_flags, float in_font_size, + float in_font_scale) : face(in_face), load_flags(in_load_flags), + font_render_size( + 0 != (in_load_flags & FT_LOAD_NO_SCALE) ? + freetype_utils::determine_ppem_and_em_scale(in_face->units_per_EM, in_font_size, in_font_scale, true) : + freetype_utils::compute_font_pixel_size(in_font_size, in_font_scale) + ) { + assert(((load_flags & FT_LOAD_NO_SCALE) == 0) || freetype_utils::is_face_eligible_for_sdf(face)); +} + +bool freetype_advance_cache::find_or_cache(uint32_t in_glyph_index, FT_Fixed& out) { + if (!face) + return false; + if (const auto found_advance = advance_map.find(in_glyph_index); found_advance != advance_map.end()) { + out = found_advance->second; + return true; + } + + if ((load_flags & FT_LOAD_NO_SCALE) != 0) { + const FT_Error error = FT_Get_Advance(face, in_glyph_index, load_flags, &out); + if (error) { + spdlog::error("无法获取字形进度:{}", error); + return false; + } + FT_Long em_scale = font_render_size; + out = FT_MulDiv(out, em_scale, FT_Long{ 64L }); + advance_map[in_glyph_index] = out; + return true; + } + + freetype_utils::apply_size_and_scale(face, font_render_size); + + const FT_Error error = FT_Get_Advance(face, in_glyph_index, load_flags, &out); + if (error) { + spdlog::error("无法获取字形进度:{}", error); + return false; + } + + if (!FT_IS_SCALABLE(face) && FT_HAS_FIXED_SIZES(face)) { + out = FT_MulFix(out, load_flags & FT_LOAD_VERTICAL_LAYOUT ? face->size->metrics.y_scale : face->size->metrics.x_scale); + } + + advance_map[in_glyph_index] = out; + return true; +} + +freetype_kerning_cache::freetype_kerning_cache(FT_Face in_face, int32_t in_kerning_flags, float in_font_size, + float in_font_scale) : face(in_face), kerning_flags(in_kerning_flags), font_render_size( + in_kerning_flags == FT_KERNING_UNSCALED ? + freetype_utils::determine_ppem_and_em_scale(in_face->units_per_EM, in_font_size, in_font_scale, true) : + freetype_utils::compute_font_pixel_size(in_font_size, in_font_scale) + ) { + assert(face); + assert(FT_HAS_KERNING(face)); + assert(kerning_flags != FT_KERNING_UNSCALED || freetype_utils::is_face_eligible_for_sdf(face)); +} + +bool freetype_kerning_cache::find_or_cache(uint32_t in_first_glyph_index, uint32_t in_second_glyph_index, + FT_Vector& out) { + const kerning_pair kerning_key(in_first_glyph_index, in_second_glyph_index); + if (const auto found_kerning = kerning_map.find(kerning_key); found_kerning != kerning_map.end()) { + out = found_kerning->second; + return true; + } + + if (kerning_flags == FT_KERNING_UNSCALED) { + const FT_Error error = FT_Get_Kerning(face, in_first_glyph_index, in_second_glyph_index, kerning_flags, &out); + if (error) { + spdlog::error("无法获取字形间距:{}", error); + return false; + } + FT_Long em_scale = font_render_size; + out.x = FT_MulFix(out.x, em_scale); + out.y = FT_MulFix(out.y, em_scale); + kerning_map[kerning_key] = out; + return true; + } + + freetype_utils::apply_size_and_scale(face, font_render_size); + + const FT_Error error = FT_Get_Kerning(face, in_first_glyph_index, in_second_glyph_index, kerning_flags, &out); + if (error) { + spdlog::error("无法获取字形间距:{}", error); + return false; + } + + if (!FT_IS_SCALABLE(face) && FT_HAS_FIXED_SIZES(face)) { + out.x = FT_MulFix(out.x, face->size->metrics.x_scale); + out.y = FT_MulFix(out.y, face->size->metrics.y_scale); + } + kerning_map[kerning_key] = out; + return true; +} + +freetype_cache_directory::freetype_cache_directory() : invalid_advance_cache(std::make_shared()) { +} + +std::shared_ptr freetype_cache_directory::get_glyph_cache(FT_Face in_face, int32_t in_flags, + float in_font_size, float in_font_scale) { + const font_key key(in_face, in_flags, in_font_size, in_font_scale); + auto result = glyph_cache_map.try_emplace(key, std::make_shared(in_face, in_flags, in_font_size, in_font_scale)); + return result.first->second; +} + +std::shared_ptr freetype_cache_directory::get_advance_cache(FT_Face in_face, int32_t in_flags, + float in_font_size, float in_font_scale) { + const font_key key(in_face, in_flags, in_font_size, in_font_scale); + auto result = advance_cache_map.try_emplace(key, std::make_shared(in_face, in_flags, in_font_size, in_font_scale)); + return result.first->second; +} + +std::shared_ptr freetype_cache_directory::get_kerning_cache(FT_Face in_face, int32_t in_flags, + float in_font_size, float in_font_scale) { + if (!in_face) + return nullptr; + if (!FT_HAS_KERNING(in_face)) + return nullptr; + if (in_flags == FT_KERNING_UNFITTED) + return nullptr; + if (!freetype_utils::is_face_eligible_for_sdf(in_face)) + return nullptr; + + const font_key key(in_face, in_flags, in_font_size, in_font_scale); + auto result = kerning_cache_map.try_emplace(key, std::make_shared(in_face, in_flags, in_font_size, in_font_scale)); + return result.first->second; +} + +void freetype_cache_directory::flush_cache() { + glyph_cache_map.clear(); + advance_cache_map.clear(); + kerning_cache_map.clear(); +} diff --git a/src/renderer/core/fonts/font_cache_freetype.h b/src/renderer/core/fonts/font_cache_freetype.h new file mode 100644 index 0000000..0748a50 --- /dev/null +++ b/src/renderer/core/fonts/font_cache_freetype.h @@ -0,0 +1,343 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include +#include +#include +#include + +#include "composite_font.h" +#include "ft2build.h" +#include "misc/type_hash.h" + +#include FT_GLYPH_H +#include FT_MODULE_H +#include FT_BITMAP_H +#include FT_ADVANCES_H +#include FT_STROKER_H +#include FT_SIZES_H +#include FT_DRIVER_H + +namespace font_constants { + inline constexpr uint32_t render_dpi = 96; +} + +enum class font_fallback { + no_fallback, + last_resort_fallback, + max_fallback, +}; + +namespace freetype_utils { + /** + * 检查字体是否适合生成SDF + * @param in_face 字体 + * @return 是否适合生成SDF + */ + bool is_face_eligible_for_sdf(FT_Face in_face); + + /** + * 检查字形是否适合生成SDF + * @param in_glyph 字形 + * @return 是否适合生成SDF + */ + bool is_glyph_eligible_for_sdf(FT_GlyphSlot in_glyph); + + FT_F26Dot6 frac_26dot6(FT_F26Dot6 in_value); + + /** + * 将26.6空间中的值向上取整 + * @param in_value 输入值 + * @return 向上取整后的值 + */ + FT_F26Dot6 floor_26dot6(FT_F26Dot6 in_value); + + /** + * 将26.6空间中的值向下取整 + * @param in_value 输入值 + * @return 向下取整后的值 + */ + FT_F26Dot6 ceil_26dot6(FT_F26Dot6 in_value); + + /** + * 将26.6空间中的值四舍五入 + * @param in_value 输入值 + * @return + */ + FT_F26Dot6 round_26dot6(FT_F26Dot6 in_value); + + /** + * 从以点为单位的字体大小(每英寸72点)和96 dpi分辨率下的任意UI缩放确定(可选四舍五入)像素尺寸(每em平方维度的像素数) + * @param in_font_size 字体大小 + * @param in_font_scale UI缩放 + * @param in_round_ppem 每em平方维度的像素数 + * @return + */ + FT_F26Dot6 determine_26dot6_ppem(float in_font_size, float in_font_scale, bool in_round_ppem); + + /** + * EmScale将设计空间距离(相对于em方块,分辨率为InEmSize单位)映射到设备像素平面中的绝对1/64像素距离(96 dpi分辨率) + * @param in_em_size 字符大小 + * @param in_ppem 每em平方维度的像素数 + * @return + */ + FT_Fixed determine_em_scale(uint16_t in_em_size, FT_F26Dot6 in_ppem); + + /** + * 在一次调用中确定(可选四舍五入)ppem,然后根据它确定EmScale + * @param in_em_size + * @param in_font_size + * @param in_font_scale + * @param in_round_ppem + * @return + */ + FT_Fixed determine_ppem_and_em_scale(uint16_t in_em_size, float in_font_size, float in_font_scale, bool in_round_ppem); + + uint32_t compute_font_pixel_size(float in_font_size, float in_font_scale); + + void apply_size_and_scale(FT_Face in_face, float in_font_size, float in_font_scale); + + void apply_size_and_scale(FT_Face in_face, uint32_t required_font_pixel_size); + + FT_Error load_glyph(FT_Face in_face, uint32_t in_glyph_index, uint32_t in_load_flags, float in_font_size, float in_font_scale); + FT_Error load_glyph(FT_Face in_face, uint32_t in_glyph_index, int32_t in_load_flags, uint32_t in_required_font_pixel_size); + + FT_Pos get_height(FT_Face in_face, font_layout_method in_layout_method); + FT_Pos get_scaled_height(FT_Face in_face, font_layout_method in_layout_method); + FT_Pos get_ascender(FT_Face in_face, font_layout_method in_layout_method); + FT_Pos get_descender(FT_Face in_face, font_layout_method in_layout_method); + + float get_bitmap_atlas_scale(FT_Face in_face); + float get_bitmap_render_scale(FT_Face in_face); + + template + std::enable_if_t, ret_type> convert_26dot6_to_rounded_pixel(param_type in_value) { + return static_cast((in_value + (1 << 5)) >> 6); + } + + template + std::enable_if_t, ret_type> convert_26dot6_to_rounded_pixel(param_type in_value) { + return static_cast(std::round(in_value / 64.0)); + } + + template + std::enable_if_t, ret_type> convert_pixel_to_26dot6(param_type in_value) { + return static_cast(in_value << 6); + } + + template + std::enable_if_t, ret_type> convert_pixel_to_26dot6(param_type in_value) { + return static_cast(in_value * 64.0); + } + + template + std::enable_if_t, ret_type> convert_pixel_to_16dot16(param_type in_value) { + return static_cast(in_value << 16); + } + + template + std::enable_if_t, ret_type> convert_pixel_to_16dot16(param_type in_value) { + return static_cast(in_value * 65536); + } +} + +class freetype_library { +public: + freetype_library(); + ~freetype_library(); + + [[nodiscard]] FT_Library get_library() const noexcept { return library; } +private: + FT_Library library; + FT_Memory custom_memory; +}; + +class freetype_face { +public: + freetype_face(const freetype_library* in_library, font_face_data_const_ptr in_memory, int32_t in_face_index, font_layout_method in_layout_method); + + explicit freetype_face(font_layout_method in_layout_method); + ~freetype_face(); + + [[nodiscard]] bool is_face_valid() const noexcept { return face != nullptr; } + [[nodiscard]] bool is_face_loading() const noexcept { return pending_async_load; } + [[nodiscard]] bool support_sdf() const noexcept { return freetype_utils::is_face_eligible_for_sdf(face); } + + [[nodiscard]] FT_Face get_face() const noexcept { return face; } + + [[nodiscard]] FT_Pos get_height() const { return freetype_utils::get_height(face, layout_method); } + [[nodiscard]] FT_Pos get_scaled_height() const { return freetype_utils::get_scaled_height(face, layout_method); } + [[nodiscard]] FT_Pos get_ascender(bool in_allow_override) const { + if (in_allow_override && ascent_override.has_value()) { + FT_F26Dot6 scaled_ascender = FT_MulFix(ascent_override.value(), face->size->metrics.y_scale); + return scaled_ascender + 0b111111 & ~0b111111; //(26.6固定点向上取整)。使用缩放上升的天花板,正如Freetype建议的那样,以避免网格拟合/提示问题。 + } + return freetype_utils::get_ascender(face, layout_method); + } + [[nodiscard]] FT_Pos get_descender(bool in_allow_override) const { + if (in_allow_override && descent_override.has_value()) { + FT_F26Dot6 scaled_descender = FT_MulFix(descent_override.value(), face->size->metrics.y_scale); + return scaled_descender + 0b111111 & ~0b111111; //(26.6固定点向下取整)。使用缩放下降的地板,正如Freetype建议的那样,以避免网格拟合/提示问题。 + } + return freetype_utils::get_descender(face, layout_method); + } + + [[nodiscard]] float get_bitmap_atlas_scale() const { return freetype_utils::get_bitmap_atlas_scale(face); } + [[nodiscard]] float get_bitmap_render_scale() const { return freetype_utils::get_bitmap_render_scale(face); } + + [[nodiscard]] const auto& get_styles() const noexcept { return styles; } + [[nodiscard]] font_layout_method get_layout_method() const noexcept { return layout_method; } + + void override_ascent(bool in_override, const int32_t in_value = 0) { + if (in_override) { + ascent_override = freetype_utils::convert_pixel_to_26dot6(in_value); + } else { + ascent_override.reset(); + } + } + void override_descent(bool in_override, const int32_t in_value = 0) { + if (in_override) { + descent_override = freetype_utils::convert_pixel_to_26dot6(in_value); + } else { + descent_override.reset(); + } + } + + void fail_async_load(); + void complete_async_load(const freetype_library* in_library, font_face_data_const_ptr in_memory, int32_t in_face_index); + + static std::vector get_available_sub_faces(const freetype_library* in_library, font_face_data_const_ptr in_memory); +private: + void parse_styles(); + + FT_Face face; + font_face_data_const_ptr memory; + + bool pending_async_load = false; + + std::optional ascent_override; + std::optional descent_override; + font_layout_method layout_method; + std::vector styles; +}; + +class freetype_glyph_cache { +public: + struct cached_glyph_data { + FT_Short height; + FT_Glyph_Metrics glyph_metrics; + FT_Size_Metrics size_metrics; + std::vector outline_points; + }; + + freetype_glyph_cache(FT_Face in_face, int32_t in_load_flags, float in_font_size, float in_font_scale); + [[nodiscard]] bool find_or_cache(uint32_t in_glyph_index, cached_glyph_data& out); +private: + FT_Face face; + const int32_t load_flags; + const uint32_t font_render_size; + std::unordered_map glyph_data_map; +}; + +class freetype_advance_cache { +public: + freetype_advance_cache(); + freetype_advance_cache(FT_Face in_face, int32_t in_load_flags, float in_font_size, float in_font_scale); + + bool find_or_cache(uint32_t in_glyph_index, FT_Fixed& out); +private: + FT_Face face; + const int32_t load_flags; + const uint32_t font_render_size; + std::unordered_map advance_map; +}; + +struct kerning_pair { + kerning_pair(uint32_t in_first_glyph_index, uint32_t in_second_glyph_index) : first_glyph_index( + in_first_glyph_index), second_glyph_index(in_second_glyph_index) { + } + + bool operator==(const kerning_pair& other) const { + return first_glyph_index == other.first_glyph_index && second_glyph_index == other.second_glyph_index; + } + + bool operator!=(const kerning_pair& other) const { return !(*this == other); } + + uint32_t first_glyph_index; + uint32_t second_glyph_index; +}; +namespace std { + template<> + struct hash + { + std::size_t operator()(const kerning_pair& key) const + { + return hash_combine(get_type_hash(key.first_glyph_index), get_type_hash(key.second_glyph_index)); + } + }; +} + +class font_key { +public: + font_key(FT_Face in_face, int32_t in_flags, float in_font_size, float in_font_scale) : face(in_face), + flags(in_flags), font_render_size(freetype_utils::compute_font_pixel_size(in_font_size, in_font_scale)), + key_hash() { + key_hash = get_type_hash(face); + key_hash = hash_combine(key_hash, get_type_hash(flags)); + key_hash = hash_combine(key_hash, get_type_hash(font_render_size)); + } + + bool operator==(const font_key& other) const { + return face == other.face && flags == other.flags && font_render_size == other.font_render_size; + } + + bool operator!=(const font_key& other) const { return !(*this == other); } + + friend uint32_t get_type_hash(const font_key& in_key) { return in_key.key_hash; } + +private: + FT_Face face; + const int32_t flags; + const uint32_t font_render_size; + uint32_t key_hash; +}; + +namespace std +{ + template<> + struct hash + { + std::size_t operator()(const font_key& key) const + { + return get_type_hash(key); + } + }; +} + +class freetype_kerning_cache { +public: + freetype_kerning_cache(FT_Face in_face, int32_t in_kerning_flags, float in_font_size, float in_font_scale); + + bool find_or_cache(uint32_t in_first_glyph_index, uint32_t in_second_glyph_index, FT_Vector& out); +private: + FT_Face face; + const int32_t kerning_flags; + const uint32_t font_render_size; + std::unordered_map kerning_map; +}; + +class freetype_cache_directory { +public: + freetype_cache_directory(); + std::shared_ptr get_glyph_cache(FT_Face in_face, int32_t in_flags, float in_font_size, float in_font_scale); + std::shared_ptr get_advance_cache(FT_Face in_face, int32_t in_flags, float in_font_size, float in_font_scale); + std::shared_ptr get_kerning_cache(FT_Face in_face, int32_t in_flags, float in_font_size, float in_font_scale); + + void flush_cache(); +private: + std::unordered_map> glyph_cache_map; + std::unordered_map> advance_cache_map; + std::unordered_map> kerning_cache_map; + std::shared_ptr invalid_advance_cache; +}; diff --git a/src/renderer/core/fonts/font_info.cpp b/src/renderer/core/fonts/font_info.cpp new file mode 100644 index 0000000..d799d58 --- /dev/null +++ b/src/renderer/core/fonts/font_info.cpp @@ -0,0 +1,38 @@ +#include "font_info.h" + +font_info::font_info(const std::shared_ptr& in_composite_font, float in_size, + const boost::flyweight& in_typeface_font_name, const font_outline_settings& in_outline_settings) : + outline_settings(in_outline_settings), font(in_composite_font), typeface_font_name(in_typeface_font_name), size(in_size) { +} + +font_info::font_info(const std::string& in_font_name, float in_size, + const font_outline_settings& in_outline_settings) : outline_settings(in_outline_settings), + typeface_font_name(in_font_name), + size(in_size) { + upgrade_legacy_font(boost::flyweight{ in_font_name }, font_hinting::DEFAULT); +} + +font_info::font_info(const boost::flyweight& in_typeface_font_name, float in_size, + const font_outline_settings& in_outline_settings) : outline_settings(in_outline_settings), + typeface_font_name(in_typeface_font_name), + size(in_size) { + upgrade_legacy_font(in_typeface_font_name, font_hinting::DEFAULT); +} + +bool font_info::has_valid_font() const { + return font != nullptr; +} + +const composite_font* font_info::get_composite_font() const { + return font.get(); +} + +float font_info::get_clamp_size() const { + GetWeightFromFT(); +} + +float font_info::get_clamp_skew() const { +} + +void font_info::upgrade_legacy_font(boost::flyweight in_legacy_font_name, font_hinting in_hinting) { +} diff --git a/src/renderer/core/fonts/font_info.h b/src/renderer/core/fonts/font_info.h new file mode 100644 index 0000000..f833647 --- /dev/null +++ b/src/renderer/core/fonts/font_info.h @@ -0,0 +1,94 @@ +#pragma once +#include + +#include "composite_font.h" +#include "font_cache_freetype.h" +#include "misc/color.h" + +struct font_outline_settings { + font_outline_settings() : outline_size(0), mitered_corners(false), separate_fill_alpha(false), + apply_outline_to_drop_shadows(false), outline_color(linear_color::black) { + + } + + font_outline_settings(int32_t in_outline_size, + const linear_color in_color = linear_color::black) : outline_size(in_outline_size), + mitered_corners(false), + separate_fill_alpha(false), + apply_outline_to_drop_shadows(false), + outline_color(in_color) { + + } + + [[nodiscard]] bool is_identical_to_for_caching(const font_outline_settings& other) const { + return outline_size == other.outline_size && + mitered_corners == other.mitered_corners && + separate_fill_alpha == other.separate_fill_alpha; + } + + [[nodiscard]] bool is_identical_to(const font_outline_settings& other) const { + return outline_size == other.outline_size && + mitered_corners == other.mitered_corners && + separate_fill_alpha == other.separate_fill_alpha && + apply_outline_to_drop_shadows == other.apply_outline_to_drop_shadows && + outline_color == other.outline_color; + } + + [[nodiscard]] bool is_visible() const { + return outline_size > 0 && outline_color.a > 0; + } + + int32_t outline_size; + bool mitered_corners; + bool separate_fill_alpha; + bool apply_outline_to_drop_shadows; + linear_color outline_color; + + inline static font_outline_settings no_outline; +}; + +struct font_info { + font_info(const std::shared_ptr& in_composite_font, float in_size, const boost::flyweight& in_typeface_font_name = {}, const font_outline_settings& in_outline_settings = {}); + font_info(const std::string& in_font_name, float in_size, const font_outline_settings& in_outline_settings = {}); + font_info(const boost::flyweight& in_typeface_font_name, float in_size, const font_outline_settings& in_outline_settings = {}); + + [[nodiscard]] bool is_legacy_identical_to(const font_info& other) const { + return outline_settings.is_identical_to_for_caching(other.outline_settings) && + font == other.font && + typeface_font_name == other.typeface_font_name && + get_clamp_size() == other.get_clamp_size(); + } + + [[nodiscard]] bool is_identical_to(const font_info& other) const { + return outline_settings.is_identical_to(other.outline_settings) && + font == other.font && + typeface_font_name == other.typeface_font_name && + size == other.size && + letter_spacing == other.letter_spacing && + skew_amount == other.skew_amount && + fallback_level == other.fallback_level && + force_monospaced == other.force_monospaced && + (force_monospaced ? monospaced_width == other.monospaced_width : true); + } + + [[nodiscard]] bool operator==(const font_info& other) const { + return is_identical_to(other); + } + + [[nodiscard]] bool has_valid_font() const; + [[nodiscard]] const composite_font* get_composite_font() const; + [[nodiscard]] float get_clamp_size() const; + [[nodiscard]] float get_clamp_skew() const; + + font_outline_settings outline_settings; + std::shared_ptr font; + boost::flyweight typeface_font_name; + float size; + int32_t letter_spacing = 0; + float skew_amount = 0.f; + font_fallback fallback_level; + bool force_monospaced = false; + float monospaced_width = 1.f; +private: + void upgrade_legacy_font(boost::flyweight in_legacy_font_name, font_hinting in_hinting); +}; diff --git a/src/renderer/core/fonts/font_rasterization_mode.h b/src/renderer/core/fonts/font_rasterization_mode.h new file mode 100644 index 0000000..ac9d4fb --- /dev/null +++ b/src/renderer/core/fonts/font_rasterization_mode.h @@ -0,0 +1,24 @@ +#pragma once + +// 字体光栅化方式 +enum class font_rasterization_mode { + // 字形按大小和倾斜直接栅格化为 Alpha 掩码位图。 + BITMAP, + // 字形被栅格化为多通道有符号距离字段,这些字段的大小和倾斜无关。 + MSDF, + // 字形被栅格化为单通道有符号距离字段,这些字段的大小和倾斜无关。内存效率更高,但边角可能会显得圆角。 + SDF, + // 字形被栅格化为近似距离字段,这些字段的大小和倾斜无关。内存和计算效率更高,但质量较低。 + SDF_APPROXIMATION +}; + +inline bool is_sdf_font_rasterization_mode(font_rasterization_mode mode) { + switch (mode) { + case font_rasterization_mode::MSDF: + case font_rasterization_mode::SDF: + case font_rasterization_mode::SDF_APPROXIMATION: + return true; + default: + return false; + } +} diff --git a/src/renderer/core/fonts/font_renderer.cpp b/src/renderer/core/fonts/font_renderer.cpp new file mode 100644 index 0000000..4895428 --- /dev/null +++ b/src/renderer/core/fonts/font_renderer.cpp @@ -0,0 +1 @@ +#include "font_renderer.h" diff --git a/src/renderer/core/fonts/font_renderer.h b/src/renderer/core/fonts/font_renderer.h new file mode 100644 index 0000000..a2af63f --- /dev/null +++ b/src/renderer/core/fonts/font_renderer.h @@ -0,0 +1,26 @@ +#pragma once +#include "font_cache_freetype.h" + +class composite_font_cache; + +struct freetype_face_glyph_data { + std::shared_ptr face_and_memory; + uint32_t glyph_index; + uint32_t glyph_flags; + font_fallback char_fallback_level; + + freetype_face_glyph_data() : face_and_memory(nullptr), glyph_index(0), glyph_flags(0), char_fallback_level(font_fallback::no_fallback) { + } +}; + +namespace font_renderer_utils { + inline const char32_t invalid_sub_char = u'\uFFFD'; + void append_glyph_flags(const freetype_face& in_face, const font_data& in_font_data, uint32_t in_glyph_flags); +} + +class font_renderer { +public: + font_renderer(const freetype_library* in_library, freetype_cache_directory* in_cache_directory, composite_font* in_composite_font_cache); + + uint16_t get_max_height(const font_info) const; +}; diff --git a/src/renderer/core/fonts/sdf_generator.cpp b/src/renderer/core/fonts/sdf_generator.cpp new file mode 100644 index 0000000..acaeee7 --- /dev/null +++ b/src/renderer/core/fonts/sdf_generator.cpp @@ -0,0 +1,300 @@ +#include "sdf_generator.h" + +#include +#include +#include + +#include +#include + +#include "font_cache_freetype.h" +#include +#include +#include + +#include "core/pixel_format/pixel.h" +#include "misc/scope_exit.h" + +#define MAX_GLYPH_SDF_SIDE 4096 + +static constexpr double SDF_CORNER_ANGLE_THRESHOLD = 3.0; + +static constexpr double SDF_BOUNDS_MITER_LIMIT = 1.0; + +class glyph_sdf_mapping { +public: + void wrap_non_mitered(const msdfgen::Shape::Bounds& in_bounds, const uint16_t in_units_per_em, const int32_t in_ppem, const float in_em_outer_spread) { + wrap(nullptr, in_bounds, in_units_per_em, in_ppem, in_em_outer_spread, 0); + } + + void wrap_mitered(const msdfgen::Shape& msdfgen_shape, const msdfgen::Shape::Bounds& in_bounds, const uint16_t in_units_per_em, const int32_t in_ppem, const float in_em_outer_spread, const double in_miter_limit) { + wrap(&msdfgen_shape, in_bounds, in_units_per_em, in_ppem, in_em_outer_spread, in_miter_limit); + } + + void set_spread(uint16_t in_units_per_em, float in_em_outer_spread, float in_em_inner_spread); + + [[nodiscard]] const auto& get_msdfgen_transformation() const { + return transformation; + } + + [[nodiscard]] int32_t get_sdf_width() const { + return sdf_bounds.max().x() - sdf_bounds.min().x(); + } + + [[nodiscard]] int32_t get_sdf_height() const { + return sdf_bounds.max().y() - sdf_bounds.min().y(); + } + + [[nodiscard]] int32_t get_bearing_x() const { + return sdf_bounds.min().x(); + } + + [[nodiscard]] int32_t get_bearing_y() const { + return sdf_bounds.min().y(); + } +private: + msdfgen::SDFTransformation transformation; + Eigen::AlignedBox2i sdf_bounds; + + void wrap(const msdfgen::Shape* in_shape, msdfgen::Shape::Bounds in_bounds, uint16_t in_units_per_em, int32_t in_ppem, float in_em_outer_spread, double in_miter_limit); +}; + +void glyph_sdf_mapping::set_spread(uint16_t in_units_per_em, float in_em_outer_spread, float in_em_inner_spread) { + const float units_per_em = in_units_per_em; + transformation.distanceMapping = msdfgen::Range(-units_per_em * in_em_outer_spread, units_per_em * in_em_inner_spread); +} + +void glyph_sdf_mapping::wrap(const msdfgen::Shape* in_shape, msdfgen::Shape::Bounds in_bounds, uint16_t in_units_per_em, + int32_t in_ppem, float in_em_outer_spread, double in_miter_limit) { + const float units_per_em = in_units_per_em; + const float unit_scale = float(in_ppem) / units_per_em; + + const float msdfgen_outer_spread = units_per_em * in_em_outer_spread; + in_bounds.l -= msdfgen_outer_spread; + in_bounds.b -= msdfgen_outer_spread; + in_bounds.r += msdfgen_outer_spread; + in_bounds.t += msdfgen_outer_spread; + + if (in_shape && in_miter_limit > 0) { + in_shape->boundMiters(in_bounds.l, in_bounds.b, in_bounds.r, in_bounds.t, msdfgen_outer_spread, in_miter_limit, 1); + } + + in_bounds.l *= unit_scale; + in_bounds.b *= unit_scale; + in_bounds.r *= unit_scale; + in_bounds.t *= unit_scale; + + in_bounds.l -= 0.5; + in_bounds.b -= 0.5; + in_bounds.r += 0.5; + in_bounds.t += 0.5; + + if (in_bounds.l > in_bounds.r) { + in_bounds.l = 0; + in_bounds.r = 0; + } + if (in_bounds.b > in_bounds.t) { + in_bounds.b = 0; + in_bounds.t = 0; + } + + sdf_bounds.min() = Eigen::Vector2i(int(std::floor(in_bounds.l)), int(std::floor(in_bounds.b))); + sdf_bounds.max() = Eigen::Vector2i(int(std::ceil(in_bounds.r)), int(std::ceil(in_bounds.t))); + + const msdfgen::Projection projection( + msdfgen::Vector2(unit_scale), + msdfgen::Vector2(-sdf_bounds.min().x(), -sdf_bounds.min().y()) / unit_scale + ); + const msdfgen::Range range(2); + transformation = msdfgen::SDFTransformation(projection, range); +} + +inline bool freetype_shape_build(std::shared_ptr in_face, uint32_t in_glyph_index, + float in_em_outer_spread, float in_em_inner_spread, int32_t in_ppem, + msdfgen::Shape& out_shape, glyph_sdf_mapping& out_glyph_sdf_mapping) { + if (!freetype_utils::is_face_eligible_for_sdf(in_face->get_face())) return false; + + uint32_t load_flags = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | + FT_LOAD_NO_BITMAP; + FT_Error error = FT_Load_Glyph(in_face->get_face(), in_glyph_index, load_flags); + if (error) { + spdlog::error("无法加载字形:{}", error); + return false; + } + + if (!freetype_utils::is_glyph_eligible_for_sdf(in_face->get_face()->glyph) + || in_face->get_face()->glyph->metrics.width <= 0 + || in_face->get_face()->glyph->metrics.height <= 0 + || in_face->get_face()->glyph->outline.n_points <= 0) { return false; } + + error = msdfgen::readFreetypeOutline(out_shape, &in_face->get_face()->glyph->outline, 1.0); + if (error) { + spdlog::error("无法读取字形轮廓:{}", error); + return false; + } + out_shape.inverseYAxis = !out_shape.inverseYAxis; + + msdfgen::Shape::Bounds bounds = out_shape.getBounds(); + msdfgen::Point2 outer_point(bounds.l - (bounds.r - bounds.l) - 1, bounds.b - (bounds.t - bounds.b) - 1); + if (msdfgen::SimpleTrueShapeDistanceFinder::oneShotDistance(out_shape, outer_point) > 0) { + for (auto& contour: out_shape.contours) { contour.reverse(); } + } + + const uint16_t units_per_em = in_face->get_face()->units_per_EM; + out_glyph_sdf_mapping.wrap_mitered(out_shape, bounds, units_per_em, in_ppem, in_em_outer_spread, + SDF_BOUNDS_MITER_LIMIT); + out_glyph_sdf_mapping.set_spread(units_per_em, in_em_outer_spread, in_em_inner_spread); + + return out_glyph_sdf_mapping.get_sdf_width() > 0 && + out_glyph_sdf_mapping.get_sdf_height() > 0 && + out_glyph_sdf_mapping.get_sdf_width() < MAX_GLYPH_SDF_SIDE && + out_glyph_sdf_mapping.get_sdf_height() < MAX_GLYPH_SDF_SIDE; +} + +static int32_t get_num_sdf_channels(sdf_generator::sdf_type in_type) { + switch (in_type) { + case sdf_generator::sdf_type::SIMPLE: + case sdf_generator::sdf_type::PERPENDICULAR: + return 1; + case sdf_generator::sdf_type::MULTICHANNEL_AND_SIMPLE: + return 4; + default: + break; + } + return 0; +} + +class sdf_generator_impl : public sdf_generator { + struct task { + request_descriptor descriptor{}; + std::vector output_pixels; + msdfgen::Shape shape; + glyph_sdf_mapping sdf_mapping; + + request_response prepare(const request_descriptor& in_request, request_output_info& out_char_info) { + auto font_face = in_request.font_face.lock(); + if (font_face && font_face->is_face_loading()) return request_response::BUSY; + + if (!font_face || !font_face->is_face_valid()) return request_response::SDF_UNAVAILABLE; + + if (freetype_shape_build( + font_face, + in_request.glyph_index, + in_request.em_outer_spread, + in_request.em_inner_spread, + in_request.ppem, + shape, + sdf_mapping + )) { + descriptor = in_request; + + out_char_info.image_width = sdf_mapping.get_sdf_width(); + out_char_info.image_height = sdf_mapping.get_sdf_height(); + out_char_info.bearing_x = sdf_mapping.get_bearing_x(); + out_char_info.bearing_y = sdf_mapping.get_bearing_y(); + + return request_response::SUCCESS; + } + return request_response::SDF_UNAVAILABLE; + } + void do_work() { + const bool overlapped_contour_support = true; + const int32_t target_widget = sdf_mapping.get_sdf_width(); + const int32_t target_height = sdf_mapping.get_sdf_height(); + const int32_t target_channels = get_num_sdf_channels(descriptor.type); + const auto float_pixels = new float[target_widget * target_height * target_channels]; + ON_SCOPE_EXIT { + delete[] float_pixels; + }; + output_pixels.resize(target_widget * target_height * target_channels); + + switch (descriptor.type) { + case sdf_type::SIMPLE: { + const msdfgen::BitmapRef bitmap{float_pixels, target_widget, target_height}; + generateSDF( + bitmap, + shape, + sdf_mapping.get_msdfgen_transformation(), + msdfgen::GeneratorConfig{overlapped_contour_support} + ); + break; + } + case sdf_type::PERPENDICULAR: { + const msdfgen::BitmapRef bitmap{float_pixels, target_widget, target_height}; + generatePSDF( + bitmap, + shape, + sdf_mapping.get_msdfgen_transformation(), + msdfgen::GeneratorConfig{overlapped_contour_support} + ); + break; + } + case sdf_type::MULTICHANNEL_AND_SIMPLE: { + const msdfgen::BitmapRef bitmap{float_pixels, target_widget, target_height}; + const msdfgen::MSDFGeneratorConfig config{ + overlapped_contour_support, + msdfgen::ErrorCorrectionConfig{ + msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY, + msdfgen::ErrorCorrectionConfig::CHECK_DISTANCE_AT_EDGE, + msdfgen::ErrorCorrectionConfig::defaultMinDeviationRatio, + msdfgen::ErrorCorrectionConfig::defaultMinImproveRatio, + output_pixels.data() // Temporarily repurpose output buffer as error correction buffer + } + }; + generateMTSDF( + bitmap, + shape, + sdf_mapping.get_msdfgen_transformation(), + config + ); + break; + } + default: + assert(false); // 不应该到达这里 + } + + const size_t end_offset = target_channels * target_widget * target_height; + uint8_t* begin_ptr = output_pixels.data(); + const uint8_t* end_ptr = begin_ptr + end_offset; + + auto src = float_pixels; + for (uint8_t* dst = begin_ptr; dst < end_ptr; ++dst, ++src) { + *dst = msdfgen::pixelFloatToByte(*src); + } + } + }; +public: + sdf_generator_impl(); + ~sdf_generator_impl(); + + request_response spawn(const request_descriptor& in_request, request_output_info& out_char_info) override { + auto new_task = std::make_shared(); + const auto result = new_task->prepare(in_request, out_char_info); + if (result == request_response::SUCCESS) { + auto task_func = [new_task] { + new_task->do_work(); + return new_task; + }; + auto task_callback = [](const std::optional>& in_result) { + if (!in_result) { + return; + } + auto& task = *in_result; + + }; + thread_pool::global().submit_with_callback(task_func, task_callback); + } + return result; + } + request_response respawn(const request_descriptor& in_request, const request_output_info& in_char_info) override { + return request_response::BAD_REQUEST; + } + void update(const for_each_request_done_callback& in_enumerator) override; + void flush() override; +private: + void sdf_task_done(); +}; + +std::unique_ptr sdf_generator::create() { + return std::make_unique(); +} diff --git a/src/renderer/core/fonts/sdf_generator.h b/src/renderer/core/fonts/sdf_generator.h new file mode 100644 index 0000000..bd10be5 --- /dev/null +++ b/src/renderer/core/fonts/sdf_generator.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include +#include +#include + +#include +#include +#include + +class freetype_face; + +class sdf_generator { +public: + enum class request_response { + SUCCESS = 0, + SDF_UNAVAILABLE, + BUSY, + BAD_REQUEST, + }; + + enum class sdf_type { + SIMPLE, + PERPENDICULAR, + MULTICHANNEL_AND_SIMPLE + }; + + struct request_output_info { + uint16_t image_width; + uint16_t image_height; + int16_t bearing_x; + int16_t bearing_y; + }; + + struct request_descriptor { + std::weak_ptr font_face; + uint32_t glyph_index; + sdf_type type; + float em_outer_spread; + float em_inner_spread; + int32_t ppem; + }; + + using for_each_request_done_callback = std::function raw_pixels)>; + + virtual ~sdf_generator() = default; + + virtual request_response spawn(const request_descriptor& in_request, request_output_info& out_char_info) = 0; + virtual request_response respawn(const request_descriptor& in_request, const request_output_info& in_char_info) = 0; + virtual void update(const for_each_request_done_callback& in_enumerator) = 0; + virtual void flush() = 0; + + static std::unique_ptr create(); +protected: + sdf_generator() = default; +}; diff --git a/src/renderer/core/fonts/shape_distance_finder.cpp b/src/renderer/core/fonts/shape_distance_finder.cpp new file mode 100644 index 0000000..6627eab --- /dev/null +++ b/src/renderer/core/fonts/shape_distance_finder.cpp @@ -0,0 +1 @@ +#include "shape_distance_finder.h" diff --git a/src/renderer/core/fonts/shape_distance_finder.h b/src/renderer/core/fonts/shape_distance_finder.h new file mode 100644 index 0000000..ec2a5b7 --- /dev/null +++ b/src/renderer/core/fonts/shape_distance_finder.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include + +#define DISTANCE_DELTA_FACTOR 1.001 + +template +class shape_distance_finder { +public: + using distance_type = typename contour_combiner::distance_type; + using edge_cache_type = typename contour_combiner::edge_selector_type::edge_cache; + + explicit shape_distance_finder(const msdfgen::Shape& in_shape) : shape(in_shape), combiner(in_shape), edge_caches(in_shape.edgeCount()) {} + distance_type distance(const msdfgen::Point2& origin) { + combiner.reset(origin); + auto* edge_cache = edge_caches.data(); + + for (auto contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { + if (contour->edges.empty()) + continue; + auto& edge_selector = combiner.edge_selector(int(contour - shape.contours.begin())); + + const msdfgen::EdgeSegment* prev_edge = contour->edges.size() >= 2 ? *(contour->edges.end() - 2) : (*contour->edges.begin()); + const msdfgen::EdgeSegment* cur_edge = contour->edges.back(); + for (auto edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { + const msdfgen::EdgeSegment* next_edge = *edge; + + edge_selector.add_edge(*edge_cache++, prev_edge, cur_edge, next_edge); + prev_edge = cur_edge; + cur_edge = next_edge; + } + } + + return combiner.distance(); + } + + static distance_type one_shot_distance(const msdfgen::Shape& in_shape, const msdfgen::Point2& in_origin) { + contour_combiner combiner(in_shape); + combiner.reset(in_origin); + + for (auto contour = in_shape.contours.begin(); contour != in_shape.contours.end(); ++contour) { + if (contour->edges.empty()) + continue; + auto& edge_selector = combiner.edge_selector(int(contour - in_shape.contours.begin())); + + const msdfgen::EdgeSegment* prev_edge = contour->edges.size() >= 2 ? *(contour->edges.end() - 2) : (*contour->edges.begin()); + const msdfgen::EdgeSegment* cur_edge = contour->edges.back(); + for (auto edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { + const msdfgen::EdgeSegment* next_edge = *edge; + + edge_cache_type dummy; + edge_selector.add_edge(dummy, prev_edge, cur_edge, next_edge); + prev_edge = cur_edge; + cur_edge = next_edge; + } + } + + return combiner.distance(); + } +private: + const msdfgen::Shape shape; + contour_combiner combiner; + std::vector edge_caches; +}; + diff --git a/src/renderer/core/fonts/text_shaper.cpp b/src/renderer/core/fonts/text_shaper.cpp new file mode 100644 index 0000000..9748a36 --- /dev/null +++ b/src/renderer/core/fonts/text_shaper.cpp @@ -0,0 +1 @@ +#include "text_shaper.h" diff --git a/src/renderer/core/fonts/text_shaper.h b/src/renderer/core/fonts/text_shaper.h new file mode 100644 index 0000000..cc2f260 --- /dev/null +++ b/src/renderer/core/fonts/text_shaper.h @@ -0,0 +1,35 @@ +#pragma once +#include "font_cache.h" +#include "font_cache_freetype.h" + +class shaped_glyph_face_data { +public: + shaped_glyph_face_data(std::weak_ptr in_font_face, uint32_t in_glyph_flags, float in_font_size, float in_font_scale, float in_font_skew, font_rasterization_mode in_rasterization_mode, int16_t in_sdf_ppem) + : font_face(in_font_face) + , glyph_flags(in_glyph_flags) + , font_size(in_font_size) + , font_scale(in_font_scale) + , font_skew(in_font_skew) + , rasterization_mode(in_rasterization_mode) + , sdf_ppem(in_sdf_ppem) + { + if (const auto font_face_ptr = font_face.lock()) { + bitmap_render_scale = font_face_ptr->get_bitmap_render_scale(); + } + } + + std::hash a; + std::weak_ptr font_face; + uint32_t glyph_flags = 0; + float font_size = 0.f; + float font_scale = 0.f; + float bitmap_render_scale; + float font_skew = 0.f; + font_rasterization_mode rasterization_mode; + int16_t sdf_ppem = 0; +}; + +class text_shaper { +public: + text_shaper(freetype_cache_directory* in_cache_directory, composite_font* in_composite_font_cache, font_renderer* in_font_renderer, font_cache* in_font_cache); +}; diff --git a/src/renderer/core/internationalization/culture.cpp b/src/renderer/core/internationalization/culture.cpp new file mode 100644 index 0000000..1d376f5 --- /dev/null +++ b/src/renderer/core/internationalization/culture.cpp @@ -0,0 +1 @@ +#include "culture.h" diff --git a/src/renderer/core/internationalization/culture.h b/src/renderer/core/internationalization/culture.h new file mode 100644 index 0000000..e3f9c17 --- /dev/null +++ b/src/renderer/core/internationalization/culture.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +class culture; +using culture_ptr = std::shared_ptr; + +class culture { +public: + ~culture() = default; + + static culture_ptr create(const std::u32string& name) { + } + + const std::u32string& get_display_name() const { + } +}; + diff --git a/src/renderer/core/internationalization/internationalization.cpp b/src/renderer/core/internationalization/internationalization.cpp new file mode 100644 index 0000000..0d127e0 --- /dev/null +++ b/src/renderer/core/internationalization/internationalization.cpp @@ -0,0 +1 @@ +#include "internationalization.h" diff --git a/src/renderer/core/internationalization/internationalization.h b/src/renderer/core/internationalization/internationalization.h new file mode 100644 index 0000000..546cb48 --- /dev/null +++ b/src/renderer/core/internationalization/internationalization.h @@ -0,0 +1,16 @@ +#pragma once +#include "misc/lazy_singleton.h" + +class internationalization { +public: + static internationalization& get() { + return lazy_singleton::get(); + } + + static void tear_down() { + lazy_singleton::tear_down(); + } + + bool set_current_culture(const std::u32string& culture) { + } +}; diff --git a/src/renderer/core/pipeline/pipeline.h b/src/renderer/core/pipeline/pipeline.h index f6420e1..58c510f 100644 --- a/src/renderer/core/pipeline/pipeline.h +++ b/src/renderer/core/pipeline/pipeline.h @@ -4,7 +4,7 @@ #include #include "core/renderer/renderer_buffer.h" -#include "misc/color.h" +#include "../../../core/misc/color.h" struct aorii_vertex_param { float param_a1; diff --git a/src/renderer/core/pipeline/rect_pipeline.cpp b/src/renderer/core/pipeline/rect_pipeline.cpp index f9fc4b3..25eede4 100644 --- a/src/renderer/core/pipeline/rect_pipeline.cpp +++ b/src/renderer/core/pipeline/rect_pipeline.cpp @@ -3,7 +3,7 @@ #include "core/renderer/renderer.h" bool rect_pipeline::init() { - param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::constant, sizeof(param), 1); + param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::CONSTANT, sizeof(param), 1); return pipeline::init(); } diff --git a/src/renderer/core/pipeline/rounded_rect_pipeline.cpp b/src/renderer/core/pipeline/rounded_rect_pipeline.cpp index 88bf463..b85dc28 100644 --- a/src/renderer/core/pipeline/rounded_rect_pipeline.cpp +++ b/src/renderer/core/pipeline/rounded_rect_pipeline.cpp @@ -3,7 +3,7 @@ #include "core/renderer/renderer.h" bool rounded_rect_pipeline::init() { - param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::constant, sizeof(param), 1); + param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::CONSTANT, sizeof(param), 1); return pipeline::init(); } diff --git a/src/renderer/core/pipeline/sdf_text_pipeline.cpp b/src/renderer/core/pipeline/sdf_text_pipeline.cpp index c869059..3f13134 100644 --- a/src/renderer/core/pipeline/sdf_text_pipeline.cpp +++ b/src/renderer/core/pipeline/sdf_text_pipeline.cpp @@ -3,8 +3,8 @@ #include "core/renderer/renderer.h" bool sdf_text_pipeline::init() { - param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::constant, sizeof(param), 1); - sdf_font_param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::constant, sizeof(sdf_font_param), 1); + param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::CONSTANT, sizeof(param), 1); + sdf_font_param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::CONSTANT, sizeof(sdf_font_param), 1); return pipeline::init(); } void sdf_text_pipeline::destroy() { diff --git a/src/renderer/core/pipeline/segment_pipeline.cpp b/src/renderer/core/pipeline/segment_pipeline.cpp index 398f8ed..b0c8fcf 100644 --- a/src/renderer/core/pipeline/segment_pipeline.cpp +++ b/src/renderer/core/pipeline/segment_pipeline.cpp @@ -4,7 +4,7 @@ bool segment_pipeline::init() { pipeline::init(); - param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::constant, sizeof(param), 1); + param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::CONSTANT, sizeof(param), 1); return true; } void segment_pipeline::destroy() { diff --git a/src/renderer/core/pipeline/texture_pipeline.cpp b/src/renderer/core/pipeline/texture_pipeline.cpp index dda5ba6..8332677 100644 --- a/src/renderer/core/pipeline/texture_pipeline.cpp +++ b/src/renderer/core/pipeline/texture_pipeline.cpp @@ -3,7 +3,7 @@ #include "core/renderer/renderer.h" bool texture_pipeline::init() { - param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::constant, sizeof(param), 1); + param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::CONSTANT, sizeof(param), 1); return pipeline::init(); } void texture_pipeline::destroy() { diff --git a/src/renderer/core/renderer/renderer.cpp b/src/renderer/core/renderer/renderer.cpp index 69f85d2..8755563 100644 --- a/src/renderer/core/renderer/renderer.cpp +++ b/src/renderer/core/renderer/renderer.cpp @@ -38,7 +38,7 @@ renderer_texture* renderer::load_image(const std::string& file_path, texture_for int target_channel_count = get_format_channel_count(in_format); unsigned int row_pitch = 0; - auto texture = create_texture(width, height, in_format, device_memory_type::gpu); + auto texture = create_texture(width, height, in_format, device_memory_type::GPU); auto data = texture->lock(&row_pitch); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { @@ -95,7 +95,7 @@ bool aorii::create_renderer(renderer_api api) { if (s_renderer) return true; switch (api) { #if DX_BACKEND - case renderer_api::dx11: s_renderer = new dx_renderer(); + case renderer_api::DX11: s_renderer = new dx_renderer(); break; #endif #if GL_BACKEND diff --git a/src/renderer/core/renderer/renderer.h b/src/renderer/core/renderer/renderer.h index e21dd56..6bd3400 100644 --- a/src/renderer/core/renderer/renderer.h +++ b/src/renderer/core/renderer/renderer.h @@ -16,16 +16,16 @@ class renderer_window; class renderer_texture; enum class renderer_api { - dx11, - opengl, - vulkan, - metal, + DX11, + OPENGL, + VULKAN, + METAL, }; enum class device_memory_type { - gpu, // 显存 - shared, // 共享内存 - cpu, // 内存 + GPU, // 显存 + SHARED, // 共享内存 + CPU, // 内存 }; enum class texture_format { @@ -249,10 +249,10 @@ public: virtual std::vector load_shader(const std::string& shader_name) = 0; renderer_buffer* create_vertex_buffer(const int32_t in_size = 4) { - return create_buffer(buffer_type::vertex, sizeof(aorii_vertex), in_size); + return create_buffer(buffer_type::VERTEX, sizeof(aorii_vertex), in_size); } renderer_buffer* create_index_buffer(const int32_t in_size = 2) { - return create_buffer(buffer_type::index, sizeof(aorii_triangle), in_size); + return create_buffer(buffer_type::INDEX, sizeof(aorii_triangle), in_size); } virtual renderer_buffer* create_buffer(buffer_type in_buffer_type, int32_t in_element_byte, int32_t in_element_count) = 0; virtual void destroy_buffer(renderer_buffer* buffer) { delete buffer; } diff --git a/src/renderer/core/renderer/renderer_buffer.h b/src/renderer/core/renderer/renderer_buffer.h index 820413b..94faa5f 100644 --- a/src/renderer/core/renderer/renderer_buffer.h +++ b/src/renderer/core/renderer/renderer_buffer.h @@ -3,9 +3,9 @@ #include enum class buffer_type { - vertex, - index, - constant, + VERTEX, + INDEX, + CONSTANT, }; class renderer_buffer { diff --git a/src/renderer/core/renderer/renderer_context.cpp b/src/renderer/core/renderer/renderer_context.cpp index 44baff8..07ec73d 100644 --- a/src/renderer/core/renderer/renderer_context.cpp +++ b/src/renderer/core/renderer/renderer_context.cpp @@ -8,7 +8,7 @@ aorii_text* text = nullptr; void renderer_context::init() { text = new aorii_text(); // D:\Projects\aorii\JetBrainsMono-Regular.ttf - text->initialize(LR"(HarmonyOS_Sans_SC_Regular.ttf)"); + text->initialize(LR"(C:\Windows\Fonts\Deng.ttf)"); text->add_font(LR"(C:\Windows\Fonts\seguiemj.ttf)"); // text->precache_common_characters(); } @@ -59,6 +59,8 @@ void renderer_context::draw_string(const Eigen::Vector2f& in_pos, const std::u32 param.param_a3 = info->tex_z; param.param_b1 = info->u_size; param.param_b2 = info->v_size; + // param.param_b3 = info->get_width() * mch.size_scale > 15 ? 0.01 : 0; + param.param_b3 = info->get_width() * mch.size_scale > 15 ? 0.01 : 0; make_rect(pos, size, in_color, 0, param); } diff --git a/src/renderer/core/renderer/renderer_text.cpp b/src/renderer/core/renderer/renderer_text.cpp index c476273..a87505c 100644 --- a/src/renderer/core/renderer/renderer_text.cpp +++ b/src/renderer/core/renderer/renderer_text.cpp @@ -47,7 +47,7 @@ void aorii_text::destroy_freetype() { bool aorii_text::initialize(const std::wstring& in_font_path) { // 预计需要的纹理数量(8张纹理几乎可以容纳所有字符) constexpr uint32_t expected_textures = 8; - texture_array = aorii::get_renderer_raw()->create_texture_array(2048, 2048, expected_textures, texture_format::R8_UNORM, device_memory_type::gpu); + texture_array = aorii::get_renderer_raw()->create_texture_array(2048, 2048, expected_textures, texture_format::R8_UNORM, device_memory_type::GPU); if (!texture_array) { spdlog::error("无法创建字符缓冲纹理"); return false; @@ -226,13 +226,6 @@ std::vector aorii_text::measure_text(const std::u32string& text, fl data.min_bottom = min_bottom; data.max_top = max_top; data.max_bottom = max_bottom; - // 如果min_top小于0, 则将max_bottom减去min_top - // if (min_top < 0) { - // data.max_bottom -= min_top; - // data.min_bottom -= min_top; - // data.max_top -= min_top; - // data.min_top = 0; - // } } std::vector result; @@ -270,7 +263,7 @@ std::vector aorii_text::measure_text(const std::u32string& text, fl // mch.offset.y() += line_height; mch.offset.y() += top_offset; - // mch.offset.x() += item->left * mch.size_scale; + // mch.offset.x() -= item->left_side_bearing * mch.size_scale; pos.x() += item->x_advance * mch.size_scale; // pos.x() -= item->left * mch.size_scale; diff --git a/src/renderer/core/renderer/renderer_text.h b/src/renderer/core/renderer/renderer_text.h index a00ee40..56503e7 100644 --- a/src/renderer/core/renderer/renderer_text.h +++ b/src/renderer/core/renderer/renderer_text.h @@ -94,7 +94,7 @@ public: private: ch_atlas_item const* cache_ch_to_atlas(char32_t ch); std::vector fonts; - const float pixel_height = 64; + const float pixel_height = 128; const int padding = 5; renderer_texture_array* texture_array; diff --git a/src/renderer/misc/color.cpp b/src/renderer/misc/color.cpp deleted file mode 100644 index ad05900..0000000 --- a/src/renderer/misc/color.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "color.h" - -const linear_color linear_color::white = linear_color(1, 1, 1, 1); diff --git a/src/renderer/misc/color.h b/src/renderer/misc/color.h deleted file mode 100644 index 42791f5..0000000 --- a/src/renderer/misc/color.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include - -class linear_color { -public: - linear_color() : r(1), g(1), b(1), a(1) { - } - linear_color(float in_r, float in_g, float in_b, float in_a = 1.0f) : r(in_r), g(in_g), b(in_b), a(in_a) {} - - static linear_color from_srgb(float in_r, float in_g, float in_b, float in_a = 1.0f) { - return linear_color( - in_r <= 0.04045f ? in_r / 12.92f : std::pow((in_r + 0.055f) / 1.055f, 2.4f), - in_g <= 0.04045f ? in_g / 12.92f : std::pow((in_g + 0.055f) / 1.055f, 2.4f), - in_b <= 0.04045f ? in_b / 12.92f : std::pow((in_b + 0.055f) / 1.055f, 2.4f), - in_a - ); - } - static linear_color from_srgb(const linear_color& in_color) { - return from_srgb(in_color.r, in_color.g, in_color.b, in_color.a); - } - - linear_color& operator+=(const linear_color& in_color) { - r += in_color.r; - g += in_color.g; - b += in_color.b; - a += in_color.a; - return *this; - } - linear_color& operator-=(const linear_color& in_color) { - r -= in_color.r; - g -= in_color.g; - b -= in_color.b; - a -= in_color.a; - return *this; - } - linear_color operator+(const linear_color& in_color) const { - return { r + in_color.r, g + in_color.g, b + in_color.b, a + in_color.a }; - } - linear_color operator-(const linear_color& in_color) const { - return { r - in_color.r, g - in_color.g, b - in_color.b, a - in_color.a }; - } - float r, g, b, a; - - - - - - - - static const linear_color white; -}; diff --git a/src/renderer/shader/aorii_sdf_text.slang b/src/renderer/shader/aorii_sdf_text.slang index 37a5eea..938fa23 100644 --- a/src/renderer/shader/aorii_sdf_text.slang +++ b/src/renderer/shader/aorii_sdf_text.slang @@ -15,6 +15,7 @@ struct PSInput { float2 altas_uv : TEXCOORD1; float altas_index : TEXCOORD2; float2 char_size : TEXCOORD3; + float range : TEXCOORD4; }; struct Constants { @@ -33,6 +34,7 @@ PSInput vertex_main(VSInput input) { output.altas_uv = altas_uv; output.altas_index = input.param_a.z; output.char_size = char_size; + output.range = input.param_b.z; return output; } @@ -42,9 +44,13 @@ SamplerState sampler_state : register(s0); float4 pixel_main(PSInput input) : SV_Target { float2 uv = input.altas_uv + input.char_size * input.uv; float distance = atlas_texture.Sample(sampler_state, float3(uv, input.altas_index)).r; - // return float4(distance, distance, distance, 1.0); - float range = 0.2; - float alpha = smoothstep(0.3, 0.6, distance); + // return float4(distance, distance, distance, 1); + float range = input.range; + // if (range == 0) { + // distance *= 1.25; + // return float4(input.color.rgb, input.color.a * distance); + // } + float alpha = smoothstep(0.49 - range, 0.6 + range, distance); float4 color = input.color; color.a *= alpha;