2#include <Video/VideoInterface.hpp>
3#include <Gfx/Graph/decoders/Tonemap.hpp>
37#define SCORE_GFX_RGB_MATRIX \
48#define SCORE_GFX_BT601_LIMITED_MATRIX \
50 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
51 0.000000000000000, -0.391762290094914, 2.017232142857143, 0.0,\n\
52 1.596026785714286, -0.812967647237771, 0.000000000000000, 0.0,\n\
53 -0.874202217873451, 0.531667823499146, -1.085630789302022, 1.0)\n"
55#define SCORE_GFX_BT601_FULL_MATRIX \
57 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
58 0.000000000000000, -0.344136286201022, 1.772000000000000, 0.0,\n\
59 1.402000000000000, -0.714136286201022, 0.000000000000000, 0.0,\n\
60 -0.703749019607843, 0.531211330460634, -0.889474509803922, 1.0)\n"
63#define SCORE_GFX_BT601_MATRIX SCORE_GFX_BT601_LIMITED_MATRIX
69#define SCORE_GFX_BT709_LIMITED_MATRIX \
71 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
72 0.000000000000000, -0.213248614273730, 2.112401785714286, 0.0,\n\
73 1.792741071428571, -0.532909328559444, 0.000000000000000, 0.0,\n\
74 -0.972945075016308, 0.301482665475862, -1.133402217873451, 1.0)\n"
76#define SCORE_GFX_BT709_FULL_MATRIX \
78 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
79 0.000000000000000, -0.187324272930649, 1.855600000000000, 0.0,\n\
80 1.574800000000000, -0.468124272930649, 0.000000000000000, 0.0,\n\
81 -0.790487843137255, 0.329009466157828, -0.931438431372549, 1.0)\n"
84#define SCORE_GFX_BT709_MATRIX SCORE_GFX_BT709_LIMITED_MATRIX
90#define SCORE_GFX_SMPTE240M_LIMITED_MATRIX \
92 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
93 0.000000000000000, -0.256532845251675, 2.079843750000000, 0.0,\n\
94 1.793651785714286, -0.542724809537390, 0.000000000000000, 0.0,\n\
95 -0.973402217873451, 0.328136638536074, -1.117059360730594, 1.0)\n"
97#define SCORE_GFX_SMPTE240M_FULL_MATRIX \
99 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
100 0.000000000000000, -0.225346499358335, 1.827000000000000, 0.0,\n\
101 1.575600000000000, -0.476746499358335, 0.000000000000000, 0.0,\n\
102 -0.790889411764706, 0.352423152296995, -0.917082352941176, 1.0)\n"
108#define SCORE_GFX_FCC_LIMITED_MATRIX \
110 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
111 0.000000000000000, -0.377792070217918, 2.026339285714286, 0.0,\n\
112 1.593750000000000, -0.810381355932203, 0.000000000000000, 0.0,\n\
113 -0.873059360730594, 0.523357104160448, -1.090202217873451, 1.0)\n"
115#define SCORE_GFX_FCC_FULL_MATRIX \
117 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
118 0.000000000000000, -0.331864406779661, 1.780000000000000, 0.0,\n\
119 1.400000000000000, -0.711864406779661, 0.000000000000000, 0.0,\n\
120 -0.702745098039216, 0.523910933865071, -0.893490196078431, 1.0)\n"
128#define SCORE_GFX_YCGCO_LIMITED_MATRIX \
130 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
131 -1.138392857142857, 1.138392857142857, -1.138392857142857, 0.0,\n\
132 1.138392857142857, 0.000000000000000, -1.138392857142857, 0.0,\n\
133 -0.073059360730594, -0.644487932159165, 1.069797782126549, 1.0)\n"
135#define SCORE_GFX_YCGCO_FULL_MATRIX \
137 1.0, 1.0, 1.0, 0.0,\n\
138 -1.0, 1.0, -1.0, 0.0,\n\
139 1.0, 0.0, -1.0, 0.0,\n\
140 0.000000000000000, -0.501960784313725, 1.003921568627451, 1.0)\n"
148#define SCORE_GFX_BT2020_LIMITED_MATRIX \
150 1.164383561643836, 1.164383561643836, 1.164383561643836, 0.0,\n\
151 0.000000000000000, -0.187326104219343, 2.141772321428571, 0.0,\n\
152 1.678674107142857, -0.650424318505057, 0.000000000000000, 0.0,\n\
153 -0.915687932159165, 0.347458498519301, -1.148145075016308, 1.0)\n"
155#define SCORE_GFX_BT2020_FULL_MATRIX \
157 1.000000000000000, 1.000000000000000, 1.000000000000000, 0.0,\n\
158 0.000000000000000, -0.164553126843658, 1.881400000000000, 0.0,\n\
159 1.474600000000000, -0.571353126843658, 0.000000000000000, 0.0,\n\
160 -0.740191372549020, 0.369396080282261, -0.944389019607843, 1.0)\n"
174#define SCORE_GFX_MSB_ALIGNED_SCALE "1.00390625"
179#define SCORE_GFX_LSB10_SCALE "64.25"
180#define SCORE_GFX_LSB12_SCALE "16.0625"
185#define SCORE_GFX_UNORM10_SCALE "1.002941176470588"
188#define SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB \
189 "const mat4 conversion_matrix = " SCORE_GFX_BT601_LIMITED_MATRIX ";\n" \
190 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
192#define SCORE_GFX_CONVERT_BT601_FULL_TO_RGB \
193 "const mat4 conversion_matrix = " SCORE_GFX_BT601_FULL_MATRIX ";\n" \
194 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
197#define SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB \
198 "const mat4 conversion_matrix = " SCORE_GFX_BT709_LIMITED_MATRIX ";\n" \
199 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
201#define SCORE_GFX_CONVERT_BT709_FULL_TO_RGB \
202 "const mat4 conversion_matrix = " SCORE_GFX_BT709_FULL_MATRIX ";\n" \
203 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
206#define SCORE_GFX_CONVERT_SMPTE240M_LIMITED_TO_RGB \
207 "const mat4 conversion_matrix = " SCORE_GFX_SMPTE240M_LIMITED_MATRIX ";\n"\
208 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
210#define SCORE_GFX_CONVERT_SMPTE240M_FULL_TO_RGB \
211 "const mat4 conversion_matrix = " SCORE_GFX_SMPTE240M_FULL_MATRIX ";\n" \
212 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
215#define SCORE_GFX_CONVERT_FCC_LIMITED_TO_RGB \
216 "const mat4 conversion_matrix = " SCORE_GFX_FCC_LIMITED_MATRIX ";\n" \
217 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
219#define SCORE_GFX_CONVERT_FCC_FULL_TO_RGB \
220 "const mat4 conversion_matrix = " SCORE_GFX_FCC_FULL_MATRIX ";\n" \
221 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
224#define SCORE_GFX_CONVERT_YCGCO_LIMITED_TO_RGB \
225 "const mat4 conversion_matrix = " SCORE_GFX_YCGCO_LIMITED_MATRIX ";\n" \
226 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
228#define SCORE_GFX_CONVERT_YCGCO_FULL_TO_RGB \
229 "const mat4 conversion_matrix = " SCORE_GFX_YCGCO_FULL_MATRIX ";\n" \
230 "vec4 convert_to_rgb(vec4 tex) { return conversion_matrix * tex; }\n"
233#define SCORE_GFX_CONVERT_BT601_TO_RGB SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB
234#define SCORE_GFX_CONVERT_BT709_TO_RGB SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB
242#define SCORE_GFX_BT2020_TO_709_MATRIX \
244 1.660491, -0.587641, -0.072850, 0.000000,\n\
245 -0.124550, 1.132900, -0.008349, 0.000000,\n\
246 -0.018151, -0.100579, 1.118730, 0.000000,\n\
247 0.000000, 0.000000, 0.000000, 1.000000\n\
250#define SCORE_GFX_BT2020_MATRIX SCORE_GFX_BT709_MATRIX
264static constexpr auto BT2020_YUV_MATRIX_LIMITED = R
"_(
265const mat3 uYuvToRgbColorTransform = mat3(
266 1.1689, 1.1689, 1.1689,
267 0.0000, -0.1881, 2.1502,
268 1.6853, -0.6530, 0.0000
270const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
273static constexpr auto BT2020_YUV_MATRIX_FULL = R
"_(
274const mat3 uYuvToRgbColorTransform = mat3(
275 1.0000, 1.0000, 1.0000,
276 0.0000, -0.1646, 1.8814,
277 1.4746, -0.5714, 0.0000
279const vec3 yuvOffset = vec3(0.0, 0.5, 0.5);
284static constexpr auto BT2020_PQ_EOTF = R
"_(
285vec3 applyEotf(vec3 v) {
286 const float m1 = 2610.0 / 16384.0;
287 const float m2 = (2523.0 / 4096.0) * 128.0;
288 const float c1 = 3424.0 / 4096.0;
289 const float c2 = (2413.0 / 4096.0) * 32.0;
290 const float c3 = (2392.0 / 4096.0) * 32.0;
291 vec3 p = pow(clamp(v, 0.0, 1.0), 1.0 / vec3(m2));
292 return pow(max(p - c1, 0.0) / (c2 - c3 * p), 1.0 / vec3(m1));
296static constexpr auto BT2020_HLG_EOTF = R
"_(
297float hlgEotfSingle(float v) {
298 const float a = 0.17883277;
299 const float b = 0.28466892;
300 const float c = 0.55991073;
301 return v <= 0.5 ? v * v / 3.0
302 : (b + exp((v - c) / a)) / 12.0;
304vec3 applyEotf(vec3 v) {
305 return vec3(hlgEotfSingle(v.r), hlgEotfSingle(v.g), hlgEotfSingle(v.b));
309static constexpr auto BT2020_LINEAR_EOTF = R
"_(
310vec3 applyEotf(vec3 v) { return v; }
313static constexpr auto BT2020_GAMMA22_EOTF = R
"_(
314vec3 applyEotf(vec3 v) { return pow(max(v, 0.0), vec3(2.2)); }
332static constexpr auto BT2020_HLG_OOTF = R
"_(
333vec3 applyHlgOotf(vec3 scene, float Lw) {
334 const vec3 hlgLuma = vec3(0.2627, 0.6780, 0.0593);
335 float gamma = 1.2 + 0.42 * log(Lw / 1000.0) / log(10.0);
336 float Ys = dot(hlgLuma, scene);
337 // Guard against Ys=0 (black) which would cause pow(0, negative) = inf
338 if (Ys <= 0.0) return vec3(0.0);
339 return Lw * pow(Ys, gamma - 1.0) * scene;
345static constexpr auto BT2020_TO_BT709_GAMUT = R
"_(
346const mat3 gamutConvert = mat3(
347 1.6605, -0.1246, -0.0182,
348 -0.5876, 1.1329, -0.1006,
349 -0.0728, -0.0083, 1.1187
355static constexpr auto SRGB_OETF = R
"_(
356vec3 srgbOetf(vec3 c) {
358 vec3 hi = 1.055 * pow(max(c, 0.0), vec3(1.0 / 2.4)) - 0.055;
359 return mix(lo, hi, step(vec3(0.0031308), c));
388static constexpr auto ICTCP_PQ_TO_LMS = R
"_(
389const mat3 ictcpToLms = mat3(
390 1.000000000000000, 1.000000000000000, 1.000000000000000,
391 0.008609037037933, -0.008609037037933, 0.560031335710679,
392 0.111029625003026, -0.111029625003026, -0.320627174987319
397static constexpr auto ICTCP_HLG_TO_LMS = R
"_(
398const mat3 ictcpToLms = mat3(
399 1.000000000000000, 1.000000000000000, 1.000000000000000,
400 0.015718580108730, -0.015718580108730, 1.021271079842234,
401 0.209581068116406, -0.209581068116406, -0.605274490992431
406static constexpr auto LMS_TO_BT2020_RGB = R
"_(
407const mat3 lmsToBt2020 = mat3(
408 3.436606694333078, -0.791329555598929, -0.025949899690593,
409 -2.506452118656270, 1.983600451792291, -0.098913714711726,
410 0.069845424323191, -0.192270896193362, 1.124863614402319
431static constexpr auto SMPTE2085_YUV_MATRIX_LIMITED = R
"_(
432// SMPTE 2085: Y'D'zD'x -> R'G'B' inverse matrix
433// Range scaling (255/219 for Y, 255/224 for D'z/D'x) baked in.
434const mat3 uYuvToRgbColorTransform = mat3(
435 1.173889720601265, 1.164383561643836, 1.180238890904243,
436 0.000000000000000, 0.000000000000000, 2.307788545607404,
437 2.295373650104259, 0.000000000000000, 0.000000000000000
439const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
442static constexpr auto SMPTE2085_YUV_MATRIX_FULL = R
"_(
443const mat3 uYuvToRgbColorTransform = mat3(
444 1.008164112986969, 1.000000000000000, 1.013616929835409,
445 0.000000000000000, 0.000000000000000, 2.027233859670817,
446 2.016328225973937, 0.000000000000000, 0.000000000000000
448const vec3 yuvOffset = vec3(0.0, 0.5, 0.5);
464static constexpr auto DISPLAY_P3_TO_BT2020_GAMUT = R
"_(
465const mat3 gamutConvert = mat3(
466 0.753833034361722, 0.045743848965358, -0.001210340354518,
467 0.198597369052617, 0.941777219811693, 0.017601717301090,
468 0.047569596585662, 0.012478931222948, 0.983608623053428
472static constexpr auto DISPLAY_P3_TO_BT709_GAMUT = R
"_(
473const mat3 gamutConvert = mat3(
474 1.224940176280561, -0.042056954709688, -0.019637554590334,
475 -0.224940176280560, 1.042056954709688, -0.078636045550632,
476 0.000000000000000, 0.000000000000000, 1.098273600140966
487 if(d.tonemap == ::Video::Tonemap::Auto)
488 return resolveAutoTonemap(
static_cast<int>(d.color_trc));
493static inline void bt2020_appendYuvMatrix(QString& shader,
const Video::ImageFormat& d)
495 if(d.color_range == AVCOL_RANGE_MPEG)
496 shader += BT2020_YUV_MATRIX_LIMITED;
498 shader += BT2020_YUV_MATRIX_FULL;
504 if(d.color_trc == AVCOL_TRC_SMPTE2084)
505 shader += BT2020_PQ_EOTF;
506 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
507 shader += BT2020_HLG_EOTF;
508 else if(d.color_trc == AVCOL_TRC_LINEAR)
509 shader += BT2020_LINEAR_EOTF;
511 shader += BT2020_GAMMA22_EOTF;
517#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 3, 100)
518#if __has_include(<libavutil/mastering_display_metadata.h>)
521 float cll =
static_cast<float>(d.content_light->MaxCLL);
522 if(cll >= 100.0f && cll <= 10000.0f)
526 if(d.mastering_display.has_luminance
527 && d.mastering_display.max_luminance.den > 0)
529 float peak =
static_cast<float>(av_q2d(d.mastering_display.max_luminance));
530 if(peak >= 100.0f && peak <= 10000.0f)
537 if(d.color_trc == AVCOL_TRC_SMPTE2084)
539 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
554 if(d.color_trc == AVCOL_TRC_SMPTE2084)
558 return 10000.0f / bt2020_contentPeakNits(d);
560 else if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
591 shader.reserve(1024);
593 bt2020_appendYuvMatrix(shader, d);
596vec4 convert_to_rgb(vec4 tex) {
597 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
598 return vec4(rgb, 1.0);
615 shader.reserve(2048);
617 bt2020_appendYuvMatrix(shader, d);
618 bt2020_appendEotf(shader, d);
621vec4 convert_to_rgb(vec4 tex) {
622 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
623 return vec4(applyEotf(rgb), 1.0);
639 shader.reserve(2048);
641 bt2020_appendYuvMatrix(shader, d);
642 bt2020_appendEotf(shader, d);
644 const float normFactor = bt2020_eotfToNormalizedFactor(d);
645 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
648vec4 convert_to_rgb(vec4 tex) {
649 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
650 vec3 linear = applyEotf(rgb);
651 // Normalize: 1.0 = content peak luminance
652 return vec4(linear * eotfNormFactor, 1.0);
687 shader.reserve(8192);
689 const bool isHLG = (d.color_trc == AVCOL_TRC_ARIB_STD_B67);
692 bt2020_appendYuvMatrix(shader, d);
695 bt2020_appendEotf(shader, d);
699 shader += BT2020_HLG_OOTF;
702 shader += BT2020_TO_BT709_GAMUT;
717 const float hlgDisplayPeak = bt2020_hlgDisplayPeakNits(d);
718 contentPeak = hlgDisplayPeak;
720 normFactor = 1.0f / contentPeak;
721 shader += QString(
"const float hlgDisplayLw = %1;\n").arg(hlgDisplayPeak, 0,
'f', 1);
725 contentPeak = bt2020_contentPeakNits(d);
726 normFactor = bt2020_eotfToNormalizedFactor(d);
728 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 6);
731 const float sdrPeak = 203.0f;
732 const auto effectiveTonemap = resolvedTonemap(d);
733 shader += tonemapShader(effectiveTonemap, contentPeak, sdrPeak);
736 const bool lumBased = isLuminanceBasedTonemap(effectiveTonemap);
744vec4 convert_to_rgb(vec4 tex) {
745 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
746 vec3 sceneLinear = applyEotf(rgb);
748 // OOTF: scene-linear -> display-linear (nits)
749 vec3 displayLinear = applyHlgOotf(sceneLinear, hlgDisplayLw);
751 // Normalize: 1.0 = content peak
752 displayLinear *= eotfNormFactor;
754 vec3 tonemapped = tonemap(displayLinear);
755 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
756 return vec4(srgbOetf(linearBt709), 1.0);
763vec4 convert_to_rgb(vec4 tex) {
764 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
765 vec3 sceneLinear = applyEotf(rgb);
766 vec3 displayLinear = applyHlgOotf(sceneLinear, hlgDisplayLw);
767 displayLinear *= eotfNormFactor;
769 vec3 linearBt709 = gamutConvert * displayLinear;
770 vec3 tonemapped = tonemap(linearBt709);
771 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
782vec4 convert_to_rgb(vec4 tex) {
783 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
784 vec3 linearBt2020 = applyEotf(rgb);
785 linearBt2020 *= eotfNormFactor;
787 vec3 tonemapped = tonemap(linearBt2020);
788 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
789 return vec4(srgbOetf(linearBt709), 1.0);
796vec4 convert_to_rgb(vec4 tex) {
797 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
798 vec3 linearBt2020 = applyEotf(rgb);
799 linearBt2020 *= eotfNormFactor;
801 vec3 linearBt709 = gamutConvert * linearBt2020;
802 vec3 tonemapped = tonemap(linearBt709);
803 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
818 switch(d.output_format)
820 case Video::OutputFormat::Passthrough:
821 return bt2020shader_passthrough(d);
822 case Video::OutputFormat::Linear:
823 return bt2020shader_linear(d);
824 case Video::OutputFormat::Normalized:
825 return bt2020shader_normalized(d);
826 case Video::OutputFormat::SDR:
828 return bt2020shader_sdr(d);
844static inline void ictcp_appendInverseMatrix(QString& shader,
const Video::ImageFormat& d)
846 if(d.color_trc == AVCOL_TRC_ARIB_STD_B67)
847 shader += ICTCP_HLG_TO_LMS;
849 shader += ICTCP_PQ_TO_LMS;
856 shader.reserve(4096);
858 ictcp_appendInverseMatrix(shader, d);
859 shader += LMS_TO_BT2020_RGB;
860 bt2020_appendEotf(shader, d);
865 if(d.color_range == AVCOL_RANGE_MPEG)
868const float yScale = 255.0 / 219.0;
869const float yOffset = 16.0 / 255.0;
870const float cScale = 255.0 / 224.0;
871const float cOffset = 128.0 / 255.0;
872vec4 convert_to_rgb(vec4 tex) {
873 // Step 1: Unpack limited range ICtCp
874 float I = (tex.x - yOffset) * yScale;
875 float Ct = (tex.y - cOffset) * cScale;
876 float Cp = (tex.z - cOffset) * cScale;
878 // Step 2: ICtCp -> transfer-encoded LMS
879 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
881 // Step 3: EOTF -> linear LMS
882 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
884 // Step 4: Linear LMS -> linear BT.2020 RGB
885 return vec4(lmsToBt2020 * lmsLinear, 1.0);
892vec4 convert_to_rgb(vec4 tex) {
894 float Ct = tex.y - 0.5;
895 float Cp = tex.z - 0.5;
897 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
898 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
899 return vec4(lmsToBt2020 * lmsLinear, 1.0);
911 shader.reserve(4096);
913 ictcp_appendInverseMatrix(shader, d);
914 shader += LMS_TO_BT2020_RGB;
915 bt2020_appendEotf(shader, d);
917 const float normFactor = bt2020_eotfToNormalizedFactor(d);
918 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
920 if(d.color_range == AVCOL_RANGE_MPEG)
923const float yScale = 255.0 / 219.0;
924const float yOffset = 16.0 / 255.0;
925const float cScale = 255.0 / 224.0;
926const float cOffset = 128.0 / 255.0;
927vec4 convert_to_rgb(vec4 tex) {
928 float I = (tex.x - yOffset) * yScale;
929 float Ct = (tex.y - cOffset) * cScale;
930 float Cp = (tex.z - cOffset) * cScale;
931 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
932 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
933 vec3 bt2020 = lmsToBt2020 * lmsLinear;
934 return vec4(bt2020 * eotfNormFactor, 1.0);
941vec4 convert_to_rgb(vec4 tex) {
943 float Ct = tex.y - 0.5;
944 float Cp = tex.z - 0.5;
945 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
946 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
947 vec3 bt2020 = lmsToBt2020 * lmsLinear;
948 return vec4(bt2020 * eotfNormFactor, 1.0);
960 shader.reserve(8192);
962 ictcp_appendInverseMatrix(shader, d);
963 shader += LMS_TO_BT2020_RGB;
964 bt2020_appendEotf(shader, d);
965 shader += BT2020_TO_BT709_GAMUT;
968 const float normFactor = bt2020_eotfToNormalizedFactor(d);
969 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
971 const float contentPeak = bt2020_contentPeakNits(d);
972 const float sdrPeak = 203.0f;
973 const auto effectiveTonemap = resolvedTonemap(d);
974 shader += tonemapShader(effectiveTonemap, contentPeak, sdrPeak);
976 const bool lumBased = isLuminanceBasedTonemap(effectiveTonemap);
977 const char* rangeUnpack;
978 if(d.color_range == AVCOL_RANGE_MPEG)
981const float yScale = 255.0 / 219.0;
982const float yOffset = 16.0 / 255.0;
983const float cScale = 255.0 / 224.0;
984const float cOffset = 128.0 / 255.0;
987 float I = (tex.x - yOffset) * yScale;
988 float Ct = (tex.y - cOffset) * cScale;
989 float Cp = (tex.z - cOffset) * cScale;
996 float Ct = tex.y - 0.5;
997 float Cp = tex.z - 0.5;
1003 shader += QString(R
"_(
1004vec4 convert_to_rgb(vec4 tex) {
1006 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
1007 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
1008 vec3 linearBt2020 = lmsToBt2020 * lmsLinear;
1009 linearBt2020 *= eotfNormFactor;
1010 vec3 tonemapped = tonemap(linearBt2020);
1011 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
1012 return vec4(srgbOetf(linearBt709), 1.0);
1014)_").arg(rangeUnpack);
1018 shader += QString(R
"_(
1019vec4 convert_to_rgb(vec4 tex) {
1021 vec3 lmsPQ = ictcpToLms * vec3(I, Ct, Cp);
1022 vec3 lmsLinear = applyEotf(clamp(lmsPQ, 0.0, 1.0));
1023 vec3 linearBt2020 = lmsToBt2020 * lmsLinear;
1024 linearBt2020 *= eotfNormFactor;
1025 vec3 linearBt709 = gamutConvert * linearBt2020;
1026 vec3 tonemapped = tonemap(linearBt709);
1027 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
1029)_").arg(rangeUnpack);
1039 shader.reserve(2048);
1041 ictcp_appendInverseMatrix(shader, d);
1043 if(d.color_range == AVCOL_RANGE_MPEG)
1046const float yScale = 255.0 / 219.0;
1047const float yOffset = 16.0 / 255.0;
1048const float cScale = 255.0 / 224.0;
1049const float cOffset = 128.0 / 255.0;
1050vec4 convert_to_rgb(vec4 tex) {
1051 float I = (tex.x - yOffset) * yScale;
1052 float Ct = (tex.y - cOffset) * cScale;
1053 float Cp = (tex.z - cOffset) * cScale;
1054 // Decode to PQ-encoded LMS, output as-is (no EOTF, no LMS->RGB)
1055 vec3 lmsPQ = clamp(ictcpToLms * vec3(I, Ct, Cp), 0.0, 1.0);
1056 return vec4(lmsPQ, 1.0);
1063vec4 convert_to_rgb(vec4 tex) {
1065 float Ct = tex.y - 0.5;
1066 float Cp = tex.z - 0.5;
1067 vec3 lmsPQ = clamp(ictcpToLms * vec3(I, Ct, Cp), 0.0, 1.0);
1068 return vec4(lmsPQ, 1.0);
1078 switch(d.output_format)
1080 case Video::OutputFormat::Passthrough:
1081 return ictcpshader_passthrough(d);
1082 case Video::OutputFormat::Linear:
1083 return ictcpshader_linear(d);
1084 case Video::OutputFormat::Normalized:
1085 return ictcpshader_normalized(d);
1086 case Video::OutputFormat::SDR:
1088 return ictcpshader_sdr(d);
1100static inline void smpte2085_appendYuvMatrix(QString& shader,
const Video::ImageFormat& d)
1102 if(d.color_range == AVCOL_RANGE_MPEG)
1103 shader += SMPTE2085_YUV_MATRIX_LIMITED;
1105 shader += SMPTE2085_YUV_MATRIX_FULL;
1114 switch(d.output_format)
1116 case Video::OutputFormat::Passthrough:
1119 shader.reserve(1024);
1120 smpte2085_appendYuvMatrix(shader, d);
1122vec4 convert_to_rgb(vec4 tex) {
1123 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1124 return vec4(rgb, 1.0);
1129 case Video::OutputFormat::Linear:
1132 shader.reserve(2048);
1133 smpte2085_appendYuvMatrix(shader, d);
1134 bt2020_appendEotf(shader, d);
1136vec4 convert_to_rgb(vec4 tex) {
1137 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1138 return vec4(applyEotf(rgb), 1.0);
1143 case Video::OutputFormat::Normalized:
1146 shader.reserve(2048);
1147 smpte2085_appendYuvMatrix(shader, d);
1148 bt2020_appendEotf(shader, d);
1149 const float normFactor = bt2020_eotfToNormalizedFactor(d);
1150 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
1152vec4 convert_to_rgb(vec4 tex) {
1153 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1154 vec3 linear = applyEotf(rgb);
1155 return vec4(linear * eotfNormFactor, 1.0);
1160 case Video::OutputFormat::SDR:
1166 shader.reserve(8192);
1167 smpte2085_appendYuvMatrix(shader, d);
1168 bt2020_appendEotf(shader, d);
1169 shader += BT2020_TO_BT709_GAMUT;
1170 shader += SRGB_OETF;
1172 const float normFactor = bt2020_eotfToNormalizedFactor(d);
1173 shader += QString(
"const float eotfNormFactor = %1;\n").arg(normFactor, 0,
'f', 4);
1174 const float contentPeak = bt2020_contentPeakNits(d);
1175 const auto effectiveTonemap = resolvedTonemap(d);
1176 shader += tonemapShader(effectiveTonemap, contentPeak, 203.0f);
1178 const bool lumBased = isLuminanceBasedTonemap(effectiveTonemap);
1182vec4 convert_to_rgb(vec4 tex) {
1183 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1184 vec3 linearBt2020 = applyEotf(rgb) * eotfNormFactor;
1185 vec3 tonemapped = tonemap(linearBt2020);
1186 vec3 linearBt709 = clamp(gamutConvert * tonemapped, 0.0, 1.0);
1187 return vec4(srgbOetf(linearBt709), 1.0);
1194vec4 convert_to_rgb(vec4 tex) {
1195 vec3 rgb = clamp(uYuvToRgbColorTransform * (tex.xyz - yuvOffset), 0.0, 1.0);
1196 vec3 linearBt2020 = applyEotf(rgb) * eotfNormFactor;
1197 vec3 linearBt709 = gamutConvert * linearBt2020;
1198 vec3 tonemapped = tonemap(linearBt709);
1199 return vec4(srgbOetf(clamp(tonemapped, 0.0, 1.0)), 1.0);
1221 const bool full_range = (d.color_range == AVCOL_RANGE_JPEG);
1223 switch(d.output_format)
1225 case Video::OutputFormat::Linear:
1226 case Video::OutputFormat::Normalized:
1230 shader.reserve(2048);
1233 shader += full_range ?
"const mat4 conversion_matrix = " SCORE_GFX_BT709_FULL_MATRIX
";\n"
1234 :
"const mat4 conversion_matrix = " SCORE_GFX_BT709_LIMITED_MATRIX
";\n";
1236 shader += DISPLAY_P3_TO_BT2020_GAMUT;
1239vec4 convert_to_rgb(vec4 tex) {
1240 vec3 p3 = (conversion_matrix * tex).rgb;
1241 // Convert P3 linear -> BT.2020 linear (preserves full gamut)
1242 return vec4(gamutConvert * p3, 1.0);
1247 case Video::OutputFormat::SDR:
1252 shader.reserve(2048);
1254 shader += full_range ?
"const mat4 conversion_matrix = " SCORE_GFX_BT709_FULL_MATRIX
";\n"
1255 :
"const mat4 conversion_matrix = " SCORE_GFX_BT709_LIMITED_MATRIX
";\n";
1257 shader += DISPLAY_P3_TO_BT709_GAMUT;
1260vec4 convert_to_rgb(vec4 tex) {
1261 vec3 p3 = (conversion_matrix * tex).rgb;
1262 // Convert P3 -> BT.709 (may clip out-of-gamut colors)
1263 return vec4(clamp(gamutConvert * p3, 0.0, 1.0), 1.0);
1268 case Video::OutputFormat::Passthrough:
1271 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1272 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1290 const bool full_range = (d.color_range == AVCOL_RANGE_JPEG);
1293 switch(d.color_primaries)
1295 case AVCOL_PRI_BT2020:
1297 return bt2020shader(d);
1299 case AVCOL_PRI_SMPTE432:
1300 case AVCOL_PRI_SMPTE431:
1304 return displayP3shader(d);
1306 case AVCOL_PRI_BT709:
1307 case AVCOL_PRI_UNSPECIFIED:
1310 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1311 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1313 case AVCOL_PRI_BT470BG:
1314 case AVCOL_PRI_SMPTE170M:
1316 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
1317 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
1319 case AVCOL_PRI_SMPTE240M:
1320 return full_range ? SCORE_GFX_CONVERT_SMPTE240M_FULL_TO_RGB
1321 : SCORE_GFX_CONVERT_SMPTE240M_LIMITED_TO_RGB;
1331 const bool full_range = (d.color_range == AVCOL_RANGE_JPEG);
1333 switch(d.color_space)
1336 return "vec4 convert_to_rgb(vec4 tex) { return tex; }";
1338 case AVCOL_SPC_BT709:
1340 if(d.color_primaries == AVCOL_PRI_SMPTE432
1341 || d.color_primaries == AVCOL_PRI_SMPTE431)
1342 return displayP3shader(d);
1343 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1344 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1347 return full_range ? SCORE_GFX_CONVERT_FCC_FULL_TO_RGB
1348 : SCORE_GFX_CONVERT_FCC_LIMITED_TO_RGB;
1350 case AVCOL_SPC_BT470BG:
1351 case AVCOL_SPC_SMPTE170M:
1352 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
1353 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
1355 case AVCOL_SPC_SMPTE240M:
1356 return full_range ? SCORE_GFX_CONVERT_SMPTE240M_FULL_TO_RGB
1357 : SCORE_GFX_CONVERT_SMPTE240M_LIMITED_TO_RGB;
1359 case AVCOL_SPC_YCGCO:
1360 return full_range ? SCORE_GFX_CONVERT_YCGCO_FULL_TO_RGB
1361 : SCORE_GFX_CONVERT_YCGCO_LIMITED_TO_RGB;
1363 case AVCOL_SPC_BT2020_NCL:
1364 case AVCOL_SPC_BT2020_CL:
1368 return bt2020shader(d);
1370 case AVCOL_SPC_SMPTE2085:
1373 return smpte2085shader(d);
1375 case AVCOL_SPC_ICTCP:
1378 return ictcpshader(d);
1380 case AVCOL_SPC_CHROMA_DERIVED_NCL:
1382 return chromaDerivedNclMatrix(d);
1384 case AVCOL_SPC_CHROMA_DERIVED_CL:
1387 return chromaDerivedNclMatrix(d);
1391 case AVCOL_SPC_UNSPECIFIED:
1392 case AVCOL_SPC_RESERVED:
1397 if(d.color_primaries == AVCOL_PRI_BT2020)
1398 return bt2020shader(d);
1399 if(d.color_primaries == AVCOL_PRI_SMPTE432
1400 || d.color_primaries == AVCOL_PRI_SMPTE431)
1401 return displayP3shader(d);
1405 return full_range ? SCORE_GFX_CONVERT_BT709_FULL_TO_RGB
1406 : SCORE_GFX_CONVERT_BT709_LIMITED_TO_RGB;
1408 return full_range ? SCORE_GFX_CONVERT_BT601_FULL_TO_RGB
1409 : SCORE_GFX_CONVERT_BT601_LIMITED_TO_RGB;
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11