Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2015 Kevin Wheatley <kevin.j.wheatley@gmail.com> | ||
3 | * Copyright (c) 2016 Ronald S. Bultje <rsbultje@gmail.com> | ||
4 | * Copyright (c) 2023 Leo Izen <leo.izen@gmail.com> | ||
5 | * | ||
6 | * This file is part of FFmpeg. | ||
7 | * | ||
8 | * FFmpeg is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU Lesser General Public | ||
10 | * License as published by the Free Software Foundation; either | ||
11 | * version 2.1 of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * FFmpeg is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * Lesser General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Lesser General Public | ||
19 | * License along with FFmpeg; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
21 | */ | ||
22 | |||
23 | /** | ||
24 | * @file Colorspace functions for libavutil | ||
25 | * @author Ronald S. Bultje <rsbultje@gmail.com> | ||
26 | * @author Leo Izen <leo.izen@gmail.com> | ||
27 | * @author Kevin Wheatley <kevin.j.wheatley@gmail.com> | ||
28 | */ | ||
29 | |||
30 | #include <stdlib.h> | ||
31 | #include <math.h> | ||
32 | |||
33 | #include "attributes.h" | ||
34 | #include "csp.h" | ||
35 | #include "pixfmt.h" | ||
36 | #include "rational.h" | ||
37 | |||
38 | #define AVR(d) { (int)(d * 100000 + 0.5), 100000 } | ||
39 | |||
40 | /* | ||
41 | * All constants explained in e.g. https://linuxtv.org/downloads/v4l-dvb-apis/ch02s06.html | ||
42 | * The older ones (bt470bg/m) are also explained in their respective ITU docs | ||
43 | * (e.g. https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.470-5-199802-S!!PDF-E.pdf) | ||
44 | * whereas the newer ones can typically be copied directly from wikipedia :) | ||
45 | */ | ||
46 | static const struct AVLumaCoefficients luma_coefficients[AVCOL_SPC_NB] = { | ||
47 | [AVCOL_SPC_FCC] = { AVR(0.30), AVR(0.59), AVR(0.11) }, | ||
48 | [AVCOL_SPC_BT470BG] = { AVR(0.299), AVR(0.587), AVR(0.114) }, | ||
49 | [AVCOL_SPC_SMPTE170M] = { AVR(0.299), AVR(0.587), AVR(0.114) }, | ||
50 | [AVCOL_SPC_BT709] = { AVR(0.2126), AVR(0.7152), AVR(0.0722) }, | ||
51 | [AVCOL_SPC_SMPTE240M] = { AVR(0.212), AVR(0.701), AVR(0.087) }, | ||
52 | [AVCOL_SPC_YCOCG] = { AVR(0.25), AVR(0.5), AVR(0.25) }, | ||
53 | [AVCOL_SPC_RGB] = { AVR(1), AVR(1), AVR(1) }, | ||
54 | [AVCOL_SPC_BT2020_NCL] = { AVR(0.2627), AVR(0.6780), AVR(0.0593) }, | ||
55 | [AVCOL_SPC_BT2020_CL] = { AVR(0.2627), AVR(0.6780), AVR(0.0593) }, | ||
56 | }; | ||
57 | |||
58 | 13710 | const struct AVLumaCoefficients *av_csp_luma_coeffs_from_avcsp(enum AVColorSpace csp) | |
59 | { | ||
60 | const AVLumaCoefficients *coeffs; | ||
61 | |||
62 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13710 times.
|
13710 | if (csp >= AVCOL_SPC_NB) |
63 | ✗ | return NULL; | |
64 | 13710 | coeffs = &luma_coefficients[csp]; | |
65 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13710 times.
|
13710 | if (!coeffs->cr.num) |
66 | ✗ | return NULL; | |
67 | |||
68 | 13710 | return coeffs; | |
69 | } | ||
70 | |||
71 | #define WP_D65 { AVR(0.3127), AVR(0.3290) } | ||
72 | #define WP_C { AVR(0.3100), AVR(0.3160) } | ||
73 | #define WP_DCI { AVR(0.3140), AVR(0.3510) } | ||
74 | #define WP_E { {1, 3}, {1, 3} } | ||
75 | |||
76 | static const AVColorPrimariesDesc color_primaries[AVCOL_PRI_NB] = { | ||
77 | [AVCOL_PRI_BT709] = { WP_D65, { { AVR(0.640), AVR(0.330) }, { AVR(0.300), AVR(0.600) }, { AVR(0.150), AVR(0.060) } } }, | ||
78 | [AVCOL_PRI_BT470M] = { WP_C, { { AVR(0.670), AVR(0.330) }, { AVR(0.210), AVR(0.710) }, { AVR(0.140), AVR(0.080) } } }, | ||
79 | [AVCOL_PRI_BT470BG] = { WP_D65, { { AVR(0.640), AVR(0.330) }, { AVR(0.290), AVR(0.600) }, { AVR(0.150), AVR(0.060) } } }, | ||
80 | [AVCOL_PRI_SMPTE170M] = { WP_D65, { { AVR(0.630), AVR(0.340) }, { AVR(0.310), AVR(0.595) }, { AVR(0.155), AVR(0.070) } } }, | ||
81 | [AVCOL_PRI_SMPTE240M] = { WP_D65, { { AVR(0.630), AVR(0.340) }, { AVR(0.310), AVR(0.595) }, { AVR(0.155), AVR(0.070) } } }, | ||
82 | [AVCOL_PRI_SMPTE428] = { WP_E, { { AVR(0.735), AVR(0.265) }, { AVR(0.274), AVR(0.718) }, { AVR(0.167), AVR(0.009) } } }, | ||
83 | [AVCOL_PRI_SMPTE431] = { WP_DCI, { { AVR(0.680), AVR(0.320) }, { AVR(0.265), AVR(0.690) }, { AVR(0.150), AVR(0.060) } } }, | ||
84 | [AVCOL_PRI_SMPTE432] = { WP_D65, { { AVR(0.680), AVR(0.320) }, { AVR(0.265), AVR(0.690) }, { AVR(0.150), AVR(0.060) } } }, | ||
85 | [AVCOL_PRI_FILM] = { WP_C, { { AVR(0.681), AVR(0.319) }, { AVR(0.243), AVR(0.692) }, { AVR(0.145), AVR(0.049) } } }, | ||
86 | [AVCOL_PRI_BT2020] = { WP_D65, { { AVR(0.708), AVR(0.292) }, { AVR(0.170), AVR(0.797) }, { AVR(0.131), AVR(0.046) } } }, | ||
87 | [AVCOL_PRI_JEDEC_P22] = { WP_D65, { { AVR(0.630), AVR(0.340) }, { AVR(0.295), AVR(0.605) }, { AVR(0.155), AVR(0.077) } } }, | ||
88 | }; | ||
89 | |||
90 | 352197 | const AVColorPrimariesDesc *av_csp_primaries_desc_from_id(enum AVColorPrimaries prm) | |
91 | { | ||
92 | const AVColorPrimariesDesc *p; | ||
93 | |||
94 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 352197 times.
|
352197 | if (prm >= AVCOL_PRI_NB) |
95 | ✗ | return NULL; | |
96 | 352197 | p = &color_primaries[prm]; | |
97 |
2/2✓ Branch 0 taken 345099 times.
✓ Branch 1 taken 7098 times.
|
352197 | if (!p->prim.r.x.num) |
98 | 345099 | return NULL; | |
99 | |||
100 | 7098 | return p; | |
101 | } | ||
102 | |||
103 | 48 | static av_always_inline AVRational abs_sub_q(AVRational r1, AVRational r2) | |
104 | { | ||
105 | 48 | AVRational diff = av_sub_q(r1, r2); | |
106 | /* denominator assumed to be positive */ | ||
107 | 48 | return av_make_q(abs(diff.num), diff.den); | |
108 | } | ||
109 | |||
110 | 6 | enum AVColorPrimaries av_csp_primaries_id_from_desc(const AVColorPrimariesDesc *prm) | |
111 | { | ||
112 | AVRational delta; | ||
113 | |||
114 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | for (enum AVColorPrimaries p = 0; p < AVCOL_PRI_NB; p++) { |
115 | 12 | const AVColorPrimariesDesc *ref = &color_primaries[p]; | |
116 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | if (!ref->prim.r.x.num) |
117 | 6 | continue; | |
118 | |||
119 | 6 | delta = abs_sub_q(prm->prim.r.x, ref->prim.r.x); | |
120 | 6 | delta = av_add_q(delta, abs_sub_q(prm->prim.r.y, ref->prim.r.y)); | |
121 | 6 | delta = av_add_q(delta, abs_sub_q(prm->prim.g.x, ref->prim.g.x)); | |
122 | 6 | delta = av_add_q(delta, abs_sub_q(prm->prim.g.y, ref->prim.g.y)); | |
123 | 6 | delta = av_add_q(delta, abs_sub_q(prm->prim.b.x, ref->prim.b.x)); | |
124 | 6 | delta = av_add_q(delta, abs_sub_q(prm->prim.b.y, ref->prim.b.y)); | |
125 | 6 | delta = av_add_q(delta, abs_sub_q(prm->wp.x, ref->wp.x)); | |
126 | 6 | delta = av_add_q(delta, abs_sub_q(prm->wp.y, ref->wp.y)); | |
127 | |||
128 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | if (av_cmp_q(delta, av_make_q(1, 1000)) < 0) |
129 | 6 | return p; | |
130 | } | ||
131 | |||
132 | ✗ | return AVCOL_PRI_UNSPECIFIED; | |
133 | } | ||
134 | |||
135 | static const double approximate_gamma[AVCOL_TRC_NB] = { | ||
136 | [AVCOL_TRC_BT709] = 1.961, | ||
137 | [AVCOL_TRC_SMPTE170M] = 1.961, | ||
138 | [AVCOL_TRC_SMPTE240M] = 1.961, | ||
139 | [AVCOL_TRC_BT1361_ECG] = 1.961, | ||
140 | [AVCOL_TRC_BT2020_10] = 1.961, | ||
141 | [AVCOL_TRC_BT2020_12] = 1.961, | ||
142 | [AVCOL_TRC_GAMMA22] = 2.2, | ||
143 | [AVCOL_TRC_IEC61966_2_1] = 2.2, | ||
144 | [AVCOL_TRC_GAMMA28] = 2.8, | ||
145 | [AVCOL_TRC_LINEAR] = 1.0, | ||
146 | [AVCOL_TRC_SMPTE428] = 2.6, | ||
147 | }; | ||
148 | |||
149 | 247 | double av_csp_approximate_trc_gamma(enum AVColorTransferCharacteristic trc) | |
150 | { | ||
151 | double gamma; | ||
152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 247 times.
|
247 | if (trc >= AVCOL_TRC_NB) |
153 | ✗ | return 0.0; | |
154 | 247 | gamma = approximate_gamma[trc]; | |
155 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 247 times.
|
247 | if (gamma > 0) |
156 | ✗ | return gamma; | |
157 | 247 | return 0.0; | |
158 | } | ||
159 | |||
160 | #define BT709_alpha 1.099296826809442 | ||
161 | #define BT709_beta 0.018053968510807 | ||
162 | |||
163 | 76 | static double trc_bt709(double Lc) | |
164 | { | ||
165 | 76 | const double a = BT709_alpha; | |
166 | 76 | const double b = BT709_beta; | |
167 | |||
168 | return (0.0 > Lc) ? 0.0 | ||
169 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 60 times.
|
136 | : ( b > Lc) ? 4.500 * Lc |
170 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 40 times.
|
60 | : a * pow(Lc, 0.45) - (a - 1.0); |
171 | } | ||
172 | |||
173 | 76 | static double trc_bt709_inv(double E) | |
174 | { | ||
175 | 76 | const double a = BT709_alpha; | |
176 | 76 | const double b = 4.500 * BT709_beta; | |
177 | |||
178 | return (0.0 > E) ? 0.0 | ||
179 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
|
152 | : ( b > E) ? E / 4.500 |
180 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 40 times.
|
76 | : pow((E + (a - 1.0)) / a, 1.0 / 0.45); |
181 | } | ||
182 | |||
183 | 799 | static double trc_gamma22(double Lc) | |
184 | { | ||
185 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 795 times.
|
799 | return (0.0 > Lc) ? 0.0 : pow(Lc, 1.0/ 2.2); |
186 | } | ||
187 | |||
188 | 799 | static double trc_gamma22_inv(double E) | |
189 | { | ||
190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 799 times.
|
799 | return (0.0 > E) ? 0.0 : pow(E, 2.2); |
191 | } | ||
192 | |||
193 | 799 | static double trc_gamma28(double Lc) | |
194 | { | ||
195 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 795 times.
|
799 | return (0.0 > Lc) ? 0.0 : pow(Lc, 1.0/ 2.8); |
196 | } | ||
197 | |||
198 | 799 | static double trc_gamma28_inv(double E) | |
199 | { | ||
200 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 799 times.
|
799 | return (0.0 > E) ? 0.0 : pow(E, 2.8); |
201 | } | ||
202 | |||
203 | 19 | static double trc_smpte240M(double Lc) | |
204 | { | ||
205 | 19 | const double a = 1.1115; | |
206 | 19 | const double b = 0.0228; | |
207 | |||
208 | return (0.0 > Lc) ? 0.0 | ||
209 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
|
34 | : ( b > Lc) ? 4.000 * Lc |
210 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
|
15 | : a * pow(Lc, 0.45) - (a - 1.0); |
211 | } | ||
212 | |||
213 | 19 | static double trc_smpte240M_inv(double E) | |
214 | { | ||
215 | 19 | const double a = 1.1115; | |
216 | 19 | const double b = 4.000 * 0.0228; | |
217 | |||
218 | return (0.0 > E) ? 0.0 | ||
219 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
38 | : ( b > E) ? E / 4.000 |
220 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
|
19 | : pow((E + (a - 1.0)) / a, 1.0 / 0.45); |
221 | } | ||
222 | |||
223 | 38 | static double trc_linear(double Lc) | |
224 | { | ||
225 | 38 | return Lc; | |
226 | } | ||
227 | |||
228 | 19 | static double trc_log(double Lc) | |
229 | { | ||
230 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 11 times.
|
19 | return (0.01 > Lc) ? 0.0 : 1.0 + log10(Lc) / 2.0; |
231 | } | ||
232 | |||
233 | 19 | static double trc_log_inv(double E) | |
234 | { | ||
235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | return (0.0 > E) ? 0.01 : pow(10.0, 2.0 * (E - 1.0)); |
236 | } | ||
237 | |||
238 | 19 | static double trc_log_sqrt(double Lc) | |
239 | { | ||
240 | // sqrt(10) / 1000 | ||
241 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 13 times.
|
19 | return (0.00316227766 > Lc) ? 0.0 : 1.0 + log10(Lc) / 2.5; |
242 | } | ||
243 | |||
244 | 19 | static double trc_log_sqrt_inv(double E) | |
245 | { | ||
246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | return (0.0 > E) ? 0.00316227766 : pow(10.0, 2.5 * (E - 1.0)); |
247 | } | ||
248 | |||
249 | 19 | static double trc_iec61966_2_4(double Lc) | |
250 | { | ||
251 | 19 | const double a = BT709_alpha; | |
252 | 19 | const double b = BT709_beta; | |
253 | |||
254 | 21 | return (-b >= Lc) ? -a * pow(-Lc, 0.45) + (a - 1.0) | |
255 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
|
36 | : ( b > Lc) ? 4.500 * Lc |
256 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 10 times.
|
17 | : a * pow( Lc, 0.45) - (a - 1.0); |
257 | } | ||
258 | |||
259 | 19 | static double trc_iec61966_2_4_inv(double E) | |
260 | { | ||
261 | 19 | const double a = BT709_alpha; | |
262 | 19 | const double b = 4.500 * BT709_beta; | |
263 | |||
264 | 21 | return (-b >= E) ? -pow((-E + (a - 1.0)) / a, 1.0 / 0.45) | |
265 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
|
36 | : ( b > E) ? E / 4.500 |
266 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 10 times.
|
17 | : pow(( E + (a - 1.0)) / a, 1.0 / 0.45); |
267 | } | ||
268 | |||
269 | 19 | static double trc_bt1361(double Lc) | |
270 | { | ||
271 | 19 | const double a = BT709_alpha; | |
272 | 19 | const double b = BT709_beta; | |
273 | |||
274 | 3 | return (-0.0045 >= Lc) ? -(a * pow(-4.0 * Lc, 0.45) + (a - 1.0)) / 4.0 | |
275 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 16 times.
|
35 | : ( b > Lc) ? 4.500 * Lc |
276 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
|
16 | : a * pow( Lc, 0.45) - (a - 1.0); |
277 | } | ||
278 | |||
279 | 19 | static double trc_bt1361_inv(double E) | |
280 | { | ||
281 | 19 | const double a = BT709_alpha; | |
282 | 19 | const double b = 4.500 * BT709_beta; | |
283 | |||
284 | 3 | return (-0.02025 >= E) ? -pow((-4.0 * E - (a - 1.0)) / a, 1.0 / 0.45) / 4.0 | |
285 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 16 times.
|
35 | : ( b > E) ? E / 4.500 |
286 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
|
16 | : pow(( E + (a - 1.0)) / a, 1.0 / 0.45); |
287 | } | ||
288 | |||
289 | 799 | static double trc_iec61966_2_1(double Lc) | |
290 | { | ||
291 | 799 | const double a = 1.055; | |
292 | 799 | const double b = 0.0031308; | |
293 | |||
294 | return (0.0 > Lc) ? 0.0 | ||
295 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 795 times.
|
1594 | : ( b > Lc) ? 12.92 * Lc |
296 |
2/2✓ Branch 0 taken 121 times.
✓ Branch 1 taken 674 times.
|
795 | : a * pow(Lc, 1.0 / 2.4) - (a - 1.0); |
297 | } | ||
298 | |||
299 | 799 | static double trc_iec61966_2_1_inv(double E) | |
300 | { | ||
301 | 799 | const double a = 1.055; | |
302 | 799 | const double b = 12.92 * 0.0031308; | |
303 | |||
304 | return (0.0 > E) ? 0.0 | ||
305 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 799 times.
|
1598 | : ( b > E) ? E / 12.92 |
306 |
2/2✓ Branch 0 taken 125 times.
✓ Branch 1 taken 674 times.
|
799 | : pow((E + (a - 1.0)) / a, 2.4); |
307 | return E; | ||
308 | } | ||
309 | |||
310 | #define PQ_c1 ( 3424.0 / 4096.0) /* c3-c2 + 1 */ | ||
311 | #define PQ_c2 ( 32.0 * 2413.0 / 4096.0) | ||
312 | #define PQ_c3 ( 32.0 * 2392.0 / 4096.0) | ||
313 | #define PQ_m (128.0 * 2523.0 / 4096.0) | ||
314 | #define PQ_n ( 0.25 * 2610.0 / 4096.0) | ||
315 | |||
316 | 19 | static double trc_smpte_st2084(double Lc) | |
317 | { | ||
318 | 19 | const double c1 = PQ_c1; | |
319 | 19 | const double c2 = PQ_c2; | |
320 | 19 | const double c3 = PQ_c3; | |
321 | 19 | const double m = PQ_m; | |
322 | 19 | const double n = PQ_n; | |
323 | 19 | const double L = Lc / 10000.0; | |
324 | 19 | const double Ln = pow(L, n); | |
325 | |||
326 | return (0.0 > Lc) ? 0.0 | ||
327 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
|
19 | : pow((c1 + c2 * Ln) / (1.0 + c3 * Ln), m); |
328 | |||
329 | } | ||
330 | |||
331 | 19 | static double trc_smpte_st2084_inv(double E) | |
332 | { | ||
333 | 19 | const double c1 = PQ_c1; | |
334 | 19 | const double c2 = PQ_c2; | |
335 | 19 | const double c3 = PQ_c3; | |
336 | 19 | const double m = PQ_m; | |
337 | 19 | const double n = PQ_n; | |
338 | 19 | const double Em = pow(E, 1.0 / m); | |
339 | |||
340 | return (c1 > Em) ? 0.0 | ||
341 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
|
19 | : 10000.0 * pow((Em - c1) / (c2 - c3 * Em), 1.0 / n); |
342 | } | ||
343 | |||
344 | #define DCI_L 48.00 | ||
345 | #define DCI_P 52.37 | ||
346 | |||
347 | 19 | static double trc_smpte_st428_1(double Lc) | |
348 | { | ||
349 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
|
19 | return (0.0 > Lc) ? 0.0 : pow(DCI_L / DCI_P * Lc, 1.0 / 2.6); |
350 | } | ||
351 | |||
352 | 19 | static double trc_smpte_st428_1_inv(double E) | |
353 | { | ||
354 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | return (0.0 > E) ? 0.0 : DCI_P / DCI_L * pow(E, 2.6); |
355 | } | ||
356 | |||
357 | #define HLG_a 0.17883277 | ||
358 | #define HLG_b 0.28466892 | ||
359 | #define HLG_c 0.55991073 | ||
360 | |||
361 | 799 | static double trc_arib_std_b67(double Lc) { | |
362 | // The function uses the definition from HEVC, which assumes that the peak | ||
363 | // white is input level = 1. (this is equivalent to scaling E = Lc * 12 and | ||
364 | // using the definition from the ARIB STD-B67 spec) | ||
365 | 799 | const double a = HLG_a; | |
366 | 799 | const double b = HLG_b; | |
367 | 799 | const double c = HLG_c; | |
368 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 795 times.
|
1594 | return (0.0 > Lc) ? 0.0 : |
369 |
2/2✓ Branch 0 taken 368 times.
✓ Branch 1 taken 427 times.
|
795 | (Lc <= 1.0 / 12.0 ? sqrt(3.0 * Lc) : a * log(12.0 * Lc - b) + c); |
370 | } | ||
371 | |||
372 | 799 | static double trc_arib_std_b67_inv(double E) | |
373 | { | ||
374 | 799 | const double a = HLG_a; | |
375 | 799 | const double b = HLG_b; | |
376 | 799 | const double c = HLG_c; | |
377 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 799 times.
|
1598 | return (0.0 > E) ? 0.0 : |
378 |
2/2✓ Branch 0 taken 375 times.
✓ Branch 1 taken 424 times.
|
799 | (E <= 0.5 ? E * E / 3.0 : (exp((E - c) / a) + b) / 12.0); |
379 | } | ||
380 | |||
381 | static const av_csp_trc_function trc_funcs[AVCOL_TRC_NB] = { | ||
382 | [AVCOL_TRC_BT709] = trc_bt709, | ||
383 | [AVCOL_TRC_GAMMA22] = trc_gamma22, | ||
384 | [AVCOL_TRC_GAMMA28] = trc_gamma28, | ||
385 | [AVCOL_TRC_SMPTE170M] = trc_bt709, | ||
386 | [AVCOL_TRC_SMPTE240M] = trc_smpte240M, | ||
387 | [AVCOL_TRC_LINEAR] = trc_linear, | ||
388 | [AVCOL_TRC_LOG] = trc_log, | ||
389 | [AVCOL_TRC_LOG_SQRT] = trc_log_sqrt, | ||
390 | [AVCOL_TRC_IEC61966_2_4] = trc_iec61966_2_4, | ||
391 | [AVCOL_TRC_BT1361_ECG] = trc_bt1361, | ||
392 | [AVCOL_TRC_IEC61966_2_1] = trc_iec61966_2_1, | ||
393 | [AVCOL_TRC_BT2020_10] = trc_bt709, | ||
394 | [AVCOL_TRC_BT2020_12] = trc_bt709, | ||
395 | [AVCOL_TRC_SMPTE2084] = trc_smpte_st2084, | ||
396 | [AVCOL_TRC_SMPTE428] = trc_smpte_st428_1, | ||
397 | [AVCOL_TRC_ARIB_STD_B67] = trc_arib_std_b67, | ||
398 | }; | ||
399 | |||
400 | 37458 | av_csp_trc_function av_csp_trc_func_from_id(enum AVColorTransferCharacteristic trc) | |
401 | { | ||
402 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37458 times.
|
37458 | if (trc >= AVCOL_TRC_NB) |
403 | ✗ | return NULL; | |
404 | 37458 | return trc_funcs[trc]; | |
405 | } | ||
406 | |||
407 | static const av_csp_trc_function trc_inv_funcs[AVCOL_TRC_NB] = { | ||
408 | [AVCOL_TRC_BT709] = trc_bt709_inv, | ||
409 | [AVCOL_TRC_GAMMA22] = trc_gamma22_inv, | ||
410 | [AVCOL_TRC_GAMMA28] = trc_gamma28_inv, | ||
411 | [AVCOL_TRC_SMPTE170M] = trc_bt709_inv, | ||
412 | [AVCOL_TRC_SMPTE240M] = trc_smpte240M_inv, | ||
413 | [AVCOL_TRC_LINEAR] = trc_linear, | ||
414 | [AVCOL_TRC_LOG] = trc_log_inv, | ||
415 | [AVCOL_TRC_LOG_SQRT] = trc_log_sqrt_inv, | ||
416 | [AVCOL_TRC_IEC61966_2_4] = trc_iec61966_2_4_inv, | ||
417 | [AVCOL_TRC_BT1361_ECG] = trc_bt1361_inv, | ||
418 | [AVCOL_TRC_IEC61966_2_1] = trc_iec61966_2_1_inv, | ||
419 | [AVCOL_TRC_BT2020_10] = trc_bt709_inv, | ||
420 | [AVCOL_TRC_BT2020_12] = trc_bt709_inv, | ||
421 | [AVCOL_TRC_SMPTE2084] = trc_smpte_st2084_inv, | ||
422 | [AVCOL_TRC_SMPTE428] = trc_smpte_st428_1_inv, | ||
423 | [AVCOL_TRC_ARIB_STD_B67] = trc_arib_std_b67_inv, | ||
424 | }; | ||
425 | |||
426 | 19 | av_csp_trc_function av_csp_trc_func_inv_from_id(enum AVColorTransferCharacteristic trc) | |
427 | { | ||
428 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if (trc >= AVCOL_TRC_NB) |
429 | ✗ | return NULL; | |
430 | 19 | return trc_inv_funcs[trc]; | |
431 | } | ||
432 | |||
433 | 1040 | static void eotf_linear(const double Lw, const double Lb, double E[3]) | |
434 | { | ||
435 |
2/2✓ Branch 0 taken 3120 times.
✓ Branch 1 taken 1040 times.
|
4160 | for (int i = 0; i < 3; i++) |
436 | 3120 | E[i] = (Lw - Lb) * E[i] + Lb; | |
437 | 1040 | } | |
438 | |||
439 | 1040 | static void eotf_linear_inv(const double Lw, const double Lb, double L[3]) | |
440 | { | ||
441 |
2/2✓ Branch 0 taken 3120 times.
✓ Branch 1 taken 1040 times.
|
4160 | for (int i = 0; i < 3; i++) |
442 | 3120 | L[i] = (L[i] - Lb) / (Lw - Lb); | |
443 | 1040 | } | |
444 | |||
445 | #define WRAP_SDR_OETF(name) \ | ||
446 | static void oetf_##name(double L[3]) \ | ||
447 | { \ | ||
448 | for (int i = 0; i < 3; i++) \ | ||
449 | L[i] = trc_##name(L[i]); \ | ||
450 | } \ | ||
451 | \ | ||
452 | static void oetf_##name##_inv(double E[3]) \ | ||
453 | { \ | ||
454 | for (int i = 0; i < 3; i++) \ | ||
455 | E[i] = trc_##name##_inv(E[i]); \ | ||
456 | } | ||
457 | |||
458 |
2/2✓ Branch 1 taken 1560 times.
✓ Branch 2 taken 520 times.
|
4160 | WRAP_SDR_OETF(gamma22) |
459 |
2/2✓ Branch 1 taken 1560 times.
✓ Branch 2 taken 520 times.
|
4160 | WRAP_SDR_OETF(gamma28) |
460 |
2/2✓ Branch 1 taken 1560 times.
✓ Branch 2 taken 520 times.
|
4160 | WRAP_SDR_OETF(iec61966_2_1) |
461 | |||
462 | #define WRAP_SDR_EOTF(name) \ | ||
463 | static void eotf_##name(double Lw, double Lb, double E[3]) \ | ||
464 | { \ | ||
465 | oetf_##name##_inv(E); \ | ||
466 | eotf_linear(Lw, Lb, E); \ | ||
467 | } \ | ||
468 | \ | ||
469 | static void eotf_##name##_inv(double Lw, double Lb, double L[3]) \ | ||
470 | { \ | ||
471 | eotf_linear_inv(Lw, Lb, L); \ | ||
472 | oetf_##name(L); \ | ||
473 | } | ||
474 | |||
475 | 1040 | WRAP_SDR_EOTF(gamma22) | |
476 | 1040 | WRAP_SDR_EOTF(gamma28) | |
477 | 1040 | WRAP_SDR_EOTF(iec61966_2_1) | |
478 | |||
479 | 1820 | static void eotf_bt1886(const double Lw, const double Lb, double E[3]) | |
480 | { | ||
481 | 1820 | const double Lw_inv = pow(Lw, 1.0 / 2.4); | |
482 | 1820 | const double Lb_inv = pow(Lb, 1.0 / 2.4); | |
483 | 1820 | const double a = pow(Lw_inv - Lb_inv, 2.4); | |
484 | 1820 | const double b = Lb_inv / (Lw_inv - Lb_inv); | |
485 | |||
486 |
2/2✓ Branch 0 taken 5460 times.
✓ Branch 1 taken 1820 times.
|
7280 | for (int i = 0; i < 3; i++) |
487 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5460 times.
|
5460 | E[i] = (-b > E[i]) ? 0.0 : a * pow(E[i] + b, 2.4); |
488 | 1820 | } | |
489 | |||
490 | 1820 | static void eotf_bt1886_inv(const double Lw, const double Lb, double L[3]) | |
491 | { | ||
492 | 1820 | const double Lw_inv = pow(Lw, 1.0 / 2.4); | |
493 | 1820 | const double Lb_inv = pow(Lb, 1.0 / 2.4); | |
494 | 1820 | const double a = pow(Lw_inv - Lb_inv, 2.4); | |
495 | 1820 | const double b = Lb_inv / (Lw_inv - Lb_inv); | |
496 | |||
497 |
2/2✓ Branch 0 taken 5460 times.
✓ Branch 1 taken 1820 times.
|
7280 | for (int i = 0; i < 3; i++) |
498 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5460 times.
|
5460 | L[i] = (0.0 > L[i]) ? 0.0 : pow(L[i] / a, 1.0 / 2.4) - b; |
499 | 1820 | } | |
500 | |||
501 | ✗ | static void eotf_smpte_st2084(const double Lw, const double Lb, double E[3]) | |
502 | { | ||
503 | ✗ | for (int i = 0; i < 3; i++) | |
504 | ✗ | E[i] = trc_smpte_st2084_inv(E[i]); | |
505 | ✗ | } | |
506 | |||
507 | ✗ | static void eotf_smpte_st2084_inv(const double Lw, const double Lb, double L[3]) | |
508 | { | ||
509 | ✗ | for (int i = 0; i < 3; i++) | |
510 | ✗ | L[i] = trc_smpte_st2084(L[i]); | |
511 | ✗ | } | |
512 | |||
513 | /* This implementation assumes an SMPTE RP 431-2 reference projector (DCI) */ | ||
514 | #define DCI_L 48.00 | ||
515 | #define DCI_P 52.37 | ||
516 | #define DCI_X (42.94 / DCI_L) | ||
517 | #define DCI_Z (45.82 / DCI_L) | ||
518 | |||
519 | 10 | static void eotf_smpte_st428_1(const double Lw_Y, const double Lb_Y, double E[3]) | |
520 | { | ||
521 | 10 | const double Lw[3] = { DCI_X * Lw_Y, Lw_Y, DCI_Z * Lw_Y }; | |
522 | 10 | const double Lb[3] = { DCI_X * Lb_Y, Lb_Y, DCI_Z * Lb_Y }; | |
523 | |||
524 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 10 times.
|
40 | for (int i = 0; i < 3; i++) { |
525 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | E[i] = (0.0 > E[i]) ? 0.0 : pow(E[i], 2.6) * DCI_P / DCI_L; |
526 | 30 | E[i] = E[i] * (Lw[i] - Lb[i]) + Lb[i]; | |
527 | } | ||
528 | 10 | } | |
529 | |||
530 | ✗ | static void eotf_smpte_st428_1_inv(const double Lw_Y, const double Lb_Y, double L[3]) | |
531 | { | ||
532 | ✗ | const double Lw[3] = { DCI_X * Lw_Y, Lw_Y, DCI_Z * Lw_Y }; | |
533 | ✗ | const double Lb[3] = { DCI_X * Lb_Y, Lb_Y, DCI_Z * Lb_Y }; | |
534 | |||
535 | ✗ | for (int i = 0; i < 3; i++) { | |
536 | ✗ | L[i] = (L[i] - Lb[i]) / (Lw[i] - Lb[i]); | |
537 | ✗ | L[i] = (0.0 > L[i]) ? 0.0 : pow(L[i] * DCI_L / DCI_P, 1.0 / 2.6); | |
538 | } | ||
539 | ✗ | } | |
540 | |||
541 | 260 | static void eotf_arib_std_b67(const double Lw, const double Lb, double E[3]) | |
542 | { | ||
543 | 260 | const double gamma = fmax(1.2 + 0.42 * log10(Lw / 1000.0), 1.0); | |
544 | |||
545 | /** | ||
546 | * Note: This equation is technically only accurate if the contrast ratio | ||
547 | * Lw:Lb is greater than 12:1; otherwise we would need to use a different, | ||
548 | * significantly more complicated solution. Ignore this as a highly | ||
549 | * degenerate case, since any real world reference display will have a | ||
550 | * static contrast ratio multiple orders of magnitude higher. | ||
551 | */ | ||
552 | 260 | const double beta = sqrt(3 * pow(Lb / Lw, 1.0 / gamma)); | |
553 | double luma; | ||
554 | |||
555 |
2/2✓ Branch 0 taken 780 times.
✓ Branch 1 taken 260 times.
|
1040 | for (int i = 0; i < 3; i++) |
556 | 780 | E[i] = trc_arib_std_b67_inv((1 - beta) * E[i] + beta); | |
557 | |||
558 | 260 | luma = 0.2627 * E[0] + 0.6780 * E[1] + 0.0593 * E[2]; | |
559 | 260 | luma = pow(fmax(luma, 0.0), gamma - 1.0); | |
560 |
2/2✓ Branch 0 taken 780 times.
✓ Branch 1 taken 260 times.
|
1040 | for (int i = 0; i < 3; i++) |
561 | 780 | E[i] *= Lw * luma; | |
562 | 260 | } | |
563 | |||
564 | 260 | static void eotf_arib_std_b67_inv(const double Lw, const double Lb, double L[3]) | |
565 | { | ||
566 | 260 | const double gamma = fmax(1.2 + 0.42 * log10(Lw / 1000.0), 1.0); | |
567 | 260 | const double beta = sqrt(3 * pow(Lb / Lw, 1 / gamma)); | |
568 | 260 | double luma = 0.2627 * L[0] + 0.6780 * L[1] + 0.0593 * L[2]; | |
569 | |||
570 |
2/2✓ Branch 0 taken 250 times.
✓ Branch 1 taken 10 times.
|
260 | if (luma > 0.0) { |
571 | 250 | luma = pow(luma / Lw, (1 - gamma) / gamma); | |
572 |
2/2✓ Branch 0 taken 750 times.
✓ Branch 1 taken 250 times.
|
1000 | for (int i = 0; i < 3; i++) |
573 | 750 | L[i] *= luma / Lw; | |
574 | } else { | ||
575 | 10 | L[0] = L[1] = L[2] = 0.0; | |
576 | } | ||
577 | |||
578 |
2/2✓ Branch 0 taken 780 times.
✓ Branch 1 taken 260 times.
|
1040 | for (int i = 0; i < 3; i++) |
579 | 780 | L[i] = (trc_arib_std_b67(L[i]) - beta) / (1 - beta); | |
580 | 260 | } | |
581 | |||
582 | static const av_csp_eotf_function eotf_funcs[AVCOL_TRC_NB] = { | ||
583 | [AVCOL_TRC_BT709] = eotf_bt1886, | ||
584 | [AVCOL_TRC_GAMMA22] = eotf_gamma22, | ||
585 | [AVCOL_TRC_GAMMA28] = eotf_gamma28, | ||
586 | [AVCOL_TRC_SMPTE170M] = eotf_bt1886, | ||
587 | [AVCOL_TRC_SMPTE240M] = eotf_bt1886, | ||
588 | [AVCOL_TRC_LINEAR] = eotf_linear, | ||
589 | /* There is no EOTF associated with these logarithmic encodings, since they | ||
590 | * are defined purely for transmission of scene referred data. */ | ||
591 | [AVCOL_TRC_LOG] = NULL, | ||
592 | [AVCOL_TRC_LOG_SQRT] = NULL, | ||
593 | /* BT.1886 is already defined for values below 0.0, as far as physically | ||
594 | * meaningful, so we can directly use it for extended range encodings */ | ||
595 | [AVCOL_TRC_IEC61966_2_4] = eotf_bt1886, | ||
596 | [AVCOL_TRC_BT1361_ECG] = eotf_bt1886, | ||
597 | [AVCOL_TRC_IEC61966_2_1] = eotf_iec61966_2_1, | ||
598 | [AVCOL_TRC_BT2020_10] = eotf_bt1886, | ||
599 | [AVCOL_TRC_BT2020_12] = eotf_bt1886, | ||
600 | [AVCOL_TRC_SMPTE2084] = eotf_smpte_st2084, | ||
601 | [AVCOL_TRC_SMPTE428] = eotf_smpte_st428_1, | ||
602 | [AVCOL_TRC_ARIB_STD_B67] = eotf_arib_std_b67, | ||
603 | }; | ||
604 | |||
605 | 84300 | av_csp_eotf_function av_csp_itu_eotf(enum AVColorTransferCharacteristic trc) | |
606 | { | ||
607 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84300 times.
|
84300 | if (trc < 0 || trc >= AVCOL_TRC_NB) |
608 | ✗ | return NULL; | |
609 | 84300 | return eotf_funcs[trc]; | |
610 | } | ||
611 | |||
612 | static const av_csp_eotf_function eotf_inv_funcs[AVCOL_TRC_NB] = { | ||
613 | [AVCOL_TRC_BT709] = eotf_bt1886_inv, | ||
614 | [AVCOL_TRC_GAMMA22] = eotf_gamma22_inv, | ||
615 | [AVCOL_TRC_GAMMA28] = eotf_gamma28_inv, | ||
616 | [AVCOL_TRC_SMPTE170M] = eotf_bt1886_inv, | ||
617 | [AVCOL_TRC_SMPTE240M] = eotf_bt1886_inv, | ||
618 | [AVCOL_TRC_LINEAR] = eotf_linear_inv, | ||
619 | [AVCOL_TRC_LOG] = NULL, | ||
620 | [AVCOL_TRC_LOG_SQRT] = NULL, | ||
621 | [AVCOL_TRC_IEC61966_2_4] = eotf_bt1886_inv, | ||
622 | [AVCOL_TRC_BT1361_ECG] = eotf_bt1886_inv, | ||
623 | [AVCOL_TRC_IEC61966_2_1] = eotf_iec61966_2_1_inv, | ||
624 | [AVCOL_TRC_BT2020_10] = eotf_bt1886_inv, | ||
625 | [AVCOL_TRC_BT2020_12] = eotf_bt1886_inv, | ||
626 | [AVCOL_TRC_SMPTE2084] = eotf_smpte_st2084_inv, | ||
627 | [AVCOL_TRC_SMPTE428] = eotf_smpte_st428_1_inv, | ||
628 | [AVCOL_TRC_ARIB_STD_B67] = eotf_arib_std_b67_inv, | ||
629 | }; | ||
630 | |||
631 | 84300 | av_csp_eotf_function av_csp_itu_eotf_inv(enum AVColorTransferCharacteristic trc) | |
632 | { | ||
633 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84300 times.
|
84300 | if (trc < 0 || trc >= AVCOL_TRC_NB) |
634 | ✗ | return NULL; | |
635 | 84300 | return eotf_inv_funcs[trc]; | |
636 | } | ||
637 |