Browse Source

Replace std::map with std::vector for O(1) access to character properties

std::map was too slow when accessing properties for individual character,
represented by signed short integers. Use std::vector instead.
Victor Luchitz 13 years ago
parent
commit
9cc171f3ed

+ 7 - 2
Include/Rocket/Core/FontGlyph.h

@@ -28,7 +28,7 @@
 #ifndef ROCKETCOREFONTGLYPH_H
 #ifndef ROCKETCOREFONTGLYPH_H
 #define ROCKETCOREFONTGLYPH_H
 #define ROCKETCOREFONTGLYPH_H
 
 
-#include <map>
+#include <vector>
 
 
 namespace Rocket {
 namespace Rocket {
 namespace Core {
 namespace Core {
@@ -42,6 +42,11 @@ namespace Core {
 class FontGlyph
 class FontGlyph
 {
 {
 public:
 public:
+	FontGlyph() : character(0), dimensions(0,0), bearing(0,0), advance(0), bitmap_data(NULL),
+		bitmap_dimensions(0,0)
+	{
+	}
+
 	/// The unicode code point for this glyph.
 	/// The unicode code point for this glyph.
 	word character;
 	word character;
 
 
@@ -61,7 +66,7 @@ public:
 	Vector2i bitmap_dimensions;
 	Vector2i bitmap_dimensions;
 };
 };
 
 
-typedef std::map< word, FontGlyph > FontGlyphMap;
+typedef std::vector< FontGlyph > FontGlyphList;
 
 
 }
 }
 }
 }

+ 36 - 18
Source/Core/FontFaceHandle.cpp

@@ -60,8 +60,8 @@ FontFaceHandle::FontFaceHandle()
 
 
 FontFaceHandle::~FontFaceHandle()
 FontFaceHandle::~FontFaceHandle()
 {
 {
-	for (FontGlyphMap::iterator i = glyphs.begin(); i != glyphs.end(); ++i)
-		delete[] i->second.bitmap_data;
+	for (FontGlyphList::iterator i = glyphs.begin(); i != glyphs.end(); ++i)
+		delete[] i->bitmap_data;
 
 
 	for (FontLayerMap::iterator i = layers.begin(); i != layers.end(); ++i)
 	for (FontLayerMap::iterator i = layers.begin(); i != layers.end(); ++i)
 		delete i->second;
 		delete i->second;
@@ -87,13 +87,20 @@ bool FontFaceHandle::Initialise(FT_Face ft_face, const String& _charset, int _si
 		return false;
 		return false;
 	}
 	}
 
 
+	// find the maximum character we are interested in
+	max_codepoint = 0;
+	for (size_t i = 0; i < charset.size(); ++i)
+		max_codepoint = Math::Max(max_codepoint, charset[i].max_codepoint);
+
 	// 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());
 	for (size_t i = 0; i < charset.size(); ++i)
 	for (size_t i = 0; i < charset.size(); ++i)
 		BuildGlyphMap(ft_face, charset[i]);
 		BuildGlyphMap(ft_face, charset[i]);
 
 
 	// Generate the metrics for the handle.
 	// Generate the metrics for the handle.
 	GenerateMetrics(ft_face);
 	GenerateMetrics(ft_face);
 
 
+	BuildKerning(ft_face);
 
 
 	// Generate the default layer and layer configuration.
 	// Generate the default layer and layer configuration.
 	base_layer = GenerateLayer(NULL);
 	base_layer = GenerateLayer(NULL);
@@ -135,7 +142,7 @@ int FontFaceHandle::GetBaseline() const
 }
 }
 
 
 // Returns the font's glyphs.
 // Returns the font's glyphs.
-const FontGlyphMap& FontFaceHandle::GetGlyphs() const
+const FontGlyphList& FontFaceHandle::GetGlyphs() const
 {
 {
 	return glyphs;
 	return glyphs;
 }
 }
@@ -149,15 +156,15 @@ int FontFaceHandle::GetStringWidth(const WString& string, word prior_character)
 	{
 	{
 		word character_code = string[i];
 		word character_code = string[i];
 
 
-		FontGlyphMap::const_iterator iterator = glyphs.find(character_code);
-		if (iterator == glyphs.end())
+		if (character_code >= glyphs.size())
 			continue;
 			continue;
+		const FontGlyph &glyph = glyphs[character_code];
 
 
 		// Adjust the cursor for the kerning between this character and the previous one.
 		// Adjust the cursor for the kerning between this character and the previous one.
 		if (prior_character != 0)
 		if (prior_character != 0)
 			width += GetKerning(prior_character, string[i]);
 			width += GetKerning(prior_character, string[i]);
 		// Adjust the cursor for this character's advance.
 		// Adjust the cursor for this character's advance.
-		width += iterator->second.advance;
+		width += glyph.advance;
 
 
 		prior_character = character_code;
 		prior_character = character_code;
 	}
 	}
@@ -283,9 +290,9 @@ int FontFaceHandle::GenerateString(GeometryList& geometry, const WString& string
 
 
 		for (; string_iterator != string_end; string_iterator++)
 		for (; string_iterator != string_end; string_iterator++)
 		{
 		{
-			FontGlyphMap::const_iterator iterator = glyphs.find(*string_iterator);
-			if (iterator == glyphs.end())
+			if (*string_iterator >= glyphs.size())
 				continue;
 				continue;
+			const FontGlyph &glyph = glyphs[*string_iterator];
 
 
 			// Adjust the cursor for the kerning between this character and the previous one.
 			// Adjust the cursor for the kerning between this character and the previous one.
 			if (prior_character != 0)
 			if (prior_character != 0)
@@ -293,7 +300,7 @@ int FontFaceHandle::GenerateString(GeometryList& geometry, const WString& string
 
 
 			layer->GenerateGeometry(&geometry[geometry_index], *string_iterator, Vector2f(position.x + line_width, position.y), layer_colour);
 			layer->GenerateGeometry(&geometry[geometry_index], *string_iterator, Vector2f(position.x + line_width, position.y), layer_colour);
 
 
-			line_width += iterator->second.advance;
+			line_width += glyph.advance;
 			prior_character = *string_iterator;
 			prior_character = *string_iterator;
 		}
 		}
 
 
@@ -354,11 +361,19 @@ void FontFaceHandle::GenerateMetrics(FT_Face ft_face)
 	underline_thickness = Math::Max(underline_thickness, 1.0f);
 	underline_thickness = Math::Max(underline_thickness, 1.0f);
 
 
 	average_advance = 0;
 	average_advance = 0;
-	for (FontGlyphMap::iterator i = glyphs.begin(); i != glyphs.end(); ++i)
-		average_advance += i->second.advance;
+	unsigned int num_visible_glyphs = 0;
+	for (FontGlyphList::iterator i = glyphs.begin(); i != glyphs.end(); ++i)
+	{
+		if (i->advance)
+		{
+			average_advance += i->advance;
+			num_visible_glyphs++;
+		}
+	}
 
 
 	// Bring the total advance down to the average advance, but scaled up 10%, just to be on the safe side.
 	// Bring the total advance down to the average advance, but scaled up 10%, just to be on the safe side.
-	average_advance = Math::RealToInteger((float) average_advance / (glyphs.size() * 0.9f));
+	if (num_visible_glyphs)
+		average_advance = Math::RealToInteger((float) average_advance / (num_visible_glyphs * 0.9f));
 
 
 	// Determine the x-height of this font face.
 	// Determine the x-height of this font face.
 	word x = (word) 'x';
 	word x = (word) 'x';
@@ -486,11 +501,13 @@ void FontFaceHandle::BuildKerning(FT_Face ft_face)
 	// Compile the kerning information for this character if the font includes it.
 	// Compile the kerning information for this character if the font includes it.
 	if (FT_HAS_KERNING(ft_face))
 	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 (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)
 			for (word rhs = (word) (Math::Max< unsigned int >(charset[i].min_codepoint, 32)); rhs <= charset[i].max_codepoint; ++rhs)
 			{
 			{
-				GlyphKerningMap& glyph_kerning = kerning.insert(FontKerningMap::value_type(rhs, GlyphKerningMap())).first->second;
+				GlyphKerningList glyph_kerning(max_codepoint+1, 0);
 
 
 				for (size_t j = 0; j < charset.size(); ++j)
 				for (size_t j = 0; j < charset.size(); ++j)
 				{
 				{
@@ -504,6 +521,8 @@ void FontFaceHandle::BuildKerning(FT_Face ft_face)
 							glyph_kerning[lhs] = kerning;
 							glyph_kerning[lhs] = kerning;
 					}
 					}
 				}
 				}
+
+				kerning[rhs] = glyph_kerning;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -511,15 +530,14 @@ void FontFaceHandle::BuildKerning(FT_Face ft_face)
 
 
 int FontFaceHandle::GetKerning(word lhs, word rhs) const
 int FontFaceHandle::GetKerning(word lhs, word rhs) const
 {
 {
-	FontKerningMap::const_iterator rhs_iterator = kerning.find(rhs);
-	if (rhs_iterator == kerning.end())
+	if (rhs >= kerning.size())
 		return 0;
 		return 0;
 
 
-	GlyphKerningMap::const_iterator lhs_iterator = rhs_iterator->second.find(lhs);
-	if (lhs_iterator == rhs_iterator->second.end())
+	const GlyphKerningList &kerning_map = kerning[rhs];
+	if (lhs >= kerning_map.size())
 		return 0;
 		return 0;
 
 
-	return lhs_iterator->second;
+	return kerning_map[lhs];
 }
 }
 
 
 // Generates (or shares) a layer derived from a font effect.
 // Generates (or shares) a layer derived from a font effect.

+ 6 - 5
Source/Core/FontFaceHandle.h

@@ -81,7 +81,7 @@ public:
 
 
 	/// Returns the font's glyphs.
 	/// Returns the font's glyphs.
 	/// @return The font's glyphs.
 	/// @return The font's glyphs.
-	const FontGlyphMap& GetGlyphs() const;
+	const FontGlyphList& GetGlyphs() const;
 
 
 	/// Returns the width a string will take up if rendered with this handle.
 	/// Returns the width a string will take up if rendered with this handle.
 	/// @param[in] string The string to measure.
 	/// @param[in] string The string to measure.
@@ -138,11 +138,11 @@ private:
 	// Generates (or shares) a layer derived from a font effect.
 	// Generates (or shares) a layer derived from a font effect.
 	FontFaceLayer* GenerateLayer(FontEffect* font_effect);
 	FontFaceLayer* GenerateLayer(FontEffect* font_effect);
 
 
-	typedef std::map< word, int > GlyphKerningMap;
-	typedef std::map< word, GlyphKerningMap > FontKerningMap;
+	typedef std::vector< int > GlyphKerningList;
+	typedef std::vector< GlyphKerningList > FontKerningList;
 
 
-	FontGlyphMap glyphs;
-	FontKerningMap kerning;
+	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;
@@ -173,6 +173,7 @@ private:
 
 
 	String raw_charset;
 	String raw_charset;
 	UnicodeRangeList charset;
 	UnicodeRangeList charset;
+	unsigned int max_codepoint;
 };
 };
 
 
 }
 }

+ 13 - 13
Source/Core/FontFaceLayer.cpp

@@ -56,7 +56,7 @@ bool FontFaceLayer::Initialise(const FontFaceHandle* _handle, FontEffect* _effec
 		colour = effect->GetColour();
 		colour = effect->GetColour();
 	}
 	}
 
 
-	const FontGlyphMap& glyphs = handle->GetGlyphs();
+	const FontGlyphList& glyphs = handle->GetGlyphs();
 
 
 	// Clone the geometry and textures from the clone layer.
 	// Clone the geometry and textures from the clone layer.
 	if (clone != NULL)
 	if (clone != NULL)
@@ -72,15 +72,14 @@ bool FontFaceLayer::Initialise(const FontFaceHandle* _handle, FontEffect* _effec
 		if (!deep_clone &&
 		if (!deep_clone &&
 			effect != NULL)
 			effect != NULL)
 		{
 		{
-			for (FontGlyphMap::const_iterator i = glyphs.begin(); i != glyphs.end(); ++i)
+			for (FontGlyphList::const_iterator i = glyphs.begin(); i != glyphs.end(); ++i)
 			{
 			{
-				const FontGlyph& glyph = i->second;
+				const FontGlyph& glyph = *i;
 
 
-				CharacterMap::iterator character_iterator = characters.find(i->first);
-				if (character_iterator == characters.end())
+				if (glyph.character >= characters.size())
 					continue;
 					continue;
 
 
-				Character& character = character_iterator->second;
+				Character& character = characters[glyph.character];
 
 
 				Vector2i glyph_origin(Math::RealToInteger(character.origin.x), Math::RealToInteger(character.origin.y));
 				Vector2i glyph_origin(Math::RealToInteger(character.origin.x), Math::RealToInteger(character.origin.y));
 				Vector2i glyph_dimensions(Math::RealToInteger(character.dimensions.x), Math::RealToInteger(character.dimensions.y));
 				Vector2i glyph_dimensions(Math::RealToInteger(character.dimensions.x), Math::RealToInteger(character.dimensions.y));
@@ -91,16 +90,17 @@ bool FontFaceLayer::Initialise(const FontFaceHandle* _handle, FontEffect* _effec
 					character.origin.y = (float) glyph_origin.y;
 					character.origin.y = (float) glyph_origin.y;
 				}
 				}
 				else
 				else
-					characters.erase(character_iterator);
+					character.texture_index = -1;
 			}
 			}
 		}
 		}
 	}
 	}
 	else
 	else
 	{
 	{
 		// Initialise the texture layout for the glyphs.
 		// Initialise the texture layout for the glyphs.
-		for (FontGlyphMap::const_iterator i = glyphs.begin(); i != glyphs.end(); ++i)
+		characters.resize(glyphs.size(), Character());
+		for (FontGlyphList::const_iterator i = glyphs.begin(); i != glyphs.end(); ++i)
 		{
 		{
-			const FontGlyph& glyph = i->second;
+			const FontGlyph& glyph = *i;
 
 
 			Vector2i glyph_origin(0, 0);
 			Vector2i glyph_origin(0, 0);
 			Vector2i glyph_dimensions = glyph.bitmap_dimensions;
 			Vector2i glyph_dimensions = glyph.bitmap_dimensions;
@@ -115,10 +115,10 @@ bool FontFaceLayer::Initialise(const FontFaceHandle* _handle, FontEffect* _effec
 			Character character;
 			Character character;
 			character.origin = Vector2f((float) (glyph_origin.x + glyph.bearing.x), (float) (glyph_origin.y - glyph.bearing.y));
 			character.origin = Vector2f((float) (glyph_origin.x + glyph.bearing.x), (float) (glyph_origin.y - glyph.bearing.y));
 			character.dimensions = Vector2f((float) glyph_dimensions.x - glyph_origin.x, (float) glyph_dimensions.y - glyph_origin.y);
 			character.dimensions = Vector2f((float) glyph_dimensions.x - glyph_origin.x, (float) glyph_dimensions.y - glyph_origin.y);
-			characters[i->first] = character;
+			characters[glyph.character] = character;
 
 
 			// Add the character's dimensions into the texture layout engine.
 			// Add the character's dimensions into the texture layout engine.
-			texture_layout.AddRectangle(i->first, glyph_dimensions - glyph_origin);
+			texture_layout.AddRectangle(glyph.character, glyph_dimensions - glyph_origin);
 		}
 		}
 
 
 		// Generate the texture layout; this will position the glyph rectangles efficiently and
 		// Generate the texture layout; this will position the glyph rectangles efficiently and
@@ -168,7 +168,7 @@ bool FontFaceLayer::GenerateTexture(const byte*& texture_data, Vector2i& texture
 		texture_id > texture_layout.GetNumTextures())
 		texture_id > texture_layout.GetNumTextures())
 		return false;
 		return false;
 
 
-	const FontGlyphMap& glyphs = handle->GetGlyphs();
+	const FontGlyphList& glyphs = handle->GetGlyphs();
 
 
 	// Generate the texture data.
 	// Generate the texture data.
 	texture_data = texture_layout.GetTexture(texture_id).AllocateTexture();
 	texture_data = texture_layout.GetTexture(texture_id).AllocateTexture();
@@ -182,7 +182,7 @@ bool FontFaceLayer::GenerateTexture(const byte*& texture_data, Vector2i& texture
 		if (character.texture_index != texture_id)
 		if (character.texture_index != texture_id)
 			continue;
 			continue;
 
 
-		const FontGlyph& glyph = glyphs.find((word) rectangle.GetId())->second;
+		const FontGlyph& glyph = glyphs[rectangle.GetId()];
 
 
 		if (effect == NULL)
 		if (effect == NULL)
 		{
 		{

+ 8 - 5
Source/Core/FontFaceLayer.h

@@ -75,11 +75,12 @@ public:
 	/// @param[in] colour The colour of the string.
 	/// @param[in] colour The colour of the string.
 	inline void GenerateGeometry(Geometry* geometry, const word character_code, const Vector2f& position, const Colourb& colour) const
 	inline void GenerateGeometry(Geometry* geometry, const word character_code, const Vector2f& position, const Colourb& colour) const
 	{
 	{
-		CharacterMap::const_iterator iterator = characters.find(character_code);
-		if (iterator == characters.end())
+		if (character_code >= characters.size())
 			return;
 			return;
 
 
-		const Character& character = (*iterator).second;
+		const Character& character = characters[character_code];
+		if (character.texture_index < 0)
+			return;
 
 
 		// Generate the geometry for the character.
 		// Generate the geometry for the character.
 		std::vector< Vertex >& character_vertices = geometry[character.texture_index].GetVertices();
 		std::vector< Vertex >& character_vertices = geometry[character.texture_index].GetVertices();
@@ -109,6 +110,8 @@ public:
 private:
 private:
 	struct Character
 	struct Character
 	{
 	{
+		Character() : texture_index(-1) { }
+
 		// The offset, in pixels, of the baseline from the start of this character's geometry.
 		// The offset, in pixels, of the baseline from the start of this character's geometry.
 		Vector2f origin;
 		Vector2f origin;
 		// The width and height, in pixels, of this character's geometry.
 		// The width and height, in pixels, of this character's geometry.
@@ -120,7 +123,7 @@ private:
 		int texture_index;
 		int texture_index;
 	};
 	};
 
 
-	typedef std::map< word, Character > CharacterMap;
+	typedef std::vector< Character > CharacterList;
 	typedef std::vector< Texture > TextureList;
 	typedef std::vector< Texture > TextureList;
 
 
 	const FontFaceHandle* handle;
 	const FontFaceHandle* handle;
@@ -128,7 +131,7 @@ private:
 
 
 	TextureLayout texture_layout;
 	TextureLayout texture_layout;
 
 
-	CharacterMap characters;
+	CharacterList characters;
 	TextureList textures;
 	TextureList textures;
 	Colourb colour;
 	Colourb colour;
 };
 };