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:
wm4 2013-05-01 23:59:00 +02:00
parent 16d40828ae
commit 872aefaa15
4 changed files with 70 additions and 12 deletions

View File

@ -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();
}

View File

@ -40,6 +40,7 @@ enum mp_csp {
MP_CSP_BT_709,
MP_CSP_SMPTE_240M,
MP_CSP_RGB,
MP_CSP_XYZ,
MP_CSP_COUNT
};

View File

@ -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

View File

@ -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