diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 4a035cb8d..155467885 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -429,7 +429,7 @@ public: // Implements Module. virtual ModuleType getModuleType() const { return M_GRAPHICS; } - virtual Image *newImage(const Image::Slices &data, const Image::Settings &settings) = 0; + virtual Image *newImage(const Texture::Slices &data, const Image::Settings &settings) = 0; virtual Image *newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings) = 0; Quad *newQuad(Quad::Viewport v, double sw, double sh); diff --git a/src/modules/graphics/Image.cpp b/src/modules/graphics/Image.cpp index 273055769..bf19212dc 100644 --- a/src/modules/graphics/Image.cpp +++ b/src/modules/graphics/Image.cpp @@ -40,7 +40,7 @@ Image::Image(const Slices &data, const Settings &settings, bool validatedata) , sRGB(isGammaCorrect() && !settings.linear) , usingDefaultTexture(false) { - if (validatedata && data.validate() == MIPMAPS_DATA) + if (validatedata && data.validate() && data.getMipmapCount() > 1) mipmapsType = MIPMAPS_DATA; } @@ -182,7 +182,7 @@ bool Image::isCompressed() const bool Image::isFormatLinear() const { - return isGammaCorrect() && !sRGB; + return isGammaCorrect() && !sRGB && format != PIXELFORMAT_sRGBA8_UNORM; } Image::MipmapsType Image::getMipmapsType() const @@ -190,168 +190,6 @@ Image::MipmapsType Image::getMipmapsType() const return mipmapsType; } -Image::Slices::Slices(TextureType textype) - : textureType(textype) -{ -} - -void Image::Slices::clear() -{ - data.clear(); -} - -void Image::Slices::set(int slice, int mipmap, love::image::ImageDataBase *d) -{ - if (textureType == TEXTURE_VOLUME) - { - if (mipmap >= (int) data.size()) - data.resize(mipmap + 1); - - if (slice >= (int) data[mipmap].size()) - data[mipmap].resize(slice + 1); - - data[mipmap][slice].set(d); - } - else - { - if (slice >= (int) data.size()) - data.resize(slice + 1); - - if (mipmap >= (int) data[slice].size()) - data[slice].resize(mipmap + 1); - - data[slice][mipmap].set(d); - } -} - -love::image::ImageDataBase *Image::Slices::get(int slice, int mipmap) const -{ - if (slice < 0 || slice >= getSliceCount(mipmap)) - return nullptr; - - if (mipmap < 0 || mipmap >= getMipmapCount(slice)) - return nullptr; - - if (textureType == TEXTURE_VOLUME) - return data[mipmap][slice].get(); - else - return data[slice][mipmap].get(); -} - -void Image::Slices::add(love::image::CompressedImageData *cdata, int startslice, int startmip, bool addallslices, bool addallmips) -{ - int slicecount = addallslices ? cdata->getSliceCount() : 1; - int mipcount = addallmips ? cdata->getMipmapCount() : 1; - - for (int mip = 0; mip < mipcount; mip++) - { - for (int slice = 0; slice < slicecount; slice++) - set(startslice + slice, startmip + mip, cdata->getSlice(slice, mip)); - } -} - -int Image::Slices::getSliceCount(int mip) const -{ - if (textureType == TEXTURE_VOLUME) - { - if (mip < 0 || mip >= (int) data.size()) - return 0; - - return (int) data[mip].size(); - } - else - return (int) data.size(); -} - -int Image::Slices::getMipmapCount(int slice) const -{ - if (textureType == TEXTURE_VOLUME) - return (int) data.size(); - else - { - if (slice < 0 || slice >= (int) data.size()) - return 0; - - return data[slice].size(); - } -} - -Image::MipmapsType Image::Slices::validate() const -{ - int slicecount = getSliceCount(); - int mipcount = getMipmapCount(0); - - if (slicecount == 0 || mipcount == 0) - throw love::Exception("At least one ImageData or CompressedImageData is required!"); - - if (textureType == TEXTURE_CUBE && slicecount != 6) - throw love::Exception("Cube textures must have exactly 6 sides."); - - image::ImageDataBase *firstdata = get(0, 0); - - int w = firstdata->getWidth(); - int h = firstdata->getHeight(); - int depth = textureType == TEXTURE_VOLUME ? slicecount : 1; - PixelFormat format = firstdata->getFormat(); - - int expectedmips = Texture::getTotalMipmapCount(w, h, depth); - - if (mipcount != expectedmips && mipcount != 1) - throw love::Exception("Image does not have all required mipmap levels (expected %d, got %d)", expectedmips, mipcount); - - if (textureType == TEXTURE_CUBE && w != h) - throw love::Exception("Cube images must have equal widths and heights for each cube face."); - - int mipw = w; - int miph = h; - int mipslices = slicecount; - - for (int mip = 0; mip < mipcount; mip++) - { - if (textureType == TEXTURE_VOLUME) - { - slicecount = getSliceCount(mip); - - if (slicecount != mipslices) - throw love::Exception("Invalid number of image data layers in mipmap level %d (expected %d, got %d)", mip+1, mipslices, slicecount); - } - - for (int slice = 0; slice < slicecount; slice++) - { - auto slicedata = get(slice, mip); - - if (slicedata == nullptr) - throw love::Exception("Missing image data (slice %d, mipmap level %d)", slice+1, mip+1); - - int realw = slicedata->getWidth(); - int realh = slicedata->getHeight(); - - if (getMipmapCount(slice) != mipcount) - throw love::Exception("All Image layers must have the same mipmap count."); - - if (mipw != realw) - throw love::Exception("Width of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, mipw, realw); - - if (miph != realh) - throw love::Exception("Height of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, miph, realh); - - if (format != slicedata->getFormat()) - throw love::Exception("All Image slices and mipmaps must have the same pixel format."); - } - - mipw = std::max(mipw / 2, 1); - miph = std::max(miph / 2, 1); - - if (textureType == TEXTURE_VOLUME) - mipslices = std::max(mipslices / 2, 1); - } - - if (mipcount > 1) - return MIPMAPS_DATA; - else - return MIPMAPS_NONE; -} - bool Image::getConstant(const char *in, SettingType &out) { return settingTypes.find(in, out); diff --git a/src/modules/graphics/Image.h b/src/modules/graphics/Image.h index e04533616..a8aeabb7c 100644 --- a/src/modules/graphics/Image.h +++ b/src/modules/graphics/Image.h @@ -24,8 +24,6 @@ #include "common/config.h" #include "common/StringMap.h" #include "common/math.h" -#include "image/ImageData.h" -#include "image/CompressedImageData.h" #include "Texture.h" namespace love @@ -39,13 +37,6 @@ public: static love::Type type; - enum MipmapsType - { - MIPMAPS_NONE, - MIPMAPS_DATA, - MIPMAPS_GENERATED, - }; - enum SettingType { SETTING_MIPMAPS, @@ -61,36 +52,6 @@ public: float dpiScale = 1.0f; }; - struct Slices - { - public: - - Slices(TextureType textype); - - void clear(); - void set(int slice, int mipmap, love::image::ImageDataBase *data); - love::image::ImageDataBase *get(int slice, int mipmap) const; - - void add(love::image::CompressedImageData *cdata, int startslice, int startmip, bool addallslices, bool addallmips); - - int getSliceCount(int mip = 0) const; - int getMipmapCount(int slice = 0) const; - - MipmapsType validate() const; - - TextureType getTextureType() const { return textureType; } - - private: - - TextureType textureType; - - // For 2D/Cube/2DArray texture types, each element in the data array has - // an array of mipmap levels. For 3D texture types, each mipmap level - // has an array of layers. - std::vector>> data; - - }; // Slices - virtual ~Image(); void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, int x, int y, bool reloadmipmaps); diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index cbbbf0b75..f54addfdb 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -382,6 +382,165 @@ bool Texture::validateDimensions(bool throwException) const return success; } +Texture::Slices::Slices(TextureType textype) + : textureType(textype) +{ +} + +void Texture::Slices::clear() +{ + data.clear(); +} + +void Texture::Slices::set(int slice, int mipmap, love::image::ImageDataBase *d) +{ + if (textureType == TEXTURE_VOLUME) + { + if (mipmap >= (int) data.size()) + data.resize(mipmap + 1); + + if (slice >= (int) data[mipmap].size()) + data[mipmap].resize(slice + 1); + + data[mipmap][slice].set(d); + } + else + { + if (slice >= (int) data.size()) + data.resize(slice + 1); + + if (mipmap >= (int) data[slice].size()) + data[slice].resize(mipmap + 1); + + data[slice][mipmap].set(d); + } +} + +love::image::ImageDataBase *Texture::Slices::get(int slice, int mipmap) const +{ + if (slice < 0 || slice >= getSliceCount(mipmap)) + return nullptr; + + if (mipmap < 0 || mipmap >= getMipmapCount(slice)) + return nullptr; + + if (textureType == TEXTURE_VOLUME) + return data[mipmap][slice].get(); + else + return data[slice][mipmap].get(); +} + +void Texture::Slices::add(love::image::CompressedImageData *cdata, int startslice, int startmip, bool addallslices, bool addallmips) +{ + int slicecount = addallslices ? cdata->getSliceCount() : 1; + int mipcount = addallmips ? cdata->getMipmapCount() : 1; + + for (int mip = 0; mip < mipcount; mip++) + { + for (int slice = 0; slice < slicecount; slice++) + set(startslice + slice, startmip + mip, cdata->getSlice(slice, mip)); + } +} + +int Texture::Slices::getSliceCount(int mip) const +{ + if (textureType == TEXTURE_VOLUME) + { + if (mip < 0 || mip >= (int) data.size()) + return 0; + + return (int) data[mip].size(); + } + else + return (int) data.size(); +} + +int Texture::Slices::getMipmapCount(int slice) const +{ + if (textureType == TEXTURE_VOLUME) + return (int) data.size(); + else + { + if (slice < 0 || slice >= (int) data.size()) + return 0; + + return data[slice].size(); + } +} + +bool Texture::Slices::validate() const +{ + int slicecount = getSliceCount(); + int mipcount = getMipmapCount(0); + + if (slicecount == 0 || mipcount == 0) + throw love::Exception("At least one ImageData or CompressedImageData is required!"); + + if (textureType == TEXTURE_CUBE && slicecount != 6) + throw love::Exception("Cube textures must have exactly 6 sides."); + + image::ImageDataBase *firstdata = get(0, 0); + + int w = firstdata->getWidth(); + int h = firstdata->getHeight(); + int depth = textureType == TEXTURE_VOLUME ? slicecount : 1; + PixelFormat format = firstdata->getFormat(); + + int expectedmips = Texture::getTotalMipmapCount(w, h, depth); + + if (mipcount != expectedmips && mipcount != 1) + throw love::Exception("Image does not have all required mipmap levels (expected %d, got %d)", expectedmips, mipcount); + + if (textureType == TEXTURE_CUBE && w != h) + throw love::Exception("Cube images must have equal widths and heights for each cube face."); + + int mipw = w; + int miph = h; + int mipslices = slicecount; + + for (int mip = 0; mip < mipcount; mip++) + { + if (textureType == TEXTURE_VOLUME) + { + slicecount = getSliceCount(mip); + + if (slicecount != mipslices) + throw love::Exception("Invalid number of image data layers in mipmap level %d (expected %d, got %d)", mip+1, mipslices, slicecount); + } + + for (int slice = 0; slice < slicecount; slice++) + { + auto slicedata = get(slice, mip); + + if (slicedata == nullptr) + throw love::Exception("Missing image data (slice %d, mipmap level %d)", slice+1, mip+1); + + int realw = slicedata->getWidth(); + int realh = slicedata->getHeight(); + + if (getMipmapCount(slice) != mipcount) + throw love::Exception("All Image layers must have the same mipmap count."); + + if (mipw != realw) + throw love::Exception("Width of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, mipw, realw); + + if (miph != realh) + throw love::Exception("Height of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, miph, realh); + + if (format != slicedata->getFormat()) + throw love::Exception("All Image slices and mipmaps must have the same pixel format."); + } + + mipw = std::max(mipw / 2, 1); + miph = std::max(miph / 2, 1); + + if (textureType == TEXTURE_VOLUME) + mipslices = std::max(mipslices / 2, 1); + } + + return true; +} + bool Texture::getConstant(const char *in, TextureType &out) { return texTypes.find(in, out); diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index c308e8fc2..42239d3df 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -33,6 +33,8 @@ #include "vertex.h" #include "renderstate.h" #include "Resource.h" +#include "image/ImageData.h" +#include "image/CompressedImageData.h" // C #include @@ -96,6 +98,43 @@ public: WrapMode r = WRAP_CLAMP; }; + enum MipmapsType + { + MIPMAPS_NONE, + MIPMAPS_DATA, + MIPMAPS_GENERATED, + }; + + struct Slices + { + public: + + Slices(TextureType textype); + + void clear(); + void set(int slice, int mipmap, love::image::ImageDataBase *data); + love::image::ImageDataBase *get(int slice, int mipmap) const; + + void add(love::image::CompressedImageData *cdata, int startslice, int startmip, bool addallslices, bool addallmips); + + int getSliceCount(int mip = 0) const; + int getMipmapCount(int slice = 0) const; + + bool validate() const; + + TextureType getTextureType() const { return textureType; } + + private: + + TextureType textureType; + + // For 2D/Cube/2DArray texture types, each element in the data array has + // an array of mipmap levels. For 3D texture types, each mipmap level + // has an array of layers. + std::vector>> data; + + }; // Slices + static Filter defaultFilter; static FilterMode defaultMipmapFilter; static float defaultMipmapSharpness; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index b443718fa..e7a821385 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -130,7 +130,7 @@ love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferType type, size_t return CreateStreamBuffer(type, size); } -love::graphics::Image *Graphics::newImage(const Image::Slices &data, const Image::Settings &settings) +love::graphics::Image *Graphics::newImage(const Texture::Slices &data, const Image::Settings &settings) { return new Image(data, settings); } diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 7255ddfc2..9dbdb3033 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -60,7 +60,7 @@ public: // Implements Module. const char *getName() const override; - love::graphics::Image *newImage(const Image::Slices &data, const Image::Settings &settings) override; + love::graphics::Image *newImage(const Texture::Slices &data, const Image::Settings &settings) override; love::graphics::Image *newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings) override; love::graphics::Canvas *newCanvas(const Canvas::Settings &settings) override; love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override; diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 451d04f82..327ec5fe8 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -757,7 +757,7 @@ getImageData(lua_State *L, int idx, bool allowcompressed, float *dpiscale) return std::make_pair(idata, cdata); } -static int w__pushNewImage(lua_State *L, Image::Slices &slices, const Image::Settings &settings) +static int w__pushNewImage(lua_State *L, Texture::Slices &slices, const Image::Settings &settings) { StrongRef i; luax_catchexcept(L, @@ -773,7 +773,7 @@ int w_newCubeImage(lua_State *L) { luax_checkgraphicscreated(L); - Image::Slices slices(TEXTURE_CUBE); + Texture::Slices slices(TEXTURE_CUBE); bool dpiscaleset = false; Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset); @@ -867,7 +867,7 @@ int w_newArrayImage(lua_State *L) { luax_checkgraphicscreated(L); - Image::Slices slices(TEXTURE_2D_ARRAY); + Texture::Slices slices(TEXTURE_2D_ARRAY); bool dpiscaleset = false; Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset); @@ -933,7 +933,7 @@ int w_newVolumeImage(lua_State *L) auto imagemodule = Module::getInstance(Module::M_IMAGE); - Image::Slices slices(TEXTURE_VOLUME); + Texture::Slices slices(TEXTURE_VOLUME); bool dpiscaleset = false; Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset); @@ -1004,7 +1004,7 @@ int w_newImage(lua_State *L) { luax_checkgraphicscreated(L); - Image::Slices slices(TEXTURE_2D); + Texture::Slices slices(TEXTURE_2D); bool dpiscaleset = false; Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset);