vo_opengl: separate kernel and window

This makes the core much more elegant, reusable, reconfigurable and also
allows us to more easily add aliases for specific configurations.

Furthermore, this lets us apply a generic blur factor / window function
to arbitrary filters, so we can finally "mix and match" in order to
fine-tune windowing functions.

A few notes are in order:

1. The current system for configuring scalers is ugly and rapidly
   getting unwieldy. I modified the man page to make it a bit more
   bearable, but long-term we have to do something about it; especially
   since..

2. There's currently no way to affect the blur factor or parameters of
   the window functions themselves. For example, I can't actually
   fine-tune the kaiser window's param1, since there's simply no way to
   do so in the current API - even though filter_kernels.c supports it
   just fine!

3. This removes some lesser used filters (especially those which are
   purely window functions to begin with). If anybody asks, you can get
   eg. the old behavior of scale=hanning by using
   scale=box:scale-window=hanning:scale-radius=1 (and yes, the result is
   just as terrible as that sounds - which is why nobody should have
   been using them in the first place).

4. This changes the semantics of the "triangle" scaler slightly - it now
   has an arbitrary radius. This can possibly produce weird results for
   people who were previously using scale-down=triangle, especially if
   in combination with scale-radius (for the usual upscaling). The
   correct fix for this is to use scale-down=bilinear_slow instead,
   which is an alias for triangle at radius 1.

In regards to the last point, in future I want to make it so that
filters have a filter-specific "preferred radius" (for the ones that
are arbitrarily tunable), once the configuration system for filters has
been redesigned (in particular in a way that will let us separate scale
and scale-down cleanly). That way, "triangle" can simply have the
preferred radius of 1 by default, while still being tunable. (Rather
than the default radius being hard-coded to 3 always)
This commit is contained in:
Niklas Haas 2015-03-25 04:40:28 +01:00
parent 00151e987d
commit 586dc5574f
5 changed files with 226 additions and 157 deletions

View File

@ -308,12 +308,16 @@ Available video output drivers are:
This filter corresponds to the old ``lanczos3`` alias if the default
radius is used, while ``lanczos2`` corresponds to a radius of 2.
(This filter is an alias for ``sinc``-windowed ``sinc``)
``ewa_lanczos``
Elliptic weighted average Lanczos scaling. Also known as Jinc.
Relatively slow, but very good quality. The radius can be
controlled with ``scale-radius``. Increasing the radius makes the
filter sharper but adds more ringing.
(This filter is an alias for ``jinc``-windowed ``jinc``)
``ewa_lanczossharp``
A slightly sharpened version of ewa_lanczos, preconfigured to use
an ideal radius and parameter. If your hardware can run it, this is
@ -344,37 +348,35 @@ Available video output drivers are:
Set filter parameters. Ignored if the filter is not tunable.
Currently, this affects the following filter parameters:
``kaiser``
Window parameter (``alpha``). Defaults to 6.33.
bcspline
Spline parameters (``B`` and ``C``). Defaults to 0.5 for both.
``mitchell``
Spline parameters (``B`` and ``C``). Defaults to 1/3 for both.
``gaussian``
gaussian
Scale parameter (``t``). Increasing this makes the result blurrier.
Defaults to 1.
``ewa_lanczos``, ``ewa_ginseng``, ``ewa_hanning``
Jinc function scaling factor (also known as a blur factor).
Decreasing this makes the result sharper, increasing it makes it
blurrier. Defaults to 1. Note that setting this too low (eg. 0.5)
leads to bad results. It's recommended to stay between 0.9 and 1.1.
``sharpen3``, ``sharpen5``
sharpen3, sharpen5
Sharpening strength. Increasing this makes the image sharper but
adds more ringing and aliasing. Defaults to 0.5.
``oversample``
oversample
Minimum distance to an edge before interpolation is used. Setting
this to 0 will always interpolate edges, whereas setting it to 0.5
will never interpolate, thus behaving as if the regular nearest
neighbour algorithm was used. Defaults to 0.0.
``scale-radius=<r>``
``scale-blur=<value>``
Kernel scaling factor (also known as a blur factor). Decreasing this
makes the result sharper, increasing it makes it blurrier (default 0).
If set to 0, the kernel's preferred blur factor is used. Note that
setting this too low (eg. 0.5) leads to bad results. It's generally
recommended to stick to values between 0.8 and 1.2.
``scale-radius=<value>``
Set radius for filters listed below, must be a float number between 1.0
and 16.0. Defaults to be 3.0 if not specified.
``sinc``, ``lanczos``, ``blackman``, ``gaussian`` and all EWA filters (eg. ``ewa_lanczos``)
``sinc`` and derivatives, ``jinc`` and derivatives, ``gaussian``, ``box`` and ``triangle``
Note that depending on filter implementation details and video scaling
ratio, the radius that actually being used might be different
@ -389,6 +391,11 @@ Available video output drivers are:
Note that this doesn't affect the special filters ``bilinear``,
``bicubic_fast`` or ``sharpen``.
``scale-window=<window>``
(Advanced users only) Choose a custom windowing function for the kernel.
Defaults to the filter's preferred window if unset. Use
``scale-window=help`` to get a list of supported windowing functions.
``scaler-resizes-only``
Disable the scaler if the video image is not resized. In that case,
``bilinear`` is used instead whatever is set with ``scale``. Bilinear
@ -469,26 +476,26 @@ Available video output drivers are:
``scale-down=<filter>``
Like ``scale``, but apply these filters on downscaling instead. If this
option is unset, the filter implied by ``scale`` will be applied.
option is unset, the filter implied by ``scale`` will be applied. Note
that this is also affected by the other options related to ``scale``,
ie. there is no ``scale-down-param1`` or similar.
``cscale-param1``, ``cscale-param2``, ``cscale-radius``, ``cscale-antiring``
Set filter parameters and radius for ``cscale``.
See ``scale-param1``, ``scale-param2``, ``scale-radius`` and
``scale-antiring``.
``tscale=<filter>``, ``tscale-param1``, ``tscale-param2``, ``tscale-antiring``
``tscale=<filter>``
The filter used for interpolating the temporal axis (frames). This is
only used if ``interpolation`` is enabled. The only valid choices
for ``tscale`` are separable convolution filters (use ``tscale=help``
to get a list). The other options (``tscale-param1`` etc.) are
analogous to their ``scale`` counterparts. The default is ``oversample``.
to get a list). The default is ``oversample``.
Note that the maximum supported filter radius is currently 3, and that
using filters with larger radius may introduce issues when pausing or
framestepping, proportional to the radius used. It is recommended to
stick to a radius of 1 or 2.
``cscale-radius``, ``tscale-radius``, ``cscale-blur``, ``tscale-blur``, etc.
Set filter parameters for ``cscale`` and ``tscale``, respectively.
See the corresponding options for ``scale``.
``linear-scaling``
Scale in linear light. This is automatically enabled if
``target-prim``, ``target-trc``, ``icc-profile`` or

View File

@ -39,13 +39,24 @@
#include "filter_kernels.h"
// NOTE: all filters are separable, symmetric, and are intended for use with
// a lookup table/texture.
// NOTE: all filters are designed for discrete convolution
const struct filter_window *mp_find_filter_window(const char *name)
{
if (!name)
return NULL;
for (const struct filter_window *w = mp_filter_windows; w->name; w++) {
if (strcmp(w->name, name) == 0)
return w;
}
return NULL;
}
const struct filter_kernel *mp_find_filter_kernel(const char *name)
{
for (const struct filter_kernel *k = mp_filter_kernels; k->name; k++) {
if (strcmp(k->name, name) == 0)
for (const struct filter_kernel *k = mp_filter_kernels; k->f.name; k++) {
if (strcmp(k->f.name, name) == 0)
return k;
}
return NULL;
@ -56,16 +67,22 @@ const struct filter_kernel *mp_find_filter_kernel(const char *name)
bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
double inv_scale)
{
assert(filter->radius > 0);
// polar filters are dependent only on the radius
assert(filter->f.radius > 0);
// Only downscaling requires widening the filter
filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
filter->f.radius *= filter->inv_scale;
// Polar filters are dependent solely on the radius
if (filter->polar) {
filter->f.radius = fmin(filter->f.radius, 16.0);
filter->size = 1;
// Safety precaution to avoid generating a gigantic shader
if (filter->f.radius > 16.0) {
filter->f.radius = 16.0;
return false;
}
return true;
}
// only downscaling requires widening the filter
filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
double support = filter->radius * filter->inv_scale;
int size = ceil(2.0 * support);
int size = ceil(2.0 * filter->f.radius);
// round up to smallest available size that's still large enough
if (size < sizes[0])
size = sizes[0];
@ -80,27 +97,42 @@ 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->inv_scale = filter->size / 2.0 / filter->radius;
filter->inv_scale *= (filter->size/2.0) / filter->f.radius;
return false;
}
}
// Sample from the blurred, windowed kernel. Note: The window is always
// stretched to the true radius, regardless of the filter blur/scale.
static double sample_filter(struct filter_kernel *filter,
struct filter_window *window, double x)
{
double bk = filter->f.blur > 0.0 ? filter->f.blur : 1.0;
double bw = window->blur > 0.0 ? window->blur : 1.0;
double c = fabs(x) / (filter->inv_scale * bk);
double w = window->weight ? window->weight(window, x/bw * window->radius
/ filter->f.radius)
: 1.0;
return c < filter->f.radius ? w * filter->f.weight(&filter->f, c) : 0.0;
}
// Calculate the 1D filtering kernel for N sample points.
// N = number of samples, which is filter->size
// The weights will be stored in out_w[0] to out_w[N - 1]
// f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1].
void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w)
static void mp_compute_weights(struct filter_kernel *filter,
struct filter_window *window,
double f, float *out_w)
{
assert(filter->size > 0);
double sum = 0;
for (int n = 0; n < filter->size; n++) {
double x = f - (n - filter->size / 2 + 1);
double c = fabs(x) / filter->inv_scale;
double w = c <= filter->radius ? filter->weight(filter, c) : 0;
double w = sample_filter(filter, window, x);
out_w[n] = w;
sum += w;
}
//normalize
// Normalize to preserve energy
for (int n = 0; n < filter->size; n++)
out_w[n] /= sum;
}
@ -109,47 +141,47 @@ void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w)
// interpreted as rectangular array of count * filter->size items.
void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array)
{
struct filter_window *window = &filter->w;
if (filter->polar) {
// Compute a 1D array indexed by radius
assert(filter->radius > 0);
for (int x = 0; x < count; x++) {
double r = x * filter->radius / (count - 1);
out_array[x] = r <= filter->radius ? filter->weight(filter, r) : 0;
double r = x * filter->f.radius / (count - 1);
out_array[x] = sample_filter(filter, window, r);
}
} else {
// Compute a 2D array indexed by subpixel position
for (int n = 0; n < count; n++) {
mp_compute_weights(filter, n / (double)(count - 1),
mp_compute_weights(filter, window, n / (double)(count - 1),
out_array + filter->size * n);
}
}
}
typedef struct filter_kernel kernel;
typedef struct filter_window params;
static double nearest(kernel *k, double x)
static double box(params *p, double x)
{
return x > 0.5 ? 0.0 : 1.0;
// This is mathematically 1.0 everywhere, the clipping is done implicitly
// based on the radius.
return 1.0;
}
static double triangle(kernel *k, double x)
static double triangle(params *p, double x)
{
if (fabs(x) > 1.0)
return 0.0;
return 1.0 - fabs(x);
return fmax(0.0, 1.0 - fabs(x / p->radius));
}
static double hanning(kernel *k, double x)
static double hanning(params *p, double x)
{
return 0.5 + 0.5 * cos(M_PI * x);
}
static double hamming(kernel *k, double x)
static double hamming(params *p, double x)
{
return 0.54 + 0.46 * cos(M_PI * x);
}
static double quadric(kernel *k, double x)
static double quadric(params *p, double x)
{
// NOTE: glumpy uses 0.75, AGG uses 0.5
if (x < 0.5)
@ -164,7 +196,7 @@ static double bc_pow3(double x)
return (x <= 0) ? 0 : x * x * x;
}
static double bicubic(kernel *k, double x)
static double bicubic(params *p, double x)
{
return (1.0/6.0) * ( bc_pow3(x + 2)
- 4 * bc_pow3(x + 1)
@ -184,19 +216,19 @@ static double bessel_i0(double epsilon, double x)
return sum;
}
static double kaiser(kernel *k, double x)
static double kaiser(params *p, double x)
{
double a = k->params[0];
double a = p->params[0];
double epsilon = 1e-12;
double i0a = 1 / bessel_i0(epsilon, a);
return bessel_i0(epsilon, a * sqrt(1 - x * x)) * i0a;
}
// Family of cubic B/C splines
static double cubic_bc(kernel *k, double x)
static double cubic_bc(params *p, double x)
{
double b = k->params[0];
double c = k->params[1];
double b = p->params[0];
double c = p->params[1];
double
p0 = (6.0 - 2.0 * b) / 6.0,
p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0,
@ -212,14 +244,14 @@ static double cubic_bc(kernel *k, double x)
return 0;
}
static double spline16(kernel *k, double x)
static double spline16(params *p, double x)
{
if (x < 1.0)
return ((x - 9.0/5.0 ) * x - 1.0/5.0 ) * x + 1.0;
return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1);
}
static double spline36(kernel *k, double x)
static double spline36(params *p, double x)
{
if(x < 1.0)
return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0;
@ -230,7 +262,7 @@ static double spline36(kernel *k, double x)
* (x - 2);
}
static double spline64(kernel *k, double x)
static double spline64(params *p, double x)
{
if (x < 1.0)
return ((49.0 / 41.0 * x - 6387.0 / 2911.0) * x - 3.0 / 2911.0) * x + 1.0;
@ -244,107 +276,84 @@ static double spline64(kernel *k, double x)
* (x - 3);
}
static double gaussian(kernel *k, double x)
static double gaussian(params *p, double x)
{
double p = k->params[0];
return pow(2.0, -(M_E / p) * x * x);
return pow(2.0, -(M_E / p->params[0]) * x * x);
}
static double sinc(kernel *k, double x)
static double sinc(params *p, double x)
{
if (x == 0.0)
if (fabs(x) < 1e-8)
return 1.0;
double pix = M_PI * x;
return sin(pix) / pix;
}
static double jinc(kernel *k, double x)
static double jinc(params *p, double x)
{
if (fabs(x) < 1e-8)
return 1.0;
double pix = M_PI * x / k->params[0]; // blur factor
double pix = M_PI * x;
return 2.0 * j1(pix) / pix;
}
static double lanczos(kernel *k, double x)
static double sphinx(params *p, double x)
{
double radius = k->size / 2;
if (x < -radius || x > radius)
return 0;
if (x == 0)
return 1;
double pix = M_PI * x;
return radius * sin(pix) * sin(pix / radius) / (pix * pix);
}
static double ewa_ginseng(kernel *k, double x)
{
// Note: This is EWA ginseng, aka sinc-windowed jinc.
// Not to be confused with tensor ginseng, aka jinc-windowed sinc.
double radius = k->radius;
if (fabs(x) >= radius)
return 0.0;
return jinc(k, x) * sinc(k, x / radius);
}
static double ewa_lanczos(kernel *k, double x)
{
double radius = k->radius;
if (fabs(x) >= radius)
return 0.0;
// First zero of the jinc function. We simply scale it to fit into the
// given radius.
double jinc_zero = 1.2196698912665045;
return jinc(k, x) * jinc(k, x * jinc_zero / radius);
}
static double ewa_hanning(kernel *k, double x)
{
double radius = k->radius;
if (fabs(x) >= radius)
return 0.0;
// Jinc windowed by the hanning window
return jinc(k, x) * hanning(k, x / radius);
}
static double blackman(kernel *k, double x)
{
double radius = k->size / 2;
if (x == 0.0)
if (fabs(x) < 1e-8)
return 1.0;
if (x > radius)
return 0.0;
x *= M_PI;
double xr = x / radius;
return (sin(x) / x) * (0.42 + 0.5 * cos(xr) + 0.08 * cos(2 * xr));
double pix = M_PI * x;
return 3.0 * (sin(pix) - pix * cos(pix)) / (pix * pix * pix);
}
const struct filter_kernel mp_filter_kernels[] = {
{"nearest", 0.5, nearest},
const struct filter_window mp_filter_windows[] = {
{"box", 1, box},
{"triangle", 1, triangle},
{"hanning", 1, hanning},
{"hamming", 1, hamming},
{"quadric", 1.5, quadric},
{"bicubic", 2, bicubic},
{"kaiser", 1, kaiser, .params = {6.33, NAN} },
{"catmull_rom", 2, cubic_bc, .params = {0.0, 0.5} },
{"mitchell", 2, cubic_bc, .params = {1.0/3.0, 1.0/3.0} },
{"hermite", 1, cubic_bc, .params = {0.0, 0.0} },
{"robidoux", 2, cubic_bc, .params = {0.3782, 0.3109}, .polar = true},
{"robidouxsharp", 2, cubic_bc, .params = {0.2620, 0.3690}, .polar = true},
{"spline16", 2, spline16},
{"spline36", 3, spline36},
{"spline64", 4, spline64},
{"gaussian", -1, gaussian, .params = {1.0, NAN} },
{"sinc", -1, sinc},
{"ewa_lanczos", -1, ewa_lanczos, .params = {1.0, NAN}, .polar = true},
{"ewa_hanning", -1, ewa_hanning, .params = {1.0, NAN}, .polar = true},
{"ewa_ginseng", -1, ewa_ginseng, .params = {1.0, NAN}, .polar = true},
// 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, ewa_lanczos,
.params = {0.9812505644269356, NAN}, .polar = true},
{"lanczos", -1, lanczos},
{"blackman", -1, blackman},
{"sinc", 1, sinc},
{"jinc", 1.2196698912665045, jinc},
{"sphinx", 1.4302966531242027, sphinx},
{0}
};
const struct filter_kernel mp_filter_kernels[] = {
// Spline filters
{{"spline16", 2, spline16}},
{{"spline36", 3, spline36}},
{{"spline64", 4, spline64}},
// Sinc filters
{{"sinc", -1, sinc}},
{{"lanczos", -1, sinc}, .window = "sinc"},
{{"ginseng", -1, sinc}, .window = "jinc"},
// Jinc filters
{{"jinc", -1, jinc}, .polar = true},
{{"ewa_lanczos", -1, jinc}, .polar = true, .window = "jinc"},
{{"ewa_hanning", -1, jinc}, .polar = true, .window = "hanning" },
{{"ewa_ginseng", -1, jinc}, .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},
.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},
.polar = true, .window = "jinc"},
// Cubic filters
{{"bicubic", 2, bicubic}},
{{"bcspline", 2, cubic_bc, .params = {0.5, 0.5} }},
{{"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 = {0.3782, 0.3109}}, .polar = true},
{{"robidouxsharp", 2, cubic_bc, .params = {0.2620, 0.3690}}, .polar = true},
// Miscalleaneous filters
{{"box", -1, box}},
{{"nearest", 0.5, box}},
{{"triangle", -1, triangle}},
{{"bilinear_slow", 1, triangle}},
{{"gaussian", -1, gaussian, .params = {1.0, NAN} }},
{{0}}
};

View File

@ -21,26 +21,34 @@
#include <stdbool.h>
struct filter_kernel {
struct filter_window {
const char *name;
double radius; // A negative value will use user specified radius instead.
double (*weight)(struct filter_kernel *kernel, double x);
double radius; // A negative value will use user specified radius instead.
double (*weight)(struct filter_window *k, double x);
double params[2]; // User-defined custom filter parameters. Not used by
// all filters
double blur; // Blur coefficient (sharpens or widens the filter)
};
// The filter params can be changed at runtime. Only used by some filters.
float params[2];
// Whether or not the filter uses polar coordinates
bool polar;
struct filter_kernel {
struct filter_window f; // the kernel itself
struct filter_window w; // window storage
// Constant values
const char *window; // default window
bool polar; // whether or not the filter uses polar coordinates
// The following values are set by mp_init_filter() at runtime.
int size; // number of coefficients (may depend on radius)
double inv_scale; // scale factor (<1.0 is upscale, >1.0 downscale)
};
extern const struct filter_window mp_filter_windows[];
extern const struct filter_kernel mp_filter_kernels[];
const struct filter_window *mp_find_filter_window(const char *name);
const struct filter_kernel *mp_find_filter_kernel(const char *name);
bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
double scale);
void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w);
void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array);
#endif /* MPLAYER_FILTER_KERNELS_H */

View File

@ -357,6 +357,9 @@ const struct gl_video_opts gl_video_opts_hq_def = {
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param);
static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param);
#define OPT_BASE_STRUCT struct gl_video_opts
const struct m_sub_options gl_video_conf = {
.opts = (const m_option_t[]) {
@ -387,6 +390,12 @@ const struct m_sub_options gl_video_conf = {
OPT_FLOAT("cscale-param2", scaler_params[1][1], 0),
OPT_FLOAT("tscale-param1", scaler_params[2][0], 0),
OPT_FLOAT("tscale-param2", scaler_params[2][1], 0),
OPT_FLOAT("scale-blur", scaler_blur[0], 0),
OPT_FLOAT("cscale-blur", scaler_blur[1], 0),
OPT_FLOAT("tscale-blur", scaler_blur[2], 0),
OPT_STRING_VALIDATE("scale-window", scaler_window[0], 0, validate_window_opt),
OPT_STRING_VALIDATE("cscale-window", scaler_window[1], 0, validate_window_opt),
OPT_STRING_VALIDATE("tscale-window", scaler_window[2], 0, validate_window_opt),
OPT_FLOATRANGE("scale-radius", scaler_radius[0], 0, 1.0, 16.0),
OPT_FLOATRANGE("cscale-radius", scaler_radius[1], 0, 1.0, 16.0),
OPT_FLOATRANGE("tscale-radius", scaler_radius[2], 0, 1.0, 3.0),
@ -893,6 +902,7 @@ static void reinit_scaler(struct gl_video *p, int scaler_unit, const char *name,
for (int n = 0; n < 2; n++)
scaler->params[n] = p->opts.scaler_params[scaler->index][n];
scaler->antiring = p->opts.scaler_antiring[scaler->index];
const struct filter_kernel *t_kernel = mp_find_filter_kernel(scaler->name);
if (!t_kernel)
@ -901,15 +911,24 @@ static void reinit_scaler(struct gl_video *p, int scaler_unit, const char *name,
scaler->kernel_storage = *t_kernel;
scaler->kernel = &scaler->kernel_storage;
const char *win = p->opts.scaler_window[scaler->index];
if (!win || !win[0])
win = t_kernel->window;
const struct filter_window *t_window = mp_find_filter_window(win);
if (t_window)
scaler->kernel->w = *t_window;
for (int n = 0; n < 2; n++) {
if (!isnan(scaler->params[n]))
scaler->kernel->params[n] = scaler->params[n];
scaler->kernel->f.params[n] = scaler->params[n];
}
scaler->antiring = p->opts.scaler_antiring[scaler->index];
float blur = p->opts.scaler_blur[scaler->index];
if (blur > 0.0)
scaler->kernel->f.blur = blur;
if (scaler->kernel->radius < 0)
scaler->kernel->radius = p->opts.scaler_radius[scaler->index];
if (scaler->kernel->f.radius < 0)
scaler->kernel->f.radius = p->opts.scaler_radius[scaler->index];
scaler->insufficient = !mp_init_filter(scaler->kernel, sizes, scale_factor);
@ -1064,7 +1083,7 @@ static void pass_sample_separated(struct gl_video *p, int src_tex,
static void pass_sample_polar(struct gl_video *p, struct scaler *scaler)
{
double radius = scaler->kernel->radius;
double radius = scaler->kernel->f.radius;
int bound = (int)ceil(radius);
bool use_ar = scaler->antiring > 0;
GLSL(vec4 color = vec4(0.0);)
@ -2491,7 +2510,7 @@ static const char *handle_scaler_opt(const char *name, bool tscale)
if (name && name[0]) {
const struct filter_kernel *kernel = mp_find_filter_kernel(name);
if (kernel && (!tscale || !kernel->polar))
return kernel->name;
return kernel->f.name;
for (const char *const *filter = tscale ? fixed_tscale_filters
: fixed_scale_filters;
@ -2520,7 +2539,7 @@ void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts,
if (queue_size && p->opts.interpolation && p->opts.scalers[2]) {
const struct filter_kernel *kernel = mp_find_filter_kernel(p->opts.scalers[2]);
if (kernel) {
double radius = kernel->radius;
double radius = kernel->f.radius;
radius = radius > 0 ? radius : p->opts.scaler_radius[2];
*queue_size = 50e3 * ceil(radius);
}
@ -2566,9 +2585,9 @@ static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
*filter; filter++) {
mp_info(log, " %s\n", *filter);
}
for (int n = 0; mp_filter_kernels[n].name; n++) {
for (int n = 0; mp_filter_kernels[n].f.name; n++) {
if (!tscale || !mp_filter_kernels[n].polar)
mp_info(log, " %s\n", mp_filter_kernels[n].name);
mp_info(log, " %s\n", mp_filter_kernels[n].f.name);
}
if (s[0])
mp_fatal(log, "No scaler named '%s' found!\n", s);
@ -2576,6 +2595,30 @@ static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
return r;
}
static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
{
char s[20] = {0};
int r = 1;
if (bstr_equals0(param, "help")) {
r = M_OPT_EXIT - 1;
} else {
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
const struct filter_window *window = mp_find_filter_window(s);
if (!window)
r = M_OPT_INVALID;
}
if (r < 1) {
mp_info(log, "Available windows:\n");
for (int n = 0; mp_filter_windows[n].name; n++)
mp_info(log, " %s\n", mp_filter_windows[n].name);
if (s[0])
mp_fatal(log, "No window named '%s' found!\n", s);
}
return r;
}
// Resize and redraw the contents of the window without further configuration.
// Intended to be used in situations where the frontend can't really be
// involved with reconfiguring the VO properly.

View File

@ -36,8 +36,10 @@ struct gl_video_opts {
int target_prim;
int target_trc;
float scaler_params[3][2];
float scaler_blur[3];
float scaler_radius[3];
float scaler_antiring[3];
char *scaler_window[3];
int linear_scaling;
int fancy_downscaling;
int sigmoid_upscaling;