Browse Source

Added basic kerning support for TrueType and BMFont fonts (resolves issue #1055.)

Alex Szpakowski 10 years ago
parent
commit
bf432aff7c

+ 18 - 2
src/modules/font/BMFontRasterizer.cpp

@@ -59,7 +59,7 @@ public:
 private:
 private:
 
 
 	std::string tag;
 	std::string tag;
-	std::map<std::string, std::string> attributes;
+	std::unordered_map<std::string, std::string> attributes;
 
 
 };
 };
 
 
@@ -226,7 +226,12 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
 		}
 		}
 		else if (tag == "kerning")
 		else if (tag == "kerning")
 		{
 		{
-			// TODO
+			uint32 firstid  = (uint32) cline.getAttributeInt("first");
+			uint32 secondid = (uint32) cline.getAttributeInt("second");
+
+			uint64 packedids = ((uint64) firstid << 32) | (uint64) secondid;
+
+			kerning[packedids] = cline.getAttributeInt("amount");
 		}
 		}
 	}
 	}
 
 
@@ -309,6 +314,17 @@ bool BMFontRasterizer::hasGlyph(uint32 glyph) const
 	return characters.find(glyph) != characters.end();
 	return characters.find(glyph) != characters.end();
 }
 }
 
 
+float BMFontRasterizer::getKerning(uint32 leftglyph, uint32 rightglyph) const
+{
+	uint64 packedglyphs = ((uint64) leftglyph << 32) | (uint64) rightglyph;
+
+	auto it = kerning.find(packedglyphs);
+	if (it != kerning.end())
+		return it->second;
+
+	return 0.0f;
+}
+
 bool BMFontRasterizer::accepts(love::filesystem::FileData *fontdef)
 bool BMFontRasterizer::accepts(love::filesystem::FileData *fontdef)
 {
 {
 	const char *data = (const char *) fontdef->getData();
 	const char *data = (const char *) fontdef->getData();

+ 7 - 3
src/modules/font/BMFontRasterizer.h

@@ -27,7 +27,7 @@
 #include "image/ImageData.h"
 #include "image/ImageData.h"
 
 
 // C++
 // C++
-#include <map>
+#include <unordered_map>
 #include <vector>
 #include <vector>
 
 
 namespace love
 namespace love
@@ -50,6 +50,7 @@ public:
 	GlyphData *getGlyphData(uint32 glyph) const override;
 	GlyphData *getGlyphData(uint32 glyph) const override;
 	int getGlyphCount() const override;
 	int getGlyphCount() const override;
 	bool hasGlyph(uint32 glyph) const override;
 	bool hasGlyph(uint32 glyph) const override;
+	float getKerning(uint32 leftglyph, uint32 rightglyph) const;
 
 
 	static bool accepts(love::filesystem::FileData *fontdef);
 	static bool accepts(love::filesystem::FileData *fontdef);
 
 
@@ -68,10 +69,13 @@ private:
 	std::string fontFolder;
 	std::string fontFolder;
 
 
 	// Image pages, indexed by their page id.
 	// Image pages, indexed by their page id.
-	std::map<int, StrongRef<image::ImageData>> images;
+	std::unordered_map<int, StrongRef<image::ImageData>> images;
 
 
 	// Glyph characters, indexed by their glyph id.
 	// Glyph characters, indexed by their glyph id.
-	std::map<uint32, BMFontCharacter> characters;
+	std::unordered_map<uint32, BMFontCharacter> characters;
+
+	// Kerning information, indexed by two (packed) characters.
+	std::unordered_map<uint64, int> kerning;
 
 
 	int fontSize;
 	int fontSize;
 	bool unicode;
 	bool unicode;

+ 5 - 0
src/modules/font/Rasterizer.cpp

@@ -95,5 +95,10 @@ bool Rasterizer::hasGlyphs(const std::string &text) const
 	return true;
 	return true;
 }
 }
 
 
+float Rasterizer::getKerning(uint32 /*leftglyph*/, uint32 /*rightglyph*/) const
+{
+	return 0.0f;
+}
+
 } // font
 } // font
 } // love
 } // love

+ 5 - 0
src/modules/font/Rasterizer.h

@@ -105,6 +105,11 @@ public:
 	 **/
 	 **/
 	virtual bool hasGlyphs(const std::string &text) const;
 	virtual bool hasGlyphs(const std::string &text) const;
 
 
+	/**
+	 * Gets the amount of horizontal kerning between two glyphs.
+	 **/
+	virtual float getKerning(uint32 leftglyph, uint32 rightglyph) const;
+
 protected:
 protected:
 
 
 	FontMetrics metrics;
 	FontMetrics metrics;

+ 7 - 0
src/modules/font/freetype/TrueTypeRasterizer.cpp

@@ -165,6 +165,13 @@ bool TrueTypeRasterizer::hasGlyph(uint32 glyph) const
 	return FT_Get_Char_Index(face, glyph) != 0;
 	return FT_Get_Char_Index(face, glyph) != 0;
 }
 }
 
 
+float TrueTypeRasterizer::getKerning(uint32 leftglyph, uint32 rightglyph) const
+{
+	FT_Vector kerning = {};
+	FT_Get_Kerning(face, leftglyph, rightglyph, FT_KERNING_DEFAULT, &kerning);
+	return float(kerning.x >> 6);
+}
+
 bool TrueTypeRasterizer::accepts(FT_Library library, love::Data *data)
 bool TrueTypeRasterizer::accepts(FT_Library library, love::Data *data)
 {
 {
 	const FT_Byte *fbase = (const FT_Byte *) data->getData();
 	const FT_Byte *fbase = (const FT_Byte *) data->getData();

+ 1 - 0
src/modules/font/freetype/TrueTypeRasterizer.h

@@ -52,6 +52,7 @@ public:
 	virtual GlyphData *getGlyphData(uint32 glyph) const;
 	virtual GlyphData *getGlyphData(uint32 glyph) const;
 	virtual int getGlyphCount() const;
 	virtual int getGlyphCount() const;
 	virtual bool hasGlyph(uint32 glyph) const;
 	virtual bool hasGlyph(uint32 glyph) const;
+	virtual float getKerning(uint32 leftglyph, uint32 rightglyph) const;
 
 
 	static bool accepts(FT_Library library, love::Data *data);
 	static bool accepts(FT_Library library, love::Data *data);
 
 

+ 33 - 2
src/modules/graphics/opengl/Font.cpp

@@ -316,6 +316,29 @@ const Font::Glyph &Font::findGlyph(uint32 glyph)
 	return addGlyph(glyph);
 	return addGlyph(glyph);
 }
 }
 
 
+float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
+{
+	uint64 packedglyphs = ((uint64) leftglyph << 32) | (uint64) rightglyph;
+
+	const auto it = kerning.find(packedglyphs);
+	if (it != kerning.end())
+		return it->second;
+
+	float k = rasterizers[0]->getKerning(leftglyph, rightglyph);
+
+	for (const auto &r : rasterizers)
+	{
+		if (r->hasGlyph(leftglyph) && r->hasGlyph(rightglyph))
+		{
+			k = r->getKerning(leftglyph, rightglyph);
+			break;
+		}
+	}
+
+	kerning[packedglyphs] = k;
+	return k;
+}
+
 float Font::getHeight() const
 float Font::getHeight() const
 {
 {
 	return (float) height;
 	return (float) height;
@@ -337,6 +360,8 @@ std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, s
 	size_t vertstartsize = vertices.size();
 	size_t vertstartsize = vertices.size();
 	vertices.reserve(vertstartsize + text.length() * 4);
 	vertices.reserve(vertstartsize + text.length() * 4);
 
 
+	uint32 prevglyph = 0;
+
 	try
 	try
 	{
 	{
 		utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
 		utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
@@ -370,17 +395,21 @@ std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, s
 				dy = offset.y;
 				dy = offset.y;
 				drawcommands.clear();
 				drawcommands.clear();
 				vertices.resize(vertstartsize);
 				vertices.resize(vertstartsize);
+				prevglyph = 0;
 				continue;
 				continue;
 			}
 			}
 
 
+			// Add kerning to the current horizontal offset.
+			dx += getKerning(prevglyph, g);
+
 			if (glyph.texture != 0)
 			if (glyph.texture != 0)
 			{
 			{
 				// Copy the vertices and set their proper relative positions.
 				// Copy the vertices and set their proper relative positions.
 				for (int j = 0; j < 4; j++)
 				for (int j = 0; j < 4; j++)
 				{
 				{
 					vertices.push_back(glyph.vertices[j]);
 					vertices.push_back(glyph.vertices[j]);
-					vertices.back().x += dx;
-					vertices.back().y += dy + lineheight;
+					vertices.back().x += dx /*+ kerning*/;
+					vertices.back().y += dy /*+ kerning.y*/ + lineheight;
 				}
 				}
 
 
 				// Check if glyph texture has changed since the last iteration.
 				// Check if glyph texture has changed since the last iteration.
@@ -403,6 +432,8 @@ std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, s
 			// Account for extra spacing given to space characters.
 			// Account for extra spacing given to space characters.
 			if (g == ' ' && extra_spacing != 0.0f)
 			if (g == ' ' && extra_spacing != 0.0f)
 				dx = floorf(dx + extra_spacing);
 				dx = floorf(dx + extra_spacing);
+
+			prevglyph = g;
 		}
 		}
 	}
 	}
 	catch (utf8::exception &e)
 	catch (utf8::exception &e)

+ 4 - 0
src/modules/graphics/opengl/Font.h

@@ -211,6 +211,7 @@ private:
 	love::font::GlyphData *getRasterizerGlyphData(uint32 glyph);
 	love::font::GlyphData *getRasterizerGlyphData(uint32 glyph);
 	const Glyph &addGlyph(uint32 glyph);
 	const Glyph &addGlyph(uint32 glyph);
 	const Glyph &findGlyph(uint32 glyph);
 	const Glyph &findGlyph(uint32 glyph);
+	float getKerning(uint32 leftglyph, uint32 rightglyph);
 	void printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
 	void printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
 
 
 	std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
 	std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
@@ -227,6 +228,9 @@ private:
 	// maps glyphs to glyph texture information
 	// maps glyphs to glyph texture information
 	std::unordered_map<uint32, Glyph> glyphs;
 	std::unordered_map<uint32, Glyph> glyphs;
 
 
+	// map of left/right glyph pairs to horizontal kerning.
+	std::unordered_map<uint64, float> kerning;
+
 	FontType type;
 	FontType type;
 	Texture::Filter filter;
 	Texture::Filter filter;