Merge default branch.

--HG--
branch : minor
This commit is contained in:
rude
2013-01-12 11:52:04 +01:00
9 changed files with 347 additions and 118 deletions
+24
View File
@@ -348,6 +348,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");
+4
View File
@@ -276,6 +276,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
+1 -1
View File
@@ -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 }
};
+24 -3
View File
@@ -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();
+16 -9
View File
@@ -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<love::image::ImageData>(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<love::image::ImageData>(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);
+213 -83
View File
@@ -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<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,
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<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;
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<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 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();
Matrix t;
t.setTransformation(ceil(x), ceil(y), angle, sx, sy, ox, oy, kx, ky);
glMultMatrixf((const GLfloat *)t.getElements());
try
{
utf8::iterator<std::string::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> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_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<GLfloat>(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<GlyphArrayDrawInfo>::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<int>(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<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;
const float width_space = static_cast<float>(getWidth(' '));
@@ -376,7 +507,6 @@ void Font::unloadVolatile()
while (it != glyphs.end())
{
g = it->second;
glDeleteLists(g->list, 1);
delete g;
glyphs.erase(it++);
}
+40 -17
View File
@@ -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<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,
@@ -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<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;
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
+9 -2
View File
@@ -26,6 +26,12 @@
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <limits>
// Conflicts with std::numeric_limits<GLushort>::max() (Windows).
#ifdef max
# undef max
#endif
namespace love
{
@@ -276,8 +282,9 @@ 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};
return type_table[int(GLushort(-1) < s * 4)];
static const GLenum type_table[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT};
return type_table[s * 4 > std::numeric_limits<GLushort>::max()];
// if buffer-size > max(GLushort) then GL_UNSIGNED_INT else GL_UNSIGNED_SHORT
}
VertexBuffer *VertexIndex::getVertexBuffer() const
+16 -3
View File
@@ -335,7 +335,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)
@@ -343,8 +348,16 @@ int w_newFont(lua_State *L)
love::font::Rasterizer *rasterizer = luax_checktype<love::font::Rasterizer>(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.");