diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 6a8aa72a8..9be6102a7 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -161,6 +161,7 @@ StringMap::Entry Graphics::suppor { "pixeleffect", Graphics::SUPPORT_SHADEREFFECT }, // for compatibility { "npot", Graphics::SUPPORT_NPOT }, { "subtractive", Graphics::SUPPORT_SUBTRACTIVE }, + { "mipmap", Graphics::SUPPORT_MIPMAP }, }; StringMap Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries)); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 98bf1bdf1..b61d765d2 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -89,6 +89,7 @@ public: SUPPORT_SHADEREFFECT, SUPPORT_NPOT, SUPPORT_SUBTRACTIVE, + SUPPORT_MIPMAP, SUPPORT_MAX_ENUM }; diff --git a/src/modules/graphics/Image.cpp b/src/modules/graphics/Image.cpp index fa9a105aa..c985cd482 100644 --- a/src/modules/graphics/Image.cpp +++ b/src/modules/graphics/Image.cpp @@ -25,9 +25,12 @@ namespace love namespace graphics { +Image::Filter Image::defaultFilter; + Image::Filter::Filter() : min(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) { return filterModes.find(in, out); diff --git a/src/modules/graphics/Image.h b/src/modules/graphics/Image.h index 5e060616c..2c063e5db 100644 --- a/src/modules/graphics/Image.h +++ b/src/modules/graphics/Image.h @@ -46,6 +46,7 @@ public: { FILTER_LINEAR = 1, FILTER_NEAREST, + FILTER_NONE, FILTER_MAX_ENUM }; @@ -54,6 +55,7 @@ public: Filter(); FilterMode min; FilterMode mag; + FilterMode mipmap; }; struct Wrap @@ -64,6 +66,10 @@ public: }; 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(FilterMode in, const char *&out); @@ -72,6 +78,9 @@ public: private: + // The default image filter + static Filter defaultFilter; + static StringMap::Entry filterModeEntries[]; static StringMap filterModes; static StringMap::Entry wrapModeEntries[]; diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 9119df4a8..b863a4dbb 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -97,8 +97,9 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy glGenTextures(1, &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, 0, GL_RGBA, format, NULL); bindTexture(0); @@ -164,8 +165,8 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy glGenTextures(1, &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, 0, GL_RGBA, format, NULL); @@ -231,8 +232,9 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT glGenTextures(1, &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, 0, GL_RGBA, format, NULL); bindTexture(0); @@ -312,6 +314,8 @@ Canvas::Canvas(int width, int height, TextureType texture_type) vertices[3].s = 1; vertices[3].t = 0; + settings.filter = Image::getDefaultFilter(); + getStrategy(); loadVolatile(); @@ -456,51 +460,26 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image) 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); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag); + setTextureFilter(f); } Image::Filter Canvas::getFilter() const { - GLint gmin, gmag; - bindTexture(img); - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin); - 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; + return getTextureFilter(); } 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); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t); + setTextureWrap(w); } Image::Wrap Canvas::getWrap() const { - GLint wrap_s, wrap_t; bindTexture(img); - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &wrap_s); - 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; + return getTextureWrap(); } bool Canvas::loadVolatile() diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index 2473c4661..d7f511188 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -21,15 +21,15 @@ #include "Font.h" #include "font/GlyphData.h" #include "Quad.h" +#include "Image.h" #include "libraries/utf8/utf8.h" #include "common/math.h" #include "common/Matrix.h" + #include - #include - #include // for max namespace love @@ -39,8 +39,8 @@ 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}; +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) @@ -48,13 +48,12 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter) , lineHeight(1) , mSpacing(1) , filter(filter) + , mipmapsharpness(0.0f) { - r->retain(); - love::font::GlyphData *gd = r->getGlyphData(32); type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA ? FONT_TRUETYPE : FONT_IMAGE); delete gd; - + // 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; @@ -68,19 +67,13 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter) break; } } - + texture_width = TEXTURE_WIDTHS[texture_size_index]; texture_height = TEXTURE_HEIGHTS[texture_size_index]; - - try - { - createTexture(); - } - catch (love::Exception &e) - { - r->release(); - throw; - } + + loadVolatile(); + + r->retain(); } Font::~Font() @@ -92,10 +85,10 @@ Font::~Font() 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, @@ -105,50 +98,50 @@ bool Font::initializeTexture(GLint format) 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); + bindTexture(t); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - (filter.mag == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - (filter.min == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + 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); - - + // 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!"); } @@ -162,6 +155,9 @@ void Font::createTexture() format, GL_UNSIGNED_BYTE, &emptyData[0]); + + setFilter(filter); + setMipmapSharpness(mipmapsharpness); } Font::Glyph *Font::addGlyph(const int glyph) @@ -184,17 +180,17 @@ Font::Glyph *Font::addGlyph(const int glyph) } Glyph *g = new Glyph; - + 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) { const GLuint t = textures.back(); - + bindTexture(t); glTexSubImage2D(GL_TEXTURE_2D, 0, @@ -212,10 +208,10 @@ Font::Glyph *Font::addGlyph(const int glyph) v.y = (float) texture_y; v.w = (float) w; v.h = (float) h; - + 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++) { @@ -231,9 +227,9 @@ Font::Glyph *Font::addGlyph(const int glyph) rowHeight = std::max(rowHeight, h + TEXTURE_PADDING); delete gd; - + glyphs[glyph] = g; - + return g; } @@ -242,7 +238,7 @@ Font::Glyph *Font::findGlyph(const int glyph) Glyph *g = glyphs[glyph]; if (!g) g = addGlyph(glyph); - + 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 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; @@ -274,11 +270,11 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing { 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 @@ -286,27 +282,27 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing dx = 0.0f; continue; } - + 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) { @@ -317,16 +313,16 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing 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 (love::Exception &e) + catch (love::Exception &) { glPopMatrix(); 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) - { + { // 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(); } @@ -493,8 +489,85 @@ float Font::getSpacing() const 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 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::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::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() { + if (Image::hasMipmapSharpnessSupport()) + glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness); + createTexture(); return true; } diff --git a/src/modules/graphics/opengl/Font.h b/src/modules/graphics/opengl/Font.h index c8cd4fb71..73baccd9b 100644 --- a/src/modules/graphics/opengl/Font.h +++ b/src/modules/graphics/opengl/Font.h @@ -49,7 +49,7 @@ public: * * @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(); @@ -126,10 +126,16 @@ public: **/ float getSpacing() const; + void setFilter(const Image::Filter &f); + const Image::Filter &getFilter(); + + void setMipmapSharpness(float sharpness); + float getMipmapSharpness() const; + // Implements Volatile. bool loadVolatile(); void unloadVolatile(); - + private: enum FontType @@ -151,13 +157,13 @@ private: 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 @@ -174,16 +180,16 @@ 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 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]; @@ -193,6 +199,14 @@ private: int texture_x, texture_y; 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); void createTexture(); Glyph *addGlyph(const int glyph); diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index ec5c3390b..f64aeec68 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -342,10 +342,10 @@ Image *Graphics::newImage(love::image::ImageData *data) { success = image->load(); } - catch(love::Exception &e) + catch(love::Exception &) { image->release(); - throw love::Exception(e.what()); + throw; } if (!success) { @@ -571,11 +571,6 @@ void Graphics::setColorMode(Graphics::ColorMode mode) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } -void Graphics::setDefaultImageFilter(const Image::Filter &f) -{ - Image::setDefaultFilter(f); -} - Graphics::BlendMode Graphics::getBlendMode() { GLint dst, src, equation; @@ -610,6 +605,11 @@ Graphics::ColorMode Graphics::getColorMode() return COLOR_REPLACE; } +void Graphics::setDefaultImageFilter(const Image::Filter &f) +{ + Image::setDefaultFilter(f); +} + const Image::Filter &Graphics::getDefaultImageFilter() const { return Image::getDefaultFilter(); diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 79646b785..c85b1b5ba 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -22,9 +22,9 @@ // STD #include // For memcpy +#include // for min/max #include -using namespace std; namespace love { @@ -33,12 +33,11 @@ namespace graphics namespace opengl { -Image::Filter Image::defaultFilter; - Image::Image(love::image::ImageData *data) : width((float)(data->getWidth())) , height((float)(data->getHeight())) , texture(0) + , mipmapsharpness(0.0f) { data->retain(); this->data = data; @@ -63,7 +62,7 @@ Image::Image(love::image::ImageData *data) vertices[3].s = 1; vertices[3].t = 0; - settings.filter = defaultFilter; + filter = getDefaultFilter(); } Image::~Image() @@ -144,143 +143,82 @@ void Image::drawq(love::graphics::Quad *quad, float x, float y, float angle, flo 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) { - GLint gmin, gmag; - 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; - } + filter = f; bind(); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag); + checkMipmapsCreated(); + setTextureFilter(f); } -Image::Filter Image::getFilter() const +const Image::Filter &Image::getFilter() const { - bind(); - - 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; + return filter; } -void Image::setWrap(Image::Wrap w) +void Image::setWrap(const Image::Wrap &w) { - GLint gs, gt; - - 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; - } + wrap = w; bind(); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt); + setTextureWrap(w); } -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(); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapsharpness); // negative bias is sharper +} - GLint gs, gt; - - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs); - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, >); - - 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; +float Image::getMipmapSharpness() const +{ + return mipmapsharpness; } void Image::bind() const @@ -303,6 +241,9 @@ void Image::unload() bool Image::loadVolatile() { + if (hasMipmapSharpnessSupport()) + glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness); + if (hasNpot()) return loadVolatileNPOT(); else @@ -313,6 +254,7 @@ bool Image::loadVolatilePOT() { glGenTextures(1,(GLuint *)&texture); bindTexture(texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -349,8 +291,9 @@ bool Image::loadVolatilePOT() GL_UNSIGNED_BYTE, data->getData()); - setFilter(settings.filter); - setWrap(settings.wrap); + setMipmapSharpness(mipmapsharpness); + setFilter(filter); + setWrap(wrap); return true; } @@ -359,6 +302,7 @@ bool Image::loadVolatileNPOT() { glGenTextures(1,(GLuint *)&texture); bindTexture(texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -375,16 +319,15 @@ bool Image::loadVolatileNPOT() GL_UNSIGNED_BYTE, data->getData()); - setFilter(settings.filter); - setWrap(settings.wrap); + setMipmapSharpness(mipmapsharpness); + setFilter(filter); + setWrap(wrap); return true; } void Image::unloadVolatile() { - settings.filter = getFilter(); - settings.wrap = getWrap(); // Delete the hardware texture. if (texture != 0) { @@ -417,14 +360,14 @@ bool Image::hasNpot() 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 diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 6597d9f1e..68c81d8d3 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -94,16 +94,19 @@ public: /** * Sets the filter mode. - * - * @param mode The filter mode. + * @param f The filter mode. **/ 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; @@ -115,10 +118,8 @@ public: void unloadVolatile(); static bool hasNpot(); - - // The default filter. - static void setDefaultFilter(const Image::Filter &f); - static const Image::Filter &getDefaultFilter(); + static bool hasMipmapSupport(); + static bool hasMipmapSharpnessSupport(); private: @@ -141,18 +142,22 @@ private: // The source vertices of the image. vertex vertices[4]; - // The settings we need to save when reloading. - struct - { - Image::Filter filter; - Image::Wrap wrap; - } settings; + // Mipmap texture LOD bias value + float mipmapsharpness; + + // Implementation-dependent maximum/minimum mipmap sharpness values + float maxmipmapsharpness; + + // The image's filter mode + Image::Filter filter; + + // The image's wrap mode + Image::Wrap wrap; bool loadVolatilePOT(); bool loadVolatileNPOT(); - // The default image filter - static Image::Filter defaultFilter; + void checkMipmapsCreated() const; }; // Image diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index ebec48a18..b0be8fcb2 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -165,6 +165,160 @@ void deleteTexture(GLuint 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, >); + + 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 } // graphics } // love diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index f8386d7e6..39439b1e2 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -22,6 +22,7 @@ #define LOVE_GRAPHICS_OPENGL_OPENGL_H #include "GLee.h" +#include "graphics/Image.h" namespace love { @@ -66,6 +67,28 @@ void bindTextureToUnit(GLuint texture, GLenum textureunit, bool restoreprev); **/ 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 } // graphics } // love diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index d3ecf7e68..6b192bee5 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -105,7 +105,7 @@ ParticleSystem::~ParticleSystem() if (pStart != 0) delete [] pStart; - + if (particleVerts != 0) delete [] particleVerts; } @@ -207,10 +207,10 @@ void ParticleSystem::setBufferSize(unsigned int size) pLast = pStart = new particle[size]; pEnd = pStart + size; - + if (particleVerts != 0) delete [] particleVerts; - + // each particle has 4 vertices 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 { if (sprite == 0) return; // just in case of failure - + int numParticles = count(); 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; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); glMultMatrixf((const GLfloat *)t.getElements()); - + const vertex * imageVerts = sprite->getVertices(); - + // set the vertex data for each particle (transformation, texcoords, color) for (int i = 0; i < numParticles; i++) { particle * p = pStart + i; - + // 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.transform(&particleVerts[i*4], &imageVerts[0], 4); - + // 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 - + particleVerts[vi].s = imageVerts[v].s; particleVerts[vi].t = imageVerts[v].t; - + // 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].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; } } - + sprite->bind(); - + glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), (GLvoid *)&particleVerts[0].r); glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&particleVerts[0].x); glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&particleVerts[0].s); - + glDrawArrays(GL_QUADS, 0, numParticles*4); - + glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); diff --git a/src/modules/graphics/opengl/ParticleSystem.h b/src/modules/graphics/opengl/ParticleSystem.h index 5c02f75d7..15651c14a 100644 --- a/src/modules/graphics/opengl/ParticleSystem.h +++ b/src/modules/graphics/opengl/ParticleSystem.h @@ -406,7 +406,7 @@ protected: // Pointer to the end of the memory allocation. particle *pEnd; - + // array of transformed vertex data for all particles, for drawing vertex * particleVerts; 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 diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index 3af47051c..10ff83f6d 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -67,23 +67,27 @@ int w_Canvas_getImageData(lua_State *L) int w_Canvas_setFilter(lua_State *L) { Canvas *canvas = luax_checkcanvas(L, 1); - const char *minstr = luaL_checkstring(L, 2); - const char *magstr = luaL_checkstring(L, 3); 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 min filter mode: %s", minstr); + return luaL_error(L, "Invalid filter mode: %s", minstr); 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); + return 0; + } int w_Canvas_getFilter(lua_State *L) { Canvas *canvas = luax_checkcanvas(L, 1); - Image::Filter f = canvas->getFilter(); + const Image::Filter f = canvas->getFilter(); const char *minstr; const char *magstr; @@ -99,23 +103,26 @@ int w_Canvas_getFilter(lua_State *L) int w_Canvas_setWrap(lua_State *L) { 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; - if (!Image::getConstant(wrap_s, w.s)) - return luaL_error(L, "Invalid wrap mode: %s", wrap_s); - if (!Image::getConstant(wrap_t, w.t)) - return luaL_error(L, "Invalid wrap mode: %s", wrap_t); + + const char *sstr = luaL_checkstring(L, 2); + const char *tstr = luaL_optstring(L, 3, sstr); + + 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); + return 0; } int w_Canvas_getWrap(lua_State *L) { Canvas *canvas = luax_checkcanvas(L, 1); - Image::Wrap w = canvas->getWrap(); + const Image::Wrap w = canvas->getWrap(); const char *wrap_s; const char *wrap_t; diff --git a/src/modules/graphics/opengl/wrap_Font.cpp b/src/modules/graphics/opengl/wrap_Font.cpp index 21d0a0bb3..5306a10e3 100644 --- a/src/modules/graphics/opengl/wrap_Font.cpp +++ b/src/modules/graphics/opengl/wrap_Font.cpp @@ -90,6 +90,78 @@ int w_Font_getLineHeight(lua_State *L) 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[] = { { "getHeight", w_Font_getHeight }, @@ -97,6 +169,10 @@ static const luaL_Reg functions[] = { "getWrap", w_Font_getWrap }, { "setLineHeight", w_Font_setLineHeight }, { "getLineHeight", w_Font_getLineHeight }, + { "setFilter", w_Font_setFilter }, + { "getFilter", w_Font_getFilter }, + { "setMipmapSharpness", w_Font_setMipmapSharpness }, + { "getMipmapSharpness", w_Font_getMipmapSharpness }, { 0, 0 } }; diff --git a/src/modules/graphics/opengl/wrap_Font.h b/src/modules/graphics/opengl/wrap_Font.h index 452c24696..a64be5863 100644 --- a/src/modules/graphics/opengl/wrap_Font.h +++ b/src/modules/graphics/opengl/wrap_Font.h @@ -38,6 +38,10 @@ int w_Font_getWidth(lua_State *L); int w_Font_getWrap(lua_State *L); int w_Font_setLineHeight(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); } // opengl diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index bfa53252f..b274106df 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -622,8 +622,10 @@ int w_setDefaultImageFilter(lua_State *L) { Image::FilterMode min; Image::FilterMode mag; + const char *minstr = luaL_checkstring(L, 1); const char *magstr = luaL_optstring(L, 2, minstr); + if (!Image::getConstant(minstr, min)) return luaL_error(L, "Invalid filter mode: %s", minstr); if (!Image::getConstant(magstr, mag)) @@ -632,6 +634,7 @@ int w_setDefaultImageFilter(lua_State *L) Image::Filter f; f.min = min; f.mag = mag; + instance->setDefaultImageFilter(f); return 0; @@ -871,7 +874,12 @@ int w_isSupported(lua_State *L) supported = false; break; 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; default: supported = false; diff --git a/src/modules/graphics/opengl/wrap_Image.cpp b/src/modules/graphics/opengl/wrap_Image.cpp index af40f3add..e92c22952 100644 --- a/src/modules/graphics/opengl/wrap_Image.cpp +++ b/src/modules/graphics/opengl/wrap_Image.cpp @@ -50,71 +50,107 @@ int w_Image_getHeight(lua_State *L) int w_Image_setFilter(lua_State *L) { Image *t = luax_checkimage(L, 1); + Image::Filter f; - Image::FilterMode min; - Image::FilterMode mag; + const char *minstr = luaL_checkstring(L, 2); 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); - if (!Image::getConstant(magstr, mag)) + if (!Image::getConstant(magstr, f.mag)) return luaL_error(L, "Invalid filter mode: %s", magstr); - f.min = min; - f.mag = mag; - t->setFilter(f); + 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_Image_getFilter(lua_State *L) { Image *t = luax_checkimage(L, 1); - Image::Filter f = t->getFilter(); - Image::FilterMode min = f.min; - Image::FilterMode mag = f.mag; + const Image::Filter f = t->getFilter(); const char *minstr; const char *magstr; - Image::getConstant(min, minstr); - Image::getConstant(mag, magstr); + Image::getConstant(f.min, minstr); + Image::getConstant(f.mag, magstr); lua_pushstring(L, minstr); 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) { Image *i = luax_checkimage(L, 1); + Image::Wrap w; - Image::WrapMode s; - Image::WrapMode t; + const char *sstr = luaL_checkstring(L, 2); 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); - if (!Image::getConstant(tstr, t)) + if (!Image::getConstant(tstr, w.t)) return luaL_error(L, "Invalid wrap mode, %s", tstr); - w.s = s; - w.t = t; i->setWrap(w); + return 0; } int w_Image_getWrap(lua_State *L) { Image *i = luax_checkimage(L, 1); - Image::Wrap w = i->getWrap(); - Image::WrapMode s = w.s; - Image::WrapMode t = w.t; + const Image::Wrap w = i->getWrap(); const char *sstr; const char *tstr; - Image::getConstant(s, sstr); - Image::getConstant(t, tstr); + Image::getConstant(w.s, sstr); + Image::getConstant(w.t, tstr); lua_pushstring(L, sstr); lua_pushstring(L, tstr); 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[] = { { "getWidth", w_Image_getWidth }, @@ -123,6 +159,8 @@ static const luaL_Reg functions[] = { "getFilter", w_Image_getFilter }, { "setWrap", w_Image_setWrap }, { "getWrap", w_Image_getWrap }, + { "setMipmapSharpness", w_Image_setMipmapSharpness }, + { "getMipmapSharpness", w_Image_getMipmapSharpness }, { 0, 0 } }; diff --git a/src/modules/graphics/opengl/wrap_Image.h b/src/modules/graphics/opengl/wrap_Image.h index 80e0fe96d..a6c8d5d6a 100644 --- a/src/modules/graphics/opengl/wrap_Image.h +++ b/src/modules/graphics/opengl/wrap_Image.h @@ -36,6 +36,11 @@ Image *luax_checkimage(lua_State *L, int idx); int w_Image_getWidth(lua_State *L); int w_Image_getHeight(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); } // opengl