Commits

Boolsheet committed 78348d4

A new blend mode that is very similar to premultiplied without the need to modify the images. Compared to the alpha blend mode, the only difference is that the source alpha does not get multiplied by itself. In this mode, the resulting alpha will never be lower than the two input alpha values (which is closer to what we experience in the real world).

Almost all systems should support it. The Microsoft software renderer is of course the exception.

Now it needs a name. "Porter-Duff compositing"? Nah, too long. Just "normal"?

Comments (0)

Files changed (4)

src/modules/graphics/Graphics.cpp

 	{ "subtractive", Graphics::BLEND_SUBTRACTIVE },
 	{ "multiplicative", Graphics::BLEND_MULTIPLICATIVE },
 	{ "premultiplied", Graphics::BLEND_PREMULTIPLIED },
+	{ "something", Graphics::BLEND_SOMETHING },
 	{ "none", Graphics::BLEND_NONE },
 };
 

src/modules/graphics/Graphics.h

 		BLEND_SUBTRACTIVE,
 		BLEND_MULTIPLICATIVE,
 		BLEND_PREMULTIPLIED,
+		BLEND_SOMETHING,
 		BLEND_NONE,
 		BLEND_MAX_ENUM
 	};

src/modules/graphics/opengl/Graphics.cpp

 	// Unload all volatile objects. These must be reloaded after
 	// the display mode change.
 	Volatile::unloadAll();
-	
+
 	uninitializeContext();
 
 	bool success = currentWindow->setWindow(width, height, fullscreen, vsync, fsaa);
 	return currentFont;
 }
 
-void Graphics::setBlendMode(Graphics::BlendMode mode)
+struct BlendState
 {
-	if (GLEE_VERSION_1_4 || GLEE_ARB_imaging)
-	{
-		if (mode == BLEND_SUBTRACTIVE)
-			glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
-		else
-			glBlendEquation(GL_FUNC_ADD);
-	}
+	GLenum func;
+	GLenum src_rgb;
+	GLenum src_a;
+	GLenum dst_rgb;
+	GLenum dst_a;
+};
+
+static void setBlendState(BlendState &state)
+{
+	int gl_1_4 = GLEE_VERSION_1_4;
+
+	if (gl_1_4 || GLEE_ARB_imaging)
+		glBlendEquation(state.func);
 	else if (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)
-	{
-		if (mode == BLEND_SUBTRACTIVE)
-			glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
-		else
-			glBlendEquationEXT(GL_FUNC_ADD_EXT);
-	}
+		glBlendEquationEXT(state.func);
 	else
 	{
-		if (mode == BLEND_SUBTRACTIVE)
-			throw Exception("This graphics card does not support the subtract blend mode!");
+		if (state.func == GL_FUNC_REVERSE_SUBTRACT)
+			throw Exception("This graphics card does not support the subtractive blend mode!");
 		// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
 	}
 
-	if (mode == BLEND_ALPHA)
-		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	else if (mode == BLEND_MULTIPLICATIVE)
-		glBlendFunc(GL_DST_COLOR, GL_ZERO);
-	else if (mode == BLEND_PREMULTIPLIED)
-		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-	else if (mode == BLEND_NONE)
-		glBlendFunc(GL_ONE, GL_ZERO);
-	else // mode == BLEND_ADDITIVE || mode == BLEND_SUBTRACTIVE
-		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+	if (state.src_rgb != state.src_a || state.dst_rgb != state.dst_a)
+	{
+		if (gl_1_4)
+			glBlendFuncSeparate(state.src_rgb, state.dst_rgb, state.src_a, state.dst_a);
+		else if (GLEE_EXT_blend_func_separate)
+			glBlendFuncSeparateEXT(state.src_rgb, state.dst_rgb, state.src_a, state.dst_a);
+		else
+			throw Exception("This graphics card does not support the something blend mode!");
+	}
+	else
+		glBlendFunc(state.src_rgb, state.dst_rgb);
+}
+
+void Graphics::setBlendMode(Graphics::BlendMode mode)
+{
+	BlendState state = {GL_FUNC_ADD, GL_ONE, GL_ZERO, GL_ONE, GL_ZERO};
+
+	switch (mode)
+	{
+	case BLEND_ALPHA:
+		state.src_rgb = state.src_a = GL_SRC_ALPHA;
+		state.dst_rgb = state.dst_a = GL_ONE_MINUS_SRC_ALPHA;
+		break;
+	case BLEND_MULTIPLICATIVE:
+		state.src_rgb = state.src_a = GL_DST_COLOR;
+		state.dst_rgb = state.dst_a = GL_ZERO;
+		break;
+	case BLEND_PREMULTIPLIED:
+		state.src_rgb = state.src_a = GL_ONE;
+		state.dst_rgb = state.dst_a = GL_ONE_MINUS_SRC_ALPHA;
+		break;
+	case BLEND_SOMETHING:
+		state.src_rgb = GL_SRC_ALPHA;
+		state.src_a = GL_ONE;
+		state.dst_rgb = state.dst_a = GL_ONE_MINUS_SRC_ALPHA;
+		break;
+	case BLEND_SUBTRACTIVE:
+		state.func = GL_FUNC_REVERSE_SUBTRACT;
+	case BLEND_ADDITIVE:
+		state.src_rgb = state.src_a = GL_SRC_ALPHA;
+		state.dst_rgb = state.dst_a = GL_ONE;
+		break;
+	case BLEND_NONE:
+		state.src_rgb = state.src_a = GL_ONE;
+		state.dst_rgb = state.dst_a = GL_ZERO;
+		break;
+	}
+
+	setBlendState(state);
 }
 
 void Graphics::setColorMode(Graphics::ColorMode mode)
 
 Graphics::BlendMode Graphics::getBlendMode() const
 {
-	GLint dst, src;
-	glGetIntegerv(GL_BLEND_DST, &dst);
-	glGetIntegerv(GL_BLEND_SRC, &src);
+	GLint src_rgb, src_a, dst_rgb, dst_a;
+	int gl_1_4 = GLEE_VERSION_1_4;
+
+	if (gl_1_4 || GLEE_EXT_blend_func_separate)
+	{
+		glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
+		glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_a);
+		glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
+		glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_a);
+	}
+	else
+	{
+		glGetIntegerv(GL_BLEND_SRC, &src_rgb);
+		glGetIntegerv(GL_BLEND_DST, &dst_rgb);
+		src_a = src_rgb;
+		dst_a = dst_rgb;
+	}
 
 	GLint equation = GL_FUNC_ADD;
 
-	if (GLEE_VERSION_1_4 || GLEE_ARB_imaging || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract))
+	if (gl_1_4 || GLEE_ARB_imaging || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract))
 		glGetIntegerv(GL_BLEND_EQUATION, &equation);
 
 	if (equation == GL_FUNC_REVERSE_SUBTRACT)  // && src == GL_SRC_ALPHA && dst == GL_ONE
 		return BLEND_SUBTRACTIVE;
-	else if (src == GL_SRC_ALPHA && dst == GL_ONE)  // && equation == GL_FUNC_ADD
-		return BLEND_ADDITIVE;
-	else if (src == GL_SRC_ALPHA && dst == GL_ONE_MINUS_SRC_ALPHA)  // && equation == GL_FUNC_ADD
-		return BLEND_ALPHA;
-	else if (src == GL_DST_COLOR && dst == GL_ZERO)  // && equation == GL_FUNC_ADD
-		return BLEND_MULTIPLICATIVE;
-	else if (src == GL_ONE && dst == GL_ONE_MINUS_SRC_ALPHA)  // && equation == GL_FUNC_ADD
-		return BLEND_PREMULTIPLIED;
-	else if (src == GL_ONE && dst == GL_ZERO)
-		return BLEND_NONE;
+	// Everything else has equation == GL_FUNC_ADD.
+	else if (src_rgb == src_a && dst_rgb == dst_a)
+	{
+		if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE)
+			return BLEND_ADDITIVE;
+		else if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE_MINUS_SRC_ALPHA)
+			return BLEND_ALPHA;
+		else if (src_rgb == GL_DST_COLOR && dst_rgb == GL_ZERO)
+			return BLEND_MULTIPLICATIVE;
+		else if (src_rgb == GL_ONE && dst_rgb == GL_ONE_MINUS_SRC_ALPHA)
+			return BLEND_PREMULTIPLIED;
+		else if (src_rgb == GL_ONE && dst_rgb == GL_ZERO)
+			return BLEND_NONE;
+	}
+	else if (src_rgb = GL_SRC_ALPHA && src_a == GL_ONE &&
+		dst_rgb == GL_ONE_MINUS_SRC_ALPHA && dst_a == GL_ONE_MINUS_SRC_ALPHA)
+		return BLEND_SOMETHING;
 
-	return BLEND_MAX_ENUM; // Should never be reached.
+	throw Exception("Unknown blend mode");
 }
 
 Graphics::ColorMode Graphics::getColorMode() const

src/modules/graphics/opengl/wrap_Graphics.cpp

 
 int w_getBlendMode(lua_State *L)
 {
-	Graphics::BlendMode mode = instance->getBlendMode();
-	const char *str;
-	if (!Graphics::getConstant(mode, str))
-		return luaL_error(L, "Invalid blend mode: %s", str);
-
-	lua_pushstring(L, str);
-	return 1;
+	try
+	{
+		Graphics::BlendMode mode = instance->getBlendMode();
+		const char *str;
+		if (!Graphics::getConstant(mode, str))
+			return luaL_error(L, "Invalid blend mode: %s", str);
+		lua_pushstring(L, str);
+		return 1;
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 }
 
 int w_getColorMode(lua_State *L)