diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 7c1a24db6..245f82600 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -19,12 +19,45 @@ **/ #include "Graphics.h" +#include "math/MathModule.h" namespace love { namespace graphics { +static bool gammaCorrect = false; + +void setGammaCorrect(bool gammacorrect) +{ + gammaCorrect = gammacorrect; +} + +bool isGammaCorrect() +{ + return gammaCorrect; +} + +void gammaCorrectColor(Colorf &c) +{ + if (isGammaCorrect()) + { + c.r = math::Math::instance.gammaToLinear(c.r); + c.g = math::Math::instance.gammaToLinear(c.g); + c.b = math::Math::instance.gammaToLinear(c.b); + } +} + +void unGammaCorrectColor(Colorf &c) +{ + if (isGammaCorrect()) + { + c.r = math::Math::instance.linearToGamma(c.r); + c.g = math::Math::instance.linearToGamma(c.g); + c.b = math::Math::instance.linearToGamma(c.b); + } +} + Graphics::~Graphics() { } @@ -150,7 +183,6 @@ StringMap::Entry Graphics::suppor { { "multicanvas", SUPPORT_MULTI_CANVAS }, { "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS }, - { "srgb", 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 304a514f2..0e5877321 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -24,6 +24,7 @@ // LOVE #include "common/Module.h" #include "common/StringMap.h" +#include "Color.h" // C++ #include @@ -33,6 +34,31 @@ namespace love namespace graphics { +/** + * Globally sets whether gamma correction is enabled. Ideally this should be set + * prior to using any Graphics module function. + **/ +void setGammaCorrect(bool gammacorrect); + +/** + * Gets whether global gamma correction is enabled. + **/ +bool isGammaCorrect(); + +/** + * Gamma-corrects a color (converts it from sRGB to linear RGB, if + * gamma correction is enabled.) + * The color's components are expected to be in the range of [0, 1]. + **/ +void gammaCorrectColor(Colorf &c); + +/** + * Un-gamma-corrects a color (converts it from linear RGB to sRGB, if + * gamma correction is enabled.) + * The color's components are expected to be in the range of [0, 1]. + **/ +void unGammaCorrectColor(Colorf &c); + class Graphics : public Module { public: @@ -74,7 +100,6 @@ public: { SUPPORT_MULTI_CANVAS, SUPPORT_MULTI_CANVAS_FORMATS, - SUPPORT_SRGB, SUPPORT_MAX_ENUM }; @@ -168,7 +193,7 @@ public: * @param width The viewport width. * @param height The viewport height. **/ - virtual bool setMode(int width, int height, bool &sRGB) = 0; + virtual bool setMode(int width, int height) = 0; /** * Un-sets the current graphics display mode (uninitializing objects if diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 7869ab71a..f3c735a16 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -398,7 +398,7 @@ void Canvas::startGrab(const std::vector &canvases) // Whether the new canvas list is different from the old one. // A more thorough check is done below. bool canvaseschanged = canvases.size() != attachedCanvases.size(); - bool hasSRGBcanvas = (format == FORMAT_SRGB); + bool hasSRGBcanvas = getSizedFormat(format) == FORMAT_SRGB; if (canvases.size() > 0) { @@ -430,7 +430,7 @@ void Canvas::startGrab(const std::vector &canvases) if (!canvaseschanged && canvases[i] != attachedCanvases[i]) canvaseschanged = true; - if (otherformat == FORMAT_SRGB) + if (getSizedFormat(otherformat) == FORMAT_SRGB) hasSRGBcanvas = true; } @@ -484,9 +484,10 @@ void Canvas::startGrab() // Make sure the correct sRGB setting is used when drawing to the canvas. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) { - if (format == FORMAT_SRGB && !gl.hasFramebufferSRGB()) + bool isSRGB = getSizedFormat(format) == FORMAT_SRGB; + if (isSRGB && !gl.hasFramebufferSRGB()) gl.setFramebufferSRGB(true); - else if (format != FORMAT_SRGB && gl.hasFramebufferSRGB()) + else if (!isSRGB && gl.hasFramebufferSRGB()) gl.setFramebufferSRGB(false); } @@ -655,8 +656,10 @@ Canvas::Format Canvas::getSizedFormat(Canvas::Format format) switch (format) { case FORMAT_NORMAL: - // 32-bit render targets don't have guaranteed support on OpenGL ES 2. - if (GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8)) + if (isGammaCorrect()) + return FORMAT_SRGB; + else if (GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8)) + // 32-bit render targets don't have guaranteed support on GLES2. return FORMAT_RGBA4; else return FORMAT_RGBA8; diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index 8d63d15f1..23e89c215 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -46,7 +46,7 @@ public: // Different Canvas render target formats. enum Format { - FORMAT_NORMAL, // Usually RGBA8 or a similar fallback. Always supported. + FORMAT_NORMAL, // Usually SRGB, RGBA8 or a similar fallback. Always supported. FORMAT_HDR, // Usually RGBA16F. Not always supported. FORMAT_RGBA4, // RGBA with 4 bits per channel. FORMAT_RGB5A1, // RGB with 5 bits per channel, and A with 1 bit. diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 095a3926e..dd13fa219 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -26,6 +26,7 @@ #include "Graphics.h" #include "font/Font.h" #include "Polyline.h" +#include "math/MathModule.h" // C++ #include @@ -70,7 +71,7 @@ Graphics::Graphics() currentWindow->getWindow(w, h, wsettings); if (currentWindow->isOpen()) - setMode(w, h, wsettings.sRGB); + setMode(w, h); } } @@ -235,7 +236,7 @@ void Graphics::setViewportSize(int width, int height) setCanvas(canvases); } -bool Graphics::setMode(int width, int height, bool &sRGB) +bool Graphics::setMode(int width, int height) { currentWindow.set(Module::getInstance(Module::M_WINDOW)); @@ -275,12 +276,12 @@ bool Graphics::setMode(int width, int height, bool &sRGB) || GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB) { if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) - gl.setFramebufferSRGB(sRGB); + gl.setFramebufferSRGB(isGammaCorrect()); } else - sRGB = false; + setGammaCorrect(false); - Canvas::screenHasSRGB = sRGB; + Canvas::screenHasSRGB = isGammaCorrect(); bool enabledebug = false; @@ -345,7 +346,7 @@ void Graphics::unSetMode() void Graphics::setActive(bool enable) { // Make sure all pending OpenGL commands have fully executed before - // returning, if we're going from active to inactive. + // returning, when going from active to inactive. This is required on iOS. if (isCreated() && this->active && !enable) glFinish(); @@ -426,13 +427,17 @@ void Graphics::reset() origin(); } -void Graphics::clear(Color c) +void Graphics::clear(Colorf c) { - glClearColor(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f); + Colorf nc = Colorf(c.r/255.0f, c.g/255.0f, c.b/255.0f, c.a/255.0f); + + gammaCorrectColor(nc); + + glClearColor(nc.r, nc.g, nc.b, nc.a); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } -void Graphics::clear(const std::vector &colors) +void Graphics::clear(const std::vector &colors) { if (colors.size() == 0) return; @@ -447,7 +452,16 @@ void Graphics::clear(const std::vector &colors) for (int i = 0; i < (int) colors.size(); i++) { - const GLfloat c[] = {colors[i].r/255.f, colors[i].g/255.f, colors[i].b/255.f, colors[i].a/255.f}; + GLfloat c[] = {colors[i].r/255.f, colors[i].g/255.f, colors[i].b/255.f, colors[i].a/255.f}; + + // TODO: Investigate a potential bug on AMD drivers in Windows/Linux + // which apparently causes the clear color to be incorrect when mixed + // sRGB and linear render targets are active. + if (isGammaCorrect()) + { + for (int i = 0; i < 3; i++) + c[i] = math::Math::instance.gammaToLinear(c[i]); + } if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0) glClearBufferfv(GL_COLOR, i, c); @@ -785,23 +799,32 @@ Text *Graphics::newText(Font *font, const std::string &text) return new Text(font, text); } -void Graphics::setColor(Color c) +bool Graphics::isGammaCorrect() const { - glVertexAttrib4f(ATTRIB_CONSTANTCOLOR, c.r/255.0f, c.g/255.0f, c.b/255.0f, c.a/255.0f); + return love::graphics::isGammaCorrect(); +} + +void Graphics::setColor(Colorf c) +{ + Colorf nc = Colorf(c.r/255.0f, c.g/255.0f, c.b/255.0f, c.a/255.0f); + + gammaCorrectColor(nc); + + glVertexAttrib4f(ATTRIB_CONSTANTCOLOR, nc.r, nc.g, nc.b, nc.a); states.back().color = c; } -Color Graphics::getColor() const +Colorf Graphics::getColor() const { return states.back().color; } -void Graphics::setBackgroundColor(Color c) +void Graphics::setBackgroundColor(Colorf c) { states.back().backgroundColor = c; } -Color Graphics::getBackgroundColor() const +Colorf Graphics::getBackgroundColor() const { return states.back().backgroundColor; } @@ -1456,10 +1479,6 @@ bool Graphics::isSupported(Support feature) const return Canvas::isMultiCanvasSupported(); case SUPPORT_MULTI_CANVAS_FORMATS: return Canvas::isMultiFormatMultiCanvasSupported(); - case SUPPORT_SRGB: - // sRGB support for the screen is guaranteed if it's supported as a - // Canvas format. - return Canvas::isFormatSupported(Canvas::FORMAT_SRGB); default: return false; } diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 1cafcfe9a..1dfe655e4 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -66,7 +66,7 @@ public: const char *getName() const; virtual void setViewportSize(int width, int height); - virtual bool setMode(int width, int height, bool &sRGB); + virtual bool setMode(int width, int height); virtual void unSetMode(); virtual void setActive(bool active); @@ -84,12 +84,12 @@ public: /** * Clears the screen to a specific color. **/ - void clear(Color c); + void clear(Colorf c); /** * Clears each active canvas to a different color. **/ - void clear(const std::vector &colors); + void clear(const std::vector &colors); /** * Discards the contents of the screen. @@ -183,26 +183,28 @@ public: Text *newText(Font *font, const std::string &text = ""); + bool isGammaCorrect() const; + /** * Sets the foreground color. * @param c The new foreground color. **/ - void setColor(Color c); + void setColor(Colorf c); /** * Gets current color. **/ - Color getColor() const; + Colorf getColor() const; /** * Sets the background Color. **/ - void setBackgroundColor(Color c); + void setBackgroundColor(Colorf c); /** * Gets the current background color. **/ - Color getBackgroundColor() const; + Colorf getBackgroundColor() const; void setFont(Font *font); Font *getFont(); @@ -463,8 +465,8 @@ private: struct DisplayState { - Color color = Color(255, 255, 255, 255); - Color backgroundColor = Color(0, 0, 0, 255); + Colorf color = Colorf(255.0, 255.0, 255.0, 255.0); + Colorf backgroundColor = Colorf(0.0, 0.0, 0.0, 255.0); BlendMode blendMode = BLEND_ALPHA; bool blendMultiplyAlpha = true; diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 9896f1bdb..c5006a66f 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -20,6 +20,7 @@ #include "Image.h" +#include "graphics/Graphics.h" #include "common/int.h" // STD @@ -81,6 +82,7 @@ Image::Image(const std::vector &imagedata, const Flags , mipmapSharpness(defaultMipmapSharpness) , compressed(false) , flags(flags) + , sRGB(false) , usingDefaultTexture(false) , textureMemorySize(0) { @@ -107,11 +109,10 @@ Image::Image(const std::vector &compressedda , mipmapSharpness(defaultMipmapSharpness) , compressed(true) , flags(flags) + , sRGB(false) , usingDefaultTexture(false) , textureMemorySize(0) { - this->flags.sRGB = (flags.sRGB || compresseddata[0]->isSRGB()); - width = compresseddata[0]->getWidth(0); height = compresseddata[0]->getHeight(0); @@ -169,6 +170,14 @@ void Image::preload() if (flags.mipmaps) filter.mipmap = defaultMipmapFilter; + + if (!isGammaCorrect()) + flags.linear = false; + + if (isGammaCorrect() && !flags.linear) + sRGB = true; + else + sRGB = false; } void Image::generateMipmaps() @@ -204,7 +213,10 @@ void Image::loadDefaultTexture() void Image::loadFromCompressedData() { - GLenum iformat = getCompressedFormat(cdata[0]->getFormat()); + GLenum iformat = getCompressedFormat(cdata[0]->getFormat(), sRGB); + + if (isGammaCorrect() && !sRGB) + flags.linear = true; int count = 1; @@ -228,13 +240,13 @@ void Image::loadFromCompressedData() void Image::loadFromImageData() { - GLenum iformat = flags.sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8; + GLenum iformat = sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8; GLenum format = GL_RGBA; // in GLES2, the internalformat and format params of TexImage have to match. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0) { - format = flags.sRGB ? GL_SRGB_ALPHA : GL_RGBA; + format = sRGB ? GL_SRGB_ALPHA : GL_RGBA; iformat = format; } @@ -257,24 +269,24 @@ bool Image::loadVolatile() { OpenGL::TempDebugGroup debuggroup("Image load"); - if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), flags.sRGB)) + if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB)) { const char *str; if (image::CompressedImageData::getConstant(cdata[0]->getFormat(), str)) { throw love::Exception("Cannot create image: " - "%s%s compressed images are not supported on this system.", flags.sRGB ? "sRGB " : "", str); + "%s%s compressed images are not supported on this system.", sRGB ? "sRGB " : "", str); } else throw love::Exception("cannot create image: format is not supported on this system."); } else if (!isCompressed()) { - if (flags.sRGB && !hasSRGBSupport()) + if (sRGB && !hasSRGBSupport()) throw love::Exception("sRGB images are not supported on this system."); // GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures. - if (flags.sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0) + if (sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0) && data.size() <= 1) { flags.mipmaps = false; @@ -392,7 +404,7 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h) // In ES2, the format parameter of TexSubImage must match the internal // format of the texture. - if (flags.sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)) + if (sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)) format = GL_SRGB_ALPHA; int mipcount = flags.mipmaps ? (int) data.size() : 1; @@ -560,93 +572,111 @@ bool Image::isCompressed() const return compressed; } -GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat) const +GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const { switch (cformat) { case image::CompressedImageData::FORMAT_DXT1: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; else return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; case image::CompressedImageData::FORMAT_DXT3: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; else return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case image::CompressedImageData::FORMAT_DXT5: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; else return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case image::CompressedImageData::FORMAT_BC4: + isSRGB = false; return GL_COMPRESSED_RED_RGTC1; case image::CompressedImageData::FORMAT_BC4s: + isSRGB = false; return GL_COMPRESSED_SIGNED_RED_RGTC1; case image::CompressedImageData::FORMAT_BC5: + isSRGB = false; return GL_COMPRESSED_RG_RGTC2; case image::CompressedImageData::FORMAT_BC5s: + isSRGB = false; return GL_COMPRESSED_SIGNED_RG_RGTC2; case image::CompressedImageData::FORMAT_BC6H: + isSRGB = false; return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; case image::CompressedImageData::FORMAT_BC6Hs: + isSRGB = false; return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; case image::CompressedImageData::FORMAT_BC7: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; else return GL_COMPRESSED_RGBA_BPTC_UNORM; case image::CompressedImageData::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; + { + if (isSRGB) + return GL_COMPRESSED_SRGB8_ETC2; + else + return GL_COMPRESSED_RGB8_ETC2; + } else + { + isSRGB = false; return GL_ETC1_RGB8_OES; + } case image::CompressedImageData::FORMAT_ETC2_RGB: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB8_ETC2; else return GL_COMPRESSED_RGB8_ETC2; case image::CompressedImageData::FORMAT_ETC2_RGBA: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; else return GL_COMPRESSED_RGBA8_ETC2_EAC; case image::CompressedImageData::FORMAT_ETC2_RGBA1: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; else return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; case image::CompressedImageData::FORMAT_EAC_R: + isSRGB = false; return GL_COMPRESSED_R11_EAC; case image::CompressedImageData::FORMAT_EAC_Rs: + isSRGB = false; return GL_COMPRESSED_SIGNED_R11_EAC; case image::CompressedImageData::FORMAT_EAC_RG: + isSRGB = false; return GL_COMPRESSED_RG11_EAC; case image::CompressedImageData::FORMAT_EAC_RGs: + isSRGB = false; return GL_COMPRESSED_SIGNED_RG11_EAC; case image::CompressedImageData::FORMAT_PVR1_RGB2: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT; else return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case image::CompressedImageData::FORMAT_PVR1_RGB4: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT; else return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; case image::CompressedImageData::FORMAT_PVR1_RGBA2: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT; else return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; case image::CompressedImageData::FORMAT_PVR1_RGBA4: - if (flags.sRGB) + if (isSRGB) return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT; else return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; default: - if (flags.sRGB) + if (isSRGB) return GL_SRGB8_ALPHA8; else return GL_RGBA8; @@ -719,7 +749,7 @@ bool Image::getConstant(FlagType in, const char *&out) StringMap::Entry Image::flagNameEntries[] = { {"mipmaps", Image::FLAG_TYPE_MIPMAPS}, - {"srgb", Image::FLAG_TYPE_SRGB}, + {"linear", Image::FLAG_TYPE_LINEAR}, }; StringMap Image::flagNames(Image::flagNameEntries, sizeof(Image::flagNameEntries)); diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 57308df22..f9966cc9c 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -55,14 +55,14 @@ public: enum FlagType { FLAG_TYPE_MIPMAPS, - FLAG_TYPE_SRGB, + FLAG_TYPE_LINEAR, FLAG_TYPE_MAX_ENUM }; struct Flags { bool mipmaps = false; - bool sRGB = false;; + bool linear = false; }; /** @@ -147,7 +147,7 @@ private: void loadFromCompressedData(); void loadFromImageData(); - GLenum getCompressedFormat(image::CompressedImageData::Format cformat) const; + GLenum getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const; // The ImageData from which the texture is created. May be empty if // Compressed image data was used to create the texture. @@ -170,6 +170,8 @@ private: // The flags used to initialize this Image. Flags flags; + 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; diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index 8b39b1728..351daaa2b 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -298,6 +298,7 @@ Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const DataType type = vertexFormat[attribindex].type; components = vertexFormat[attribindex].components; + return type; } diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index 302c12d47..6a9552b89 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -43,11 +43,6 @@ namespace love::math::RandomGenerator rng; -Colorf colorToFloat(const Color &c) -{ - return Colorf((float)c.r/255.0f, (float)c.g/255.0f, (float)c.b/255.0f, (float)c.a/255.0f); -} - float calculate_variation(float inner, float outer, float var) { float low = inner - (outer/2.0f)*var; @@ -706,30 +701,36 @@ love::Vector ParticleSystem::getOffset() const return offset; } -void ParticleSystem::setColor(const Color &color) +void ParticleSystem::setColor(const Colorf &color) { - colors.resize(1); - colors[0] = colorToFloat(color); + setColor({color}); } -void ParticleSystem::setColor(const std::vector &newColors) +void ParticleSystem::setColor(const std::vector &newColors) { - colors.resize(newColors.size()); - for (size_t i = 0; i < newColors.size(); ++i) - colors[i] = colorToFloat(newColors[i]); -} + colors = newColors; -std::vector ParticleSystem::getColor() const -{ - // The particle system stores colors as floats... - std::vector ncolors(colors.size()); - - for (size_t i = 0; i < colors.size(); ++i) + for (Colorf &c : colors) { - ncolors[i].r = (unsigned char) (colors[i].r * 255); - ncolors[i].g = (unsigned char) (colors[i].g * 255); - ncolors[i].b = (unsigned char) (colors[i].b * 255); - ncolors[i].a = (unsigned char) (colors[i].a * 255); + // We want to store the colors as [0, 1], rather than [0, 255]. + c.r /= 255.0f; + c.g /= 255.0f; + c.b /= 255.0f; + c.a /= 255.0f; + } +} + +std::vector ParticleSystem::getColor() const +{ + // The particle system stores colors in the range of [0, 1]... + std::vector ncolors(colors); + + for (Colorf &c : ncolors) + { + c.r *= 255.0f; + c.g *= 255.0f; + c.b *= 255.0f; + c.a *= 255.0f; } return ncolors; diff --git a/src/modules/graphics/opengl/ParticleSystem.h b/src/modules/graphics/opengl/ParticleSystem.h index 05c16fcfc..6a1be26d4 100644 --- a/src/modules/graphics/opengl/ParticleSystem.h +++ b/src/modules/graphics/opengl/ParticleSystem.h @@ -421,18 +421,18 @@ public: * Sets the color of the particles. * @param color The color. **/ - void setColor(const Color &color); + void setColor(const Colorf &color); /** * Sets the color of the particles. * @param newColors Array of colors **/ - void setColor(const std::vector &newColors); + void setColor(const std::vector &newColors); /** * Returns the color of the particles. **/ - std::vector getColor() const; + std::vector getColor() const; /** * Sets a list of Quads to use for particles over their lifetime. diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index a27e333f4..56362d6fb 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -54,7 +54,7 @@ int w_reset(lua_State *) int w_clear(lua_State *L) { - std::vector colors(1); + std::vector colors(1); if (lua_isnoneornil(L, 1)) colors[0].set(0, 0, 0, 0); @@ -67,20 +67,20 @@ int w_clear(lua_State *L) for (int j = 1; j <= 4; j++) lua_rawgeti(L, i + 1, j); - colors[i].r = (unsigned char) luaL_checkinteger(L, -4); - colors[i].g = (unsigned char) luaL_checkinteger(L, -3); - colors[i].b = (unsigned char) luaL_checkinteger(L, -2); - colors[i].a = (unsigned char) luaL_optinteger(L, -1, 255); + colors[i].r = (float) luaL_checknumber(L, -4); + colors[i].g = (float) luaL_checknumber(L, -3); + colors[i].b = (float) luaL_checknumber(L, -2); + colors[i].a = (float) luaL_optnumber(L, -1, 255); lua_pop(L, 4); } } else { - colors[0].r = (unsigned char) luaL_checkinteger(L, 1); - colors[0].g = (unsigned char) luaL_checkinteger(L, 2); - colors[0].b = (unsigned char) luaL_checkinteger(L, 3); - colors[0].a = (unsigned char) luaL_optinteger(L, 4, 255); + colors[0].r = (float) luaL_checknumber(L, 1); + colors[0].g = (float) luaL_checknumber(L, 2); + colors[0].b = (float) luaL_checknumber(L, 3); + colors[0].a = (float) luaL_optnumber(L, 4, 255); } luax_catchexcept(L, [&]() { @@ -136,6 +136,12 @@ int w_isActive(lua_State *L) return 1; } +int w_isGammaCorrect(lua_State *L) +{ + luax_pushboolean(L, instance()->isGammaCorrect()); + return 1; +} + int w_getWidth(lua_State *L) { lua_pushinteger(L, instance()->getWidth()); @@ -245,7 +251,7 @@ int w_newImage(lua_State *L) { luaL_checktype(L, 2, LUA_TTABLE); flags.mipmaps = luax_boolflag(L, 2, imageFlagName(Image::FLAG_TYPE_MIPMAPS), flags.mipmaps); - flags.sRGB = luax_boolflag(L, 2, imageFlagName(Image::FLAG_TYPE_SRGB), flags.sRGB); + flags.linear = luax_boolflag(L, 2, imageFlagName(Image::FLAG_TYPE_LINEAR), flags.linear); } bool releasedata = false; @@ -617,24 +623,6 @@ static Mesh::DrawMode luax_optmeshdrawmode(lua_State *L, int idx, Mesh::DrawMode return def; } -static inline size_t writeVertexByteData(lua_State *L, int startidx, int components, char *data) -{ - uint8 *componentdata = (uint8 *) data; - for (int i = 0; i < components; i++) - componentdata[i] = (uint8) luaL_optnumber(L, startidx + i, 255); - - return sizeof(uint8) * components; -} - -static inline size_t writeVertexFloatData(lua_State *L, int startidx, int components, char *data) -{ - float *componentdata = (float *) data; - for (int i = 0; i < components; i++) - componentdata[i] = (float) luaL_optnumber(L, startidx + i, 0); - - return sizeof(float) * components; -} - static Mesh *newStandardMesh(lua_State *L) { Mesh *t = nullptr; @@ -670,10 +658,20 @@ static Mesh *newStandardMesh(lua_State *L) v.y = (float) luaL_checknumber(L, -7); v.s = (float) luaL_optnumber(L, -6, 0.0); v.t = (float) luaL_optnumber(L, -5, 0.0); - v.r = (unsigned char) luaL_optinteger(L, -4, 255); - v.g = (unsigned char) luaL_optinteger(L, -3, 255); - v.b = (unsigned char) luaL_optinteger(L, -2, 255); - v.a = (unsigned char) luaL_optinteger(L, -1, 255); + + Colorf c = { + (float) luaL_optnumber(L, -4, 255) / 255.0f, + (float) luaL_optnumber(L, -3, 255) / 255.0f, + (float) luaL_optnumber(L, -2, 255) / 255.0f, + (float) luaL_optnumber(L, -1, 255) / 255.0f + }; + + gammaCorrectColor(c); + + v.r = (unsigned char) (c.r * 255.0f); + v.g = (unsigned char) (c.g * 255.0f); + v.b = (unsigned char) (c.b * 255.0f); + v.a = (unsigned char) (c.a * 255.0f); lua_pop(L, 9); vertices.push_back(v); @@ -799,16 +797,7 @@ static Mesh *newCustomMesh(lua_State *L) } // Fetch the values from Lua and store them in data buffer. - switch (vertexformat[i].type) - { - case Mesh::DATA_BYTE: - writeVertexByteData(L, -components, components, data); - break; - case Mesh::DATA_FLOAT: - default: - writeVertexFloatData(L, -components, components, data); - break; - } + luax_writeAttributeData(L, -components, vertexformat[i].type, components, data); lua_pop(L, components); @@ -867,25 +856,25 @@ int w_newText(lua_State *L) int w_setColor(lua_State *L) { - Color c; + Colorf c; if (lua_istable(L, 1)) { for (int i = 1; i <= 4; i++) lua_rawgeti(L, 1, i); - c.r = (unsigned char) luaL_checknumber(L, -4); - c.g = (unsigned char) luaL_checknumber(L, -3); - c.b = (unsigned char) luaL_checknumber(L, -2); - c.a = (unsigned char) luaL_optnumber(L, -1, 255); + c.r = (float) luaL_checknumber(L, -4); + c.g = (float) luaL_checknumber(L, -3); + c.b = (float) luaL_checknumber(L, -2); + c.a = (float) luaL_optnumber(L, -1, 255); lua_pop(L, 4); } else { - c.r = (unsigned char) luaL_checknumber(L, 1); - c.g = (unsigned char) luaL_checknumber(L, 2); - c.b = (unsigned char) luaL_checknumber(L, 3); - c.a = (unsigned char) luaL_optnumber(L, 4, 255); + c.r = (float) luaL_checknumber(L, 1); + c.g = (float) luaL_checknumber(L, 2); + c.b = (float) luaL_checknumber(L, 3); + c.a = (float) luaL_optnumber(L, 4, 255); } instance()->setColor(c); return 0; @@ -893,35 +882,35 @@ int w_setColor(lua_State *L) int w_getColor(lua_State *L) { - Color c = instance()->getColor(); - lua_pushinteger(L, c.r); - lua_pushinteger(L, c.g); - lua_pushinteger(L, c.b); - lua_pushinteger(L, c.a); + Colorf c = instance()->getColor(); + lua_pushnumber(L, c.r); + lua_pushnumber(L, c.g); + lua_pushnumber(L, c.b); + lua_pushnumber(L, c.a); return 4; } int w_setBackgroundColor(lua_State *L) { - Color c; + Colorf c; if (lua_istable(L, 1)) { for (int i = 1; i <= 4; i++) lua_rawgeti(L, 1, i); - c.r = (unsigned char) luaL_checknumber(L, -4); - c.g = (unsigned char) luaL_checknumber(L, -3); - c.b = (unsigned char) luaL_checknumber(L, -2); - c.a = (unsigned char) luaL_optnumber(L, -1, 255); + c.r = (float) luaL_checknumber(L, -4); + c.g = (float) luaL_checknumber(L, -3); + c.b = (float) luaL_checknumber(L, -2); + c.a = (float) luaL_optnumber(L, -1, 255); lua_pop(L, 4); } else { - c.r = (unsigned char) luaL_checknumber(L, 1); - c.g = (unsigned char) luaL_checknumber(L, 2); - c.b = (unsigned char) luaL_checknumber(L, 3); - c.a = (unsigned char) luaL_optnumber(L, 4, 255); + c.r = (float) luaL_checknumber(L, 1); + c.g = (float) luaL_checknumber(L, 2); + c.b = (float) luaL_checknumber(L, 3); + c.a = (float) luaL_optnumber(L, 4, 255); } instance()->setBackgroundColor(c); return 0; @@ -929,11 +918,11 @@ int w_setBackgroundColor(lua_State *L) int w_getBackgroundColor(lua_State *L) { - Color c = instance()->getBackgroundColor(); - lua_pushinteger(L, c.r); - lua_pushinteger(L, c.g); - lua_pushinteger(L, c.b); - lua_pushinteger(L, c.a); + Colorf c = instance()->getBackgroundColor(); + lua_pushnumber(L, c.r); + lua_pushnumber(L, c.g); + lua_pushnumber(L, c.b); + lua_pushnumber(L, c.a); return 4; } @@ -1837,6 +1826,7 @@ static const luaL_Reg functions[] = { "isCreated", w_isCreated }, { "isActive", w_isActive }, + { "isGammaCorrect", w_isGammaCorrect }, { "getWidth", w_getWidth }, { "getHeight", w_getHeight }, { "getDimensions", w_getDimensions }, diff --git a/src/modules/graphics/opengl/wrap_Graphics.h b/src/modules/graphics/opengl/wrap_Graphics.h index 6540d60bc..35e630854 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.h +++ b/src/modules/graphics/opengl/wrap_Graphics.h @@ -47,6 +47,7 @@ int w_discard(lua_State *L); int w_present(lua_State *L); int w_isCreated(lua_State *L); int w_isActive(lua_State *L); +int w_isGammaCorrect(lua_State *L); int w_getWidth(lua_State *L); int w_getHeight(lua_State *L); int w_getDimensions(lua_State *L); diff --git a/src/modules/graphics/opengl/wrap_Graphics.lua b/src/modules/graphics/opengl/wrap_Graphics.lua index a78f0f84e..5a52d4b3e 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.lua +++ b/src/modules/graphics/opengl/wrap_Graphics.lua @@ -66,9 +66,66 @@ uniform LOVE_UNIFORM_PRECISION mat3 NormalMatrix; #endif uniform mediump vec4 love_ScreenSize;]] +GLSL.FUNCTIONS = [[ +float gammaToLinearPrecise(float c) { + return c <= 0.04045 ? c * 0.077399380804954 : pow((c + 0.055) * 0.9478672985782, 2.4); +} +vec3 gammaToLinearPrecise(vec3 c) { + bvec3 leq = lessThanEqual(c, vec3(0.04045)); + c.r = leq.r ? c.r * 0.077399380804954 : pow((c.r + 0.055) * 0.9478672985782, 2.4); + c.g = leq.g ? c.g * 0.077399380804954 : pow((c.g + 0.055) * 0.9478672985782, 2.4); + c.b = leq.b ? c.b * 0.077399380804954 : pow((c.b + 0.055) * 0.9478672985782, 2.4); + return c; +} +vec4 gammaToLinearPrecise(vec4 c) { return vec4(gammaToLinearPrecise(c.rgb), c.a); } +float linearToGammaPrecise(float c) { + return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055; +} +vec3 linearToGammaPrecise(vec3 c) { + bvec3 lt = lessThan(c, vec3(0.0031308)); + c.r = lt.r ? c.r * 12.92 : 1.055 * pow(c.r, 1.0 / 2.4) - 0.055; + c.g = lt.g ? c.g * 12.92 : 1.055 * pow(c.g, 1.0 / 2.4) - 0.055; + c.b = lt.b ? c.b * 12.92 : 1.055 * pow(c.b, 1.0 / 2.4) - 0.055; + return c; +} +vec4 linearToGammaPrecise(vec4 c) { return vec4(linearToGammaPrecise(c.rgb), c.a); } + +// pow(x, 2.2) isn't an amazing approximation, but at least it's efficient... +mediump float gammaToLinearFast(mediump float c) { return pow(max(c, 0.0), 2.2); } +mediump vec3 gammaToLinearFast(mediump vec3 c) { return pow(max(c, vec3(0.0)), vec3(2.2)); } +mediump vec4 gammaToLinearFast(mediump vec4 c) { return vec4(gammaToLinearFast(c.rgb), c.a); } +mediump float linearToGammaFast(mediump float c) { return pow(max(c, 0.0), 1.0 / 2.2); } +mediump vec3 linearToGammaFast(mediump vec3 c) { return pow(max(c, vec3(0.0)), vec3(1.0 / 2.2)); } +mediump vec4 linearToGammaFast(mediump vec4 c) { return vec4(linearToGammaFast(c.rgb), c.a); } + +#ifdef LOVE_PRECISE_GAMMA +#define gammaToLinear gammaToLinearPrecise +#define linearToGamma linearToGammaPrecise +#else +#define gammaToLinear gammaToLinearFast +#define linearToGamma linearToGammaFast +#endif + +#ifdef LOVE_GAMMA_CORRECT +#define gammaCorrectColor gammaToLinear +#define unGammaCorrectColor linearToGamma +#define gammaCorrectColorPrecise gammaToLinearPrecise +#define unGammaCorrectColorPrecise linearToGammaPrecise +#define gammaCorrectColorFast gammaToLinearFast +#define unGammaCorrectColorFast linearToGammaFast +#else +#define gammaCorrectColor +#define unGammaCorrectColor +#define gammaCorrectColorPrecise +#define unGammaCorrectColorPrecise +#define gammaCorrectColorFast +#define unGammaCorrectColorFast +#endif]] + GLSL.VERTEX = { HEADER = [[ #define VERTEX +#define LOVE_PRECISE_GAMMA attribute vec4 VertexPosition; attribute vec4 VertexTexCoord; @@ -85,7 +142,7 @@ uniform mediump float love_PointSize; FOOTER = [[ void main() { VaryingTexCoord = VertexTexCoord; - VaryingColor = VertexColor * ConstantColor; + VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor; #ifdef GL_ES gl_PointSize = love_PointSize; #endif @@ -134,7 +191,10 @@ void main() { local function createVertexCode(vertexcode, lang) local vertexcodes = { lang == "glsles" and GLSL.VERSION_ES or GLSL.VERSION, - GLSL.SYNTAX, GLSL.VERTEX.HEADER, GLSL.UNIFORMS, + GLSL.SYNTAX, + love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "", + GLSL.VERTEX.HEADER, GLSL.UNIFORMS, + GLSL.FUNCTIONS, lang == "glsles" and "#line 1" or "#line 0", vertexcode, GLSL.VERTEX.FOOTER, @@ -145,7 +205,10 @@ end local function createPixelCode(pixelcode, is_multicanvas, lang) local pixelcodes = { lang == "glsles" and GLSL.VERSION_ES or GLSL.VERSION, - GLSL.SYNTAX, GLSL.PIXEL.HEADER, GLSL.UNIFORMS, + GLSL.SYNTAX, + love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "", + GLSL.PIXEL.HEADER, GLSL.UNIFORMS, + GLSL.FUNCTIONS, lang == "glsles" and "#line 1" or "#line 0", pixelcode, is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER, diff --git a/src/modules/graphics/opengl/wrap_Image.cpp b/src/modules/graphics/opengl/wrap_Image.cpp index d598bde85..8a3a147fa 100644 --- a/src/modules/graphics/opengl/wrap_Image.cpp +++ b/src/modules/graphics/opengl/wrap_Image.cpp @@ -131,8 +131,8 @@ int w_Image_getFlags(lua_State *L) lua_pushboolean(L, flags.mipmaps); lua_setfield(L, -2, imageFlagName(Image::FLAG_TYPE_MIPMAPS)); - lua_pushboolean(L, flags.sRGB); - lua_setfield(L, -2, imageFlagName(Image::FLAG_TYPE_SRGB)); + lua_pushboolean(L, flags.linear); + lua_setfield(L, -2, imageFlagName(Image::FLAG_TYPE_LINEAR)); return 1; } diff --git a/src/modules/graphics/opengl/wrap_Mesh.cpp b/src/modules/graphics/opengl/wrap_Mesh.cpp index 0538edc6d..6d6c25b94 100644 --- a/src/modules/graphics/opengl/wrap_Mesh.cpp +++ b/src/modules/graphics/opengl/wrap_Mesh.cpp @@ -42,6 +42,7 @@ Mesh *luax_checkmesh(lua_State *L, int idx) static inline size_t writeByteData(lua_State *L, int startidx, int components, char *data) { uint8 *componentdata = (uint8 *) data; + for (int i = 0; i < components; i++) componentdata[i] = (uint8) luaL_optnumber(L, startidx + i, 255); @@ -51,13 +52,14 @@ static inline size_t writeByteData(lua_State *L, int startidx, int components, c static inline size_t writeFloatData(lua_State *L, int startidx, int components, char *data) { float *componentdata = (float *) data; + for (int i = 0; i < components; i++) componentdata[i] = (float) luaL_optnumber(L, startidx + i, 0); return sizeof(float) * components; } -static inline char *writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, int components, char *data) +char *luax_writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, int components, char *data) { switch (type) { @@ -70,24 +72,34 @@ static inline char *writeAttributeData(lua_State *L, int startidx, Mesh::DataTyp } } -template -static inline size_t readData(lua_State *L, int components, const char *data) +static inline size_t readByteData(lua_State *L, int components, const char *data) { - const T *componentdata = (const T *) data; + const uint8 *componentdata = (const uint8 *) data; + for (int i = 0; i < components; i++) lua_pushnumber(L, (lua_Number) componentdata[i]); - return sizeof(T) * components; + return sizeof(uint8) * components; } -static inline const char *readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data) +static inline size_t readFloatData(lua_State *L, int components, const char *data) +{ + const float *componentdata = (const float *) data; + + for (int i = 0; i < components; i++) + lua_pushnumber(L, componentdata[i]); + + return sizeof(float) * components; +} + +const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data) { switch (type) { case Mesh::DATA_BYTE: - return data + readData(L, components, data); + return data + readByteData(L, components, data); case Mesh::DATA_FLOAT: - return data + readData(L, components, data); + return data + readFloatData(L, components, data); default: return data; } @@ -125,7 +137,7 @@ int w_Mesh_setVertices(lua_State *L) for (const Mesh::AttribFormat &format : vertexformat) { // Fetch the values from Lua and store them in data buffer. - data = writeAttributeData(L, idx, format.type, format.components, data); + data = luax_writeAttributeData(L, idx, format.type, format.components, data); idx += format.components; } @@ -159,7 +171,7 @@ int w_Mesh_setVertex(lua_State *L) lua_rawgeti(L, 3, i); // Fetch the values from Lua and store them in data buffer. - writtendata = writeAttributeData(L, -format.components, format.type, format.components, writtendata); + writtendata = luax_writeAttributeData(L, -format.components, format.type, format.components, writtendata); idx += format.components; lua_pop(L, format.components); @@ -170,7 +182,7 @@ int w_Mesh_setVertex(lua_State *L) for (const Mesh::AttribFormat &format : vertexformat) { // Fetch the values from Lua and store them in data buffer. - writtendata = writeAttributeData(L, idx, format.type, format.components, writtendata); + writtendata = luax_writeAttributeData(L, idx, format.type, format.components, writtendata); idx += format.components; } } @@ -195,7 +207,7 @@ int w_Mesh_getVertex(lua_State *L) for (const Mesh::AttribFormat &format : vertexformat) { - readdata = readAttributeData(L, format.type, format.components, readdata); + readdata = luax_readAttributeData(L, format.type, format.components, readdata); n += format.components; } @@ -216,7 +228,7 @@ int w_Mesh_setVertexAttribute(lua_State *L) char data[sizeof(float) * 4]; // Fetch the values from Lua and store them in the data buffer. - writeAttributeData(L, 4, type, components, data); + luax_writeAttributeData(L, 4, type, components, data); luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); }); return 0; @@ -237,7 +249,7 @@ int w_Mesh_getVertexAttribute(lua_State *L) luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); }); - readAttributeData(L, type, components, data); + luax_readAttributeData(L, type, components, data); return components; } diff --git a/src/modules/graphics/opengl/wrap_Mesh.h b/src/modules/graphics/opengl/wrap_Mesh.h index 5ba254cad..fe8334a85 100644 --- a/src/modules/graphics/opengl/wrap_Mesh.h +++ b/src/modules/graphics/opengl/wrap_Mesh.h @@ -32,6 +32,9 @@ namespace graphics namespace opengl { +char *luax_writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, int components, char *data); +const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data); + Mesh *luax_checkmesh(lua_State *L, int idx); int w_Mesh_setVertices(lua_State *L); int w_Mesh_setVertex(lua_State *L); diff --git a/src/modules/graphics/opengl/wrap_ParticleSystem.cpp b/src/modules/graphics/opengl/wrap_ParticleSystem.cpp index 399db4e0a..5aea58fe4 100644 --- a/src/modules/graphics/opengl/wrap_ParticleSystem.cpp +++ b/src/modules/graphics/opengl/wrap_ParticleSystem.cpp @@ -499,7 +499,7 @@ int w_ParticleSystem_setColors(lua_State *L) if (nColors > 8) return luaL_error(L, "At most eight (8) colors may be used."); - std::vector colors(nColors); + std::vector colors(nColors); for (int i = 0; i < nColors; i++) { @@ -512,15 +512,15 @@ int w_ParticleSystem_setColors(lua_State *L) // push args[i+2][j+1] onto the stack lua_rawgeti(L, i + 2, j + 1); - unsigned char r = (unsigned char) luaL_checkinteger(L, -4); - unsigned char g = (unsigned char) luaL_checkinteger(L, -3); - unsigned char b = (unsigned char) luaL_checkinteger(L, -2); - unsigned char a = (unsigned char) luaL_optinteger(L, -1, 255); + float r = (float) luaL_checknumber(L, -4); + float g = (float) luaL_checknumber(L, -3); + float b = (float) luaL_checknumber(L, -2); + float a = (float) luaL_optnumber(L, -1, 255); // pop the color components from the stack lua_pop(L, 4); - colors[i] = Color(r, g, b, a); + colors[i] = Colorf(r, g, b, a); } t->setColor(colors); @@ -538,22 +538,22 @@ int w_ParticleSystem_setColors(lua_State *L) if (nColors == 1) { - unsigned char r = (unsigned char) luaL_checkinteger(L, 2); - unsigned char g = (unsigned char) luaL_checkinteger(L, 3); - unsigned char b = (unsigned char) luaL_checkinteger(L, 4); - unsigned char a = (unsigned char) luaL_optinteger(L, 5, 255); - t->setColor(Color(r,g,b,a)); + float r = (float) luaL_checknumber(L, 2); + float g = (float) luaL_checknumber(L, 3); + float b = (float) luaL_checknumber(L, 4); + float a = (float) luaL_optnumber(L, 5, 255); + t->setColor(Colorf(r,g,b,a)); } else { - std::vector colors(nColors); + std::vector colors(nColors); for (int i = 0; i < nColors; ++i) { - unsigned char r = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 1); - unsigned char g = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 2); - unsigned char b = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 3); - unsigned char a = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 4); - colors[i] = Color(r,g,b,a); + float r = (float) luaL_checknumber(L, 1 + i*4 + 1); + float g = (float) luaL_checknumber(L, 1 + i*4 + 2); + float b = (float) luaL_checknumber(L, 1 + i*4 + 3); + float a = (float) luaL_checknumber(L, 1 + i*4 + 4); + colors[i] = Colorf(r,g,b,a); } t->setColor(colors); } @@ -566,19 +566,19 @@ int w_ParticleSystem_getColors(lua_State *L) { ParticleSystem *t = luax_checkparticlesystem(L, 1); - const std::vector &colors =t->getColor(); + const std::vector &colors =t->getColor(); for (size_t i = 0; i < colors.size(); i++) { lua_createtable(L, 4, 0); - lua_pushinteger(L, colors[i].r); + lua_pushnumber(L, colors[i].r); lua_rawseti(L, -2, 1); - lua_pushinteger(L, colors[i].g); + lua_pushnumber(L, colors[i].g); lua_rawseti(L, -2, 2); - lua_pushinteger(L, colors[i].b); + lua_pushnumber(L, colors[i].b); lua_rawseti(L, -2, 3); - lua_pushinteger(L, colors[i].a); + lua_pushnumber(L, colors[i].a); lua_rawseti(L, -2, 4); } diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index 562ac8f6e..cbd8cfdff 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -20,8 +20,11 @@ #include "wrap_Shader.h" #include "graphics/wrap_Texture.h" +#include "math/MathModule.h" + #include #include +#include namespace love { @@ -148,7 +151,7 @@ int w_Shader_sendInt(lua_State *L) return 0; } -int w_Shader_sendFloat(lua_State *L) +static int w__Shader_sendFloat(lua_State *L, bool colors) { Shader *shader = luax_checkshader(L, 1); const char *name = luaL_checkstring(L, 2); @@ -157,7 +160,7 @@ int w_Shader_sendFloat(lua_State *L) if (count < 1) return luaL_error(L, "No variable to send."); - float *values = 0; + float *values = nullptr; size_t dimension = 1; if (lua_isnumber(L, 3) || lua_isboolean(L, 3)) @@ -170,6 +173,24 @@ int w_Shader_sendFloat(lua_State *L) if (!values) return luaL_error(L, "Error in arguments."); + if (colors) + { + bool gammacorrect = love::graphics::isGammaCorrect(); + const auto &m = love::math::Math::instance; + + for (int i = 0; i < count; i++) + { + for (int j = 0; j < (int) dimension; j++) + { + // the fourth component (alpha) is always already linear, if it exists. + if (gammacorrect && i < 4) + values[i * dimension + j] = m.gammaToLinear(values[i * dimension + j] / 255.0f); + else + values[i * dimension + j] /= 255.0f; + } + } + } + bool should_error = false; try { @@ -189,6 +210,16 @@ int w_Shader_sendFloat(lua_State *L) return 0; } +int w_Shader_sendFloat(lua_State *L) +{ + return w__Shader_sendFloat(L, false); +} + +int w_Shader_sendColor(lua_State *L) +{ + return w__Shader_sendFloat(L, true); +} + int w_Shader_sendMatrix(lua_State *L) { int count = lua_gettop(L) - 2; @@ -372,6 +403,7 @@ static const luaL_Reg functions[] = { "sendInt", w_Shader_sendInt }, { "sendBoolean", w_Shader_sendInt }, { "sendFloat", w_Shader_sendFloat }, + { "sendColor", w_Shader_sendColor }, { "sendMatrix", w_Shader_sendMatrix }, { "sendTexture", w_Shader_sendTexture }, { "send", w_Shader_send }, diff --git a/src/modules/graphics/opengl/wrap_Shader.h b/src/modules/graphics/opengl/wrap_Shader.h index 7b31c3d0f..8cc773835 100644 --- a/src/modules/graphics/opengl/wrap_Shader.h +++ b/src/modules/graphics/opengl/wrap_Shader.h @@ -22,6 +22,7 @@ #define LOVE_GRAPHICS_OPENGL_WRAP_PROGRAM_H #include "common/runtime.h" +#include "common/config.h" #include "Shader.h" namespace love @@ -35,6 +36,7 @@ Shader *luax_checkshader(lua_State *L, int idx); int w_Shader_getWarnings(lua_State *L); int w_Shader_sendInt(lua_State *L); int w_Shader_sendFloat(lua_State *L); +int w_Shader_sendColor(lua_State *L); int w_Shader_sendMatrix(lua_State *L); int w_Shader_sendTexture(lua_State *L); int w_Shader_send(lua_State *L); diff --git a/src/modules/graphics/opengl/wrap_SpriteBatch.cpp b/src/modules/graphics/opengl/wrap_SpriteBatch.cpp index c550fd6f7..52f76d3c8 100644 --- a/src/modules/graphics/opengl/wrap_SpriteBatch.cpp +++ b/src/modules/graphics/opengl/wrap_SpriteBatch.cpp @@ -144,19 +144,19 @@ int w_SpriteBatch_setColor(lua_State *L) for (int i = 1; i <= 4; i++) lua_rawgeti(L, 2, i); - c.r = (unsigned char) luaL_checkinteger(L, -4); - c.g = (unsigned char) luaL_checkinteger(L, -3); - c.b = (unsigned char) luaL_checkinteger(L, -2); - c.a = (unsigned char) luaL_optinteger(L, -1, 255); + c.r = (unsigned char) luaL_checknumber(L, -4); + c.g = (unsigned char) luaL_checknumber(L, -3); + c.b = (unsigned char) luaL_checknumber(L, -2); + c.a = (unsigned char) luaL_optnumber(L, -1, 255); lua_pop(L, 4); } else { - c.r = (unsigned char)luaL_checkinteger(L, 2); - c.g = (unsigned char)luaL_checkinteger(L, 3); - c.b = (unsigned char)luaL_checkinteger(L, 4); - c.a = (unsigned char)luaL_optinteger(L, 5, 255); + c.r = (unsigned char) luaL_checknumber(L, 2); + c.g = (unsigned char) luaL_checknumber(L, 3); + c.b = (unsigned char) luaL_checknumber(L, 4); + c.a = (unsigned char) luaL_optnumber(L, 5, 255); } t->setColor(c); @@ -169,14 +169,14 @@ int w_SpriteBatch_getColor(lua_State *L) SpriteBatch *t = luax_checkspritebatch(L, 1); const Color *color = t->getColor(); - // getColor returns NULL if no color is set. + // getColor returns null if no color is set. if (!color) return 0; - lua_pushinteger(L, (lua_Integer) color->r); - lua_pushinteger(L, (lua_Integer) color->g); - lua_pushinteger(L, (lua_Integer) color->b); - lua_pushinteger(L, (lua_Integer) color->a); + lua_pushnumber(L, color->r); + lua_pushnumber(L, color->g); + lua_pushnumber(L, color->b); + lua_pushnumber(L, color->a); return 4; } diff --git a/src/modules/love/love.cpp b/src/modules/love/love.cpp index 0271a940f..a9ab99180 100644 --- a/src/modules/love/love.cpp +++ b/src/modules/love/love.cpp @@ -55,6 +55,11 @@ # include "libraries/luautf8/lutf8lib.h" #endif +// For love::graphics::setGammaCorrect. +#ifdef LOVE_ENABLE_GRAPHICS +# include "graphics/Graphics.h" +#endif + // Scripts #include "scripts/nogame.lua.h" #include "scripts/boot.lua.h" @@ -238,6 +243,14 @@ static int w_love_isVersionCompatible(lua_State *L) return 1; } +static int w__setGammaCorrect(lua_State *L) +{ +#ifdef LOVE_ENABLE_GRAPHICS + love::graphics::setGammaCorrect((bool) lua_toboolean(L, 1)); +#endif + return 0; +} + int luaopen_love(lua_State *L) { love::luax_insistpinnedthread(L); @@ -268,6 +281,9 @@ int luaopen_love(lua_State *L) lua_setfield(L, -2, "_setAccelerometerAsJoystick"); #endif + lua_pushcfunction(L, w__setGammaCorrect); + lua_setfield(L, -2, "_setGammaCorrect"); + lua_newtable(L); for (int i = 0; love::VERSION_COMPATIBILITY[i] != nullptr; i++) diff --git a/src/modules/math/MathModule.cpp b/src/modules/math/MathModule.cpp index 3ad86d671..71c3459df 100644 --- a/src/modules/math/MathModule.cpp +++ b/src/modules/math/MathModule.cpp @@ -208,11 +208,7 @@ bool Math::isConvex(const std::vector &polygon) **/ float Math::gammaToLinear(float c) const { - if (c > 1.0f) - return 1.0f; - else if (c < 0.0f) - return 0.0f; - else if (c <= 0.04045) + if (c <= 0.04045f) return c / 12.92f; else return powf((c + 0.055f) / 1.055f, 2.4f); @@ -223,14 +219,10 @@ float Math::gammaToLinear(float c) const **/ float Math::linearToGamma(float c) const { - if (c > 1.0f) - return 1.0f; - else if (c < 0.0f) - return 0.0f; - else if (c < 0.0031308f) + if (c < 0.0031308f) return c * 12.92f; else - return 1.055f * powf(c, 0.41666f) - 0.055f; + return 1.055f * powf(c, 1.0f / 2.4f) - 0.055f; } CompressedData *Math::compress(Compressor::Format format, love::Data *rawdata, int level) diff --git a/src/modules/window/Window.cpp b/src/modules/window/Window.cpp index 0131b3c2d..e2a823630 100644 --- a/src/modules/window/Window.cpp +++ b/src/modules/window/Window.cpp @@ -77,7 +77,6 @@ StringMap::Entry Window::settingEntri {"centered", SETTING_CENTERED}, {"display", SETTING_DISPLAY}, {"highdpi", SETTING_HIGHDPI}, - {"srgb", SETTING_SRGB}, {"refreshrate", SETTING_REFRESHRATE}, {"x", SETTING_X}, {"y", SETTING_Y}, diff --git a/src/modules/window/Window.h b/src/modules/window/Window.h index 8c70b1531..e70496144 100644 --- a/src/modules/window/Window.h +++ b/src/modules/window/Window.h @@ -57,7 +57,6 @@ public: SETTING_CENTERED, SETTING_DISPLAY, SETTING_HIGHDPI, - SETTING_SRGB, SETTING_REFRESHRATE, SETTING_X, SETTING_Y, @@ -210,7 +209,6 @@ struct WindowSettings bool centered = true; int display = 0; bool highdpi = false; - bool sRGB = false; double refreshrate = 0.0; bool useposition = false; int x = 0; diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index fb40b1304..ffe95f632 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -58,9 +58,14 @@ Window::Window() , context(nullptr) , displayedWindowError(false) , displayedContextError(false) + , hasSDL203orEarlier(false) { if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) throw love::Exception("Could not initialize SDL video subsystem (%s)", SDL_GetError()); + + SDL_version version = {}; + SDL_GetVersion(&version); + hasSDL203orEarlier = (version.major == 2 && version.minor == 0 && version.patch <= 3); } Window::~Window() @@ -84,12 +89,17 @@ void Window::setGLFramebufferAttributes(int msaa, bool sRGB) SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (msaa > 0) ? msaa : 0); - // SDL or GLX may have bugs with this: https://bugzilla.libsdl.org/show_bug.cgi?id=2897 - // It's fine to leave the attribute disabled on desktops though, because in - // practice the framebuffer will be sRGB-capable even if it's not requested. -#if !defined(LOVE_LINUX) SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0); -#endif + + const char *driver = SDL_GetCurrentVideoDriver(); + if (driver && strstr(driver, "x11") == driver) + { + // Always disable the sRGB flag when GLX is used with older SDL versions, + // because of this bug: https://bugzilla.libsdl.org/show_bug.cgi?id=2897 + // In practice GLX will always give an sRGB-capable framebuffer anyway. + if (hasSDL203orEarlier) + SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 0); + } #if defined(LOVE_WINDOWS) // Avoid the Microsoft OpenGL 1.1 software renderer on Windows. Apparently @@ -154,7 +164,7 @@ bool Window::checkGLVersion(const ContextAttribs &attribs) return true; } -bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa, bool sRGB) +bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa) { bool preferGLES = false; @@ -171,6 +181,14 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla if (curdriver && strstr(curdriver, glesdriver) == curdriver) { preferGLES = true; + + // Prior to SDL 2.0.4, backends that use OpenGL ES didn't properly + // ask for a sRGB framebuffer when requested by SDL_GL_SetAttribute. + // FIXME: This doesn't account for windowing backends that sometimes + // use EGL, e.g. the X11 and windows SDL backends. + if (hasSDL203orEarlier) + graphics::setGammaCorrect(false); + break; } } @@ -192,11 +210,8 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla {2, 0, true, debug}, // OpenGL ES 2. }; - SDL_version sdlversion = {}; - SDL_GetVersion(&sdlversion); - // OpenGL ES 3+ contexts are only properly supported in SDL 2.0.4+. - if (sdlversion.major == 2 && sdlversion.minor == 0 && sdlversion.patch <= 3) + if (hasSDL203orEarlier) attribslist.erase(attribslist.begin() + 1); // Move OpenGL ES to the front of the list if we should prefer GLES. @@ -225,7 +240,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla } int curMSAA = msaa; - bool curSRGB = sRGB; + bool curSRGB = love::graphics::isGammaCorrect(); setGLFramebufferAttributes(curMSAA, curSRGB); setGLContextAttributes(attribs); @@ -308,7 +323,10 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla } if (context) + { + love::graphics::setGammaCorrect(curSRGB); break; + } } if (!context || !window) @@ -428,7 +446,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) close(); - if (!createWindowAndContext(x, y, width, height, sdlflags, f.msaa, f.sRGB)) + if (!createWindowAndContext(x, y, width, height, sdlflags, f.msaa)) return false; // Make sure the window keeps any previously set icon. @@ -451,7 +469,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) auto gfx = Module::getInstance(Module::M_GRAPHICS); if (gfx != nullptr) - gfx->setMode(curMode.pixelwidth, curMode.pixelheight, curMode.settings.sRGB); + gfx->setMode(curMode.pixelwidth, curMode.pixelheight); return true; } @@ -521,8 +539,6 @@ void Window::updateSettings(const WindowSettings &newsettings) else SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); - curMode.settings.sRGB = newsettings.sRGB; - // Verify MSAA setting. int buffers = 0; int samples = 0; diff --git a/src/modules/window/sdl/Window.h b/src/modules/window/sdl/Window.h index d985f7b77..29b340201 100644 --- a/src/modules/window/sdl/Window.h +++ b/src/modules/window/sdl/Window.h @@ -119,7 +119,7 @@ private: void setGLFramebufferAttributes(int msaa, bool sRGB); void setGLContextAttributes(const ContextAttribs &attribs); bool checkGLVersion(const ContextAttribs &attribs); - bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa, bool sRGB); + bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa); // Update the saved window settings based on the window's actual state. void updateSettings(const WindowSettings &newsettings); @@ -149,6 +149,8 @@ private: bool displayedWindowError; bool displayedContextError; + bool hasSDL203orEarlier; + }; // Window } // sdl diff --git a/src/modules/window/wrap_Window.cpp b/src/modules/window/wrap_Window.cpp index b32c0196d..e805d29ea 100644 --- a/src/modules/window/wrap_Window.cpp +++ b/src/modules/window/wrap_Window.cpp @@ -107,7 +107,6 @@ 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) - 1; settings.highdpi = luax_boolflag(L, 3, settingName(Window::SETTING_HIGHDPI), false); - settings.sRGB = luax_boolflag(L, 3, settingName(Window::SETTING_SRGB), false); lua_getfield(L, 3, settingName(Window::SETTING_X)); lua_getfield(L, 3, settingName(Window::SETTING_Y)); @@ -175,9 +174,6 @@ 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)); - lua_pushnumber(L, settings.refreshrate); lua_setfield(L, -2, settingName(Window::SETTING_REFRESHRATE)); diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index f463e00bf..61d3c2457 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -351,7 +351,6 @@ function love.init() resizable = false, centered = true, highdpi = false, - srgb = false, }, modules = { event = true, @@ -375,6 +374,7 @@ function love.init() identity = false, appendidentity = false, accelerometerjoystick = true, -- Only relevant for Android / iOS. + gammacorrect = false, } -- Console hack, part 1. @@ -408,6 +408,10 @@ function love.init() love._setAccelerometerAsJoystick(c.accelerometerjoystick) end + if love._setGammaCorrect then + love._setGammaCorrect(c.gammacorrect) + end + -- Gets desired modules. for k,v in ipairs{ "thread", @@ -471,13 +475,12 @@ function love.init() centered = c.window.centered, display = c.window.display, highdpi = c.window.highdpi, - srgb = c.window.srgb, x = c.window.x, y = c.window.y, }), "Could not set window mode") love.window.setTitle(c.window.title or c.title) if c.window.icon then - assert(love.image, "If an icon is set in love.conf, love.image has to be loaded!") + assert(love.image, "If an icon is set in love.conf, love.image must be loaded!") love.window.setIcon(love.image.newImageData(c.window.icon)) end end @@ -589,13 +592,7 @@ function love.errhand(msg) love.graphics.reset() local font = love.graphics.setNewFont(math.floor(love.window.toPixels(14))) - local sRGB = select(3, love.window.getMode()).srgb - if sRGB and love.math then - love.graphics.setBackgroundColor(love.math.gammaToLinear(89, 157, 220)) - else - love.graphics.setBackgroundColor(89, 157, 220) - end - + love.graphics.setBackgroundColor(89, 157, 220) love.graphics.setColor(255, 255, 255, 255) local trace = debug.traceback() diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index b93ba1852..956b67d5d 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -654,7 +654,6 @@ 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, @@ -688,6 +687,8 @@ const unsigned char boot_lua[] = 0x73, 0x74, 0x69, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x2d, 0x2d, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x2f, 0x20, 0x69, 0x4f, 0x53, 0x2e, 0x0a, + 0x09, 0x09, 0x67, 0x61, 0x6d, 0x6d, 0x61, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, + 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x2d, 0x2d, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x31, 0x2e, 0x0a, @@ -757,6 +758,12 @@ const unsigned char boot_lua[] = 0x2e, 0x61, 0x63, 0x63, 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x29, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x5f, 0x73, 0x65, 0x74, 0x47, 0x61, 0x6d, 0x6d, 0x61, + 0x43, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x5f, 0x73, 0x65, 0x74, 0x47, 0x61, 0x6d, 0x6d, 0x61, 0x43, 0x6f, + 0x72, 0x72, 0x65, 0x63, 0x74, 0x28, 0x63, 0x2e, 0x67, 0x61, 0x6d, 0x6d, 0x61, 0x63, 0x6f, 0x72, 0x72, 0x65, + 0x63, 0x74, 0x29, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x2d, 0x2d, 0x20, 0x47, 0x65, 0x74, 0x73, 0x20, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73, @@ -866,8 +873,6 @@ 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, 0x09, 0x78, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x78, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x79, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x79, 0x2c, 0x0a, 0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, @@ -880,8 +885,8 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x22, 0x49, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x63, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x2c, - 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x68, 0x61, 0x73, 0x20, 0x74, 0x6f, - 0x20, 0x62, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x21, 0x22, 0x29, 0x0a, + 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, + 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x21, 0x22, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2e, 0x6e, 0x65, 0x77, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x28, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, @@ -1075,21 +1080,9 @@ const unsigned char boot_lua[] = 0x6e, 0x74, 0x28, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x28, 0x31, 0x34, 0x29, 0x29, 0x29, 0x0a, - 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x52, 0x47, 0x42, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x28, 0x33, 0x2c, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, - 0x67, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x29, 0x29, 0x2e, 0x73, 0x72, 0x67, 0x62, 0x0a, - 0x09, 0x69, 0x66, 0x20, 0x73, 0x52, 0x47, 0x42, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, - 0x6d, 0x61, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, - 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x6c, - 0x6f, 0x76, 0x65, 0x2e, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x67, 0x61, 0x6d, 0x6d, 0x61, 0x54, 0x6f, 0x4c, 0x69, - 0x6e, 0x65, 0x61, 0x72, 0x28, 0x38, 0x39, 0x2c, 0x20, 0x31, 0x35, 0x37, 0x2c, 0x20, 0x32, 0x32, 0x30, 0x29, - 0x29, 0x0a, - 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, - 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x38, - 0x39, 0x2c, 0x20, 0x31, 0x35, 0x37, 0x2c, 0x20, 0x32, 0x32, 0x30, 0x29, 0x0a, - 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, + 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x38, 0x39, + 0x2c, 0x20, 0x31, 0x35, 0x37, 0x2c, 0x20, 0x32, 0x32, 0x30, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x0a, diff --git a/src/scripts/nogame.lua b/src/scripts/nogame.lua index 7b6b8e680..0e9f56efe 100644 --- a/src/scripts/nogame.lua +++ b/src/scripts/nogame.lua @@ -994,6 +994,7 @@ function love.nogame() function love.conf(t) t.title = "L\195\150VE " .. love._version .. " (" .. love._version_codename .. ")" + t.gammacorrect = true t.modules.audio = false t.modules.sound = false t.modules.physics = false diff --git a/src/scripts/nogame.lua.h b/src/scripts/nogame.lua.h index 92ef83e7f..23ee81bbc 100644 --- a/src/scripts/nogame.lua.h +++ b/src/scripts/nogame.lua.h @@ -4313,6 +4313,8 @@ const unsigned char nogame_lua[] = 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x28, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x29, 0x22, 0x0a, + 0x09, 0x09, 0x74, 0x2e, 0x67, 0x61, 0x6d, 0x6d, 0x61, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x3d, + 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x73, 0x6f, 0x75, 0x6e, 0x64, 0x20,