Make Movit work in premultiplied alpha.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 14 Jan 2013 01:27:35 +0000 (02:27 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 14 Jan 2013 01:27:35 +0000 (02:27 +0100)
This is a pretty big change, even though the most visible change right now
is that OverlayEffect looks better in the edges of upscaled material
(which you won't really notice too much, given that our handling of
different resolutions already sucks). Since most material is going to
assume postmultiplied alpha, we need to track the status around the graph
pretty much as we already do with gamma and colorspaces, so it's quite
a lot of new code (and associated test complexity). It really does look
better, though.

Negative sides: MixEffect has gotten less flexible since it now also
handles the alpha; for instance, you can't really use it to subtract
things the same way anymore. Also, I think the glow effect has been broken by
the changes to MixEffect, so I'll need to fix it and then add a test so it
doesn't break again.

alpha_division_effect.cpp [new file with mode: 0644]
alpha_division_effect.frag [new file with mode: 0644]
alpha_division_effect.h [new file with mode: 0644]
alpha_division_effect_test.cpp [new file with mode: 0644]
alpha_multiplication_effect.cpp [new file with mode: 0644]
alpha_multiplication_effect.frag [new file with mode: 0644]
alpha_multiplication_effect.h [new file with mode: 0644]
alpha_multiplication_effect_test.cpp [new file with mode: 0644]
blue.frag [new file with mode: 0644]

diff --git a/alpha_division_effect.cpp b/alpha_division_effect.cpp
new file mode 100644 (file)
index 0000000..9f7b3b7
--- /dev/null
@@ -0,0 +1,7 @@
+#include "alpha_division_effect.h"
+#include "util.h"
+
+std::string AlphaDivisionEffect::output_fragment_shader()
+{
+       return read_file("alpha_division_effect.frag");
+}
diff --git a/alpha_division_effect.frag b/alpha_division_effect.frag
new file mode 100644 (file)
index 0000000..6b58148
--- /dev/null
@@ -0,0 +1,8 @@
+// Note: Division by zero will give inf or nan, whose conversion to
+// integer types is implementation-defined. However, anything is fine for
+// alpha=0, since that's undefined anyway.
+vec4 FUNCNAME(vec2 tc) {
+       vec4 x = INPUT(tc);
+       x.rgb /= x.aaa; 
+       return x;
+}
diff --git a/alpha_division_effect.h b/alpha_division_effect.h
new file mode 100644 (file)
index 0000000..e700112
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _ALPHA_DIVISION_EFFECT_H
+#define _ALPHA_DIVISION_EFFECT_H 1
+
+// Convert premultiplied alpha to postmultiplied alpha, simply by multiplying.
+
+#include "effect.h"
+
+class AlphaDivisionEffect : public Effect {
+public:
+       AlphaDivisionEffect() {}
+       virtual std::string effect_type_id() const { return "AlphaDivisionEffect"; }
+       std::string output_fragment_shader();
+};
+
+#endif // !defined(_ALPHA_DIVISION_EFFECT_H)
diff --git a/alpha_division_effect_test.cpp b/alpha_division_effect_test.cpp
new file mode 100644 (file)
index 0000000..8c29702
--- /dev/null
@@ -0,0 +1,35 @@
+// Unit tests for AlphaDivisionEffect.
+
+#include "test_util.h"
+#include "gtest/gtest.h"
+
+TEST(AlphaDivisionEffectTest, SimpleTest) {
+       const int size = 2;
+       float data[4 * size] = {
+               0.1f, 0.5f, 0.1f, 0.5f,
+               0.2f, 0.2f, 1.0f, 1.0f,
+       };
+       float expected_data[4 * size] = {
+               0.2f, 1.0f, 0.2f, 0.5f,
+               0.2f, 0.2f, 1.0f, 1.0f,
+       };
+       float out_data[4 * size];
+       EffectChainTester tester(data, 1, size, FORMAT_RGBA_PREMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, 4, size);
+}
+
+TEST(AlphaDivisionEffectTest, ZeroAlphaIsPreserved) {
+       const int size = 2;
+       float data[4 * size] = {
+               0.1f, 0.5f, 0.1f, 0.0f,
+               0.0f, 0.0f, 0.0f, 0.0f,
+       };
+       float out_data[4 * size];
+       EffectChainTester tester(data, 1, size, FORMAT_RGBA_PREMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       EXPECT_EQ(0.0f, out_data[3]);
+       EXPECT_EQ(0.0f, out_data[7]);
+}
diff --git a/alpha_multiplication_effect.cpp b/alpha_multiplication_effect.cpp
new file mode 100644 (file)
index 0000000..dff6f15
--- /dev/null
@@ -0,0 +1,7 @@
+#include "alpha_multiplication_effect.h"
+#include "util.h"
+
+std::string AlphaMultiplicationEffect::output_fragment_shader()
+{
+       return read_file("alpha_multiplication_effect.frag");
+}
diff --git a/alpha_multiplication_effect.frag b/alpha_multiplication_effect.frag
new file mode 100644 (file)
index 0000000..0a0a6eb
--- /dev/null
@@ -0,0 +1,5 @@
+vec4 FUNCNAME(vec2 tc) {
+       vec4 x = INPUT(tc);
+       x.rgb *= x.aaa; 
+       return x;
+}
diff --git a/alpha_multiplication_effect.h b/alpha_multiplication_effect.h
new file mode 100644 (file)
index 0000000..c64f2dc
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _ALPHA_MULTIPLICATION_EFFECT_H
+#define _ALPHA_MULTIPLICATION_EFFECT_H 1
+
+// Convert postmultiplied alpha to premultiplied alpha, simply by multiplying.
+
+#include "effect.h"
+
+class AlphaMultiplicationEffect : public Effect {
+public:
+       AlphaMultiplicationEffect() {}
+       virtual std::string effect_type_id() const { return "AlphaMultiplicationEffect"; }
+       std::string output_fragment_shader();
+};
+
+#endif // !defined(_ALPHA_MULTIPLICATION_EFFECT_H)
diff --git a/alpha_multiplication_effect_test.cpp b/alpha_multiplication_effect_test.cpp
new file mode 100644 (file)
index 0000000..eab4b25
--- /dev/null
@@ -0,0 +1,23 @@
+// Unit tests for AlphaMultiplicationEffect.
+
+#include "test_util.h"
+#include "gtest/gtest.h"
+
+TEST(AlphaMultiplicationEffectTest, SimpleTest) {
+       const int size = 3;
+       float data[4 * size] = {
+               1.0f, 0.2f, 0.2f, 0.0f,
+               0.2f, 1.0f, 0.2f, 0.5f,
+               0.2f, 0.2f, 1.0f, 1.0f,
+       };
+       float expected_data[4 * size] = {
+               0.0f, 0.0f, 0.0f, 0.0f,
+               0.1f, 0.5f, 0.1f, 0.5f,
+               0.2f, 0.2f, 1.0f, 1.0f,
+       };
+       float out_data[4 * size];
+       EffectChainTester tester(data, 1, size, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_PREMULTIPLIED);
+
+       expect_equal(expected_data, out_data, 4, size);
+}
diff --git a/blue.frag b/blue.frag
new file mode 100644 (file)
index 0000000..fae19ba
--- /dev/null
+++ b/blue.frag
@@ -0,0 +1,3 @@
+vec4 FUNCNAME(vec2 tc) {
+       return vec4(0.0, 0.0, 1.0, 1.0);
+}