]> git.sesse.net Git - movit/blob - gamma_compression_effect_test.cpp
Fix YCbCrInput after the ResourcePool texture changes.
[movit] / gamma_compression_effect_test.cpp
1 // Unit tests for GammaCompressionEffect.
2 //
3 // Pretty much the inverse of the GammaExpansionEffect tests;
4 // EffectChainTest tests that they are actually inverses.
5 // However, the accuracy tests are somewhat simpler, since we
6 // only need to care about absolute errors and not relative.
7
8 #include <math.h>
9 #include <GL/glew.h>
10 #include "gtest/gtest.h"
11 #include "image_format.h"
12 #include "test_util.h"
13
14 TEST(GammaCompressionEffectTest, sRGB_KeyValues) {
15         float data[] = {
16                 0.0f, 1.0f,
17                 0.00309f, 0.00317f,   // On either side of the discontinuity.
18                 -0.5f, 1.5f,          // To check clamping.
19         };
20         float expected_data[] = {
21                 0.0f, 1.0f,
22                 0.040f, 0.041f,
23                 0.0f, 1.0f,
24         };
25         float out_data[4];
26         EffectChainTester tester(data, 2, 3, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
27         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
28
29         expect_equal(expected_data, out_data, 2, 3);
30 }
31
32 TEST(GammaCompressionEffectTest, sRGB_RampAlwaysIncreases) {
33         float data[256], out_data[256];
34         for (unsigned i = 0; i < 256; ++i) {
35                 data[i] = i / 255.0f;
36         }
37         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
38         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
39
40         for (unsigned i = 1; i < 256; ++i) {
41                 EXPECT_GT(out_data[i], out_data[i - 1])
42                    << "No increase between " << i-1 << " and " << i;
43         }
44 }
45
46 TEST(GammaCompressionEffectTest, sRGB_Accuracy) {
47         float data[256], expected_data[256], out_data[256];
48
49         for (int i = 0; i < 256; ++i) {
50                 double x = i / 255.0;
51
52                 expected_data[i] = x;
53
54                 // From the Wikipedia article on sRGB.
55                 if (x < 0.04045) {
56                         data[i] = x / 12.92;
57                 } else {
58                         data[i] = pow((x + 0.055) / 1.055, 2.4);
59                 }
60         }
61
62         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F);
63         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_sRGB);
64
65         // Maximum absolute error is 25% of one pixel level. For comparison,
66         // a straightforward ALU solution (using a branch and pow()), used as a
67         // “high anchor” to indicate limitations of float arithmetic etc.,
68         // reaches maximum absolute error of 3.7% of one pixel level
69         // and rms of 3.2e-6.
70         expect_equal(expected_data, out_data, 256, 1, 0.25 / 255.0, 1e-4);
71 }
72
73 TEST(GammaCompressionEffectTest, Rec709_KeyValues) {
74         float data[] = {
75                 0.0f, 1.0f,
76                 0.017778f, 0.018167f,  // On either side of the discontinuity.
77         };
78         float expected_data[] = {
79                 0.0f, 1.0f,
80                 0.080f, 0.082f,
81         };
82         float out_data[4];
83         EffectChainTester tester(data, 2, 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
84         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_REC_709);
85
86         expect_equal(expected_data, out_data, 2, 2);
87 }
88
89 TEST(GammaCompressionEffectTest, Rec709_RampAlwaysIncreases) {
90         float data[256], out_data[256];
91         for (unsigned i = 0; i < 256; ++i) {
92                 data[i] = i / 255.0f;
93         }
94         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
95         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_REC_709);
96
97         for (unsigned i = 1; i < 256; ++i) {
98                 EXPECT_GT(out_data[i], out_data[i - 1])
99                    << "No increase between " << i-1 << " and " << i;
100         }
101 }
102
103 TEST(GammaCompressionEffectTest, Rec709_Accuracy) {
104         float data[256], expected_data[256], out_data[256];
105
106         for (int i = 0; i < 256; ++i) {
107                 double x = i / 255.0;
108
109                 expected_data[i] = x;
110
111                 // Rec. 2020, page 3.
112                 if (x < 0.018 * 4.5) {
113                         data[i] = x / 4.5;
114                 } else {
115                         data[i] = pow((x + 0.099) / 1.099, 1.0 / 0.45);
116                 }
117         }
118
119         EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F);
120         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_REC_709);
121
122         // Maximum absolute error is 25% of one pixel level. For comparison,
123         // a straightforward ALU solution (using a branch and pow()), used as a
124         // “high anchor” to indicate limitations of float arithmetic etc.,
125         // reaches maximum absolute error of 3.7% of one pixel level
126         // and rms of 3.5e-6.
127         expect_equal(expected_data, out_data, 256, 1, 0.25 / 255.0, 1e-5);
128 }
129
130 // This test tests the same gamma ramp as Rec709_Accuracy, but with 10-bit
131 // input range and somewhat looser error bounds. (One could claim that this is
132 // already on the limit of what we can reasonably do with fp16 input, if you
133 // look at the local relative error.)
134 TEST(GammaCompressionEffectTest, Rec2020_10Bit_Accuracy) {
135         float data[1024], expected_data[1024], out_data[1024];
136
137         for (int i = 0; i < 1024; ++i) {
138                 double x = i / 1023.0;
139
140                 expected_data[i] = x;
141
142                 // Rec. 2020, page 3.
143                 if (x < 0.018 * 4.5) {
144                         data[i] = x / 4.5;
145                 } else {
146                         data[i] = pow((x + 0.099) / 1.099, 1.0 / 0.45);
147                 }
148         }
149
150         EffectChainTester tester(data, 1024, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F);
151         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_REC_2020_10_BIT);
152
153         // Maximum absolute error is 30% of one pixel level. For comparison,
154         // a straightforward ALU solution (using a branch and pow()), used as a
155         // “high anchor” to indicate limitations of float arithmetic etc.,
156         // reaches maximum absolute error of 25.2% of one pixel level
157         // and rms of 1.8e-6, so this is probably mostly related to input precision.
158         expect_equal(expected_data, out_data, 1024, 1, 0.30 / 1023.0, 1e-5);
159 }
160
161 TEST(GammaCompressionEffectTest, Rec2020_12BitIsVeryCloseToRec709) {
162         float data[4096];
163         for (unsigned i = 0; i < 4096; ++i) {
164                 data[i] = i / 4095.0f;
165         }
166         float out_data_709[4096];
167         float out_data_2020[4096];
168
169         EffectChainTester tester(data, 4096, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
170         tester.run(out_data_709, GL_RED, COLORSPACE_sRGB, GAMMA_REC_709);
171         EffectChainTester tester2(data, 4096, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
172         tester2.run(out_data_2020, GL_RED, COLORSPACE_sRGB, GAMMA_REC_2020_12_BIT);
173
174         double sqdiff = 0.0;
175         for (unsigned i = 0; i < 4096; ++i) {
176                 EXPECT_NEAR(out_data_709[i], out_data_2020[i], 0.001);
177                 sqdiff += (out_data_709[i] - out_data_2020[i]) * (out_data_709[i] - out_data_2020[i]);
178         }
179         EXPECT_GT(sqdiff, 1e-6);
180 }
181
182 // The fp16 _input_ provided by FlatInput is not enough to distinguish between
183 // all of the possible 12-bit input values (every other level translates to the
184 // same value). Thus, this test has extremely loose bounds; if we ever decide
185 // to start supporting fp32, we should re-run this and tighten them a lot.
186 TEST(GammaCompressionEffectTest, Rec2020_12Bit_Inaccuracy) {
187         float data[4096], expected_data[4096], out_data[4096];
188
189         for (int i = 0; i < 4096; ++i) {
190                 double x = i / 4095.0;
191
192                 expected_data[i] = x;
193
194                 // Rec. 2020, page 3.
195                 if (x < 0.0181 * 4.5) {
196                         data[i] = x / 4.5;
197                 } else {
198                         data[i] = pow((x + 0.0993) / 1.0993, 1.0 / 0.45);
199                 }
200         }
201
202         EffectChainTester tester(data, 4096, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F);
203         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_REC_2020_12_BIT);
204
205         // Maximum absolute error is 120% of one pixel level. For comparison,
206         // a straightforward ALU solution (using a branch and pow()), used as a
207         // “high anchor” to indicate limitations of float arithmetic etc.,
208         // reaches maximum absolute error of 71.1% of one pixel level
209         // and rms of 0.9e-6, so this is probably a combination of input
210         // precision and inaccuracies in the polynomial approximation.
211         expect_equal(expected_data, out_data, 4096, 1, 1.2 / 4095.0, 1e-5);
212 }