diff --git a/src/modules/graphics/Image.cpp b/src/modules/graphics/Image.cpp index 3bfa2265a..1e639c3b0 100644 --- a/src/modules/graphics/Image.cpp +++ b/src/modules/graphics/Image.cpp @@ -39,6 +39,8 @@ Image::Image(const Slices &data, const Settings &settings, bool validatedata) , data(data) , mipmapsType(settings.mipmaps ? MIPMAPS_GENERATED : MIPMAPS_NONE) , sRGB(isGammaCorrect() && !settings.linear) + , usingDefaultTexture(false) + , textureMemorySize(0) { if (validatedata && data.validate() == MIPMAPS_DATA) mipmapsType = MIPMAPS_DATA; @@ -46,11 +48,137 @@ Image::Image(const Slices &data, const Settings &settings, bool validatedata) ++imageCount; } +Image::Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings) + : Image(Slices(textype), settings, false) +{ + if (isPixelFormatCompressed(format)) + throw love::Exception("This constructor is only supported for non-compressed pixel formats."); + + if (textype == TEXTURE_2D_ARRAY) + layers = slices; + else if (textype == TEXTURE_VOLUME) + depth = slices; + + init(format, width, height, settings); +} + +Image::Image(const Slices &slices, const Settings &settings) + : Image(slices, settings, true) +{ + if (texType == TEXTURE_2D_ARRAY) + this->layers = data.getSliceCount(); + else if (texType == TEXTURE_VOLUME) + this->depth = data.getSliceCount(); + + love::image::ImageDataBase *slice = data.get(0, 0); + init(slice->getFormat(), slice->getWidth(), slice->getHeight(), settings); +} + Image::~Image() { --imageCount; } +void Image::init(PixelFormat fmt, int w, int h, const Settings &settings) +{ + pixelWidth = w; + pixelHeight = h; + + width = (int) (pixelWidth / settings.pixeldensity + 0.5); + height = (int) (pixelHeight / settings.pixeldensity + 0.5); + + mipmapCount = mipmapsType == MIPMAPS_NONE ? 1 : getMipmapCount(w, h); + format = fmt; + + if (isCompressed() && mipmapsType == MIPMAPS_GENERATED) + mipmapsType = MIPMAPS_NONE; + + if (getMipmapCount() > 1) + filter.mipmap = defaultMipmapFilter; + + initQuad(); +} + +void Image::uploadImageData(love::image::ImageDataBase *d, int level, int slice) +{ + love::image::ImageData *id = dynamic_cast(d); + + love::thread::EmptyLock lock; + if (id != nullptr) + lock.setLock(id->getMutex()); + + Rect rect = {0, 0, d->getWidth(), d->getHeight()}; + uploadByteData(d->getFormat(), d->getData(), d->getSize(), rect, level, slice); +} + +void Image::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps) +{ + // No effect if the texture hasn't been created yet. + if (getHandle() == 0 || usingDefaultTexture) + return; + + if (d->getFormat() != getPixelFormat()) + throw love::Exception("Pixel formats must match."); + + if (mipmap < 0 || (mipmapsType != MIPMAPS_DATA && mipmap > 0) || mipmap >= getMipmapCount()) + throw love::Exception("Invalid image mipmap index."); + + if (slice < 0 || (texType == TEXTURE_CUBE && slice >= 6) + || (texType == TEXTURE_VOLUME && slice >= std::max(getDepth() >> mipmap, 1)) + || (texType == TEXTURE_2D_ARRAY && slice >= getLayerCount())) + { + throw love::Exception("Invalid image slice index."); + } + + love::image::ImageDataBase *oldd = data.get(slice, mipmap); + + if (oldd == nullptr) + throw love::Exception("Image does not store ImageData!"); + + int w = d->getWidth(); + int h = d->getHeight(); + + if (w != oldd->getWidth() || h != oldd->getHeight()) + throw love::Exception("Dimensions must match the texture's dimensions for the specified mipmap level."); + + Graphics::flushStreamDrawsGlobal(); + + d->retain(); + oldd->release(); + + data.set(slice, mipmap, d); + + uploadImageData(d, mipmap, slice); + + if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1) + generateMipmaps(); +} + +void Image::replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps) +{ + Graphics::flushStreamDrawsGlobal(); + + uploadByteData(format, data, size, rect, mipmap, slice); + + if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1) + generateMipmaps(); +} + +bool Image::isCompressed() const +{ + return isPixelFormatCompressed(format); +} + +bool Image::isFormatLinear() const +{ + return isGammaCorrect() && !sRGB; +} + +Image::MipmapsType Image::getMipmapsType() const +{ + return mipmapsType; +} + Image::Slices::Slices(TextureType textype) : textureType(textype) { diff --git a/src/modules/graphics/Image.h b/src/modules/graphics/Image.h index e15defec8..ea6274908 100644 --- a/src/modules/graphics/Image.h +++ b/src/modules/graphics/Image.h @@ -85,19 +85,24 @@ public: virtual ~Image(); - virtual void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps) = 0; - virtual void replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps) = 0; + void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps); + void replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps); - virtual bool isFormatLinear() const = 0; - virtual bool isCompressed() const = 0; - - virtual MipmapsType getMipmapsType() const = 0; + bool isFormatLinear() const; + bool isCompressed() const; + MipmapsType getMipmapsType() const; static int imageCount; protected: - Image(const Slices &data, const Settings &settings, bool validatedata); + Image(const Slices &data, const Settings &settings); + Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings); + + void uploadImageData(love::image::ImageDataBase *d, int level, int slice); + virtual void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, const Rect &rect, int level, int slice) = 0; + + virtual void generateMipmaps() = 0; // The settings used to initialize this Image. Settings settings; @@ -106,7 +111,19 @@ protected: MipmapsType mipmapsType; bool sRGB; - + + // True if the image wasn't able to be properly created and it had to fall + // back to a default texture. + bool usingDefaultTexture; + + size_t textureMemorySize; + +private: + + Image(const Slices &data, const Settings &settings, bool validatedata); + + void init(PixelFormat fmt, int w, int h, const Settings &settings); + }; // Image } // graphics diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 05c94265e..4908b01d8 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -454,11 +454,10 @@ ptrdiff_t Canvas::getHandle() const love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r) { - love::image::ImageData *imagedata = love::graphics::Canvas::newImageData(module, slice, mipmap, r); - PixelFormat dataformat = imagedata->getFormat(); + love::image::ImageData *data = love::graphics::Canvas::newImageData(module, slice, mipmap, r); bool isSRGB = false; - OpenGL::TextureFormat fmt = gl.convertPixelFormat(dataformat, false, isSRGB); + OpenGL::TextureFormat fmt = gl.convertPixelFormat(data->getFormat(), false, isSRGB); GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO()); @@ -470,14 +469,14 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, mipmap, layer, face); } - glReadPixels(r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, imagedata->getData()); + glReadPixels(r.x, r.y, r.w, r.h, fmt.externalformat, fmt.type, data->getData()); if (slice > 0 || mipmap > 0) gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0); gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); - return imagedata; + return data; } void Canvas::generateMipmaps() diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index ab3cb2be8..2a94a9a56 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -34,37 +34,17 @@ namespace opengl { Image::Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings) - : love::graphics::Image(Slices(textype), settings, false) + : love::graphics::Image(textype, format, width, height, slices, settings) , texture(0) - , compressed(false) - , usingDefaultTexture(false) - , textureMemorySize(0) { - if (isPixelFormatCompressed(format)) - throw love::Exception("This constructor is only supported for non-compressed pixel formats."); - - if (textype == TEXTURE_VOLUME) - depth = slices; - else if (textype == TEXTURE_2D_ARRAY) - layers = slices; - - init(format, width, height, settings); + loadVolatile(); } Image::Image(const Slices &slices, const Settings &settings) - : love::graphics::Image(slices, settings, true) + : love::graphics::Image(slices, settings) , texture(0) - , compressed(false) - , usingDefaultTexture(false) - , textureMemorySize(0) { - if (texType == TEXTURE_2D_ARRAY) - this->layers = data.getSliceCount(); - else if (texType == TEXTURE_VOLUME) - this->depth = data.getSliceCount(); - - love::image::ImageDataBase *slice = data.get(0, 0); - init(slice->getFormat(), slice->getWidth(), slice->getHeight(), settings); + loadVolatile(); } Image::~Image() @@ -72,33 +52,13 @@ Image::~Image() unloadVolatile(); } -void Image::init(PixelFormat fmt, int w, int h, const Settings &settings) -{ - pixelWidth = w; - pixelHeight = h; - - width = (int) (pixelWidth / settings.pixeldensity + 0.5); - height = (int) (pixelHeight / settings.pixeldensity + 0.5); - - mipmapCount = mipmapsType == MIPMAPS_NONE ? 1 : getMipmapCount(w, h); - format = fmt; - compressed = isPixelFormatCompressed(format); - - if (compressed && mipmapsType == MIPMAPS_GENERATED) - mipmapsType = MIPMAPS_NONE; - - if (getMipmapCount() > 1) - filter.mipmap = defaultMipmapFilter; - - initQuad(); - loadVolatile(); -} - void Image::generateMipmaps() { if (getMipmapCount() > 1 && !isCompressed() && (GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object)) { + gl.bindTextureToUnit(this, 0, false); + GLenum gltextype = OpenGL::getGLTextureType(texType); if (gl.bugs.generateMipmapsRequiresTexture2DEnable) @@ -189,6 +149,10 @@ void Image::loadData() void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, const Rect &r, int level, int slice) { + OpenGL::TempDebugGroup debuggroup("Image data upload"); + + gl.bindTextureToUnit(this, 0, false); + OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false, sRGB); GLenum gltarget = OpenGL::getGLTextureType(texType); @@ -214,18 +178,6 @@ void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t siz } } -void Image::uploadImageData(love::image::ImageDataBase *d, int level, int slice) -{ - love::image::ImageData *id = dynamic_cast(d); - - love::thread::EmptyLock lock; - if (id != nullptr) - lock.setLock(id->getMutex()); - - Rect rect = {0, 0, d->getWidth(), d->getHeight()}; - uploadByteData(d->getFormat(), d->getData(), d->getSize(), rect, level, slice); -} - bool Image::loadVolatile() { if (texture != 0) @@ -343,67 +295,6 @@ void Image::unloadVolatile() textureMemorySize = 0; } -void Image::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps) -{ - // No effect if the texture hasn't been created yet. - if (getHandle() == 0 || usingDefaultTexture) - return; - - if (d->getFormat() != getPixelFormat()) - throw love::Exception("Pixel formats must match."); - - if (mipmap < 0 || (mipmapsType != MIPMAPS_DATA && mipmap > 0) || mipmap >= getMipmapCount()) - throw love::Exception("Invalid image mipmap index."); - - if (slice < 0 || (texType == TEXTURE_CUBE && slice >= 6) - || (texType == TEXTURE_VOLUME && slice >= std::max(getDepth() >> mipmap, 1)) - || (texType == TEXTURE_2D_ARRAY && slice >= getLayerCount())) - { - throw love::Exception("Invalid image slice index."); - } - - love::image::ImageDataBase *oldd = data.get(slice, mipmap); - - if (oldd == nullptr) - throw love::Exception("Image does not store ImageData!"); - - int w = d->getWidth(); - int h = d->getHeight(); - - if (w != oldd->getWidth() || h != oldd->getHeight()) - throw love::Exception("Dimensions must match the texture's dimensions for the specified mipmap level."); - - Graphics::flushStreamDrawsGlobal(); - - d->retain(); - oldd->release(); - - data.set(slice, mipmap, d); - - OpenGL::TempDebugGroup debuggroup("Image replace pixels"); - - gl.bindTextureToUnit(this, 0, false); - - uploadImageData(d, mipmap, slice); - - if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1) - generateMipmaps(); -} - -void Image::replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps) -{ - Graphics::flushStreamDrawsGlobal(); - - OpenGL::TempDebugGroup debuggroup("Image replace pixels"); - - gl.bindTextureToUnit(this, 0, false); - - uploadByteData(format, data, size, rect, mipmap, slice); - - if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1) - generateMipmaps(); -} - ptrdiff_t Image::getHandle() const { return texture; @@ -476,8 +367,9 @@ bool Image::setMipmapSharpness(float sharpness) Graphics::flushStreamDrawsGlobal(); float maxbias = gl.getMaxLODBias(); + if (maxbias > 0.01f) - maxbias -= 0.0f; + maxbias -= 0.01f; mipmapSharpness = std::min(std::max(sharpness, -maxbias), maxbias); @@ -489,21 +381,6 @@ bool Image::setMipmapSharpness(float sharpness) return true; } -bool Image::isFormatLinear() const -{ - return isGammaCorrect() && !sRGB; -} - -bool Image::isCompressed() const -{ - return compressed; -} - -Image::MipmapsType Image::getMipmapsType() const -{ - return mipmapsType; -} - bool Image::isFormatSupported(PixelFormat pixelformat) { return OpenGL::isPixelFormatSupported(pixelformat, false, true, false); diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 7a5fdd81a..af7e771ad 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -54,38 +54,20 @@ public: bool setMipmapSharpness(float sharpness) override; - void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, bool reloadmipmaps) override; - void replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps) override; - - bool isFormatLinear() const override; - bool isCompressed() const override; - MipmapsType getMipmapsType() const override; - static bool isFormatSupported(PixelFormat pixelformat); static bool hasSRGBSupport(); private: - void init(PixelFormat fmt, int w, int h, const Settings &settings); + void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, const Rect &rect, int level, int slice) override; + void generateMipmaps() override; - void generateMipmaps(); void loadDefaultTexture(); void loadData(); - void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, const Rect &rect, int level, int slice); - void uploadImageData(love::image::ImageDataBase *d, int level, int slice); // OpenGL texture identifier. GLuint texture; - // Whether this Image is using a compressed texture. - bool compressed; - - // True if the image wasn't able to be properly created and it had to fall - // back to a default texture. - bool usingDefaultTexture; - - size_t textureMemorySize; - }; // Image } // opengl