2 #include <Video/VideoInterface.hpp>
86 #define SCORE_GFX_RGB_MATRIX \
93 #define SCORE_GFX_BT601_MATRIX \
95 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
96 0.0 , -0.391762290094914, 2.017232142857142, 0.0,\n\
97 1.596026785714286, -0.812967647237771, 0.0 , 0.0,\n\
98 -0.874202217873451, 0.531667823499146, -1.085630789302022, 1.0)\n"
100 #define SCORE_GFX_BT709_MATRIX \
102 1.164383561643836 , 1.164383561643836 , 1.164383561643836 , 0.0,\n\
103 0.0 , -0.21324861427373 , 2.112401785714286 , 0.0,\n\
104 1.792741071428571 , -0.532909328559444, 0.0 , 0.0,\n\
105 -0.972945075016308, 0.301482665475862 , -1.133402217873451, 1.0)\n"
107 #define SCORE_GFX_BT2020_TO_709_MATRIX \
109 1.660491f, -0.587641f, -0.072850f, 0.000000f,\n\
110 -0.124550f, 1.132900f, -0.008349f, 0.000000f,\n\
111 -0.018151f, -0.100579f, 1.118730f, 0.000000f,\n\
112 0.000000f, 0.000000f, 0.000000f, 1.000000f\n\
115 #define SCORE_GFX_BT2020_MATRIX SCORE_GFX_BT709_MATRIX
116 #define SCORE_GFX_CONVERT_BT601_TO_RGB \
117 "const mat4 conversion_matrix = " SCORE_GFX_BT601_MATRIX \
119 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
121 #define SCORE_GFX_CONVERT_BT709_TO_RGB \
122 "const mat4 conversion_matrix = " SCORE_GFX_BT709_MATRIX \
124 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
132 static constexpr
auto SCORE_GFX_CONVERT_BT2020_TO_RGB_HEADER = R
"_(
133 const int COLOR_TRANSFER_LINEAR = 1;
134 const int COLOR_TRANSFER_GAMMA_2_2 = 10;
135 const int COLOR_TRANSFER_ST2084 = 6;
136 const int COLOR_TRANSFER_HLG = 7;
139 static constexpr
auto SCORE_GFX_CONVERT_BT2020_TO_RGB_HEADER_LIMITED_RANGE = R
"_(
140 const mat3 uYuvToRgbColorTransform = mat3(
141 1.1689f, 1.1689f, 1.1689f,
142 0.0000f, -0.1881f, 2.1502f,
143 1.6853f, -0.6530f, 0.0000f
146 static constexpr
auto SCORE_GFX_CONVERT_BT2020_TO_RGB_HEADER_FULL_RANGE = R
"_(
147 const mat3 uYuvToRgbColorTransform = mat3(
148 1.0000f, 1.0000f, 1.0000f,
149 0.0000f, -0.1646f, 1.8814f,
150 1.4746f, -0.5714f, 0.0000f
153 static constexpr
auto SCORE_GFX_CONVERT_BT2020_TO_RGB = R
"_(
155 const int uApplyHdrToSdrToneMapping = 1;
158 // BT.2100 / BT.2020 HLG EOTF for one channel.
159 float hlgEotfSingleChannel(float hlgChannel) {
161 // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
162 // Reference implementation:
163 // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=265-279;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
164 const float a = 0.17883277;
165 const float b = 0.28466892;
166 const float c = 0.55991073;
167 return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0
168 : (b + exp((hlgChannel - c) / a)) / 12.0;
171 // BT.2100 / BT.2020 HLG EOTF.
172 vec3 hlgEotf(vec3 hlgColor) {
173 return vec3(hlgEotfSingleChannel(hlgColor.r),
174 hlgEotfSingleChannel(hlgColor.g),
175 hlgEotfSingleChannel(hlgColor.b));
178 // BT.2100 / BT.2020 PQ EOTF.
179 vec3 pqEotf(vec3 pqColor) {
181 // https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
182 // Reference implementation:
183 // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=250-263;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
184 const float m1 = (2610.0 / 16384.0);
185 const float m2 = (2523.0 / 4096.0) * 128.0;
186 const float c1 = (3424.0 / 4096.0);
187 const float c2 = (2413.0 / 4096.0) * 32.0;
188 const float c3 = (2392.0 / 4096.0) * 32.0;
190 vec3 temp = pow(clamp(pqColor, 0.0, 1.0), 1.0 / vec3(m2));
191 temp = max(temp - c1, 0.0) / (c2 - c3 * temp);
192 return pow(temp, 1.0 / vec3(m1));
195 // Applies the appropriate EOTF to convert nonlinear electrical values to linear
196 // optical values. Input and output are both normalized to [0, 1].
197 vec3 applyEotf(vec3 electricalColor) {
198 if (uInputColorTransfer == COLOR_TRANSFER_ST2084) {
199 return pqEotf(electricalColor);
200 } else if (uInputColorTransfer == COLOR_TRANSFER_HLG) {
201 return hlgEotf(electricalColor);
203 // Output red as an obviously visible error.
204 return vec3(1.0, 0.0, 0.0);
208 // Apply the HLG BT2020 to BT709 OOTF.
209 vec3 applyHlgBt2020ToBt709Ootf(vec3 linearRgbBt2020) {
210 // Reference ("HLG Reference OOTF" section):
211 // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf
212 // Matrix values based on computeXYZMatrix(BT2020Primaries, BT2020WhitePoint)
213 // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/utils/HostColorSpace.cpp;l=200-232;drc=86bd214059cd6150304888a285941bf74af5b687
214 const mat3 RGB_TO_XYZ_BT2020 =
215 mat3(0.63695805f, 0.26270021f, 0.00000000f, 0.14461690f, 0.67799807f,
216 0.02807269f, 0.16888098f, 0.05930172f, 1.06098506f);
217 // Matrix values based on computeXYZMatrix(BT709Primaries, BT709WhitePoint)
218 const mat3 XYZ_TO_RGB_BT709 =
219 mat3(3.24096994f, -0.96924364f, 0.05563008f, -1.53738318f, 1.87596750f,
220 -0.20397696f, -0.49861076f, 0.04155506f, 1.05697151f);
221 // hlgGamma is 1.2 + 0.42 * log10(nominalPeakLuminance/1000);
222 // nominalPeakLuminance was selected to use a 500 as a typical value, used
224 // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62,
225 // b/199162498#comment35, and
226 // https://www.microsoft.com/applied-sciences/uploads/projects/investigation-of-hdr-vs-tone-mapped-sdr/investigation-of-hdr-vs-tone-mapped-sdr.pdf.
227 const float hlgGamma = 1.0735674018211279;
229 vec3 linearXyzBt2020 = RGB_TO_XYZ_BT2020 * linearRgbBt2020;
230 vec3 linearXyzBt709 =
231 linearXyzBt2020 * pow(linearXyzBt2020[1], hlgGamma - 1.0);
232 vec3 linearRgbBt709 = clamp((XYZ_TO_RGB_BT709 * linearXyzBt709), 0.0, 1.0);
233 return linearRgbBt709;
236 // Apply the PQ BT2020 to BT709 OOTF.
237 vec3 applyPqBt2020ToBt709Ootf(vec3 linearRgbBt2020) {
238 float pqPeakLuminance = 10000.0;
239 float sdrPeakLuminance = 500.0;
241 return linearRgbBt2020 * pqPeakLuminance / sdrPeakLuminance;
244 vec3 applyBt2020ToBt709Ootf(vec3 linearRgbBt2020) {
245 if (uInputColorTransfer == COLOR_TRANSFER_ST2084) {
246 return applyPqBt2020ToBt709Ootf(linearRgbBt2020);
247 } else if (uInputColorTransfer == COLOR_TRANSFER_HLG) {
248 return applyHlgBt2020ToBt709Ootf(linearRgbBt2020);
250 // Output green as an obviously visible error.
251 return vec3(0.0, 1.0, 0.0);
255 // BT.2100 / BT.2020 HLG OETF for one channel.
256 float hlgOetfSingleChannel(float linearChannel) {
258 // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
259 // Reference implementation:
260 // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=529-543;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
261 const float a = 0.17883277;
262 const float b = 0.28466892;
263 const float c = 0.55991073;
265 return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel)
266 : a * log(12.0 * linearChannel - b) + c;
269 // BT.2100 / BT.2020 HLG OETF.
270 vec3 hlgOetf(vec3 linearColor) {
271 return vec3(hlgOetfSingleChannel(linearColor.r),
272 hlgOetfSingleChannel(linearColor.g),
273 hlgOetfSingleChannel(linearColor.b));
276 // BT.2100 / BT.2020, PQ / ST2084 OETF.
277 vec3 pqOetf(vec3 linearColor) {
279 // https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
280 // Reference implementation:
281 // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=514-527;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
282 const float m1 = (2610.0 / 16384.0);
283 const float m2 = (2523.0 / 4096.0) * 128.0;
284 const float c1 = (3424.0 / 4096.0);
285 const float c2 = (2413.0 / 4096.0) * 32.0;
286 const float c3 = (2392.0 / 4096.0) * 32.0;
288 vec3 temp = pow(linearColor, vec3(m1));
289 temp = (c1 + c2 * temp) / (1.0 + c3 * temp);
290 return pow(temp, vec3(m2));
293 // BT.709 gamma 2.2 OETF for one channel.
294 float gamma22OetfSingleChannel(float linearChannel) {
296 // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2
297 return pow(linearChannel, (1.0 / 2.2));
300 // BT.709 gamma 2.2 OETF.
301 vec3 gamma22Oetf(vec3 linearColor) {
302 return vec3(gamma22OetfSingleChannel(linearColor.r),
303 gamma22OetfSingleChannel(linearColor.g),
304 gamma22OetfSingleChannel(linearColor.b));
307 // Applies the appropriate OETF to convert linear optical signals to nonlinear
308 // electrical signals. Input and output are both normalized to [0, 1].
309 vec3 applyOetf(vec3 linearColor) {
310 if (uOutputColorTransfer == COLOR_TRANSFER_ST2084) {
311 return pqOetf(linearColor);
312 } else if (uOutputColorTransfer == COLOR_TRANSFER_HLG) {
313 return hlgOetf(linearColor);
314 } else if (uOutputColorTransfer == COLOR_TRANSFER_GAMMA_2_2) {
315 return gamma22Oetf(linearColor);
316 } else if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR) {
319 // Output blue as an obviously visible error.
320 return vec3(0.0, 0.0, 1.0);
324 vec3 yuvToRgb(vec3 yuv) {
325 const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
326 return clamp(uYuvToRgbColorTransform * (yuv - yuvOffset), 0.0, 1.0);
329 vec4 convert_to_rgb(vec4 tex) {
330 vec3 srcYuv = tex.xyz;
331 vec3 opticalColorBt2020 = applyEotf(yuvToRgb(srcYuv));
333 (uApplyHdrToSdrToneMapping == 1)
334 ? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0)
335 : vec4(opticalColorBt2020, 1.0);
336 vec4 transformedColors = opticalColor;
337 return vec4(applyOetf(transformedColors.rgb), 1.0);
343 shader.reserve(8000);
345 shader += SCORE_GFX_CONVERT_BT2020_TO_RGB_HEADER;
346 if(d.color_range == AVCOL_RANGE_MPEG)
347 shader += SCORE_GFX_CONVERT_BT2020_TO_RGB_HEADER_LIMITED_RANGE;
349 shader += SCORE_GFX_CONVERT_BT2020_TO_RGB_HEADER_FULL_RANGE;
351 if(d.color_trc == AVCOL_TRC_SMPTE2084)
352 shader +=
"const int uInputColorTransfer = COLOR_TRANSFER_ST2084; \n";
353 else if(d.color_trc == AVCOL_TRC_GAMMA22)
354 shader +=
"const int uInputColorTransfer = COLOR_TRANSFER_GAMMA_2_2; \n";
355 else if(d.color_trc == AVCOL_TRC_LINEAR)
356 shader +=
"const int uInputColorTransfer = COLOR_TRANSFER_LINEAR; \n";
357 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
358 shader +=
"const int uInputColorTransfer = COLOR_TRANSFER_HLG; \n";
360 shader +=
"const int uInputColorTransfer = COLOR_TRANSFER_GAMMA_2_2; \n";
362 shader +=
"const int uOutputColorTransfer = COLOR_TRANSFER_GAMMA_2_2; \n";
364 shader += SCORE_GFX_CONVERT_BT2020_TO_RGB;
377 switch(d.color_space)
380 return "vec4 convert_to_rgb(vec4 tex) { return tex; }";
381 case AVCOL_SPC_BT709:
382 return SCORE_GFX_CONVERT_BT709_TO_RGB;
384 return SCORE_GFX_CONVERT_BT601_TO_RGB;
385 case AVCOL_SPC_BT470BG:
386 return SCORE_GFX_CONVERT_BT601_TO_RGB;
387 case AVCOL_SPC_SMPTE170M:
388 return SCORE_GFX_CONVERT_BT601_TO_RGB;
389 case AVCOL_SPC_SMPTE240M:
391 return SCORE_GFX_CONVERT_BT709_TO_RGB;
392 case AVCOL_SPC_YCGCO:
393 return SCORE_GFX_CONVERT_BT709_TO_RGB;
394 case AVCOL_SPC_BT2020_NCL:
395 return bt2020shader(d);
396 case AVCOL_SPC_BT2020_CL:
397 return bt2020shader(d);
398 case AVCOL_SPC_SMPTE2085:
399 return bt2020shader(d);
400 case AVCOL_SPC_CHROMA_DERIVED_NCL:
401 return bt2020shader(d);
402 case AVCOL_SPC_CHROMA_DERIVED_CL:
403 return bt2020shader(d);
404 case AVCOL_SPC_ICTCP:
405 return bt2020shader(d);
409 case AVCOL_SPC_UNSPECIFIED:
410 case AVCOL_SPC_RESERVED:
413 return SCORE_GFX_BT709_MATRIX;
Graphics rendering pipeline for ossia score.
Definition: Filter/PreviewWidget.hpp:12