Browse Source

Clamp all color arguments to [0, 1] in cases where values outside that range don't make sense (fixed-point color values & sRGB colors). Closes issue #1315.

--HG--
branch : minor
Alex Szpakowski 7 years ago
parent
commit
6dea2ab714

+ 12 - 0
src/common/runtime.h

@@ -22,6 +22,7 @@
 #define LOVE_RUNTIME_H
 #define LOVE_RUNTIME_H
 
 
 // LOVE
 // LOVE
+#include "config.h"
 #include "types.h"
 #include "types.h"
 #include "deprecation.h"
 #include "deprecation.h"
 
 
@@ -35,6 +36,7 @@ extern "C" {
 
 
 // C++
 // C++
 #include <exception>
 #include <exception>
+#include <algorithm>
 
 
 namespace love
 namespace love
 {
 {
@@ -204,6 +206,16 @@ inline float luax_checkfloat(lua_State *L, int idx)
 	return static_cast<float>(luaL_checknumber(L, idx));
 	return static_cast<float>(luaL_checknumber(L, idx));
 }
 }
 
 
+inline lua_Number luax_checknumberclamped01(lua_State *L, int idx)
+{
+	return std::min(std::max(luaL_checknumber(L, idx), 0.0), 1.0);
+}
+
+inline lua_Number luax_optnumberclamped01(lua_State *L, int idx, double def)
+{
+	return std::min(std::max(luaL_optnumber(L, idx, def), 0.0), 1.0);
+}
+
 /**
 /**
  * Require at least 'min' number of items on the stack.
  * Require at least 'min' number of items on the stack.
  * @param L The Lua state.
  * @param L The Lua state.

+ 5 - 5
src/modules/graphics/Font.cpp

@@ -947,7 +947,7 @@ bool Font::hasGlyph(uint32 glyph) const
 		if (r->hasGlyph(glyph))
 		if (r->hasGlyph(glyph))
 			return true;
 			return true;
 	}
 	}
-	
+
 	return false;
 	return false;
 }
 }
 
 
@@ -955,7 +955,7 @@ bool Font::hasGlyphs(const std::string &text) const
 {
 {
 	if (text.size() == 0)
 	if (text.size() == 0)
 		return false;
 		return false;
-	
+
 	try
 	try
 	{
 	{
 		utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
 		utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
@@ -973,7 +973,7 @@ bool Font::hasGlyphs(const std::string &text) const
 	{
 	{
 		throw love::Exception("UTF-8 decoding error: %s", e.what());
 		throw love::Exception("UTF-8 decoding error: %s", e.what());
 	}
 	}
-	
+
 	return true;
 	return true;
 }
 }
 
 
@@ -984,9 +984,9 @@ void Font::setFallbacks(const std::vector<Font *> &fallbacks)
 		if (f->rasterizers[0]->getDataType() != this->rasterizers[0]->getDataType())
 		if (f->rasterizers[0]->getDataType() != this->rasterizers[0]->getDataType())
 			throw love::Exception("Font fallbacks must be of the same font type.");
 			throw love::Exception("Font fallbacks must be of the same font type.");
 	}
 	}
-	
+
 	rasterizers.resize(1);
 	rasterizers.resize(1);
-	
+
 	// NOTE: this won't invalidate already-rasterized glyphs.
 	// NOTE: this won't invalidate already-rasterized glyphs.
 	for (const Font *f : fallbacks)
 	for (const Font *f : fallbacks)
 		rasterizers.push_back(f->rasterizers[0]);
 		rasterizers.push_back(f->rasterizers[0]);

+ 4 - 4
src/modules/graphics/wrap_Graphics.cpp

@@ -2445,10 +2445,10 @@ int w_points(lua_State *L)
 				positions[i].x = luax_checkfloat(L, -6);
 				positions[i].x = luax_checkfloat(L, -6);
 				positions[i].y = luax_checkfloat(L, -5);
 				positions[i].y = luax_checkfloat(L, -5);
 
 
-				colors[i].r = (float) luaL_optnumber(L, -4, 1.0);
-				colors[i].g = (float) luaL_optnumber(L, -3, 1.0);
-				colors[i].b = (float) luaL_optnumber(L, -2, 1.0);
-				colors[i].a = (float) luaL_optnumber(L, -1, 1.0);
+				colors[i].r = (float) luax_optnumberclamped01(L, -4, 1.0);
+				colors[i].g = (float) luax_optnumberclamped01(L, -3, 1.0);
+				colors[i].b = (float) luax_optnumberclamped01(L, -2, 1.0);
+				colors[i].a = (float) luax_optnumberclamped01(L, -1, 1.0);
 
 
 				lua_pop(L, 7);
 				lua_pop(L, 7);
 			}
 			}

+ 2 - 2
src/modules/graphics/wrap_Mesh.cpp

@@ -42,7 +42,7 @@ static inline size_t writeUnorm8Data(lua_State *L, int startidx, int components,
 	uint8 *componentdata = (uint8 *) data;
 	uint8 *componentdata = (uint8 *) data;
 
 
 	for (int i = 0; i < components; i++)
 	for (int i = 0; i < components; i++)
-		componentdata[i] = (uint8) (luaL_optnumber(L, startidx + i, 1.0) * 255.0);
+		componentdata[i] = (uint8) (luax_optnumberclamped01(L, startidx + i, 1.0) * 255.0);
 
 
 	return sizeof(uint8) * components;
 	return sizeof(uint8) * components;
 }
 }
@@ -52,7 +52,7 @@ static inline size_t writeUnorm16Data(lua_State *L, int startidx, int components
 	uint16 *componentdata = (uint16 *) data;
 	uint16 *componentdata = (uint16 *) data;
 
 
 	for (int i = 0; i < components; i++)
 	for (int i = 0; i < components; i++)
-		componentdata[i] = (uint16) (luaL_optnumber(L, startidx + i, 1.0) * 65535.0);
+		componentdata[i] = (uint16) (luax_optnumberclamped01(L, startidx + i, 1.0) * 65535.0);
 
 
 	return sizeof(uint16) * components;
 	return sizeof(uint16) * components;
 }
 }

+ 9 - 6
src/modules/graphics/wrap_Shader.cpp

@@ -52,13 +52,13 @@ static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info
 	return std::min(std::max(lua_gettop(L) - startidx + 1, 1), info->count);
 	return std::min(std::max(lua_gettop(L) - startidx + 1, 1), info->count);
 }
 }
 
 
-template <typename T>
+template <typename T, typename luaT, luaT (*checknum)(lua_State *, int)>
 static void _updateNumbers(lua_State *L, int startidx, T *values, int components, int count)
 static void _updateNumbers(lua_State *L, int startidx, T *values, int components, int count)
 {
 {
 	if (components == 1)
 	if (components == 1)
 	{
 	{
 		for (int i = 0; i < count; ++i)
 		for (int i = 0; i < count; ++i)
-			values[i] = (T) luaL_checknumber(L, startidx + i);
+			values[i] = (T) checknum(L, startidx + i);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -69,7 +69,7 @@ static void _updateNumbers(lua_State *L, int startidx, T *values, int components
 			for (int k = 1; k <= components; k++)
 			for (int k = 1; k <= components; k++)
 			{
 			{
 				lua_rawgeti(L, startidx + i, k);
 				lua_rawgeti(L, startidx + i, k);
-				values[i * components + k - 1] = (T) luaL_checknumber(L, -1);
+				values[i * components + k - 1] = (T) checknum(L, -1);
 			}
 			}
 
 
 			lua_pop(L, components);
 			lua_pop(L, components);
@@ -83,7 +83,10 @@ int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader
 	int components = info->components;
 	int components = info->components;
 	float *values = info->floats;
 	float *values = info->floats;
 
 
-	_updateNumbers(L, startidx, values, components, count);
+	if (colors)
+		_updateNumbers<float, lua_Number, luax_checknumberclamped01>(L, startidx, values, components, count);
+	else
+		_updateNumbers<float, lua_Number, luaL_checknumber>(L, startidx, values, components, count);
 
 
 	if (colors && graphics::isGammaCorrect())
 	if (colors && graphics::isGammaCorrect())
 	{
 	{
@@ -104,7 +107,7 @@ int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader
 int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
 int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
 {
 {
 	int count = _getCount(L, startidx, info);
 	int count = _getCount(L, startidx, info);
-	_updateNumbers(L, startidx, info->ints, info->components, count);
+	_updateNumbers<int, lua_Integer, luaL_checkinteger>(L, startidx, info->ints, info->components, count);
 	luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
 	luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
 	return 0;
 	return 0;
 }
 }
@@ -112,7 +115,7 @@ int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::
 int w_Shader_sendUnsignedInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
 int w_Shader_sendUnsignedInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
 {
 {
 	int count = _getCount(L, startidx, info);
 	int count = _getCount(L, startidx, info);
-	_updateNumbers(L, startidx, info->uints, info->components, count);
+	_updateNumbers<unsigned int, lua_Integer, luaL_checkinteger>(L, startidx, info->uints, info->components, count);
 	luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
 	luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
 	return 0;
 	return 0;
 }
 }

+ 4 - 6
src/modules/image/wrap_ImageData.cpp

@@ -88,22 +88,20 @@ int w_ImageData_getDimensions(lua_State *L)
 	return 2;
 	return 2;
 }
 }
 
 
-// TODO: rgba16f
-
 static void luax_checkpixel_rgba8(lua_State *L, int startidx, Pixel &p)
 static void luax_checkpixel_rgba8(lua_State *L, int startidx, Pixel &p)
 {
 {
 	for (int i = 0; i < 3; i++)
 	for (int i = 0; i < 3; i++)
-		p.rgba8[i] = (uint8) (luaL_checknumber(L, startidx + i) * 255.0);
+		p.rgba8[i] = (uint8) (luax_checknumberclamped01(L, startidx + i) * 255.0);
 
 
-	p.rgba8[3] = (uint8) (luaL_optnumber(L, startidx + 3, 1.0) * 255.0);
+	p.rgba8[3] = (uint8) (luax_optnumberclamped01(L, startidx + 3, 1.0) * 255.0);
 }
 }
 
 
 static void luax_checkpixel_rgba16(lua_State *L, int startidx, Pixel &p)
 static void luax_checkpixel_rgba16(lua_State *L, int startidx, Pixel &p)
 {
 {
 	for (int i = 0; i < 3; i++)
 	for (int i = 0; i < 3; i++)
-		p.rgba16[i] = (uint16) (luaL_checknumber(L, startidx + i) * 65535.0);
+		p.rgba16[i] = (uint16) (luax_checknumberclamped01(L, startidx + i) * 65535.0);
 
 
-	p.rgba16[3] = (uint16) (luaL_optnumber(L, startidx + 3, 1.0) * 65535.0);
+	p.rgba16[3] = (uint16) (luax_optnumberclamped01(L, startidx + 3, 1.0) * 65535.0);
 }
 }
 
 
 static void luax_checkpixel_rgba16f(lua_State *L, int startidx, Pixel &p)
 static void luax_checkpixel_rgba16f(lua_State *L, int startidx, Pixel &p)

+ 14 - 9
src/modules/image/wrap_ImageData.lua

@@ -27,10 +27,15 @@ local ImageData = ImageData_mt.__index
 
 
 local tonumber, assert, error = tonumber, assert, error
 local tonumber, assert, error = tonumber, assert, error
 local type, pcall = type, pcall
 local type, pcall = type, pcall
-local floor = math.floor
+local floor = math.floor
+local min, max = math.min, math.max
 
 
 local function inside(x, y, w, h)
 local function inside(x, y, w, h)
 	return x >= 0 and x < w and y >= 0 and y < h
 	return x >= 0 and x < w and y >= 0 and y < h
+end
+
+local function clamp01(x)
+	return min(max(x, 0), 1)
 end
 end
 
 
 -- Implement thread-safe ImageData:mapPixel regardless of whether the FFI is
 -- Implement thread-safe ImageData:mapPixel regardless of whether the FFI is
@@ -111,10 +116,10 @@ local conversions = {
 			return tonumber(self.r) / 255, tonumber(self.g) / 255, tonumber(self.b) / 255, tonumber(self.a) / 255
 			return tonumber(self.r) / 255, tonumber(self.g) / 255, tonumber(self.b) / 255, tonumber(self.a) / 255
 		end,
 		end,
 		fromlua = function(self, r, g, b, a)
 		fromlua = function(self, r, g, b, a)
-			self.r = r * 255
-			self.g = g * 255
-			self.b = b * 255
-			self.a = a == nil and 255 or a * 255
+			self.r = clamp01(r) * 255
+			self.g = clamp01(g) * 255
+			self.b = clamp01(b) * 255
+			self.a = a == nil and 255 or clamp01(a) * 255
 		end,
 		end,
 	},
 	},
 	rgba16 = {
 	rgba16 = {
@@ -123,10 +128,10 @@ local conversions = {
 			return tonumber(self.r) / 65535, tonumber(self.g) / 65535, tonumber(self.b) / 65535, tonumber(self.a) / 65535
 			return tonumber(self.r) / 65535, tonumber(self.g) / 65535, tonumber(self.b) / 65535, tonumber(self.a) / 65535
 		end,
 		end,
 		fromlua = function(self, r, g, b, a)
 		fromlua = function(self, r, g, b, a)
-			self.r = r * 65535
-			self.g = g * 65535
-			self.b = b * 65535
-			self.a = a == nil and 65535 or a * 65535
+			self.r = clamp01(r) * 65535
+			self.g = clamp01(g) * 65535
+			self.b = clamp01(b) * 65535
+			self.a = a == nil and 65535 or clamp01(a) * 65535
 		end,
 		end,
 	},
 	},
 	rgba16f = {
 	rgba16f = {

+ 2 - 2
src/modules/math/wrap_Math.cpp

@@ -264,7 +264,7 @@ static int getGammaArgs(lua_State *L, float color[4])
 		for (int i = 1; i <= n && i <= 4; i++)
 		for (int i = 1; i <= n && i <= 4; i++)
 		{
 		{
 			lua_rawgeti(L, 1, i);
 			lua_rawgeti(L, 1, i);
-			color[i - 1] = (float) luaL_checknumber(L, -1);
+			color[i - 1] = (float) luax_checknumberclamped01(L, -1);
 			numcomponents++;
 			numcomponents++;
 		}
 		}
 
 
@@ -275,7 +275,7 @@ static int getGammaArgs(lua_State *L, float color[4])
 		int n = lua_gettop(L);
 		int n = lua_gettop(L);
 		for (int i = 1; i <= n && i <= 4; i++)
 		for (int i = 1; i <= n && i <= 4; i++)
 		{
 		{
-			color[i - 1] = (float) luaL_checknumber(L, i);
+			color[i - 1] = (float) luax_checknumberclamped01(L, i);
 			numcomponents++;
 			numcomponents++;
 		}
 		}
 	}
 	}

+ 7 - 2
src/modules/math/wrap_Math.lua

@@ -26,6 +26,11 @@ local love_math, ffifuncspointer = ...
 
 
 local type, tonumber, error = type, tonumber, error
 local type, tonumber, error = type, tonumber, error
 local floor = math.floor
 local floor = math.floor
+local min, max = math.min, math.max
+
+local function clamp01(x)
+	return min(max(x, 0), 1)
+end
 
 
 local rng = love_math._getRandomGenerator()
 local rng = love_math._getRandomGenerator()
 
 
@@ -95,7 +100,7 @@ end
 
 
 local function gammaToLinear(c)
 local function gammaToLinear(c)
 	if c ~= nil then
 	if c ~= nil then
-		return tonumber(ffifuncs.gammaToLinear(c))
+		return tonumber(ffifuncs.gammaToLinear(clamp01(c)))
 	end
 	end
 	return c
 	return c
 end
 end
@@ -110,7 +115,7 @@ end
 
 
 local function linearToGamma(c)
 local function linearToGamma(c)
 	if c ~= nil then
 	if c ~= nil then
-		return tonumber(ffifuncs.linearToGamma(c))
+		return tonumber(ffifuncs.linearToGamma(clamp01(c)))
 	end
 	end
 	return c
 	return c
 end
 end