mirror of
https://github.com/love2d/love.git
synced 2026-08-20 04:30:09 +02:00
Initial image mipmapping implementation. Includes Image:set/getMipmapFilter and Image:set/getMipmapSharpness. setMipMapFilter requires a filter mode, or nil to disable mipmapping.
Enabling mipmapping on an image that does not have power-of-two dimensions will raise an error, due to inconsistent support in older graphics chipsets
This commit is contained in:
@@ -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
|
||||||
{
|
{
|
||||||
@@ -37,6 +37,7 @@ 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;
|
||||||
@@ -61,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 = getDefaultFilter();
|
filter = getDefaultFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::~Image()
|
Image::~Image()
|
||||||
@@ -144,26 +145,72 @@ void Image::drawq(love::graphics::Quad *quad, float x, float y, float angle, flo
|
|||||||
|
|
||||||
void Image::setFilter(const Image::Filter &f)
|
void Image::setFilter(const Image::Filter &f)
|
||||||
{
|
{
|
||||||
|
filter = f;
|
||||||
|
|
||||||
bind();
|
bind();
|
||||||
|
|
||||||
|
if (f.mipmap == FILTER_NEAREST || f.mipmap == FILTER_LINEAR)
|
||||||
|
{
|
||||||
|
if (!hasMipmapSupport())
|
||||||
|
throw love::Exception("Mipmaps are not supported on this system!");
|
||||||
|
|
||||||
|
if (width != next_p2(width) || height != next_p2(height))
|
||||||
|
throw love::Exception("Could not generate mipmaps: image does not have power of two dimensions!");
|
||||||
|
|
||||||
|
GLboolean aremipmapscreated;
|
||||||
|
glGetTexParameteriv(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLint *)&aremipmapscreated);
|
||||||
|
|
||||||
|
// generate mipmaps for this image if we haven't already
|
||||||
|
if (!aremipmapscreated)
|
||||||
|
{
|
||||||
|
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 pixel in texture to trigger mip chain generation
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)width, (GLsizei)height, GL_RGBA, GL_UNSIGNED_BYTE, getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setTextureFilter(f);
|
setTextureFilter(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Filter Image::getFilter() const
|
const Image::Filter &Image::getFilter() const
|
||||||
{
|
{
|
||||||
bind();
|
return filter;
|
||||||
return getTextureFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::setWrap(Image::Wrap &w)
|
void Image::setWrap(Image::Wrap &w)
|
||||||
{
|
{
|
||||||
|
wrap = w;
|
||||||
|
|
||||||
bind();
|
bind();
|
||||||
setTextureWrap(w);
|
setTextureWrap(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Wrap Image::getWrap() const
|
const Image::Wrap &Image::getWrap() const
|
||||||
{
|
{
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setMipmapSharpness(float sharpness)
|
||||||
|
{
|
||||||
|
if (!(GLEE_VERSION_1_4 || GLEE_EXT_texture_lod_bias))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// LOD bias has the range (-maxbias, maxbias)
|
||||||
|
mipmapsharpness = std::min(std::max(sharpness, -maxmipmapsharpness + 0.01f), maxmipmapsharpness - 0.01f);
|
||||||
|
|
||||||
bind();
|
bind();
|
||||||
return getTextureWrap();
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapsharpness);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Image::getMipmapSharpness() const
|
||||||
|
{
|
||||||
|
return mipmapsharpness;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::bind() const
|
void Image::bind() const
|
||||||
@@ -186,6 +233,8 @@ void Image::unload()
|
|||||||
|
|
||||||
bool Image::loadVolatile()
|
bool Image::loadVolatile()
|
||||||
{
|
{
|
||||||
|
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness);
|
||||||
|
|
||||||
if (hasNpot())
|
if (hasNpot())
|
||||||
return loadVolatileNPOT();
|
return loadVolatileNPOT();
|
||||||
else
|
else
|
||||||
@@ -196,9 +245,10 @@ 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);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -221,6 +271,13 @@ bool Image::loadVolatilePOT()
|
|||||||
GL_RGBA,
|
GL_RGBA,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
|
if (hasMipmapSupport())
|
||||||
|
{
|
||||||
|
// auto-generate mipmaps when texture is modified, if mipmapping is enabled
|
||||||
|
bool genmipmaps = (filter.mipmap == FILTER_LINEAR) || (filter.mipmap == FILTER_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, genmipmaps ? GL_TRUE : GL_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D,
|
glTexSubImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
@@ -231,9 +288,9 @@ bool Image::loadVolatilePOT()
|
|||||||
GL_RGBA,
|
GL_RGBA,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
data->getData());
|
data->getData());
|
||||||
|
|
||||||
setFilter(settings.filter);
|
setFilter(filter);
|
||||||
setWrap(settings.wrap);
|
setWrap(wrap);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -242,11 +299,19 @@ 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);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
if (hasMipmapSupport())
|
||||||
|
{
|
||||||
|
// auto-generate mipmaps when texture is modified, if mipmapping is enabled
|
||||||
|
bool genmipmaps = (filter.mipmap == FILTER_LINEAR) || (filter.mipmap == FILTER_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, genmipmaps ? GL_TRUE : GL_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
@@ -257,17 +322,15 @@ bool Image::loadVolatileNPOT()
|
|||||||
GL_RGBA,
|
GL_RGBA,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
data->getData());
|
data->getData());
|
||||||
|
|
||||||
setFilter(settings.filter);
|
setFilter(filter);
|
||||||
setWrap(settings.wrap);
|
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)
|
||||||
{
|
{
|
||||||
@@ -300,6 +363,11 @@ bool Image::hasNpot()
|
|||||||
return GLEE_ARB_texture_non_power_of_two != 0;
|
return GLEE_ARB_texture_non_power_of_two != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Image::hasMipmapSupport()
|
||||||
|
{
|
||||||
|
return (GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -101,11 +101,15 @@ public:
|
|||||||
**/
|
**/
|
||||||
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 &w);
|
void setWrap(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,6 +121,7 @@ public:
|
|||||||
void unloadVolatile();
|
void unloadVolatile();
|
||||||
|
|
||||||
static bool hasNpot();
|
static bool hasNpot();
|
||||||
|
static bool hasMipmapSupport();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -138,13 +143,17 @@ 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;
|
float maxmipmapsharpness;
|
||||||
Image::Wrap wrap;
|
|
||||||
} 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();
|
||||||
|
|||||||
@@ -61,12 +61,22 @@ int w_Image_setFilter(lua_State *L)
|
|||||||
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))
|
||||||
return luaL_error(L, "Invalid filter mode: %s", magstr);
|
return luaL_error(L, "Invalid filter mode: %s", magstr);
|
||||||
|
|
||||||
|
const Image::Filter curfilter = t->getFilter();
|
||||||
|
|
||||||
Image::Filter f;
|
Image::Filter f;
|
||||||
f.min = min;
|
f.min = min;
|
||||||
f.mag = mag;
|
f.mag = mag;
|
||||||
|
f.mipmap = curfilter.mipmap;
|
||||||
|
|
||||||
t->setFilter(f);
|
try
|
||||||
|
{
|
||||||
|
t->setFilter(f);
|
||||||
|
}
|
||||||
|
catch(love::Exception &e)
|
||||||
|
{
|
||||||
|
return luaL_error(L, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -74,18 +84,61 @@ int w_Image_setFilter(lua_State *L)
|
|||||||
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;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_Image_setMipmapFilter(lua_State *L)
|
||||||
|
{
|
||||||
|
Image *i = luax_checkimage(L, 1);
|
||||||
|
|
||||||
|
const Image::Filter curfilter = i->getFilter();
|
||||||
|
Image::Filter f;
|
||||||
|
f.min = curfilter.min;
|
||||||
|
f.mag = curfilter.mag;
|
||||||
|
|
||||||
|
if (lua_isnoneornil(L, 2)) // disable mipmapping if no arguments are given
|
||||||
|
f.mipmap = Image::FILTER_NONE;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *filterstr = luaL_checkstring(L, 2);
|
||||||
|
if (!Image::getConstant(filterstr, f.mipmap))
|
||||||
|
return luaL_error(L, "Invalid filter mode: %s", filterstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
i->setFilter(f);
|
||||||
|
}
|
||||||
|
catch(love::Exception &e)
|
||||||
|
{
|
||||||
|
return luaL_error(L, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_Image_getMipmapFilter(lua_State *L)
|
||||||
|
{
|
||||||
|
Image *i = luax_checkimage(L, 1);
|
||||||
|
|
||||||
|
const Image::Filter f = i->getFilter();
|
||||||
|
const char *filterstr;
|
||||||
|
|
||||||
|
if (Image::getConstant(f.mipmap, filterstr))
|
||||||
|
lua_pushstring(L, filterstr);
|
||||||
|
else
|
||||||
|
lua_pushnil(L); // return nil if mipmap filter is FILTER_NONE
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -113,18 +166,36 @@ int w_Image_setWrap(lua_State *L)
|
|||||||
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);
|
||||||
|
|
||||||
|
float sharpness = i->getMipmapSharpness();
|
||||||
|
lua_pushnumber(L, sharpness);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg functions[] =
|
||||||
{
|
{
|
||||||
{ "getWidth", w_Image_getWidth },
|
{ "getWidth", w_Image_getWidth },
|
||||||
@@ -133,6 +204,10 @@ 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 },
|
||||||
|
{ "setMipmapFilter", w_Image_setMipmapFilter },
|
||||||
|
{ "getMipmapFilter", w_Image_getMipmapFilter },
|
||||||
|
{ "setMipmapSharpness", w_Image_setMipmapSharpness },
|
||||||
|
{ "getMipmapSharpness", w_Image_getMipmapSharpness },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,13 @@ 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_setMipmapFilter(lua_State *L);
|
||||||
|
int w_Image_getMipmapFilter(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
|
||||||
|
|||||||
Reference in New Issue
Block a user