From 863701391c7657b8fccb264f2161f84a46ce540c Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Wed, 26 Nov 2014 21:35:08 +0100 Subject: [PATCH] vo_opengl: Linearize non-RGB sRGB files correctly (eg. JPEG) Signed-off-by: wm4 --- video/csputils.h | 7 +++++++ video/out/gl_video.c | 32 +++++++++++++++++++++++++------- video/out/gl_video_shaders.glsl | 16 ++++++++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/video/csputils.h b/video/csputils.h index 33bac87f64..1ad88bf261 100644 --- a/video/csputils.h +++ b/video/csputils.h @@ -67,6 +67,13 @@ enum mp_csp_prim { MP_CSP_PRIM_COUNT }; +enum mp_csp_trc { + MP_CSP_TRC_NONE, + MP_CSP_TRC_BT_2020_APPROX, + MP_CSP_TRC_BT_2020_EXACT, + MP_CSP_TRC_SRGB +}; + // Any enum mp_csp_prim value is a valid index (except MP_CSP_PRIM_COUNT) extern const char *const mp_csp_prim_names[MP_CSP_PRIM_COUNT]; diff --git a/video/out/gl_video.c b/video/out/gl_video.c index 3e87f20ab5..908442ad3d 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -901,11 +901,25 @@ static void compile_shaders(struct gl_video *p) bool use_conv_gamma = p->conv_gamma != 1.0; bool use_const_luma = p->image_params.colorspace == MP_CSP_BT_2020_C; + enum mp_csp_trc gamma_fun = MP_CSP_TRC_NONE; + // Linear light scaling is only enabled when either color correction // option (3dlut or srgb) is enabled, otherwise scaling is done in the // source space. We also need to linearize for constant luminance systems. - bool convert_to_linear_gamma = - (!p->is_linear_rgb && use_cms) || use_const_luma; + if ((!p->is_linear_rgb && use_cms) || use_const_luma) { + // We just use the color level range to distinguish between PC + // content like images, which are most likely sRGB, and TV content + // like movies, which are most likely BT.2020 + if (p->image_params.colorlevels == MP_CSP_LEVELS_PC && !p->hwdec_active) { + // FIXME: I don't know if hwdec sets the color levels to PC or not, + // but let's avoid the bug just in case. + gamma_fun = MP_CSP_TRC_SRGB; + } else if (p->opts.approx_gamma) { + gamma_fun = MP_CSP_TRC_BT_2020_APPROX; + } else { + gamma_fun = MP_CSP_TRC_BT_2020_EXACT; + } + } // Figure out the right color spaces we need to convert, if any enum mp_csp_prim prim_src = p->image_params.primaries, prim_dest; @@ -944,9 +958,11 @@ static void compile_shaders(struct gl_video *p) char *header_osd = talloc_strdup(tmp, header); shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV_APPROX", - use_cms && p->opts.approx_gamma); + use_cms && gamma_fun == MP_CSP_TRC_BT_2020_APPROX); shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV_BT2020", - use_cms && !p->opts.approx_gamma); + use_cms && gamma_fun == MP_CSP_TRC_BT_2020_EXACT); + shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV_SRGB", + use_cms && gamma_fun == MP_CSP_TRC_SRGB); shader_def_opt(&header_osd, "USE_OSD_CMS_MATRIX", use_cms_matrix); shader_def_opt(&header_osd, "USE_OSD_3DLUT", p->use_lut_3d); // 3DLUT overrides SRGB @@ -981,9 +997,11 @@ static void compile_shaders(struct gl_video *p) shader_def_opt(&header_conv, "USE_CONV_GAMMA", use_conv_gamma); shader_def_opt(&header_conv, "USE_CONST_LUMA", use_const_luma); shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_APPROX", - convert_to_linear_gamma && p->opts.approx_gamma); + gamma_fun == MP_CSP_TRC_BT_2020_APPROX); shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_BT2020", - convert_to_linear_gamma && !p->opts.approx_gamma); + gamma_fun == MP_CSP_TRC_BT_2020_EXACT); + shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_SRGB", + gamma_fun == MP_CSP_TRC_SRGB); if (p->opts.alpha_mode > 0 && p->has_alpha && p->plane_count > 3) shader_def(&header_conv, "USE_ALPHA_PLANE", "3"); if (p->opts.alpha_mode == 2 && p->has_alpha) @@ -1019,7 +1037,7 @@ static void compile_shaders(struct gl_video *p) // Don't sample from input video textures before converting the input to // linear light. - if (use_input_gamma || use_conv_gamma || convert_to_linear_gamma) + if (use_input_gamma || use_conv_gamma || gamma_fun != MP_CSP_TRC_NONE) use_indirect = true; // It doesn't make sense to scale the chroma with cscale in the 1. scale diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl index bb6530295f..ff2a019b30 100644 --- a/video/out/gl_video_shaders.glsl +++ b/video/out/gl_video_shaders.glsl @@ -43,6 +43,12 @@ // Earlier GLSL doesn't support mix() with bvec #if __VERSION__ >= 130 +vec3 srgb_expand(vec3 v) +{ + return mix(v / 12.92, pow((v + vec3(0.055))/1.055, vec3(2.4)), + lessThanEqual(vec3(0.04045), v)); +} + vec3 srgb_compand(vec3 v) { return mix(v * 12.92, 1.055 * pow(v, vec3(1.0/2.4)) - 0.055, @@ -98,6 +104,9 @@ void main() { #ifdef USE_OSD_LINEAR_CONV_BT2020 color.rgb = bt2020_expand(color.rgb); #endif +#ifdef USE_OSD_LINEAR_CONV_SRGB + color.rgb = srgb_expand(color.rgb); +#endif #ifdef USE_OSD_CMS_MATRIX // Convert to the right target gamut first (to BT.709 for sRGB, // and to BT.2020 for 3DLUT). Normal clamping here as perceptually @@ -439,6 +448,13 @@ void main() { // ... and actual BT.2020 (two-part function) color = bt2020_expand(color); #endif +#ifdef USE_LINEAR_LIGHT_SRGB + // This is not needed for most sRGB content since we can use GL_SRGB to + // directly sample RGB texture in linear light, but for things which are + // also sRGB but in a different format (such as JPEG's YUV), we need + // to convert to linear light manually. + color = srgb_expand(color); +#endif #ifdef USE_CONST_LUMA // Calculate the green channel from the expanded RYcB // The BT.2020 specification says Yc = 0.2627*R + 0.6780*G + 0.0593*B