mirror of https://github.com/mpv-player/mpv
vo_opengl: XYZ input support
Useful for the j2k decoder. Matrix taken from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html (XYZ to sRGB, whitepoint D65) Gamma conversion follows what libswscale does (2.6 in, 2.2 out). If linear RGB is used internally for scaling, the gamma conversion will be undone by setting the exponent to 1. Unfortunately, the two gamma values don't compensate each others exactly (2.2 vs. 1/0.45=2.22...), so output is a little bit incorrect in sRGB or color-managed mode. But for now try hard to match libswscale output, which may or may not be correct.
This commit is contained in:
parent
16d40828ae
commit
872aefaa15
|
@ -40,6 +40,7 @@ char * const mp_csp_names[MP_CSP_COUNT] = {
|
|||
"BT.709 (HD)",
|
||||
"SMPTE-240M",
|
||||
"RGB",
|
||||
"XYZ",
|
||||
};
|
||||
|
||||
char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
|
||||
|
@ -165,10 +166,30 @@ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4])
|
|||
int format = params->colorspace.format;
|
||||
if (format <= MP_CSP_AUTO || format >= MP_CSP_COUNT)
|
||||
format = MP_CSP_BT_601;
|
||||
int levels_in = params->colorspace.levels_in;
|
||||
if (levels_in <= MP_CSP_LEVELS_AUTO || levels_in >= MP_CSP_LEVELS_COUNT)
|
||||
levels_in = MP_CSP_LEVELS_TV;
|
||||
|
||||
switch (format) {
|
||||
case MP_CSP_BT_601: luma_coeffs(m, 0.299, 0.587, 0.114 ); break;
|
||||
case MP_CSP_BT_709: luma_coeffs(m, 0.2126, 0.7152, 0.0722); break;
|
||||
case MP_CSP_SMPTE_240M: luma_coeffs(m, 0.2122, 0.7013, 0.0865); break;
|
||||
case MP_CSP_RGB: {
|
||||
static const float ident[3][4] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
|
||||
memcpy(m, ident, sizeof(ident));
|
||||
levels_in = -1;
|
||||
break;
|
||||
}
|
||||
case MP_CSP_XYZ: {
|
||||
static const float xyz_to_rgb[3][4] = {
|
||||
{3.2404542, -1.5371385, -0.4985314},
|
||||
{-0.9692660, 1.8760108, 0.0415560},
|
||||
{0.0556434, -0.2040259, 1.0572252},
|
||||
};
|
||||
memcpy(m, xyz_to_rgb, sizeof(xyz_to_rgb));
|
||||
levels_in = -1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
};
|
||||
|
@ -183,9 +204,6 @@ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4])
|
|||
m[i][COL_V] = huesin * u + huecos * m[i][COL_V];
|
||||
}
|
||||
|
||||
int levels_in = params->colorspace.levels_in;
|
||||
if (levels_in <= MP_CSP_LEVELS_AUTO || levels_in >= MP_CSP_LEVELS_COUNT)
|
||||
levels_in = MP_CSP_LEVELS_TV;
|
||||
assert(params->input_bits >= 8);
|
||||
assert(params->texture_bits >= params->input_bits);
|
||||
double s = (1 << params->input_bits-8) / ((1<<params->texture_bits)-1.);
|
||||
|
@ -193,10 +211,12 @@ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4])
|
|||
struct yuvlevels { double ymin, ymax, cmin, cmid; }
|
||||
yuvlim = { 16*s, 235*s, 16*s, 128*s },
|
||||
yuvfull = { 0*s, 255*s, 1*s, 128*s }, // '1' for symmetry around 128
|
||||
anyfull = { 0*s, 255*s, -255*s/2, 0 },
|
||||
yuvlev;
|
||||
switch (levels_in) {
|
||||
case MP_CSP_LEVELS_TV: yuvlev = yuvlim; break;
|
||||
case MP_CSP_LEVELS_PC: yuvlev = yuvfull; break;
|
||||
case -1: yuvlev = anyfull; break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ enum mp_csp {
|
|||
MP_CSP_BT_709,
|
||||
MP_CSP_SMPTE_240M,
|
||||
MP_CSP_RGB,
|
||||
MP_CSP_XYZ,
|
||||
MP_CSP_COUNT
|
||||
};
|
||||
|
||||
|
|
|
@ -168,6 +168,8 @@ struct gl_video {
|
|||
bool is_yuv, is_rgb;
|
||||
bool is_linear_rgb;
|
||||
|
||||
float input_gamma, conv_gamma;
|
||||
|
||||
// per pixel (full pixel when packed, each component when planar)
|
||||
int plane_bits;
|
||||
int plane_count;
|
||||
|
@ -452,6 +454,11 @@ static void update_uniforms(struct gl_video *p, GLuint program)
|
|||
.texture_bits = (p->plane_bits + 7) & ~7,
|
||||
};
|
||||
mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
|
||||
if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
|
||||
cparams.colorspace.format = MP_CSP_XYZ;
|
||||
cparams.input_bits = 8;
|
||||
cparams.texture_bits = 8;
|
||||
}
|
||||
|
||||
loc = gl->GetUniformLocation(program, "transform");
|
||||
if (loc >= 0) {
|
||||
|
@ -463,11 +470,16 @@ static void update_uniforms(struct gl_video *p, GLuint program)
|
|||
loc = gl->GetUniformLocation(program, "colormatrix");
|
||||
if (loc >= 0) {
|
||||
float yuv2rgb[3][4] = {{0}};
|
||||
if (p->is_yuv)
|
||||
mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
|
||||
mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
|
||||
gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]);
|
||||
}
|
||||
|
||||
gl->Uniform1f(gl->GetUniformLocation(program, "input_gamma"),
|
||||
p->input_gamma);
|
||||
|
||||
gl->Uniform1f(gl->GetUniformLocation(program, "conv_gamma"),
|
||||
p->conv_gamma);
|
||||
|
||||
gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"),
|
||||
1.0 / cparams.rgamma,
|
||||
1.0 / cparams.ggamma,
|
||||
|
@ -704,8 +716,22 @@ static void compile_shaders(struct gl_video *p)
|
|||
char *header_final = talloc_strdup(tmp, "");
|
||||
char *header_sep = NULL;
|
||||
|
||||
bool convert_input_to_linear = !p->is_linear_rgb &&
|
||||
(p->opts.srgb || p->use_lut_3d);
|
||||
float input_gamma = 1.0;
|
||||
float conv_gamma = 1.0;
|
||||
|
||||
if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
|
||||
input_gamma *= 2.6;
|
||||
conv_gamma *= 1.0 / 2.2;
|
||||
}
|
||||
|
||||
if (!p->is_linear_rgb && (p->opts.srgb || p->use_lut_3d))
|
||||
conv_gamma *= 1.0 / 0.45;
|
||||
|
||||
p->input_gamma = input_gamma;
|
||||
p->conv_gamma = conv_gamma;
|
||||
|
||||
bool convert_input_gamma = p->input_gamma != 1.0;
|
||||
bool convert_input_to_linear = p->conv_gamma != 1.0;
|
||||
|
||||
if (p->image_format == IMGFMT_NV12 || p->image_format == IMGFMT_NV21) {
|
||||
shader_def(&header_conv, "USE_CONV", "CONV_NV12");
|
||||
|
@ -716,8 +742,9 @@ static void compile_shaders(struct gl_video *p)
|
|||
shader_def_opt(&header_conv, "USE_GBRP", p->image_format == IMGFMT_GBRP);
|
||||
shader_def_opt(&header_conv, "USE_SWAP_UV", p->image_format == IMGFMT_NV21);
|
||||
shader_def_opt(&header_conv, "USE_YGRAY", p->is_yuv && p->plane_count == 1);
|
||||
shader_def_opt(&header_conv, "USE_COLORMATRIX", p->is_yuv);
|
||||
shader_def_opt(&header_conv, "USE_LINEAR_CONV", convert_input_to_linear);
|
||||
shader_def_opt(&header_conv, "USE_INPUT_GAMMA", convert_input_gamma);
|
||||
shader_def_opt(&header_conv, "USE_COLORMATRIX", !p->is_rgb);
|
||||
shader_def_opt(&header_conv, "USE_CONV_GAMMA", convert_input_to_linear);
|
||||
if (p->opts.enable_alpha && p->plane_count == 4)
|
||||
shader_def(&header_conv, "USE_ALPHA_PLANE", "3");
|
||||
|
||||
|
@ -745,7 +772,7 @@ static void compile_shaders(struct gl_video *p)
|
|||
|
||||
// Don't sample from input video textures before converting the input to
|
||||
// linear light. (Unneeded when sRGB textures are used.)
|
||||
if (convert_input_to_linear)
|
||||
if (convert_input_gamma || convert_input_to_linear)
|
||||
use_indirect = true;
|
||||
|
||||
// It doesn't make sense to scale the chroma with cscale in the 1. scale
|
||||
|
@ -1634,6 +1661,12 @@ static bool init_format(int fmt, struct gl_video *init)
|
|||
plane_format[0] = byte_formats[1];
|
||||
}
|
||||
|
||||
// XYZ (same roganization as RGB packed, but requires conversion matrix)
|
||||
if (!supported && fmt == IMGFMT_XYZ12) {
|
||||
supported = true;
|
||||
plane_format[0] = IMGFMT_RGB48;
|
||||
}
|
||||
|
||||
// All formats in mp_to_gl_formats[] are supported
|
||||
// If it's not in the table, it will be rejected below.
|
||||
// Includes packed RGB and YUV formats
|
||||
|
|
|
@ -122,6 +122,7 @@ uniform sampler3D lut_3d;
|
|||
uniform sampler2D dither;
|
||||
uniform mat4x3 colormatrix;
|
||||
uniform vec3 inv_gamma;
|
||||
uniform float input_gamma;
|
||||
uniform float conv_gamma;
|
||||
uniform float dither_quantization;
|
||||
uniform float dither_multiply;
|
||||
|
@ -351,11 +352,14 @@ void main() {
|
|||
// wrong for 9/10 bit input
|
||||
color.gb = vec2(128.0/255.0);
|
||||
#endif
|
||||
#ifdef USE_INPUT_GAMMA
|
||||
color = pow(color, vec3(input_gamma));
|
||||
#endif
|
||||
#ifdef USE_COLORMATRIX
|
||||
color = mat3(colormatrix) * color + colormatrix[3];
|
||||
#endif
|
||||
#ifdef USE_LINEAR_CONV
|
||||
color = pow(color, vec3(1.0/0.45));
|
||||
#ifdef USE_CONV_GAMMA
|
||||
color = pow(color, vec3(conv_gamma));
|
||||
#endif
|
||||
#ifdef USE_LINEAR_CONV_INV
|
||||
// Convert from linear RGB to gamma RGB before putting it through the 3D-LUT
|
||||
|
|
Loading…
Reference in New Issue