mirror of
https://github.com/love2d/love.git
synced 2026-08-19 12:14:20 +02:00
Text drawing performance improvements (issue #532)
This commit is contained in:
@@ -39,6 +39,9 @@ namespace graphics
|
|||||||
namespace opengl
|
namespace opengl
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const int Font::TEXTURE_WIDTHS[] = {128, 256, 256, 512, 512, 1024, 1024};
|
||||||
|
const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512, 1024};
|
||||||
|
|
||||||
Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
|
Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
|
||||||
: rasterizer(r)
|
: rasterizer(r)
|
||||||
, height(r->getHeight())
|
, height(r->getHeight())
|
||||||
@@ -47,10 +50,37 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
|
|||||||
, filter(filter)
|
, filter(filter)
|
||||||
{
|
{
|
||||||
r->retain();
|
r->retain();
|
||||||
|
|
||||||
love::font::GlyphData *gd = r->getGlyphData(32);
|
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();
|
|
||||||
|
// try to find the best texture size match for the font size
|
||||||
|
// default to the largest texture size if no rough match is found
|
||||||
|
texture_size_index = NUM_TEXTURE_SIZES - 1;
|
||||||
|
for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
|
||||||
|
{
|
||||||
|
// base our chosen texture width/height on a very rough guess of the total size taken up by the font's used glyphs
|
||||||
|
// the estimate is likely larger than the actual total size taken up, which is good since texture changes are expensive
|
||||||
|
if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
|
||||||
|
{
|
||||||
|
texture_size_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
texture_width = TEXTURE_WIDTHS[texture_size_index];
|
||||||
|
texture_height = TEXTURE_HEIGHTS[texture_size_index];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
createTexture();
|
||||||
|
}
|
||||||
|
catch (love::Exception &e)
|
||||||
|
{
|
||||||
|
r->release();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::~Font()
|
Font::~Font()
|
||||||
@@ -59,9 +89,30 @@ Font::~Font()
|
|||||||
unloadVolatile();
|
unloadVolatile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Font::initializeTexture(GLint format)
|
||||||
|
{
|
||||||
|
GLint internalformat = (format == GL_LUMINANCE_ALPHA) ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8;
|
||||||
|
|
||||||
|
// clear errors before initializing
|
||||||
|
while (glGetError() != GL_NO_ERROR);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
internalformat,
|
||||||
|
(GLsizei)texture_width,
|
||||||
|
(GLsizei)texture_height,
|
||||||
|
0,
|
||||||
|
format,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return glGetError() == GL_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
void Font::createTexture()
|
void Font::createTexture()
|
||||||
{
|
{
|
||||||
texture_x = texture_y = rowHeight = TEXTURE_PADDING;
|
texture_x = texture_y = rowHeight = TEXTURE_PADDING;
|
||||||
|
|
||||||
GLuint t;
|
GLuint t;
|
||||||
glGenTextures(1, &t);
|
glGenTextures(1, &t);
|
||||||
textures.push_back(t);
|
textures.push_back(t);
|
||||||
@@ -74,65 +125,85 @@ void Font::createTexture()
|
|||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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);
|
GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
|
||||||
// Initialize the texture
|
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
|
||||||
0,
|
// try to initialize the texture, attempting smaller sizes if initialization fails
|
||||||
GL_RGBA,
|
bool initialized = false;
|
||||||
(GLsizei)TEXTURE_WIDTH,
|
while (texture_size_index >= 0)
|
||||||
(GLsizei)TEXTURE_HEIGHT,
|
{
|
||||||
0,
|
texture_width = TEXTURE_WIDTHS[texture_size_index];
|
||||||
format,
|
texture_height = TEXTURE_HEIGHTS[texture_size_index];
|
||||||
GL_UNSIGNED_BYTE,
|
|
||||||
NULL);
|
initialized = initializeTexture(format);
|
||||||
|
|
||||||
|
if (initialized || texture_size_index <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
--texture_size_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
// cleanup before throwing
|
||||||
|
deleteTexture(t);
|
||||||
|
bindTexture(0);
|
||||||
|
textures.pop_back();
|
||||||
|
|
||||||
|
throw love::Exception("Could not create font texture!");
|
||||||
|
}
|
||||||
|
|
||||||
// Fill the texture with transparent black
|
// Fill the texture with transparent black
|
||||||
std::vector<GLubyte> emptyData(TEXTURE_WIDTH * TEXTURE_HEIGHT * (type == FONT_TRUETYPE ? 2 : 4), 0);
|
std::vector<GLubyte> emptyData(texture_width * texture_height * (type == FONT_TRUETYPE ? 2 : 4), 0);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D,
|
glTexSubImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
0,
|
0, 0,
|
||||||
0,
|
(GLsizei)texture_width,
|
||||||
(GLsizei)TEXTURE_WIDTH,
|
(GLsizei)texture_height,
|
||||||
(GLsizei)TEXTURE_HEIGHT,
|
|
||||||
format,
|
format,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
&emptyData[0]);
|
&emptyData[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::Glyph *Font::addGlyph(int glyph)
|
Font::Glyph *Font::addGlyph(const int glyph)
|
||||||
{
|
{
|
||||||
love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
|
love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
|
||||||
int w = gd->getWidth();
|
int w = gd->getWidth();
|
||||||
int h = gd->getHeight();
|
int h = gd->getHeight();
|
||||||
|
|
||||||
if (texture_x + w + TEXTURE_PADDING > TEXTURE_WIDTH)
|
if (texture_x + w + TEXTURE_PADDING > texture_width)
|
||||||
{
|
{
|
||||||
// out of space - new row!
|
// out of space - new row!
|
||||||
texture_x = TEXTURE_PADDING;
|
texture_x = TEXTURE_PADDING;
|
||||||
texture_y += rowHeight;
|
texture_y += rowHeight;
|
||||||
rowHeight = TEXTURE_PADDING;
|
rowHeight = TEXTURE_PADDING;
|
||||||
}
|
}
|
||||||
if (texture_y + h + TEXTURE_PADDING > TEXTURE_HEIGHT)
|
if (texture_y + h + TEXTURE_PADDING > texture_height)
|
||||||
{
|
{
|
||||||
// totally out of space - new texture!
|
// totally out of space - new texture!
|
||||||
createTexture();
|
createTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
Glyph *g = new Glyph;
|
Glyph *g = new Glyph;
|
||||||
g->list = g->texture = 0;
|
|
||||||
|
g->texture = 0;
|
||||||
g->spacing = gd->getAdvance();
|
g->spacing = gd->getAdvance();
|
||||||
|
|
||||||
|
memset(&g->quad, 0, sizeof(GlyphQuad));
|
||||||
|
|
||||||
// don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers
|
// don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers
|
||||||
if (w > 0 && h > 0)
|
if (w > 0 && h > 0)
|
||||||
{
|
{
|
||||||
g->list = glGenLists(1);
|
const GLuint t = textures.back();
|
||||||
if (0 == g->list)
|
|
||||||
{
|
|
||||||
delete g;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint t = textures.back();
|
|
||||||
bindTexture(t);
|
bindTexture(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());
|
glTexSubImage2D(GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
texture_x,
|
||||||
|
texture_y,
|
||||||
|
w, h,
|
||||||
|
(type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
gd->getData());
|
||||||
|
|
||||||
g->texture = t;
|
g->texture = t;
|
||||||
|
|
||||||
@@ -141,25 +212,17 @@ Font::Glyph *Font::addGlyph(int glyph)
|
|||||||
v.y = (float) texture_y;
|
v.y = (float) texture_y;
|
||||||
v.w = (float) w;
|
v.w = (float) w;
|
||||||
v.h = (float) h;
|
v.h = (float) h;
|
||||||
Quad *q = new Quad(v, (const float) TEXTURE_WIDTH, (const float) TEXTURE_HEIGHT);
|
|
||||||
const vertex *verts = q->getVertices();
|
Quad q = Quad(v, (const float) texture_width, (const float) texture_height);
|
||||||
|
const vertex *verts = q.getVertices();
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
|
||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
// copy vertex data to the glyph and set proper bearing
|
||||||
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].x);
|
for (int i = 0; i < 4; i++)
|
||||||
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].s);
|
{
|
||||||
|
g->quad.vertices[i] = verts[i];
|
||||||
glNewList(g->list, GL_COMPILE);
|
g->quad.vertices[i].x += gd->getBearingX();
|
||||||
glPushMatrix();
|
g->quad.vertices[i].y -= gd->getBearingY();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w > 0)
|
if (w > 0)
|
||||||
@@ -168,7 +231,18 @@ Font::Glyph *Font::addGlyph(int glyph)
|
|||||||
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
|
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
|
||||||
|
|
||||||
delete gd;
|
delete gd;
|
||||||
|
|
||||||
glyphs[glyph] = g;
|
glyphs[glyph] = g;
|
||||||
|
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font::Glyph *Font::findGlyph(const int glyph)
|
||||||
|
{
|
||||||
|
Glyph *g = glyphs[glyph];
|
||||||
|
if (!g)
|
||||||
|
g = addGlyph(glyph);
|
||||||
|
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,61 +251,120 @@ float Font::getHeight() const
|
|||||||
return static_cast<float>(height);
|
return static_cast<float>(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::print(std::string text, float x, float y, float letter_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
void Font::print(const std::string &text, float x, float y, float letter_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
float dx = 0.0f; // spacing counter for newline handling
|
float dx = 0.0f; // spacing counter for newline handling
|
||||||
|
float dy = 0.0f;
|
||||||
|
|
||||||
|
// keeps track of when we need to switch textures in our vertex array
|
||||||
|
std::vector<GlyphArrayDrawInfo> glyphinfolist;
|
||||||
|
|
||||||
|
std::vector<GlyphQuad> glyphquads;
|
||||||
|
glyphquads.reserve(text.size()); // pre-allocate space for the maximum possible number of quads
|
||||||
|
|
||||||
|
int quadindex = 0;
|
||||||
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
|
||||||
Matrix t;
|
Matrix t;
|
||||||
t.setTransformation(ceil(x), ceil(y), angle, sx, sy, ox, oy, kx, ky);
|
t.setTransformation(ceil(x), ceil(y), angle, sx, sy, ox, oy, kx, ky);
|
||||||
glMultMatrixf((const GLfloat *)t.getElements());
|
glMultMatrixf((const GLfloat *)t.getElements());
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
utf8::iterator<std::string::iterator> i(text.begin(), text.begin(), text.end());
|
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
|
||||||
utf8::iterator<std::string::iterator> end(text.end(), text.begin(), text.end());
|
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
|
||||||
|
|
||||||
while (i != end)
|
while (i != end)
|
||||||
{
|
{
|
||||||
int g = *i++;
|
int g = *i++;
|
||||||
|
|
||||||
if (g == '\n')
|
if (g == '\n')
|
||||||
{
|
{
|
||||||
// wrap newline, but do not print it
|
// wrap newline, but do not print it
|
||||||
glTranslatef(-dx, floor(getHeight() * getLineHeight() + 0.5f), 0);
|
dy += floor(getHeight() * getLineHeight() + 0.5f);
|
||||||
dx = 0.0f;
|
dx = 0.0f;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Glyph *glyph = glyphs[g];
|
|
||||||
if (!glyph) glyph = addGlyph(g);
|
Glyph *glyph = findGlyph(g);
|
||||||
glPushMatrix();
|
|
||||||
// 1.25 is magic line height for true type fonts
|
// we only care about the vertices of glyphs which have a texture
|
||||||
if (type == FONT_TRUETYPE) glTranslatef(0, floor(getHeight() / 1.25f + 0.5f), 0);
|
if (glyph->texture != 0)
|
||||||
bindTexture(glyph->texture);
|
{
|
||||||
glCallList(glyph->list);
|
// copy glyphquad (4 vertices) from original glyph to our current quad list
|
||||||
glPopMatrix();
|
glyphquads.push_back(glyph->quad);
|
||||||
glTranslatef(static_cast<GLfloat>(glyph->spacing + letter_spacing), 0, 0);
|
|
||||||
|
// 1.25 is magic line height for true type fonts
|
||||||
|
float lineheight = (type == FONT_TRUETYPE) ? floor(getHeight() / 1.25f + 0.5f) : 0.0f;
|
||||||
|
|
||||||
|
// set proper relative position
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
glyphquads[quadindex].vertices[i].x += dx;
|
||||||
|
glyphquads[quadindex].vertices[i].y += dy + lineheight;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t listsize = glyphinfolist.size();
|
||||||
|
|
||||||
|
// check if current glyph texture has changed since the previous iteration
|
||||||
|
if (listsize == 0 || glyphinfolist[listsize-1].texture != glyph->texture)
|
||||||
|
{
|
||||||
|
// keep track of each sub-section of the string whose glyphs use different textures than the previous section
|
||||||
|
GlyphArrayDrawInfo glyphdrawinfo;
|
||||||
|
glyphdrawinfo.startquad = quadindex;
|
||||||
|
glyphdrawinfo.numquads = 0;
|
||||||
|
glyphdrawinfo.texture = glyph->texture;
|
||||||
|
glyphinfolist.push_back(glyphdrawinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
++quadindex;
|
||||||
|
++glyphinfolist[glyphinfolist.size()-1].numquads;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance the x position for the next glyph
|
||||||
dx += glyph->spacing + letter_spacing;
|
dx += glyph->spacing + letter_spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(utf8::exception &e)
|
catch (love::Exception &e)
|
||||||
|
{
|
||||||
|
glPopMatrix();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (utf8::exception &e)
|
||||||
{
|
{
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
throw love::Exception("%s", e.what());
|
throw love::Exception("%s", e.what());
|
||||||
}
|
}
|
||||||
glPopMatrix();
|
|
||||||
}
|
if (quadindex > 0 && glyphinfolist.size() > 0)
|
||||||
|
{
|
||||||
void Font::print(char character, float x, float y)
|
// sort glyph draw info list by texture first, and quad position in memory second (using the struct's < operator)
|
||||||
{
|
std::sort(glyphinfolist.begin(), glyphinfolist.end());
|
||||||
Glyph *glyph = glyphs[character];
|
|
||||||
if (!glyph) glyph = addGlyph(character);
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
if (0 != glyph->texture)
|
|
||||||
{
|
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].x);
|
||||||
glPushMatrix();
|
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].s);
|
||||||
glTranslatef(x, floor(y+getHeight() + 0.5f), 0.0f);
|
|
||||||
bindTexture(glyph->texture);
|
// we need to draw a new vertex array for every section of the string that uses a different texture than the previous section
|
||||||
glCallList(glyph->list);
|
std::vector<GlyphArrayDrawInfo>::const_iterator it;
|
||||||
glPopMatrix();
|
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
||||||
|
{
|
||||||
|
bindTexture(it->texture);
|
||||||
|
|
||||||
|
int startvertex = it->startquad * 4;
|
||||||
|
int numvertices = it->numquads * 4;
|
||||||
|
|
||||||
|
glDrawArrays(GL_QUADS, startvertex, numvertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::getWidth(const std::string &str)
|
int Font::getWidth(const std::string &str)
|
||||||
@@ -253,8 +386,7 @@ int Font::getWidth(const std::string &str)
|
|||||||
while (i != end)
|
while (i != end)
|
||||||
{
|
{
|
||||||
int c = *i++;
|
int c = *i++;
|
||||||
g = glyphs[c];
|
g = findGlyph(c);
|
||||||
if (!g) g = addGlyph(c);
|
|
||||||
width += static_cast<int>(g->spacing * mSpacing);
|
width += static_cast<int>(g->spacing * mSpacing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,12 +409,11 @@ int Font::getWidth(const char *str)
|
|||||||
|
|
||||||
int Font::getWidth(const char character)
|
int Font::getWidth(const char character)
|
||||||
{
|
{
|
||||||
Glyph *g = glyphs[character];
|
Glyph *g = findGlyph(character);
|
||||||
if (!g) g = addGlyph(character);
|
|
||||||
return g->spacing;
|
return g->spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Font::getWrap(const std::string text, float wrap, int *max_width)
|
std::vector<std::string> Font::getWrap(const std::string &text, float wrap, int *max_width)
|
||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
const float width_space = static_cast<float>(getWidth(' '));
|
const float width_space = static_cast<float>(getWidth(' '));
|
||||||
@@ -376,7 +507,6 @@ void Font::unloadVolatile()
|
|||||||
while (it != glyphs.end())
|
while (it != glyphs.end())
|
||||||
{
|
{
|
||||||
g = it->second;
|
g = it->second;
|
||||||
glDeleteLists(g->list, 1);
|
|
||||||
delete g;
|
delete g;
|
||||||
glyphs.erase(it++);
|
glyphs.erase(it++);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
#include "graphics/Image.h"
|
#include "graphics/Image.h"
|
||||||
|
|
||||||
#include "OpenGL.h"
|
#include "OpenGL.h"
|
||||||
#include "GLee.h"
|
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
@@ -69,16 +68,7 @@ public:
|
|||||||
* @param kx Shear along the x axis.
|
* @param kx Shear along the x axis.
|
||||||
* @param ky Shear along the y axis.
|
* @param ky Shear along the y axis.
|
||||||
**/
|
**/
|
||||||
void print(std::string text, float x, float y, float letter_spacing = 0.0f, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
|
void print(const std::string &text, float x, float y, float letter_spacing = 0.0f, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the character at the designated position.
|
|
||||||
*
|
|
||||||
* @param character A character.
|
|
||||||
* @param x The x-coordinate.
|
|
||||||
* @param y The y-coordinate.
|
|
||||||
**/
|
|
||||||
void print(char character, float x, float y);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the height of the font.
|
* Returns the height of the font.
|
||||||
@@ -109,7 +99,7 @@ public:
|
|||||||
* @param max_width Optional output of the maximum width
|
* @param max_width Optional output of the maximum width
|
||||||
* Returns a vector with the lines.
|
* Returns a vector with the lines.
|
||||||
**/
|
**/
|
||||||
std::vector<std::string> getWrap(const std::string text, float wrap, int *max_width = 0);
|
std::vector<std::string> getWrap(const std::string &text, float wrap, int *max_width = 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,
|
||||||
@@ -139,6 +129,7 @@ public:
|
|||||||
// Implements Volatile.
|
// Implements Volatile.
|
||||||
bool loadVolatile();
|
bool loadVolatile();
|
||||||
void unloadVolatile();
|
void unloadVolatile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
enum FontType
|
enum FontType
|
||||||
@@ -147,12 +138,35 @@ private:
|
|||||||
FONT_IMAGE,
|
FONT_IMAGE,
|
||||||
FONT_UNKNOWN
|
FONT_UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// thin wrapper for an array of 4 vertices
|
||||||
|
struct GlyphQuad
|
||||||
|
{
|
||||||
|
vertex vertices[4];
|
||||||
|
};
|
||||||
|
|
||||||
struct Glyph
|
struct Glyph
|
||||||
{
|
{
|
||||||
GLuint list;
|
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
int spacing;
|
int spacing;
|
||||||
|
GlyphQuad quad;
|
||||||
|
};
|
||||||
|
|
||||||
|
// used to determine when to change textures in the vertex array generated when printing text
|
||||||
|
struct GlyphArrayDrawInfo
|
||||||
|
{
|
||||||
|
GLuint texture;
|
||||||
|
int startquad, numquads;
|
||||||
|
|
||||||
|
// used when sorting with std::sort
|
||||||
|
// sorts by texture first (binding textures is expensive) and relative position in memory second
|
||||||
|
bool operator < (const GlyphArrayDrawInfo &other) const
|
||||||
|
{
|
||||||
|
if (texture != other.texture)
|
||||||
|
return texture < other.texture;
|
||||||
|
else
|
||||||
|
return startquad < other.startquad;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
love::font::Rasterizer *rasterizer;
|
love::font::Rasterizer *rasterizer;
|
||||||
@@ -160,20 +174,29 @@ private:
|
|||||||
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
|
||||||
|
|
||||||
|
int texture_size_index;
|
||||||
|
int texture_width;
|
||||||
|
int texture_height;
|
||||||
|
|
||||||
std::vector<GLuint> textures; // vector of packed textures
|
std::vector<GLuint> textures; // vector of packed textures
|
||||||
std::map<int, Glyph *> glyphs; // maps glyphs to display lists
|
std::map<int, Glyph *> glyphs; // maps glyphs to quad information
|
||||||
FontType type;
|
FontType type;
|
||||||
Image::Filter filter;
|
Image::Filter filter;
|
||||||
|
|
||||||
|
static const int NUM_TEXTURE_SIZES = 7;
|
||||||
|
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
|
||||||
|
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
|
||||||
|
|
||||||
static const int TEXTURE_WIDTH = 512;
|
|
||||||
static const int TEXTURE_HEIGHT = 512;
|
|
||||||
static const int TEXTURE_PADDING = 1;
|
static const int TEXTURE_PADDING = 1;
|
||||||
|
|
||||||
int texture_x, texture_y;
|
int texture_x, texture_y;
|
||||||
int rowHeight;
|
int rowHeight;
|
||||||
|
|
||||||
|
bool initializeTexture(GLint format);
|
||||||
void createTexture();
|
void createTexture();
|
||||||
Glyph *addGlyph(int glyph);
|
Glyph *addGlyph(const int glyph);
|
||||||
|
Glyph *findGlyph (const int glyph);
|
||||||
}; // Font
|
}; // Font
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -290,8 +290,16 @@ int w_newFont(lua_State *L)
|
|||||||
|
|
||||||
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.
|
Font *font = NULL;
|
||||||
Font *font = instance->newFont(rasterizer, instance->getDefaultImageFilter());
|
try
|
||||||
|
{
|
||||||
|
// Create the font.
|
||||||
|
font = instance->newFont(rasterizer, instance->getDefaultImageFilter());
|
||||||
|
}
|
||||||
|
catch (love::Exception &e)
|
||||||
|
{
|
||||||
|
return luaL_error(L, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
if (font == 0)
|
if (font == 0)
|
||||||
return luaL_error(L, "Could not load font.");
|
return luaL_error(L, "Could not load font.");
|
||||||
|
|||||||
Reference in New Issue
Block a user