+TEST(GammaExpansionEffectTest, Rec709_Accuracy) {
+ float data[256], expected_data[256], out_data[256];
+
+ for (int i = 0; i < 256; ++i) {
+ double x = i / 255.0;
+
+ data[i] = x;
+
+ // Rec. 2020, page 3.
+ if (x < 0.018 * 4.5) {
+ expected_data[i] = x / 4.5;
+ } else {
+ expected_data[i] = pow((x + 0.099) / 1.099, 1.0 / 0.45);
+ }
+ }
+
+ EffectChainTester tester(data, 256, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_709, GL_RGBA32F);
+ tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+ // Accuracy limits; for comparison, limits for a straightforward ALU solution
+ // (using a branch and pow()) in parenthesis, used as a “high anchor” to
+ // indicate limitations of float arithmetic etc.:
+ //
+ // Maximum absolute error: 0.1% of max energy (0.046%)
+ // Maximum relative error: 1.0% of correct answer (0.080%)
+ // 10% of difference to next pixel level (6.19%)
+ // Allowed RMS error: 0.0001 (0.000010)
+ //
+ test_accuracy(expected_data, out_data, 256, 1e-3, 0.01, 0.1, 1e-4);
+}
+
+// This test tests the same gamma ramp as Rec709_Accuracy, but with 10-bit
+// input range and somewhat looser error bounds. (One could claim that this is
+// already on the limit of what we can reasonably do with fp16 input, if you
+// look at the local relative error.)
+TEST(GammaExpansionEffectTest, Rec2020_10Bit_Accuracy) {
+ float data[1024], expected_data[1024], out_data[1024];
+
+ for (int i = 0; i < 1024; ++i) {
+ double x = i / 1023.0;
+
+ data[i] = x;
+
+ // Rec. 2020, page 3.
+ if (x < 0.018 * 4.5) {
+ expected_data[i] = x / 4.5;
+ } else {
+ expected_data[i] = pow((x + 0.099) / 1.099, 1.0 / 0.45);
+ }
+ }
+
+ EffectChainTester tester(data, 1024, 1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_REC_2020_10_BIT, GL_RGBA32F);
+ tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+ // Accuracy limits; for comparison, limits for a straightforward ALU solution
+ // (using a branch and pow()) in parenthesis, used as a “high anchor” to
+ // indicate limitations of float arithmetic etc.:
+ //
+ // Maximum absolute error: 0.1% of max energy (0.036%)
+ // Maximum relative error: 1.0% of correct answer (0.064%)
+ // 30% of difference to next pixel level (24.9%)
+ // Allowed RMS error: 0.0001 (0.000005)
+ //
+ test_accuracy(expected_data, out_data, 1024, 1e-3, 0.01, 0.30, 1e-4);
+}
+