Support freetype font fallbacks by storing a sequence of fonts
This commit is contained in:
parent
e4222f35ff
commit
6a8563cce9
2 changed files with 69 additions and 27 deletions
|
|
@ -92,4 +92,7 @@ namespace psemek::fonts
|
|||
|
||||
std::unique_ptr<font_builder> load_freetype_font(wgpu::device device, std::filesystem::path const & path);
|
||||
|
||||
// Font paths are specified in order
|
||||
std::unique_ptr<font_builder> load_combined_freetype_font(wgpu::device device, std::vector<std::filesystem::path> const & paths);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,13 +71,20 @@ namespace psemek::fonts
|
|||
|
||||
struct face_shared
|
||||
{
|
||||
util::blob data;
|
||||
wgpu::device device;
|
||||
|
||||
struct face_data
|
||||
{
|
||||
util::blob data;
|
||||
FT_Face face;
|
||||
};
|
||||
|
||||
std::vector<face_data> faces = {};
|
||||
|
||||
~face_shared()
|
||||
{
|
||||
FT_Done_Face(face);
|
||||
for (auto const & face : faces)
|
||||
FT_Done_Face(face.face);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -89,6 +96,9 @@ namespace psemek::fonts
|
|||
, type_(type)
|
||||
, size_(size)
|
||||
{
|
||||
for (auto const & face : state_->faces)
|
||||
ft_check_result(FT_Set_Char_Size(face.face, 0, size_ << 6, 0, 0), "Failed to select freetype face size: ");
|
||||
|
||||
font::shape("x", {});
|
||||
xheight_ = -shaped_text_[0].position[1].min;
|
||||
shaped_text_.clear();
|
||||
|
|
@ -101,7 +111,7 @@ namespace psemek::fonts
|
|||
|
||||
std::string_view name() const override
|
||||
{
|
||||
return FT_Get_Postscript_Name(state_->face);
|
||||
return FT_Get_Postscript_Name(state_->faces[0].face);
|
||||
}
|
||||
|
||||
math::vector<int, 2> size() const override
|
||||
|
|
@ -148,6 +158,12 @@ namespace psemek::fonts
|
|||
|
||||
std::vector<page> pages_;
|
||||
|
||||
struct glyph_mapping_data
|
||||
{
|
||||
std::uint32_t font_index;
|
||||
std::uint32_t glyph_index;
|
||||
};
|
||||
|
||||
struct glyph_data
|
||||
{
|
||||
int page;
|
||||
|
|
@ -156,8 +172,8 @@ namespace psemek::fonts
|
|||
math::vector<int, 2> advance;
|
||||
};
|
||||
|
||||
util::hash_map<char32_t, std::uint32_t> glyph_mapping_;
|
||||
util::hash_map<std::uint32_t, glyph_data> glyphs_;
|
||||
util::hash_map<char32_t, glyph_mapping_data> glyph_mapping_;
|
||||
util::hash_map<std::uint32_t, util::hash_map<std::uint32_t, glyph_data>> glyphs_;
|
||||
|
||||
std::vector<shaped_glyph> shaped_text_;
|
||||
|
||||
|
|
@ -168,40 +184,53 @@ namespace psemek::fonts
|
|||
|
||||
shaped_text_.clear();
|
||||
|
||||
auto face = state_->face;
|
||||
|
||||
ft_check_result(FT_Set_Char_Size(face, 0, size_ << 6, 0, 0), "Failed to select freetype face size: ");
|
||||
|
||||
bool need_update_pages = false;
|
||||
|
||||
for (char32_t ch : string)
|
||||
{
|
||||
std::uint32_t glyph_id;
|
||||
glyph_mapping_data mapping{0, 0};
|
||||
|
||||
if (auto it = glyph_mapping_.find(ch); it != glyph_mapping_.end())
|
||||
{
|
||||
glyph_id = it->second;
|
||||
mapping = it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
glyph_id = FT_Get_Char_Index(face, ch);
|
||||
if (glyph_id == 0)
|
||||
glyph_id = FT_Get_Char_Index(face, '?');
|
||||
|
||||
glyph_mapping_[ch] = glyph_id;
|
||||
int glyph_index = 0;
|
||||
for (int font_index = 0; font_index < state_->faces.size(); ++font_index)
|
||||
{
|
||||
glyph_index = FT_Get_Char_Index(state_->faces[font_index].face, ch);
|
||||
if (glyph_index != 0)
|
||||
{
|
||||
mapping = {font_index, glyph_index};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (glyph_index == 0)
|
||||
{
|
||||
mapping.font_index = 0;
|
||||
mapping.glyph_index = FT_Get_Char_Index(state_->faces[0].face, '?');
|
||||
}
|
||||
|
||||
glyph_mapping_[ch] = mapping;
|
||||
}
|
||||
|
||||
auto face = state_->faces[mapping.font_index].face;
|
||||
|
||||
glyph_data * data = nullptr;
|
||||
|
||||
if (auto it = glyphs_.find(glyph_id); it != glyphs_.end())
|
||||
auto & glyphs = glyphs_[mapping.font_index];
|
||||
|
||||
if (auto it = glyphs.find(mapping.glyph_index); it != glyphs.end())
|
||||
{
|
||||
data = &(it->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = &glyphs_[glyph_id];
|
||||
data = &glyphs[mapping.glyph_index];
|
||||
|
||||
FT_Load_Glyph(face, glyph_id, type_ == font_type::bitmap ? FT_LOAD_TARGET_LIGHT : FT_LOAD_DEFAULT);
|
||||
FT_Load_Glyph(face, mapping.glyph_index, type_ == font_type::bitmap ? FT_LOAD_TARGET_LIGHT : FT_LOAD_DEFAULT);
|
||||
FT_Render_Glyph(face->glyph, type_ == font_type::bitmap ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_SDF);
|
||||
|
||||
if (face->glyph->bitmap.width + 2 * padding > page_size || face->glyph->bitmap.rows + 2 * padding > page_size)
|
||||
|
|
@ -210,7 +239,7 @@ namespace psemek::fonts
|
|||
data->part = {{{0, 0}, {0, 0}}};
|
||||
data->offset = {0, 0};
|
||||
data->advance = {size_, 0};
|
||||
log::warning() << "Glyph with ID " << glyph_id << " (U+" << std::hex << (int)ch << ") is larger than font atlas page: " << face->glyph->bitmap.width << "x" << face->glyph->bitmap.rows;
|
||||
log::warning() << "Glyph with ID " << mapping.glyph_index << " (U+" << std::hex << (int)ch << ") is larger than font atlas page: " << face->glyph->bitmap.width << "x" << face->glyph->bitmap.rows;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -319,7 +348,7 @@ namespace psemek::fonts
|
|||
|
||||
std::string_view name() const override
|
||||
{
|
||||
return FT_Get_Postscript_Name(state_->face);
|
||||
return FT_Get_Postscript_Name(state_->faces[0].face);
|
||||
}
|
||||
|
||||
std::unique_ptr<font> create(font_type type, int size) override
|
||||
|
|
@ -336,17 +365,27 @@ namespace psemek::fonts
|
|||
}
|
||||
|
||||
std::unique_ptr<font_builder> load_freetype_font(wgpu::device device, std::filesystem::path const & path)
|
||||
{
|
||||
return load_combined_freetype_font(device, {path});
|
||||
}
|
||||
|
||||
std::unique_ptr<font_builder> load_combined_freetype_font(wgpu::device device, std::vector<std::filesystem::path> const & paths)
|
||||
{
|
||||
auto result = std::make_shared<face_shared>(device);
|
||||
|
||||
for (auto const & path : paths)
|
||||
{
|
||||
auto font_data = io::read_full(io::file_istream{path});
|
||||
|
||||
FT_Face face;
|
||||
FT_Face face = nullptr;
|
||||
ft_check_result(FT_New_Memory_Face(ft_library(), reinterpret_cast<FT_Byte const *>(font_data.data()), font_data.size(), 0, &face), util::to_string("Failed to load font ", path, ": "));
|
||||
|
||||
auto result = std::make_unique<freetype_font_builder>(std::make_shared<face_shared>(std::move(font_data), device, face));
|
||||
result->faces.push_back({std::move(font_data), face});
|
||||
|
||||
log::debug() << "Loaded font " << result->name() << " (" << path << ")";
|
||||
log::debug() << "Loaded font " << FT_Get_Postscript_Name(face) << " (" << path << ")";
|
||||
}
|
||||
|
||||
return result;
|
||||
return std::make_unique<freetype_font_builder>(std::move(result));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue