Add some more simple tests to EffectChainTest.
[movit] / effect_chain_test.cpp
1 // Unit tests for EffectChain.
2 //
3 // Note that this also contains the tests for some of the simpler effects.
4
5 #include "effect_chain.h"
6 #include "flat_input.h"
7 #include "mirror_effect.h"
8 #include "opengl.h"
9 #include "gtest/gtest.h"
10
11 #include <stdio.h>
12 #include <math.h>
13
14 #include <algorithm>
15
16 class EffectChainTester {
17 public:
18         EffectChainTester(const float *data, unsigned width, unsigned height, ColorSpace color_space, GammaCurve gamma_curve)
19                 : chain(width, height), width(width), height(height)
20         {
21                 ImageFormat format;
22                 format.color_space = color_space;
23                 format.gamma_curve = gamma_curve;
24         
25                 FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
26                 input->set_pixel_data(data);
27                 chain.add_input(input);
28         }
29
30         EffectChain *get_chain() { return &chain; }
31
32         void run(float *out_data, ColorSpace color_space, GammaCurve gamma_curve)
33         {
34                 ImageFormat format;
35                 format.color_space = color_space;
36                 format.gamma_curve = gamma_curve;
37                 chain.add_output(format);
38                 chain.finalize();
39
40                 glViewport(0, 0, width, height);
41                 chain.render_to_screen();
42
43                 glReadPixels(0, 0, width, height, GL_RED, GL_FLOAT, out_data);
44
45                 // Flip upside-down to compensate for different origin.
46                 for (unsigned y = 0; y < height / 2; ++y) {
47                         unsigned flip_y = height - y - 1;
48                         for (unsigned x = 0; x < width; ++x) {
49                                 std::swap(out_data[y * width + x], out_data[flip_y * width + x]);
50                         }
51                 }
52         }
53
54 private:
55         EffectChain chain;
56         unsigned width, height;
57 };
58
59 void expect_equal(const float *ref, const float *result, unsigned width, unsigned height)
60 {
61         float largest_difference = -1.0f;
62         float squared_difference = 0.0f;
63
64         for (unsigned y = 0; y < height; ++y) {
65                 for (unsigned x = 0; x < width; ++x) {
66                         float diff = ref[y * width + x] - result[y * width + x];
67                         largest_difference = std::max(largest_difference, fabsf(diff));
68                         squared_difference += diff * diff;
69                 }
70         }
71
72         const float largest_difference_limit = 1.5 / 255.0;
73         const float rms_limit = 0.5 / 255.0;
74
75         EXPECT_LT(largest_difference, largest_difference_limit);
76
77         float rms = sqrt(squared_difference) / (width * height);
78         EXPECT_LT(rms, rms_limit);
79
80         if (largest_difference >= largest_difference_limit || rms >= rms_limit) {
81                 fprintf(stderr, "Dumping matrices for easier debugging, since at least one test failed.\n");
82
83                 fprintf(stderr, "Reference:\n");
84                 for (unsigned y = 0; y < height; ++y) {
85                         for (unsigned x = 0; x < width; ++x) {
86                                 fprintf(stderr, "%7.4f ", ref[y * width + x]);
87                         }
88                         fprintf(stderr, "\n");
89                 }
90
91                 fprintf(stderr, "\nResult:\n");
92                 for (unsigned y = 0; y < height; ++y) {
93                         for (unsigned x = 0; x < width; ++x) {
94                                 fprintf(stderr, "%7.4f ", result[y * width + x]);
95                         }
96                         fprintf(stderr, "\n");
97                 }
98         }
99 }
100
101 TEST(EffectChainTest, EmptyChain) {
102         float data[] = {
103                 0.0f, 0.25f, 0.3f,
104                 0.75f, 1.0f, 1.0f,
105         };
106         float out_data[6];
107         EffectChainTester tester(data, 3, 2, COLORSPACE_sRGB, GAMMA_LINEAR);
108         tester.run(out_data, COLORSPACE_sRGB, GAMMA_LINEAR);
109
110         expect_equal(data, out_data, 3, 2);
111 }
112
113 // An effect that does nothing.
114 class IdentityEffect : public Effect {
115 public:
116         IdentityEffect() {}
117         virtual std::string effect_type_id() const { return "IdentityEffect"; }
118         std::string output_fragment_shader() { return read_file("identity.frag"); }
119 };
120
121 TEST(EffectChainTest, Identity) {
122         float data[] = {
123                 0.0f, 0.25f, 0.3f,
124                 0.75f, 1.0f, 1.0f,
125         };
126         float out_data[6];
127         EffectChainTester tester(data, 3, 2, COLORSPACE_sRGB, GAMMA_LINEAR);
128         tester.get_chain()->add_effect(new IdentityEffect());
129         tester.run(out_data, COLORSPACE_sRGB, GAMMA_LINEAR);
130
131         expect_equal(data, out_data, 3, 2);
132 }
133
134 // An effect that does nothing, but requests texture bounce.
135 class BouncingIdentityEffect : public Effect {
136 public:
137         BouncingIdentityEffect() {}
138         virtual std::string effect_type_id() const { return "IdentityEffect"; }
139         std::string output_fragment_shader() { return read_file("identity.frag"); }
140         bool needs_texture_bounce() const { return true; }
141 };
142
143 TEST(EffectChainTest, TextureBouncePreservesIdentity) {
144         float data[] = {
145                 0.0f, 0.25f, 0.3f,
146                 0.75f, 1.0f, 1.0f,
147         };
148         float out_data[6];
149         EffectChainTester tester(data, 3, 2, COLORSPACE_sRGB, GAMMA_LINEAR);
150         tester.get_chain()->add_effect(new BouncingIdentityEffect());
151         tester.run(out_data, COLORSPACE_sRGB, GAMMA_LINEAR);
152
153         expect_equal(data, out_data, 3, 2);
154 }
155
156 TEST(MirrorTest, BasicTest) {
157         float data[] = {
158                 0.0f, 0.25f, 0.3f,
159                 0.75f, 1.0f, 1.0f,
160         };
161         float expected_data[6] = {
162                 0.3f, 0.25f, 0.0f,
163                 1.0f, 1.0f, 0.75f,
164         };
165         float out_data[6];
166         EffectChainTester tester(data, 3, 2, COLORSPACE_sRGB, GAMMA_LINEAR);
167         tester.get_chain()->add_effect(new MirrorEffect());
168         tester.run(out_data, COLORSPACE_sRGB, GAMMA_LINEAR);
169
170         expect_equal(expected_data, out_data, 3, 2);
171 }