Merged in slime73/love-mipmap (pull request #18: Add mipmapping support to images)

This commit is contained in:
vrld
2013-01-12 23:54:15 +01:00
20 changed files with 672 additions and 318 deletions
+1
View File
@@ -160,6 +160,7 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
{ "pixeleffect", Graphics::SUPPORT_PIXELEFFECT }, { "pixeleffect", Graphics::SUPPORT_PIXELEFFECT },
{ "npot", Graphics::SUPPORT_NPOT }, { "npot", Graphics::SUPPORT_NPOT },
{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE }, { "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
{ "mipmap", Graphics::SUPPORT_MIPMAP },
}; };
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries)); StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
+1
View File
@@ -89,6 +89,7 @@ public:
SUPPORT_PIXELEFFECT, SUPPORT_PIXELEFFECT,
SUPPORT_NPOT, SUPPORT_NPOT,
SUPPORT_SUBTRACTIVE, SUPPORT_SUBTRACTIVE,
SUPPORT_MIPMAP,
SUPPORT_MAX_ENUM SUPPORT_MAX_ENUM
}; };
+13
View File
@@ -25,9 +25,12 @@ namespace love
namespace graphics namespace graphics
{ {
Image::Filter Image::defaultFilter;
Image::Filter::Filter() Image::Filter::Filter()
: min(FILTER_LINEAR) : min(FILTER_LINEAR)
, mag(FILTER_LINEAR) , mag(FILTER_LINEAR)
, mipmap(FILTER_NONE)
{ {
} }
@@ -41,6 +44,16 @@ Image::~Image()
{ {
} }
void Image::setDefaultFilter(const Filter &f)
{
defaultFilter = f;
}
const Image::Filter &Image::getDefaultFilter()
{
return defaultFilter;
}
bool Image::getConstant(const char *in, FilterMode &out) bool Image::getConstant(const char *in, FilterMode &out)
{ {
return filterModes.find(in, out); return filterModes.find(in, out);
+9
View File
@@ -46,6 +46,7 @@ public:
{ {
FILTER_LINEAR = 1, FILTER_LINEAR = 1,
FILTER_NEAREST, FILTER_NEAREST,
FILTER_NONE,
FILTER_MAX_ENUM FILTER_MAX_ENUM
}; };
@@ -54,6 +55,7 @@ public:
Filter(); Filter();
FilterMode min; FilterMode min;
FilterMode mag; FilterMode mag;
FilterMode mipmap;
}; };
struct Wrap struct Wrap
@@ -64,6 +66,10 @@ public:
}; };
virtual ~Image(); virtual ~Image();
// The default filter.
static void setDefaultFilter(const Filter &f);
static const Filter &getDefaultFilter();
static bool getConstant(const char *in, FilterMode &out); static bool getConstant(const char *in, FilterMode &out);
static bool getConstant(FilterMode in, const char *&out); static bool getConstant(FilterMode in, const char *&out);
@@ -72,6 +78,9 @@ public:
private: private:
// The default image filter
static Filter defaultFilter;
static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[]; static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes; static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[]; static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
+14 -35
View File
@@ -97,8 +97,9 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
glGenTextures(1, &img); glGenTextures(1, &img);
bindTexture(img); bindTexture(img);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); setTextureFilter(Image::getDefaultFilter());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
0, GL_RGBA, format, NULL); 0, GL_RGBA, format, NULL);
bindTexture(0); bindTexture(0);
@@ -164,8 +165,8 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
glGenTextures(1, &img); glGenTextures(1, &img);
bindTexture(img); bindTexture(img);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); setTextureFilter(Image::getDefaultFilter());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
0, GL_RGBA, format, NULL); 0, GL_RGBA, format, NULL);
@@ -231,8 +232,9 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
glGenTextures(1, &img); glGenTextures(1, &img);
bindTexture(img); bindTexture(img);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); setTextureFilter(Image::getDefaultFilter());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
0, GL_RGBA, format, NULL); 0, GL_RGBA, format, NULL);
bindTexture(0); bindTexture(0);
@@ -312,6 +314,8 @@ Canvas::Canvas(int width, int height, TextureType texture_type)
vertices[3].s = 1; vertices[3].s = 1;
vertices[3].t = 0; vertices[3].t = 0;
settings.filter = Image::getDefaultFilter();
getStrategy(); getStrategy();
loadVolatile(); loadVolatile();
@@ -456,51 +460,26 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
void Canvas::setFilter(const Image::Filter &f) void Canvas::setFilter(const Image::Filter &f)
{ {
GLint gmin = (f.min == Image::FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR;
GLint gmag = (f.mag == Image::FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR;
bindTexture(img); bindTexture(img);
setTextureFilter(f);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
} }
Image::Filter Canvas::getFilter() const Image::Filter Canvas::getFilter() const
{ {
GLint gmin, gmag;
bindTexture(img); bindTexture(img);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin); return getTextureFilter();
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
Image::Filter f;
f.min = (gmin == GL_NEAREST) ? Image::FILTER_NEAREST : Image::FILTER_LINEAR;
f.mag = (gmag == GL_NEAREST) ? Image::FILTER_NEAREST : Image::FILTER_LINEAR;
return f;
} }
void Canvas::setWrap(const Image::Wrap &w) void Canvas::setWrap(const Image::Wrap &w)
{ {
GLint wrap_s = (w.s == Image::WRAP_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
GLint wrap_t = (w.t == Image::WRAP_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
bindTexture(img); bindTexture(img);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s); setTextureWrap(w);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
} }
Image::Wrap Canvas::getWrap() const Image::Wrap Canvas::getWrap() const
{ {
GLint wrap_s, wrap_t;
bindTexture(img); bindTexture(img);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &wrap_s); return getTextureWrap();
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &wrap_t);
Image::Wrap w;
w.s = (wrap_s == GL_CLAMP_TO_EDGE) ? Image::WRAP_CLAMP : Image::WRAP_REPEAT;
w.t = (wrap_t == GL_CLAMP_TO_EDGE) ? Image::WRAP_CLAMP : Image::WRAP_REPEAT;
return w;
} }
bool Canvas::loadVolatile() bool Canvas::loadVolatile()
+137 -64
View File
@@ -21,15 +21,15 @@
#include "Font.h" #include "Font.h"
#include "font/GlyphData.h" #include "font/GlyphData.h"
#include "Quad.h" #include "Quad.h"
#include "Image.h"
#include "libraries/utf8/utf8.h" #include "libraries/utf8/utf8.h"
#include "common/math.h" #include "common/math.h"
#include "common/Matrix.h" #include "common/Matrix.h"
#include <math.h> #include <math.h>
#include <sstream> #include <sstream>
#include <algorithm> // for max #include <algorithm> // for max
namespace love namespace love
@@ -39,8 +39,8 @@ namespace graphics
namespace opengl namespace opengl
{ {
const int Font::TEXTURE_WIDTHS[] = {128, 256, 256, 512, 512, 1024, 1024}; const int Font::TEXTURE_WIDTHS[] = {128, 256, 256, 512, 512, 1024, 1024};
const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512, 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)
@@ -48,13 +48,12 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
, lineHeight(1) , lineHeight(1)
, mSpacing(1) , mSpacing(1)
, filter(filter) , filter(filter)
, mipmapsharpness(0.0f)
{ {
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;
// try to find the best texture size match for the font size // try to find the best texture size match for the font size
// default to the largest texture size if no rough match is found // default to the largest texture size if no rough match is found
texture_size_index = NUM_TEXTURE_SIZES - 1; texture_size_index = NUM_TEXTURE_SIZES - 1;
@@ -68,19 +67,13 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
break; break;
} }
} }
texture_width = TEXTURE_WIDTHS[texture_size_index]; texture_width = TEXTURE_WIDTHS[texture_size_index];
texture_height = TEXTURE_HEIGHTS[texture_size_index]; texture_height = TEXTURE_HEIGHTS[texture_size_index];
try loadVolatile();
{
createTexture(); r->retain();
}
catch (love::Exception &e)
{
r->release();
throw;
}
} }
Font::~Font() Font::~Font()
@@ -92,10 +85,10 @@ Font::~Font()
bool Font::initializeTexture(GLint format) bool Font::initializeTexture(GLint format)
{ {
GLint internalformat = (format == GL_LUMINANCE_ALPHA) ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8; GLint internalformat = (format == GL_LUMINANCE_ALPHA) ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8;
// clear errors before initializing // clear errors before initializing
while (glGetError() != GL_NO_ERROR); while (glGetError() != GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D, glTexImage2D(GL_TEXTURE_2D,
0, 0,
internalformat, internalformat,
@@ -105,50 +98,50 @@ bool Font::initializeTexture(GLint format)
format, format,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
NULL); NULL);
return glGetError() == GL_NO_ERROR; 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);
bindTexture(t); bindTexture(t);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
(filter.mag == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
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_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);
// try to initialize the texture, attempting smaller sizes if initialization fails // try to initialize the texture, attempting smaller sizes if initialization fails
bool initialized = false; bool initialized = false;
while (texture_size_index >= 0) while (texture_size_index >= 0)
{ {
texture_width = TEXTURE_WIDTHS[texture_size_index]; texture_width = TEXTURE_WIDTHS[texture_size_index];
texture_height = TEXTURE_HEIGHTS[texture_size_index]; texture_height = TEXTURE_HEIGHTS[texture_size_index];
initialized = initializeTexture(format); initialized = initializeTexture(format);
if (initialized || texture_size_index <= 0) if (initialized || texture_size_index <= 0)
break; break;
--texture_size_index; --texture_size_index;
} }
if (!initialized) if (!initialized)
{ {
// cleanup before throwing // cleanup before throwing
deleteTexture(t); deleteTexture(t);
bindTexture(0); bindTexture(0);
textures.pop_back(); textures.pop_back();
throw love::Exception("Could not create font texture!"); throw love::Exception("Could not create font texture!");
} }
@@ -162,6 +155,9 @@ void Font::createTexture()
format, format,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
&emptyData[0]); &emptyData[0]);
setFilter(filter);
setMipmapSharpness(mipmapsharpness);
} }
Font::Glyph *Font::addGlyph(const int glyph) Font::Glyph *Font::addGlyph(const int glyph)
@@ -184,17 +180,17 @@ Font::Glyph *Font::addGlyph(const int glyph)
} }
Glyph *g = new Glyph; Glyph *g = new Glyph;
g->texture = 0; g->texture = 0;
g->spacing = gd->getAdvance(); g->spacing = gd->getAdvance();
memset(&g->quad, 0, sizeof(GlyphQuad)); 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)
{ {
const GLuint t = textures.back(); const GLuint t = textures.back();
bindTexture(t); bindTexture(t);
glTexSubImage2D(GL_TEXTURE_2D, glTexSubImage2D(GL_TEXTURE_2D,
0, 0,
@@ -212,10 +208,10 @@ Font::Glyph *Font::addGlyph(const 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 = Quad(v, (const float) texture_width, (const float) texture_height); Quad q = Quad(v, (const float) texture_width, (const float) texture_height);
const vertex *verts = q.getVertices(); const vertex *verts = q.getVertices();
// 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++)
{ {
@@ -231,9 +227,9 @@ Font::Glyph *Font::addGlyph(const 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; return g;
} }
@@ -242,7 +238,7 @@ Font::Glyph *Font::findGlyph(const int glyph)
Glyph *g = glyphs[glyph]; Glyph *g = glyphs[glyph];
if (!g) if (!g)
g = addGlyph(glyph); g = addGlyph(glyph);
return g; return g;
} }
@@ -255,15 +251,15 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing
{ {
float dx = 0.0f; // spacing counter for newline handling float dx = 0.0f; // spacing counter for newline handling
float dy = 0.0f; float dy = 0.0f;
// keeps track of when we need to switch textures in our vertex array // 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; std::vector<GlyphQuad> glyphquads;
glyphquads.reserve(text.size()); // pre-allocate space for the maximum possible number of quads glyphquads.reserve(text.size()); // pre-allocate space for the maximum possible number of quads
int quadindex = 0; int quadindex = 0;
glPushMatrix(); glPushMatrix();
Matrix t; Matrix t;
@@ -274,11 +270,11 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing
{ {
utf8::iterator<std::string::const_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::const_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
@@ -286,27 +282,27 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing
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 // 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 glyphquad (4 vertices) from original glyph to our current quad list
glyphquads.push_back(glyph->quad); glyphquads.push_back(glyph->quad);
// 1.25 is magic line height for true type fonts // 1.25 is magic line height for true type fonts
float lineheight = (type == FONT_TRUETYPE) ? floor(getHeight() / 1.25f + 0.5f) : 0.0f; float lineheight = (type == FONT_TRUETYPE) ? floor(getHeight() / 1.25f + 0.5f) : 0.0f;
// set proper relative position // set proper relative position
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
glyphquads[quadindex].vertices[i].x += dx; glyphquads[quadindex].vertices[i].x += dx;
glyphquads[quadindex].vertices[i].y += dy + lineheight; glyphquads[quadindex].vertices[i].y += dy + lineheight;
} }
size_t listsize = glyphinfolist.size(); size_t listsize = glyphinfolist.size();
// check if current glyph texture has changed since the previous iteration // check if current glyph texture has changed since the previous iteration
if (listsize == 0 || glyphinfolist[listsize-1].texture != glyph->texture) if (listsize == 0 || glyphinfolist[listsize-1].texture != glyph->texture)
{ {
@@ -317,16 +313,16 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing
glyphdrawinfo.texture = glyph->texture; glyphdrawinfo.texture = glyph->texture;
glyphinfolist.push_back(glyphdrawinfo); glyphinfolist.push_back(glyphdrawinfo);
} }
++quadindex; ++quadindex;
++glyphinfolist[glyphinfolist.size()-1].numquads; ++glyphinfolist[glyphinfolist.size()-1].numquads;
} }
// 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 &e) catch (love::Exception &)
{ {
glPopMatrix(); glPopMatrix();
throw; throw;
@@ -338,32 +334,32 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing
} }
if (quadindex > 0 && glyphinfolist.size() > 0) 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) // 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()); std::sort(glyphinfolist.begin(), glyphinfolist.end());
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].x); glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].s); 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 // 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; std::vector<GlyphArrayDrawInfo>::const_iterator it;
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it) for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
{ {
bindTexture(it->texture); bindTexture(it->texture);
int startvertex = it->startquad * 4; int startvertex = it->startquad * 4;
int numvertices = it->numquads * 4; int numvertices = it->numquads * 4;
glDrawArrays(GL_QUADS, startvertex, numvertices); glDrawArrays(GL_QUADS, startvertex, numvertices);
} }
glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
} }
glPopMatrix(); glPopMatrix();
} }
@@ -493,8 +489,85 @@ float Font::getSpacing() const
return mSpacing; return mSpacing;
} }
void Font::checkMipmapsCreated() const
{
if (filter.mipmap != Image::FILTER_NEAREST && filter.mipmap != Image::FILTER_LINEAR)
return;
if (!Image::hasMipmapSupport())
throw love::Exception("Mipmap filtering is not supported on this system!");
GLboolean mipmapscreated;
glGetTexParameteriv(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLint *)&mipmapscreated);
// generate mipmaps for this image if we haven't already
if (!mipmapscreated)
{
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
glGenerateMipmap(GL_TEXTURE_2D);
else if (GLEE_EXT_framebuffer_object)
glGenerateMipmapEXT(GL_TEXTURE_2D);
else
{
// modify single texel to trigger mipmap chain generation
std::vector<GLubyte> emptydata(type == FONT_TRUETYPE ? 2 : 4);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
1, 1,
type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA,
GL_UNSIGNED_BYTE,
&emptydata[0]);
}
}
}
void Font::setFilter(const Image::Filter &f)
{
filter = f;
std::vector<GLuint>::const_iterator it;
for (it = textures.begin(); it != textures.end(); ++it)
{
bindTexture(*it);
checkMipmapsCreated();
setTextureFilter(f);
}
}
const Image::Filter &Font::getFilter()
{
return filter;
}
void Font::setMipmapSharpness(float sharpness)
{
if (!Image::hasMipmapSharpnessSupport())
return;
// LOD bias has the range (-maxbias, maxbias)
mipmapsharpness = std::min(std::max(sharpness, -maxmipmapsharpness + 0.01f), maxmipmapsharpness - 0.01f);
std::vector<GLuint>::const_iterator it;
for (it = textures.begin(); it != textures.end(); ++it)
{
bindTexture(*it);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapsharpness); // negative bias is sharper
}
}
float Font::getMipmapSharpness() const
{
return mipmapsharpness;
}
bool Font::loadVolatile() bool Font::loadVolatile()
{ {
if (Image::hasMipmapSharpnessSupport())
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness);
createTexture(); createTexture();
return true; return true;
} }
+21 -7
View File
@@ -49,7 +49,7 @@ public:
* *
* @param data The font data to construct from. * @param data The font data to construct from.
**/ **/
Font(love::font::Rasterizer *r, const Image::Filter &filter = Image::Filter()); Font(love::font::Rasterizer *r, const Image::Filter &filter = Image::getDefaultFilter());
virtual ~Font(); virtual ~Font();
@@ -126,10 +126,16 @@ public:
**/ **/
float getSpacing() const; float getSpacing() const;
void setFilter(const Image::Filter &f);
const Image::Filter &getFilter();
void setMipmapSharpness(float sharpness);
float getMipmapSharpness() const;
// Implements Volatile. // Implements Volatile.
bool loadVolatile(); bool loadVolatile();
void unloadVolatile(); void unloadVolatile();
private: private:
enum FontType enum FontType
@@ -151,13 +157,13 @@ private:
int spacing; int spacing;
GlyphQuad quad; GlyphQuad quad;
}; };
// 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 startquad, numquads;
// 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
bool operator < (const GlyphArrayDrawInfo &other) const bool operator < (const GlyphArrayDrawInfo &other) const
@@ -174,16 +180,16 @@ 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_size_index;
int texture_width; int texture_width;
int texture_height; 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 quad information 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 NUM_TEXTURE_SIZES = 7;
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES]; static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES]; static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
@@ -193,6 +199,14 @@ private:
int texture_x, texture_y; int texture_x, texture_y;
int rowHeight; int rowHeight;
// Mipmap texture LOD bias value
float mipmapsharpness;
// Implementation-dependent maximum/minimum mipmap sharpness values
float maxmipmapsharpness;
void checkMipmapsCreated() const;
bool initializeTexture(GLint format); bool initializeTexture(GLint format);
void createTexture(); void createTexture();
Glyph *addGlyph(const int glyph); Glyph *addGlyph(const int glyph);
+7 -7
View File
@@ -339,10 +339,10 @@ Image *Graphics::newImage(love::image::ImageData *data)
{ {
success = image->load(); success = image->load();
} }
catch(love::Exception &e) catch(love::Exception &)
{ {
image->release(); image->release();
throw love::Exception(e.what()); throw;
} }
if (!success) if (!success)
{ {
@@ -567,11 +567,6 @@ void Graphics::setColorMode(Graphics::ColorMode mode)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
} }
void Graphics::setDefaultImageFilter(const Image::Filter &f)
{
Image::setDefaultFilter(f);
}
Graphics::BlendMode Graphics::getBlendMode() Graphics::BlendMode Graphics::getBlendMode()
{ {
GLint dst, src, equation; GLint dst, src, equation;
@@ -606,6 +601,11 @@ Graphics::ColorMode Graphics::getColorMode()
return COLOR_REPLACE; return COLOR_REPLACE;
} }
void Graphics::setDefaultImageFilter(const Image::Filter &f)
{
Image::setDefaultFilter(f);
}
const Image::Filter &Graphics::getDefaultImageFilter() const const Image::Filter &Graphics::getDefaultImageFilter() const
{ {
return Image::getDefaultFilter(); return Image::getDefaultFilter();
+77 -134
View File
@@ -22,9 +22,9 @@
// STD // STD
#include <cstring> // For memcpy #include <cstring> // For memcpy
#include <algorithm> // for min/max
#include <iostream> #include <iostream>
using namespace std;
namespace love namespace love
{ {
@@ -33,12 +33,11 @@ namespace graphics
namespace opengl namespace opengl
{ {
Image::Filter Image::defaultFilter;
Image::Image(love::image::ImageData *data) Image::Image(love::image::ImageData *data)
: width((float)(data->getWidth())) : width((float)(data->getWidth()))
, height((float)(data->getHeight())) , height((float)(data->getHeight()))
, texture(0) , texture(0)
, mipmapsharpness(0.0f)
{ {
data->retain(); data->retain();
this->data = data; this->data = data;
@@ -63,7 +62,7 @@ Image::Image(love::image::ImageData *data)
vertices[3].s = 1; vertices[3].s = 1;
vertices[3].t = 0; vertices[3].t = 0;
settings.filter = defaultFilter; filter = getDefaultFilter();
} }
Image::~Image() Image::~Image()
@@ -144,143 +143,82 @@ void Image::drawq(love::graphics::Quad *quad, float x, float y, float angle, flo
drawv(t, v); drawv(t, v);
} }
void Image::checkMipmapsCreated() const
{
if (filter.mipmap != FILTER_NEAREST && filter.mipmap != FILTER_LINEAR)
return;
if (!hasMipmapSupport())
throw love::Exception("Mipmap filtering is not supported on this system!");
// some old GPUs/systems claim support for NPOT textures, but fail when generating mipmaps
// we can't detect which systems will do this, so we fail gracefully for all NPOT images
int w = int(width), h = int(height);
if (w != next_p2(w) || h != next_p2(h))
throw love::Exception("Could not generate mipmaps: image does not have power of two dimensions!");
bind();
GLboolean mipmapscreated;
glGetTexParameteriv(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLint *)&mipmapscreated);
// generate mipmaps for this image if we haven't already
if (!mipmapscreated)
{
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
glGenerateMipmap(GL_TEXTURE_2D);
else if (GLEE_EXT_framebuffer_object)
glGenerateMipmapEXT(GL_TEXTURE_2D);
else
// modify single texel to trigger mipmap chain generation
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data->getData());
}
}
void Image::setFilter(const Image::Filter &f) void Image::setFilter(const Image::Filter &f)
{ {
GLint gmin, gmag; filter = f;
gmin = gmag = 0; // so that they're not used uninitialized
switch (f.min)
{
case FILTER_LINEAR:
gmin = GL_LINEAR;
break;
case FILTER_NEAREST:
gmin = GL_NEAREST;
break;
default:
break;
}
switch (f.mag)
{
case FILTER_LINEAR:
gmag = GL_LINEAR;
break;
case FILTER_NEAREST:
gmag = GL_NEAREST;
break;
default:
break;
}
bind(); bind();
checkMipmapsCreated();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin); setTextureFilter(f);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
} }
Image::Filter Image::getFilter() const const Image::Filter &Image::getFilter() const
{ {
bind(); return filter;
GLint gmin, gmag;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
Image::Filter f;
switch (gmin)
{
case GL_NEAREST:
f.min = FILTER_NEAREST;
break;
case GL_LINEAR:
default:
f.min = FILTER_LINEAR;
break;
}
switch (gmin)
{
case GL_NEAREST:
f.mag = FILTER_NEAREST;
break;
case GL_LINEAR:
default:
f.mag = FILTER_LINEAR;
break;
}
return f;
} }
void Image::setWrap(Image::Wrap w) void Image::setWrap(const Image::Wrap &w)
{ {
GLint gs, gt; wrap = w;
switch (w.s)
{
case WRAP_CLAMP:
gs = GL_CLAMP_TO_EDGE;
break;
case WRAP_REPEAT:
default:
gs = GL_REPEAT;
break;
}
switch (w.t)
{
case WRAP_CLAMP:
gt = GL_CLAMP_TO_EDGE;
break;
case WRAP_REPEAT:
default:
gt = GL_REPEAT;
break;
}
bind(); bind();
setTextureWrap(w);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
} }
Image::Wrap Image::getWrap() const const Image::Wrap &Image::getWrap() const
{ {
return wrap;
}
void Image::setMipmapSharpness(float sharpness)
{
if (!hasMipmapSharpnessSupport())
return;
// LOD bias has the range (-maxbias, maxbias)
mipmapsharpness = std::min(std::max(sharpness, -maxmipmapsharpness + 0.01f), maxmipmapsharpness - 0.01f);
bind(); bind();
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapsharpness); // negative bias is sharper
}
GLint gs, gt; float Image::getMipmapSharpness() const
{
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs); return mipmapsharpness;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
Wrap w;
switch (gs)
{
case GL_CLAMP_TO_EDGE:
w.s = WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.s = WRAP_REPEAT;
break;
}
switch (gt)
{
case GL_CLAMP_TO_EDGE:
w.t = WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.t = WRAP_REPEAT;
break;
}
return w;
} }
void Image::bind() const void Image::bind() const
@@ -303,6 +241,9 @@ void Image::unload()
bool Image::loadVolatile() bool Image::loadVolatile()
{ {
if (hasMipmapSharpnessSupport())
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness);
if (hasNpot()) if (hasNpot())
return loadVolatileNPOT(); return loadVolatileNPOT();
else else
@@ -313,6 +254,7 @@ bool Image::loadVolatilePOT()
{ {
glGenTextures(1,(GLuint *)&texture); glGenTextures(1,(GLuint *)&texture);
bindTexture(texture); bindTexture(texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -349,8 +291,9 @@ bool Image::loadVolatilePOT()
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
data->getData()); data->getData());
setFilter(settings.filter); setMipmapSharpness(mipmapsharpness);
setWrap(settings.wrap); setFilter(filter);
setWrap(wrap);
return true; return true;
} }
@@ -359,6 +302,7 @@ bool Image::loadVolatileNPOT()
{ {
glGenTextures(1,(GLuint *)&texture); glGenTextures(1,(GLuint *)&texture);
bindTexture(texture); bindTexture(texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -375,16 +319,15 @@ bool Image::loadVolatileNPOT()
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
data->getData()); data->getData());
setFilter(settings.filter); setMipmapSharpness(mipmapsharpness);
setWrap(settings.wrap); setFilter(filter);
setWrap(wrap);
return true; return true;
} }
void Image::unloadVolatile() void Image::unloadVolatile()
{ {
settings.filter = getFilter();
settings.wrap = getWrap();
// Delete the hardware texture. // Delete the hardware texture.
if (texture != 0) if (texture != 0)
{ {
@@ -417,14 +360,14 @@ bool Image::hasNpot()
return GLEE_ARB_texture_non_power_of_two != 0; return GLEE_ARB_texture_non_power_of_two != 0;
} }
void Image::setDefaultFilter(const Image::Filter &f) bool Image::hasMipmapSupport()
{ {
defaultFilter = f; return (GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap) != 0;
} }
const Image::Filter &Image::getDefaultFilter() bool Image::hasMipmapSharpnessSupport()
{ {
return defaultFilter; return (GLEE_VERSION_1_4 || GLEE_EXT_texture_lod_bias) != 0;
} }
} // opengl } // opengl
+22 -17
View File
@@ -96,16 +96,19 @@ public:
/** /**
* Sets the filter mode. * Sets the filter mode.
* * @param f The filter mode.
* @param mode The filter mode.
**/ **/
void setFilter(const Image::Filter &f); void setFilter(const Image::Filter &f);
Image::Filter getFilter() const; const Image::Filter &getFilter() const;
void setWrap(Image::Wrap r); void setWrap(const Image::Wrap &w);
Image::Wrap getWrap() const; const Image::Wrap &getWrap() const;
void setMipmapSharpness(float sharpness);
float getMipmapSharpness() const;
void bind() const; void bind() const;
@@ -117,10 +120,8 @@ public:
void unloadVolatile(); void unloadVolatile();
static bool hasNpot(); static bool hasNpot();
static bool hasMipmapSupport();
// The default filter. static bool hasMipmapSharpnessSupport();
static void setDefaultFilter(const Image::Filter &f);
static const Image::Filter &getDefaultFilter();
private: private:
@@ -143,18 +144,22 @@ private:
// The source vertices of the image. // The source vertices of the image.
vertex vertices[4]; vertex vertices[4];
// The settings we need to save when reloading. // Mipmap texture LOD bias value
struct float mipmapsharpness;
{
Image::Filter filter; // Implementation-dependent maximum/minimum mipmap sharpness values
Image::Wrap wrap; float maxmipmapsharpness;
} settings;
// The image's filter mode
Image::Filter filter;
// The image's wrap mode
Image::Wrap wrap;
bool loadVolatilePOT(); bool loadVolatilePOT();
bool loadVolatileNPOT(); bool loadVolatileNPOT();
// The default image filter void checkMipmapsCreated() const;
static Image::Filter defaultFilter;
}; // Image }; // Image
+154
View File
@@ -52,6 +52,160 @@ void deleteTexture(GLuint texture)
glDeleteTextures(1, &texture); glDeleteTextures(1, &texture);
} }
void setTextureFilter(const graphics::Image::Filter &f)
{
GLint gmin, gmag;
if (f.mipmap == Image::FILTER_NONE)
{
if (f.min == Image::FILTER_NEAREST)
gmin = GL_NEAREST;
else // f.min == Image::FILTER_LINEAR
gmin = GL_LINEAR;
}
else
{
if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_NEAREST)
gmin = GL_NEAREST_MIPMAP_NEAREST;
else if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_LINEAR)
gmin = GL_NEAREST_MIPMAP_LINEAR;
else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_NEAREST)
gmin = GL_LINEAR_MIPMAP_NEAREST;
else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_LINEAR)
gmin = GL_LINEAR_MIPMAP_LINEAR;
else
gmin = GL_LINEAR;
}
switch (f.mag)
{
case Image::FILTER_NEAREST:
gmag = GL_NEAREST;
break;
case Image::FILTER_LINEAR:
default:
gmag = GL_LINEAR;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
}
graphics::Image::Filter getTextureFilter()
{
GLint gmin, gmag;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
Image::Filter f;
switch (gmin)
{
case GL_NEAREST:
f.min = Image::FILTER_NEAREST;
f.mipmap = Image::FILTER_NONE;
break;
case GL_NEAREST_MIPMAP_NEAREST:
f.min = f.mipmap = Image::FILTER_NEAREST;
break;
case GL_NEAREST_MIPMAP_LINEAR:
f.min = Image::FILTER_NEAREST;
f.mipmap = Image::FILTER_LINEAR;
break;
case GL_LINEAR_MIPMAP_NEAREST:
f.min = Image::FILTER_LINEAR;
f.mipmap = Image::FILTER_NEAREST;
break;
case GL_LINEAR_MIPMAP_LINEAR:
f.min = f.mipmap = Image::FILTER_LINEAR;
break;
case GL_LINEAR:
default:
f.min = Image::FILTER_LINEAR;
f.mipmap = Image::FILTER_NONE;
break;
}
switch (gmag)
{
case GL_NEAREST:
f.mag = Image::FILTER_NEAREST;
break;
case GL_LINEAR:
default:
f.mag = Image::FILTER_LINEAR;
break;
}
return f;
}
void setTextureWrap(const graphics::Image::Wrap &w)
{
GLint gs, gt;
switch (w.s)
{
case Image::WRAP_CLAMP:
gs = GL_CLAMP_TO_EDGE;
break;
case Image::WRAP_REPEAT:
default:
gs = GL_REPEAT;
break;
}
switch (w.t)
{
case Image::WRAP_CLAMP:
gt = GL_CLAMP_TO_EDGE;
break;
case Image::WRAP_REPEAT:
default:
gt = GL_REPEAT;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
}
graphics::Image::Wrap getTextureWrap()
{
GLint gs, gt;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
Image::Wrap w;
switch (gs)
{
case GL_CLAMP_TO_EDGE:
w.s = Image::WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.s = Image::WRAP_REPEAT;
break;
}
switch (gt)
{
case GL_CLAMP_TO_EDGE:
w.t = Image::WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.t = Image::WRAP_REPEAT;
break;
}
return w;
}
} // opengl } // opengl
} // graphics } // graphics
} // love } // love
+23
View File
@@ -22,6 +22,7 @@
#define LOVE_COMMON_OPENGL_H #define LOVE_COMMON_OPENGL_H
#include "GLee.h" #include "GLee.h"
#include "graphics/Image.h"
namespace love namespace love
{ {
@@ -48,6 +49,28 @@ void bindTexture(GLuint texture, bool override = false);
**/ **/
void deleteTexture(GLuint texture); void deleteTexture(GLuint texture);
/**
* Sets the image filter mode for the currently bound texture
* @param f The image filter to set
*/
void setTextureFilter(const graphics::Image::Filter &f);
/**
* Returns the image filter mode for the currently bound texture
*/
graphics::Image::Filter getTextureFilter();
/**
* Sets the image wrap mode for the currently bound texture
* @param w The wrap mode to set
*/
void setTextureWrap(const graphics::Image::Wrap &w);
/**
* Returns the image wrap mode for the currently bound texture
*/
graphics::Image::Wrap getTextureWrap();
} // opengl } // opengl
} // graphics } // graphics
} // love } // love
+17 -16
View File
@@ -105,7 +105,7 @@ ParticleSystem::~ParticleSystem()
if (pStart != 0) if (pStart != 0)
delete [] pStart; delete [] pStart;
if (particleVerts != 0) if (particleVerts != 0)
delete [] particleVerts; delete [] particleVerts;
} }
@@ -207,10 +207,10 @@ void ParticleSystem::setBufferSize(unsigned int size)
pLast = pStart = new particle[size]; pLast = pStart = new particle[size];
pEnd = pStart + size; pEnd = pStart + size;
if (particleVerts != 0) if (particleVerts != 0)
delete [] particleVerts; delete [] particleVerts;
// each particle has 4 vertices // each particle has 4 vertices
particleVerts = new vertex[size*4]; particleVerts = new vertex[size*4];
} }
@@ -468,7 +468,7 @@ bool ParticleSystem::isFull() const
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{ {
if (sprite == 0) return; // just in case of failure if (sprite == 0) return; // just in case of failure
int numParticles = count(); int numParticles = count();
if (numParticles == 0) return; // don't bother if there's nothing to do if (numParticles == 0) return; // don't bother if there's nothing to do
@@ -478,25 +478,26 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
static Matrix t; static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
glMultMatrixf((const GLfloat *)t.getElements()); glMultMatrixf((const GLfloat *)t.getElements());
const vertex * imageVerts = sprite->getVertices(); const vertex * imageVerts = sprite->getVertices();
// set the vertex data for each particle (transformation, texcoords, color) // set the vertex data for each particle (transformation, texcoords, color)
for (int i = 0; i < numParticles; i++) for (int i = 0; i < numParticles; i++)
{ {
particle * p = pStart + i; particle * p = pStart + i;
// particle vertices are sprite vertices transformed by particle information // particle vertices are sprite vertices transformed by particle information
t.setTransformation(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f); t.setTransformation(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
t.transform(&particleVerts[i*4], &imageVerts[0], 4); t.transform(&particleVerts[i*4], &imageVerts[0], 4);
// set the texture coordinate and color data for particle vertices // set the texture coordinate and color data for particle vertices
for (int v = 0; v < 4; v++) { for (int v = 0; v < 4; v++)
{
int vi = (i * 4) + v; // current vertex index for particle int vi = (i * 4) + v; // current vertex index for particle
particleVerts[vi].s = imageVerts[v].s; particleVerts[vi].s = imageVerts[v].s;
particleVerts[vi].t = imageVerts[v].t; particleVerts[vi].t = imageVerts[v].t;
// particle colors are stored as floats (0-1) but vertex colors are stored as unsigned bytes (0-255) // particle colors are stored as floats (0-1) but vertex colors are stored as unsigned bytes (0-255)
particleVerts[vi].r = p->color.r*255; particleVerts[vi].r = p->color.r*255;
particleVerts[vi].g = p->color.g*255; particleVerts[vi].g = p->color.g*255;
@@ -504,19 +505,19 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
particleVerts[vi].a = p->color.a*255; particleVerts[vi].a = p->color.a*255;
} }
} }
sprite->bind(); sprite->bind();
glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), (GLvoid *)&particleVerts[0].r); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), (GLvoid *)&particleVerts[0].r);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&particleVerts[0].x); glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&particleVerts[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&particleVerts[0].s); glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&particleVerts[0].s);
glDrawArrays(GL_QUADS, 0, numParticles*4); glDrawArrays(GL_QUADS, 0, numParticles*4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
+1 -1
View File
@@ -406,7 +406,7 @@ protected:
// Pointer to the end of the memory allocation. // Pointer to the end of the memory allocation.
particle *pEnd; particle *pEnd;
// array of transformed vertex data for all particles, for drawing // array of transformed vertex data for all particles, for drawing
vertex * particleVerts; vertex * particleVerts;
+19 -12
View File
@@ -67,23 +67,27 @@ int w_Canvas_getImageData(lua_State *L)
int w_Canvas_setFilter(lua_State *L) int w_Canvas_setFilter(lua_State *L)
{ {
Canvas *canvas = luax_checkcanvas(L, 1); Canvas *canvas = luax_checkcanvas(L, 1);
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_checkstring(L, 3);
Image::Filter f; Image::Filter f;
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min)) if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid min filter mode: %s", minstr); return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag)) if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid max filter mode: %s", magstr); return luaL_error(L, "Invalid filter mode: %s", magstr);
canvas->setFilter(f); canvas->setFilter(f);
return 0; return 0;
} }
int w_Canvas_getFilter(lua_State *L) int w_Canvas_getFilter(lua_State *L)
{ {
Canvas *canvas = luax_checkcanvas(L, 1); Canvas *canvas = luax_checkcanvas(L, 1);
Image::Filter f = canvas->getFilter(); const Image::Filter f = canvas->getFilter();
const char *minstr; const char *minstr;
const char *magstr; const char *magstr;
@@ -99,23 +103,26 @@ int w_Canvas_getFilter(lua_State *L)
int w_Canvas_setWrap(lua_State *L) int w_Canvas_setWrap(lua_State *L)
{ {
Canvas *canvas = luax_checkcanvas(L, 1); Canvas *canvas = luax_checkcanvas(L, 1);
const char *wrap_s = luaL_checkstring(L, 2);
const char *wrap_t = luaL_checkstring(L, 3);
Image::Wrap w; Image::Wrap w;
if (!Image::getConstant(wrap_s, w.s))
return luaL_error(L, "Invalid wrap mode: %s", wrap_s); const char *sstr = luaL_checkstring(L, 2);
if (!Image::getConstant(wrap_t, w.t)) const char *tstr = luaL_optstring(L, 3, sstr);
return luaL_error(L, "Invalid wrap mode: %s", wrap_t);
if (!Image::getConstant(sstr, w.s))
return luaL_error(L, "Invalid wrap mode: %s", sstr);
if (!Image::getConstant(tstr, w.t))
return luaL_error(L, "Invalid wrap mode, %s", tstr);
canvas->setWrap(w); canvas->setWrap(w);
return 0; return 0;
} }
int w_Canvas_getWrap(lua_State *L) int w_Canvas_getWrap(lua_State *L)
{ {
Canvas *canvas = luax_checkcanvas(L, 1); Canvas *canvas = luax_checkcanvas(L, 1);
Image::Wrap w = canvas->getWrap(); const Image::Wrap w = canvas->getWrap();
const char *wrap_s; const char *wrap_s;
const char *wrap_t; const char *wrap_t;
+76
View File
@@ -90,6 +90,78 @@ int w_Font_getLineHeight(lua_State *L)
return 1; return 1;
} }
int w_Font_setFilter(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
Image::Filter f;
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr);
if (lua_isnoneornil(L, 4))
f.mipmap = Image::FILTER_NONE; // mipmapping is disabled unless third argument is given
else
{
const char *mipmapstr = luaL_checkstring(L, 4);
if (!Image::getConstant(mipmapstr, f.mipmap))
return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
}
try
{
t->setFilter(f);
}
catch(love::Exception &e)
{
return luaL_error(L, "%s", e.what());
}
return 0;
}
int w_Font_getFilter(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const Image::Filter f = t->getFilter();
const char *minstr;
const char *magstr;
Image::getConstant(f.min, minstr);
Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr);
lua_pushstring(L, magstr);
const char *mipmapstr;
if (Image::getConstant(f.mipmap, mipmapstr))
lua_pushstring(L, mipmapstr);
else
lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
return 3;
}
int w_Font_setMipmapSharpness(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
float sharpness = (float) luaL_checknumber(L, 2);
t->setMipmapSharpness(sharpness);
return 0;
}
int w_Font_getMipmapSharpness(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getMipmapSharpness());
return 1;
}
static const luaL_Reg functions[] = static const luaL_Reg functions[] =
{ {
{ "getHeight", w_Font_getHeight }, { "getHeight", w_Font_getHeight },
@@ -97,6 +169,10 @@ static const luaL_Reg functions[] =
{ "getWrap", w_Font_getWrap }, { "getWrap", w_Font_getWrap },
{ "setLineHeight", w_Font_setLineHeight }, { "setLineHeight", w_Font_setLineHeight },
{ "getLineHeight", w_Font_getLineHeight }, { "getLineHeight", w_Font_getLineHeight },
{ "setFilter", w_Font_setFilter },
{ "getFilter", w_Font_getFilter },
{ "setMipmapSharpness", w_Font_setMipmapSharpness },
{ "getMipmapSharpness", w_Font_getMipmapSharpness },
{ 0, 0 } { 0, 0 }
}; };
+4
View File
@@ -38,6 +38,10 @@ int w_Font_getWidth(lua_State *L);
int w_Font_getWrap(lua_State *L); int w_Font_getWrap(lua_State *L);
int w_Font_setLineHeight(lua_State *L); int w_Font_setLineHeight(lua_State *L);
int w_Font_getLineHeight(lua_State *L); int w_Font_getLineHeight(lua_State *L);
int w_Font_setFilter(lua_State *L);
int w_Font_getFilter(lua_State *L);
int w_Font_setMipmapSharpness(lua_State *L);
int w_Font_getMipmapSharpness(lua_State *L);
extern "C" int luaopen_font(lua_State *L); extern "C" int luaopen_font(lua_State *L);
} // opengl } // opengl
@@ -601,8 +601,10 @@ int w_setDefaultImageFilter(lua_State *L)
{ {
Image::FilterMode min; Image::FilterMode min;
Image::FilterMode mag; Image::FilterMode mag;
const char *minstr = luaL_checkstring(L, 1); const char *minstr = luaL_checkstring(L, 1);
const char *magstr = luaL_optstring(L, 2, minstr); const char *magstr = luaL_optstring(L, 2, minstr);
if (!Image::getConstant(minstr, min)) if (!Image::getConstant(minstr, min))
return luaL_error(L, "Invalid filter mode: %s", minstr); return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, mag)) if (!Image::getConstant(magstr, mag))
@@ -611,6 +613,7 @@ int w_setDefaultImageFilter(lua_State *L)
Image::Filter f; Image::Filter f;
f.min = min; f.min = min;
f.mag = mag; f.mag = mag;
instance->setDefaultImageFilter(f); instance->setDefaultImageFilter(f);
return 0; return 0;
@@ -850,7 +853,12 @@ int w_isSupported(lua_State *L)
supported = false; supported = false;
break; break;
case Graphics::SUPPORT_SUBTRACTIVE: case Graphics::SUPPORT_SUBTRACTIVE:
supported = (GLEE_VERSION_1_4 || GLEE_ARB_imaging) || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract); if (!((GLEE_VERSION_1_4 || GLEE_ARB_imaging) || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)))
supported = false;
break;
case Graphics::SUPPORT_MIPMAP:
if (!Image::hasMipmapSupport())
supported = false;
break; break;
default: default:
supported = false; supported = false;
+62 -24
View File
@@ -50,71 +50,107 @@ int w_Image_getHeight(lua_State *L)
int w_Image_setFilter(lua_State *L) int w_Image_setFilter(lua_State *L)
{ {
Image *t = luax_checkimage(L, 1); Image *t = luax_checkimage(L, 1);
Image::Filter f; Image::Filter f;
Image::FilterMode min;
Image::FilterMode mag;
const char *minstr = luaL_checkstring(L, 2); const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr); const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, min))
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr); return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, mag)) if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr); return luaL_error(L, "Invalid filter mode: %s", magstr);
f.min = min; if (lua_isnoneornil(L, 4))
f.mag = mag; f.mipmap = Image::FILTER_NONE; // mipmapping is disabled unless third argument is given
t->setFilter(f); else
{
const char *mipmapstr = luaL_checkstring(L, 4);
if (!Image::getConstant(mipmapstr, f.mipmap))
return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
}
try
{
t->setFilter(f);
}
catch(love::Exception &e)
{
return luaL_error(L, "%s", e.what());
}
return 0; return 0;
} }
int w_Image_getFilter(lua_State *L) int w_Image_getFilter(lua_State *L)
{ {
Image *t = luax_checkimage(L, 1); Image *t = luax_checkimage(L, 1);
Image::Filter f = t->getFilter(); const Image::Filter f = t->getFilter();
Image::FilterMode min = f.min;
Image::FilterMode mag = f.mag;
const char *minstr; const char *minstr;
const char *magstr; const char *magstr;
Image::getConstant(min, minstr); Image::getConstant(f.min, minstr);
Image::getConstant(mag, magstr); Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr); lua_pushstring(L, minstr);
lua_pushstring(L, magstr); lua_pushstring(L, magstr);
return 2;
const char *mipmapstr;
if (Image::getConstant(f.mipmap, mipmapstr))
lua_pushstring(L, mipmapstr);
else
lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
return 3;
} }
int w_Image_setWrap(lua_State *L) int w_Image_setWrap(lua_State *L)
{ {
Image *i = luax_checkimage(L, 1); Image *i = luax_checkimage(L, 1);
Image::Wrap w; Image::Wrap w;
Image::WrapMode s;
Image::WrapMode t;
const char *sstr = luaL_checkstring(L, 2); const char *sstr = luaL_checkstring(L, 2);
const char *tstr = luaL_optstring(L, 3, sstr); const char *tstr = luaL_optstring(L, 3, sstr);
if (!Image::getConstant(sstr, s))
if (!Image::getConstant(sstr, w.s))
return luaL_error(L, "Invalid wrap mode: %s", sstr); return luaL_error(L, "Invalid wrap mode: %s", sstr);
if (!Image::getConstant(tstr, t)) if (!Image::getConstant(tstr, w.t))
return luaL_error(L, "Invalid wrap mode, %s", tstr); return luaL_error(L, "Invalid wrap mode, %s", tstr);
w.s = s;
w.t = t;
i->setWrap(w); i->setWrap(w);
return 0; return 0;
} }
int w_Image_getWrap(lua_State *L) int w_Image_getWrap(lua_State *L)
{ {
Image *i = luax_checkimage(L, 1); Image *i = luax_checkimage(L, 1);
Image::Wrap w = i->getWrap(); const Image::Wrap w = i->getWrap();
Image::WrapMode s = w.s;
Image::WrapMode t = w.t;
const char *sstr; const char *sstr;
const char *tstr; const char *tstr;
Image::getConstant(s, sstr); Image::getConstant(w.s, sstr);
Image::getConstant(t, tstr); Image::getConstant(w.t, tstr);
lua_pushstring(L, sstr); lua_pushstring(L, sstr);
lua_pushstring(L, tstr); lua_pushstring(L, tstr);
return 2; return 2;
} }
int w_Image_setMipmapSharpness(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
float sharpness = (float) luaL_checknumber(L, 2);
i->setMipmapSharpness(sharpness);
return 0;
}
int w_Image_getMipmapSharpness(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
lua_pushnumber(L, i->getMipmapSharpness());
return 1;
}
static const luaL_Reg functions[] = static const luaL_Reg functions[] =
{ {
{ "getWidth", w_Image_getWidth }, { "getWidth", w_Image_getWidth },
@@ -123,6 +159,8 @@ static const luaL_Reg functions[] =
{ "getFilter", w_Image_getFilter }, { "getFilter", w_Image_getFilter },
{ "setWrap", w_Image_setWrap }, { "setWrap", w_Image_setWrap },
{ "getWrap", w_Image_getWrap }, { "getWrap", w_Image_getWrap },
{ "setMipmapSharpness", w_Image_setMipmapSharpness },
{ "getMipmapSharpness", w_Image_getMipmapSharpness },
{ 0, 0 } { 0, 0 }
}; };
+5
View File
@@ -36,6 +36,11 @@ Image *luax_checkimage(lua_State *L, int idx);
int w_Image_getWidth(lua_State *L); int w_Image_getWidth(lua_State *L);
int w_Image_getHeight(lua_State *L); int w_Image_getHeight(lua_State *L);
int w_Image_setFilter(lua_State *L); int w_Image_setFilter(lua_State *L);
int w_image_getFilter(lua_State *L);
int w_Image_setWrap(lua_State *L);
int w_Image_getWrap(lua_State *L);
int w_Image_setMipmapSharpness(lua_State *L);
int w_Image_getMipmapSharpness(lua_State *L);
extern "C" int luaopen_image(lua_State *L); extern "C" int luaopen_image(lua_State *L);
} // opengl } // opengl