Skip to content

Commit

Permalink
ImageData can track whether they're meant to be loaded into a Texture…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
slime73 committed Oct 14, 2023
1 parent bd55100 commit 8c13943
Show file tree
Hide file tree
Showing 26 changed files with 149 additions and 105 deletions.
10 changes: 2 additions & 8 deletions src/common/pixelformat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,8 @@ PixelFormat getSRGBPixelFormat(PixelFormat format)
case PIXELFORMAT_ASTC_10x10_UNORM: return PIXELFORMAT_ASTC_10x10_sRGB;
case PIXELFORMAT_ASTC_12x10_UNORM: return PIXELFORMAT_ASTC_12x10_sRGB;
case PIXELFORMAT_ASTC_12x12_UNORM: return PIXELFORMAT_ASTC_12x12_sRGB;
default:
break;
default: return format;
}

return format;
}

PixelFormat getLinearPixelFormat(PixelFormat format)
Expand Down Expand Up @@ -398,11 +395,8 @@ PixelFormat getLinearPixelFormat(PixelFormat format)
case PIXELFORMAT_ASTC_10x10_sRGB: return PIXELFORMAT_ASTC_10x10_UNORM;
case PIXELFORMAT_ASTC_12x10_sRGB: return PIXELFORMAT_ASTC_12x10_UNORM;
case PIXELFORMAT_ASTC_12x12_sRGB: return PIXELFORMAT_ASTC_12x12_UNORM;
default:
break;
default: return format;
}

return format;
}

size_t getPixelFormatBlockSize(PixelFormat format)
Expand Down
4 changes: 3 additions & 1 deletion src/modules/graphics/GraphicsReadback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ GraphicsReadback::GraphicsReadback(Graphics *gfx, ReadbackMethod method, Texture
}

textureFormat = getLinearPixelFormat(texture->getPixelFormat());
isFormatLinear = isGammaCorrect() && !isPixelFormatSRGB(texture->getPixelFormat());

if (!image::ImageData::validPixelFormat(textureFormat))
{
Expand All @@ -96,7 +97,7 @@ GraphicsReadback::GraphicsReadback(Graphics *gfx, ReadbackMethod method, Texture
if (isRT && !caps.features[Graphics::FEATURE_COPY_RENDER_TARGET_TO_BUFFER])
throw love::Exception("readbackTextureAsync is not supported on this system.");
else if (!isRT && !caps.features[Graphics::FEATURE_COPY_TEXTURE_TO_BUFFER])
throw love::Exception("readbackTextureAsync a with non-render-target textures is not supported on this system.");
throw love::Exception("readbackTextureAsync with a non-render-target texture is not supported on this system.");
}
else
{
Expand Down Expand Up @@ -158,6 +159,7 @@ void *GraphicsReadback::prepareReadbackDest(size_t size)
throw love::Exception("The love.image module must be loaded for readbackTexture.");

imageData.set(module->newImageData(rect.w, rect.h, textureFormat, nullptr), Acquire::NORETAIN);
imageData->setLinear(isFormatLinear);
return imageData->getData();
}
}
Expand Down
1 change: 1 addition & 0 deletions src/modules/graphics/GraphicsReadback.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class GraphicsReadback : public love::Object
StrongRef<love::image::ImageData> imageData;
Rect rect = {};
PixelFormat textureFormat = PIXELFORMAT_UNKNOWN;
bool isFormatLinear = false;
int imageDataX = 0;
int imageDataY = 0;

Expand Down
6 changes: 5 additions & 1 deletion src/modules/graphics/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
love::image::ImageDataBase *slice = slices->get(0, 0);

format = slice->getFormat();
if (isGammaCorrect() && !settings.linear)
if (isGammaCorrect() && !slice->isLinear())
format = getSRGBPixelFormat(format);

pixelWidth = slice->getWidth();
Expand Down Expand Up @@ -886,6 +886,7 @@ bool Texture::Slices::validate() const
int w = firstdata->getWidth();
int h = firstdata->getHeight();
PixelFormat format = firstdata->getFormat();
bool linear = firstdata->isLinear();

if (textureType == TEXTURE_CUBE && w != h)
throw love::Exception("Cube textures must have equal widths and heights for each cube face.");
Expand Down Expand Up @@ -925,6 +926,9 @@ bool Texture::Slices::validate() const

if (format != slicedata->getFormat())
throw love::Exception("All texture slices and mipmaps must have the same pixel format.");

if (linear != slicedata->isLinear())
throw love::Exception("All texture slices and mipmaps must have the same linear setting.");
}

mipw = std::max(mipw / 2, 1);
Expand Down
18 changes: 13 additions & 5 deletions src/modules/image/CompressedImageData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ love::Type CompressedImageData::type("CompressedImageData", &Data::type);

CompressedImageData::CompressedImageData(const std::list<FormatHandler *> &formats, Data *filedata)
: format(PIXELFORMAT_UNKNOWN)
, sRGB(false)
{
FormatHandler *parser = nullptr;

Expand All @@ -46,7 +45,7 @@ CompressedImageData::CompressedImageData(const std::list<FormatHandler *> &forma
if (parser == nullptr)
throw love::Exception("Could not parse compressed data: Unknown format.");

memory = parser->parseCompressed(filedata, dataImages, format, sRGB);
memory = parser->parseCompressed(filedata, dataImages, format);

if (memory == nullptr)
throw love::Exception("Could not parse compressed data.");
Expand All @@ -56,11 +55,14 @@ CompressedImageData::CompressedImageData(const std::list<FormatHandler *> &forma

if (dataImages.size() == 0 || memory->getSize() == 0)
throw love::Exception("Could not parse compressed data: No valid data?");

// This throws away some information the decoder could give us, but we
// can't really rely on it I think...
format = getLinearPixelFormat(format);
}

CompressedImageData::CompressedImageData(const CompressedImageData &c)
: format(c.format)
, sRGB(c.sRGB)
{
memory.set(c.memory->clone(), Acquire::NORETAIN);

Expand Down Expand Up @@ -134,9 +136,15 @@ PixelFormat CompressedImageData::getFormat() const
return format;
}

bool CompressedImageData::isSRGB() const
void CompressedImageData::setLinear(bool linear)
{
for (auto &slice : dataImages)
slice->setLinear(linear);
}

bool CompressedImageData::isLinear() const
{
return sRGB;
return dataImages.empty() ? false : dataImages[0]->isLinear();
}

CompressedSlice *CompressedImageData::getSlice(int slice, int miplevel) const
Expand Down
4 changes: 2 additions & 2 deletions src/modules/image/CompressedImageData.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ class CompressedImageData : public Data
**/
PixelFormat getFormat() const;

bool isSRGB() const;
void setLinear(bool linear);
bool isLinear() const;

CompressedSlice *getSlice(int slice, int miplevel) const;

protected:

PixelFormat format;
bool sRGB;

// Single block of memory containing all of the sub-images.
StrongRef<ByteData> memory;
Expand Down
2 changes: 0 additions & 2 deletions src/modules/image/CompressedSlice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ CompressedSlice::CompressedSlice(PixelFormat format, int width, int height, Byte
, memory(memory)
, offset(offset)
, dataSize(size)
, sRGB(false)
{
}

Expand All @@ -40,7 +39,6 @@ CompressedSlice::CompressedSlice(const CompressedSlice &s)
, memory(s.memory)
, offset(s.offset)
, dataSize(s.dataSize)
, sRGB(s.sRGB)
{
}

Expand Down
2 changes: 0 additions & 2 deletions src/modules/image/CompressedSlice.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,13 @@ class CompressedSlice : public ImageDataBase
CompressedSlice *clone() const override;
void *getData() const override { return (uint8 *) memory->getData() + offset; }
size_t getSize() const override { return dataSize; }
bool isSRGB() const override { return sRGB; }
size_t getOffset() const { return offset; }

private:

StrongRef<ByteData> memory;
size_t offset;
size_t dataSize;
bool sRGB;

}; // CompressedSlice

Expand Down
2 changes: 1 addition & 1 deletion src/modules/image/FormatHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ bool FormatHandler::canParseCompressed(Data* /*data*/)
return false;
}

StrongRef<ByteData> FormatHandler::parseCompressed(Data* /*filedata*/, std::vector<StrongRef<CompressedSlice>>& /*images*/, PixelFormat& /*format*/, bool& /*sRGB*/)
StrongRef<ByteData> FormatHandler::parseCompressed(Data* /*filedata*/, std::vector<StrongRef<CompressedSlice>>& /*images*/, PixelFormat& /*format*/)
{
throw love::Exception("Compressed image parsing is not implemented for this format backend.");
}
Expand Down
3 changes: 1 addition & 2 deletions src/modules/image/FormatHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,12 @@ class FormatHandler : public love::Object
* @param[out] images The list of sub-images generated. Byte data is a
* pointer to the returned data.
* @param[out] format The format of the Compressed Data.
* @param[out] sRGB Whether the texture is sRGB-encoded.
*
* @return The single block of memory containing the parsed images.
**/
virtual StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB);
PixelFormat &format);

/**
* Frees raw pixel memory allocated by the format handler.
Expand Down
9 changes: 4 additions & 5 deletions src/modules/image/ImageData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ void ImageData::decode(Data *data)
else
delete[] this->data;

// This throws away some information the decoder could give us, but we
// can't really rely on it I think...
decodedimage.format = getLinearPixelFormat(decodedimage.format);

this->width = decodedimage.width;
this->height = decodedimage.height;
this->data = decodedimage.data;
Expand Down Expand Up @@ -247,11 +251,6 @@ void *ImageData::getData() const
return data;
}

bool ImageData::isSRGB() const
{
return false;
}

bool ImageData::inside(int x, int y) const
{
return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
Expand Down
3 changes: 1 addition & 2 deletions src/modules/image/ImageData.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ImageData : public ImageDataBase
static love::Type type;

ImageData(Data *data);
ImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8_UNORM);
ImageData(int width, int height, PixelFormat format);
ImageData(int width, int height, PixelFormat format, void *data, bool own);
ImageData(const ImageData &c);
virtual ~ImageData();
Expand Down Expand Up @@ -116,7 +116,6 @@ class ImageData : public ImageDataBase
ImageData *clone() const override;
void *getData() const override;
size_t getSize() const override;
bool isSRGB() const override;

size_t getPixelSize() const;

Expand Down
11 changes: 11 additions & 0 deletions src/modules/image/ImageDataBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ ImageDataBase::ImageDataBase(PixelFormat format, int width, int height)
: format(format)
, width(width)
, height(height)
, linear(false)
{
}

Expand All @@ -47,5 +48,15 @@ int ImageDataBase::getHeight() const
return height;
}

void ImageDataBase::setLinear(bool linear)
{
this->linear = linear;
}

bool ImageDataBase::isLinear() const
{
return linear;
}

} // image
} // love
5 changes: 4 additions & 1 deletion src/modules/image/ImageDataBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class ImageDataBase : public Data
int getWidth() const;
int getHeight() const;

virtual bool isSRGB() const = 0;
void setLinear(bool linear);
bool isLinear() const;

protected:

Expand All @@ -50,6 +51,8 @@ class ImageDataBase : public Data
int width;
int height;

bool linear;

}; // ImageDataBase

} // image
Expand Down
4 changes: 1 addition & 3 deletions src/modules/image/magpie/ASTCHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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?)");
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/modules/image/magpie/ASTCHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ASTCHandler : public FormatHandler

StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
PixelFormat &format) override;

}; // ASTCHandler

Expand Down
Loading

0 comments on commit 8c13943

Please sign in to comment.