mirror of
https://github.com/love2d/love.git
synced 2026-08-15 07:41:11 +02:00
Merged in from main repository (originally from love-mipmap fork)
This commit is contained in:
@@ -161,6 +161,7 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::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::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
||||
|
||||
@@ -89,6 +89,7 @@ public:
|
||||
SUPPORT_SHADEREFFECT,
|
||||
SUPPORT_NPOT,
|
||||
SUPPORT_SUBTRACTIVE,
|
||||
SUPPORT_MIPMAP,
|
||||
SUPPORT_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
|
||||
static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
|
||||
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 <math.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <algorithm> // 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<GlyphArrayDrawInfo> glyphinfolist;
|
||||
|
||||
|
||||
std::vector<GlyphQuad> glyphquads;
|
||||
glyphquads.reserve(text.size()); // pre-allocate space for the maximum possible number of quads
|
||||
|
||||
|
||||
int quadindex = 0;
|
||||
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
Matrix t;
|
||||
@@ -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> 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<GlyphArrayDrawInfo>::const_iterator it;
|
||||
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
||||
{
|
||||
bindTexture(it->texture);
|
||||
|
||||
|
||||
int startvertex = it->startquad * 4;
|
||||
int numvertices = it->numquads * 4;
|
||||
|
||||
|
||||
glDrawArrays(GL_QUADS, startvertex, numvertices);
|
||||
}
|
||||
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
@@ -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<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()
|
||||
{
|
||||
if (Image::hasMipmapSharpnessSupport())
|
||||
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness);
|
||||
|
||||
createTexture();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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<GLuint> textures; // vector of packed textures
|
||||
std::map<int, Glyph *> glyphs; // maps glyphs to quad information
|
||||
FontType type;
|
||||
Image::Filter filter;
|
||||
|
||||
|
||||
static const int NUM_TEXTURE_SIZES = 7;
|
||||
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
|
||||
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
// STD
|
||||
#include <cstring> // For memcpy
|
||||
#include <algorithm> // for min/max
|
||||
|
||||
#include <iostream>
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
// Conflicts with std::numeric_limits<GLushort>::max() (Windows).
|
||||
#ifdef max
|
||||
# undef max
|
||||
#endif
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user