video: Better support for XYZ input

With this change, XYZ input is directly converted to the output
colorspace wherever possible, and to the colorspace specified by the
tags and/or --primaries option, otherwise.

This commit also restructures some of the CMS code in gl_video.c to
hopefully make it clearer which decision is being done where and why.
This commit is contained in:
Niklas Haas 2014-03-31 04:51:47 +02:00 committed by wm4
parent 17762a1919
commit 7f3ea12802
4 changed files with 67 additions and 11 deletions

View File

@ -435,6 +435,36 @@ static void luma_coeffs(float m[3][4], float lr, float lg, float lb)
// Constant coefficients (m[x][3]) not set here
}
/**
* \brief get the coefficients of an xyz -> rgb conversion matrix
* \param params parameters for the conversion, only brightness is used
* \param prim primaries of the RGB space to transform to
* \param m array to store the coefficients into
*/
void mp_get_xyz2rgb_coeffs(struct mp_csp_params *params, struct mp_csp_primaries prim, float m[3][4])
{
float tmp[3][3], brightness = params->brightness;
mp_get_rgb2xyz_matrix(prim, tmp);
mp_invert_matrix3x3(tmp);
// Since this outputs linear RGB rather than companded RGB, we
// want to linearize any brightness additions. 2 is a reasonable
// approximation for any sort of gamma function that could be in use.
// As this is an aesthetic setting only, any exact values do not matter.
if (brightness < 0) {
brightness *= -brightness;
} else {
brightness *= brightness;
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
m[i][j] = tmp[i][j];
m[i][COL_C] = brightness;
}
}
/**
* \brief get the coefficients of the yuv -> rgb conversion matrix
* \param params struct specifying the properties of the conversion like

View File

@ -127,6 +127,7 @@ enum mp_csp_equalizer_param {
| (1 << MP_CSP_EQ_SATURATION) )
#define MP_CSP_EQ_CAPS_GAMMA (1 << MP_CSP_EQ_GAMMA)
#define MP_CSP_EQ_CAPS_BRIGHTNESS (1 << MP_CSP_EQ_BRIGHTNESS)
extern const char *const mp_csp_equalizer_names[MP_CSP_EQ_COUNT];
@ -188,6 +189,8 @@ struct mp_csp_primaries mp_get_csp_primaries(enum mp_csp_prim csp);
void mp_get_cms_matrix(struct mp_csp_primaries src, struct mp_csp_primaries dest, float cms_matrix[3][3]);
void mp_get_rgb2xyz_matrix(struct mp_csp_primaries space, float m[3][3]);
void mp_get_xyz2rgb_coeffs(struct mp_csp_params *params, struct mp_csp_primaries prim, float xyz2rgb[3][4]);
void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float yuv2rgb[3][4]);
void mp_gen_yuv2rgb_map(struct mp_csp_params *params, uint8_t *map, int size);

View File

@ -566,7 +566,10 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
// since that's the most likely scenario. Proper VOs should ignore
// this field as well as the matrix and treat XYZ input as absolute,
// but for VOs which use the matrix (and hence, consult this field)
// this is the correct parameter.
// this is the correct parameter. This doubles as a reasonable output
// gamut for VOs which *do* use the specialized XYZ matrix but don't
// know any better output gamut other than whatever the source is
// tagged with.
if (params->primaries == MP_CSP_PRIM_AUTO)
params->primaries = MP_CSP_PRIM_BT_709;
} else {

View File

@ -575,9 +575,13 @@ static void update_uniforms(struct gl_video *p, GLuint program)
loc = gl->GetUniformLocation(program, "colormatrix");
if (loc >= 0) {
float yuv2rgb[3][4] = {{0}};
mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]);
float m[3][4] = {{0}};
if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
mp_get_xyz2rgb_coeffs(&cparams, p->csp_src, m);
} else {
mp_get_yuv2rgb_coeffs(&cparams, m);
}
gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &m[0][0]);
}
gl->Uniform1f(gl->GetUniformLocation(program, "input_gamma"),
@ -880,15 +884,29 @@ static void compile_shaders(struct gl_video *p)
bool convert_to_linear_gamma = !p->is_linear_rgb && use_cms || use_const_luma;
// Figure out the right color spaces we need to convert, if any
enum mp_csp_prim dest = p->opts.srgb ? MP_CSP_PRIM_BT_709 : MP_CSP_PRIM_BT_2020;
bool use_cms_matrix = false;
if (use_cms && p->image_params.primaries != dest) {
p->csp_src = mp_get_csp_primaries(p->image_params.primaries);
p->csp_dest = mp_get_csp_primaries(dest);
use_cms_matrix = true;
enum mp_csp_prim prim_src = p->image_params.primaries, prim_dest;
if (use_cms) {
// sRGB mode wants sRGB aka BT.709 primaries, but the 3DLUT is
// always built against BT.2020.
prim_dest = p->opts.srgb ? MP_CSP_PRIM_BT_709 : MP_CSP_PRIM_BT_2020;
} else {
// If no CMS is being done we just want to output stuff as-is,
// in the native colorspace of the source.
prim_dest = prim_src;
}
// XYZ input has no defined input color space, so we can directly convert
// it to whatever output space we actually need.
if (p->image_desc.flags & MP_IMGFLAG_XYZ)
prim_src = prim_dest;
// Set the colorspace primaries and figure out whether we need to perform
// an extra conversion.
p->csp_src = mp_get_csp_primaries(prim_src);
p->csp_dest = mp_get_csp_primaries(prim_dest);
bool use_cms_matrix = prim_src != prim_dest;
if (p->gl_target == GL_TEXTURE_RECTANGLE) {
shader_def(&header, "VIDEO_SAMPLER", "sampler2DRect");
shader_def_opt(&header, "USE_RECTANGLE", true);
@ -1368,6 +1386,8 @@ static void init_video(struct gl_video *p, const struct mp_image_params *params)
int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
if (p->is_yuv && p->image_params.colorspace != MP_CSP_BT_2020_C)
eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX;
if (p->image_desc.flags & MP_IMGFLAG_XYZ)
eq_caps |= MP_CSP_EQ_CAPS_BRIGHTNESS;
p->video_eq.capabilities = eq_caps;
debug_check_gl(p, "before video texture creation");