diff options
Diffstat (limited to 'video/out/filter_kernels.c')
-rw-r--r-- | video/out/filter_kernels.c | 80 |
1 files changed, 44 insertions, 36 deletions
diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c index bfbd4e9465..d5df985395 100644 --- a/video/out/filter_kernels.c +++ b/video/out/filter_kernels.c @@ -5,10 +5,10 @@ * (https://github.com/glumpy/glumpy/blob/master/glumpy/library/build-spatial-filters.py) * * Also see: - * - http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h + * - https://sourceforge.net/p/agg/svn/HEAD/tree/agg-2.4/include/agg_image_filters.h * - Vapoursynth plugin fmtconv (WTFPL Licensed), which is based on * dither plugin for avisynth from the same author: - * https://github.com/vapoursynth/fmtconv/tree/master/src/fmtc + * https://gitlab.com/EleonoreMizo/fmtconv/-/tree/master/src/fmtc * - Paul Heckbert's "zoom" * - XBMC: ConvolutionKernels.cpp etc. * @@ -60,16 +60,19 @@ bool mp_init_filter(struct filter_kernel *filter, const int *sizes, double inv_scale) { assert(filter->f.radius > 0); + double blur = filter->f.blur > 0.0 ? filter->f.blur : 1.0; + filter->radius = blur * filter->f.radius; + // Only downscaling requires widening the filter filter->filter_scale = MPMAX(1.0, inv_scale); - double src_radius = filter->f.radius * filter->filter_scale; + double src_radius = filter->radius * filter->filter_scale; // Polar filters are dependent solely on the radius if (filter->polar) { filter->size = 1; // Not meaningful for EWA/polar scalers. // Safety precaution to avoid generating a gigantic shader if (src_radius > 16.0) { src_radius = 16.0; - filter->filter_scale = src_radius / filter->f.radius; + filter->filter_scale = src_radius / filter->radius; return false; } return true; @@ -89,7 +92,7 @@ bool mp_init_filter(struct filter_kernel *filter, const int *sizes, // largest filter available. This is incorrect, but better than refusing // to do anything. filter->size = cursize[-1]; - filter->filter_scale = (filter->size/2.0) / filter->f.radius; + filter->filter_scale = (filter->size/2.0) / filter->radius; return false; } } @@ -116,7 +119,7 @@ static double sample_window(struct filter_window *kernel, double x) static double sample_filter(struct filter_kernel *filter, double x) { // The window is always stretched to the entire kernel - double w = sample_window(&filter->w, x / filter->f.radius * filter->w.radius); + double w = sample_window(&filter->w, x / filter->radius * filter->w.radius); double k = w * sample_window(&filter->f, x); return k < 0 ? (1 - filter->clamp) * k : k; } @@ -158,10 +161,10 @@ void mp_compute_lut(struct filter_kernel *filter, int count, int stride, filter->radius_cutoff = 0.0; // Compute a 1D array indexed by radius for (int x = 0; x < count; x++) { - double r = x * filter->f.radius / (count - 1); + double r = x * filter->radius / (count - 1); out_array[x] = sample_filter(filter, r); - if (fabs(out_array[x]) > filter->value_cutoff) + if (fabs(out_array[x]) > 1e-3f) filter->radius_cutoff = r; } } else { @@ -187,6 +190,11 @@ static double triangle(params *p, double x) return fmax(0.0, 1.0 - fabs(x / p->radius)); } +static double cosine(params *p, double x) +{ + return cos(x); +} + static double hanning(params *p, double x) { return 0.5 + 0.5 * cos(M_PI * x); @@ -199,7 +207,7 @@ static double hamming(params *p, double x) static double quadric(params *p, double x) { - if (x < 0.75) { + if (x < 0.5) { return 0.75 - x * x; } else if (x < 1.5) { double t = x - 1.5; @@ -208,15 +216,6 @@ static double quadric(params *p, double x) return 0.0; } -#define POW3(x) ((x) <= 0 ? 0 : (x) * (x) * (x)) -static double bicubic(params *p, double x) -{ - return (1.0/6.0) * ( POW3(x + 2) - - 4 * POW3(x + 1) - + 6 * POW3(x) - - 4 * POW3(x - 1)); -} - static double bessel_i0(double x) { double s = 1.0; @@ -235,7 +234,7 @@ static double kaiser(params *p, double x) { if (x > 1) return 0; - double i0a = 1.0 / bessel_i0(p->params[1]); + double i0a = 1.0 / bessel_i0(p->params[0]); return bessel_i0(p->params[0] * sqrt(1.0 - x * x)) * i0a; } @@ -339,6 +338,7 @@ const struct filter_window mp_filter_windows[] = { {"box", 1, box}, {"triangle", 1, triangle}, {"bartlett", 1, triangle}, + {"cosine", M_PI_2, cosine}, {"hanning", 1, hanning}, {"tukey", 1, hanning, .taper = 0.5}, {"hamming", 1, hamming}, @@ -353,6 +353,9 @@ const struct filter_window mp_filter_windows[] = { {0} }; +#define JINC_R3 3.2383154841662362 +#define JINC_R4 4.2410628637960699 + const struct filter_kernel mp_filter_kernels[] = { // Spline filters {{"spline16", 2, spline16}}, @@ -363,25 +366,30 @@ const struct filter_kernel mp_filter_kernels[] = { {{"lanczos", 3, sinc, .resizable = true}, .window = "sinc"}, {{"ginseng", 3, sinc, .resizable = true}, .window = "jinc"}, // Jinc filters - {{"jinc", 3, jinc, .resizable = true}, .polar = true}, - {{"ewa_lanczos", 3, jinc, .resizable = true}, .polar = true, .window = "jinc"}, - {{"ewa_hanning", 3, jinc, .resizable = true}, .polar = true, .window = "hanning" }, - {{"ewa_ginseng", 3, jinc, .resizable = true}, .polar = true, .window = "sinc"}, - // Radius is based on the true jinc radius, slightly sharpened as per - // calculations by Nicolas Robidoux. Source: Imagemagick's magick/resize.c - {{"ewa_lanczossharp", 3.2383154841662362, jinc, .blur = 0.9812505644269356, - .resizable = true}, .polar = true, .window = "jinc"}, - // Similar to the above, but softened instead. This one makes hash patterns - // disappear completely. Blur determined by trial and error. - {{"ewa_lanczossoft", 3.2383154841662362, jinc, .blur = 1.015, - .resizable = true}, .polar = true, .window = "jinc"}, + {{"jinc", JINC_R3, jinc, .resizable = true}, .polar = true}, + {{"ewa_lanczos", JINC_R3, jinc, .resizable = true}, .polar = true, .window = "jinc"}, + {{"ewa_hanning", JINC_R3, jinc, .resizable = true}, .polar = true, .window = "hanning" }, + {{"ewa_ginseng", JINC_R3, jinc, .resizable = true}, .polar = true, .window = "sinc"}, + // Slightly sharpened to minimize the 1D step response error (to better + // preserve horizontal/vertical lines) + {{"ewa_lanczossharp", JINC_R3, jinc, .blur = 0.9812505837223707, .resizable = true}, + .polar = true, .window = "jinc"}, + // Similar to the above, but sharpened substantially to the point of + // minimizing the total impulse response error on an integer grid. Tends + // to preserve hash patterns well. Very sharp but rings a lot. + {{"ewa_lanczos4sharpest", JINC_R4, jinc, .blur = 0.8845120932605005, .resizable = true}, + .polar = true, .window = "jinc"}, + // Similar to the above, but softened instead, to make even/odd integer + // contributions exactly symmetrical. Designed to smooth out hash patterns. + {{"ewa_lanczossoft", JINC_R3, jinc, .blur = 1.0164667662867047, .resizable = true}, + .polar = true, .window = "jinc"}, // Very soft (blurred) hanning-windowed jinc; removes almost all aliasing. - // Blur paramater picked to match orthogonal and diagonal contributions - {{"haasnsoft", 3.2383154841662362, jinc, .blur = 1.11, .resizable = true}, - .polar = true, .window = "hanning"}, + // Blur parameter picked to match orthogonal and diagonal contributions + {{"haasnsoft", JINC_R3, jinc, .blur = 1.11, .resizable = true}, + .polar = true, .window = "hanning"}, // Cubic filters - {{"bicubic", 2, bicubic}}, - {{"bcspline", 2, cubic_bc, .params = {0.5, 0.5} }}, + {{"bicubic", 2, cubic_bc, .params = {1.0, 0.0} }}, + {{"hermite", 1, cubic_bc, .params = {0.0, 0.0} }}, {{"catmull_rom", 2, cubic_bc, .params = {0.0, 0.5} }}, {{"mitchell", 2, cubic_bc, .params = {1.0/3.0, 1.0/3.0} }}, {{"robidoux", 2, cubic_bc, .params = {12 / (19 + 9 * M_SQRT2), |