Another round of include-what-you-use.
[movit] / gamma_expansion_effect_test.cpp
1 // Unit tests for GammaExpansionEffect.
2
3 #include <GL/glew.h>
4 #include <math.h>
5
6 #include "gamma_expansion_effect.h"
7 #include "gtest/gtest.h"
8 #include "gtest/gtest-message.h"
9 #include "test_util.h"
10
11 TEST(GammaExpansionEffectTest, sRGB_KeyValues) {
12         float data[] = {
13                 0.0f, 1.0f,
14                 0.040f, 0.041f,  // On either side of the discontinuity.
15         };
16         float expected_data[] = {
17                 0.0f, 1.0f,
18                 0.00309f, 0.00317f, 
19         };
20         float out_data[4];
21         EffectChainTester tester(data, 2, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
22         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
23
24         expect_equal(expected_data, out_data, 2, 2);
25 }
26
27 TEST(GammaExpansionEffectTest, sRGB_RampAlwaysIncreases) {
28         float data[256], out_data[256];
29         for (unsigned i = 0; i < 256; ++i) {
30                 data[i] = i / 255.0f;
31         }
32         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB);
33         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
34
35         for (unsigned i = 1; i < 256; ++i) {
36                 EXPECT_GT(out_data[i], out_data[i - 1])
37                    << "No increase between " << i-1 << " and " << i;
38         }
39 }
40
41 TEST(GammaExpansionEffectTest, sRGB_AlphaIsUnchanged) {
42         float data[] = {
43                 0.0f, 0.0f, 0.0f, 0.0f,
44                 0.0f, 0.0f, 0.0f, 0.25f,
45                 0.0f, 0.0f, 0.0f, 0.5f,
46                 0.0f, 0.0f, 0.0f, 0.75f,
47                 0.0f, 0.0f, 0.0f, 1.0f,
48         };
49         float out_data[5 * 4];
50         EffectChainTester tester(data, 5, 1, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_sRGB);
51         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
52
53         expect_equal(data, out_data, 5, 1);
54 }
55
56 TEST(GammaExpansionEffectTest, sRGB_Accuracy) {
57         float data[256], expected_data[256], out_data[256];
58
59         for (int i = 0; i < 256; ++i) {
60                 double x = i / 255.0;
61
62                 data[i] = x;
63
64                 // From the Wikipedia article on sRGB.
65                 if (x < 0.04045) {
66                         expected_data[i] = x / 12.92;
67                 } else {
68                         expected_data[i] = pow((x + 0.055) / 1.055, 2.4);
69                 }
70         }
71
72         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_sRGB, GL_RGBA32F);
73         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
74
75         // Accuracy limits; for comparison, limits for a straightforward ALU solution
76         // (using a branch and pow()) in parenthesis, used as a “high anchor” to
77         // indicate limitations of float arithmetic etc.:
78         //
79         //   Maximum absolute error: 0.1% of max energy (0.051%)
80         //   Maximum relative error: 2.5% of correct answer (0.093%)
81         //                           25% of difference to next pixel level (6.18%)
82         //   Allowed RMS error:      0.0001 (0.000010)
83         //
84         test_accuracy(expected_data, out_data, 256, 1e-3, 0.025, 0.25, 1e-4);
85 }
86
87 TEST(GammaExpansionEffectTest, Rec709_KeyValues) {
88         float data[] = {
89                 0.0f, 1.0f,
90                 0.080f, 0.082f,  // On either side of the discontinuity.
91         };
92         float expected_data[] = {
93                 0.0f, 1.0f,
94                 0.017778f, 0.018167f,
95         };
96         float out_data[4];
97         EffectChainTester tester(data, 2, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_709);
98         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
99
100         expect_equal(expected_data, out_data, 2, 2);
101 }
102
103 TEST(GammaExpansionEffectTest, Rec709_RampAlwaysIncreases) {
104         float data[256], out_data[256];
105         for (unsigned i = 0; i < 256; ++i) {
106                 data[i] = i / 255.0f;
107         }
108         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_709);
109         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
110
111         for (unsigned i = 1; i < 256; ++i) {
112                 EXPECT_GT(out_data[i], out_data[i - 1])
113                    << "No increase between " << i-1 << " and " << i;
114         }
115 }
116
117 TEST(GammaExpansionEffectTest, Rec709_AlphaIsUnchanged) {
118         float data[] = {
119                 0.0f, 0.0f, 0.0f, 0.0f,
120                 0.0f, 0.0f, 0.0f, 0.25f,
121                 0.0f, 0.0f, 0.0f, 0.5f,
122                 0.0f, 0.0f, 0.0f, 0.75f,
123                 0.0f, 0.0f, 0.0f, 1.0f,
124         };
125         float out_data[5 * 4];
126         EffectChainTester tester(data, 5, 1, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_REC_709);
127         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
128
129         expect_equal(data, out_data, 5, 1);
130 }
131
132 TEST(GammaExpansionEffectTest, Rec709_Accuracy) {
133         float data[256], expected_data[256], out_data[256];
134
135         for (int i = 0; i < 256; ++i) {
136                 double x = i / 255.0;
137
138                 data[i] = x;
139
140                 // Rec. 2020, page 3.
141                 if (x < 0.018 * 4.5) {
142                         expected_data[i] = x / 4.5;
143                 } else {
144                         expected_data[i] = pow((x + 0.099) / 1.099, 1.0 / 0.45);
145                 }
146         }
147
148         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_709, GL_RGBA32F);
149         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
150
151         // Accuracy limits; for comparison, limits for a straightforward ALU solution
152         // (using a branch and pow()) in parenthesis, used as a “high anchor” to
153         // indicate limitations of float arithmetic etc.:
154         //
155         //   Maximum absolute error: 0.1% of max energy (0.046%)
156         //   Maximum relative error: 1.0% of correct answer (0.080%)
157         //                           10% of difference to next pixel level (6.19%)
158         //   Allowed RMS error:      0.0001 (0.000010)
159         //
160         test_accuracy(expected_data, out_data, 256, 1e-3, 0.01, 0.1, 1e-4);
161 }
162
163 // This test tests the same gamma ramp as Rec709_Accuracy, but with 10-bit
164 // input range and somewhat looser error bounds. (One could claim that this is
165 // already on the limit of what we can reasonably do with fp16 input, if you
166 // look at the local relative error.)
167 TEST(GammaExpansionEffectTest, Rec2020_10Bit_Accuracy) {
168         float data[1024], expected_data[1024], out_data[1024];
169
170         for (int i = 0; i < 1024; ++i) {
171                 double x = i / 1023.0;
172
173                 data[i] = x;
174
175                 // Rec. 2020, page 3.
176                 if (x < 0.018 * 4.5) {
177                         expected_data[i] = x / 4.5;
178                 } else {
179                         expected_data[i] = pow((x + 0.099) / 1.099, 1.0 / 0.45);
180                 }
181         }
182
183         EffectChainTester tester(data, 1024, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_2020_10_BIT, GL_RGBA32F);
184         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
185
186         // Accuracy limits; for comparison, limits for a straightforward ALU solution
187         // (using a branch and pow()) in parenthesis, used as a “high anchor” to
188         // indicate limitations of float arithmetic etc.:
189         //
190         //   Maximum absolute error: 0.1% of max energy (0.036%)
191         //   Maximum relative error: 1.0% of correct answer (0.064%)
192         //                           30% of difference to next pixel level (24.9%)
193         //   Allowed RMS error:      0.0001 (0.000005)
194         //
195         test_accuracy(expected_data, out_data, 1024, 1e-3, 0.01, 0.30, 1e-4);
196 }
197
198 TEST(GammaExpansionEffectTest, Rec2020_12BitIsVeryCloseToRec709) {
199         float data[256];
200         for (unsigned i = 0; i < 256; ++i) {
201                 data[i] = i / 255.0f;
202         }
203         float out_data_709[256];
204         float out_data_2020[256];
205
206         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_709);
207         tester.run(out_data_709, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
208         EffectChainTester tester2(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_2020_12_BIT);
209         tester2.run(out_data_2020, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
210
211         double sqdiff = 0.0;
212         for (unsigned i = 0; i < 256; ++i) {
213                 EXPECT_NEAR(out_data_709[i], out_data_2020[i], 1e-3);
214                 sqdiff += (out_data_709[i] - out_data_2020[i]) * (out_data_709[i] - out_data_2020[i]);
215         }
216         EXPECT_GT(sqdiff, 1e-6);
217 }
218
219 // The fp16 _input_ provided by FlatInput is not enough to distinguish between
220 // all of the possible 12-bit input values (every other level translates to the
221 // same value). Thus, this test has extremely loose bounds; if we ever decide
222 // to start supporting fp32, we should re-run this and tighten them a lot.
223 TEST(GammaExpansionEffectTest, Rec2020_12Bit_Inaccuracy) {
224         float data[4096], expected_data[4096], out_data[4096];
225
226         for (int i = 0; i < 4096; ++i) {
227                 double x = i / 4095.0;
228
229                 data[i] = x;
230
231                 // Rec. 2020, page 3.
232                 if (x < 0.0181 * 4.5) {
233                         expected_data[i] = x / 4.5;
234                 } else {
235                         expected_data[i] = pow((x + 0.0993) / 1.0993, 1.0/0.45);
236                 }
237         }
238
239         EffectChainTester tester(data, 4096, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_2020_12_BIT, GL_RGBA32F);
240         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
241
242         // Accuracy limits; for comparison, limits for a straightforward ALU solution
243         // (using a branch and pow()) in parenthesis, used as a “high anchor” to
244         // indicate limitations of float arithmetic etc.:
245         //
246         //   Maximum absolute error: 0.1% of max energy (0.050%)
247         //   Maximum relative error: 1.0% of correct answer (0.050%)
248         //                           250% of difference to next pixel level (100.00%)
249         //   Allowed RMS error:      0.0001 (0.000003)
250         //
251         test_accuracy(expected_data, out_data, 4096, 1e-3, 0.01, 2.50, 1e-4);
252 }