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
240static constexpr auto BT2020_YUV_MATRIX_LIMITED = R
"_(
241const mat3 uYuvToRgbColorTransform = mat3(
242 1.1689, 1.1689, 1.1689,
243 0.0000, -0.1881, 2.1502,
244 1.6853, -0.6530, 0.0000
246const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
249static constexpr auto BT2020_YUV_MATRIX_FULL = R
"_(
250const mat3 uYuvToRgbColorTransform = mat3(
251 1.0000, 1.0000, 1.0000,
252 0.0000, -0.1646, 1.8814,
253 1.4746, -0.5714, 0.0000
255const vec3 yuvOffset = vec3(0.0, 0.5, 0.5);
260static constexpr auto BT2020_PQ_EOTF = R
"_(
261vec3 applyEotf(vec3 v) {
262 const float m1 = 2610.0 / 16384.0;
263 const float m2 = (2523.0 / 4096.0) * 128.0;
264 const float c1 = 3424.0 / 4096.0;
265 const float c2 = (2413.0 / 4096.0) * 32.0;
266 const float c3 = (2392.0 / 4096.0) * 32.0;
267 vec3 p = pow(clamp(v, 0.0, 1.0), 1.0 / vec3(m2));
268 return pow(max(p - c1, 0.0) / (c2 - c3 * p), 1.0 / vec3(m1));
272static constexpr auto BT2020_HLG_EOTF = R
"_(
273float hlgEotfSingle(float v) {
274 const float a = 0.17883277;
275 const float b = 0.28466892;
276 const float c = 0.55991073;
277 return v <= 0.5 ? v * v / 3.0
278 : (b + exp((v - c) / a)) / 12.0;
280vec3 applyEotf(vec3 v) {
281 return vec3(hlgEotfSingle(v.r), hlgEotfSingle(v.g), hlgEotfSingle(v.b));
285static constexpr auto BT2020_LINEAR_EOTF = R
"_(
286vec3 applyEotf(vec3 v) { return v; }
289static constexpr auto BT2020_GAMMA22_EOTF = R
"_(
290vec3 applyEotf(vec3 v) { return pow(max(v, 0.0), vec3(2.2)); }
308static constexpr auto BT2020_HLG_OOTF = R
"_(
309vec3 applyHlgOotf(vec3 scene, float Lw) {
310 const vec3 hlgLuma = vec3(0.2627, 0.6780, 0.0593);
311 float gamma = 1.2 + 0.42 * log(Lw / 1000.0) / log(10.0);
312 float Ys = dot(hlgLuma, scene);
313 // Guard against Ys=0 (black) which would cause pow(0, negative) = inf
314 if (Ys <= 0.0) return vec3(0.0);
315 return Lw * pow(Ys, gamma - 1.0) * scene;
321static constexpr auto BT2020_TO_BT709_GAMUT = R
"_(
322const mat3 gamutConvert = mat3(
323 1.6605, -0.1246, -0.0182,
324 -0.5876, 1.1329, -0.1006,
325 -0.0728, -0.0083, 1.1187
331static constexpr auto SRGB_OETF = R
"_(
332vec3 srgbOetf(vec3 c) {
334 vec3 hi = 1.055 * pow(max(c, 0.0), vec3(1.0 / 2.4)) - 0.055;
335 return mix(lo, hi, step(vec3(0.0031308), c));
364static constexpr auto ICTCP_PQ_TO_LMS = R
"_(
365const mat3 ictcpToLms = mat3(
366 1.000000000000000, 1.000000000000000, 1.000000000000000,
367 0.008609037037933, -0.008609037037933, 0.560031335710679,
368 0.111029625003026, -0.111029625003026, -0.320627174987319
373static constexpr auto ICTCP_HLG_TO_LMS = R
"_(
374const mat3 ictcpToLms = mat3(
375 1.000000000000000, 1.000000000000000, 1.000000000000000,
376 0.015718580108730, -0.015718580108730, 1.021271079842234,
377 0.209581068116406, -0.209581068116406, -0.605274490992431
382static constexpr auto LMS_TO_BT2020_RGB = R
"_(
383const mat3 lmsToBt2020 = mat3(
384 3.436606694333078, -0.791329555598929, -0.025949899690593,
385 -2.506452118656270, 1.983600451792291, -0.098913714711726,
386 0.069845424323191, -0.192270896193362, 1.124863614402319
407static constexpr auto SMPTE2085_YUV_MATRIX_LIMITED = R
"_(
408// SMPTE 2085: Y'D'zD'x -> R'G'B' inverse matrix
409// Range scaling (255/219 for Y, 255/224 for D'z/D'x) baked in.
410const mat3 uYuvToRgbColorTransform = mat3(
411 1.173889720601265, 1.164383561643836, 1.180238890904243,
412 0.000000000000000, 0.000000000000000, 2.307788545607404,
413 2.295373650104259, 0.000000000000000, 0.000000000000000
415const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
418static constexpr auto SMPTE2085_YUV_MATRIX_FULL = R
"_(
419const mat3 uYuvToRgbColorTransform = mat3(
420 1.008164112986969, 1.000000000000000, 1.013616929835409,
421 0.000000000000000, 0.000000000000000, 2.027233859670817,
422 2.016328225973937, 0.000000000000000, 0.000000000000000
424const vec3 yuvOffset = vec3(0.0, 0.5, 0.5);
440static constexpr auto DISPLAY_P3_TO_BT2020_GAMUT = R
"_(
441const mat3 gamutConvert = mat3(
442 0.753833034361722, 0.045743848965358, -0.001210340354518,
443 0.198597369052617, 0.941777219811693, 0.017601717301090,
444 0.047569596585662, 0.012478931222948, 0.983608623053428
448static constexpr auto DISPLAY_P3_TO_BT709_GAMUT = R
"_(
449const mat3 gamutConvert = mat3(
450 1.224940176280561, -0.042056954709688, -0.019637554590334,
451 -0.224940176280560, 1.042056954709688, -0.078636045550632,
452 0.000000000000000, 0.000000000000000, 1.098273600140966
463 if(d.tonemap == ::Video::Tonemap::Auto)
464 return resolveAutoTonemap(
static_cast<int>(d.color_trc));
469static inline void bt2020_appendYuvMatrix(QString& shader,
const Video::ImageFormat& d)
471 if(d.color_range == AVCOL_RANGE_MPEG)
472 shader += BT2020_YUV_MATRIX_LIMITED;
474 shader += BT2020_YUV_MATRIX_FULL;
480 if(d.color_trc == AVCOL_TRC_SMPTE2084)
481 shader += BT2020_PQ_EOTF;
482 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
483 shader += BT2020_HLG_EOTF;
484 else if(d.color_trc == AVCOL_TRC_LINEAR)
485 shader += BT2020_LINEAR_EOTF;
487 shader += BT2020_GAMMA22_EOTF;
493#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 3, 100)
494#if __has_include(<libavutil/mastering_display_metadata.h>)
497 float cll =
static_cast<float>(d.content_light->MaxCLL);
498 if(cll >= 100.0f && cll <= 10000.0f)
502 if(d.mastering_display.has_luminance
503 && d.mastering_display.max_luminance.den > 0)
505 float peak =
static_cast<float>(av_q2d(d.mastering_display.max_luminance));
506 if(peak >= 100.0f && peak <= 10000.0f)
513 if(d.color_trc == AVCOL_TRC_SMPTE2084)
515 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
530 if(d.color_trc == AVCOL_TRC_SMPTE2084)
534 return 10000.0f / bt2020_contentPeakNits(d);
536 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
567 shader.reserve(1024);
569 bt2020_appendYuvMatrix(shader, d);
572vec4 convert_to_rgb(vec4 tex) {
573 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
574 return vec4(rgb, 1.0);
591 shader.reserve(2048);
593 bt2020_appendYuvMatrix(shader, d);
594 bt2020_appendEotf(shader, d);
597vec4 convert_to_rgb(vec4 tex) {
598 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
599 return vec4(applyEotf(rgb), 1.0);
615 shader.reserve(2048);
617 bt2020_appendYuvMatrix(shader, d);
618 bt2020_appendEotf(shader, d);
620 const float normFactor = bt2020_eotfToNormalizedFactor(d);
621 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
624vec4 convert_to_rgb(vec4 tex) {
625 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
626 vec3 linear = applyEotf(rgb);
627 // Normalize: 1.0 = content peak luminance
628 return vec4(linear * eotfNormFactor, 1.0);
663 shader.reserve(8192);
665 const bool isHLG = (d.color_trc == AVCOL_TRC_ARIB_STD_B67);
668 bt2020_appendYuvMatrix(shader, d);
671 bt2020_appendEotf(shader, d);
675 shader += BT2020_HLG_OOTF;
678 shader += BT2020_TO_BT709_GAMUT;
693 const float hlgDisplayPeak = bt2020_hlgDisplayPeakNits(d);
694 contentPeak = hlgDisplayPeak;
696 normFactor = 1.0f / contentPeak;
697 shader += QString(
"const float hlgDisplayLw = %1;\n").arg(hlgDisplayPeak, 0,
'f', 1);
701 contentPeak = bt2020_contentPeakNits(d);
702 normFactor = bt2020_eotfToNormalizedFactor(d);
704 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 6);
707 const float sdrPeak = 203.0f;
708 const auto effectiveTonemap = resolvedTonemap(d);
709 shader += tonemapShader(effectiveTonemap, contentPeak, sdrPeak);
712 const bool lumBased = isLuminanceBasedTonemap(effectiveTonemap);
720vec4 convert_to_rgb(vec4 tex) {
721 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
722 vec3 sceneLinear = applyEotf(rgb);
724 // OOTF: scene-linear -> display-linear (nits)
725 vec3 displayLinear = applyHlgOotf(sceneLinear, hlgDisplayLw);
727 // Normalize: 1.0 = content peak
728 displayLinear *= eotfNormFactor;
730 vec3 tonemapped = tonemap(displayLinear);
731 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
732 return vec4(srgbOetf(linearBt709), 1.0);
739vec4 convert_to_rgb(vec4 tex) {
740 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
741 vec3 sceneLinear = applyEotf(rgb);
742 vec3 displayLinear = applyHlgOotf(sceneLinear, hlgDisplayLw);
743 displayLinear *= eotfNormFactor;
745 vec3 linearBt709 = gamutConvert * displayLinear;
746 vec3 tonemapped = tonemap(linearBt709);
747 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
758vec4 convert_to_rgb(vec4 tex) {
759 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
760 vec3 linearBt2020 = applyEotf(rgb);
761 linearBt2020 *= eotfNormFactor;
763 vec3 tonemapped = tonemap(linearBt2020);
764 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
765 return vec4(srgbOetf(linearBt709), 1.0);
772vec4 convert_to_rgb(vec4 tex) {
773 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
774 vec3 linearBt2020 = applyEotf(rgb);
775 linearBt2020 *= eotfNormFactor;
777 vec3 linearBt709 = gamutConvert * linearBt2020;
778 vec3 tonemapped = tonemap(linearBt709);
779 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
794 switch(d.output_format)
796 case Video::OutputFormat::Passthrough:
797 return bt2020shader_passthrough(d);
798 case Video::OutputFormat::Linear:
799 return bt2020shader_linear(d);
800 case Video::OutputFormat::Normalized:
801 return bt2020shader_normalized(d);
802 case Video::OutputFormat::SDR:
804 return bt2020shader_sdr(d);
820static inline void ictcp_appendInverseMatrix(QString& shader,
const Video::ImageFormat& d)
822 if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
823 shader += ICTCP_HLG_TO_LMS;
825 shader += ICTCP_PQ_TO_LMS;
832 shader.reserve(4096);
834 ictcp_appendInverseMatrix(shader, d);
835 shader += LMS_TO_BT2020_RGB;
836 bt2020_appendEotf(shader, d);
841 if(d.color_range == AVCOL_RANGE_MPEG)
844const float yScale = 255.0 / 219.0;
845const float yOffset = 16.0 / 255.0;
846const float cScale = 255.0 / 224.0;
847const float cOffset = 128.0 / 255.0;
848vec4 convert_to_rgb(vec4 tex) {
849 // Step 1: Unpack limited range ICtCp
850 float I = (tex.x - yOffset) * yScale;
851 float Ct = (tex.y - cOffset) * cScale;
852 float Cp = (tex.z - cOffset) * cScale;
854 // Step 2: ICtCp -> transfer-encoded LMS
855 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
857 // Step 3: EOTF -> linear LMS
858 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
860 // Step 4: Linear LMS -> linear BT.2020 RGB
861 return vec4(lmsToBt2020 * lmsLinear, 1.0);
868vec4 convert_to_rgb(vec4 tex) {
870 float Ct = tex.y - 0.5;
871 float Cp = tex.z - 0.5;
873 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
874 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
875 return vec4(lmsToBt2020 * lmsLinear, 1.0);
887 shader.reserve(4096);
889 ictcp_appendInverseMatrix(shader, d);
890 shader += LMS_TO_BT2020_RGB;
891 bt2020_appendEotf(shader, d);
893 const float normFactor = bt2020_eotfToNormalizedFactor(d);
894 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
896 if(d.color_range == AVCOL_RANGE_MPEG)
899const float yScale = 255.0 / 219.0;
900const float yOffset = 16.0 / 255.0;
901const float cScale = 255.0 / 224.0;
902const float cOffset = 128.0 / 255.0;
903vec4 convert_to_rgb(vec4 tex) {
904 float I = (tex.x - yOffset) * yScale;
905 float Ct = (tex.y - cOffset) * cScale;
906 float Cp = (tex.z - cOffset) * cScale;
907 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
908 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
909 vec3 bt2020 = lmsToBt2020 * lmsLinear;
910 return vec4(bt2020 * eotfNormFactor, 1.0);
917vec4 convert_to_rgb(vec4 tex) {
919 float Ct = tex.y - 0.5;
920 float Cp = tex.z - 0.5;
921 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
922 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
923 vec3 bt2020 = lmsToBt2020 * lmsLinear;
924 return vec4(bt2020 * eotfNormFactor, 1.0);
936 shader.reserve(8192);
938 ictcp_appendInverseMatrix(shader, d);
939 shader += LMS_TO_BT2020_RGB;
940 bt2020_appendEotf(shader, d);
941 shader += BT2020_TO_BT709_GAMUT;
944 const float normFactor = bt2020_eotfToNormalizedFactor(d);
945 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
947 const float contentPeak = bt2020_contentPeakNits(d);
948 const float sdrPeak = 203.0f;
949 const auto effectiveTonemap = resolvedTonemap(d);
950 shader += tonemapShader(effectiveTonemap, contentPeak, sdrPeak);
952 const bool lumBased = isLuminanceBasedTonemap(effectiveTonemap);
953 const char* rangeUnpack;
954 if(d.color_range == AVCOL_RANGE_MPEG)
957const float yScale = 255.0 / 219.0;
958const float yOffset = 16.0 / 255.0;
959const float cScale = 255.0 / 224.0;
960const float cOffset = 128.0 / 255.0;
963 float I = (tex.x - yOffset) * yScale;
964 float Ct = (tex.y - cOffset) * cScale;
965 float Cp = (tex.z - cOffset) * cScale;
972 float Ct = tex.y - 0.5;
973 float Cp = tex.z - 0.5;
979 shader += QString(R
"_(
980vec4 convert_to_rgb(vec4 tex) {
982 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
983 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
984 vec3 linearBt2020 = lmsToBt2020 * lmsLinear;
985 linearBt2020 *= eotfNormFactor;
986 vec3 tonemapped = tonemap(linearBt2020);
987 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
988 return vec4(srgbOetf(linearBt709), 1.0);
990)_").arg(rangeUnpack);
994 shader += QString(R
"_(
995vec4 convert_to_rgb(vec4 tex) {
997 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
998 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
999 vec3 linearBt2020 = lmsToBt2020 * lmsLinear;
1000 linearBt2020 *= eotfNormFactor;
1001 vec3 linearBt709 = gamutConvert * linearBt2020;
1002 vec3 tonemapped = tonemap(linearBt709);
1003 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
1005)_").arg(rangeUnpack);
1015 shader.reserve(2048);
1017 ictcp_appendInverseMatrix(shader, d);
1019 if(d.color_range == AVCOL_RANGE_MPEG)
1022const float yScale = 255.0 / 219.0;
1023const float yOffset = 16.0 / 255.0;
1024const float cScale = 255.0 / 224.0;
1025const float cOffset = 128.0 / 255.0;
1026vec4 convert_to_rgb(vec4 tex) {
1027 float I = (tex.x - yOffset) * yScale;
1028 float Ct = (tex.y - cOffset) * cScale;
1029 float Cp = (tex.z - cOffset) * cScale;
1030 // Decode to PQ-encoded LMS, output as-is (no EOTF, no LMS->RGB)
1031 vec3 lmsPQ = clamp(ictcpToLms * vec3(I, Ct, Cp), 0.0, 1.0);
1032 return vec4(lmsPQ, 1.0);
1039vec4 convert_to_rgb(vec4 tex) {
1041 float Ct = tex.y - 0.5;
1042 float Cp = tex.z - 0.5;
1043 vec3 lmsPQ = clamp(ictcpToLms * vec3(I, Ct, Cp), 0.0, 1.0);
1044 return vec4(lmsPQ, 1.0);
1054 switch(d.output_format)
1056 case Video::OutputFormat::Passthrough:
1057 return ictcpshader_passthrough(d);
1058 case Video::OutputFormat::Linear:
1059 return ictcpshader_linear(d);
1060 case Video::OutputFormat::Normalized:
1061 return ictcpshader_normalized(d);
1062 case Video::OutputFormat::SDR:
1064 return ictcpshader_sdr(d);
1076static inline void smpte2085_appendYuvMatrix(QString& shader,
const Video::ImageFormat& d)
1078 if(d.color_range == AVCOL_RANGE_MPEG)
1079 shader += SMPTE2085_YUV_MATRIX_LIMITED;
1081 shader += SMPTE2085_YUV_MATRIX_FULL;
1090 switch(d.output_format)
1092 case Video::OutputFormat::Passthrough:
1095 shader.reserve(1024);
1096 smpte2085_appendYuvMatrix(shader, d);
1098vec4 convert_to_rgb(vec4 tex) {
1099 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1100 return vec4(rgb, 1.0);
1105 case Video::OutputFormat::Linear:
1108 shader.reserve(2048);
1109 smpte2085_appendYuvMatrix(shader, d);
1110 bt2020_appendEotf(shader, d);
1112vec4 convert_to_rgb(vec4 tex) {
1113 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1114 return vec4(applyEotf(rgb), 1.0);
1119 case Video::OutputFormat::Normalized:
1122 shader.reserve(2048);
1123 smpte2085_appendYuvMatrix(shader, d);
1124 bt2020_appendEotf(shader, d);
1125 const float normFactor = bt2020_eotfToNormalizedFactor(d);
1126 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
1128vec4 convert_to_rgb(vec4 tex) {
1129 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1130 vec3 linear = applyEotf(rgb);
1131 return vec4(linear * eotfNormFactor, 1.0);
1136 case Video::OutputFormat::SDR:
1142 shader.reserve(8192);
1143 smpte2085_appendYuvMatrix(shader, d);
1144 bt2020_appendEotf(shader, d);
1145 shader += BT2020_TO_BT709_GAMUT;
1146 shader += SRGB_OETF;
1148 const float normFactor = bt2020_eotfToNormalizedFactor(d);
1149 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
1150 const float contentPeak = bt2020_contentPeakNits(d);
1151 const auto effectiveTonemap = resolvedTonemap(d);
1152 shader += tonemapShader(effectiveTonemap, contentPeak, 203.0f);
1154 const bool lumBased = isLuminanceBasedTonemap(effectiveTonemap);
1158vec4 convert_to_rgb(vec4 tex) {
1159 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1160 vec3 linearBt2020 = applyEotf(rgb) * eotfNormFactor;
1161 vec3 tonemapped = tonemap(linearBt2020);
1162 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
1163 return vec4(srgbOetf(linearBt709), 1.0);
1170vec4 convert_to_rgb(vec4 tex) {
1171 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1172 vec3 linearBt2020 = applyEotf(rgb) * eotfNormFactor;
1173 vec3 linearBt709 = gamutConvert * linearBt2020;
1174 vec3 tonemapped = tonemap(linearBt709);
1175 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
1197 const bool full_range = (d.color_range == AVCOL_RANGE_JPEG);
1199 switch(d.output_format)
1201 case Video::OutputFormat::Linear:
1202 case Video::OutputFormat::Normalized:
1206 shader.reserve(2048);
1209 shader += full_range ?
"const mat4 conversion_matrix = " SCORE_GFX_BT709_FULL_MATRIX
";\n"
1210 :
"const mat4 conversion_matrix = " SCORE_GFX_BT709_LIMITED_MATRIX
";\n";
1212 shader += DISPLAY_P3_TO_BT2020_GAMUT;
1215vec4 convert_to_rgb(vec4 tex) {
1216 vec3 p3 = (conversion_matrix * tex).rgb;
1217 // Convert P3 linear -> BT.2020 linear (preserves full gamut)
1218 return vec4(gamutConvert * p3, 1.0);
1223 case Video::OutputFormat::SDR:
1228 shader.reserve(2048);
1230 shader += full_range ?
"const mat4 conversion_matrix = " SCORE_GFX_BT709_FULL_MATRIX
";\n"
1231 :
"const mat4 conversion_matrix = " SCORE_GFX_BT709_LIMITED_MATRIX
";\n";
1233 shader += DISPLAY_P3_TO_BT709_GAMUT;
1236vec4 convert_to_rgb(vec4 tex) {
1237 vec3 p3 = (conversion_matrix * tex).rgb;
1238 // Convert P3 -> BT.709 (may clip out-of-gamut colors)
1239 return vec4(clamp(gamutConvert * p3, 0.0, 1.0), 1.0);
1244 case Video::OutputFormat::Passthrough:
1247 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1248 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1266 const bool full_range = (d.color_range == AVCOL_RANGE_JPEG);
1269 switch(d.color_primaries)
1271 case AVCOL_PRI_BT2020:
1273 return bt2020shader(d);
1275 case AVCOL_PRI_SMPTE432:
1276 case AVCOL_PRI_SMPTE431:
1280 return displayP3shader(d);
1282 case AVCOL_PRI_BT709:
1283 case AVCOL_PRI_UNSPECIFIED:
1286 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1287 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1289 case AVCOL_PRI_BT470BG:
1290 case AVCOL_PRI_SMPTE170M:
1292 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
1293 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
1295 case AVCOL_PRI_SMPTE240M:
1296 return full_range ? SCORE_GFX_CONVERT_SMPTE240M_FULL_TO_RGB
1297 : SCORE_GFX_CONVERT_SMPTE240M_LIMITED_TO_RGB;
1307 const bool full_range = (d.color_range == AVCOL_RANGE_JPEG);
1309 switch(d.color_space)
1312 return "vec4 convert_to_rgb(vec4 tex) { return tex; }";
1314 case AVCOL_SPC_BT709:
1316 if(d.color_primaries == AVCOL_PRI_SMPTE432
1317 || d.color_primaries == AVCOL_PRI_SMPTE431)
1318 return displayP3shader(d);
1319 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1320 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1323 return full_range ? SCORE_GFX_CONVERT_FCC_FULL_TO_RGB
1324 : SCORE_GFX_CONVERT_FCC_LIMITED_TO_RGB;
1326 case AVCOL_SPC_BT470BG:
1327 case AVCOL_SPC_SMPTE170M:
1328 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
1329 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
1331 case AVCOL_SPC_SMPTE240M:
1332 return full_range ? SCORE_GFX_CONVERT_SMPTE240M_FULL_TO_RGB
1333 : SCORE_GFX_CONVERT_SMPTE240M_LIMITED_TO_RGB;
1335 case AVCOL_SPC_YCGCO:
1336 return full_range ? SCORE_GFX_CONVERT_YCGCO_FULL_TO_RGB
1337 : SCORE_GFX_CONVERT_YCGCO_LIMITED_TO_RGB;
1339 case AVCOL_SPC_BT2020_NCL:
1340 case AVCOL_SPC_BT2020_CL:
1344 return bt2020shader(d);
1346 case AVCOL_SPC_SMPTE2085:
1349 return smpte2085shader(d);
1351 case AVCOL_SPC_ICTCP:
1354 return ictcpshader(d);
1356 case AVCOL_SPC_CHROMA_DERIVED_NCL:
1358 return chromaDerivedNclMatrix(d);
1360 case AVCOL_SPC_CHROMA_DERIVED_CL:
1363 return chromaDerivedNclMatrix(d);
1367 case AVCOL_SPC_UNSPECIFIED:
1368 case AVCOL_SPC_RESERVED:
1373 if(d.color_primaries == AVCOL_PRI_BT2020)
1374 return bt2020shader(d);
1375 if(d.color_primaries == AVCOL_PRI_SMPTE432
1376 || d.color_primaries == AVCOL_PRI_SMPTE431)
1377 return displayP3shader(d);
1381 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1382 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1384 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
1385 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:12