From cbff71d08995e7fda994d5bd8fe16f8c220daf15 Mon Sep 17 00:00:00 2001 From: Bill Meltsner Date: Sun, 6 Mar 2011 05:02:35 -0500 Subject: [PATCH] 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 --- src/modules/graphics/opengl/Font.cpp | 169 ++++++++++++++---- src/modules/graphics/opengl/Font.h | 35 +++- src/modules/graphics/opengl/Graphics.cpp | 3 + src/modules/graphics/opengl/wrap_Graphics.cpp | 8 - 4 files changed, 167 insertions(+), 48 deletions(-) diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index 43dcad8b1..40ef5b392 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -20,10 +20,13 @@ #include "Font.h" #include +#include "Quad.h" #include #include +#include // 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(gd->getBearingX()), static_cast(-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(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(spacing[g]), 0, 0); - dx += spacing[g]; + glTranslatef(static_cast(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((spacing[(int)line[i]] * mSpacing)); + g = glyphs[line[i]]; + if (!g) g = addGlyph(line[i]); + temp += static_cast(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((spacing[(int)line[i]] * mSpacing)); + g = glyphs[line[i]]; + if (!g) g = addGlyph(line[i]); + temp += static_cast(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::iterator it = glyphs.begin(); + Glyph * g; + while (it != glyphs.end()) { + g = it->second; + glDeleteLists(g->list, 1); + delete g; + glyphs.erase(it++); + } + std::vector::iterator iter = textures.begin(); + while (iter != textures.end()) { + glDeleteTextures(1, (GLuint*)&*iter); + iter++; + } + textures.clear(); } } // opengl diff --git a/src/modules/graphics/opengl/Font.h b/src/modules/graphics/opengl/Font.h index e1c4f3902..fe597e26a 100644 --- a/src/modules/graphics/opengl/Font.h +++ b/src/modules/graphics/opengl/Font.h @@ -22,7 +22,9 @@ #define LOVE_GRAPHICS_OPENGL_FONT_H // STD +#include #include +#include // LOVE #include @@ -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 textures; // vector of packed textures + std::map 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, diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index e592ad5a7..0efcb9c1e 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/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; diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 50e1a3780..31326bda9 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/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(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(L, 1, "Rasterizer", FONT_RASTERIZER_T); // Create the font.