瀏覽代碼

Added sRGB (gamma-correct) support for Images, Canvases, and the main screen.

love.graphics.newImage(path, “srgb”) creates a new Image whose texels are treated as being in the sRGB color space, so they are linearized when drawing/sampling from the image.

love.graphics.newCanvas(w, h, “srgb”) creates a new Canvas whose texels are treated as being in the sRGB color space, so drawing to the Canvas does a linear->sRGB conversion (but blends linearly), and sampling from it (drawing it or using it in a shader) converts from sRGB to linear space.

The "srgb" window flag does the same as Canvases for the main screen.
Alex Szpakowski 11 年之前
父節點
當前提交
ce4fdf4ac9

+ 1 - 0
src/modules/graphics/Graphics.cpp

@@ -186,6 +186,7 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
 	{ "dxt", Graphics::SUPPORT_DXT },
 	{ "dxt", Graphics::SUPPORT_DXT },
 	{ "bc5", Graphics::SUPPORT_BC5 },
 	{ "bc5", Graphics::SUPPORT_BC5 },
 	{ "instancing", Graphics::SUPPORT_INSTANCING },
 	{ "instancing", Graphics::SUPPORT_INSTANCING },
+	{ "srgb", Graphics::SUPPORT_SRGB },
 };
 };
 
 
 StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
 StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));

+ 2 - 1
src/modules/graphics/Graphics.h

@@ -95,6 +95,7 @@ public:
 		SUPPORT_DXT,
 		SUPPORT_DXT,
 		SUPPORT_BC5,
 		SUPPORT_BC5,
 		SUPPORT_INSTANCING,
 		SUPPORT_INSTANCING,
+		SUPPORT_SRGB,
 		SUPPORT_MAX_ENUM
 		SUPPORT_MAX_ENUM
 	};
 	};
 
 
@@ -128,7 +129,7 @@ public:
 	 * @param width The viewport width.
 	 * @param width The viewport width.
 	 * @param height The viewport height.
 	 * @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
 	 * Un-sets the current graphics display mode (uninitializing objects if

+ 19 - 0
src/modules/graphics/Texture.cpp

@@ -109,6 +109,16 @@ bool Texture::getConstant(WrapMode in, const char  *&out)
 	return wrapModes.find(in, 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<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
 StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
 {
 {
 	{ "linear", Texture::FILTER_LINEAR },
 	{ "linear", Texture::FILTER_LINEAR },
@@ -125,6 +135,15 @@ StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEnt
 
 
 StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
 StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
 
 
+StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM>::Entry Texture::formatEntries[] =
+{
+	{"normal", Texture::FORMAT_NORMAL},
+	{"hdr", Texture::FORMAT_HDR},
+	{"srgb", Texture::FORMAT_SRGB},
+};
+
+StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM> Texture::formats(Texture::formatEntries, sizeof(Texture::formatEntries));
+
 
 
 } // graphics
 } // graphics
 } // love
 } // love

+ 14 - 0
src/modules/graphics/Texture.h

@@ -55,6 +55,14 @@ public:
 		FILTER_MAX_ENUM
 		FILTER_MAX_ENUM
 	};
 	};
 
 
+	enum Format
+	{
+		FORMAT_NORMAL,
+		FORMAT_HDR,
+		FORMAT_SRGB,
+		FORMAT_MAX_ENUM
+	};
+
 	struct Filter
 	struct Filter
 	{
 	{
 		Filter();
 		Filter();
@@ -111,6 +119,9 @@ public:
 	static bool getConstant(const char *in, WrapMode &out);
 	static bool getConstant(const char *in, WrapMode &out);
 	static bool getConstant(WrapMode in, const char  *&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:
 protected:
 
 
 	int width;
 	int width;
@@ -132,6 +143,9 @@ private:
 	static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
 	static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
 	static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
 	static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
 
 
+	static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
+	static StringMap<Format, FORMAT_MAX_ENUM> formats;
+
 }; // Texture
 }; // Texture
 
 
 } // graphics
 } // graphics

+ 44 - 28
src/modules/graphics/opengl/Canvas.cpp

@@ -369,7 +369,7 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
 	}
 	}
 };
 };
 
 
-FramebufferStrategy *strategy = NULL;
+FramebufferStrategy *strategy = nullptr;
 
 
 FramebufferStrategy strategyNone;
 FramebufferStrategy strategyNone;
 
 
@@ -379,8 +379,9 @@ FramebufferStrategyPackedEXT strategyPackedEXT;
 
 
 FramebufferStrategyEXT strategyEXT;
 FramebufferStrategyEXT strategyEXT;
 
 
-Canvas *Canvas::current = NULL;
+Canvas *Canvas::current = nullptr;
 OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
 OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
+bool Canvas::screenHasSRGB = false;
 
 
 static void getStrategy()
 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)
 	: fbo(0)
     , resolve_fbo(0)
     , resolve_fbo(0)
 	, texture(0)
 	, texture(0)
     , fsaa_buffer(0)
     , fsaa_buffer(0)
 	, depth_stencil(0)
 	, depth_stencil(0)
-	, texture_type(texture_type)
+	, format(format)
     , fsaa_samples(fsaa)
     , fsaa_samples(fsaa)
 	, fsaa_dirty(false)
 	, fsaa_dirty(false)
 {
 {
@@ -507,13 +508,17 @@ bool Canvas::loadVolatile()
 
 
 	GLint internalformat;
 	GLint internalformat;
 	GLenum textype;
 	GLenum textype;
-	switch (texture_type)
+	switch (format)
 	{
 	{
-	case TYPE_HDR:
+	case Texture::FORMAT_HDR:
 		internalformat = GL_RGBA16F;
 		internalformat = GL_RGBA16F;
 		textype = GL_FLOAT;
 		textype = GL_FLOAT;
 		break;
 		break;
-	case TYPE_NORMAL:
+	case Texture::FORMAT_SRGB:
+		internalformat = GL_SRGB8_ALPHA8;
+		textype = GL_UNSIGNED_BYTE;
+		break;
+	case Texture::FORMAT_NORMAL:
 	default:
 	default:
 		internalformat = GL_RGBA8;
 		internalformat = GL_RGBA8;
 		textype = GL_UNSIGNED_BYTE;
 		textype = GL_UNSIGNED_BYTE;
@@ -681,6 +686,12 @@ void Canvas::setupGrab()
 	// Switch back to modelview matrix
 	// Switch back to modelview matrix
 	glMatrixMode(GL_MODELVIEW);
 	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)
 	if (fsaa_buffer != 0)
 		fsaa_dirty = true;
 		fsaa_dirty = true;
 }
 }
@@ -708,8 +719,8 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
 		if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
 		if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
 			throw love::Exception("All canvas arguments must have the same dimensions.");
 			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)
 		if (canvases[i]->getFSAA() != 0)
 			throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
 			throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
@@ -763,12 +774,22 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
 	glPopMatrix();
 	glPopMatrix();
 	glMatrixMode(GL_MODELVIEW);
 	glMatrixMode(GL_MODELVIEW);
 
 
-	// bind default
-	if (!switchingToOtherCanvas)
+	if (switchingToOtherCanvas)
+	{
+		if (format == FORMAT_SRGB)
+			glDisable(GL_FRAMEBUFFER_SRGB);
+	}
+	else
 	{
 	{
+		// bind system framebuffer.
 		strategy->bindFBO(0);
 		strategy->bindFBO(0);
 		current = nullptr;
 		current = nullptr;
 		gl.setViewport(systemViewport);
 		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);
 	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()
 bool Canvas::isMultiCanvasSupported()
 {
 {
 	// system must support at least 4 simultanious active canvases.
 	// system must support at least 4 simultanious active canvases.
@@ -952,23 +985,6 @@ void Canvas::bindDefaultCanvas()
 		current->stopGrab();
 		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<Canvas::TextureType, Canvas::TYPE_MAX_ENUM>::Entry Canvas::textureTypeEntries[] =
-{
-	{"normal", Canvas::TYPE_NORMAL},
-	{"hdr",    Canvas::TYPE_HDR},
-};
-StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM> Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries));
-
 } // opengl
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

+ 9 - 17
src/modules/graphics/opengl/Canvas.h

@@ -39,14 +39,7 @@ class Canvas : public Texture
 {
 {
 public:
 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();
 	virtual ~Canvas();
 
 
 	// Implements Volatile.
 	// Implements Volatile.
@@ -92,9 +85,9 @@ public:
 		return status;
 		return status;
 	}
 	}
 
 
-	inline TextureType getTextureType() const
+	inline Texture::Format getTextureFormat() const
 	{
 	{
-		return texture_type;
+		return format;
 	}
 	}
 
 
 	inline int getFSAA() const
 	inline int getFSAA() const
@@ -106,17 +99,18 @@ public:
 
 
 	static bool isSupported();
 	static bool isSupported();
 	static bool isHDRSupported();
 	static bool isHDRSupported();
+	static bool isSRGBSupported();
 	static bool isMultiCanvasSupported();
 	static bool isMultiCanvasSupported();
 
 
-	static bool getConstant(const char *in, TextureType &out);
-	static bool getConstant(TextureType in, const char *&out);
-
 	static Canvas *current;
 	static Canvas *current;
 	static void bindDefaultCanvas();
 	static void bindDefaultCanvas();
 
 
 	// The viewport dimensions of the system (default) framebuffer.
 	// The viewport dimensions of the system (default) framebuffer.
 	static OpenGL::Viewport systemViewport;
 	static OpenGL::Viewport systemViewport;
 
 
+	// Whether the main screen should have linear -> sRGB conversions enabled.
+	static bool screenHasSRGB;
+
 private:
 private:
 
 
 	bool createFSAAFBO(GLenum internalformat);
 	bool createFSAAFBO(GLenum internalformat);
@@ -128,7 +122,7 @@ private:
 	GLuint fsaa_buffer;
 	GLuint fsaa_buffer;
 	GLuint depth_stencil;
 	GLuint depth_stencil;
 
 
-	TextureType texture_type;
+	Format format;
 
 
 	GLenum status;
 	GLenum status;
 
 
@@ -140,9 +134,7 @@ private:
 	void setupGrab();
 	void setupGrab();
 	void drawv(const Matrix &t, const Vertex *v);
 	void drawv(const Matrix &t, const Vertex *v);
 
 
-	static StringMap<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];
-	static StringMap<TextureType, TYPE_MAX_ENUM> textureTypes;
-};
+}; // Canvas
 
 
 } // opengl
 } // opengl
 } // graphics
 } // graphics

+ 33 - 9
src/modules/graphics/opengl/Graphics.cpp

@@ -57,8 +57,13 @@ Graphics::Graphics()
 {
 {
 	currentWindow = love::window::sdl::Window::createSingleton();
 	currentWindow = love::window::sdl::Window::createSingleton();
 
 
+	int w, h;
+	love::window::WindowSettings wsettings;
+
+	currentWindow->getWindow(w, h, wsettings);
+
 	if (currentWindow->isCreated())
 	if (currentWindow->isCreated())
-		setMode(currentWindow->getWidth(), currentWindow->getHeight());
+		setMode(w, h, wsettings.sRGB);
 }
 }
 
 
 Graphics::~Graphics()
 Graphics::~Graphics()
@@ -150,7 +155,7 @@ void Graphics::setViewportSize(int width, int height)
 		c->startGrab(c->getAttachedCanvases());
 		c->startGrab(c->getAttachedCanvases());
 }
 }
 
 
-bool Graphics::setMode(int width, int height)
+bool Graphics::setMode(int width, int height, bool &sRGB)
 {
 {
 	this->width = width;
 	this->width = width;
 	this->height = height;
 	this->height = height;
@@ -207,6 +212,19 @@ bool Graphics::setMode(int width, int height)
 	glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit);
 	glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit);
 	matrixLimit -= 5;
 	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;
 	bool enabledebug = false;
 
 
 	if (GLEE_VERSION_3_0)
 	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_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);
 	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");
 	::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
 }
 }
 
 
@@ -374,10 +395,10 @@ void Graphics::discardStencil()
 	glDisable(GL_STENCIL_TEST);
 	glDisable(GL_STENCIL_TEST);
 }
 }
 
 
-Image *Graphics::newImage(love::image::ImageData *data)
+Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format)
 {
 {
 	// Create the image.
 	// Create the image.
-	Image *image = new Image(data);
+	Image *image = new Image(data, format);
 
 
 	if (!isCreated())
 	if (!isCreated())
 		return image;
 		return image;
@@ -401,10 +422,10 @@ Image *Graphics::newImage(love::image::ImageData *data)
 	return image;
 	return image;
 }
 }
 
 
-Image *Graphics::newImage(love::image::CompressedData *cdata)
+Image *Graphics::newImage(love::image::CompressedData *cdata, Texture::Format format)
 {
 {
 	// Create the image.
 	// Create the image.
-	Image *image = new Image(cdata);
+	Image *image = new Image(cdata, format);
 
 
 	if (!isCreated())
 	if (!isCreated())
 		return image;
 		return image;
@@ -448,11 +469,14 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
 	return new ParticleSystem(texture, 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");
 		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())
 	if (width > gl.getMaxTextureSize())
 		throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
 		throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
 	else if (height > gl.getMaxTextureSize())
 	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())
 	while (GL_NO_ERROR != glGetError())
 		/* clear opengl error flag */;
 		/* 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();
 	GLenum err = canvas->getStatus();
 
 
 	// everything ok, return canvas (early out)
 	// everything ok, return canvas (early out)

+ 4 - 4
src/modules/graphics/opengl/Graphics.h

@@ -112,7 +112,7 @@ public:
 	void restoreState(const DisplayState &s);
 	void restoreState(const DisplayState &s);
 
 
 	virtual void setViewportSize(int width, int height);
 	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();
 	virtual void unSetMode();
 
 
 	void setDebug(bool enable);
 	void setDebug(bool enable);
@@ -191,8 +191,8 @@ public:
 	/**
 	/**
 	 * Creates an Image object with padding and/or optimization.
 	 * 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);
 	Quad *newQuad(Quad::Viewport v, float sw, float sh);
 
 
@@ -205,7 +205,7 @@ public:
 
 
 	ParticleSystem *newParticleSystem(Texture *texture, int size);
 	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);
 	Shader *newShader(const Shader::ShaderSources &sources);
 
 

+ 41 - 10
src/modules/graphics/opengl/Image.cpp

@@ -36,7 +36,7 @@ float Image::maxMipmapSharpness = 0.0f;
 Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
 Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
 float Image::defaultMipmapSharpness = 0.0f;
 float Image::defaultMipmapSharpness = 0.0f;
 
 
-Image::Image(love::image::ImageData *data)
+Image::Image(love::image::ImageData *data, Texture::Format format)
 	: data(data)
 	: data(data)
 	, cdata(nullptr)
 	, cdata(nullptr)
 	, paddedWidth(width)
 	, paddedWidth(width)
@@ -45,6 +45,7 @@ Image::Image(love::image::ImageData *data)
 	, mipmapSharpness(defaultMipmapSharpness)
 	, mipmapSharpness(defaultMipmapSharpness)
 	, mipmapsCreated(false)
 	, mipmapsCreated(false)
 	, compressed(false)
 	, compressed(false)
+	, format(format)
 	, usingDefaultTexture(false)
 	, usingDefaultTexture(false)
 {
 {
 	width = data->getWidth();
 	width = data->getWidth();
@@ -54,7 +55,7 @@ Image::Image(love::image::ImageData *data)
 	preload();
 	preload();
 }
 }
 
 
-Image::Image(love::image::CompressedData *cdata)
+Image::Image(love::image::CompressedData *cdata, Texture::Format format)
 	: data(nullptr)
 	: data(nullptr)
 	, cdata(cdata)
 	, cdata(cdata)
 	, paddedWidth(width)
 	, paddedWidth(width)
@@ -63,6 +64,7 @@ Image::Image(love::image::CompressedData *cdata)
 	, mipmapSharpness(defaultMipmapSharpness)
 	, mipmapSharpness(defaultMipmapSharpness)
 	, mipmapsCreated(false)
 	, mipmapsCreated(false)
 	, compressed(true)
 	, compressed(true)
+	, format(format)
 	, usingDefaultTexture(false)
 	, usingDefaultTexture(false)
 {
 {
 	width = cdata->getWidth(0);
 	width = cdata->getWidth(0);
@@ -328,6 +330,9 @@ void Image::unload()
 
 
 bool Image::loadVolatile()
 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()))
 	if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat()))
 	{
 	{
 		const char *str;
 		const char *str;
@@ -400,9 +405,10 @@ void Image::uploadTexturePadded()
 	}
 	}
 	else if (data)
 	else if (data)
 	{
 	{
+		GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
 		glTexImage2D(GL_TEXTURE_2D,
 		glTexImage2D(GL_TEXTURE_2D,
 		             0,
 		             0,
-		             GL_RGBA8,
+		             iformat,
 		             (GLsizei)paddedWidth,
 		             (GLsizei)paddedWidth,
 		             (GLsizei)paddedHeight,
 		             (GLsizei)paddedHeight,
 		             0,
 		             0,
@@ -437,9 +443,10 @@ void Image::uploadTexture()
 	}
 	}
 	else if (data)
 	else if (data)
 	{
 	{
+		GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
 		glTexImage2D(GL_TEXTURE_2D,
 		glTexImage2D(GL_TEXTURE_2D,
 		             0,
 		             0,
-		             GL_RGBA8,
+		             iformat,
 		             (GLsizei)width,
 		             (GLsizei)width,
 		             (GLsizei)height,
 		             (GLsizei)height,
 		             0,
 		             0,
@@ -497,6 +504,11 @@ bool Image::refresh()
 	return true;
 	return true;
 }
 }
 
 
+Texture::Format Image::getFormat() const
+{
+	return format;
+}
+
 void Image::uploadDefaultTexture()
 void Image::uploadDefaultTexture()
 {
 {
 	usingDefaultTexture = true;
 	usingDefaultTexture = true;
@@ -561,16 +573,27 @@ bool Image::isCompressed() const
 	return compressed;
 	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:
 	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:
 	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:
 	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:
 	case image::CompressedData::FORMAT_BC4:
 		return GL_COMPRESSED_RED_RGTC1;
 		return GL_COMPRESSED_RED_RGTC1;
 	case image::CompressedData::FORMAT_BC4s:
 	case image::CompressedData::FORMAT_BC4s:
@@ -580,7 +603,10 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format format) const
 	case image::CompressedData::FORMAT_BC5s:
 	case image::CompressedData::FORMAT_BC5s:
 		return GL_COMPRESSED_SIGNED_RG_RGTC2;
 		return GL_COMPRESSED_SIGNED_RG_RGTC2;
 	default:
 	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;
 	return false;
 }
 }
 
 
+bool Image::hasSRGBSupport()
+{
+	return GLEE_VERSION_2_1 || GLEE_EXT_texture_sRGB;
+}
+
 } // opengl
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

+ 10 - 3
src/modules/graphics/opengl/Image.h

@@ -56,14 +56,14 @@ public:
 	 *
 	 *
 	 * @param data The data from which to load the image.
 	 * @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.
 	 * Creates a new Image with compressed image data.
 	 *
 	 *
 	 * @param cdata The compressed data from which to load the image.
 	 * @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.
 	 * Destructor. Deletes the hardware texture and other resources.
@@ -120,6 +120,8 @@ public:
 	 **/
 	 **/
 	bool refresh();
 	bool refresh();
 
 
+	Texture::Format getFormat() const;
+
 	static void setDefaultMipmapSharpness(float sharpness);
 	static void setDefaultMipmapSharpness(float sharpness);
 	static float getDefaultMipmapSharpness();
 	static float getDefaultMipmapSharpness();
 	static void setDefaultMipmapFilter(FilterMode f);
 	static void setDefaultMipmapFilter(FilterMode f);
@@ -133,6 +135,8 @@ public:
 	static bool hasCompressedTextureSupport();
 	static bool hasCompressedTextureSupport();
 	static bool hasCompressedTextureSupport(image::CompressedData::Format format);
 	static bool hasCompressedTextureSupport(image::CompressedData::Format format);
 
 
+	static bool hasSRGBSupport();
+
 private:
 private:
 
 
 	void uploadDefaultTexture();
 	void uploadDefaultTexture();
@@ -162,6 +166,9 @@ private:
 	// Whether this Image is using a compressed texture.
 	// Whether this Image is using a compressed texture.
 	bool compressed;
 	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
 	// True if the image wasn't able to be properly created and it had to fall
 	// back to a default texture.
 	// back to a default texture.
 	bool usingDefaultTexture;
 	bool usingDefaultTexture;
@@ -180,7 +187,7 @@ private:
 	static FilterMode defaultMipmapFilter;
 	static FilterMode defaultMipmapFilter;
 	static float defaultMipmapSharpness;
 	static float defaultMipmapSharpness;
 
 
-	GLenum getCompressedFormat(image::CompressedData::Format format) const;
+	GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
 
 
 }; // Image
 }; // Image
 
 

+ 9 - 4
src/modules/graphics/opengl/wrap_Canvas.cpp

@@ -110,12 +110,14 @@ int w_Canvas_clear(lua_State *L)
 	return 0;
 	return 0;
 }
 }
 
 
-int w_Canvas_getType(lua_State *L)
+int w_Canvas_getFormat(lua_State *L)
 {
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
 	Canvas *canvas = luax_checkcanvas(L, 1);
-	Canvas::TextureType type = canvas->getTextureType();
+	Texture::Format format = canvas->getTextureFormat();
 	const char *str;
 	const char *str;
-	Canvas::getConstant(type, str);
+	if (!Texture::getConstant(format, str))
+		return luaL_error(L, "Unknown texture format.");
+
 	lua_pushstring(L, str);
 	lua_pushstring(L, str);
 	return 1;
 	return 1;
 }
 }
@@ -142,8 +144,11 @@ static const luaL_Reg functions[] =
 	{ "getImageData", w_Canvas_getImageData },
 	{ "getImageData", w_Canvas_getImageData },
 	{ "getPixel", w_Canvas_getPixel },
 	{ "getPixel", w_Canvas_getPixel },
 	{ "clear", w_Canvas_clear },
 	{ "clear", w_Canvas_clear },
-	{ "getType", w_Canvas_getType },
+	{ "getFormat", w_Canvas_getFormat },
 	{ "getFSAA", w_Canvas_getFSAA },
 	{ "getFSAA", w_Canvas_getFSAA },
+
+	// Deprecated since 0.9.1.
+	{ "getType", w_Canvas_getFormat },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 

+ 1 - 1
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_getImageData(lua_State *L);
 int w_Canvas_getPixel(lua_State * L);
 int w_Canvas_getPixel(lua_State * L);
 int w_Canvas_clear(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);
 int w_Canvas_getFSAA(lua_State *L);
 extern "C" int luaopen_canvas(lua_State *L);
 extern "C" int luaopen_canvas(lua_State *L);
 
 

+ 23 - 10
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -152,8 +152,17 @@ int w_getMaxTextureSize(lua_State *L)
 
 
 int w_newImage(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.
 	// Convert to FileData, if necessary.
 	if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
 	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.");
 		return luaL_error(L, "Error creating image.");
 
 
 	// Create the image.
 	// Create the image.
-	Image *image = 0;
+	Image *image = nullptr;
 	EXCEPT_GUARD(
 	EXCEPT_GUARD(
 		if (cdata)
 		if (cdata)
-			image = instance->newImage(cdata);
+			image = instance->newImage(cdata, format);
 		else if (data)
 		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.");
 		return luaL_error(L, "Could not load image.");
 
 
 	// Push the type.
 	// Push the type.
@@ -325,12 +334,12 @@ int w_newCanvas(lua_State *L)
 	const char *str = luaL_optstring(L, 3, "normal");
 	const char *str = luaL_optstring(L, 3, "normal");
 	int fsaa        = luaL_optint(L, 4, 0);
 	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;
 	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)
 	if (canvas == nullptr)
 		return luaL_error(L, "Canvas not created, but no error thrown. I don't even...");
 		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)
 			if (!GLEE_ARB_draw_instanced)
 				supported = false;
 				supported = false;
 			break;
 			break;
+		case Graphics::SUPPORT_SRGB:
+			if (!Canvas::isSRGBSupported())
+				supported = false;
+			break;
 		default:
 		default:
 			supported = false;
 			supported = false;
 		}
 		}

+ 2 - 0
src/modules/window/Window.cpp

@@ -50,6 +50,7 @@ WindowSettings::WindowSettings()
 	, centered(true)
 	, centered(true)
 	, display(0)
 	, display(0)
 	, highdpi(false)
 	, highdpi(false)
+	, sRGB(false)
 {
 {
 }
 }
 
 
@@ -86,6 +87,7 @@ StringMap<Window::Setting, Window::SETTING_MAX_ENUM>::Entry Window::settingEntri
 	{"centered", SETTING_CENTERED},
 	{"centered", SETTING_CENTERED},
 	{"display", SETTING_DISPLAY},
 	{"display", SETTING_DISPLAY},
 	{"highdpi", SETTING_HIGHDPI},
 	{"highdpi", SETTING_HIGHDPI},
+	{"srgb", SETTING_SRGB},
 };
 };
 
 
 StringMap<Window::Setting, Window::SETTING_MAX_ENUM> Window::settings(Window::settingEntries, sizeof(Window::settingEntries));
 StringMap<Window::Setting, Window::SETTING_MAX_ENUM> Window::settings(Window::settingEntries, sizeof(Window::settingEntries));

+ 2 - 0
src/modules/window/Window.h

@@ -57,6 +57,7 @@ public:
 		SETTING_CENTERED,
 		SETTING_CENTERED,
 		SETTING_DISPLAY,
 		SETTING_DISPLAY,
 		SETTING_HIGHDPI,
 		SETTING_HIGHDPI,
+		SETTING_SRGB,
 		SETTING_MAX_ENUM
 		SETTING_MAX_ENUM
 	};
 	};
 
 
@@ -157,6 +158,7 @@ struct WindowSettings
 	bool centered; // = true
 	bool centered; // = true
 	int display; // = 0
 	int display; // = 0
 	bool highdpi; // false
 	bool highdpi; // false
+	bool sRGB; // false
 
 
 }; // WindowSettings
 }; // WindowSettings
 
 

+ 12 - 6
src/modules/window/sdl/Window.cpp

@@ -154,7 +154,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 	if (!window)
 	if (!window)
 	{
 	{
 		// In Windows and Linux, some GL attributes are set on window creation.
 		// 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();
 		const char *title = windowTitle.c_str();
 		int pos = f.centered ? centeredpos : uncenteredpos;
 		int pos = f.centered ? centeredpos : uncenteredpos;
@@ -190,7 +190,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 
 
 	SDL_RaiseWindow(window);
 	SDL_RaiseWindow(window);
 
 
-	if (!setContext(f.fsaa, f.vsync))
+	if (!setContext(f.fsaa, f.vsync, f.sRGB))
 		return false;
 		return false;
 
 
 	created = true;
 	created = true;
@@ -206,7 +206,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		SDL_GL_GetDrawableSize(window, &width, &height);
 		SDL_GL_GetDrawableSize(window, &width, &height);
 #endif
 #endif
 
 
-		gfx->setMode(width, height);
+		gfx->setMode(width, height, curMode.settings.sRGB);
 	}
 	}
 
 
 	// Make sure the mouse keeps its previous grab setting.
 	// Make sure the mouse keeps its previous grab setting.
@@ -226,7 +226,7 @@ bool Window::onWindowResize(int width, int height)
 	return true;
 	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
 	// We would normally only need to recreate the context if FSAA changes or
 	// SDL_GL_MakeCurrent is unsuccessful, but in Windows MakeCurrent can
 	// 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.
 	// Make sure the proper attributes are set.
-	setWindowGLAttributes(fsaa);
+	setWindowGLAttributes(fsaa, sRGB);
 
 
 	context = SDL_GL_CreateContext(window);
 	context = SDL_GL_CreateContext(window);
 
 
@@ -290,7 +290,7 @@ bool Window::setContext(int fsaa, bool vsync)
 	return true;
 	return true;
 }
 }
 
 
-void Window::setWindowGLAttributes(int fsaa) const
+void Window::setWindowGLAttributes(int fsaa, bool sRGB) const
 {
 {
 	// Set GL window attributes.
 	// Set GL window attributes.
 	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
 	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_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0);
 	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (fsaa > 0) ? fsaa : 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?
 	// Do we want a debug context?
 	const char *debugenv = SDL_GetHint("LOVE_GRAPHICS_DEBUG");
 	const char *debugenv = SDL_GetHint("LOVE_GRAPHICS_DEBUG");
 	if (debugenv && *debugenv == '1')
 	if (debugenv && *debugenv == '1')
@@ -370,6 +374,8 @@ void Window::updateSettings(const WindowSettings &newsettings)
 	else
 	else
 #endif
 #endif
 		SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
 		SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
+
+	curMode.settings.sRGB = newsettings.sRGB;
 }
 }
 
 
 void Window::getWindow(int &width, int &height, WindowSettings &settings)
 void Window::getWindow(int &width, int &height, WindowSettings &settings)

+ 2 - 2
src/modules/window/sdl/Window.h

@@ -90,8 +90,8 @@ public:
 
 
 private:
 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.
 	// Update the saved window settings based on the window's actual state.
 	void updateSettings(const WindowSettings &newsettings);
 	void updateSettings(const WindowSettings &newsettings);

+ 4 - 0
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.centered = luax_boolflag(L, 3, settingName(Window::SETTING_CENTERED), true);
 	settings.display = luax_intflag(L, 3, settingName(Window::SETTING_DISPLAY), 1);
 	settings.display = luax_intflag(L, 3, settingName(Window::SETTING_DISPLAY), 1);
 	settings.highdpi = luax_boolflag(L, 3, settingName(Window::SETTING_HIGHDPI), false);
 	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.
 	// Display index is 1-based in Lua and 0-based internally.
 	settings.display--;
 	settings.display--;
@@ -151,6 +152,9 @@ int w_getMode(lua_State *L)
 	luax_pushboolean(L, settings.highdpi);
 	luax_pushboolean(L, settings.highdpi);
 	lua_setfield(L, -2, settingName(Window::SETTING_HIGHDPI));
 	lua_setfield(L, -2, settingName(Window::SETTING_HIGHDPI));
 
 
+	luax_pushboolean(L, settings.sRGB);
+	lua_setfield(L, -2, settingName(Window::SETTING_SRGB));
+
 	return 3;
 	return 3;
 }
 }
 
 

+ 2 - 0
src/scripts/boot.lua

@@ -301,6 +301,7 @@ function love.init()
 			resizable = false,
 			resizable = false,
 			centered = true,
 			centered = true,
 			highdpi = false,
 			highdpi = false,
+			srgb = false,
 		},
 		},
 		modules = {
 		modules = {
 			event = true,
 			event = true,
@@ -390,6 +391,7 @@ function love.init()
 			centered = c.window.centered,
 			centered = c.window.centered,
 			display = c.window.display,
 			display = c.window.display,
 			highdpi = c.window.highdpi,
 			highdpi = c.window.highdpi,
+			srgb = c.window.srgb,
 		}), "Could not set window mode")
 		}), "Could not set window mode")
 		love.window.setTitle(c.window.title or c.title)
 		love.window.setTitle(c.window.title or c.title)
 		if c.window.icon then
 		if c.window.icon then

+ 3 - 0
src/scripts/boot.lua.h

@@ -539,6 +539,7 @@ const unsigned char boot_lua[] =
 	0x2c, 0x0a,
 	0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 
 	0x09, 0x09, 0x09, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 
 	0x2c, 0x0a,
 	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, 0x7d, 0x2c, 0x0a,
 	0x09, 0x09, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 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,
 	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,
 	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, 
 	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,
 	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, 
 	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,
 	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, 
 	0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54,