2#include <Video/VideoInterface.hpp>
3#include <Gfx/Graph/decoders/Tonemap.hpp>
34#define SCORE_GFX_RGB_MATRIX \
45#define SCORE_GFX_BT601_LIMITED_MATRIX \
47 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
48 0.000000000000000, -0.391762290094914, 2.017232142857143, 0.0,\n\
49 1.596026785714286, -0.812967647237771, 0.000000000000000, 0.0,\n\
50 -0.874202217873451, 0.531667823499146, -1.085630789302022, 1.0)\n"
52#define SCORE_GFX_BT601_FULL_MATRIX \
54 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
55 0.000000000000000, -0.344136286201022, 1.772000000000000, 0.0,\n\
56 1.402000000000000, -0.714136286201022, 0.000000000000000, 0.0,\n\
57 -0.701000000000000, 0.529136286201022, -0.886000000000000, 1.0)\n"
60#define SCORE_GFX_BT601_MATRIX SCORE_GFX_BT601_LIMITED_MATRIX
66#define SCORE_GFX_BT709_LIMITED_MATRIX \
68 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
69 0.000000000000000, -0.213248614273730, 2.112401785714286, 0.0,\n\
70 1.792741071428571, -0.532909328559444, 0.000000000000000, 0.0,\n\
71 -0.972945075016308, 0.301482665475862, -1.133402217873451, 1.0)\n"
73#define SCORE_GFX_BT709_FULL_MATRIX \
75 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
76 0.000000000000000, -0.187324272930649, 1.855600000000000, 0.0,\n\
77 1.574800000000000, -0.468124272930649, 0.000000000000000, 0.0,\n\
78 -0.787400000000000, 0.327724272930649, -0.927800000000000, 1.0)\n"
81#define SCORE_GFX_BT709_MATRIX SCORE_GFX_BT709_LIMITED_MATRIX
87#define SCORE_GFX_SMPTE240M_LIMITED_MATRIX \
89 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
90 0.000000000000000, -0.256532845251675, 2.079843750000000, 0.0,\n\
91 1.793651785714286, -0.542724809537390, 0.000000000000000, 0.0,\n\
92 -0.973402217873451, 0.328136638536074, -1.117059360730594, 1.0)\n"
94#define SCORE_GFX_SMPTE240M_FULL_MATRIX \
96 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
97 0.000000000000000, -0.225346499358335, 1.827000000000000, 0.0,\n\
98 1.575600000000000, -0.476746499358335, 0.000000000000000, 0.0,\n\
99 -0.787800000000000, 0.351046499358335, -0.913500000000000, 1.0)\n"
105#define SCORE_GFX_FCC_LIMITED_MATRIX \
107 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
108 0.000000000000000, -0.377792070217918, 2.026339285714286, 0.0,\n\
109 1.593750000000000, -0.810381355932203, 0.000000000000000, 0.0,\n\
110 -0.873059360730594, 0.523357104160448, -1.090202217873451, 1.0)\n"
112#define SCORE_GFX_FCC_FULL_MATRIX \
114 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
115 0.000000000000000, -0.331864406779661, 1.780000000000000, 0.0,\n\
116 1.400000000000000, -0.711864406779661, 0.000000000000000, 0.0,\n\
117 -0.700000000000000, 0.521864406779661, -0.890000000000000, 1.0)\n"
125#define SCORE_GFX_YCGCO_LIMITED_MATRIX \
127 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
128 -1.138392857142857, 1.138392857142857, -1.138392857142857, 0.0,\n\
129 1.138392857142857, 0.000000000000000, -1.138392857142857, 0.0,\n\
130 -0.073059360730594, -0.644487932159165, 1.069797782126549, 1.0)\n"
132#define SCORE_GFX_YCGCO_FULL_MATRIX \
134 1.0, 1.0, 1.0, 0.0,\n\
135 -1.0, 1.0, -1.0, 0.0,\n\
136 1.0, 0.0, -1.0, 0.0,\n\
137 0.0, -0.5, 0.5, 1.0)\n"
145#define SCORE_GFX_BT2020_LIMITED_MATRIX \
147 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
148 0.000000000000000, -0.187326104219343, 2.141772321428571, 0.0,\n\
149 1.678674107142857, -0.650424318505057, 0.000000000000000, 0.0,\n\
150 -0.915687932159165, 0.347458498519301, -1.148145075016308, 1.0)\n"
152#define SCORE_GFX_BT2020_FULL_MATRIX \
154 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
155 0.000000000000000, -0.164553126843658, 1.881400000000000, 0.0,\n\
156 1.474600000000000, -0.571353126843658, 0.000000000000000, 0.0,\n\
157 -0.737300000000000, 0.367953126843658, -0.940700000000000, 1.0)\n"
164#define SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB \
165 "const mat4 conversion_matrix = " SCORE_GFX_BT601_LIMITED_MATRIX ";\n" \
166 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
168#define SCORE_GFX_CONVERT_BT601_FULL_TO_RGB \
169 "const mat4 conversion_matrix = " SCORE_GFX_BT601_FULL_MATRIX ";\n" \
170 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
173#define SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB \
174 "const mat4 conversion_matrix = " SCORE_GFX_BT709_LIMITED_MATRIX ";\n" \
175 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
177#define SCORE_GFX_CONVERT_BT709_FULL_TO_RGB \
178 "const mat4 conversion_matrix = " SCORE_GFX_BT709_FULL_MATRIX ";\n" \
179 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
182#define SCORE_GFX_CONVERT_SMPTE240M_LIMITED_TO_RGB \
183 "const mat4 conversion_matrix = " SCORE_GFX_SMPTE240M_LIMITED_MATRIX ";\n"\
184 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
186#define SCORE_GFX_CONVERT_SMPTE240M_FULL_TO_RGB \
187 "const mat4 conversion_matrix = " SCORE_GFX_SMPTE240M_FULL_MATRIX ";\n" \
188 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
191#define SCORE_GFX_CONVERT_FCC_LIMITED_TO_RGB \
192 "const mat4 conversion_matrix = " SCORE_GFX_FCC_LIMITED_MATRIX ";\n" \
193 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
195#define SCORE_GFX_CONVERT_FCC_FULL_TO_RGB \
196 "const mat4 conversion_matrix = " SCORE_GFX_FCC_FULL_MATRIX ";\n" \
197 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
200#define SCORE_GFX_CONVERT_YCGCO_LIMITED_TO_RGB \
201 "const mat4 conversion_matrix = " SCORE_GFX_YCGCO_LIMITED_MATRIX ";\n" \
202 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
204#define SCORE_GFX_CONVERT_YCGCO_FULL_TO_RGB \
205 "const mat4 conversion_matrix = " SCORE_GFX_YCGCO_FULL_MATRIX ";\n" \
206 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
209#define SCORE_GFX_CONVERT_BT601_TO_RGB SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB
210#define SCORE_GFX_CONVERT_BT709_TO_RGB SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB
218#define SCORE_GFX_BT2020_TO_709_MATRIX \
220 1.660491, -0.587641, -0.072850, 0.000000,\n\
221 -0.124550, 1.132900, -0.008349, 0.000000,\n\
222 -0.018151, -0.100579, 1.118730, 0.000000,\n\
223 0.000000, 0.000000, 0.000000, 1.000000\n\
226#define SCORE_GFX_BT2020_MATRIX SCORE_GFX_BT709_MATRIX
237static constexpr auto BT2020_YUV_MATRIX_LIMITED = R
"_(
238const mat3 uYuvToRgbColorTransform = mat3(
239 1.1689, 1.1689, 1.1689,
240 0.0000, -0.1881, 2.1502,
241 1.6853, -0.6530, 0.0000
243const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
246static constexpr auto BT2020_YUV_MATRIX_FULL = R
"_(
247const mat3 uYuvToRgbColorTransform = mat3(
248 1.0000, 1.0000, 1.0000,
249 0.0000, -0.1646, 1.8814,
250 1.4746, -0.5714, 0.0000
252const vec3 yuvOffset = vec3(0.0, 0.5, 0.5);
257static constexpr auto BT2020_PQ_EOTF = R
"_(
258vec3 applyEotf(vec3 v) {
259 const float m1 = 2610.0 / 16384.0;
260 const float m2 = (2523.0 / 4096.0) * 128.0;
261 const float c1 = 3424.0 / 4096.0;
262 const float c2 = (2413.0 / 4096.0) * 32.0;
263 const float c3 = (2392.0 / 4096.0) * 32.0;
264 vec3 p = pow(clamp(v, 0.0, 1.0), 1.0 / vec3(m2));
265 return pow(max(p - c1, 0.0) / (c2 - c3 * p), 1.0 / vec3(m1));
269static constexpr auto BT2020_HLG_EOTF = R
"_(
270float hlgEotfSingle(float v) {
271 const float a = 0.17883277;
272 const float b = 0.28466892;
273 const float c = 0.55991073;
274 return v <= 0.5 ? v * v / 3.0
275 : (b + exp((v - c) / a)) / 12.0;
277vec3 applyEotf(vec3 v) {
278 return vec3(hlgEotfSingle(v.r), hlgEotfSingle(v.g), hlgEotfSingle(v.b));
282static constexpr auto BT2020_LINEAR_EOTF = R
"_(
283vec3 applyEotf(vec3 v) { return v; }
286static constexpr auto BT2020_GAMMA22_EOTF = R
"_(
287vec3 applyEotf(vec3 v) { return pow(max(v, 0.0), vec3(2.2)); }
292static constexpr auto BT2020_TO_BT709_GAMUT = R
"_(
293const mat3 gamutConvert = mat3(
294 1.6605, -0.1246, -0.0182,
295 -0.5876, 1.1329, -0.1006,
296 -0.0728, -0.0083, 1.1187
302static constexpr auto SRGB_OETF = R
"_(
303vec3 srgbOetf(vec3 c) {
305 vec3 hi = 1.055 * pow(max(c, 0.0), vec3(1.0 / 2.4)) - 0.055;
306 return mix(lo, hi, step(vec3(0.0031308), c));
315static inline void bt2020_appendYuvMatrix(QString& shader,
const Video::ImageFormat& d)
317 if(d.color_range == AVCOL_RANGE_MPEG)
318 shader += BT2020_YUV_MATRIX_LIMITED;
320 shader += BT2020_YUV_MATRIX_FULL;
326 if(d.color_trc == AVCOL_TRC_SMPTE2084)
327 shader += BT2020_PQ_EOTF;
328 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
329 shader += BT2020_HLG_EOTF;
330 else if(d.color_trc == AVCOL_TRC_LINEAR)
331 shader += BT2020_LINEAR_EOTF;
333 shader += BT2020_GAMMA22_EOTF;
339#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 3, 100)
340#if __has_include(<libavutil/mastering_display_metadata.h>)
343 float cll =
static_cast<float>(d.content_light->MaxCLL);
344 if(cll >= 100.0f && cll <= 10000.0f)
348 if(d.mastering_display.has_luminance
349 && d.mastering_display.max_luminance.den > 0)
351 float peak =
static_cast<float>(av_q2d(d.mastering_display.max_luminance));
352 if(peak >= 100.0f && peak <= 10000.0f)
359 if(d.color_trc == AVCOL_TRC_SMPTE2084)
361 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
371 if(d.color_trc == AVCOL_TRC_SMPTE2084)
375 return 10000.0f / bt2020_contentPeakNits(d);
377 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
397 shader.reserve(1024);
399 bt2020_appendYuvMatrix(shader, d);
402vec4 convert_to_rgb(vec4 tex) {
403 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
404 return vec4(rgb, 1.0);
421 shader.reserve(2048);
423 bt2020_appendYuvMatrix(shader, d);
424 bt2020_appendEotf(shader, d);
427vec4 convert_to_rgb(vec4 tex) {
428 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
429 return vec4(applyEotf(rgb), 1.0);
445 shader.reserve(2048);
447 bt2020_appendYuvMatrix(shader, d);
448 bt2020_appendEotf(shader, d);
450 const float normFactor = bt2020_eotfToNormalizedFactor(d);
451 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
454vec4 convert_to_rgb(vec4 tex) {
455 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
456 vec3 linear = applyEotf(rgb);
457 // Normalize: 1.0 = content peak luminance
458 return vec4(linear * eotfNormFactor, 1.0);
486 shader.reserve(8192);
489 bt2020_appendYuvMatrix(shader, d);
492 bt2020_appendEotf(shader, d);
495 shader += BT2020_TO_BT709_GAMUT;
501 const float normFactor = bt2020_eotfToNormalizedFactor(d);
502 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
506 const float contentPeak = bt2020_contentPeakNits(d);
507 const float sdrPeak = 203.0f;
508 shader += tonemapShader(d.tonemap, contentPeak, sdrPeak);
512 const bool lumBased = isLuminanceBasedTonemap(d.tonemap);
518vec4 convert_to_rgb(vec4 tex) {
519 // Step 1: YUV decode → BT.2020 RGB
520 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
522 // Step 2: EOTF → linear light (BT.2020 primaries)
523 vec3 linearBt2020 = applyEotf(rgb);
525 // Step 3: Normalize so 1.0 = content peak
526 linearBt2020 *= eotfNormFactor;
528 // Step 4: Tone map in BT.2020 (luminance-based, gamut-agnostic)
529 vec3 tonemapped = tonemap(linearBt2020);
531 // Step 5: Gamut conversion BT.2020 → BT.709
532 vec3 linearBt709 = gamutConvert * tonemapped;
533 linearBt709 = clamp(linearBt709, 0.0, 1.0);
535 // Step 6: sRGB OETF for display
536 return vec4(srgbOetf(linearBt709), 1.0);
544vec4 convert_to_rgb(vec4 tex) {
545 // Step 1: YUV decode → BT.2020 RGB
546 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
548 // Step 2: EOTF → linear light (BT.2020 primaries)
549 vec3 linearBt2020 = applyEotf(rgb);
551 // Step 3: Normalize so 1.0 = content peak
552 linearBt2020 *= eotfNormFactor;
554 // Step 4: Gamut conversion BT.2020 → BT.709 (BEFORE tonemapping)
555 vec3 linearBt709 = gamutConvert * linearBt2020;
556 // Note: do NOT clamp here — values outside [0,1] represent
557 // BT.2020-exclusive colors. The tonemapper will handle them.
559 // Step 5: Tone map in BT.709 (per-channel / assumes BT.709 input)
560 vec3 tonemapped = tonemap(linearBt709);
562 // Step 6: Clamp and sRGB OETF for display
563 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
577 switch(d.output_format)
579 case Video::OutputFormat::Passthrough:
580 return bt2020shader_passthrough(d);
581 case Video::OutputFormat::Linear:
582 return bt2020shader_linear(d);
583 case Video::OutputFormat::Normalized:
584 return bt2020shader_normalized(d);
585 case Video::OutputFormat::SDR:
587 return bt2020shader_sdr(d);
597 const bool full_range = (d.color_range == AVCOL_RANGE_JPEG);
599 switch(d.color_space)
602 return "vec4 convert_to_rgb(vec4 tex) { return tex; }";
604 case AVCOL_SPC_BT709:
605 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
606 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
609 return full_range ? SCORE_GFX_CONVERT_FCC_FULL_TO_RGB
610 : SCORE_GFX_CONVERT_FCC_LIMITED_TO_RGB;
612 case AVCOL_SPC_BT470BG:
613 case AVCOL_SPC_SMPTE170M:
614 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
615 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
617 case AVCOL_SPC_SMPTE240M:
618 return full_range ? SCORE_GFX_CONVERT_SMPTE240M_FULL_TO_RGB
619 : SCORE_GFX_CONVERT_SMPTE240M_LIMITED_TO_RGB;
621 case AVCOL_SPC_YCGCO:
622 return full_range ? SCORE_GFX_CONVERT_YCGCO_FULL_TO_RGB
623 : SCORE_GFX_CONVERT_YCGCO_LIMITED_TO_RGB;
625 case AVCOL_SPC_BT2020_NCL:
626 case AVCOL_SPC_BT2020_CL:
630 case AVCOL_SPC_SMPTE2085:
631 case AVCOL_SPC_CHROMA_DERIVED_NCL:
632 case AVCOL_SPC_CHROMA_DERIVED_CL:
633 case AVCOL_SPC_ICTCP:
634 return bt2020shader(d);
638 case AVCOL_SPC_UNSPECIFIED:
639 case AVCOL_SPC_RESERVED:
645 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
646 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
648 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
649 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:12