Sfoglia il codice sorgente

New font engine is implemented and appears to work perfectly. Bugs may be discovered and the internals can certainly be optimized and improved (at some point, perhaps, a less naive packing algorithm would be nice) but for now, it's good to go.

--HG--
branch : newfont
Bill Meltsner 14 anni fa
parent
commit
cbff71d089

+ 137 - 32
src/modules/graphics/opengl/Font.cpp

@@ -20,10 +20,13 @@
 
 #include "Font.h"
 #include <font/GlyphData.h>
+#include "Quad.h"
 
 #include <common/math.h>
 #include <math.h>
 
+#include <algorithm> // for max
+
 namespace love
 {
 namespace graphics
@@ -32,15 +35,101 @@ namespace opengl
 {
 
 	Font::Font(love::font::Rasterizer * r, const Image::Filter& filter)
-	: rasterizer(r), height(r->getHeight()), lineHeight(1), mSpacing(1)
+	: rasterizer(r), height(r->getHeight()), lineHeight(1), mSpacing(1), filter(filter)
 	{
-		love::font::GlyphData * gd = r->getGlyphData(0);
+		love::font::GlyphData * gd = r->getGlyphData(32);
 		type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA ? FONT_TRUETYPE : FONT_IMAGE);
 		delete gd;
+		createTexture();
 	}
 
 	Font::~Font()
 	{
+		unloadVolatile();
+	}
+	
+	void Font::createTexture()
+	{
+		texture_x = texture_y = rowHeight = 0;
+		GLuint t;
+		glGenTextures(1, &t);
+		textures.push_back(t);
+		glBindTexture(GL_TEXTURE_2D, t);
+		
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+						(filter.mag == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+						(filter.min == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST);
+		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
+		glTexImage2D(GL_TEXTURE_2D, 
+					 0, 
+					 GL_RGBA, 
+					 (GLsizei)TEXTURE_WIDTH, 
+					 (GLsizei)TEXTURE_HEIGHT, 
+					 0, 
+					 format, 
+					 GL_UNSIGNED_BYTE, 
+					 NULL);
+	}
+	
+	Font::Glyph * Font::addGlyph(int glyph)
+	{
+		Glyph * g = new Glyph;
+		g->list = glGenLists(1);
+		if (g->list == 0) { // opengl failed to generate the list
+			delete g;
+			return NULL;
+		}
+		love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
+		g->spacing = gd->getAdvance();
+		int w = gd->getWidth();
+		int h = gd->getHeight();
+		if (texture_x + w > TEXTURE_WIDTH) { // out of space - new row!
+			texture_x = 0;
+			texture_y += rowHeight;
+			rowHeight = 0;
+		}
+		if (texture_y + h > TEXTURE_HEIGHT) { // totally out of space - new texture!
+			createTexture();
+		}
+		GLuint t = textures.back();
+		glBindTexture(GL_TEXTURE_2D, t);
+		glTexSubImage2D(GL_TEXTURE_2D, 0, texture_x, texture_y, w, h, (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA), GL_UNSIGNED_BYTE, gd->getData());
+		
+		Quad::Viewport v;
+		v.x = texture_x;
+		v.y = texture_y;
+		v.w = w;
+		v.h = h;
+		Quad * q = new Quad(v, TEXTURE_WIDTH, TEXTURE_HEIGHT);
+		const vertex * verts = q->getVertices();
+		
+		glEnableClientState(GL_VERTEX_ARRAY);
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].x);
+		glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].s);
+		
+		glNewList(g->list, GL_COMPILE);
+		glBindTexture(GL_TEXTURE_2D, t);
+		glPushMatrix();
+		glTranslatef(static_cast<float>(gd->getBearingX()), static_cast<float>(-gd->getBearingY()), 0.0f);
+		glDrawArrays(GL_QUADS, 0, 4);
+		glPopMatrix();
+		glEndList();
+		
+		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		glDisableClientState(GL_VERTEX_ARRAY);
+		
+		delete q;
+		delete gd;
+		
+		texture_x += w;
+		rowHeight = std::max(rowHeight, h);
+		
+		glyphs[glyph] = g;
+		return g;
 	}
 
 	float Font::getHeight() const
@@ -48,9 +137,9 @@ namespace opengl
 		return static_cast<float>(height);
 	}
 
-	void Font::print(std::string text, float x, float y, float angle, float sx, float sy) const
+	void Font::print(std::string text, float x, float y, float angle, float sx, float sy)
 	{
-		/*float dx = 0.0f; // spacing counter for newline handling
+		float dx = 0.0f; // spacing counter for newline handling
 		glPushMatrix();
 
 		glTranslatef(ceil(x), ceil(y), 0.0f);
@@ -63,57 +152,66 @@ namespace opengl
 				dx = 0.0f;
 				continue;
 			}
-			if (!glyphs[g]) g = 32; // space
+			Glyph * glyph = glyphs[g];
+			if (!glyph) glyph = addGlyph(g);
 			glPushMatrix();
 			// 1.25 is magic line height for true type fonts
 			if (type == FONT_TRUETYPE) glTranslatef(0, floor(getHeight() / 1.25f + 0.5f), 0);
-			glyphs[g]->draw(0, 0, 0, 1, 1, 0, 0);
+			glCallList(glyph->list);
 			glPopMatrix();
-			glTranslatef(static_cast<GLfloat>(spacing[g]), 0, 0);
-			dx += spacing[g];
+			glTranslatef(static_cast<GLfloat>(glyph->spacing), 0, 0);
+			dx += glyph->spacing;
 		}
-		glPopMatrix();*/
+		glPopMatrix();
 	}
 
-	void Font::print(char character, float x, float y) const
+	void Font::print(char character, float x, float y)
 	{
-		/*if (!glyphs[(int)character]) character = ' ';
+		Glyph * glyph = glyphs[character];
+		if (!glyph) glyph = addGlyph(character);
 		glPushMatrix();
 		glTranslatef(x, floor(y+getHeight() + 0.5f), 0.0f);
-		glCallList(list+character);
-		glPopMatrix();*/
+		glCallList(glyph->list);
+		glPopMatrix();
 	}
 
-	int Font::getWidth(const std::string & line) const
+	int Font::getWidth(const std::string & line)
 	{
 		if(line.size() == 0) return 0;
 		int temp = 0;
+		
+		Glyph * g;
 
 		for(unsigned int i = 0; i < line.size(); i++)
 		{
-			temp += static_cast<int>((spacing[(int)line[i]] * mSpacing));
+			g = glyphs[line[i]];
+			if (!g) g = addGlyph(line[i]);
+			temp += static_cast<int>(g->spacing * mSpacing);
 		}
 
 		return temp;
 	}
 
-	int Font::getWidth(const char * line) const
+	int Font::getWidth(const char * line)
 	{
 		return this->getWidth(std::string(line));
 	}
 
-	int Font::getWidth(const char character) const
+	int Font::getWidth(const char character)
 	{
-		return spacing[(int)character];
+		Glyph * g = glyphs[character];
+		if (!g) g = addGlyph(character);
+		return g->spacing;
 	}
 
-	int Font::getWrap(const std::string & line, float wrap, int * lines) const
+	int Font::getWrap(const std::string & line, float wrap, int * lines)
 	{
 		if(line.size() == 0) return 0;
 		int maxw = 0;
 		int linen = 1;
 		int temp = 0;
 		std::string text;
+		Glyph * g;
 
 		for(unsigned int i = 0; i < line.size(); i++)
 		{
@@ -127,7 +225,9 @@ namespace opengl
 				temp = getWidth(text);
 				linen++;
 			}
-			temp += static_cast<int>((spacing[(int)line[i]] * mSpacing));
+			g = glyphs[line[i]];
+			if (!g) g = addGlyph(line[i]);
+			temp += static_cast<int>(g->spacing * mSpacing);
 			text += line[i];
 		}
 
@@ -137,7 +237,7 @@ namespace opengl
 		return maxw;
 	}
 
-	int Font::getWrap(const char * line, float wrap, int * lines) const
+	int Font::getWrap(const char * line, float wrap, int * lines)
 	{
 		return getWrap(std::string(line), wrap, lines);
 	}
@@ -164,21 +264,26 @@ namespace opengl
 
 	bool Font::loadVolatile()
 	{
-		/*// reload all glyphs
-		for(unsigned int i = 0; i < MAX_CHARS; i++)
-		{
-			glyphs[i]->load();
-			glNewList(list + i, GL_COMPILE);
-			glyphs[i]->draw(0, 0, 0, 1, 1, 0, 0);
-			glEndList();
-		}
-		return true;*/
+		createTexture();
 	}
 
 	void Font::unloadVolatile()
 	{
-		/*// delete the glyphs
-		glDeleteLists(list, MAX_CHARS);*/
+		// nuke everything from orbit
+		std::map<int, Glyph *>::iterator it = glyphs.begin();
+		Glyph * g;
+		while (it != glyphs.end()) {
+			g = it->second;
+			glDeleteLists(g->list, 1);
+			delete g;
+			glyphs.erase(it++);
+		}
+		std::vector<GLuint>::iterator iter = textures.begin();
+		while (iter != textures.end()) {
+			glDeleteTextures(1, (GLuint*)&*iter);
+			iter++;
+		}
+		textures.clear();
 	}
 
 } // opengl

+ 27 - 8
src/modules/graphics/opengl/Font.h

@@ -22,7 +22,9 @@
 #define LOVE_GRAPHICS_OPENGL_FONT_H
 
 // STD
+#include <map>
 #include <string>
+#include <vector>
 
 // LOVE
 #include <common/Object.h>
@@ -48,13 +50,30 @@ namespace opengl
 			FONT_UNKNOWN
 		};
 		
+		struct Glyph
+		{
+			GLuint list;
+			int spacing;
+		};
+		
 		love::font::Rasterizer * rasterizer;
 
 		int height;
 		float lineHeight;
 		float mSpacing; // modifies the spacing by multiplying it with this value
-		GLuint list; // the list of glyphs, for quicker drawing
+		std::vector<GLuint> textures; // vector of packed textures
+		std::map<int, Glyph *> glyphs; // maps glyphs to display lists
 		FontType type;
+		Image::Filter filter;
+		
+		static const int TEXTURE_WIDTH = 512;
+		static const int TEXTURE_HEIGHT = 512;
+		
+		int texture_x, texture_y;
+		int rowHeight;
+		
+		void createTexture();
+		Glyph * addGlyph(int glyph);
 
 	public:
 
@@ -75,7 +94,7 @@ namespace opengl
 		* @param y The y-coordinate.
 		* @param angle The amount of rotation.
 		**/
-		void print(std::string text, float x, float y, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f) const;
+		void print(std::string text, float x, float y, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f);
 
 		/**
 		* Prints the character at the designated position.
@@ -84,7 +103,7 @@ namespace opengl
 		* @param x The x-coordinate.
 		* @param y The y-coordinate.
 		**/
-		void print(char character, float x, float y) const;
+		void print(char character, float x, float y);
 
 		/**
 		* Returns the height of the font.
@@ -96,15 +115,15 @@ namespace opengl
 		*
 		* @param line A line of text.
 		**/
-		int getWidth(const std::string & line) const;
-		int getWidth(const char * line) const;
+		int getWidth(const std::string & line);
+		int getWidth(const char * line);
 
 		/**
 		* Returns the width of the passed character.
 		*
 		* @param character A character.
 		**/
-		int getWidth(const char character) const;
+		int getWidth(const char character);
 
 		/**
 		 * Returns the maximal width of a wrapped string
@@ -114,8 +133,8 @@ namespace opengl
 		 * @param wrap The number of pixels to wrap at
 		 * @param lines Optional output of the number of lines needed
 		 **/
-		int getWrap(const std::string & line, float wrap, int *lines = 0) const;
-		int getWrap(const char * line, float wrap, int *lines = 0) const;
+		int getWrap(const std::string & line, float wrap, int *lines = 0);
+		int getWrap(const char * line, float wrap, int *lines = 0);
 
 		/**
 		* Sets the line height (which should be a number to multiply the font size by,

+ 3 - 0
src/modules/graphics/opengl/Graphics.cpp

@@ -281,6 +281,9 @@ namespace opengl
 		// Reset modelview matrix
 		glMatrixMode(GL_MODELVIEW);
 		glLoadIdentity();
+		
+		// Set pixel row alignment
+		glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
 
 		// Set the new display mode as the current display mode.
 		currentMode.width = width;

+ 0 - 8
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -255,10 +255,6 @@ namespace opengl
 			luax_convobj(L, idxs, 2, "font", "newRasterizer");
 		}
 
-		// Convert to FontData, if necessary.
-		if(luax_istype(L, 1, FONT_RASTERIZER_T))
-			luax_convobj(L, 1, "font", "newFontData");
-
 		love::font::Rasterizer * rasterizer = luax_checktype<love::font::Rasterizer>(L, 1, "Rasterizer", FONT_RASTERIZER_T);
 
 		// Create the font.
@@ -450,10 +446,6 @@ namespace opengl
 				luax_convobj(L, idxs, 2, "font", "newRasterizer");
 			}
 
-			// Convert to FontData, if necessary.
-			if(luax_istype(L, 1, FONT_RASTERIZER_T))
-				luax_convobj(L, 1, "font", "newFontData");
-
 			love::font::Rasterizer * rasterizer = luax_checktype<love::font::Rasterizer>(L, 1, "Rasterizer", FONT_RASTERIZER_T);
 
 			// Create the font.