Browse Source

Use HarfBuzz kerning instead of FreeType's (#639)

Matthew Schäfer 8 months ago
parent
commit
47476069e9

+ 2 - 2
Samples/basic/harfbuzz/src/FontEngineInterfaceHarfBuzz.cpp

@@ -45,8 +45,8 @@ bool FontEngineInterfaceHarfBuzz::LoadFontFace(const String& file_name, int face
 	return FontProvider::LoadFontFace(file_name, face_index, fallback_face, weight);
 	return FontProvider::LoadFontFace(file_name, face_index, fallback_face, weight);
 }
 }
 
 
-bool FontEngineInterfaceHarfBuzz::LoadFontFace(Span<const byte> data, int face_index, const String& font_family, Style::FontStyle style, Style::FontWeight weight,
-	bool fallback_face)
+bool FontEngineInterfaceHarfBuzz::LoadFontFace(Span<const byte> data, int face_index, const String& font_family, Style::FontStyle style,
+	Style::FontWeight weight, bool fallback_face)
 {
 {
 	return FontProvider::LoadFontFace(data, face_index, font_family, style, weight, fallback_face);
 	return FontProvider::LoadFontFace(data, face_index, font_family, style, weight, fallback_face);
 }
 }

+ 24 - 67
Samples/basic/harfbuzz/src/FontFaceHandleHarfBuzz.cpp

@@ -68,12 +68,10 @@ bool FontFaceHandleHarfBuzz::Initialize(FontFaceHandleFreetype face, int font_si
 	if (!FreeType::InitialiseFaceHandle(ft_face, font_size, glyphs, metrics, load_default_glyphs))
 	if (!FreeType::InitialiseFaceHandle(ft_face, font_size, glyphs, metrics, load_default_glyphs))
 		return false;
 		return false;
 
 
-	has_kerning = Rml::FreeType::HasKerning(ft_face);
-	FillKerningPairCache();
-
 	hb_font = hb_ft_font_create_referenced((FT_Face)ft_face);
 	hb_font = hb_ft_font_create_referenced((FT_Face)ft_face);
 	RMLUI_ASSERT(hb_font != nullptr);
 	RMLUI_ASSERT(hb_font != nullptr);
 	hb_font_set_ptem(hb_font, (float)font_size);
 	hb_font_set_ptem(hb_font, (float)font_size);
+	hb_ft_font_set_funcs(hb_font);
 
 
 	// Generate the default layer and layer configuration.
 	// Generate the default layer and layer configuration.
 	base_layer = GetOrCreateLayer(nullptr);
 	base_layer = GetOrCreateLayer(nullptr);
@@ -98,7 +96,7 @@ const FallbackFontGlyphMap& FontFaceHandleHarfBuzz::GetFallbackGlyphs() const
 }
 }
 
 
 int FontFaceHandleHarfBuzz::GetStringWidth(StringView string, const TextShapingContext& text_shaping_context,
 int FontFaceHandleHarfBuzz::GetStringWidth(StringView string, const TextShapingContext& text_shaping_context,
-	const LanguageDataMap& registered_languages, Character prior_character)
+	const LanguageDataMap& registered_languages, Character /*prior_character*/)
 {
 {
 	int width = 0;
 	int width = 0;
 
 
@@ -109,10 +107,9 @@ int FontFaceHandleHarfBuzz::GetStringWidth(StringView string, const TextShapingC
 	hb_buffer_add_utf8(shaping_buffer, string.begin(), (int)string.size(), 0, (int)string.size());
 	hb_buffer_add_utf8(shaping_buffer, string.begin(), (int)string.size(), 0, (int)string.size());
 	hb_shape(hb_font, shaping_buffer, nullptr, 0);
 	hb_shape(hb_font, shaping_buffer, nullptr, 0);
 
 
-	FontGlyphIndex prior_glyph_codepoint = FreeType::GetGlyphIndexFromCharacter(ft_face, prior_character);
-
 	unsigned int glyph_count = 0;
 	unsigned int glyph_count = 0;
 	hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(shaping_buffer, &glyph_count);
 	hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(shaping_buffer, &glyph_count);
+	hb_glyph_position_t* glyph_positions = hb_buffer_get_glyph_positions(shaping_buffer, &glyph_count);
 
 
 	for (int g = 0; g < (int)glyph_count; ++g)
 	for (int g = 0; g < (int)glyph_count; ++g)
 	{
 	{
@@ -121,19 +118,19 @@ int FontFaceHandleHarfBuzz::GetStringWidth(StringView string, const TextShapingC
 		if (IsASCIIControlCharacter(character))
 		if (IsASCIIControlCharacter(character))
 			continue;
 			continue;
 
 
-		FontGlyphIndex glyph_codepoint = glyph_info[g].codepoint;
-		const FontGlyph* glyph = GetOrAppendGlyph(glyph_codepoint, character);
+		FontGlyphIndex glyph_index = glyph_info[g].codepoint;
+		const FontGlyph* glyph = GetOrAppendGlyph(glyph_index, character);
 		if (!glyph)
 		if (!glyph)
 			continue;
 			continue;
 
 
-		// Adjust the cursor for the kerning between this character and the previous one.
-		width += GetKerning(prior_glyph_codepoint, glyph_codepoint);
-
 		// Adjust the cursor for this character's advance.
 		// Adjust the cursor for this character's advance.
-		width += glyph->advance;
-		width += (int)text_shaping_context.letter_spacing;
+		if (glyph_index != 0)
+			width += glyph_positions[g].x_advance >> 6;
+		else
+			// Use the unshaped advance for unsupported characters.
+			width += glyph->advance;
 
 
-		prior_glyph_codepoint = glyph_codepoint;
+		width += (int)text_shaping_context.letter_spacing;
 	}
 	}
 
 
 	hb_buffer_destroy(shaping_buffer);
 	hb_buffer_destroy(shaping_buffer);
@@ -265,7 +262,6 @@ int FontFaceHandleHarfBuzz::GenerateString(RenderManager& render_manager, Textur
 		RMLUI_ASSERT(geometry_index + num_textures <= (int)mesh_list.size());
 		RMLUI_ASSERT(geometry_index + num_textures <= (int)mesh_list.size());
 
 
 		line_width = 0;
 		line_width = 0;
-		FontGlyphIndex prior_glyph_codepoint = 0;
 
 
 		// Set the mesh and textures to the geometries.
 		// Set the mesh and textures to the geometries.
 		for (int tex_index = 0; tex_index < num_textures; ++tex_index)
 		for (int tex_index = 0; tex_index < num_textures; ++tex_index)
@@ -279,6 +275,7 @@ int FontFaceHandleHarfBuzz::GenerateString(RenderManager& render_manager, Textur
 
 
 		unsigned int glyph_count = 0;
 		unsigned int glyph_count = 0;
 		hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(shaping_buffer, &glyph_count);
 		hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(shaping_buffer, &glyph_count);
+		hb_glyph_position_t* glyph_positions = hb_buffer_get_glyph_positions(shaping_buffer, &glyph_count);
 
 
 		mesh_list[geometry_index].mesh.indices.reserve(string.size() * 6);
 		mesh_list[geometry_index].mesh.indices.reserve(string.size() * 6);
 		mesh_list[geometry_index].mesh.vertices.reserve(string.size() * 4);
 		mesh_list[geometry_index].mesh.vertices.reserve(string.size() * 4);
@@ -291,25 +288,28 @@ int FontFaceHandleHarfBuzz::GenerateString(RenderManager& render_manager, Textur
 			if (IsASCIIControlCharacter(character))
 			if (IsASCIIControlCharacter(character))
 				continue;
 				continue;
 
 
-			FontGlyphIndex glyph_codepoint = glyph_info[g].codepoint;
-			const FontGlyph* glyph = GetOrAppendGlyph(glyph_codepoint, character);
+			FontGlyphIndex glyph_index = glyph_info[g].codepoint;
+			const FontGlyph* glyph = GetOrAppendGlyph(glyph_index, character);
 			if (!glyph)
 			if (!glyph)
 				continue;
 				continue;
 
 
-			// Adjust the cursor for the kerning between this character and the previous one.
-			line_width += GetKerning(prior_glyph_codepoint, glyph_codepoint);
-
 			ColourbPremultiplied glyph_color = layer_colour;
 			ColourbPremultiplied glyph_color = layer_colour;
 			// Use white vertex colors on RGB glyphs.
 			// Use white vertex colors on RGB glyphs.
 			if (layer == base_layer && glyph->color_format == ColorFormat::RGBA8)
 			if (layer == base_layer && glyph->color_format == ColorFormat::RGBA8)
 				glyph_color = ColourbPremultiplied(layer_colour.alpha, layer_colour.alpha);
 				glyph_color = ColourbPremultiplied(layer_colour.alpha, layer_colour.alpha);
 
 
-			layer->GenerateGeometry(&mesh_list[geometry_index], glyph_codepoint, character, Vector2f(position.x + line_width, position.y),
-				glyph_color);
+			Vector2f glyph_offset = {float(glyph_positions[g].x_offset >> 6), float(glyph_positions[g].y_offset >> 6)};
+			Vector2f glyph_geometry_position = Vector2f{position.x + line_width, position.y} + glyph_offset;
+			layer->GenerateGeometry(&mesh_list[geometry_index], glyph_index, character, glyph_geometry_position, glyph_color);
+
+			// Adjust the cursor for this character's advance.
+			if (glyph_index != 0)
+				line_width += glyph_positions[g].x_advance >> 6;
+			else
+				// Use the unshaped advance for unsupported characters.
+				line_width += glyph->advance;
 
 
-			line_width += glyph->advance;
 			line_width += (int)text_shaping_context.letter_spacing;
 			line_width += (int)text_shaping_context.letter_spacing;
-			prior_glyph_codepoint = glyph_codepoint;
 		}
 		}
 
 
 		geometry_index += num_textures;
 		geometry_index += num_textures;
@@ -383,49 +383,6 @@ bool FontFaceHandleHarfBuzz::AppendFallbackGlyph(Character character)
 	return false;
 	return false;
 }
 }
 
 
-void FontFaceHandleHarfBuzz::FillKerningPairCache()
-{
-	if (!has_kerning)
-		return;
-
-	static constexpr char32_t KerningCache_AsciiSubsetBegin = 32;
-	static constexpr char32_t KerningCache_AsciiSubsetLast = 126;
-
-	for (char32_t i = KerningCache_AsciiSubsetBegin; i <= KerningCache_AsciiSubsetLast; i++)
-	{
-		for (char32_t j = KerningCache_AsciiSubsetBegin; j <= KerningCache_AsciiSubsetLast; j++)
-		{
-			const bool first_iteration = (i == KerningCache_AsciiSubsetBegin && j == KerningCache_AsciiSubsetBegin);
-
-			// Fetch the kerning from the font face. Submit zero font size on subsequent iterations for performance reasons.
-			const int kerning = FreeType::GetKerning(ft_face, first_iteration ? metrics.size : 0,
-				FreeType::GetGlyphIndexFromCharacter(ft_face, Character(i)), FreeType::GetGlyphIndexFromCharacter(ft_face, Character(j)));
-			if (kerning != 0)
-			{
-				kerning_pair_cache.emplace(AsciiPair((i << 8) | j), KerningIntType(kerning));
-			}
-		}
-	}
-}
-
-int FontFaceHandleHarfBuzz::GetKerning(FontGlyphIndex lhs, FontGlyphIndex rhs) const
-{
-	// Check if we have no kerning, or if we are have an unsupported character.
-	if (!has_kerning || lhs == 0 || rhs == 0)
-		return 0;
-
-	// See if the kerning pair has been cached.
-	const auto it = kerning_pair_cache.find(AsciiPair((int(lhs) << 8) | int(rhs)));
-	if (it != kerning_pair_cache.end())
-	{
-		return it->second;
-	}
-
-	// Fetch it from the font face instead.
-	const int result = FreeType::GetKerning(ft_face, metrics.size, lhs, rhs);
-	return result;
-}
-
 const FontGlyph* FontFaceHandleHarfBuzz::GetOrAppendGlyph(FontGlyphIndex glyph_index, Character& character, bool look_in_fallback_fonts)
 const FontGlyph* FontFaceHandleHarfBuzz::GetOrAppendGlyph(FontGlyphIndex glyph_index, Character& character, bool look_in_fallback_fonts)
 {
 {
 	if (glyph_index == 0 && look_in_fallback_fonts && character != Character::Replacement)
 	if (glyph_index == 0 && look_in_fallback_fonts && character != Character::Replacement)

+ 0 - 13
Samples/basic/harfbuzz/src/FontFaceHandleHarfBuzz.h

@@ -119,12 +119,6 @@ private:
 	// Build and append fallback glyph to 'fallback_glyphs'.
 	// Build and append fallback glyph to 'fallback_glyphs'.
 	bool AppendFallbackGlyph(Character character);
 	bool AppendFallbackGlyph(Character character);
 
 
-	// Build a kerning cache for common characters.
-	void FillKerningPairCache();
-
-	// Return the kerning for a codepoint pair.
-	int GetKerning(FontGlyphIndex lhs, FontGlyphIndex rhs) const;
-
 	/// Retrieve a glyph from the given code index, building and appending a new glyph if not already built.
 	/// Retrieve a glyph from the given code index, building and appending a new glyph if not already built.
 	/// @param[in] glyph_index  The glyph index.
 	/// @param[in] glyph_index  The glyph index.
 	/// @param[in-out] character  The character codepoint, can be changed e.g. to the replacement character if no glyph is found..
 	/// @param[in-out] character  The character codepoint, can be changed e.g. to the replacement character if no glyph is found..
@@ -168,13 +162,6 @@ private:
 	// Each font layer that generated geometry or textures, indexed by the font-effect's fingerprint key.
 	// Each font layer that generated geometry or textures, indexed by the font-effect's fingerprint key.
 	FontLayerCache layer_cache;
 	FontLayerCache layer_cache;
 
 
-	// Pre-cache kerning pairs for some ascii subset of all characters.
-	using AsciiPair = uint16_t;
-	using KerningIntType = int16_t;
-	using KerningPairs = UnorderedMap<AsciiPair, KerningIntType>;
-	KerningPairs kerning_pair_cache;
-
-	bool has_kerning = false;
 	bool is_layers_dirty = false;
 	bool is_layers_dirty = false;
 	int version = 0;
 	int version = 0;
 
 

+ 0 - 26
Samples/basic/harfbuzz/src/FreeTypeInterface.cpp

@@ -80,32 +80,6 @@ bool AppendGlyph(FontFaceHandleFreetype face, int font_size, FontGlyphIndex glyp
 	return true;
 	return true;
 }
 }
 
 
-int GetKerning(FontFaceHandleFreetype face, int font_size, FontGlyphIndex lhs, FontGlyphIndex rhs)
-{
-	FT_Face ft_face = (FT_Face)face;
-
-	RMLUI_ASSERT(FT_HAS_KERNING(ft_face));
-
-	// Set face size again in case it was used at another size in another font face handle.
-	// Font size value of zero assumes it is already set.
-	if (font_size > 0)
-	{
-		float bitmap_scaling_factor = 1.0f;
-		if (!SetFontSize(ft_face, font_size, bitmap_scaling_factor) || bitmap_scaling_factor != 1.0f)
-			return 0;
-	}
-
-	FT_Vector ft_kerning;
-
-	FT_Error ft_error = FT_Get_Kerning(ft_face, lhs, rhs, FT_KERNING_DEFAULT, &ft_kerning);
-
-	if (ft_error)
-		return 0;
-
-	int kerning = ft_kerning.x >> 6;
-	return kerning;
-}
-
 FontGlyphIndex GetGlyphIndexFromCharacter(FontFaceHandleFreetype face, Character character)
 FontGlyphIndex GetGlyphIndexFromCharacter(FontFaceHandleFreetype face, Character character)
 {
 {
 	return FT_Get_Char_Index((FT_Face)face, (FT_ULong)character);
 	return FT_Get_Char_Index((FT_Face)face, (FT_ULong)character);

+ 0 - 4
Samples/basic/harfbuzz/src/FreeTypeInterface.h

@@ -42,10 +42,6 @@ bool InitialiseFaceHandle(FontFaceHandleFreetype face, int font_size, FontGlyphM
 // Build a new glyph representing the given glyph index and append to 'glyphs'.
 // Build a new glyph representing the given glyph index and append to 'glyphs'.
 bool AppendGlyph(FontFaceHandleFreetype face, int font_size, FontGlyphIndex glyph_index, Character character, FontGlyphMap& glyphs);
 bool AppendGlyph(FontFaceHandleFreetype face, int font_size, FontGlyphIndex glyph_index, Character character, FontGlyphMap& glyphs);
 
 
-// Returns the kerning between two characters given by glyph indices.
-// 'font_size' value of zero assumes the font size is already set on the face, and skips this step for performance reasons.
-int GetKerning(FontFaceHandleFreetype face, int font_size, FontGlyphIndex lhs, FontGlyphIndex rhs);
-
 // Returns the corresponding glyph index from a character code.
 // Returns the corresponding glyph index from a character code.
 FontGlyphIndex GetGlyphIndexFromCharacter(FontFaceHandleFreetype face, Character character);
 FontGlyphIndex GetGlyphIndexFromCharacter(FontFaceHandleFreetype face, Character character);