diff --git a/video/csputils.c b/video/csputils.c index 23eb099f69..e7ea5a388b 100644 --- a/video/csputils.c +++ b/video/csputils.c @@ -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<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(); } diff --git a/video/csputils.h b/video/csputils.h index d66bb86fa3..184e1ee76a 100644 --- a/video/csputils.h +++ b/video/csputils.h @@ -40,6 +40,7 @@ enum mp_csp { MP_CSP_BT_709, MP_CSP_SMPTE_240M, MP_CSP_RGB, + MP_CSP_XYZ, MP_CSP_COUNT }; diff --git a/video/out/gl_video.c b/video/out/gl_video.c index 43dbb843f1..a000624f9c 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -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 diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl index 2f10e5fdf6..b968cb2c87 100644 --- a/video/out/gl_video_shaders.glsl +++ b/video/out/gl_video_shaders.glsl @@ -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