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
+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;