ImageData can track whether they're meant to be loaded into a Texture with a non-sRGB format when gamma correct rendering is enabled.

Fixes #1556.

* Add ImageData/CompressedImageData:setLinear and ImageData/CompressedImageData:isLinear.
  The flag is used as a hint when loading a texture from the data to determine if the format should not be treated as sRGB-encoded. The 'linear' flag in newImage overrides this.

* love.graphics.readbackTexture automatically sets the linear flag on ImageData it returns when the texture's format is linear.
  This allows an ImageData generated via readback to be fed back into a new Texture and the format will match the original Canvas.

* Also clean up some image decoding code.
This commit is contained in:
Sasha Szpakowski
2023-10-14 20:48:21 -03:00
parent bd55100d56
commit 8c13943f64
26 changed files with 149 additions and 105 deletions
+1 -3
View File
@@ -105,7 +105,7 @@ bool ASTCHandler::canParseCompressed(Data *data)
return true;
}
StrongRef<ByteData> ASTCHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> ASTCHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format)
{
if (!canParseCompressed(filedata))
throw love::Exception("Could not decode compressed data (not an .astc file?)");
@@ -138,8 +138,6 @@ StrongRef<ByteData> ASTCHandler::parseCompressed(Data *filedata, std::vector<Str
images.emplace_back(new CompressedSlice(cformat, sizeX, sizeY, memory, 0, totalsize), Acquire::NORETAIN);
format = cformat;
sRGB = false;
return memory;
}
+1 -1
View File
@@ -45,7 +45,7 @@ public:
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
PixelFormat &format) override;
}; // ASTCHandler
+24 -32
View File
@@ -137,10 +137,8 @@ enum KTXGLInternalFormat
KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD
};
PixelFormat convertFormat(uint32 glformat, bool &sRGB)
PixelFormat convertFormat(uint32 glformat)
{
sRGB = false;
// hnnngg ASTC...
switch (glformat)
@@ -160,18 +158,15 @@ PixelFormat convertFormat(uint32 glformat, bool &sRGB)
case KTX_GL_COMPRESSED_RGB8_ETC2:
return PIXELFORMAT_ETC2_RGB_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ETC2:
sRGB = true;
return PIXELFORMAT_ETC2_RGB_UNORM;
return PIXELFORMAT_ETC2_RGB_sRGB;
case KTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
return PIXELFORMAT_ETC2_RGBA1_UNORM;
case KTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
sRGB = true;
return PIXELFORMAT_ETC2_RGBA1_UNORM;
return PIXELFORMAT_ETC2_RGBA1_sRGB;
case KTX_GL_COMPRESSED_RGBA8_ETC2_EAC:
return PIXELFORMAT_ETC2_RGBA_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
sRGB = true;
return PIXELFORMAT_ETC2_RGBA_UNORM;
return PIXELFORMAT_ETC2_RGBA_sRGB;
// PVRTC.
case KTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
@@ -185,15 +180,15 @@ PixelFormat convertFormat(uint32 glformat, bool &sRGB)
// DXT.
case KTX_GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
sRGB = true;
return PIXELFORMAT_DXT1_sRGB;
case KTX_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
return PIXELFORMAT_DXT1_UNORM;
case KTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
sRGB = true;
return PIXELFORMAT_DXT3_sRGB;
case KTX_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
return PIXELFORMAT_DXT3_UNORM;
case KTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
sRGB = true;
return PIXELFORMAT_DXT5_sRGB;
case KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
return PIXELFORMAT_DXT5_UNORM;
@@ -209,7 +204,7 @@ PixelFormat convertFormat(uint32 glformat, bool &sRGB)
// BC6 and BC7.
case KTX_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
sRGB = true;
return PIXELFORMAT_BC7_sRGB;
case KTX_GL_COMPRESSED_RGBA_BPTC_UNORM:
return PIXELFORMAT_BC7_UNORM;
case KTX_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
@@ -219,59 +214,59 @@ PixelFormat convertFormat(uint32 glformat, bool &sRGB)
// ASTC.
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_4x4_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
return PIXELFORMAT_ASTC_4x4_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_5x4_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
return PIXELFORMAT_ASTC_5x4_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_5x5_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
return PIXELFORMAT_ASTC_5x5_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_6x5_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
return PIXELFORMAT_ASTC_6x5_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_6x6_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
return PIXELFORMAT_ASTC_6x6_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_8x5_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
return PIXELFORMAT_ASTC_8x5_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_8x6_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
return PIXELFORMAT_ASTC_8x6_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_8x8_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
return PIXELFORMAT_ASTC_8x8_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_10x5_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
return PIXELFORMAT_ASTC_10x5_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_10x6_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
return PIXELFORMAT_ASTC_10x6_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_10x8_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
return PIXELFORMAT_ASTC_10x8_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_10x10_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
return PIXELFORMAT_ASTC_10x10_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_12x10_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
return PIXELFORMAT_ASTC_12x10_UNORM;
case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
sRGB = true;
return PIXELFORMAT_ASTC_12x12_sRGB;
case KTX_GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
return PIXELFORMAT_ASTC_12x12_UNORM;
default:
@@ -298,7 +293,7 @@ bool KTXHandler::canParseCompressed(Data *data)
return true;
}
StrongRef<ByteData> KTXHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> KTXHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format)
{
if (!canParseCompressed(filedata))
throw love::Exception("Could not decode compressed data (not a KTX file?)");
@@ -314,8 +309,7 @@ StrongRef<ByteData> KTXHandler::parseCompressed(Data *filedata, std::vector<Stro
header.numberOfMipmapLevels = std::max(header.numberOfMipmapLevels, 1u);
bool isSRGB = false;
PixelFormat cformat = convertFormat(header.glInternalFormat, isSRGB);
PixelFormat cformat = convertFormat(header.glInternalFormat);
if (cformat == PIXELFORMAT_UNKNOWN)
throw love::Exception("Unsupported image format in KTX file.");
@@ -386,8 +380,6 @@ StrongRef<ByteData> KTXHandler::parseCompressed(Data *filedata, std::vector<Stro
}
format = cformat;
sRGB = isSRGB;
return memory;
}
+1 -1
View File
@@ -44,7 +44,7 @@ public:
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
PixelFormat &format) override;
}; // KTXHandler
+1 -3
View File
@@ -114,7 +114,7 @@ bool PKMHandler::canParseCompressed(Data *data)
return true;
}
StrongRef<ByteData> PKMHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> PKMHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format)
{
if (!canParseCompressed(filedata))
throw love::Exception("Could not decode compressed data (not a PKM file?)");
@@ -148,8 +148,6 @@ StrongRef<ByteData> PKMHandler::parseCompressed(Data *filedata, std::vector<Stro
images.emplace_back(new CompressedSlice(cformat, width, height, memory, 0, totalsize), Acquire::NORETAIN);
format = cformat;
sRGB = false;
return memory;
}
+1 -1
View File
@@ -44,7 +44,7 @@ public:
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
PixelFormat &format) override;
}; // PKMHandler
+4 -3
View File
@@ -475,7 +475,7 @@ bool PVRHandler::canParseCompressed(Data *data)
return false;
}
StrongRef<ByteData> PVRHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> PVRHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format)
{
if (!canParseCompressed(filedata))
throw love::Exception("Could not decode compressed data (not a PVR file?)");
@@ -510,6 +510,9 @@ StrongRef<ByteData> PVRHandler::parseCompressed(Data *filedata, std::vector<Stro
PixelFormat cformat = convertFormat(pixelformat, channeltype);
if (header3.colorSpace == 1)
cformat == getSRGBPixelFormat(cformat);
if (cformat == PIXELFORMAT_UNKNOWN)
throw love::Exception("Could not parse PVR file: unsupported image format.");
@@ -551,8 +554,6 @@ StrongRef<ByteData> PVRHandler::parseCompressed(Data *filedata, std::vector<Stro
}
format = cformat;
sRGB = (header3.colorSpace == 1);
return memory;
}
+1 -1
View File
@@ -42,7 +42,7 @@ public:
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
PixelFormat &format) override;
}; // PVRHandler
+34 -27
View File
@@ -32,13 +32,10 @@ namespace image
namespace magpie
{
static PixelFormat convertFormat(dds::dxinfo::DXGIFormat dxformat, bool &sRGB, bool &bgra)
static PixelFormat convertFormat(dds::dxinfo::DXGIFormat dxformat)
{
using namespace dds::dxinfo;
sRGB = false;
bgra = false;
switch (dxformat)
{
case DXGI_FORMAT_R32G32B32A32_TYPELESS:
@@ -65,9 +62,9 @@ static PixelFormat convertFormat(dds::dxinfo::DXGIFormat dxformat, bool &sRGB, b
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
return PIXELFORMAT_RGBA8_UNORM;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
return PIXELFORMAT_RGBA8_sRGB;
case DXGI_FORMAT_R16G16_TYPELESS:
case DXGI_FORMAT_R16G16_FLOAT:
@@ -98,21 +95,21 @@ static PixelFormat convertFormat(dds::dxinfo::DXGIFormat dxformat, bool &sRGB, b
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC1_UNORM_SRGB);
return PIXELFORMAT_DXT1_UNORM;
case DXGI_FORMAT_BC1_UNORM_SRGB:
return PIXELFORMAT_DXT1_sRGB;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC2_UNORM_SRGB);
return PIXELFORMAT_DXT3_UNORM;
case DXGI_FORMAT_BC2_UNORM_SRGB:
return PIXELFORMAT_DXT3_sRGB;
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC3_UNORM_SRGB);
return PIXELFORMAT_DXT5_UNORM;
case DXGI_FORMAT_BC3_UNORM_SRGB:
return PIXELFORMAT_DXT5_sRGB;
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
@@ -136,10 +133,9 @@ static PixelFormat convertFormat(dds::dxinfo::DXGIFormat dxformat, bool &sRGB, b
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
return PIXELFORMAT_BGRA8_UNORM;
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);
bgra = true;
return PIXELFORMAT_RGBA8_UNORM;
return PIXELFORMAT_BGRA8_sRGB;
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
@@ -150,9 +146,9 @@ static PixelFormat convertFormat(dds::dxinfo::DXGIFormat dxformat, bool &sRGB, b
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC7_UNORM_SRGB);
return PIXELFORMAT_BC7_UNORM;
case DXGI_FORMAT_BC7_UNORM_SRGB:
return PIXELFORMAT_BC7_sRGB;
default:
return PIXELFORMAT_UNKNOWN;
@@ -164,9 +160,13 @@ bool DDSHandler::canDecode(Data *data)
using namespace dds::dxinfo;
DXGIFormat dxformat = dds::getDDSPixelFormat(data->getData(), data->getSize());
bool isSRGB = false;
bool bgra = false;
PixelFormat format = convertFormat(dxformat, isSRGB, bgra);
PixelFormat format = convertFormat(dxformat);
// We convert BGRA to RGBA
if (format == PIXELFORMAT_BGRA8_UNORM)
format = PIXELFORMAT_RGBA8_UNORM;
else if (format == PIXELFORMAT_BGRA8_sRGB)
format = PIXELFORMAT_RGBA8_sRGB;
return ImageData::validPixelFormat(format);
}
@@ -177,9 +177,19 @@ FormatHandler::DecodedImage DDSHandler::decode(Data *data)
dds::Parser parser(data->getData(), data->getSize());
bool isSRGB = false;
img.format = convertFormat(parser.getFormat());
bool bgra = false;
img.format = convertFormat(parser.getFormat(), isSRGB, bgra);
if (img.format == PIXELFORMAT_BGRA8_UNORM)
{
img.format = PIXELFORMAT_RGBA8_UNORM;
bgra = true;
}
else if (img.format == PIXELFORMAT_BGRA8_sRGB)
{
img.format = PIXELFORMAT_RGBA8_sRGB;
bgra = true;
}
if (!ImageData::validPixelFormat(img.format))
throw love::Exception("Could not parse DDS pixel data: Unsupported format.");
@@ -229,14 +239,12 @@ bool DDSHandler::canParseCompressed(Data *data)
return dds::isCompressedDDS(data->getData(), data->getSize());
}
StrongRef<ByteData> DDSHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> DDSHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format)
{
if (!dds::isCompressedDDS(filedata->getData(), filedata->getSize()))
throw love::Exception("Could not decode compressed data (not a DDS file?)");
PixelFormat texformat = PIXELFORMAT_UNKNOWN;
bool isSRGB = false;
bool bgra = false;
size_t dataSize = 0;
@@ -245,7 +253,7 @@ StrongRef<ByteData> DDSHandler::parseCompressed(Data *filedata, std::vector<Stro
// Attempt to parse the dds file.
dds::Parser parser(filedata->getData(), filedata->getSize());
texformat = convertFormat(parser.getFormat(), isSRGB, bgra);
texformat = convertFormat(parser.getFormat());
if (texformat == PIXELFORMAT_UNKNOWN)
throw love::Exception("Could not parse compressed data: Unsupported format.");
@@ -280,7 +288,6 @@ StrongRef<ByteData> DDSHandler::parseCompressed(Data *filedata, std::vector<Stro
}
format = texformat;
sRGB = isSRGB;
return memory;
}
+1 -1
View File
@@ -48,7 +48,7 @@ public:
bool canParseCompressed(Data *data) override;
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
PixelFormat &format) override;
}; // DDSHandler