summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/en/vo.rst26
-rw-r--r--video/out/gl_lcms.c27
-rw-r--r--video/out/gl_lcms.h1
-rw-r--r--video/out/gl_video.c34
-rw-r--r--video/out/gl_video.h1
-rw-r--r--video/out/gl_video_shaders.glsl41
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;