aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/vo.rst14
-rw-r--r--video/out/gl_video.c43
-rw-r--r--video/out/gl_video.h3
-rw-r--r--video/out/gl_video_shaders.glsl11
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);