TODO
This commit is contained in:
parent
8e635ecc87
commit
037fb1f3a3
@ -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)
|
||||
|
@ -2,17 +2,19 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
BIN
resource/5a5326c4ce213d25d227b2b3ef16391a3493138886822091.jpg
Normal file
BIN
resource/5a5326c4ce213d25d227b2b3ef16391a3493138886822091.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 MiB |
BIN
resource/61532343_p0.jpg
Normal file
BIN
resource/61532343_p0.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 533 KiB |
BIN
resource/69054578_p0.jpg
Normal file
BIN
resource/69054578_p0.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 995 KiB |
BIN
resource/IMG_5687.jpg
Normal file
BIN
resource/IMG_5687.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 MiB |
BIN
resource/JetBrainsMono-Light.ttf
Normal file
BIN
resource/JetBrainsMono-Light.ttf
Normal file
Binary file not shown.
BIN
resource/JetBrainsMono-Regular.ttf
Normal file
BIN
resource/JetBrainsMono-Regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 3.4 MiB |
@ -60,6 +60,35 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename f, typename callback, typename ...args>
|
||||
auto submit_with_callback(f&& in_func, callback&& in_callback, args&&... in_args) {
|
||||
using return_type = std::invoke_result_t<f, args...>;
|
||||
|
||||
if (stop) { throw std::runtime_error("submit on stopped ThreadPool"); }
|
||||
|
||||
auto task = std::make_shared<std::packaged_task<return_type()>>(
|
||||
std::bind(std::forward<f>(in_func), std::forward<args>(in_args)...)
|
||||
);
|
||||
|
||||
auto res = task->get_future();
|
||||
|
||||
{
|
||||
std::lock_guard lock(queue_mutex);
|
||||
tasks.emplace([task, in_callback] {
|
||||
std::optional<return_type> callback_value;
|
||||
try {
|
||||
callback_value = (*task)();
|
||||
} catch (...) {
|
||||
callback_value = std::nullopt;
|
||||
}
|
||||
in_callback(callback_value);
|
||||
});
|
||||
}
|
||||
|
||||
condition.notify_one();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 提交一个任务到线程池,并在主线程中执行回调函数
|
||||
* @tparam f 任务函数类型
|
||||
|
2
src/core/misc/color.cpp
Normal file
2
src/core/misc/color.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include "color.h"
|
||||
|
59
src/core/misc/color.h
Normal file
59
src/core/misc/color.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include <complex>
|
||||
|
||||
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 };
|
||||
};
|
17
src/core/misc/intrusive_unset_optional_state.h
Normal file
17
src/core/misc/intrusive_unset_optional_state.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
namespace aorii_private {
|
||||
template <typename optional_type, bool has_intrusive_unset_optional_state>
|
||||
struct optional_storage;
|
||||
}
|
||||
|
||||
struct intrusive_unset_optional_state {
|
||||
template<typename>
|
||||
friend class std::optional;
|
||||
|
||||
template<typename, bool>
|
||||
friend struct aorii_private::optional_storage;
|
||||
private:
|
||||
explicit intrusive_unset_optional_state() = default;
|
||||
};
|
63
src/core/misc/lazy_singleton.h
Normal file
63
src/core/misc/lazy_singleton.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <stdexcept>
|
||||
|
||||
class lazy_singleton_func {
|
||||
protected:
|
||||
template<class T> static void construct(void* in_place) { new (in_place) T(); }
|
||||
template<class T> static void destruct(T* in_instance) { in_instance->~T(); }
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class lazy_singleton : public lazy_singleton_func {
|
||||
public:
|
||||
static T& get() {
|
||||
return *get_lazy(construct<T>).ptr;
|
||||
}
|
||||
|
||||
static T* try_get() {
|
||||
return get_lazy(construct<T>).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<T*>(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{};
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
//
|
||||
// Created by 46944 on 24-10-15.
|
||||
//
|
170
src/core/misc/type_hash.h
Normal file
170
src/core/misc/type_hash.h
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
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<uintptr_t>(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_scalar_v<ScalarType> && !std::is_same_v<ScalarType, char32_t*> && !std::is_same_v<ScalarType, const char32_t*>>* = nullptr
|
||||
>
|
||||
[[nodiscard]] uint32_t get_type_hash(ScalarType value)
|
||||
{
|
||||
if constexpr (std::is_integral_v<ScalarType>)
|
||||
{
|
||||
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<ScalarType>)
|
||||
{
|
||||
if constexpr (std::is_same_v<ScalarType, float>)
|
||||
{
|
||||
return *(uint32_t*)&value;
|
||||
}
|
||||
else if constexpr (std::is_same_v<ScalarType, double>)
|
||||
{
|
||||
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<ScalarType>)
|
||||
{
|
||||
return get_type_hash((__underlying_type(ScalarType))value);
|
||||
}
|
||||
else if constexpr (std::is_pointer_v<ScalarType>)
|
||||
{
|
||||
// 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<std::remove_pointer_t<ScalarType>>::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<!std::is_same_v<const T, const char32_t>>* = nullptr
|
||||
>
|
||||
uint32_t get_type_hash(T (&array)[N])
|
||||
{
|
||||
return pointer_hash(array);
|
||||
}
|
||||
|
||||
// template <
|
||||
// typename T,
|
||||
// std::enable_if_t<std::is_same_v<const T, const char32_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 <typename T>
|
||||
[[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 <typename T>
|
||||
[[nodiscard]] uint32_t get_type_hash_helper(const T& V) { return get_type_hash(V); }
|
@ -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})
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -114,7 +114,7 @@ void dx_window::begin_frame() {
|
||||
// if (test_texture) context.draw_texture({ 0.f, 0.f }, test_texture->size().cast<float>(), 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 });
|
||||
|
43
src/renderer/core/fonts/composite_font.cpp
Normal file
43
src/renderer/core/fonts/composite_font.cpp
Normal file
@ -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<uint8_t> 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<uint8_t const*>(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<font_face_data_lazy_load>(in_filename);
|
||||
break;
|
||||
case font_loading_policy::STREAM:
|
||||
impl = std::make_unique<font_face_data_stream>(in_filename);
|
||||
break;
|
||||
}
|
||||
}
|
160
src/renderer/core/fonts/composite_font.h
Normal file
160
src/renderer/core/fonts/composite_font.h
Normal file
@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "font_rasterization_mode.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <boost/flyweight/flyweight.hpp>
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
// 字体渲染的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<font_face_data_impl> impl;
|
||||
};
|
||||
|
||||
using font_face_data_ptr = std::shared_ptr<font_face_data>;
|
||||
using font_face_data_const_ptr = std::shared_ptr<const font_face_data>;
|
||||
|
||||
// 字体渲染设置
|
||||
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<std::string>& in_name) : name(std::move(in_name)) {}
|
||||
|
||||
typeface_entry(const boost::flyweight<std::string>& 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<std::string>& get_name() const noexcept { return name; }
|
||||
[[nodiscard]] const font_data& get_font() const noexcept { return font; }
|
||||
// 字体名称
|
||||
boost::flyweight<std::string> 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<typeface_entry>& get_fonts() const noexcept { return fonts; }
|
||||
|
||||
std::vector<typeface_entry> 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<Eigen::Vector2i> 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<composite_subfont> sub_typefaces;
|
||||
private:
|
||||
bool enable_ascent_descent_override = true;
|
||||
};
|
||||
|
2
src/renderer/core/fonts/font_cache.cpp
Normal file
2
src/renderer/core/fonts/font_cache.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include "font_cache.h"
|
||||
|
79
src/renderer/core/fonts/font_cache.h
Normal file
79
src/renderer/core/fonts/font_cache.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#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<shaped_glyph_face_data> 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 {
|
||||
|
||||
};
|
92
src/renderer/core/fonts/font_cache_composite_font.cpp
Normal file
92
src/renderer/core/fonts/font_cache_composite_font.cpp
Normal file
@ -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<std::string>& 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<const font_data*>& 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<cached_typeface_data>(composite_font_->default_typeface));
|
||||
cached_typefaces_.push_back(std::make_shared<cached_typeface_data>(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<cached_typeface_data>(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<cached_font_range>& 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<int32_t>(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<int32_t>(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<const font_data*>& 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();
|
||||
}
|
111
src/renderer/core/fonts/font_cache_composite_font.h
Normal file
111
src/renderer/core/fonts/font_cache_composite_font.h
Normal file
@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
#include "composite_font.h"
|
||||
#include "boost/flyweight.hpp"
|
||||
#include <boost/icl/interval.hpp>
|
||||
|
||||
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<std::string>& in_name) const;
|
||||
|
||||
void get_cached_font_data(std::vector<const font_data*>& out_font_data) const;
|
||||
private:
|
||||
struct cached_font_data {
|
||||
cached_font_data() : font_data(nullptr) {}
|
||||
cached_font_data(const boost::flyweight<std::string>& 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<std::string>& lhs, const boost::flyweight<std::string>& 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<std::string> name;
|
||||
const font_data* font_data;
|
||||
};
|
||||
const typeface* typeface_;
|
||||
std::vector<cached_font_data> 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<const font_data*>& 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<int32_t>& in_code_points,
|
||||
const std::shared_ptr<cached_typeface_data>& 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<int32_t> range;
|
||||
std::shared_ptr<cached_typeface_data> 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<std::shared_ptr<cached_typeface_data>> cached_typefaces_;
|
||||
std::vector<cached_font_range> cached_priority_font_ranges_;
|
||||
std::vector<cached_font_range> 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();
|
||||
};
|
529
src/renderer/core/fonts/font_cache_freetype.cpp
Normal file
529
src/renderer/core/fonts/font_cache_freetype.cpp
Normal file
@ -0,0 +1,529 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "font_cache_freetype.h"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
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<FT_Long>(std::max(0.f, in_font_size)) * (FT_Long)font_constants::render_dpi + 36) / 72,
|
||||
convert_pixel_to_16dot16<FT_Long>(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<FT_F26Dot6>(std::max(0.f, in_font_size));
|
||||
const FT_Long fixed_font_scale = convert_pixel_to_16dot16<FT_Long>(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<uint32_t>(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<uint32_t>(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<FT_Fixed>(1)) {
|
||||
// 固定大小字体不支持缩放,但我们在 apply_size_and_scale 中计算了要用于字形的缩放比例
|
||||
return static_cast<float>(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<FT_Fixed>(1)) {
|
||||
// 固定大小字体不支持缩放,但我们在 apply_size_and_scale 中计算了用于字形的缩放比例
|
||||
return static_cast<float>(in_face->size->metrics.x_scale) / 65536.f; // 16.16 -> pixel scale
|
||||
}
|
||||
}
|
||||
return 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
freetype_library::freetype_library() {
|
||||
custom_memory = static_cast<FT_Memory>(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<std::string> freetype_face::get_available_sub_faces(const freetype_library* in_library, font_face_data_const_ptr in_memory) {
|
||||
std::vector<std::string> 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<freetype_advance_cache>()) {
|
||||
}
|
||||
|
||||
std::shared_ptr<freetype_glyph_cache> 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<freetype_glyph_cache>(in_face, in_flags, in_font_size, in_font_scale));
|
||||
return result.first->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<freetype_advance_cache> 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<freetype_advance_cache>(in_face, in_flags, in_font_size, in_font_scale));
|
||||
return result.first->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<freetype_kerning_cache> 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<freetype_kerning_cache>(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();
|
||||
}
|
343
src/renderer/core/fonts/font_cache_freetype.h
Normal file
343
src/renderer/core/fonts/font_cache_freetype.h
Normal file
@ -0,0 +1,343 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#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<typename ret_type, typename param_type>
|
||||
std::enable_if_t<std::is_integral_v<param_type>, ret_type> convert_26dot6_to_rounded_pixel(param_type in_value) {
|
||||
return static_cast<ret_type>((in_value + (1 << 5)) >> 6);
|
||||
}
|
||||
|
||||
template<typename ret_type, typename param_type>
|
||||
std::enable_if_t<std::is_floating_point_v<param_type>, ret_type> convert_26dot6_to_rounded_pixel(param_type in_value) {
|
||||
return static_cast<ret_type>(std::round(in_value / 64.0));
|
||||
}
|
||||
|
||||
template<typename ret_type, typename param_type>
|
||||
std::enable_if_t<std::is_integral_v<param_type>, ret_type> convert_pixel_to_26dot6(param_type in_value) {
|
||||
return static_cast<ret_type>(in_value << 6);
|
||||
}
|
||||
|
||||
template<typename ret_type, typename param_type>
|
||||
std::enable_if_t<std::is_floating_point_v<param_type>, ret_type> convert_pixel_to_26dot6(param_type in_value) {
|
||||
return static_cast<ret_type>(in_value * 64.0);
|
||||
}
|
||||
|
||||
template<typename ret_type, typename param_type>
|
||||
std::enable_if_t<std::is_integral_v<param_type>, ret_type> convert_pixel_to_16dot16(param_type in_value) {
|
||||
return static_cast<ret_type>(in_value << 16);
|
||||
}
|
||||
|
||||
template<typename ret_type, typename param_type>
|
||||
std::enable_if_t<std::is_floating_point_v<param_type>, ret_type> convert_pixel_to_16dot16(param_type in_value) {
|
||||
return static_cast<ret_type>(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<FT_F26Dot6>(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<FT_F26Dot6>(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<std::string> 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<FT_F26Dot6> ascent_override;
|
||||
std::optional<FT_F26Dot6> descent_override;
|
||||
font_layout_method layout_method;
|
||||
std::vector<std::string> 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<FT_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<uint32_t, cached_glyph_data> 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<uint32_t, FT_Fixed> 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<kerning_pair>
|
||||
{
|
||||
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<font_key>
|
||||
{
|
||||
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_pair, FT_Vector> kerning_map;
|
||||
};
|
||||
|
||||
class freetype_cache_directory {
|
||||
public:
|
||||
freetype_cache_directory();
|
||||
std::shared_ptr<freetype_glyph_cache> get_glyph_cache(FT_Face in_face, int32_t in_flags, float in_font_size, float in_font_scale);
|
||||
std::shared_ptr<freetype_advance_cache> get_advance_cache(FT_Face in_face, int32_t in_flags, float in_font_size, float in_font_scale);
|
||||
std::shared_ptr<freetype_kerning_cache> 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<font_key, std::shared_ptr<freetype_glyph_cache>> glyph_cache_map;
|
||||
std::unordered_map<font_key, std::shared_ptr<freetype_advance_cache>> advance_cache_map;
|
||||
std::unordered_map<font_key, std::shared_ptr<freetype_kerning_cache>> kerning_cache_map;
|
||||
std::shared_ptr<freetype_advance_cache> invalid_advance_cache;
|
||||
};
|
38
src/renderer/core/fonts/font_info.cpp
Normal file
38
src/renderer/core/fonts/font_info.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "font_info.h"
|
||||
|
||||
font_info::font_info(const std::shared_ptr<const composite_font>& in_composite_font, float in_size,
|
||||
const boost::flyweight<std::string>& 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<std::string>{ in_font_name }, font_hinting::DEFAULT);
|
||||
}
|
||||
|
||||
font_info::font_info(const boost::flyweight<std::string>& 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<std::string> in_legacy_font_name, font_hinting in_hinting) {
|
||||
}
|
94
src/renderer/core/fonts/font_info.h
Normal file
94
src/renderer/core/fonts/font_info.h
Normal file
@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#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<const composite_font>& in_composite_font, float in_size, const boost::flyweight<std::string>& 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<std::string>& 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<const composite_font> font;
|
||||
boost::flyweight<std::string> 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<std::string> in_legacy_font_name, font_hinting in_hinting);
|
||||
};
|
24
src/renderer/core/fonts/font_rasterization_mode.h
Normal file
24
src/renderer/core/fonts/font_rasterization_mode.h
Normal file
@ -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;
|
||||
}
|
||||
}
|
1
src/renderer/core/fonts/font_renderer.cpp
Normal file
1
src/renderer/core/fonts/font_renderer.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "font_renderer.h"
|
26
src/renderer/core/fonts/font_renderer.h
Normal file
26
src/renderer/core/fonts/font_renderer.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "font_cache_freetype.h"
|
||||
|
||||
class composite_font_cache;
|
||||
|
||||
struct freetype_face_glyph_data {
|
||||
std::shared_ptr<freetype_face> 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;
|
||||
};
|
300
src/renderer/core/fonts/sdf_generator.cpp
Normal file
300
src/renderer/core/fonts/sdf_generator.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
#include "sdf_generator.h"
|
||||
|
||||
#include <msdfgen/core/contour-combiners.h>
|
||||
#include <msdfgen/core/ShapeDistanceFinder.h>
|
||||
#include <msdfgen/core/DistanceMapping.h>
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "font_cache_freetype.h"
|
||||
#include <queue>
|
||||
#include <future>
|
||||
#include <async/thread_pool.h>
|
||||
|
||||
#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<freetype_face> 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<uint8_t> 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<float, 1> 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<float, 1> 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<float, 4> 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<task>();
|
||||
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<std::shared_ptr<task>>& 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> sdf_generator::create() {
|
||||
return std::make_unique<sdf_generator_impl>();
|
||||
}
|
56
src/renderer/core/fonts/sdf_generator.h
Normal file
56
src/renderer/core/fonts/sdf_generator.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include <freetype/freetype.h>
|
||||
#include <msdfgen/msdfgen.h>
|
||||
#include <msdfgen/msdfgen-ext.h>
|
||||
|
||||
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<freetype_face> 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<void(request_descriptor request_id, std::vector<uint8_t> 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<sdf_generator> create();
|
||||
protected:
|
||||
sdf_generator() = default;
|
||||
};
|
1
src/renderer/core/fonts/shape_distance_finder.cpp
Normal file
1
src/renderer/core/fonts/shape_distance_finder.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "shape_distance_finder.h"
|
65
src/renderer/core/fonts/shape_distance_finder.h
Normal file
65
src/renderer/core/fonts/shape_distance_finder.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <msdfgen/msdfgen.h>
|
||||
#include <msdfgen/core/edge-selectors.h>
|
||||
|
||||
#define DISTANCE_DELTA_FACTOR 1.001
|
||||
|
||||
template<typename contour_combiner>
|
||||
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_cache_type> edge_caches;
|
||||
};
|
||||
|
1
src/renderer/core/fonts/text_shaper.cpp
Normal file
1
src/renderer/core/fonts/text_shaper.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "text_shaper.h"
|
35
src/renderer/core/fonts/text_shaper.h
Normal file
35
src/renderer/core/fonts/text_shaper.h
Normal file
@ -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<freetype_face> 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<uint32_t> a;
|
||||
std::weak_ptr<freetype_face> 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);
|
||||
};
|
1
src/renderer/core/internationalization/culture.cpp
Normal file
1
src/renderer/core/internationalization/culture.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "culture.h"
|
18
src/renderer/core/internationalization/culture.h
Normal file
18
src/renderer/core/internationalization/culture.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class culture;
|
||||
using culture_ptr = std::shared_ptr<culture>;
|
||||
|
||||
class culture {
|
||||
public:
|
||||
~culture() = default;
|
||||
|
||||
static culture_ptr create(const std::u32string& name) {
|
||||
}
|
||||
|
||||
const std::u32string& get_display_name() const {
|
||||
}
|
||||
};
|
||||
|
@ -0,0 +1 @@
|
||||
#include "internationalization.h"
|
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "misc/lazy_singleton.h"
|
||||
|
||||
class internationalization {
|
||||
public:
|
||||
static internationalization& get() {
|
||||
return lazy_singleton<internationalization>::get();
|
||||
}
|
||||
|
||||
static void tear_down() {
|
||||
lazy_singleton<internationalization>::tear_down();
|
||||
}
|
||||
|
||||
bool set_current_culture(const std::u32string& culture) {
|
||||
}
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
#include "core/renderer/renderer_buffer.h"
|
||||
#include "misc/color.h"
|
||||
#include "../../../core/misc/color.h"
|
||||
|
||||
struct aorii_vertex_param {
|
||||
float param_a1;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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<int8_t> 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; }
|
||||
|
@ -3,9 +3,9 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
enum class buffer_type {
|
||||
vertex,
|
||||
index,
|
||||
constant,
|
||||
VERTEX,
|
||||
INDEX,
|
||||
CONSTANT,
|
||||
};
|
||||
|
||||
class renderer_buffer {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<measured_ch> 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<measured_ch> result;
|
||||
@ -270,7 +263,7 @@ std::vector<measured_ch> 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;
|
||||
|
@ -94,7 +94,7 @@ public:
|
||||
private:
|
||||
ch_atlas_item const* cache_ch_to_atlas(char32_t ch);
|
||||
std::vector<font_data> fonts;
|
||||
const float pixel_height = 64;
|
||||
const float pixel_height = 128;
|
||||
const int padding = 5;
|
||||
|
||||
renderer_texture_array* texture_array;
|
||||
|
@ -1,3 +0,0 @@
|
||||
#include "color.h"
|
||||
|
||||
const linear_color linear_color::white = linear_color(1, 1, 1, 1);
|
@ -1,51 +0,0 @@
|
||||
#pragma once
|
||||
#include <complex>
|
||||
|
||||
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;
|
||||
};
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user