diff options
-rw-r--r-- | DOCS/man/vo.rst | 14 | ||||
-rw-r--r-- | video/out/gl_video.c | 43 | ||||
-rw-r--r-- | video/out/gl_video.h | 3 | ||||
-rw-r--r-- | video/out/gl_video_shaders.glsl | 11 |
4 files changed, 69 insertions, 2 deletions
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index c296e705be..0566fd6817 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -457,6 +457,18 @@ Available video output drivers are: This is automatically disabled for anamorphic video, because this feature doesn't work correctly with this. + ``sigmoid-upscaling`` + When upscaling in linear light, use a sigmoidal color transform + to avoid emphasizing ringing artifacts. + + ``sigmoid-center`` + The center of the sigmoid curve used for ``sigmoid-upscaling``, must + be a float between 0.0 and 1.0. Defaults to 0.75 if not specified. + + ``sigmoid-slope`` + The slope of the sigmoid curve used for ``sigmoid-upscaling``, must + be a float between 1.0 and 20.0. Defaults to 6.5 if not specified. + ``no-npot`` Force use of power-of-2 texture sizes. For debugging only. Borders will be distorted due to filtering. @@ -598,7 +610,7 @@ Available video output drivers are: This is equivalent to:: - --vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgba16:fancy-downscaling + --vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgba16:fancy-downscaling:sigmoid-upscaling Note that some cheaper LCDs do dithering that gravely interferes with ``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps. diff --git a/video/out/gl_video.c b/video/out/gl_video.c index 5b3fe7b34d..c6077c6ad2 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -181,6 +181,9 @@ struct gl_video { // state for luma (0) and chroma (1) scalers struct scaler scalers[2]; + // true if scaler is currently upscaling + bool upscaling; + struct mp_csp_equalizer video_eq; struct mp_image_params image_params; @@ -319,6 +322,8 @@ const struct gl_video_opts gl_video_opts_def = { .dither_size = 6, .fbo_format = GL_RGBA, .scale_sep = 1, + .sigmoid_center = 0.75, + .sigmoid_slope = 6.5, .scalers = { "bilinear", "bilinear" }, .scaler_params = {{NAN, NAN}, {NAN, NAN}}, .scaler_radius = {NAN, NAN}, @@ -333,6 +338,9 @@ const struct gl_video_opts gl_video_opts_hq_def = { .fbo_format = GL_RGBA16, .scale_sep = 1, .fancy_downscaling = 1, + .sigmoid_center = 0.75, + .sigmoid_slope = 6.5, + .sigmoid_upscaling = 1, .scalers = { "spline36", "bilinear" }, .scaler_params = {{NAN, NAN}, {NAN, NAN}}, .scaler_radius = {NAN, NAN}, @@ -364,6 +372,9 @@ const struct m_sub_options gl_video_conf = { OPT_FLOATRANGE("cradius", scaler_radius[1], 0, 1.0, 32.0), OPT_FLAG("scaler-resizes-only", scaler_resizes_only, 0), OPT_FLAG("fancy-downscaling", fancy_downscaling, 0), + OPT_FLAG("sigmoid-upscaling", sigmoid_upscaling, 0), + OPT_FLOATRANGE("sigmoid-center", sigmoid_center, 0, 0.0, 1.0), + OPT_FLOATRANGE("sigmoid-slope", sigmoid_slope, 0, 1.0, 20.0), OPT_FLAG("indirect", indirect, 0), OPT_FLAG("scale-sep", scale_sep, 0), OPT_CHOICE("fbo-format", fbo_format, 0, @@ -405,6 +416,7 @@ static void uninit_rendering(struct gl_video *p); static void delete_shaders(struct gl_video *p); static void check_gl_features(struct gl_video *p); static bool init_format(int fmt, struct gl_video *init); +static double get_scale_factor(struct gl_video *p); static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp, int n_channels) @@ -683,6 +695,21 @@ static void update_uniforms(struct gl_video *p, GLuint program) gl->Uniform1f(gl->GetUniformLocation(program, "conv_gamma"), p->conv_gamma); + // Coefficients for the sigmoidal transform are taken from the + // formula here: http://www.imagemagick.org/Usage/color_mods/#sigmoidal + float sig_center = p->opts.sigmoid_center; + float sig_slope = p->opts.sigmoid_slope; + + // This function needs to go through (0,0) and (1,1) so we compute the + // values at 1 and 0, and then scale/shift them, respectively. + float sig_offset = 1.0/(1+expf(sig_slope * sig_center)); + float sig_scale = 1.0/(1+expf(sig_slope * (sig_center-1))) - sig_offset; + + gl->Uniform1f(gl->GetUniformLocation(program, "sig_center"), sig_center); + gl->Uniform1f(gl->GetUniformLocation(program, "sig_slope"), sig_slope); + gl->Uniform1f(gl->GetUniformLocation(program, "sig_scale"), sig_scale); + gl->Uniform1f(gl->GetUniformLocation(program, "sig_offset"), sig_offset); + float gamma = p->opts.gamma ? p->opts.gamma : 1.0; gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"), 1.0 / (cparams.rgamma * gamma), @@ -1034,6 +1061,12 @@ static void compile_shaders(struct gl_video *p) gamma_fun = MP_CSP_TRC_BT_2020_EXACT; } + bool use_linear_light = gamma_fun != MP_CSP_TRC_NONE || p->is_linear_rgb; + + // Optionally transform to sigmoidal color space if requested, but only + // when upscaling in linear light + bool use_sigmoid = p->opts.sigmoid_upscaling && use_linear_light && p->upscaling; + // Figure out the right color spaces we need to convert, if any enum mp_csp_prim prim_src = p->image_params.primaries, prim_dest; if (use_cms) { @@ -1114,11 +1147,13 @@ static void compile_shaders(struct gl_video *p) gamma_fun == MP_CSP_TRC_BT_2020_EXACT); shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_SRGB", gamma_fun == MP_CSP_TRC_SRGB); + shader_def_opt(&header_conv, "USE_SIGMOID", use_sigmoid); 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_SIGMOID_INV", use_sigmoid); shader_def_opt(&header_final, "USE_GAMMA_POW", p->opts.gamma > 0); shader_def_opt(&header_final, "USE_CMS_MATRIX", use_cms_matrix); shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d); @@ -1146,7 +1181,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 || gamma_fun != MP_CSP_TRC_NONE) + if (use_input_gamma || use_conv_gamma || use_linear_light) use_indirect = true; // It doesn't make sense to scale the chroma with cscale in the 1. scale @@ -1849,6 +1884,12 @@ static void check_resize(struct gl_video *p) if (strcmp(p->scalers[n].name, expected_scaler(p, n)) != 0) need_scaler_reinit = true; } + if (p->upscaling != (get_scale_factor(p) > 1.0)) { + p->upscaling = !p->upscaling; + // Switching between upscaling and downscaling also requires sigmoid + // to be toggled + need_scaler_reinit |= p->opts.sigmoid_upscaling; + } if (need_scaler_reinit) { reinit_rendering(p); } else if (need_scaler_update) { diff --git a/video/out/gl_video.h b/video/out/gl_video.h index 1ee0ec9213..37c819ecdb 100644 --- a/video/out/gl_video.h +++ b/video/out/gl_video.h @@ -39,6 +39,9 @@ struct gl_video_opts { int approx_gamma; int scale_sep; int fancy_downscaling; + int sigmoid_upscaling; + float sigmoid_center; + float sigmoid_slope; int scaler_resizes_only; int npot; int pbo; diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl index 2f5940c26f..4037e42449 100644 --- a/video/out/gl_video_shaders.glsl +++ b/video/out/gl_video_shaders.glsl @@ -179,6 +179,10 @@ uniform mat2 dither_trafo; uniform vec3 inv_gamma; uniform float input_gamma; uniform float conv_gamma; +uniform float sig_center; +uniform float sig_slope; +uniform float sig_scale; +uniform float sig_offset; uniform float dither_quantization; uniform float dither_center; uniform float filter_param1_l; @@ -425,7 +429,14 @@ void main() { // The BT.2020 specification says Yc = 0.2627*R + 0.6780*G + 0.0593*B color.g = (color.g - 0.2627*color.r - 0.0593*color.b)/0.6780; #endif +#ifdef USE_SIGMOID + color = sig_center - log(1.0/(color * sig_scale + sig_offset) - 1.0)/sig_slope; +#endif // Image upscaling happens roughly here +#ifdef USE_SIGMOID_INV + // Inverse of USE_SIGMOID + color = (1.0/(1.0 + exp(sig_slope * (sig_center - color))) - sig_offset) / sig_scale; +#endif #ifdef USE_GAMMA_POW // User-defined gamma correction factor (via the gamma sub-option) color = pow(color, inv_gamma); |