// Note that this also contains the tests for some of the simpler effects.
#include <GL/glew.h>
+#include <assert.h>
+#include "effect.h"
#include "effect_chain.h"
#include "flat_input.h"
#include "gtest/gtest.h"
+#include "input.h"
#include "mirror_effect.h"
+#include "multiply_effect.h"
#include "resize_effect.h"
#include "test_util.h"
+#include "util.h"
TEST(EffectChainTest, EmptyChain) {
float data[] = {
expect_equal(data, out_data, 4, size);
}
+// An effect that does nothing, and specifies that it preserves blank alpha.
+class BlankAlphaPreservingEffect : public Effect {
+public:
+ BlankAlphaPreservingEffect() {}
+ virtual std::string effect_type_id() const { return "BlankAlphaPreservingEffect"; }
+ std::string output_fragment_shader() { return read_file("identity.frag"); }
+ virtual AlphaHandling alpha_handling() const { return INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK; }
+};
+
+TEST(EffectChainTest, NoAlphaConversionsWithBlankAlphaPreservingEffect) {
+ const int size = 3;
+ float data[4 * size] = {
+ 0.0f, 0.0f, 1.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 1.0f,
+ };
+ float out_data[4 * size];
+ EffectChainTester tester(NULL, size, 1);
+ tester.get_chain()->add_input(new BlueInput());
+ tester.get_chain()->add_effect(new BlankAlphaPreservingEffect());
+ RewritingEffect<MirrorEffect> *effect = new RewritingEffect<MirrorEffect>();
+ tester.get_chain()->add_effect(effect);
+ tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
+
+ Node *node = effect->replaced_node;
+ EXPECT_EQ(1, node->incoming_links.size());
+ EXPECT_EQ(0, node->outgoing_links.size());
+
+ expect_equal(data, out_data, 4, size);
+}
+
+// This is the counter-test to NoAlphaConversionsWithBlankAlphaPreservingEffect;
+// just to be sure that with a normal INPUT_AND_OUTPUT_PREMULTIPLIED_ALPHA effect,
+// an alpha conversion _should_ be inserted at the very end. (There is some overlap
+// with other tests.)
+TEST(EffectChainTest, AlphaConversionsWithNonBlankAlphaPreservingEffect) {
+ const int size = 3;
+ float data[4 * size] = {
+ 0.0f, 0.0f, 1.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 1.0f,
+ };
+ float out_data[4 * size];
+ EffectChainTester tester(NULL, size, 1);
+ tester.get_chain()->add_input(new BlueInput());
+ tester.get_chain()->add_effect(new IdentityEffect()); // Not BlankAlphaPreservingEffect.
+ RewritingEffect<MirrorEffect> *effect = new RewritingEffect<MirrorEffect>();
+ tester.get_chain()->add_effect(effect);
+ tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
+
+ Node *node = effect->replaced_node;
+ EXPECT_EQ(1, node->incoming_links.size());
+ EXPECT_EQ(1, node->outgoing_links.size());
+ EXPECT_EQ("AlphaDivisionEffect", node->outgoing_links[0]->effect->effect_type_id());
+
+ expect_equal(data, out_data, 4, size);
+}
+
// Effectively scales down its input linearly by 4x (and repeating it),
// which is not attainable without mipmaps.
class MipmapNeedingEffect : public Effect {
expect_equal(expected_data, out_data, 4, 16);
}
-// An effect that multiplies with a constant. Used below.
-class MultiplyEffect : public Effect {
-public:
- MultiplyEffect() { register_float("factor", &factor); }
- virtual std::string effect_type_id() const { return "MultiplyEffect"; }
- std::string output_fragment_shader() { return read_file("multiply.frag"); }
- virtual AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; }
-
-private:
- float factor;
-};
-
// An effect that adds its two inputs together. Used below.
class AddEffect : public Effect {
public:
};
float out_data[2 * 2];
+ const float half[] = { 0.5f, 0.5f, 0.5f, 0.5f };
+ const float two[] = { 2.0f, 2.0f, 2.0f, 0.5f };
+
MultiplyEffect *mul_half = new MultiplyEffect();
- ASSERT_TRUE(mul_half->set_float("factor", 0.5f));
+ ASSERT_TRUE(mul_half->set_vec4("factor", half));
MultiplyEffect *mul_two = new MultiplyEffect();
- ASSERT_TRUE(mul_two->set_float("factor", 2.0f));
+ ASSERT_TRUE(mul_two->set_vec4("factor", two));
EffectChainTester tester(NULL, 2, 2);
};
float out_data[2 * 2];
+ const float half[] = { 0.5f, 0.5f, 0.5f, 0.5f };
+ const float two[] = { 2.0f, 2.0f, 2.0f, 0.5f };
+
MultiplyEffect *mul_half = new MultiplyEffect();
- ASSERT_TRUE(mul_half->set_float("factor", 0.5f));
+ ASSERT_TRUE(mul_half->set_vec4("factor", half));
MultiplyEffect *mul_two = new MultiplyEffect();
- ASSERT_TRUE(mul_two->set_float("factor", 2.0f));
+ ASSERT_TRUE(mul_two->set_vec4("factor", two));
BouncingIdentityEffect *bounce = new BouncingIdentityEffect();
// since bilinear scaling from 2x2 → 3x3 → 2x2 is not very exact.
expect_equal(data, out_data, size, size);
}
+
+extern bool movit_initialized;
+
+// Does not use EffectChainTest, so that it can construct an EffectChain without
+// a shared ResourcePool (which is also properly destroyed afterwards).
+// Also turns on debugging to test that code path.
+TEST(EffectChainTest, IdentityWithOwnPool) {
+ const int width = 3, height = 2;
+ float data[] = {
+ 0.0f, 0.25f, 0.3f,
+ 0.75f, 1.0f, 1.0f,
+ };
+ const float expected_data[] = {
+ 0.75f, 1.0f, 1.0f,
+ 0.0f, 0.25f, 0.3f,
+ };
+ float out_data[6];
+
+ EffectChain chain(width, height);
+ movit_initialized = false;
+ init_movit(".", MOVIT_DEBUG_ON);
+
+ ImageFormat format;
+ format.color_space = COLORSPACE_sRGB;
+ format.gamma_curve = GAMMA_LINEAR;
+
+ FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
+ input->set_pixel_data(data);
+ chain.add_input(input);
+ chain.add_output(format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
+
+ GLuint texnum, fbo;
+ glGenTextures(1, &texnum);
+ check_error();
+ glBindTexture(GL_TEXTURE_2D, texnum);
+ check_error();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ check_error();
+
+ glGenFramebuffers(1, &fbo);
+ check_error();
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ check_error();
+ glFramebufferTexture2D(
+ GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ texnum,
+ 0);
+ check_error();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ check_error();
+
+ chain.finalize();
+
+ chain.render_to_fbo(fbo, width, height);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glReadPixels(0, 0, width, height, GL_RED, GL_FLOAT, out_data);
+
+ expect_equal(expected_data, out_data, width, height);
+
+ // Reset the debug status again.
+ movit_initialized = false;
+ init_movit(".", MOVIT_DEBUG_OFF);
+}