diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index f29ae4a47..10f8cf8e1 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -186,6 +186,7 @@ StringMap::Entry Graphics::suppor { "dxt", Graphics::SUPPORT_DXT }, { "bc5", Graphics::SUPPORT_BC5 }, { "instancing", Graphics::SUPPORT_INSTANCING }, + { "srgb", Graphics::SUPPORT_SRGB }, }; StringMap Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries)); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 25fc45c50..e91d96e8f 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -95,6 +95,7 @@ public: SUPPORT_DXT, SUPPORT_BC5, SUPPORT_INSTANCING, + SUPPORT_SRGB, SUPPORT_MAX_ENUM }; @@ -128,7 +129,7 @@ public: * @param width The viewport width. * @param height The viewport height. **/ - virtual bool setMode(int width, int height) = 0; + virtual bool setMode(int width, int height, bool &sRGB) = 0; /** * Un-sets the current graphics display mode (uninitializing objects if diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index 25034a3d0..0a8d85541 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -109,6 +109,16 @@ bool Texture::getConstant(WrapMode in, const char *&out) return wrapModes.find(in, out); } +bool Texture::getConstant(const char *in, Format &out) +{ + return formats.find(in, out); +} + +bool Texture::getConstant(Format in, const char *&out) +{ + return formats.find(in, out); +} + StringMap::Entry Texture::filterModeEntries[] = { { "linear", Texture::FILTER_LINEAR }, @@ -125,6 +135,15 @@ StringMap::Entry Texture::wrapModeEnt StringMap Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries)); +StringMap::Entry Texture::formatEntries[] = +{ + {"normal", Texture::FORMAT_NORMAL}, + {"hdr", Texture::FORMAT_HDR}, + {"srgb", Texture::FORMAT_SRGB}, +}; + +StringMap Texture::formats(Texture::formatEntries, sizeof(Texture::formatEntries)); + } // graphics } // love diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index 7b5a525a4..d9c83171e 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -55,6 +55,14 @@ public: FILTER_MAX_ENUM }; + enum Format + { + FORMAT_NORMAL, + FORMAT_HDR, + FORMAT_SRGB, + FORMAT_MAX_ENUM + }; + struct Filter { Filter(); @@ -111,6 +119,9 @@ public: static bool getConstant(const char *in, WrapMode &out); static bool getConstant(WrapMode in, const char *&out); + static bool getConstant(const char *in, Format &out); + static bool getConstant(Format in, const char *&out); + protected: int width; @@ -132,6 +143,9 @@ private: static StringMap::Entry wrapModeEntries[]; static StringMap wrapModes; + static StringMap::Entry formatEntries[]; + static StringMap formats; + }; // Texture } // graphics diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 090aeb028..ed9ff6113 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -369,7 +369,7 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT } }; -FramebufferStrategy *strategy = NULL; +FramebufferStrategy *strategy = nullptr; FramebufferStrategy strategyNone; @@ -379,8 +379,9 @@ FramebufferStrategyPackedEXT strategyPackedEXT; FramebufferStrategyEXT strategyEXT; -Canvas *Canvas::current = NULL; +Canvas *Canvas::current = nullptr; OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport(); +bool Canvas::screenHasSRGB = false; static void getStrategy() { @@ -397,13 +398,13 @@ static void getStrategy() } } -Canvas::Canvas(int width, int height, TextureType texture_type, int fsaa) +Canvas::Canvas(int width, int height, Texture::Format format, int fsaa) : fbo(0) , resolve_fbo(0) , texture(0) , fsaa_buffer(0) , depth_stencil(0) - , texture_type(texture_type) + , format(format) , fsaa_samples(fsaa) , fsaa_dirty(false) { @@ -507,13 +508,17 @@ bool Canvas::loadVolatile() GLint internalformat; GLenum textype; - switch (texture_type) + switch (format) { - case TYPE_HDR: + case Texture::FORMAT_HDR: internalformat = GL_RGBA16F; textype = GL_FLOAT; break; - case TYPE_NORMAL: + case Texture::FORMAT_SRGB: + internalformat = GL_SRGB8_ALPHA8; + textype = GL_UNSIGNED_BYTE; + break; + case Texture::FORMAT_NORMAL: default: internalformat = GL_RGBA8; textype = GL_UNSIGNED_BYTE; @@ -681,6 +686,12 @@ void Canvas::setupGrab() // Switch back to modelview matrix glMatrixMode(GL_MODELVIEW); + // Make sure the correct sRGB setting is used when drawing to the canvas. + if (format == FORMAT_SRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + else if (screenHasSRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + if (fsaa_buffer != 0) fsaa_dirty = true; } @@ -708,8 +719,8 @@ void Canvas::startGrab(const std::vector &canvases) if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height) throw love::Exception("All canvas arguments must have the same dimensions."); - if (canvases[i]->getTextureType() != texture_type) - throw love::Exception("All canvas arguments must have the same texture type."); + if (canvases[i]->getTextureFormat() != format) + throw love::Exception("All canvas arguments must have the same texture format."); if (canvases[i]->getFSAA() != 0) throw love::Exception("Multi-canvas rendering is not supported with FSAA."); @@ -763,12 +774,22 @@ void Canvas::stopGrab(bool switchingToOtherCanvas) glPopMatrix(); glMatrixMode(GL_MODELVIEW); - // bind default - if (!switchingToOtherCanvas) + if (switchingToOtherCanvas) { + if (format == FORMAT_SRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + } + else + { + // bind system framebuffer. strategy->bindFBO(0); current = nullptr; gl.setViewport(systemViewport); + + if (format == FORMAT_SRGB && !screenHasSRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + else if (format != FORMAT_SRGB && screenHasSRGB) + glEnable(GL_FRAMEBUFFER_SRGB); } } @@ -940,6 +961,18 @@ bool Canvas::isHDRSupported() return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float); } +bool Canvas::isSRGBSupported() +{ + if (GLEE_VERSION_3_0) + return true; + + if (!isSupported()) + return false; + + return (GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB) + && GLEE_EXT_texture_sRGB; +} + bool Canvas::isMultiCanvasSupported() { // system must support at least 4 simultanious active canvases. @@ -952,23 +985,6 @@ void Canvas::bindDefaultCanvas() current->stopGrab(); } -bool Canvas::getConstant(const char *in, Canvas::TextureType &out) -{ - return textureTypes.find(in, out); -} - -bool Canvas::getConstant(Canvas::TextureType in, const char *&out) -{ - return textureTypes.find(in, out); -} - -StringMap::Entry Canvas::textureTypeEntries[] = -{ - {"normal", Canvas::TYPE_NORMAL}, - {"hdr", Canvas::TYPE_HDR}, -}; -StringMap Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries)); - } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index 86db8f0f9..2d87b0bbc 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -39,14 +39,7 @@ class Canvas : public Texture { public: - enum TextureType - { - TYPE_NORMAL, - TYPE_HDR, - TYPE_MAX_ENUM - }; - - Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL, int fsaa = 0); + Canvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0); virtual ~Canvas(); // Implements Volatile. @@ -92,9 +85,9 @@ public: return status; } - inline TextureType getTextureType() const + inline Texture::Format getTextureFormat() const { - return texture_type; + return format; } inline int getFSAA() const @@ -106,17 +99,18 @@ public: static bool isSupported(); static bool isHDRSupported(); + static bool isSRGBSupported(); static bool isMultiCanvasSupported(); - static bool getConstant(const char *in, TextureType &out); - static bool getConstant(TextureType in, const char *&out); - static Canvas *current; static void bindDefaultCanvas(); // The viewport dimensions of the system (default) framebuffer. static OpenGL::Viewport systemViewport; + // Whether the main screen should have linear -> sRGB conversions enabled. + static bool screenHasSRGB; + private: bool createFSAAFBO(GLenum internalformat); @@ -128,7 +122,7 @@ private: GLuint fsaa_buffer; GLuint depth_stencil; - TextureType texture_type; + Format format; GLenum status; @@ -140,9 +134,7 @@ private: void setupGrab(); void drawv(const Matrix &t, const Vertex *v); - static StringMap::Entry textureTypeEntries[]; - static StringMap textureTypes; -}; +}; // Canvas } // opengl } // graphics diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 8539c140b..64298dc51 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -57,8 +57,13 @@ Graphics::Graphics() { currentWindow = love::window::sdl::Window::createSingleton(); + int w, h; + love::window::WindowSettings wsettings; + + currentWindow->getWindow(w, h, wsettings); + if (currentWindow->isCreated()) - setMode(currentWindow->getWidth(), currentWindow->getHeight()); + setMode(w, h, wsettings.sRGB); } Graphics::~Graphics() @@ -150,7 +155,7 @@ void Graphics::setViewportSize(int width, int height) c->startGrab(c->getAttachedCanvases()); } -bool Graphics::setMode(int width, int height) +bool Graphics::setMode(int width, int height, bool &sRGB) { this->width = width; this->height = height; @@ -207,6 +212,19 @@ bool Graphics::setMode(int width, int height) glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit); matrixLimit -= 5; + // Set whether drawing converts input from linear -> sRGB colorspace. + if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB) + { + if (sRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + else + glDisable(GL_FRAMEBUFFER_SRGB); + } + else + sRGB = false; + + Canvas::screenHasSRGB = sRGB; + bool enabledebug = false; if (GLEE_VERSION_3_0) @@ -284,6 +302,9 @@ void Graphics::setDebug(bool enable) glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE); glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE); + if (GLEE_VERSION_4_3 || GLEE_KHR_debug) + glEnable(GL_DEBUG_OUTPUT); + ::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n"); } @@ -374,10 +395,10 @@ void Graphics::discardStencil() glDisable(GL_STENCIL_TEST); } -Image *Graphics::newImage(love::image::ImageData *data) +Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format) { // Create the image. - Image *image = new Image(data); + Image *image = new Image(data, format); if (!isCreated()) return image; @@ -401,10 +422,10 @@ Image *Graphics::newImage(love::image::ImageData *data) return image; } -Image *Graphics::newImage(love::image::CompressedData *cdata) +Image *Graphics::newImage(love::image::CompressedData *cdata, Texture::Format format) { // Create the image. - Image *image = new Image(cdata); + Image *image = new Image(cdata, format); if (!isCreated()) return image; @@ -448,11 +469,14 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size) return new ParticleSystem(texture, size); } -Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type, int fsaa) +Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int fsaa) { - if (texture_type == Canvas::TYPE_HDR && !Canvas::isHDRSupported()) + if (format == Texture::FORMAT_HDR && !Canvas::isHDRSupported()) throw Exception("HDR Canvases are not supported by your OpenGL implementation"); + if (format == Texture::FORMAT_SRGB && !Canvas::isSRGBSupported()) + throw Exception("sRGB Canvases are not supported by your OpenGL implementation"); + if (width > gl.getMaxTextureSize()) throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width); else if (height > gl.getMaxTextureSize()) @@ -461,7 +485,7 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_t while (GL_NO_ERROR != glGetError()) /* clear opengl error flag */; - Canvas *canvas = new Canvas(width, height, texture_type, fsaa); + Canvas *canvas = new Canvas(width, height, format, fsaa); GLenum err = canvas->getStatus(); // everything ok, return canvas (early out) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index ecba2806e..276b06378 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -112,7 +112,7 @@ public: void restoreState(const DisplayState &s); virtual void setViewportSize(int width, int height); - virtual bool setMode(int width, int height); + virtual bool setMode(int width, int height, bool &sRGB); virtual void unSetMode(); void setDebug(bool enable); @@ -191,8 +191,8 @@ public: /** * Creates an Image object with padding and/or optimization. **/ - Image *newImage(love::image::ImageData *data); - Image *newImage(love::image::CompressedData *cdata); + Image *newImage(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL); + Image *newImage(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL); Quad *newQuad(Quad::Viewport v, float sw, float sh); @@ -205,7 +205,7 @@ public: ParticleSystem *newParticleSystem(Texture *texture, int size); - Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL, int fsaa = 0); + Canvas *newCanvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0); Shader *newShader(const Shader::ShaderSources &sources); diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index e3ef771dd..2166fd4a5 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -36,7 +36,7 @@ float Image::maxMipmapSharpness = 0.0f; Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE; float Image::defaultMipmapSharpness = 0.0f; -Image::Image(love::image::ImageData *data) +Image::Image(love::image::ImageData *data, Texture::Format format) : data(data) , cdata(nullptr) , paddedWidth(width) @@ -45,6 +45,7 @@ Image::Image(love::image::ImageData *data) , mipmapSharpness(defaultMipmapSharpness) , mipmapsCreated(false) , compressed(false) + , format(format) , usingDefaultTexture(false) { width = data->getWidth(); @@ -54,7 +55,7 @@ Image::Image(love::image::ImageData *data) preload(); } -Image::Image(love::image::CompressedData *cdata) +Image::Image(love::image::CompressedData *cdata, Texture::Format format) : data(nullptr) , cdata(cdata) , paddedWidth(width) @@ -63,6 +64,7 @@ Image::Image(love::image::CompressedData *cdata) , mipmapSharpness(defaultMipmapSharpness) , mipmapsCreated(false) , compressed(true) + , format(format) , usingDefaultTexture(false) { width = cdata->getWidth(0); @@ -328,6 +330,9 @@ void Image::unload() bool Image::loadVolatile() { + if (format == FORMAT_SRGB && !hasSRGBSupport()) + throw love::Exception("sRGB images are not supported on this system."); + if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat())) { const char *str; @@ -400,9 +405,10 @@ void Image::uploadTexturePadded() } else if (data) { + GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8; glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA8, + iformat, (GLsizei)paddedWidth, (GLsizei)paddedHeight, 0, @@ -437,9 +443,10 @@ void Image::uploadTexture() } else if (data) { + GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8; glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA8, + iformat, (GLsizei)width, (GLsizei)height, 0, @@ -497,6 +504,11 @@ bool Image::refresh() return true; } +Texture::Format Image::getFormat() const +{ + return format; +} + void Image::uploadDefaultTexture() { usingDefaultTexture = true; @@ -561,16 +573,27 @@ bool Image::isCompressed() const return compressed; } -GLenum Image::getCompressedFormat(image::CompressedData::Format format) const +GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const { - switch (format) + bool srgb = format == FORMAT_SRGB; + + switch (cformat) { case image::CompressedData::FORMAT_DXT1: - return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + if (srgb) + return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; + else + return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; case image::CompressedData::FORMAT_DXT3: - return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + if (srgb) + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + else + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case image::CompressedData::FORMAT_DXT5: - return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + if (srgb) + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + else + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case image::CompressedData::FORMAT_BC4: return GL_COMPRESSED_RED_RGTC1; case image::CompressedData::FORMAT_BC4s: @@ -580,7 +603,10 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format format) const case image::CompressedData::FORMAT_BC5s: return GL_COMPRESSED_SIGNED_RG_RGTC2; default: - return GL_RGBA8; + if (srgb) + return GL_SRGB8_ALPHA8; + else + return GL_RGBA8; } } @@ -632,6 +658,11 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format) return false; } +bool Image::hasSRGBSupport() +{ + return GLEE_VERSION_2_1 || GLEE_EXT_texture_sRGB; +} + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 865c5f3bd..d23f385ec 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -56,14 +56,14 @@ public: * * @param data The data from which to load the image. **/ - Image(love::image::ImageData *data); + Image(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL); /** * Creates a new Image with compressed image data. * * @param cdata The compressed data from which to load the image. **/ - Image(love::image::CompressedData *cdata); + Image(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL); /** * Destructor. Deletes the hardware texture and other resources. @@ -120,6 +120,8 @@ public: **/ bool refresh(); + Texture::Format getFormat() const; + static void setDefaultMipmapSharpness(float sharpness); static float getDefaultMipmapSharpness(); static void setDefaultMipmapFilter(FilterMode f); @@ -133,6 +135,8 @@ public: static bool hasCompressedTextureSupport(); static bool hasCompressedTextureSupport(image::CompressedData::Format format); + static bool hasSRGBSupport(); + private: void uploadDefaultTexture(); @@ -162,6 +166,9 @@ private: // Whether this Image is using a compressed texture. bool compressed; + // The format to interpret the texture's data as. + Texture::Format format; + // True if the image wasn't able to be properly created and it had to fall // back to a default texture. bool usingDefaultTexture; @@ -180,7 +187,7 @@ private: static FilterMode defaultMipmapFilter; static float defaultMipmapSharpness; - GLenum getCompressedFormat(image::CompressedData::Format format) const; + GLenum getCompressedFormat(image::CompressedData::Format cformat) const; }; // Image diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index cbc9b6ed9..a3c31b7f9 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -110,12 +110,14 @@ int w_Canvas_clear(lua_State *L) return 0; } -int w_Canvas_getType(lua_State *L) +int w_Canvas_getFormat(lua_State *L) { Canvas *canvas = luax_checkcanvas(L, 1); - Canvas::TextureType type = canvas->getTextureType(); + Texture::Format format = canvas->getTextureFormat(); const char *str; - Canvas::getConstant(type, str); + if (!Texture::getConstant(format, str)) + return luaL_error(L, "Unknown texture format."); + lua_pushstring(L, str); return 1; } @@ -142,8 +144,11 @@ static const luaL_Reg functions[] = { "getImageData", w_Canvas_getImageData }, { "getPixel", w_Canvas_getPixel }, { "clear", w_Canvas_clear }, - { "getType", w_Canvas_getType }, + { "getFormat", w_Canvas_getFormat }, { "getFSAA", w_Canvas_getFSAA }, + + // Deprecated since 0.9.1. + { "getType", w_Canvas_getFormat }, { 0, 0 } }; diff --git a/src/modules/graphics/opengl/wrap_Canvas.h b/src/modules/graphics/opengl/wrap_Canvas.h index 89c7952f2..4e48ffe03 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.h +++ b/src/modules/graphics/opengl/wrap_Canvas.h @@ -39,7 +39,7 @@ int w_Canvas_renderTo(lua_State *L); int w_Canvas_getImageData(lua_State *L); int w_Canvas_getPixel(lua_State * L); int w_Canvas_clear(lua_State *L); -int w_Canvas_getType(lua_State *L); +int w_Canvas_getFormat(lua_State *L); int w_Canvas_getFSAA(lua_State *L); extern "C" int luaopen_canvas(lua_State *L); diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 4026de632..7cd02c52f 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -152,8 +152,17 @@ int w_getMaxTextureSize(lua_State *L) int w_newImage(lua_State *L) { - love::image::ImageData *data = 0; - love::image::CompressedData *cdata = 0; + love::image::ImageData *data = nullptr; + love::image::CompressedData *cdata = nullptr; + + Texture::Format format = Texture::FORMAT_NORMAL; + const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2); + + if (fstr != nullptr && !Texture::getConstant(fstr, format)) + return luaL_error(L, "Invalid texture format: %s", fstr); + + if (format == Texture::FORMAT_HDR) // For now... + return luaL_error(L, "HDR images are not supported."); // Convert to FileData, if necessary. if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T)) @@ -185,15 +194,15 @@ int w_newImage(lua_State *L) return luaL_error(L, "Error creating image."); // Create the image. - Image *image = 0; + Image *image = nullptr; EXCEPT_GUARD( if (cdata) - image = instance->newImage(cdata); + image = instance->newImage(cdata, format); else if (data) - image = instance->newImage(data); + image = instance->newImage(data, format); ) - if (image == 0) + if (image == nullptr) return luaL_error(L, "Could not load image."); // Push the type. @@ -325,12 +334,12 @@ int w_newCanvas(lua_State *L) const char *str = luaL_optstring(L, 3, "normal"); int fsaa = luaL_optint(L, 4, 0); - Canvas::TextureType texture_type; - if (!Canvas::getConstant(str, texture_type)) - return luaL_error(L, "Invalid canvas type: %s", str); + Texture::Format format; + if (!Texture::getConstant(str, format)) + return luaL_error(L, "Invalid texture format: %s", str); Canvas *canvas = nullptr; - EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type, fsaa);) + EXCEPT_GUARD(canvas = instance->newCanvas(width, height, format, fsaa);) if (canvas == nullptr) return luaL_error(L, "Canvas not created, but no error thrown. I don't even..."); @@ -999,6 +1008,10 @@ int w_isSupported(lua_State *L) if (!GLEE_ARB_draw_instanced) supported = false; break; + case Graphics::SUPPORT_SRGB: + if (!Canvas::isSRGBSupported()) + supported = false; + break; default: supported = false; } diff --git a/src/modules/window/Window.cpp b/src/modules/window/Window.cpp index 6243830aa..1e3216c8a 100644 --- a/src/modules/window/Window.cpp +++ b/src/modules/window/Window.cpp @@ -50,6 +50,7 @@ WindowSettings::WindowSettings() , centered(true) , display(0) , highdpi(false) + , sRGB(false) { } @@ -86,6 +87,7 @@ StringMap::Entry Window::settingEntri {"centered", SETTING_CENTERED}, {"display", SETTING_DISPLAY}, {"highdpi", SETTING_HIGHDPI}, + {"srgb", SETTING_SRGB}, }; StringMap Window::settings(Window::settingEntries, sizeof(Window::settingEntries)); diff --git a/src/modules/window/Window.h b/src/modules/window/Window.h index ad247fd0b..b52900c66 100644 --- a/src/modules/window/Window.h +++ b/src/modules/window/Window.h @@ -57,6 +57,7 @@ public: SETTING_CENTERED, SETTING_DISPLAY, SETTING_HIGHDPI, + SETTING_SRGB, SETTING_MAX_ENUM }; @@ -157,6 +158,7 @@ struct WindowSettings bool centered; // = true int display; // = 0 bool highdpi; // false + bool sRGB; // false }; // WindowSettings diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 6f5f0f6e1..a5b0d1326 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -154,7 +154,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) if (!window) { // In Windows and Linux, some GL attributes are set on window creation. - setWindowGLAttributes(f.fsaa); + setWindowGLAttributes(f.fsaa, f.sRGB); const char *title = windowTitle.c_str(); int pos = f.centered ? centeredpos : uncenteredpos; @@ -190,7 +190,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) SDL_RaiseWindow(window); - if (!setContext(f.fsaa, f.vsync)) + if (!setContext(f.fsaa, f.vsync, f.sRGB)) return false; created = true; @@ -206,7 +206,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) SDL_GL_GetDrawableSize(window, &width, &height); #endif - gfx->setMode(width, height); + gfx->setMode(width, height, curMode.settings.sRGB); } // Make sure the mouse keeps its previous grab setting. @@ -226,7 +226,7 @@ bool Window::onWindowResize(int width, int height) return true; } -bool Window::setContext(int fsaa, bool vsync) +bool Window::setContext(int fsaa, bool vsync, bool sRGB) { // We would normally only need to recreate the context if FSAA changes or // SDL_GL_MakeCurrent is unsuccessful, but in Windows MakeCurrent can @@ -238,7 +238,7 @@ bool Window::setContext(int fsaa, bool vsync) } // Make sure the proper attributes are set. - setWindowGLAttributes(fsaa); + setWindowGLAttributes(fsaa, sRGB); context = SDL_GL_CreateContext(window); @@ -290,7 +290,7 @@ bool Window::setContext(int fsaa, bool vsync) return true; } -void Window::setWindowGLAttributes(int fsaa) const +void Window::setWindowGLAttributes(int fsaa, bool sRGB) const { // Set GL window attributes. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); @@ -304,6 +304,10 @@ void Window::setWindowGLAttributes(int fsaa) const SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (fsaa > 0) ? fsaa : 0); +#if SDL_VERSION_ATLEAST(2,0,1) + SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0); +#endif + // Do we want a debug context? const char *debugenv = SDL_GetHint("LOVE_GRAPHICS_DEBUG"); if (debugenv && *debugenv == '1') @@ -370,6 +374,8 @@ void Window::updateSettings(const WindowSettings &newsettings) else #endif SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); + + curMode.settings.sRGB = newsettings.sRGB; } void Window::getWindow(int &width, int &height, WindowSettings &settings) diff --git a/src/modules/window/sdl/Window.h b/src/modules/window/sdl/Window.h index 81796663a..3af5d46c5 100644 --- a/src/modules/window/sdl/Window.h +++ b/src/modules/window/sdl/Window.h @@ -90,8 +90,8 @@ public: private: - bool setContext(int fsaa, bool vsync); - void setWindowGLAttributes(int fsaa) const; + bool setContext(int fsaa, bool vsync, bool sRGB); + void setWindowGLAttributes(int fsaa, bool sRGB) const; // Update the saved window settings based on the window's actual state. void updateSettings(const WindowSettings &newsettings); diff --git a/src/modules/window/wrap_Window.cpp b/src/modules/window/wrap_Window.cpp index 395875111..42c14fa74 100644 --- a/src/modules/window/wrap_Window.cpp +++ b/src/modules/window/wrap_Window.cpp @@ -96,6 +96,7 @@ int w_setMode(lua_State *L) settings.centered = luax_boolflag(L, 3, settingName(Window::SETTING_CENTERED), true); settings.display = luax_intflag(L, 3, settingName(Window::SETTING_DISPLAY), 1); settings.highdpi = luax_boolflag(L, 3, settingName(Window::SETTING_HIGHDPI), false); + settings.sRGB = luax_boolflag(L, 3, settingName(Window::SETTING_SRGB), false); // Display index is 1-based in Lua and 0-based internally. settings.display--; @@ -151,6 +152,9 @@ int w_getMode(lua_State *L) luax_pushboolean(L, settings.highdpi); lua_setfield(L, -2, settingName(Window::SETTING_HIGHDPI)); + luax_pushboolean(L, settings.sRGB); + lua_setfield(L, -2, settingName(Window::SETTING_SRGB)); + return 3; } diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index df1f8cd3b..d5cc212fd 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -301,6 +301,7 @@ function love.init() resizable = false, centered = true, highdpi = false, + srgb = false, }, modules = { event = true, @@ -390,6 +391,7 @@ function love.init() centered = c.window.centered, display = c.window.display, highdpi = c.window.highdpi, + srgb = c.window.srgb, }), "Could not set window mode") love.window.setTitle(c.window.title or c.title) if c.window.icon then diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index 892d7dc88..aeb746199 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -539,6 +539,7 @@ const unsigned char boot_lua[] = 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, + 0x09, 0x09, 0x09, 0x73, 0x72, 0x67, 0x62, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x09, 0x09, 0x7d, 0x2c, 0x0a, 0x09, 0x09, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a, @@ -672,6 +673,8 @@ const unsigned char boot_lua[] = 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x2c, 0x0a, + 0x09, 0x09, 0x09, 0x73, 0x72, 0x67, 0x62, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2c, 0x0a, 0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54,