ImageData (and Images loaded from them) now support different data formats. Resolves issue #1048.

Currently exposed formats are rgba8 and rgba16 (normalized), and rgba16f and rgba32f (floating-point). Some systems, especially mobile ones, won't support every format when creating a love.graphics Image. Use love.graphics.getRawImageFormats to check for support.

love.image.newImageData now takes an optional format parameter as its third argument when creating an empty sized ImageData. It defaults to rgba8.

16-bit PNGs, .hdr images, and floating-point OpenEXR images can now be loaded via love.image.newImageData and love.graphics.newImage.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2016-05-22 15:28:20 -03:00
parent 3e6d01748e
commit e71f95595c
31 changed files with 12018 additions and 241 deletions
+1 -1
View File
@@ -633,7 +633,7 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, i
gl.bindFramebuffer(GL_FRAMEBUFFER, prevfbo);
// The new ImageData now owns the pixel data, so we don't delete it here.
return image->newImageData(w, h, pixels, true);
return image->newImageData(w, h, image::ImageData::FORMAT_RGBA8, pixels, true);
}
bool Canvas::resolveMSAA(bool restoreprev)
+1 -1
View File
@@ -1600,7 +1600,7 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool
{
// Tell the new ImageData that it owns the screenshot data, so we don't
// need to delete it here.
img = image->newImageData(w, h, (void *) screenshot, true);
img = image->newImageData(w, h, image::ImageData::FORMAT_RGBA8, screenshot, true);
}
catch (love::Exception &)
{
+96 -22
View File
@@ -66,6 +66,8 @@ static bool verifyMipmapLevels(const std::vector<T> &miplevels)
int width = miplevels[0]->getWidth();
int height = miplevels[0]->getHeight();
auto format = miplevels[0]->getFormat();
int expectedlevels = getMipmapCount(width, height);
// All mip levels must be present when not using auto-generated mipmaps.
@@ -82,6 +84,9 @@ static bool verifyMipmapLevels(const std::vector<T> &miplevels)
throw love::Exception("Width of image mipmap level %d is incorrect (expected %d, got %d)", i+1, width, miplevels[i]->getWidth());
if (miplevels[i]->getHeight() != height)
throw love::Exception("Height of image mipmap level %d is incorrect (expected %d, got %d)", i+1, height, miplevels[i]->getHeight());
if (miplevels[i]->getFormat() != format)
throw love::Exception("All image mipmap levels must have the same format.");
}
return true;
@@ -140,12 +145,8 @@ Image::Image(const std::vector<love::image::CompressedImageData *> &compressedda
}
}
for (const auto &cd : compresseddata)
{
for (image::CompressedImageData *cd : compresseddata)
cdata.push_back(cd);
if (cd->getFormat() != cdata[0]->getFormat())
throw love::Exception("All image mipmap levels must have the same format.");
}
preload();
loadVolatile();
@@ -256,15 +257,12 @@ void Image::loadFromCompressedData()
void Image::loadFromImageData()
{
GLenum iformat = sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
GLenum format = GL_RGBA;
GLenum glformat = GL_RGBA;
GLenum gltype = GL_UNSIGNED_BYTE;
GLenum iformat = getFormat(data[0]->getFormat(), glformat, gltype, sRGB);
// in GLES2, the internalformat and format params of TexImage have to match.
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
{
format = sRGB ? GL_SRGB_ALPHA : GL_RGBA;
iformat = format;
}
if (isGammaCorrect() && !sRGB)
flags.linear = true;
int mipcount = flags.mipmaps ? (int) data.size() : 1;
@@ -274,7 +272,7 @@ void Image::loadFromImageData()
love::thread::Lock lock(id->getMutex());
glTexImage2D(GL_TEXTURE_2D, i, iformat, id->getWidth(), id->getHeight(),
0, format, GL_UNSIGNED_BYTE, id->getData());
0, glformat, gltype, id->getData());
}
if (data.size() <= 1)
@@ -416,12 +414,9 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h)
return true;
}
GLenum format = GL_RGBA;
// In ES2, the format parameter of TexSubImage must match the internal
// format of the texture.
if (sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0))
format = GL_SRGB_ALPHA;
GLenum glformat = GL_RGBA;
GLenum gltype = GL_UNSIGNED_BYTE;
getFormat(data[0]->getFormat(), glformat, gltype, sRGB);
int mipcount = flags.mipmaps ? (int) data.size() : 1;
@@ -432,8 +427,8 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h)
pdata += yoffset * data[i]->getWidth() + xoffset;
thread::Lock lock(data[i]->getMutex());
glTexSubImage2D(GL_TEXTURE_2D, i, xoffset, yoffset, w, h, format,
GL_UNSIGNED_BYTE, pdata);
glTexSubImage2D(GL_TEXTURE_2D, i, xoffset, yoffset, w, h, glformat,
gltype, pdata);
xoffset /= 2;
yoffset /= 2;
@@ -596,6 +591,53 @@ bool Image::isCompressed() const
return compressed;
}
GLenum Image::getFormat(image::ImageData::Format format, GLenum &glformat, GLenum &gltype, bool &isSRGB) const
{
using image::ImageData;
GLenum internalformat = GL_RGBA8;
glformat = GL_RGBA;
gltype = GL_UNSIGNED_BYTE;
switch (format)
{
case ImageData::FORMAT_RGBA8:
if (isSRGB)
{
internalformat = GL_SRGB8_ALPHA8;
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
glformat = GL_SRGB_ALPHA;
}
break;
case ImageData::FORMAT_RGBA16:
internalformat = GL_RGBA16;
gltype = GL_UNSIGNED_SHORT;
isSRGB = false;
break;
case ImageData::FORMAT_RGBA16F:
internalformat = GL_RGBA16F;
// HALF_FLOAT_OES has a different value than HALF_FLOAT... of course
if (GLAD_OES_texture_half_float && !GLAD_ES_VERSION_3_0)
gltype = GL_HALF_FLOAT_OES;
else
gltype = GL_HALF_FLOAT;
isSRGB = false;
break;
case ImageData::FORMAT_RGBA32F:
internalformat = GL_RGBA32F;
gltype = GL_FLOAT;
isSRGB = false;
break;
default:
break;
}
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
internalformat = glformat;
return internalformat;
}
GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const
{
using image::CompressedImageData;
@@ -701,6 +743,38 @@ bool Image::hasAnisotropicFilteringSupport()
return GLAD_EXT_texture_filter_anisotropic != GL_FALSE;
}
bool Image::hasTextureSupport(image::ImageData::Format format)
{
using image::ImageData;
switch (format)
{
case ImageData::FORMAT_RGBA8:
return true;
case ImageData::FORMAT_RGBA16:
return GLAD_VERSION_1_1 || GLAD_EXT_texture_norm16;
case ImageData::FORMAT_RGBA16F:
return GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_half_float_pixel) || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float;
case ImageData::FORMAT_RGBA32F:
return GLAD_VERSION_3_0 || GLAD_ARB_texture_float || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_float;
default:
return false;
}
}
bool Image::hasTextureFilteringSupport(image::ImageData::Format format)
{
switch (format)
{
case image::ImageData::FORMAT_RGBA16F:
return GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear;
case image::ImageData::FORMAT_RGBA32F:
return GLAD_VERSION_1_1;
default:
return true;
}
}
bool Image::hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB)
{
using image::CompressedImageData;
+3
View File
@@ -128,6 +128,8 @@ public:
static FilterMode getDefaultMipmapFilter();
static bool hasAnisotropicFilteringSupport();
static bool hasTextureSupport(image::ImageData::Format format);
static bool hasTextureFilteringSupport(image::ImageData::Format format);
static bool hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB);
static bool hasSRGBSupport();
@@ -147,6 +149,7 @@ private:
void loadFromCompressedData();
void loadFromImageData();
GLenum getFormat(image::ImageData::Format format, GLenum &glformat, GLenum &gltype, bool &isSRGB) const;
GLenum getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const;
// The ImageData from which the texture is created. May be empty if
+21 -1
View File
@@ -1416,13 +1416,32 @@ int w_getCanvasFormats(lua_State *L)
return 1;
}
int w_getRawImageFormats(lua_State *L)
{
lua_createtable(L, 0, (int) image::ImageData::FORMAT_MAX_ENUM);
for (int i = 0; i < (int) image::ImageData::FORMAT_MAX_ENUM; i++)
{
auto format = (image::ImageData::Format) i;
const char *name = nullptr;
if (!image::ImageData::getConstant(format, name))
continue;
luax_pushboolean(L, Image::hasTextureSupport(format));
lua_setfield(L, -2, name);
}
return 1;
}
int w_getCompressedImageFormats(lua_State *L)
{
lua_createtable(L, 0, (int) image::CompressedImageData::FORMAT_MAX_ENUM);
for (int i = 0; i < (int) image::CompressedImageData::FORMAT_MAX_ENUM; i++)
{
image::CompressedImageData::Format format = (image::CompressedImageData::Format) i;
auto format = (image::CompressedImageData::Format) i;
const char *name = nullptr;
if (format == image::CompressedImageData::FORMAT_UNKNOWN)
@@ -1996,6 +2015,7 @@ static const luaL_Reg functions[] =
{ "getSupported", w_getSupported },
{ "getCanvasFormats", w_getCanvasFormats },
{ "getRawImageFormats", w_getRawImageFormats },
{ "getCompressedImageFormats", w_getCompressedImageFormats },
{ "getRendererInfo", w_getRendererInfo },
{ "getSystemLimits", w_getSystemLimits },