From 8bbf68c69c3810607b9571685db2104ca80fbc6d Mon Sep 17 00:00:00 2001 From: vrld Date: Wed, 28 Nov 2012 16:01:04 +0100 Subject: [PATCH 1/8] Match types in VertexIndex::getType() --- src/modules/graphics/opengl/VertexBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl/VertexBuffer.cpp b/src/modules/graphics/opengl/VertexBuffer.cpp index 2c3951061..ef3c671b6 100644 --- a/src/modules/graphics/opengl/VertexBuffer.cpp +++ b/src/modules/graphics/opengl/VertexBuffer.cpp @@ -276,7 +276,7 @@ size_t VertexIndex::getIndexCount(size_t elements) const GLenum VertexIndex::getType(size_t s) const { // Calculates if unsigned short is big enough to hold all the vertex indices. - static const GLint type_table[] = {GL_UNSIGNED_INT, GL_UNSIGNED_SHORT}; + static const GLenum type_table[] = {GL_UNSIGNED_INT, GL_UNSIGNED_SHORT}; return type_table[int(GLushort(-1) < s * 4)]; } From b0abcbda71d70af96b3a0f51c6979974c80374d9 Mon Sep 17 00:00:00 2001 From: vrld Date: Wed, 28 Nov 2012 16:14:10 +0100 Subject: [PATCH 2/8] Fix: Returning wrong type in VertexIndex::getType() --- src/modules/graphics/opengl/VertexBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl/VertexBuffer.cpp b/src/modules/graphics/opengl/VertexBuffer.cpp index ef3c671b6..645f55a5b 100644 --- a/src/modules/graphics/opengl/VertexBuffer.cpp +++ b/src/modules/graphics/opengl/VertexBuffer.cpp @@ -276,7 +276,7 @@ size_t VertexIndex::getIndexCount(size_t elements) const GLenum VertexIndex::getType(size_t s) const { // Calculates if unsigned short is big enough to hold all the vertex indices. - static const GLenum type_table[] = {GL_UNSIGNED_INT, GL_UNSIGNED_SHORT}; + static const GLenum type_table[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT}; return type_table[int(GLushort(-1) < s * 4)]; } From 62e2c752e5714949aaa857318265e29195344803 Mon Sep 17 00:00:00 2001 From: vrld Date: Sat, 1 Dec 2012 14:00:05 +0100 Subject: [PATCH 3/8] More verbose VertexIndex::getType() (using numeric_limits) --- src/modules/graphics/opengl/VertexBuffer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl/VertexBuffer.cpp b/src/modules/graphics/opengl/VertexBuffer.cpp index 645f55a5b..8837ea5f5 100644 --- a/src/modules/graphics/opengl/VertexBuffer.cpp +++ b/src/modules/graphics/opengl/VertexBuffer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace love { @@ -277,7 +278,8 @@ GLenum VertexIndex::getType(size_t s) const { // Calculates if unsigned short is big enough to hold all the vertex indices. static const GLenum type_table[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT}; - return type_table[int(GLushort(-1) < s * 4)]; + return type_table[s * 4 > std::numeric_limits::max()]; + // if buffer-size > max(GLushort) then GL_UNSIGNED_INT else GL_UNSIGNED_SHORT } VertexBuffer *VertexIndex::getVertexBuffer() const From b10ff8da4297094c3b2eb5745aeabb5477c73f09 Mon Sep 17 00:00:00 2001 From: Bart van Strien Date: Sat, 15 Dec 2012 13:58:34 +0100 Subject: [PATCH 4/8] Apply patch that fixes File:read from reading too much (on EOF) (bug #530) --- src/modules/filesystem/physfs/File.cpp | 27 +++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/modules/filesystem/physfs/File.cpp b/src/modules/filesystem/physfs/File.cpp index 1f40cb7bd..44b13ef46 100644 --- a/src/modules/filesystem/physfs/File.cpp +++ b/src/modules/filesystem/physfs/File.cpp @@ -118,13 +118,34 @@ FileData *File::read(int64 size) if (!isOpen && !open(READ)) throw love::Exception("Could not read file %s.", filename.c_str()); - int64 max = (int64)PHYSFS_fileLength(file); + int64 max = getSize(); + int64 cur = tell(); size = (size == ALL) ? max : size; - size = (size > max) ? max : size; + + // Clamping because the file offset may be in a weird position. + if (cur < 0) + cur = 0; + else if (cur > max) + cur = max; + + if (cur + size > max) + size = max - cur; FileData *fileData = new FileData(size, getFilename()); + int64 bytesRead = read(fileData->getData(), size); - read(fileData->getData(), size); + if (bytesRead < 0 || (bytesRead == 0 && bytesRead != size)) + { + delete fileData; + throw love::Exception("Could not read from file."); + } + if (bytesRead < size) + { + FileData *tmpFileData = new FileData(bytesRead, getFilename()); + memcpy(tmpFileData->getData(), fileData->getData(), (size_t) bytesRead); + delete fileData; + fileData = tmpFileData; + } if (!isOpen) close(); From 82ea98aa65caa403e28ec0e11e4ee207c9a0a7d7 Mon Sep 17 00:00:00 2001 From: Bart van Strien Date: Sat, 15 Dec 2012 14:13:23 +0100 Subject: [PATCH 5/8] Disable Data:getPointer (issue #520) --- src/common/wrap_Data.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/wrap_Data.cpp b/src/common/wrap_Data.cpp index 13cf27f10..c7470249a 100644 --- a/src/common/wrap_Data.cpp +++ b/src/common/wrap_Data.cpp @@ -44,7 +44,7 @@ int w_Data_getSize(lua_State *L) const luaL_Reg w_Data_functions[] = { - { "getPointer", w_Data_getPointer }, +// { "getPointer", w_Data_getPointer }, { "getSize", w_Data_getSize }, { 0, 0 } }; From 68a895e7c6dcaa1e7d8244820f551c5b3d451627 Mon Sep 17 00:00:00 2001 From: Bart van Strien Date: Sat, 15 Dec 2012 14:26:46 +0100 Subject: [PATCH 6/8] Catch errors in love.font.newRasterizer, add luax_pconvobj which pcalls, allowing love.graphics.newFont to clean up before re-erroring --- src/common/runtime.cpp | 24 ++++++++++++++++++ src/common/runtime.h | 4 +++ src/modules/font/freetype/wrap_Font.cpp | 25 ++++++++++++------- src/modules/graphics/opengl/wrap_Graphics.cpp | 7 +++++- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 11738b1eb..b0a06dcba 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -343,6 +343,30 @@ int luax_convobj(lua_State *L, int idxs[], int n, const char *mod, const char *f return 0; } +int luax_pconvobj(lua_State *L, int idx, const char *mod, const char *fn) +{ + // Convert string to a file. + luax_getfunction(L, mod, fn); + lua_pushvalue(L, idx); // The initial argument. + int ret = lua_pcall(L, 1, 1, 0); // Call the function, one arg, one return value. + if (ret == 0) + lua_replace(L, idx); // Replace the initial argument with the new object. + return ret; +} + +int luax_pconvobj(lua_State *L, int idxs[], int n, const char *mod, const char *fn) +{ + luax_getfunction(L, mod, fn); + for (int i = 0; i < n; i++) + { + lua_pushvalue(L, idxs[i]); // The arguments. + } + int ret = lua_pcall(L, n, 1, 0); // Call the function, n args, one return value. + if (ret == 0) + lua_replace(L, idxs[0]); // Replace the initial argument with the new object. + return ret; +} + int luax_strtofile(lua_State *L, int idx) { return luax_convobj(L, idx, "filesystem", "newFile"); diff --git a/src/common/runtime.h b/src/common/runtime.h index 3d3c2a5e1..926631a92 100644 --- a/src/common/runtime.h +++ b/src/common/runtime.h @@ -267,6 +267,10 @@ int luax_convobj(lua_State *L, int idx, const char *module, const char *function **/ int luax_convobj(lua_State *L, int idxs[], int n, const char *module, const char *function); +// pcall versions of the above +int luax_pconvobj(lua_State *L, int idx, const char *module, const char *function); +int luax_pconvobj(lua_State *L, int idxs[], int n, const char *module, const char *function); + /** * 'Insist' that a table 'k' exists in the table at idx. Insistence involves that the * table (k) is created if it does not exist in the table at idx. The table at idx must diff --git a/src/modules/font/freetype/wrap_Font.cpp b/src/modules/font/freetype/wrap_Font.cpp index 441276e49..019930426 100644 --- a/src/modules/font/freetype/wrap_Font.cpp +++ b/src/modules/font/freetype/wrap_Font.cpp @@ -39,18 +39,25 @@ static Font *instance = 0; int w_newRasterizer(lua_State *L) { Rasterizer *t = NULL; - if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T)) + try { - love::image::ImageData *d = luax_checktype(L, 1, "ImageData", IMAGE_IMAGE_DATA_T); - const char *g = luaL_checkstring(L, 2); - std::string glyphs(g); - t = instance->newRasterizer(d, glyphs); + if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T)) + { + love::image::ImageData *d = luax_checktype(L, 1, "ImageData", IMAGE_IMAGE_DATA_T); + const char *g = luaL_checkstring(L, 2); + std::string glyphs(g); + t = instance->newRasterizer(d, glyphs); + } + else if (luax_istype(L, 1, DATA_T)) + { + Data *d = luax_checkdata(L, 1); + int size = luaL_checkint(L, 2); + t = instance->newRasterizer(d, size); + } } - else if (luax_istype(L, 1, DATA_T)) + catch (love::Exception &e) { - Data *d = luax_checkdata(L, 1); - int size = luaL_checkint(L, 2); - t = instance->newRasterizer(d, size); + return luaL_error(L, "%s", e.what()); } luax_newtype(L, "Rasterizer", FONT_RASTERIZER_T, t); diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index cd87f2e3e..5bd68e7f2 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -277,7 +277,12 @@ int w_newFont(lua_State *L) if (luax_istype(L, 1, DATA_T)) { int idxs[] = {1, 2}; - luax_convobj(L, idxs, 2, "font", "newRasterizer"); + int ret = luax_pconvobj(L, idxs, 2, "font", "newRasterizer"); + if (ret != 0) + { + font_data->release(); + return lua_error(L); + } } if (font_data) From a8dc813258fc3f3eb90be18ab1323882c3543232 Mon Sep 17 00:00:00 2001 From: Alexander Szpakowski Date: Sat, 15 Dec 2012 22:18:40 +0100 Subject: [PATCH 7/8] Text drawing performance improvements (issue #532) --- src/modules/graphics/opengl/Font.cpp | 296 +++++++++++++----- src/modules/graphics/opengl/Font.h | 57 +++- src/modules/graphics/opengl/wrap_Graphics.cpp | 12 +- 3 files changed, 263 insertions(+), 102 deletions(-) diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index 40911ccf3..2473c4661 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -39,6 +39,9 @@ namespace graphics 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) : rasterizer(r) , height(r->getHeight()) @@ -47,10 +50,37 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter) , filter(filter) { r->retain(); + love::font::GlyphData *gd = r->getGlyphData(32); type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA ? FONT_TRUETYPE : FONT_IMAGE); 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() @@ -59,9 +89,30 @@ Font::~Font() 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() { texture_x = texture_y = rowHeight = TEXTURE_PADDING; + GLuint t; glGenTextures(1, &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_T, GL_CLAMP_TO_EDGE); GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA); - // Initialize the texture - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - (GLsizei)TEXTURE_WIDTH, - (GLsizei)TEXTURE_HEIGHT, - 0, - format, - GL_UNSIGNED_BYTE, - NULL); + + + // try to initialize the texture, attempting smaller sizes if initialization fails + bool initialized = false; + while (texture_size_index >= 0) + { + texture_width = TEXTURE_WIDTHS[texture_size_index]; + texture_height = TEXTURE_HEIGHTS[texture_size_index]; + + 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 - std::vector emptyData(TEXTURE_WIDTH * TEXTURE_HEIGHT * (type == FONT_TRUETYPE ? 2 : 4), 0); + std::vector emptyData(texture_width * texture_height * (type == FONT_TRUETYPE ? 2 : 4), 0); glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, - 0, - (GLsizei)TEXTURE_WIDTH, - (GLsizei)TEXTURE_HEIGHT, + 0, 0, + (GLsizei)texture_width, + (GLsizei)texture_height, format, GL_UNSIGNED_BYTE, &emptyData[0]); } -Font::Glyph *Font::addGlyph(int glyph) +Font::Glyph *Font::addGlyph(const int glyph) { love::font::GlyphData *gd = rasterizer->getGlyphData(glyph); int w = gd->getWidth(); 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! texture_x = TEXTURE_PADDING; texture_y += rowHeight; 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! createTexture(); } Glyph *g = new Glyph; - g->list = g->texture = 0; + + g->texture = 0; 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 if (w > 0 && h > 0) { - g->list = glGenLists(1); - if (0 == g->list) - { - delete g; - return NULL; - } - - GLuint t = textures.back(); + const GLuint t = textures.back(); + 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; @@ -141,25 +212,17 @@ Font::Glyph *Font::addGlyph(int glyph) v.y = (float) texture_y; v.w = (float) w; v.h = (float) h; - Quad *q = new Quad(v, (const float) TEXTURE_WIDTH, (const float) 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); - 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; + + Quad q = Quad(v, (const float) texture_width, (const float) texture_height); + const vertex *verts = q.getVertices(); + + // copy vertex data to the glyph and set proper bearing + for (int i = 0; i < 4; i++) + { + g->quad.vertices[i] = verts[i]; + g->quad.vertices[i].x += gd->getBearingX(); + g->quad.vertices[i].y -= gd->getBearingY(); + } } if (w > 0) @@ -168,7 +231,18 @@ Font::Glyph *Font::addGlyph(int glyph) rowHeight = std::max(rowHeight, h + TEXTURE_PADDING); delete gd; + glyphs[glyph] = g; + + return g; +} + +Font::Glyph *Font::findGlyph(const int glyph) +{ + Glyph *g = glyphs[glyph]; + if (!g) + g = addGlyph(glyph); + return g; } @@ -177,61 +251,120 @@ float Font::getHeight() const return static_cast(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 dy = 0.0f; + + // keeps track of when we need to switch textures in our vertex array + std::vector glyphinfolist; + + std::vector glyphquads; + glyphquads.reserve(text.size()); // pre-allocate space for the maximum possible number of quads + + int quadindex = 0; + glPushMatrix(); Matrix t; t.setTransformation(ceil(x), ceil(y), angle, sx, sy, ox, oy, kx, ky); glMultMatrixf((const GLfloat *)t.getElements()); + try { - utf8::iterator i(text.begin(), text.begin(), text.end()); - utf8::iterator end(text.end(), text.begin(), text.end()); + utf8::iterator i(text.begin(), text.begin(), text.end()); + utf8::iterator end(text.end(), text.begin(), text.end()); + while (i != end) { int g = *i++; + if (g == '\n') { // wrap newline, but do not print it - glTranslatef(-dx, floor(getHeight() * getLineHeight() + 0.5f), 0); + dy += floor(getHeight() * getLineHeight() + 0.5f); dx = 0.0f; continue; } - 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); - bindTexture(glyph->texture); - glCallList(glyph->list); - glPopMatrix(); - glTranslatef(static_cast(glyph->spacing + letter_spacing), 0, 0); + + Glyph *glyph = findGlyph(g); + + // we only care about the vertices of glyphs which have a texture + if (glyph->texture != 0) + { + // copy glyphquad (4 vertices) from original glyph to our current quad list + glyphquads.push_back(glyph->quad); + + // 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; } } - catch(utf8::exception &e) + catch (love::Exception &e) + { + glPopMatrix(); + throw; + } + catch (utf8::exception &e) { glPopMatrix(); throw love::Exception("%s", e.what()); } - glPopMatrix(); -} - -void Font::print(char character, float x, float y) -{ - Glyph *glyph = glyphs[character]; - if (!glyph) glyph = addGlyph(character); - - if (0 != glyph->texture) - { - glPushMatrix(); - glTranslatef(x, floor(y+getHeight() + 0.5f), 0.0f); - bindTexture(glyph->texture); - glCallList(glyph->list); - glPopMatrix(); + + if (quadindex > 0 && glyphinfolist.size() > 0) + { + // 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()); + + 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 that uses a different texture than the previous section + std::vector::const_iterator it; + 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) @@ -253,8 +386,7 @@ int Font::getWidth(const std::string &str) while (i != end) { int c = *i++; - g = glyphs[c]; - if (!g) g = addGlyph(c); + g = findGlyph(c); width += static_cast(g->spacing * mSpacing); } } @@ -277,12 +409,11 @@ int Font::getWidth(const char *str) int Font::getWidth(const char character) { - Glyph *g = glyphs[character]; - if (!g) g = addGlyph(character); + Glyph *g = findGlyph(character); return g->spacing; } -std::vector Font::getWrap(const std::string text, float wrap, int *max_width) +std::vector Font::getWrap(const std::string &text, float wrap, int *max_width) { using namespace std; const float width_space = static_cast(getWidth(' ')); @@ -376,7 +507,6 @@ void Font::unloadVolatile() while (it != glyphs.end()) { g = it->second; - glDeleteLists(g->list, 1); delete g; glyphs.erase(it++); } diff --git a/src/modules/graphics/opengl/Font.h b/src/modules/graphics/opengl/Font.h index 0dde331b6..c8cd4fb71 100644 --- a/src/modules/graphics/opengl/Font.h +++ b/src/modules/graphics/opengl/Font.h @@ -32,7 +32,6 @@ #include "graphics/Image.h" #include "OpenGL.h" -#include "GLee.h" namespace love { @@ -69,16 +68,7 @@ public: * @param kx Shear along the x 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); - - /** - * 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); + 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); /** * Returns the height of the font. @@ -109,7 +99,7 @@ public: * @param max_width Optional output of the maximum width * Returns a vector with the lines. **/ - std::vector getWrap(const std::string text, float wrap, int *max_width = 0); + std::vector 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, @@ -139,6 +129,7 @@ public: // Implements Volatile. bool loadVolatile(); void unloadVolatile(); + private: enum FontType @@ -147,12 +138,35 @@ private: FONT_IMAGE, FONT_UNKNOWN }; + + // thin wrapper for an array of 4 vertices + struct GlyphQuad + { + vertex vertices[4]; + }; struct Glyph { - GLuint list; GLuint texture; 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; @@ -160,20 +174,29 @@ private: int height; float lineHeight; float mSpacing; // modifies the spacing by multiplying it with this value + + int texture_size_index; + int texture_width; + int texture_height; + std::vector textures; // vector of packed textures - std::map glyphs; // maps glyphs to display lists + std::map glyphs; // maps glyphs to quad information FontType type; 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; int texture_x, texture_y; int rowHeight; + bool initializeTexture(GLint format); void createTexture(); - Glyph *addGlyph(int glyph); + Glyph *addGlyph(const int glyph); + Glyph *findGlyph (const int glyph); }; // Font } // opengl diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 5bd68e7f2..9d434acc8 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -290,8 +290,16 @@ int w_newFont(lua_State *L) love::font::Rasterizer *rasterizer = luax_checktype(L, 1, "Rasterizer", FONT_RASTERIZER_T); - // Create the font. - Font *font = instance->newFont(rasterizer, instance->getDefaultImageFilter()); + Font *font = NULL; + try + { + // Create the font. + font = instance->newFont(rasterizer, instance->getDefaultImageFilter()); + } + catch (love::Exception &e) + { + return luaL_error(L, e.what()); + } if (font == 0) return luaL_error(L, "Could not load font."); From 697f4de80aacc81dfcd3ad5df9ebd658c360fce3 Mon Sep 17 00:00:00 2001 From: rude Date: Sat, 12 Jan 2013 11:28:32 +0100 Subject: [PATCH 8/8] Fix compilation issue in MSVC2012. --- src/modules/graphics/opengl/VertexBuffer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/graphics/opengl/VertexBuffer.cpp b/src/modules/graphics/opengl/VertexBuffer.cpp index 8837ea5f5..d3ffc6172 100644 --- a/src/modules/graphics/opengl/VertexBuffer.cpp +++ b/src/modules/graphics/opengl/VertexBuffer.cpp @@ -28,6 +28,11 @@ #include #include +// Conflicts with std::numeric_limits::max() (Windows). +#ifdef max +# undef max +#endif + namespace love { namespace graphics