1 #include <assert.h>
3 #include "effect_util.h"
4 #include "gamma_expansion_effect.h"
5 #include "util.h"
7 using namespace std;
9 namespace movit {
11 GammaExpansionEffect::GammaExpansionEffect()
12         : source_curve(GAMMA_LINEAR)
13 {
14         register_int("source_curve", (int *)&source_curve);
15         register_uniform_float("linear_scale", &uniform_linear_scale);
16         register_uniform_float_array("c", uniform_c, 5);
17         register_uniform_float("beta", &uniform_beta);
18 }
21 {
22         if (source_curve == GAMMA_LINEAR) {
24         }
25         if (source_curve == GAMMA_sRGB ||
26             source_curve == GAMMA_REC_709 ||  // Also includes Rec. 601, and 10-bit Rec. 2020.
27             source_curve == GAMMA_REC_2020_12_BIT) {
29         }
30         assert(false);
31 }
33 void GammaExpansionEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
34 {
35         Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
37         // All of these curves follow a continuous curve that's piecewise defined;
38         // very low values (up to some β) are linear. Above β, we have a power curve
39         // that looks like this:
40         //
41         //   y = ((x + ɑ - 1) / ɑ)^ɣ
42         //
43         // However, pow() is relatively slow in GLSL, so we approximate this
44         // part by a minimax polynomial, whose coefficients are precalculated
45         // in Maple. (It is very hard to accurately model the curve as a whole
46         // using minimax polynomials; both Maple and Mathematica generally
47         // just error out if you ask them to optimize over 0..1 with a higher-degree
48         // polynomial.)
49         //
50         // We put some extra weight on areas near β to keep a continuous curve,
51         // and near 1.0, since we'd really like f(1.0) = 1.0, or approximately so.
52         // The following Maple commands, using sRGB below as an example, will
53         // compute the coefficients:
54         //
55         // > alpha := 1.055;
56         // > beta := 0.04045;
57         // > gamma_ := 2.4;
58         // > w := x -> piecewise(x < beta + 0.001, 10, x > 0.999, 10, 1);
59         // > numapprox[minimax](((x + alpha - 1) / alpha)^gamma_, x=beta..1, [4,0], w(x), 'maxerror');
60         //
61         // The variable 'maxerror' will then contain the maximum absolute error
62         // at any point of the curve, and we report this along with the absolute
63         // error at beta and at 1.0. Keep in mind that along this curve,
64         // the smallest minimum difference between any two 8-bit sRGB pixel levels
65         // (in the exponential part of the curve) in linear light is that
66         // between 11/255 and 12/255, which is about 0.00033 (or three to four
67         // times of the sRGB maxerror below). The choice of a fourth-degree
68         // polynomial was made with this in mind; we have not cared equally
69         // much about 10- and 12-bit Rec. 2020.
70         //
71         // NOTE: The error at beta is compared to the _linear_ part of the curve.
72         // Since the standards give these with only a few decimals, it means that
73         // the linear and exponential parts will not match up exactly, and even
74         // a perfect approximation will have error > 0 here; sometimes, even larger
75         // than maxerror for the curve itself.
77         if (source_curve == GAMMA_sRGB) {
78                 // From the Wikipedia article on sRGB; ɑ (called a+1 there) = 1.055,
79                 // β = 0.04045, ɣ = 2.4.
80                 // maxerror      = 0.000094
81                 // error at beta = 0.000012
82                 // error at 1.0  = 0.000012
83                 //
84                 // Note that the worst _relative_ error by far is just at the beginning
85                 // of the exponential curve, ie., just around β.
86                 uniform_linear_scale = 1.0 / 12.92;
87                 uniform_c = 0.001324469581;
88                 uniform_c = 0.02227416690;
89                 uniform_c = 0.5917615253;
90                 uniform_c = 0.4733532353;
91                 uniform_c = -0.08880738120;
92                 uniform_beta = 0.04045;
93         }
94         if (source_curve == GAMMA_REC_709) {  // Also includes Rec. 601, and 10-bit Rec. 2020.
95                 // Rec. 2020, page 3; ɑ = 1.099, β = 0.018 * 4.5, ɣ = 1/0.45.
96                 // maxerror      = 0.000043
97                 // error at beta = 0.000051 (see note above!)
98                 // error at 1.0  = 0.000004
99                 //
100                 // Note that Rec. 2020 only gives the other direction, which is why
101                 // our beta and gamma are different from the numbers mentioned
102                 // (we've inverted the formula).
103                 uniform_linear_scale = 1.0 / 4.5;
104                 uniform_c = 0.005137028744;
105                 uniform_c = 0.09802596889;
106                 uniform_c = 0.7255768864;
107                 uniform_c = 0.2135067966;
108                 uniform_c = -0.04225094667;
109                 uniform_beta = 0.018 * 4.5;
110         }
111         if (source_curve == GAMMA_REC_2020_12_BIT) {
112                 // Rec. 2020, page 3; ɑ = 1.0993, β = 0.0181 * 4.5, ɣ = 1/0.45.
113                 // maxerror      = 0.000042
114                 // error at beta = 0.000005
115                 // error at 1.0  = 0.000004
116                 //
117                 // Note that Rec. 2020 only gives the other direction, which is why
118                 // our beta and gamma are different from the numbers mentioned
119                 // (we've inverted the formula).
120                 uniform_linear_scale = 1.0 / 4.5;
121                 uniform_c = 0.005167545928;
122                 uniform_c = 0.09835585809;
123                 uniform_c = 0.7254820139;
124                 uniform_c = 0.2131291155;
125                 uniform_c = -0.04213877222;
126                 uniform_beta = 0.0181 * 4.5;
127         }
128 }
130 }  // namespace movit