Browse Source

Do not precalculate kerning for all characters

Precalculating kerning for all character pairs consumed a lot of RAM (N*N complexity). For CJK fonts that contain tens thousands of glyphs it was a highly unpractical approach resulting libRocket consuming >1.5GB of RAM. Instead, encapsulate FT_Get_Kerning call in FontFaceHandle::GetKerning and let FreeType handle the kerning on the fly. In my measurements, the performance difference is negligible, especially when compiled compiled geometry is utilized.
Victor Luchits 12 years ago
parent
commit
fa07b1e0e1
2 changed files with 22 additions and 46 deletions
  1. 18 42
      Source/Core/FontFaceHandle.cpp
  2. 4 4
      Source/Core/FontFaceHandle.h

+ 18 - 42
Source/Core/FontFaceHandle.cpp

@@ -55,6 +55,8 @@ FontFaceHandle::FontFaceHandle()
 	underline_position = 0;
 	underline_position = 0;
 	underline_thickness = 0;
 	underline_thickness = 0;
 
 
+	ft_face = NULL;
+
 	base_layer = NULL;
 	base_layer = NULL;
 }
 }
 
 
@@ -87,6 +89,8 @@ bool FontFaceHandle::Initialise(FT_Face ft_face, const String& _charset, int _si
 		return false;
 		return false;
 	}
 	}
 
 
+	this->ft_face = ft_face;
+
 	// find the maximum character we are interested in
 	// find the maximum character we are interested in
 	max_codepoint = 0;
 	max_codepoint = 0;
 	for (size_t i = 0; i < charset.size(); ++i)
 	for (size_t i = 0; i < charset.size(); ++i)
@@ -95,12 +99,10 @@ bool FontFaceHandle::Initialise(FT_Face ft_face, const String& _charset, int _si
 	// Construct the list of the characters specified by the charset.
 	// Construct the list of the characters specified by the charset.
 	glyphs.resize(max_codepoint+1, FontGlyph());
 	glyphs.resize(max_codepoint+1, FontGlyph());
 	for (size_t i = 0; i < charset.size(); ++i)
 	for (size_t i = 0; i < charset.size(); ++i)
-		BuildGlyphMap(ft_face, charset[i]);
+		BuildGlyphMap(charset[i]);
 
 
 	// Generate the metrics for the handle.
 	// Generate the metrics for the handle.
-	GenerateMetrics(ft_face);
-
-	BuildKerning(ft_face);
+	GenerateMetrics();
 
 
 	// Generate the default layer and layer configuration.
 	// Generate the default layer and layer configuration.
 	base_layer = GenerateLayer(NULL);
 	base_layer = GenerateLayer(NULL);
@@ -351,7 +353,7 @@ void FontFaceHandle::OnReferenceDeactivate()
 	delete this;
 	delete this;
 }
 }
 
 
-void FontFaceHandle::GenerateMetrics(FT_Face ft_face)
+void FontFaceHandle::GenerateMetrics()
 {
 {
 	line_height = ft_face->size->metrics.height >> 6;
 	line_height = ft_face->size->metrics.height >> 6;
 	baseline = line_height - (ft_face->size->metrics.ascender >> 6);
 	baseline = line_height - (ft_face->size->metrics.ascender >> 6);
@@ -384,7 +386,7 @@ void FontFaceHandle::GenerateMetrics(FT_Face ft_face)
 		x_height = 0;
 		x_height = 0;
 }
 }
 
 
-void FontFaceHandle::BuildGlyphMap(FT_Face ft_face, const UnicodeRange& unicode_range)
+void FontFaceHandle::BuildGlyphMap(const UnicodeRange& unicode_range)
 {
 {
 	for (word character_code = (word) (Math::Max< unsigned int >(unicode_range.min_codepoint, 32)); character_code <= unicode_range.max_codepoint; ++character_code)
 	for (word character_code = (word) (Math::Max< unsigned int >(unicode_range.min_codepoint, 32)); character_code <= unicode_range.max_codepoint; ++character_code)
 	{
 	{
@@ -496,48 +498,22 @@ void FontFaceHandle::BuildGlyph(FontGlyph& glyph, FT_GlyphSlot ft_glyph)
 		glyph.bitmap_data = NULL;
 		glyph.bitmap_data = NULL;
 }
 }
 
 
-void FontFaceHandle::BuildKerning(FT_Face ft_face)
-{
-	// Compile the kerning information for this character if the font includes it.
-	if (FT_HAS_KERNING(ft_face))
-	{
-		kerning.resize(max_codepoint+1, GlyphKerningList(max_codepoint+1, 0));
-
-		for (size_t i = 0; i < charset.size(); ++i)
-		{
-			for (word rhs = (word) (Math::Max< unsigned int >(charset[i].min_codepoint, 32)); rhs <= charset[i].max_codepoint; ++rhs)
-			{
-				GlyphKerningList glyph_kerning(max_codepoint+1, 0);
-
-				for (size_t j = 0; j < charset.size(); ++j)
-				{
-					for (word lhs = (word) (Math::Max< unsigned int >(charset[j].min_codepoint, 32)); lhs <= charset[j].max_codepoint; ++lhs)
-					{
-						FT_Vector ft_kerning;
-						FT_Get_Kerning(ft_face, FT_Get_Char_Index(ft_face, lhs), FT_Get_Char_Index(ft_face, rhs), FT_KERNING_DEFAULT, &ft_kerning);
-
-						int kerning = ft_kerning.x >> 6;
-						if (kerning != 0)
-							glyph_kerning[lhs] = kerning;
-					}
-				}
-
-				kerning[rhs] = glyph_kerning;
-			}
-		}
-	}
-}
-
 int FontFaceHandle::GetKerning(word lhs, word rhs) const
 int FontFaceHandle::GetKerning(word lhs, word rhs) const
 {
 {
-	if (rhs >= kerning.size())
+	if (!FT_HAS_KERNING(ft_face))
 		return 0;
 		return 0;
 
 
-	const GlyphKerningList &kerning_map = kerning[rhs];
-	if (lhs >= kerning_map.size())
+	FT_Vector ft_kerning;
+
+	FT_Error ft_error = FT_Get_Kerning(ft_face, 
+		FT_Get_Char_Index(ft_face, lhs), FT_Get_Char_Index(ft_face, rhs),
+		FT_KERNING_DEFAULT, &ft_kerning);
+
+	if (ft_error != 0)
 		return 0;
 		return 0;
 
 
-	return kerning_map[lhs];
+	int kerning = ft_kerning.x >> 6;
+	return kerning;
 }
 }
 
 
 // Generates (or shares) a layer derived from a font effect.
 // Generates (or shares) a layer derived from a font effect.

+ 4 - 4
Source/Core/FontFaceHandle.h

@@ -127,12 +127,11 @@ protected:
 	virtual void OnReferenceDeactivate();
 	virtual void OnReferenceDeactivate();
 
 
 private:
 private:
-	void GenerateMetrics(FT_Face ft_face);
+	void GenerateMetrics(void);
 
 
-	void BuildGlyphMap(FT_Face ft_face, const UnicodeRange& unicode_range);
+	void BuildGlyphMap(const UnicodeRange& unicode_range);
 	void BuildGlyph(FontGlyph& glyph, FT_GlyphSlot ft_glyph);
 	void BuildGlyph(FontGlyph& glyph, FT_GlyphSlot ft_glyph);
 
 
-	void BuildKerning(FT_Face ft_face);
 	int GetKerning(word lhs, word rhs) const;
 	int GetKerning(word lhs, word rhs) const;
 
 
 	// Generates (or shares) a layer derived from a font effect.
 	// Generates (or shares) a layer derived from a font effect.
@@ -141,8 +140,9 @@ private:
 	typedef std::vector< int > GlyphKerningList;
 	typedef std::vector< int > GlyphKerningList;
 	typedef std::vector< GlyphKerningList > FontKerningList;
 	typedef std::vector< GlyphKerningList > FontKerningList;
 
 
+	FT_Face ft_face;
+
 	FontGlyphList glyphs;
 	FontGlyphList glyphs;
-	FontKerningList kerning;
 
 
 	typedef std::map< const FontEffect*, FontFaceLayer* > FontLayerMap;
 	typedef std::map< const FontEffect*, FontFaceLayer* > FontLayerMap;
 	typedef std::map< String, FontFaceLayer* > FontLayerCache;
 	typedef std::map< String, FontFaceLayer* > FontLayerCache;