mirror of
https://github.com/love2d/love.git
synced 2026-08-17 11:00:31 +02:00
Slightly improved performance and cleaned up code a bit for love.graphics.print
This commit is contained in:
@@ -41,7 +41,9 @@ namespace opengl
|
|||||||
class Canvas : public DrawGable, public Volatile
|
class Canvas : public DrawGable, public Volatile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum TextureType {
|
|
||||||
|
enum TextureType
|
||||||
|
{
|
||||||
TYPE_NORMAL,
|
TYPE_NORMAL,
|
||||||
TYPE_HDR,
|
TYPE_HDR,
|
||||||
TYPE_MAX_ENUM
|
TYPE_MAX_ENUM
|
||||||
@@ -103,6 +105,7 @@ void setFilter(const Image::Filter &f);
|
|||||||
static bool isSupported();
|
static bool isSupported();
|
||||||
static bool isHDRSupported();
|
static bool isHDRSupported();
|
||||||
static bool isMultiCanvasSupported();
|
static bool isMultiCanvasSupported();
|
||||||
|
|
||||||
static bool getConstant(const char *in, TextureType &out);
|
static bool getConstant(const char *in, TextureType &out);
|
||||||
static bool getConstant(TextureType in, const char *&out);
|
static bool getConstant(TextureType in, const char *&out);
|
||||||
|
|
||||||
@@ -115,6 +118,7 @@ void setFilter(const Image::Filter &f);
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class Shader;
|
friend class Shader;
|
||||||
|
|
||||||
GLsizei width;
|
GLsizei width;
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
|
|||||||
for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
|
for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
|
||||||
{
|
{
|
||||||
// Make a rough estimate of the total used texture size, based on glyph
|
// Make a rough estimate of the total used texture size, based on glyph
|
||||||
// height. THe estimated size is likely larger than the actual total
|
// height. The estimated size is likely larger than the actual total
|
||||||
// size, which is good because texture changes are expensive.
|
// size, which is good because texture switching is expensive.
|
||||||
if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
|
if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
|
||||||
{
|
{
|
||||||
textureSizeIndex = i;
|
textureSizeIndex = i;
|
||||||
@@ -133,7 +133,7 @@ void Font::createTexture()
|
|||||||
|
|
||||||
GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
|
GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
|
||||||
|
|
||||||
// try to initialize the texture, attempting smaller sizes if initialization fails
|
// Initialize the texture, attempting smaller sizes if initialization fails.
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
while (textureSizeIndex >= 0)
|
while (textureSizeIndex >= 0)
|
||||||
{
|
{
|
||||||
@@ -150,7 +150,7 @@ void Font::createTexture()
|
|||||||
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
{
|
{
|
||||||
// cleanup before throwing
|
// Clean up before throwing.
|
||||||
gl.deleteTexture(t);
|
gl.deleteTexture(t);
|
||||||
gl.bindTexture(0);
|
gl.bindTexture(0);
|
||||||
textures.pop_back();
|
textures.pop_back();
|
||||||
@@ -158,7 +158,7 @@ void Font::createTexture()
|
|||||||
throw love::Exception("Could not create font texture!");
|
throw love::Exception("Could not create font texture!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill the texture with transparent black
|
// Fill the texture with transparent black.
|
||||||
std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0);
|
std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D,
|
glTexSubImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
@@ -196,7 +196,7 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
|
|||||||
g->texture = 0;
|
g->texture = 0;
|
||||||
g->spacing = gd->getAdvance();
|
g->spacing = gd->getAdvance();
|
||||||
|
|
||||||
memset(&g->quad, 0, sizeof(GlyphQuad));
|
memset(g->vertices, 0, sizeof(vertex) * 4);
|
||||||
|
|
||||||
// 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)
|
||||||
@@ -225,9 +225,9 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
|
|||||||
// copy vertex data to the glyph and set proper bearing
|
// copy vertex data to the glyph and set proper bearing
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
g->quad.vertices[i] = verts[i];
|
g->vertices[i] = verts[i];
|
||||||
g->quad.vertices[i].x += gd->getBearingX();
|
g->vertices[i].x += gd->getBearingX();
|
||||||
g->quad.vertices[i].y -= gd->getBearingY();
|
g->vertices[i].y -= gd->getBearingY();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,11 +245,12 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
|
|||||||
|
|
||||||
Font::Glyph *Font::findGlyph(uint32 glyph)
|
Font::Glyph *Font::findGlyph(uint32 glyph)
|
||||||
{
|
{
|
||||||
Glyph *g = glyphs[glyph];
|
std::map<uint32, Glyph *>::const_iterator it = glyphs.find(glyph);
|
||||||
if (!g)
|
|
||||||
g = addGlyph(glyph);
|
|
||||||
|
|
||||||
return g;
|
if (it != glyphs.end())
|
||||||
|
return it->second;
|
||||||
|
else
|
||||||
|
return addGlyph(glyph);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getHeight() const
|
float Font::getHeight() const
|
||||||
@@ -259,22 +260,20 @@ float Font::getHeight() const
|
|||||||
|
|
||||||
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)
|
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
|
// Spacing counter and newline handling.
|
||||||
|
float dx = 0.0f;
|
||||||
float dy = 0.0f;
|
float dy = 0.0f;
|
||||||
|
|
||||||
// keeps track of when we need to switch textures in our vertex array
|
float lineheight = getBaseline();
|
||||||
|
|
||||||
|
// Keeps track of when we need to switch textures in our vertex array.
|
||||||
std::vector<GlyphArrayDrawInfo> glyphinfolist;
|
std::vector<GlyphArrayDrawInfo> glyphinfolist;
|
||||||
|
|
||||||
std::vector<GlyphQuad> glyphquads;
|
// Pre-allocate space for the maximum possible number of vertices.
|
||||||
glyphquads.reserve(text.size()); // pre-allocate space for the maximum possible number of quads
|
std::vector<vertex> glyphverts;
|
||||||
|
glyphverts.reserve(text.length() * 4);
|
||||||
|
|
||||||
int quadindex = 0;
|
int vertexcount = 0;
|
||||||
|
|
||||||
glPushMatrix();
|
|
||||||
|
|
||||||
Matrix t;
|
|
||||||
t.setTransformation(ceil(x), ceil(y), angle, sx, sy, ox, oy, kx, ky);
|
|
||||||
glMultMatrixf((const GLfloat *)t.getElements());
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -287,90 +286,79 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing
|
|||||||
|
|
||||||
if (g == '\n')
|
if (g == '\n')
|
||||||
{
|
{
|
||||||
// wrap newline, but do not print it
|
// Wrap newline, but do not print it.
|
||||||
dy += floor(getHeight() * getLineHeight() + 0.5f);
|
dy += floorf(getHeight() * getLineHeight() + 0.5f);
|
||||||
dx = 0.0f;
|
dx = 0.0f;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Glyph *glyph = findGlyph(g);
|
Glyph *glyph = findGlyph(g);
|
||||||
|
|
||||||
// we only care about the vertices of glyphs which have a texture
|
|
||||||
if (glyph->texture != 0)
|
if (glyph->texture != 0)
|
||||||
{
|
{
|
||||||
// copy glyphquad (4 vertices) from original glyph to our current quad list
|
// Copy the vertices and set their proper relative positions.
|
||||||
glyphquads.push_back(glyph->quad);
|
for (int j = 0; j < 4; j++)
|
||||||
|
|
||||||
float lineheight = getBaseline();
|
|
||||||
|
|
||||||
// set proper relative position
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
{
|
||||||
glyphquads[quadindex].vertices[i].x += dx;
|
glyphverts.push_back(glyph->vertices[j]);
|
||||||
glyphquads[quadindex].vertices[i].y += dy + lineheight;
|
glyphverts.back().x += dx;
|
||||||
|
glyphverts.back().y += dy + lineheight;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t listsize = glyphinfolist.size();
|
// Check if glyph texture has changed since the last iteration.
|
||||||
|
if (glyphinfolist.size() == 0 || glyphinfolist.back().texture != glyph->texture)
|
||||||
// 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
|
// keep track of each sub-section of the string whose glyphs use different textures than the previous section
|
||||||
GlyphArrayDrawInfo glyphdrawinfo;
|
GlyphArrayDrawInfo gdrawinfo;
|
||||||
glyphdrawinfo.startquad = quadindex;
|
gdrawinfo.startvertex = vertexcount;
|
||||||
glyphdrawinfo.numquads = 0;
|
gdrawinfo.vertexcount = 0;
|
||||||
glyphdrawinfo.texture = glyph->texture;
|
gdrawinfo.texture = glyph->texture;
|
||||||
glyphinfolist.push_back(glyphdrawinfo);
|
glyphinfolist.push_back(gdrawinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
++quadindex;
|
vertexcount += 4;
|
||||||
++glyphinfolist[glyphinfolist.size()-1].numquads;
|
glyphinfolist.back().vertexcount += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance the x position for the next glyph
|
// Advance the x position for the next glyph.
|
||||||
dx += glyph->spacing + letter_spacing;
|
dx += glyph->spacing + letter_spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (love::Exception &)
|
|
||||||
{
|
|
||||||
glPopMatrix();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (utf8::exception &e)
|
catch (utf8::exception &e)
|
||||||
{
|
{
|
||||||
glPopMatrix();
|
|
||||||
throw love::Exception("Decoding error: %s", e.what());
|
throw love::Exception("Decoding error: %s", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quadindex > 0 && glyphinfolist.size() > 0)
|
if (vertexcount <= 0 || glyphinfolist.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
|
||||||
|
Matrix t;
|
||||||
|
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
|
||||||
|
glMultMatrixf((const GLfloat *)t.getElements());
|
||||||
|
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
||||||
|
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphverts[0].x);
|
||||||
|
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphverts[0].s);
|
||||||
|
|
||||||
|
// We need to draw a new vertex array for every section of the string which
|
||||||
|
// uses a different texture than the previous section.
|
||||||
|
std::vector<GlyphArrayDrawInfo>::const_iterator it;
|
||||||
|
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
||||||
{
|
{
|
||||||
// Sort glyph draw info list by texture first, and quad position in
|
gl.bindTexture(it->texture);
|
||||||
// memory second (using the struct's < operator).
|
glDrawArrays(GL_QUADS, it->startvertex, it->vertexcount);
|
||||||
std::sort(glyphinfolist.begin(), glyphinfolist.end());
|
|
||||||
|
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
|
||||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
||||||
|
|
||||||
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].x);
|
|
||||||
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].s);
|
|
||||||
|
|
||||||
// We need to draw a new vertex array for every section of the string
|
|
||||||
// which uses a different texture than the previous section.
|
|
||||||
std::vector<GlyphArrayDrawInfo>::const_iterator it;
|
|
||||||
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
|
||||||
{
|
|
||||||
gl.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,25 +148,20 @@ 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 texture;
|
GLuint texture;
|
||||||
int spacing;
|
int spacing;
|
||||||
GlyphQuad quad;
|
vertex vertices[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
// used to determine when to change textures in the vertex array generated when printing text
|
// used to determine when to change textures in the vertex array generated when printing text
|
||||||
struct GlyphArrayDrawInfo
|
struct GlyphArrayDrawInfo
|
||||||
{
|
{
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
int startquad, numquads;
|
int startvertex;
|
||||||
|
int vertexcount;
|
||||||
|
|
||||||
// used when sorting with std::sort
|
// used when sorting with std::sort
|
||||||
// sorts by texture first (binding textures is expensive) and relative position in memory second
|
// sorts by texture first (binding textures is expensive) and relative position in memory second
|
||||||
@@ -175,10 +170,15 @@ private:
|
|||||||
if (texture != other.texture)
|
if (texture != other.texture)
|
||||||
return texture < other.texture;
|
return texture < other.texture;
|
||||||
else
|
else
|
||||||
return startquad < other.startquad;
|
return startvertex < other.startvertex;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool initializeTexture(GLint format);
|
||||||
|
void createTexture();
|
||||||
|
Glyph *addGlyph(uint32 glyph);
|
||||||
|
Glyph *findGlyph(uint32 glyph);
|
||||||
|
|
||||||
love::font::Rasterizer *rasterizer;
|
love::font::Rasterizer *rasterizer;
|
||||||
|
|
||||||
int height;
|
int height;
|
||||||
@@ -207,10 +207,6 @@ private:
|
|||||||
int textureX, textureY;
|
int textureX, textureY;
|
||||||
int rowHeight;
|
int rowHeight;
|
||||||
|
|
||||||
bool initializeTexture(GLint format);
|
|
||||||
void createTexture();
|
|
||||||
Glyph *addGlyph(uint32 glyph);
|
|
||||||
Glyph *findGlyph(uint32 glyph);
|
|
||||||
}; // Font
|
}; // Font
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
Reference in New Issue
Block a user