diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 0e928ec20..8670f5ec3 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -67,6 +67,8 @@ Image::Image(love::image::CompressedData *cdata, const Flags &flags) , usingDefaultTexture(false) , textureMemorySize(0) { + this->flags.sRGB = (flags.sRGB || cdata->isSRGB()); + width = cdata->getWidth(0); height = cdata->getHeight(0); @@ -273,20 +275,22 @@ void Image::loadTextureFromImageData() bool Image::loadVolatile() { - if (flags.sRGB && !hasSRGBSupport()) - throw love::Exception("sRGB images are not supported on this system."); - - if (isCompressed() && cdata.get() && !hasCompressedTextureSupport(cdata->getFormat())) + if (isCompressed() && !hasCompressedTextureSupport(cdata->getFormat(), flags.sRGB)) { const char *str; if (image::CompressedData::getConstant(cdata->getFormat(), str)) { throw love::Exception("Cannot create image: " - "%s compressed images are not supported on this system.", str); + "%s%s compressed images are not supported on this system.", flags.sRGB ? "sRGB " : "", str); } else throw love::Exception("cannot create image: format is not supported on this system."); } + else if (!isCompressed()) + { + if (flags.sRGB && !hasSRGBSupport()) + throw love::Exception("sRGB images are not supported on this system."); + } if (maxMipmapSharpness == 0.0f && GLAD_VERSION_1_4) glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness); @@ -505,22 +509,55 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; else return GL_COMPRESSED_RGBA_BPTC_UNORM; - case image::CompressedData::FORMAT_BC7SRGB: - return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; case image::CompressedData::FORMAT_ETC1: // The ETC2 format can load ETC1 textures. if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility) return GL_COMPRESSED_RGB8_ETC2; else return GL_ETC1_RGB8_OES; + case image::CompressedData::FORMAT_ETC2_RGB: + if (flags.sRGB) + return GL_COMPRESSED_SRGB8_ETC2; + else + return GL_COMPRESSED_RGB8_ETC2; + case image::CompressedData::FORMAT_ETC2_RGBA: + if (flags.sRGB) + return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + else + return GL_COMPRESSED_RGBA8_ETC2_EAC; + case image::CompressedData::FORMAT_ETC2_RGBA1: + if (flags.sRGB) + return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; + else + return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case image::CompressedData::FORMAT_EAC_R: + return GL_COMPRESSED_R11_EAC; + case image::CompressedData::FORMAT_EAC_Rs: + return GL_COMPRESSED_SIGNED_R11_EAC; + case image::CompressedData::FORMAT_EAC_RG: + return GL_COMPRESSED_RG11_EAC; + case image::CompressedData::FORMAT_EAC_RGs: + return GL_COMPRESSED_SIGNED_RG11_EAC; case image::CompressedData::FORMAT_PVR1_RGB2: - return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if (flags.sRGB) + return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT; + else + return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case image::CompressedData::FORMAT_PVR1_RGB4: - return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if (flags.sRGB) + return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT; + else + return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; case image::CompressedData::FORMAT_PVR1_RGBA2: - return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + if (flags.sRGB) + return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT; + else + return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; case image::CompressedData::FORMAT_PVR1_RGBA4: - return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if (flags.sRGB) + return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT; + else + return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; default: if (flags.sRGB) return GL_SRGB8_ALPHA8; @@ -534,7 +571,7 @@ bool Image::hasAnisotropicFilteringSupport() return GLAD_EXT_texture_filter_anisotropic; } -bool Image::hasCompressedTextureSupport(image::CompressedData::Format format) +bool Image::hasCompressedTextureSupport(image::CompressedData::Format format, bool sRGB) { switch (format) { @@ -552,16 +589,26 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format) case image::CompressedData::FORMAT_BC6H: case image::CompressedData::FORMAT_BC6Hs: case image::CompressedData::FORMAT_BC7: - case image::CompressedData::FORMAT_BC7SRGB: return GLAD_VERSION_4_2 || GLAD_ARB_texture_compression_bptc; case image::CompressedData::FORMAT_ETC1: // ETC2 support guarantees ETC1 support as well. return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture; + case image::CompressedData::FORMAT_ETC2_RGB: + case image::CompressedData::FORMAT_ETC2_RGBA: + case image::CompressedData::FORMAT_ETC2_RGBA1: + case image::CompressedData::FORMAT_EAC_R: + case image::CompressedData::FORMAT_EAC_Rs: + case image::CompressedData::FORMAT_EAC_RG: + case image::CompressedData::FORMAT_EAC_RGs: + return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility; case image::CompressedData::FORMAT_PVR1_RGB2: case image::CompressedData::FORMAT_PVR1_RGB4: case image::CompressedData::FORMAT_PVR1_RGBA2: case image::CompressedData::FORMAT_PVR1_RGBA4: - return GLAD_IMG_texture_compression_pvrtc; + if (sRGB) + return GLAD_EXT_pvrtc_sRGB; + else + return GLAD_IMG_texture_compression_pvrtc; default: return false; } diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 9a61a1ba8..769d936ad 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -141,7 +141,7 @@ public: static FilterMode getDefaultMipmapFilter(); static bool hasAnisotropicFilteringSupport(); - static bool hasCompressedTextureSupport(image::CompressedData::Format format); + static bool hasCompressedTextureSupport(image::CompressedData::Format format, bool sRGB); static bool hasSRGBSupport(); static bool getConstant(const char *in, FlagType &out); diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 8ffe22496..d49b6fd34 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -1036,7 +1036,7 @@ int w_getCompressedImageFormats(lua_State *L) if (!image::CompressedData::getConstant(format, name)) continue; - luax_pushboolean(L, Image::hasCompressedTextureSupport(format)); + luax_pushboolean(L, Image::hasCompressedTextureSupport(format, false)); lua_setfield(L, -2, name); } diff --git a/src/modules/image/CompressedData.cpp b/src/modules/image/CompressedData.cpp index 227072643..d0055bd12 100644 --- a/src/modules/image/CompressedData.cpp +++ b/src/modules/image/CompressedData.cpp @@ -27,6 +27,7 @@ namespace image CompressedData::CompressedData() : format(FORMAT_UNKNOWN) + , sRGB(false) , data(nullptr) , dataSize(0) { @@ -62,7 +63,7 @@ void *CompressedData::getData(int miplevel) const { checkMipmapLevelExists(miplevel); - return (void *) &dataImages[miplevel].data[0]; + return &dataImages[miplevel].data[0]; } int CompressedData::getWidth(int miplevel) const @@ -84,6 +85,11 @@ CompressedData::Format CompressedData::getFormat() const return format; } +bool CompressedData::isSRGB() const +{ + return sRGB; +} + void CompressedData::checkMipmapLevelExists(int miplevel) const { if (miplevel < 0 || miplevel >= (int) dataImages.size()) @@ -103,22 +109,28 @@ bool CompressedData::getConstant(CompressedData::Format in, const char *&out) StringMap::Entry CompressedData::formatEntries[] = { {"unknown", CompressedData::FORMAT_UNKNOWN}, - {"dxt1", CompressedData::FORMAT_DXT1}, - {"dxt3", CompressedData::FORMAT_DXT3}, - {"dxt5", CompressedData::FORMAT_DXT5}, - {"bc4", CompressedData::FORMAT_BC4}, - {"bc4s", CompressedData::FORMAT_BC4s}, - {"bc5", CompressedData::FORMAT_BC5}, - {"bc5s", CompressedData::FORMAT_BC5s}, - {"bc6h", CompressedData::FORMAT_BC6H}, - {"bc6hs", CompressedData::FORMAT_BC6Hs}, - {"bc7", CompressedData::FORMAT_BC7}, - {"bc7srgb", CompressedData::FORMAT_BC7SRGB}, - {"etc1", CompressedData::FORMAT_ETC1}, - {"pvr1rgb2", CompressedData::FORMAT_PVR1_RGB2}, - {"pvr1rgb4", CompressedData::FORMAT_PVR1_RGB4}, - {"pvr1rgba2", CompressedData::FORMAT_PVR1_RGBA2}, - {"pvr1rgba4", CompressedData::FORMAT_PVR1_RGBA4}, + {"DXT1", CompressedData::FORMAT_DXT1}, + {"DXT3", CompressedData::FORMAT_DXT3}, + {"DXT5", CompressedData::FORMAT_DXT5}, + {"BC4", CompressedData::FORMAT_BC4}, + {"BC4s", CompressedData::FORMAT_BC4s}, + {"BC5", CompressedData::FORMAT_BC5}, + {"BC5s", CompressedData::FORMAT_BC5s}, + {"BC6h", CompressedData::FORMAT_BC6H}, + {"BC6hs", CompressedData::FORMAT_BC6Hs}, + {"BC7", CompressedData::FORMAT_BC7}, + {"ETC1", CompressedData::FORMAT_ETC1}, + {"ETC2rgb", CompressedData::FORMAT_ETC2_RGB}, + {"ETC2rgba", CompressedData::FORMAT_ETC2_RGBA}, + {"ETC2rgba1", CompressedData::FORMAT_ETC2_RGBA1}, + {"EACr", CompressedData::FORMAT_EAC_R}, + {"EACrs", CompressedData::FORMAT_EAC_Rs}, + {"EACrg", CompressedData::FORMAT_EAC_RG}, + {"EACrgs", CompressedData::FORMAT_EAC_RGs}, + {"PVR1rgb2", CompressedData::FORMAT_PVR1_RGB2}, + {"PVR1rgb4", CompressedData::FORMAT_PVR1_RGB4}, + {"PVR1rgba2", CompressedData::FORMAT_PVR1_RGBA2}, + {"PVR1rgba4", CompressedData::FORMAT_PVR1_RGBA4}, }; StringMap CompressedData::formats(CompressedData::formatEntries, sizeof(CompressedData::formatEntries)); diff --git a/src/modules/image/CompressedData.h b/src/modules/image/CompressedData.h index 41196fac6..80f18e13b 100644 --- a/src/modules/image/CompressedData.h +++ b/src/modules/image/CompressedData.h @@ -57,8 +57,14 @@ public: FORMAT_BC6H, FORMAT_BC6Hs, FORMAT_BC7, - FORMAT_BC7SRGB, FORMAT_ETC1, + FORMAT_ETC2_RGB, + FORMAT_ETC2_RGBA, + FORMAT_ETC2_RGBA1, + FORMAT_EAC_R, + FORMAT_EAC_Rs, + FORMAT_EAC_RG, + FORMAT_EAC_RGs, FORMAT_PVR1_RGB2, FORMAT_PVR1_RGB4, FORMAT_PVR1_RGBA2, @@ -113,6 +119,8 @@ public: **/ Format getFormat() const; + bool isSRGB() const; + static bool getConstant(const char *in, Format &out); static bool getConstant(Format in, const char *&out); @@ -120,6 +128,8 @@ protected: Format format; + bool sRGB; + // Single block of memory containing all of the sub-images. uint8 *data; size_t dataSize; diff --git a/src/modules/image/magpie/CompressedData.cpp b/src/modules/image/magpie/CompressedData.cpp index bf238f5c2..e1d11d55a 100644 --- a/src/modules/image/magpie/CompressedData.cpp +++ b/src/modules/image/magpie/CompressedData.cpp @@ -43,8 +43,7 @@ CompressedData::CompressedData(std::list formats, lov if (parser == nullptr) throw love::Exception("Could not parse compressed data: Unknown format."); - // DataImages SubImage vector will be populated by a parser. - data = parser->parse(filedata, dataImages, dataSize, format); + data = parser->parse(filedata, dataImages, dataSize, format, sRGB); if (data == nullptr) throw love::Exception("Could not parse compressed data."); diff --git a/src/modules/image/magpie/CompressedFormatHandler.h b/src/modules/image/magpie/CompressedFormatHandler.h index e04d33447..246342195 100644 --- a/src/modules/image/magpie/CompressedFormatHandler.h +++ b/src/modules/image/magpie/CompressedFormatHandler.h @@ -60,10 +60,12 @@ public: * to the returned data. * @param[out] dataSize The total size in bytes of 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 uint8 *parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format) = 0; + virtual uint8 *parse(filesystem::FileData *filedata, std::vector &images, + size_t &dataSize, CompressedData::Format &format, bool &sRGB) = 0; }; // CompressedFormatHandler diff --git a/src/modules/image/magpie/KTXHandler.cpp b/src/modules/image/magpie/KTXHandler.cpp index bd7397de4..1767ca369 100644 --- a/src/modules/image/magpie/KTXHandler.cpp +++ b/src/modules/image/magpie/KTXHandler.cpp @@ -67,7 +67,6 @@ enum KTXGLInternalFormat { KTX_GL_ETC1_RGB8_OES = 0x8D64, - // LOVE doesn't support EAC or ETC2 yet, but it won't be hard to add. KTX_GL_COMPRESSED_R11_EAC = 0x9270, KTX_GL_COMPRESSED_SIGNED_R11_EAC = 0x9271, KTX_GL_COMPRESSED_RG11_EAC = 0x9272, @@ -91,12 +90,37 @@ enum KTXGLInternalFormat KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3 }; -CompressedData::Format convertFormat(uint32 glformat) +CompressedData::Format convertFormat(uint32 glformat, bool &sRGB) { + sRGB = false; + switch (glformat) { case KTX_GL_ETC1_RGB8_OES: return CompressedData::FORMAT_ETC1; + case KTX_GL_COMPRESSED_R11_EAC: + return CompressedData::FORMAT_EAC_R; + case KTX_GL_COMPRESSED_SIGNED_R11_EAC: + return CompressedData::FORMAT_EAC_Rs; + case KTX_GL_COMPRESSED_RG11_EAC: + return CompressedData::FORMAT_EAC_RG; + case KTX_GL_COMPRESSED_SIGNED_RG11_EAC: + return CompressedData::FORMAT_EAC_RGs; + case KTX_GL_COMPRESSED_RGB8_ETC2: + return CompressedData::FORMAT_ETC2_RGB; + case KTX_GL_COMPRESSED_SRGB8_ETC2: + sRGB = true; + return CompressedData::FORMAT_ETC2_RGB; + case KTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + return CompressedData::FORMAT_ETC2_RGBA1; + case KTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + sRGB = true; + return CompressedData::FORMAT_ETC2_RGBA1; + case KTX_GL_COMPRESSED_RGBA8_ETC2_EAC: + return CompressedData::FORMAT_ETC2_RGBA; + case KTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + sRGB = true; + return CompressedData::FORMAT_ETC2_RGBA; case KTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: return CompressedData::FORMAT_PVR1_RGB4; case KTX_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: @@ -135,7 +159,7 @@ bool KTXHandler::canParse(const filesystem::FileData *data) return true; } -uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format) +uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB) { if (!canParse(filedata)) throw love::Exception("Could not decode compressed data (not a KTX file?)"); @@ -151,7 +175,8 @@ uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format); + virtual uint8 *parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB); }; // KTXHandler diff --git a/src/modules/image/magpie/PKMHandler.cpp b/src/modules/image/magpie/PKMHandler.cpp index 0662c5dac..73e498d1d 100644 --- a/src/modules/image/magpie/PKMHandler.cpp +++ b/src/modules/image/magpie/PKMHandler.cpp @@ -74,6 +74,21 @@ CompressedData::Format convertFormat(uint16 texformat) { case ETC1_RGB_NO_MIPMAPS: return CompressedData::FORMAT_ETC1; + case ETC2PACKAGE_RGB_NO_MIPMAPS: + return CompressedData::FORMAT_ETC2_RGB; + case ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD: + case ETC2PACKAGE_RGBA_NO_MIPMAPS: + return CompressedData::FORMAT_ETC2_RGBA; + case ETC2PACKAGE_RGBA1_NO_MIPMAPS: + return CompressedData::FORMAT_ETC2_RGBA1; + case ETC2PACKAGE_R_NO_MIPMAPS: + return CompressedData::FORMAT_EAC_R; + case ETC2PACKAGE_RG_NO_MIPMAPS: + return CompressedData::FORMAT_EAC_RG; + case ETC2PACKAGE_R_SIGNED_NO_MIPMAPS: + return CompressedData::FORMAT_EAC_Rs; + case ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS: + return CompressedData::FORMAT_EAC_RGs; default: return CompressedData::FORMAT_UNKNOWN; } @@ -98,7 +113,7 @@ bool PKMHandler::canParse(const filesystem::FileData *data) return true; } -uint8 *PKMHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format) +uint8 *PKMHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB) { if (!canParse(filedata)) throw love::Exception("Could not decode compressed data (not a PKM file?)"); @@ -146,6 +161,7 @@ uint8 *PKMHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format); + virtual uint8 *parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB); }; // PKMHandler diff --git a/src/modules/image/magpie/PVRHandler.cpp b/src/modules/image/magpie/PVRHandler.cpp index 3ccd7aa98..c5c34b0e6 100644 --- a/src/modules/image/magpie/PVRHandler.cpp +++ b/src/modules/image/magpie/PVRHandler.cpp @@ -294,7 +294,7 @@ bool PVRHandler::canParse(const filesystem::FileData *data) return false; } -uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format) +uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB) { if (!canParse(filedata)) throw love::Exception("Could not decode compressed data (not a PVR file?)"); @@ -376,6 +376,7 @@ uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format); + virtual uint8 *parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB); }; // PVRHandler diff --git a/src/modules/image/magpie/ddsHandler.cpp b/src/modules/image/magpie/ddsHandler.cpp index b117bcea1..12c35c0ee 100644 --- a/src/modules/image/magpie/ddsHandler.cpp +++ b/src/modules/image/magpie/ddsHandler.cpp @@ -32,12 +32,13 @@ bool DDSHandler::canParse(const filesystem::FileData *data) return dds::isCompressedDDS(data->getData(), data->getSize()); } -uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format) +uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB) { if (!dds::isDDS(filedata->getData(), filedata->getSize())) throw love::Exception("Could not decode compressed data (not a DDS file?)"); CompressedData::Format texformat = CompressedData::FORMAT_UNKNOWN; + bool isSRGB = false; uint8 *data = nullptr; dataSize = 0; @@ -48,7 +49,7 @@ uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vectorgetData(), filedata->getSize()); - texformat = convertFormat(parser.getFormat()); + texformat = convertFormat(parser.getFormat(), isSRGB); if (texformat == CompressedData::FORMAT_UNKNOWN) throw love::Exception("Could not parse compressed data: Unsupported format."); @@ -96,11 +97,14 @@ uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format); + virtual uint8 *parse(filesystem::FileData *filedata, std::vector &images, size_t &dataSize, CompressedData::Format &format, bool &sRGB); private: - static CompressedData::Format convertFormat(dds::Format ddsformat); + static CompressedData::Format convertFormat(dds::Format ddsformat, bool &sRGB); }; // DDSHandler