diff options
-rw-r--r-- | DOCS/man/en/vo.rst | 26 | ||||
-rw-r--r-- | video/out/gl_lcms.c | 27 | ||||
-rw-r--r-- | video/out/gl_lcms.h | 1 | ||||
-rw-r--r-- | video/out/gl_video.c | 34 | ||||
-rw-r--r-- | video/out/gl_video.h | 1 | ||||
-rw-r--r-- | video/out/gl_video_shaders.glsl | 41 |
6 files changed, 69 insertions, 61 deletions
diff --git a/DOCS/man/en/vo.rst b/DOCS/man/en/vo.rst index 44b6e05723..4e4e08ee80 100644 --- a/DOCS/man/en/vo.rst +++ b/DOCS/man/en/vo.rst @@ -317,18 +317,14 @@ Available video output drivers are: by very few OpenGL cards. ``srgb`` - Enable gamma-correct scaling by working in linear light. This - makes use of sRGB textures and framebuffers. - This option forces the options ``indirect`` and ``gamma``. + Convert and color correct the output to sRGB before displaying it on + the screen. This option enables linear light scaling. It also forces + the options ``indirect`` and ``gamma``. - .. note:: - - for YUV colorspaces, gamma 1/0.45 (2.222) is assumed. RGB input is - always assumed to be in sRGB. - - This option is not really useful, as gamma-correct scaling has not much - influence on typical video playback. Most visible effect comes from - slightly different gamma. + This option is equivalent to using ``icc-profile`` with an sRGB ICC + profile, but it is implemented without a 3DLUT and does not require + LittleCMS 2. If both ``srgb`` and ``icc-profile`` are present, the + latter takes precedence, as they are somewhat redundant. ``pbo`` Enable use of PBOs. This is slightly faster, but can sometimes lead to @@ -449,7 +445,9 @@ Available video output drivers are: ``icc-profile=<file>`` Load an ICC profile and use it to transform linear RGB to screen output. - Needs LittleCMS2 support compiled in. + Needs LittleCMS2 support compiled in. This option overrides the ``srgb`` + property, as using both is somewhat redundant. It also enables linear + light scaling. ``icc-cache=<file>`` Store and load the 3D LUT created from the ICC profile in this file. @@ -467,7 +465,7 @@ Available video output drivers are: 3 absolute colorimetric - ``icc-approx-gamma`` + ``approx-gamma`` Approximate the actual BT.709 gamma function as a pure power curve of 1.95. A number of video editing programs and studios apparently use this for mastering instead of the true curve. Most notably, anything in the @@ -475,6 +473,8 @@ Available video output drivers are: compatible with it. It's a sound idea to try enabling this flag first when watching movies and shows to see if things look better that way. + This only affects the output when using either ``icc-profile`` or``srgb``. + ``3dlut-size=<r>x<g>x<b>`` Size of the 3D LUT generated from the ICC profile in each dimension. Default is 128x256x64. diff --git a/video/out/gl_lcms.c b/video/out/gl_lcms.c index 8c30ce3719..08f89b81d9 100644 --- a/video/out/gl_lcms.c +++ b/video/out/gl_lcms.c @@ -73,7 +73,6 @@ const struct m_sub_options mp_icc_conf = { OPT_STRING("icc-profile", profile, 0), OPT_STRING("icc-cache", cache, 0), OPT_INT("icc-intent", intent, 0), - OPT_FLAG("icc-approx-gamma", approx, 0), OPT_STRING_VALIDATE("3dlut-size", size_str, 0, validate_3dlut_size_opt), {0} }, @@ -130,8 +129,11 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, goto error_exit; char *cache_info = - talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d, approx=%d\n", - opts->intent, s_r, s_g, s_b, opts->approx); + // Gamma is included in the header to help uniquely identify it, + // because we may change the parameter in the future or make it + // customizable. + talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d, gamma=2.4", + opts->intent, s_r, s_g, s_b); // check cache if (opts->cache) { @@ -166,22 +168,9 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, .Blue = {0.15, 0.06, 1.0}, }; - cmsToneCurve *tonecurve; - if (opts->approx) { - /* Apple's CMS, among other programs that rely on it, uses 1.95 as a - faster approximation of this curve. It's not quite correct, but the - option is provided for compatibility with such incorrect clips. */ - tonecurve = cmsBuildGamma(NULL, 1.95); - } else { - /* Rec BT.709 defines the tone curve as: - V = 1.099 * L^0.45 - 0.099 for L >= 0.018 - V = 4.500 * L for L < 0.018 - - The 0.081 parameter comes from inserting 0.018 into the function */ - tonecurve = cmsBuildParametricToneCurve(NULL, 4, - (cmsFloat64Number[5]){1/0.45, 1/1.099, 0.099/1.099, 1/4.5, 0.081}); - } - + // 2.4 is arbitrarily used as a gamma compression factor for the 3DLUT, + // reducing artifacts due to rounding errors on wide gamut profiles + cmsToneCurve *tonecurve = cmsBuildGamma(NULL, 2.4); cmsHPROFILE vid_profile = cmsCreateRGBProfile(&d65, &bt709prim, (cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve}); cmsFreeToneCurve(tonecurve); diff --git a/video/out/gl_lcms.h b/video/out/gl_lcms.h index 4a8e0cfb6a..a579b78f43 100644 --- a/video/out/gl_lcms.h +++ b/video/out/gl_lcms.h @@ -8,7 +8,6 @@ struct mp_icc_opts { char *cache; char *size_str; int intent; - int approx; }; struct lut3d; diff --git a/video/out/gl_video.c b/video/out/gl_video.c index be418be461..976fa3b486 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -306,6 +306,7 @@ const struct m_sub_options gl_video_conf = { .opts = (m_option_t[]) { OPT_FLOATRANGE("gamma", gamma, 0, 0.0, 10.0), OPT_FLAG("srgb", srgb, 0), + OPT_FLAG("approx-gamma", approx_gamma, 0), OPT_FLAG("npot", npot, 0), OPT_FLAG("pbo", pbo, 0), OPT_CHOICE("stereo", stereo_mode, 0, @@ -835,10 +836,11 @@ static void compile_shaders(struct gl_video *p) shader_def_opt(&header, "USE_ALPHA", p->has_alpha); char *header_osd = talloc_strdup(tmp, header); - shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV", p->opts.srgb && - !p->use_lut_3d); + shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV", p->opts.srgb || + p->use_lut_3d); shader_def_opt(&header_osd, "USE_OSD_3DLUT", p->use_lut_3d); - shader_def_opt(&header_osd, "USE_OSD_SRGB", p->opts.srgb); + // 3DLUT overrides SRGB + shader_def_opt(&header_osd, "USE_OSD_SRGB", !p->use_lut_3d && p->opts.srgb); for (int n = 0; n < SUBBITMAP_COUNT; n++) { const char *name = osd_shaders[n]; @@ -861,14 +863,16 @@ static void compile_shaders(struct gl_video *p) 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; + bool use_input_gamma = p->input_gamma != 1.0; + bool use_conv_gamma = p->conv_gamma != 1.0; + + // Linear light scaling is only enabled when either color correction + // option (3dlut or srgb) is enabled, otherwise scaling is done in the + // source space. + bool convert_to_linear_gamma = !p->is_linear_rgb && (p->opts.srgb || p->use_lut_3d); if (p->image_format == IMGFMT_NV12 || p->image_format == IMGFMT_NV21) { shader_def(&header_conv, "USE_CONV", "CONV_NV12"); @@ -881,18 +885,20 @@ static void compile_shaders(struct gl_video *p) shader_def_opt(&header_conv, "USE_SWAP_UV", p->image_format == IMGFMT_NV21); shader_def_opt(&header_conv, "USE_YGRAY", p->is_yuv && !p->is_packed_yuv && p->plane_count == 1); - shader_def_opt(&header_conv, "USE_INPUT_GAMMA", convert_input_gamma); + shader_def_opt(&header_conv, "USE_INPUT_GAMMA", use_input_gamma); shader_def_opt(&header_conv, "USE_COLORMATRIX", !p->is_rgb); - shader_def_opt(&header_conv, "USE_CONV_GAMMA", convert_input_to_linear); + shader_def_opt(&header_conv, "USE_CONV_GAMMA", use_conv_gamma); + shader_def_opt(&header_conv, "USE_LINEAR_LIGHT", convert_to_linear_gamma); + shader_def_opt(&header_conv, "USE_APPROX_GAMMA", p->opts.approx_gamma); 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) shader_def(&header_conv, "USE_ALPHA_BLEND", "1"); - shader_def_opt(&header_final, "USE_LINEAR_CONV_INV", p->use_lut_3d); shader_def_opt(&header_final, "USE_GAMMA_POW", p->opts.gamma > 0); shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d); - shader_def_opt(&header_final, "USE_SRGB", p->opts.srgb); + // 3DLUT overrides SRGB + shader_def_opt(&header_final, "USE_SRGB", p->opts.srgb && !p->use_lut_3d); shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0); shader_def_opt(&header_final, "USE_TEMPORAL_DITHER", p->opts.temporal_dither); @@ -913,8 +919,8 @@ static void compile_shaders(struct gl_video *p) bool use_indirect = p->opts.indirect; // Don't sample from input video textures before converting the input to - // linear light. (Unneeded when sRGB textures are used.) - if (convert_input_gamma || convert_input_to_linear) + // linear light. + if (use_input_gamma || use_conv_gamma) 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.h b/video/out/gl_video.h index e997e88fb6..989ac3b008 100644 --- a/video/out/gl_video.h +++ b/video/out/gl_video.h @@ -33,6 +33,7 @@ struct gl_video_opts { int indirect; float gamma; int srgb; + int approx_gamma; int scale_sep; int fancy_downscaling; int scaler_resizes_only; diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl index cd7e051dcd..15bb600e61 100644 --- a/video/out/gl_video_shaders.glsl +++ b/video/out/gl_video_shaders.glsl @@ -82,11 +82,15 @@ void main() { color = vertex_color; #ifdef USE_OSD_LINEAR_CONV - // If no 3dlut is being used, we need to pull up to linear light for - // the sRGB function. *IF* 3dlut is used, we do not. + // Although we are not scaling in linear light, both 3DLUT and SRGB still + // operate on linear light inputs so we have to convert to it before + // either step can be applied. color.rgb = bt709_expand(color.rgb); + // NOTE: This always applies the true BT709, maybe we need to use + // approx-gamma here too? #endif #ifdef USE_OSD_3DLUT + color.rgb = pow(color.rgb, vec3(1/2.4)); // linear -> 2.4 3DLUT space color = vec4(texture3D(lut_3d, color.rgb).rgb, color.a); #endif #ifdef USE_OSD_SRGB @@ -369,35 +373,44 @@ void main() { color.gb = vec2(128.0/255.0); #endif #ifdef USE_INPUT_GAMMA + // Pre-colormatrix input gamma correction (eg. for MP_IMGFLAG_XYZ) color = pow(color, vec3(input_gamma)); #endif #ifdef USE_COLORMATRIX + // Conversion from Y'CbCr or other spaces to RGB color = mat3(colormatrix) * color + colormatrix[3]; color = clamp(color, 0, 1); #endif #ifdef USE_CONV_GAMMA + // Post-colormatrix converted gamma correction (eg. for MP_IMGFLAG_XYZ) 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 - // in the final stage. - color = pow(color, vec3(0.45)); +#ifdef USE_LINEAR_LIGHT + // If we are scaling in linear light (SRGB or 3DLUT option enabled), we + // expand our source colors before scaling +#ifdef USE_APPROX_GAMMA + // We differentiate between approximate BT.709 (gamma 1.95) ... + color = pow(color, vec3(1.95)); +#else + // ... and actual BT709 (two-part function) + color = bt709_expand(color); +#endif #endif + // Image upscaling happens roughly here #ifdef USE_GAMMA_POW + // User-defined gamma correction factor (via the gamma sub-option) color = pow(color, inv_gamma); #endif #ifdef USE_3DLUT + // For the 3DLUT we are arbitrarily using 2.4 as input gamma to reduce + // the amount of rounding errors, so we pull up to that space first and + // then pass it through the 3D texture. + color = pow(color, vec3(1/2.4)); color = texture3D(lut_3d, color).rgb; #endif #ifdef USE_SRGB - // Go from "linear" (0.45) to BT.709 to true linear to sRGB -#ifndef USE_3DLUT - // Unless we are using a 3DLUT, in which case USE_LINEAR_CONV_INV - // already triggered earlier. - color = pow(color, vec3(0.45)); -#endif - color = bt709_expand(color.rgb); - color.rgb = srgb_compand(color.rgb); + // Compand from the linear scaling gamma to the sRGB output gamma + color = srgb_compand(color.rgb); #endif #ifdef USE_DITHER vec2 dither_pos = gl_FragCoord.xy / dither_size; |