Loading...
Searching...
No Matches
XYZ12.hpp
1#pragma once
2#include <Gfx/Graph/decoders/ColorSpace.hpp>
3#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
4
5extern "C" {
6#include <libavformat/avformat.h>
7}
8
9namespace score::gfx
10{
11
29{
30 static const constexpr auto frag = R"_(#version 450
31
32)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
33
34layout(binding=3) uniform sampler2D y_tex;
35
36layout(location = 0) in vec2 v_texcoord;
37layout(location = 0) out vec4 fragColor;
38
39%2
40
41vec4 processTexture(vec4 tex) {
42 vec4 processed = convert_to_rgb(tex);
43 { %1 }
44 return processed;
45}
46
47void main ()
48{
49 vec2 tc = score_tc(v_texcoord);
50 int x = int(floor(tc.x * mat.texSz.x) * 3.);
51 int y = int(tc.y * mat.texSz.y);
52 // 12 bits at the top of the word: full scale is 4095 << 4 = 65520.
53 const float s = 65535.0 / 65520.0;
54 float X = texelFetch(y_tex, ivec2(x + 0, y), 0).r * s;
55 float Y = texelFetch(y_tex, ivec2(x + 1, y), 0).r * s;
56 float Z = texelFetch(y_tex, ivec2(x + 2, y), 0).r * s;
57 fragColor = processTexture(vec4(X, Y, Z, 1.));
58})_";
59
60 // SMPTE 428-1 decoding: linear XYZ with 1.0 = the 48 cd/m^2 reference
61 // white. The 52.37 cd/m^2 code-value peak lands at 1.091.
62 static constexpr auto dcdm_eotf = R"_(
63vec3 dcdmToXYZ(vec3 v) {
64 return pow(clamp(v, 0.0, 1.0), vec3(2.6)) * (52.37 / 48.0);
65}
66)_";
67
68 // XYZ -> linear RGB for D65 primaries. GLSL matrices are column-major.
69 static constexpr auto xyz_to_bt709 = R"_(
70const mat3 xyzToRgb = mat3(
71 3.2404542, -0.9692660, 0.0556434,
72 -1.5371385, 1.8760108, -0.2040259,
73 -0.4985314, 0.0415560, 1.0572252
74);
75)_";
76 static constexpr auto xyz_to_bt2020 = R"_(
77const mat3 xyzToRgb = mat3(
78 1.7166512, -0.6666844, 0.0176399,
79 -0.3556708, 1.6164812, -0.0427706,
80 -0.2533663, 0.0157685, 0.9421031
81);
82)_";
83
84 static QString colorShader(const Video::ImageFormat& d)
85 {
86 QString shader;
87 shader.reserve(1024);
88 switch(d.output_format)
89 {
90 case Video::OutputFormat::Passthrough:
91 // The X'Y'Z' code values, untouched.
92 shader += "vec4 convert_to_rgb(vec4 tex) { return tex; }\n";
93 break;
94
95 case Video::OutputFormat::Linear:
96 // Linear BT.2020, 1.0 = reference white: the wide-gamut convention of
97 // the other decoders, and wide enough to hold DCI-P3 masters.
98 shader += dcdm_eotf;
99 shader += xyz_to_bt2020;
100 shader += R"_(
101vec4 convert_to_rgb(vec4 tex) {
102 return vec4(xyzToRgb * dcdmToXYZ(tex.xyz), 1.0);
103}
104)_";
105 break;
106
107 case Video::OutputFormat::Normalized:
108 // Linear BT.2020, 1.0 = the code-value peak (52.37 cd/m^2).
109 shader += dcdm_eotf;
110 shader += xyz_to_bt2020;
111 shader += R"_(
112vec4 convert_to_rgb(vec4 tex) {
113 return vec4(xyzToRgb * dcdmToXYZ(tex.xyz) * (48.0 / 52.37), 1.0);
114}
115)_";
116 break;
117
118 case Video::OutputFormat::SDR:
119 default:
120 // sRGB, with the reference white at 1.0. Out-of-gamut and above-white
121 // values clip, as a DCI-P3 master on a BT.709 display must.
122 shader += dcdm_eotf;
123 shader += xyz_to_bt709;
124 shader += SRGB_OETF;
125 shader += R"_(
126vec4 convert_to_rgb(vec4 tex) {
127 vec3 rgb = clamp(xyzToRgb * dcdmToXYZ(tex.xyz), 0.0, 1.0);
128 return vec4(srgbOetf(rgb), 1.0);
129}
130)_";
131 break;
132 }
133 return shader;
134 }
135
136 explicit XYZ12Decoder(Video::ImageFormat& d, QString f = "")
137 : decoder{d}
138 , filter{std::move(f)}
139 {
140 }
141
142 Video::ImageFormat& decoder;
143 QString filter;
144
145 std::pair<QShader, QShader> init(RenderList& r) override
146 {
147 auto& rhi = *r.state.rhi;
148 const auto w = decoder.width, h = decoder.height;
149
150 {
151 auto tex = rhi.newTexture(QRhiTexture::R16, QSize{w * 3, h}, 1, QRhiTexture::Flag{});
152 tex->create();
153
154 auto sampler = rhi.newSampler(
155 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
156 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
157 sampler->create();
158
159 samplers.push_back({sampler, tex});
160 }
161
163 r.state, vertexShader(),
164 QString(frag).arg(filter).arg(colorShader(decoder)));
165 }
166
167 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
168 {
169 const auto w = decoder.width, h = decoder.height;
170
171 // 3 R16 samples per pixel = 6 bytes per pixel
172 QRhiTextureUploadEntry entry{
173 0, 0, createTextureUpload(frame.data[0], w * 3, h, 2, frame.linesize[0])};
174
175 QRhiTextureUploadDescription desc{entry};
176 res.uploadTexture(samplers[0].texture, desc);
177 }
178};
179
180}
Processes and renders a video frame on the GPU.
Definition GPUVideoDecoder.hpp:161
static QRhiTextureSubresourceUploadDescription createTextureUpload(uint8_t *pixels, int w, int h, int bytesPerPixel, int stride)
Utility method to create a QRhiTextureSubresourceUploadDescription.
Definition GPUVideoDecoder.cpp:22
List of nodes to be rendered to an output.
Definition RenderList.hpp:33
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:274
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag, int multiViewCount)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:1359
Definition VideoInterface.hpp:29
Decodes XYZ12LE, the DCDM X'Y'Z' of JPEG 2000 DCPs.
Definition XYZ12.hpp:29
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition XYZ12.hpp:145
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition XYZ12.hpp:167