diff --git a/DOCS/interface-changes/mks-scalers.txt b/DOCS/interface-changes/mks-scalers.txt new file mode 100644 index 0000000000000..99f3f4420193d --- /dev/null +++ b/DOCS/interface-changes/mks-scalers.txt @@ -0,0 +1 @@ +add `mks2013` and `mks2021` (Magic Kernel Sharp) scale filters diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index f9f1763c05bb3..aaf5ee8fb47fc 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -5920,6 +5920,17 @@ them. set to ``0.0`` and ``0.5`` respectively. This filter is sharper than ``mitchell``, but prone to ringing. + ``mks2013`` + Magic Kernel Sharp 2013. The quadratic B-spline ("magic kernel") + combined with a sharpening step, expressed as a single kernel with a + support of radius 2.5. See https://johncostella.com/magic/ for details. + + ``mks2021`` + Magic Kernel Sharp 2021. A refinement of ``mks2013`` with a flatter + frequency response (support of radius 4.5), making it closer to true + interpolation, at a slightly higher cost and with marginally more + ringing. + ``oversample`` A version of nearest neighbour that (naively) oversamples pixels, so that pixels overlapping edges get linearly interpolated instead of diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c index 4f4ec58d5add9..9b977f6a0f765 100644 --- a/video/out/filter_kernels.c +++ b/video/out/filter_kernels.c @@ -319,6 +319,36 @@ static double spline64(params *p, double x) } } +// Magic Kernel Sharp family: the quadratic B-spline ("magic kernel") +// See https://johncostella.com/magic/ +static double magic_sharp_2013(double x) +{ + // quadric(x) convolved with {-1/4, +3/2, -1/4} + if (x < 0.5) + return 17.0/16.0 - 7.0/4.0 * (x * x); + if (x < 1.5) + return (x * x) - 11.0/4.0 * x + 7.0/4.0; + if (x < 2.5) { + x -= 5.0/2.0; + return -1.0/8.0 * (x * x); + } + return 0.0; +} + +static double mks2013(params *p, double x) +{ + // Scaled to mks2013(0) == 1 + return 16.0/17.0 * magic_sharp_2013(x); +} + +static double mks2021(params *p, double x) +{ + // magic_sharp_2013(x) convolved with {+1/36, 0, +34/36, 0, +1/36}, + // scaled to mks2021(0) == 1 + return (magic_sharp_2013(fabs(x - 2.0)) + 34.0 * magic_sharp_2013(x) + + magic_sharp_2013(x + 2.0)) * 16.0/577.0; +} + static double gaussian(params *p, double x) { return exp(-2.0 * x * x / p->params[0]); @@ -420,6 +450,9 @@ const struct filter_kernel mp_filter_kernels[] = { {{SCALER_EWA_ROBIDOUXSHARP, 2,cubic_bc, .params = {6 / (13 + 7 * M_SQRT2), 7 / (2 + 12 * M_SQRT2)}}, .polar = true}, + // Magic Kernel Sharp filters, see https://johncostella.com/magic/ + {{SCALER_MKS2013, 2.5, mks2013}}, + {{SCALER_MKS2021, 4.5, mks2021}}, // Miscellaneous filters {{SCALER_BOX, 1, box, .resizable = true}}, {{SCALER_NEAREST, 0.5, box}}, diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h index 5d4e293576b9b..20222a313e33a 100644 --- a/video/out/filter_kernels.h +++ b/video/out/filter_kernels.h @@ -42,6 +42,8 @@ enum scaler_filter { SCALER_ROBIDOUXSHARP, SCALER_EWA_ROBIDOUX, SCALER_EWA_ROBIDOUXSHARP, + SCALER_MKS2013, + SCALER_MKS2021, SCALER_BOX, SCALER_NEAREST, SCALER_TRIANGLE, diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index 5150c900a782a..5f890a04c2b4f 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -298,6 +298,8 @@ struct gl_video { {"mitchell", SCALER_MITCHELL}, \ {"robidoux", SCALER_ROBIDOUX}, \ {"robidouxsharp", SCALER_ROBIDOUXSHARP}, \ + {"mks2013", SCALER_MKS2013}, \ + {"mks2021", SCALER_MKS2021}, \ {"box", SCALER_BOX}, \ {"nearest", SCALER_NEAREST}, \ {"triangle", SCALER_TRIANGLE}, \