mirror of
https://github.com/love2d/love.git
synced 2026-08-18 19:54:22 +02:00
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
This commit is contained in:
@@ -20,10 +20,13 @@
|
|||||||
|
|
||||||
#include "Font.h"
|
#include "Font.h"
|
||||||
#include <font/GlyphData.h>
|
#include <font/GlyphData.h>
|
||||||
|
#include "Quad.h"
|
||||||
|
|
||||||
#include <common/math.h>
|
#include <common/math.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <algorithm> // for max
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -32,15 +35,101 @@ namespace opengl
|
|||||||
{
|
{
|
||||||
|
|
||||||
Font::Font(love::font::Rasterizer * r, const Image::Filter& filter)
|
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);
|
type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA ? FONT_TRUETYPE : FONT_IMAGE);
|
||||||
delete gd;
|
delete gd;
|
||||||
|
createTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::~Font()
|
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
|
float Font::getHeight() const
|
||||||
@@ -48,9 +137,9 @@ namespace opengl
|
|||||||
return static_cast<float>(height);
|
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();
|
glPushMatrix();
|
||||||
|
|
||||||
glTranslatef(ceil(x), ceil(y), 0.0f);
|
glTranslatef(ceil(x), ceil(y), 0.0f);
|
||||||
@@ -63,57 +152,66 @@ namespace opengl
|
|||||||
dx = 0.0f;
|
dx = 0.0f;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!glyphs[g]) g = 32; // space
|
Glyph * glyph = glyphs[g];
|
||||||
|
if (!glyph) glyph = addGlyph(g);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
// 1.25 is magic line height for true type fonts
|
// 1.25 is magic line height for true type fonts
|
||||||
if (type == FONT_TRUETYPE) glTranslatef(0, floor(getHeight() / 1.25f + 0.5f), 0);
|
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();
|
glPopMatrix();
|
||||||
glTranslatef(static_cast<GLfloat>(spacing[g]), 0, 0);
|
glTranslatef(static_cast<GLfloat>(glyph->spacing), 0, 0);
|
||||||
dx += spacing[g];
|
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();
|
glPushMatrix();
|
||||||
glTranslatef(x, floor(y+getHeight() + 0.5f), 0.0f);
|
glTranslatef(x, floor(y+getHeight() + 0.5f), 0.0f);
|
||||||
glCallList(list+character);
|
glCallList(glyph->list);
|
||||||
glPopMatrix();*/
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::getWidth(const std::string & line) const
|
int Font::getWidth(const std::string & line)
|
||||||
{
|
{
|
||||||
if(line.size() == 0) return 0;
|
if(line.size() == 0) return 0;
|
||||||
int temp = 0;
|
int temp = 0;
|
||||||
|
|
||||||
|
Glyph * g;
|
||||||
|
|
||||||
for(unsigned int i = 0; i < line.size(); i++)
|
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;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::getWidth(const char * line) const
|
int Font::getWidth(const char * line)
|
||||||
{
|
{
|
||||||
return this->getWidth(std::string(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;
|
if(line.size() == 0) return 0;
|
||||||
int maxw = 0;
|
int maxw = 0;
|
||||||
int linen = 1;
|
int linen = 1;
|
||||||
int temp = 0;
|
int temp = 0;
|
||||||
std::string text;
|
std::string text;
|
||||||
|
Glyph * g;
|
||||||
|
|
||||||
for(unsigned int i = 0; i < line.size(); i++)
|
for(unsigned int i = 0; i < line.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -127,7 +225,9 @@ namespace opengl
|
|||||||
temp = getWidth(text);
|
temp = getWidth(text);
|
||||||
linen++;
|
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];
|
text += line[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +237,7 @@ namespace opengl
|
|||||||
return maxw;
|
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);
|
return getWrap(std::string(line), wrap, lines);
|
||||||
}
|
}
|
||||||
@@ -164,21 +264,26 @@ namespace opengl
|
|||||||
|
|
||||||
bool Font::loadVolatile()
|
bool Font::loadVolatile()
|
||||||
{
|
{
|
||||||
/*// reload all glyphs
|
createTexture();
|
||||||
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;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::unloadVolatile()
|
void Font::unloadVolatile()
|
||||||
{
|
{
|
||||||
/*// delete the glyphs
|
// nuke everything from orbit
|
||||||
glDeleteLists(list, MAX_CHARS);*/
|
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
|
} // opengl
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
#define LOVE_GRAPHICS_OPENGL_FONT_H
|
#define LOVE_GRAPHICS_OPENGL_FONT_H
|
||||||
|
|
||||||
// STD
|
// STD
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include <common/Object.h>
|
#include <common/Object.h>
|
||||||
@@ -48,13 +50,30 @@ namespace opengl
|
|||||||
FONT_UNKNOWN
|
FONT_UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Glyph
|
||||||
|
{
|
||||||
|
GLuint list;
|
||||||
|
int spacing;
|
||||||
|
};
|
||||||
|
|
||||||
love::font::Rasterizer * rasterizer;
|
love::font::Rasterizer * rasterizer;
|
||||||
|
|
||||||
int height;
|
int height;
|
||||||
float lineHeight;
|
float lineHeight;
|
||||||
float mSpacing; // modifies the spacing by multiplying it with this value
|
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;
|
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:
|
public:
|
||||||
|
|
||||||
@@ -75,7 +94,7 @@ namespace opengl
|
|||||||
* @param y The y-coordinate.
|
* @param y The y-coordinate.
|
||||||
* @param angle The amount of rotation.
|
* @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.
|
* Prints the character at the designated position.
|
||||||
@@ -84,7 +103,7 @@ namespace opengl
|
|||||||
* @param x The x-coordinate.
|
* @param x The x-coordinate.
|
||||||
* @param y The y-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.
|
* Returns the height of the font.
|
||||||
@@ -96,15 +115,15 @@ namespace opengl
|
|||||||
*
|
*
|
||||||
* @param line A line of text.
|
* @param line A line of text.
|
||||||
**/
|
**/
|
||||||
int getWidth(const std::string & line) const;
|
int getWidth(const std::string & line);
|
||||||
int getWidth(const char * line) const;
|
int getWidth(const char * line);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the width of the passed character.
|
* Returns the width of the passed character.
|
||||||
*
|
*
|
||||||
* @param character A character.
|
* @param character A character.
|
||||||
**/
|
**/
|
||||||
int getWidth(const char character) const;
|
int getWidth(const char character);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the maximal width of a wrapped string
|
* Returns the maximal width of a wrapped string
|
||||||
@@ -114,8 +133,8 @@ namespace opengl
|
|||||||
* @param wrap The number of pixels to wrap at
|
* @param wrap The number of pixels to wrap at
|
||||||
* @param lines Optional output of the number of lines needed
|
* @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 std::string & line, float wrap, int *lines = 0);
|
||||||
int getWrap(const char * line, float wrap, int *lines = 0) const;
|
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,
|
* Sets the line height (which should be a number to multiply the font size by,
|
||||||
|
|||||||
@@ -281,6 +281,9 @@ namespace opengl
|
|||||||
// Reset modelview matrix
|
// Reset modelview matrix
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
|
||||||
|
// Set pixel row alignment
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
|
||||||
|
|
||||||
// Set the new display mode as the current display mode.
|
// Set the new display mode as the current display mode.
|
||||||
currentMode.width = width;
|
currentMode.width = width;
|
||||||
|
|||||||
@@ -255,10 +255,6 @@ namespace opengl
|
|||||||
luax_convobj(L, idxs, 2, "font", "newRasterizer");
|
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);
|
love::font::Rasterizer * rasterizer = luax_checktype<love::font::Rasterizer>(L, 1, "Rasterizer", FONT_RASTERIZER_T);
|
||||||
|
|
||||||
// Create the font.
|
// Create the font.
|
||||||
@@ -450,10 +446,6 @@ namespace opengl
|
|||||||
luax_convobj(L, idxs, 2, "font", "newRasterizer");
|
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);
|
love::font::Rasterizer * rasterizer = luax_checktype<love::font::Rasterizer>(L, 1, "Rasterizer", FONT_RASTERIZER_T);
|
||||||
|
|
||||||
// Create the font.
|
// Create the font.
|
||||||
|
|||||||
Reference in New Issue
Block a user