Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Include/RmlUi/Core/FontEngineInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class RMLUICORE_API FontEngineInterface {
/// Called when RmlUi is being shut down.
virtual void Shutdown();

virtual void OnBeginFrame();

/// Called by RmlUi when it wants to load a font face from file.
/// @param[in] file_name The file to load the face from.
/// @param[in] face_index The index of the font face within a font collection.
Expand Down Expand Up @@ -118,6 +120,8 @@ class RMLUICORE_API FontEngineInterface {
virtual int GenerateString(RenderManager& render_manager, FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, StringView string,
Vector2f position, ColourbPremultiplied colour, float opacity, const TextShapingContext& text_shaping_context, TexturedMeshList& mesh_list);

virtual bool EnsureGlyphs(FontFaceHandle face_handle, StringView string);

/// Called by RmlUi to determine if the text geometry is required to be re-generated. Whenever the returned version
/// is changed, all geometry belonging to the given face handle will be re-generated.
/// @param[in] face_handle The font handle.
Expand Down
Binary file added Samples/assets/NotoSansJP-Regular.ttf
Binary file not shown.
9 changes: 7 additions & 2 deletions Source/Core/ElementText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,26 @@ void ElementText::OnRender()
return;

RenderManager& render_manager = GetContext()->GetRenderManager();
FontEngineInterface& font_engine_interface = *GetFontEngineInterface();

// If our font effects have potentially changed, update it and force a geometry generation if necessary.
if (font_effects_dirty && UpdateFontEffects())
geometry_dirty = true;

// Dirty geometry if font version has changed.
int new_version = GetFontEngineInterface()->GetVersion(font_face_handle);
int new_version = font_engine_interface.GetVersion(font_face_handle);
if (new_version != font_handle_version)
{
font_handle_version = new_version;
geometry_dirty = true;
}

// Regenerate the geometry if the colour or font configuration has altered.
if (geometry_dirty)
bool should_regenerate = geometry_dirty;
if (!should_regenerate)
for (size_t i = 0; i < lines.size(); ++i)
should_regenerate = !font_engine_interface.EnsureGlyphs(font_face_handle, lines[i].text) || should_regenerate;
if (should_regenerate)
GenerateGeometry(render_manager, font_face_handle);

// Regenerate text decoration if necessary.
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/FontEngineDefault/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ target_sources(rmlui_core PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/FontTypes.h"
"${CMAKE_CURRENT_SOURCE_DIR}/FreeTypeInterface.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/FreeTypeInterface.h"
"${CMAKE_CURRENT_SOURCE_DIR}/LruList.h"
"${CMAKE_CURRENT_SOURCE_DIR}/SpriteSet.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/SpriteSet.h"
)

target_compile_definitions(rmlui_core PRIVATE "RMLUI_FONT_ENGINE_FREETYPE")
11 changes: 11 additions & 0 deletions Source/Core/FontEngineDefault/FontEngineInterfaceDefault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ void FontEngineInterfaceDefault::Shutdown()
FontProvider::Shutdown();
}

void FontEngineInterfaceDefault::OnBeginFrame()
{
FontProvider::OnBeginFrame();
}

bool FontEngineInterfaceDefault::LoadFontFace(const String& file_name, int face_index, bool fallback_face, Style::FontWeight weight)
{
return FontProvider::LoadFontFace(file_name, face_index, fallback_face, weight);
Expand Down Expand Up @@ -88,6 +93,12 @@ int FontEngineInterfaceDefault::GenerateString(RenderManager& render_manager, Fo
(int)font_effects_handle);
}

bool FontEngineInterfaceDefault::EnsureGlyphs(FontFaceHandle handle, StringView string)
{
auto handle_default = reinterpret_cast<FontFaceHandleDefault*>(handle);
return handle_default->EnsureGlyphs(string);
}

int FontEngineInterfaceDefault::GetVersion(FontFaceHandle handle)
{
auto handle_default = reinterpret_cast<FontFaceHandleDefault*>(handle);
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/FontEngineDefault/FontEngineInterfaceDefault.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class RMLUICORE_API FontEngineInterfaceDefault : public FontEngineInterface {
/// Called when RmlUi is being shut down.
void Shutdown() override;

void OnBeginFrame() override;

/// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
bool LoadFontFace(const String& file_name, int face_index, bool fallback_face, Style::FontWeight weight) override;

Expand All @@ -66,6 +68,8 @@ class RMLUICORE_API FontEngineInterfaceDefault : public FontEngineInterface {
Vector2f position, ColourbPremultiplied colour, float opacity, const TextShapingContext& text_shaping_context,
TexturedMeshList& mesh_list) override;

bool EnsureGlyphs(FontFaceHandle face_handle, StringView string) override;

/// Returns the current version of the font face.
int GetVersion(FontFaceHandle handle) override;

Expand Down
6 changes: 6 additions & 0 deletions Source/Core/FontEngineDefault/FontFace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ FontFaceHandleDefault* FontFace::GetHandle(int size, bool load_default_glyphs)
return result;
}

void FontFace::OnBeginFrame()
{
for (auto iterator = handles.begin(); iterator != handles.end(); ++iterator)
iterator->second->PurgeUnusedGlyphs();
}

void FontFace::ReleaseFontResources()
{
HandleMap().swap(handles);
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/FontEngineDefault/FontFace.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class FontFace {
/// @return The font handle.
FontFaceHandleDefault* GetHandle(int size, bool load_default_glyphs);

void OnBeginFrame();

/// Releases resources owned by sized font faces, including their textures and rendered glyphs.
void ReleaseFontResources();

Expand Down
62 changes: 57 additions & 5 deletions Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ bool FontFaceHandleDefault::Initialize(FontFaceHandleFreetype face, int font_siz

if (!FreeType::InitialiseFaceHandle(ft_face, font_size, glyphs, metrics, load_default_glyphs))
return false;
for (auto iterator = glyphs.begin(); iterator != glyphs.end(); ++iterator)
new_glyphs.push_back(&*iterator);

has_kerning = FreeType::HasKerning(ft_face);
FillKerningPairCache();
Expand All @@ -70,6 +72,8 @@ bool FontFaceHandleDefault::Initialize(FontFaceHandleFreetype face, int font_siz
base_layer = GetOrCreateLayer(nullptr);
layer_configurations.push_back(LayerConfiguration{base_layer});

new_glyphs.clear();

return true;
}

Expand All @@ -83,6 +87,21 @@ const FontGlyphMap& FontFaceHandleDefault::GetGlyphs() const
return glyphs;
}

void FontFaceHandleDefault::PurgeUnusedGlyphs() {
glyph_lru_list.tick();
Vector<Character> purged_characters;
while (glyph_lru_list.getLastEntryAge() > 600) {
const Character character = *glyph_lru_list.getLast();
glyphs.erase(character);
glyph_lru_list_handle_map.erase(character);
glyph_lru_list.evictLast();
purged_characters.push_back(character);
}
if (!purged_characters.empty())
for (auto& pair : layers)
pair.layer->RemoveGlyphs(purged_characters);
}

int FontFaceHandleDefault::GetStringWidth(StringView string, float letter_spacing, Character prior_character)
{
RMLUI_ZoneScoped;
Expand Down Expand Up @@ -173,7 +192,7 @@ int FontFaceHandleDefault::GenerateLayerConfiguration(const FontEffectList& font
return (int)(layer_configurations.size() - 1);
}

bool FontFaceHandleDefault::GenerateLayerTexture(Vector<byte>& texture_data, Vector2i& texture_dimensions, const FontEffect* font_effect,
bool FontFaceHandleDefault::GenerateLayerTexture(Span<const byte>& texture_data, Vector2i& texture_dimensions, const FontEffect* font_effect,
int texture_id, int handle_version) const
{
if (handle_version != version)
Expand Down Expand Up @@ -269,6 +288,29 @@ int FontFaceHandleDefault::GenerateString(RenderManager& render_manager, Texture
return Math::Max(line_width, 0);
}

bool FontFaceHandleDefault::EnsureGlyphs(StringView string)
{
bool all_alive = true;
for (auto it_string = StringIteratorU8(string); it_string; ++it_string)
{
Character character = *it_string;
if ((char32_t)character < (char32_t)' ')
continue;
if (glyphs.find(character) == glyphs.end())
{
new_characters.push_back(character);
all_alive = false;
}
else
{
glyph_lru_list.ping(glyph_lru_list_handle_map[character]);
}
}
if (!all_alive)
is_layers_dirty = true;
return all_alive;
}

bool FontFaceHandleDefault::UpdateLayersOnDirty()
{
bool result = false;
Expand All @@ -282,11 +324,16 @@ bool FontFaceHandleDefault::UpdateLayersOnDirty()
// Regenerate all the layers.
// Note: The layer regeneration needs to happen in the order in which the layers were created,
// otherwise we may end up cloning a layer which has not yet been regenerated. This means trouble!
for (const auto character : new_characters)
new_glyphs.push_back(&*glyphs.find(character));

for (auto& pair : layers)
{
GenerateLayer(pair.layer.get());
}

new_characters.clear();
new_glyphs.clear();
result = true;
}

Expand All @@ -300,8 +347,9 @@ int FontFaceHandleDefault::GetVersion() const

bool FontFaceHandleDefault::AppendGlyph(Character character)
{
bool result = FreeType::AppendGlyph(ft_face, metrics.size, character, glyphs);
return result;
if (!FreeType::AppendGlyph(ft_face, metrics.size, character, glyphs))
return false;
return true;
}

void FontFaceHandleDefault::FillKerningPairCache()
Expand Down Expand Up @@ -377,6 +425,8 @@ const FontGlyph* FontFaceHandleDefault::GetOrAppendGlyph(Character& character, b
RMLUI_ERROR;
return nullptr;
}
glyph_lru_list_handle_map.emplace(character, glyph_lru_list.add(character));
new_characters.push_back(character);

is_layers_dirty = true;
}
Expand Down Expand Up @@ -409,6 +459,8 @@ const FontGlyph* FontFaceHandleDefault::GetOrAppendGlyph(Character& character, b
if (it_glyph == glyphs.end())
return nullptr;
}
glyph_lru_list_handle_map.emplace(character, glyph_lru_list.add(character));
new_characters.push_back(character);
}
else
{
Expand Down Expand Up @@ -448,7 +500,7 @@ bool FontFaceHandleDefault::GenerateLayer(FontFaceLayer* layer)

if (!font_effect)
{
result = layer->Generate(this);
result = layer->Generate(this, new_glyphs);
}
else
{
Expand All @@ -471,7 +523,7 @@ bool FontFaceHandleDefault::GenerateLayer(FontFaceLayer* layer)
}

// Create a new layer.
result = layer->Generate(this, clone, clone_glyph_origins);
result = layer->Generate(this, new_glyphs, clone, clone_glyph_origins);

// Cache the layer in the layer cache if it generated its own textures (ie, didn't clone).
if (!clone)
Expand Down
13 changes: 12 additions & 1 deletion Source/Core/FontEngineDefault/FontFaceHandleDefault.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
#include "../../../Include/RmlUi/Core/Geometry.h"
#include "../../../Include/RmlUi/Core/Texture.h"
#include "../../../Include/RmlUi/Core/Traits.h"
#include "../../../Include/RmlUi/Core/Types.h"
#include "FontTypes.h"
#include "LruList.h"

namespace Rml {

Expand All @@ -56,6 +58,8 @@ class FontFaceHandleDefault final : public NonCopyMoveable {

const FontGlyphMap& GetGlyphs() const;

void PurgeUnusedGlyphs();

/// Returns the width a string will take up if rendered with this handle.
/// @param[in] string The string to measure.
/// @param[in] prior_character The optionally-specified character that immediately precedes the string. This may have an impact on the string
Expand All @@ -73,7 +77,7 @@ class FontFaceHandleDefault final : public NonCopyMoveable {
/// @param[in] font_effect The font effect used for the layer.
/// @param[in] texture_id The index of the texture within the layer to generate.
/// @param[in] handle_version The version of the handle data. Function returns false if out of date.
bool GenerateLayerTexture(Vector<byte>& texture_data, Vector2i& texture_dimensions, const FontEffect* font_effect, int texture_id,
bool GenerateLayerTexture(Span<const byte>& texture_data, Vector2i& texture_dimensions, const FontEffect* font_effect, int texture_id,
int handle_version) const;

/// Generates the geometry required to render a single line of text.
Expand All @@ -88,6 +92,8 @@ class FontFaceHandleDefault final : public NonCopyMoveable {
/// @return The width, in pixels, of the string geometry.
int GenerateString(RenderManager& render_manager, TexturedMeshList& mesh_list, StringView string, Vector2f position, ColourbPremultiplied colour,
float opacity, float letter_spacing, int layer_configuration);

bool EnsureGlyphs(StringView string);

/// Version is changed whenever the layers are dirtied, requiring regeneration of string geometry.
int GetVersion() const;
Expand Down Expand Up @@ -117,7 +123,12 @@ class FontFaceHandleDefault final : public NonCopyMoveable {
// (Re-)generate a layer in this font face handle.
bool GenerateLayer(FontFaceLayer* layer);

using GlyphLruList = LruList<Character>;
FontGlyphMap glyphs;
GlyphLruList glyph_lru_list;
UnorderedMap<Character, GlyphLruList::Handle> glyph_lru_list_handle_map;
Vector<Character> new_characters;
Vector<const FontGlyphMap::value_type*> new_glyphs;

struct EffectLayerPair {
const FontEffect* font_effect;
Expand Down
Loading
Loading