PR #25846 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25846 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25846.patch
Now that "area" is no longer a pseudo-alias, we can also replace the "resolved name" field by a pure function of the enum value. I decided to roll that cosmetic change into the filter fix because it depends on it anyways. >From aac195cdd8b92109a2f33298ff9c89f7143237aa Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 7 Nov 2026 21:24:53 +0100 Subject: [PATCH 1/6] swscale/filters: fix SWS_SCALE_AREA kernel This behaved like an anti-aliased point filter when downscaling, which is not the same as a true area filter. That should behave like a trapezoid, not a box, approaching a bilinear kernel (triangle filter) as the ratio approaches 1.0. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/filters.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/libswscale/filters.c b/libswscale/filters.c index fb54d2bbd8b..2b2c1455238 100644 --- a/libswscale/filters.c +++ b/libswscale/filters.c @@ -205,16 +205,6 @@ int ff_sws_filter_generate(void *log, const SwsFilterParams *params, stretch = 1.0 / ratio; } - if (scaler == SWS_SCALE_AREA) { - /** - * SWS_SCALE_AREA is a pseudo-filter that is equivalent to bilinear - * filtering for upscaling (since bilinear just evenly mixes samples - * according to the relative distance), and equivalent to (anti-aliased) - * point sampling for downscaling. - */ - scaler = ratio >= 1.0 ? SWS_SCALE_BILINEAR : SWS_SCALE_POINT; - } - SwsFilterFunction fun = filter_functions[scaler]; if (!fun.kernel) return AVERROR(EINVAL); @@ -230,8 +220,13 @@ int ff_sws_filter_generate(void *log, const SwsFilterParams *params, return AVERROR(EINVAL); } - if (fun.radius < 0.0) /* tunable width kernels like lanczos */ + if (scaler == SWS_SCALE_AREA) { + /* Above ratio 1.0, this behaves like bilinear */ + fun.params[0] = fmin(ratio, 1.0); + fun.radius = 0.5 + 0.5 * fun.params[0]; + } else if (fun.radius < 0.0) { /* tunable width kernels like lanczos */ fun.radius = fun.params[0]; + } double radius; switch (scaler) { @@ -241,6 +236,9 @@ int ff_sws_filter_generate(void *log, const SwsFilterParams *params, case SWS_SCALE_BILINEAR: radius = 1.0 - SWS_MAX_REDUCE_CUTOFF; break; + case SWS_SCALE_AREA: + radius = fun.radius - SWS_MAX_REDUCE_CUTOFF * fun.params[0]; + break; default: /* Numerically estimate radius of nontrivial or parametric kernels */ radius = est_filter_radius(&fun); @@ -322,6 +320,19 @@ av_unused static double box(double x, const double *params) return 1.0; } +/** + * Overlap between a destination pixel (unit box) and a source pixel of width + * params[0] <= 1, i.e. the convolution of two box filters (a trapezoid). + */ +static double area(double x, const double *params) +{ + const double w = params[0]; + const double edge = 0.5 - 0.5 * w; + if (x <= edge) + return 1.0; + return fmax(1.0 - (x - edge) / w, 0.0); +} + av_unused static double triangle(double x, const double *params) { return 1.0 - x; @@ -465,5 +476,5 @@ static const SwsFilterFunction filter_functions[SWS_SCALE_NB] = { [SWS_SCALE_SINC] = { "sinc", RADIUS_MAX, sinc }, [SWS_SCALE_LANCZOS] = { "lanczos", -1.0, sinc, sinc, .params = { 3.0 } }, [SWS_SCALE_SPLINE] = { "spline", RADIUS_MAX, spline }, - /* SWS_SCALE_AREA is a pseudo-filter, see code above */ + [SWS_SCALE_AREA] = { "area", 1.0, area }, }; -- 2.54.0 >From 578cddf1f0502483919c82ca5b67886b0cd5d1a4 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 7 Nov 2026 21:31:06 +0100 Subject: [PATCH 2/6] swscale/filters: add SwsFilterWeights.scaler Signed-off-by: Niklas Haas <[email protected]> --- libswscale/filters.c | 1 + libswscale/filters.h | 1 + 2 files changed, 2 insertions(+) diff --git a/libswscale/filters.c b/libswscale/filters.c index 2b2c1455238..4c878c54bb7 100644 --- a/libswscale/filters.c +++ b/libswscale/filters.c @@ -256,6 +256,7 @@ int ff_sws_filter_generate(void *log, const SwsFilterParams *params, filter = av_refstruct_alloc_ext(sizeof(*filter), 0, NULL, sws_filter_free); if (!filter) return AVERROR(ENOMEM); + filter->scaler = scaler; memcpy(filter->name, fun.name, sizeof(filter->name)); filter->src_size = params->src_size; filter->dst_size = params->dst_size; diff --git a/libswscale/filters.h b/libswscale/filters.h index a10e6f39640..5190dad1dfe 100644 --- a/libswscale/filters.h +++ b/libswscale/filters.h @@ -107,6 +107,7 @@ typedef struct SwsFilterWeights { /** * Copy of the parameters used to generate this filter, for reference. */ + SwsScaler scaler; /* SWS_SCALE_AUTO is resolved */ int src_size; int dst_size; double virtual_size; -- 2.54.0 >From c49f199eb20e320c123ac4e617ed44686b1c8247 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 7 Nov 2026 21:11:01 +0100 Subject: [PATCH 3/6] swscale/filters: add ff_sws_scaler_name() Signed-off-by: Niklas Haas <[email protected]> --- libswscale/filters.c | 18 ++++++++++++++++++ libswscale/filters.h | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/libswscale/filters.c b/libswscale/filters.c index 4c878c54bb7..19d4f1bb115 100644 --- a/libswscale/filters.c +++ b/libswscale/filters.c @@ -479,3 +479,21 @@ static const SwsFilterFunction filter_functions[SWS_SCALE_NB] = { [SWS_SCALE_SPLINE] = { "spline", RADIUS_MAX, spline }, [SWS_SCALE_AREA] = { "area", 1.0, area }, }; + +const char *ff_sws_scaler_name(SwsScaler scaler) +{ + switch (scaler) { + case SWS_SCALE_AUTO: return "auto"; + case SWS_SCALE_BILINEAR: return "bilinear"; + case SWS_SCALE_BICUBIC: return "bicubic"; + case SWS_SCALE_POINT: return "point"; + case SWS_SCALE_AREA: return "area"; + case SWS_SCALE_GAUSSIAN: return "gaussian"; + case SWS_SCALE_SINC: return "sinc"; + case SWS_SCALE_LANCZOS: return "lanczos"; + case SWS_SCALE_SPLINE: return "spline"; + case SWS_SCALE_NB: break; + } + + return NULL; +} diff --git a/libswscale/filters.h b/libswscale/filters.h index 5190dad1dfe..c28995484f6 100644 --- a/libswscale/filters.h +++ b/libswscale/filters.h @@ -41,6 +41,11 @@ enum { SWS_FILTER_SIZE_MAX = 256, }; +/** + * Return the friendly name of a given scaler enum, or NULL if invalid. + */ +const char *ff_sws_scaler_name(SwsScaler scaler); + /* Parameters for filter generation. */ typedef struct SwsFilterParams { /** -- 2.54.0 >From ba7dace794499986ab4fea250a66a5254e39fe36 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 7 Nov 2026 21:33:45 +0100 Subject: [PATCH 4/6] swscale/ops: use ff_sws_scaler_name() instead of SwsFilterWeights.name The latter field is about to be removed. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 07dd1aba482..26c9b22305a 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -948,7 +948,7 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op) break; const SwsFilterWeights *kernel = op->rw.filter.kernel; av_bprintf(bp, " + %d tap %s filter (%c)", - kernel->filter_size, kernel->name, + kernel->filter_size, ff_sws_scaler_name(kernel->scaler), op->rw.filter.op == SWS_OP_FILTER_H ? 'H' : 'V'); break; case SWS_OP_LSHIFT: @@ -1013,7 +1013,7 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op) const SwsFilterWeights *kernel = op->filter.kernel; av_bprintf(bp, "%-20s: %d -> %d %s (%d taps)", name, kernel->src_size, kernel->dst_size, - kernel->name, kernel->filter_size); + ff_sws_scaler_name(kernel->scaler), kernel->filter_size); break; } case SWS_OP_LUT_3D: -- 2.54.0 >From ffc56fb9d6c0caab4fb5f97feac9d788cbbeb998 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 7 Nov 2026 21:35:36 +0100 Subject: [PATCH 5/6] avfilter/vf_scale_cuda: use ff_sws_scaler_name() See previous commit. Signed-off-by: Niklas Haas <[email protected]> --- libavfilter/vf_scale_cuda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavfilter/vf_scale_cuda.c b/libavfilter/vf_scale_cuda.c index 55839acfc49..efa6c3f059e 100644 --- a/libavfilter/vf_scale_cuda.c +++ b/libavfilter/vf_scale_cuda.c @@ -1059,7 +1059,7 @@ static int cudascale_filter_init(AVFilterContext *ctx, goto fail; av_log(ctx, AV_LOG_VERBOSE, " using %d tap '%s' filter: %d -> %d\n", - f->filter_size, weights->name, src_size, dst_size); + f->filter_size, ff_sws_scaler_name(weights->scaler), src_size, dst_size); ret = 0; -- 2.54.0 >From da8caefc69499491a8befe58e0dbfb0c1eab299a Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 7 Nov 2026 21:36:50 +0100 Subject: [PATCH 6/6] swscale/filters: drop SwsFilterWeights.name field Redundant with the more flexible ff_sws_scaler_name(). Also drops the now-unused internal name[] field. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/filters.c | 22 ++++++++++------------ libswscale/filters.h | 1 - 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/libswscale/filters.c b/libswscale/filters.c index 19d4f1bb115..96e1308dfdb 100644 --- a/libswscale/filters.c +++ b/libswscale/filters.c @@ -38,7 +38,6 @@ typedef double (*SwsFilterKernel)(double x, const double *params); typedef struct SwsFilterFunction { - char name[16]; double radius; /* negative means resizable */ SwsFilterKernel kernel; SwsFilterKernel window; /* optional */ @@ -216,7 +215,7 @@ int ff_sws_filter_generate(void *log, const SwsFilterParams *params, if (!validate_params(&fun, scaler)) { av_log(log, AV_LOG_ERROR, "Invalid parameters for scaler %s: {%f, %f}\n", - fun.name, fun.params[0], fun.params[1]); + ff_sws_scaler_name(scaler), fun.params[0], fun.params[1]); return AVERROR(EINVAL); } @@ -257,7 +256,6 @@ int ff_sws_filter_generate(void *log, const SwsFilterParams *params, if (!filter) return AVERROR(ENOMEM); filter->scaler = scaler; - memcpy(filter->name, fun.name, sizeof(filter->name)); filter->src_size = params->src_size; filter->dst_size = params->dst_size; filter->virtual_size = virtual_size; @@ -267,7 +265,7 @@ int ff_sws_filter_generate(void *log, const SwsFilterParams *params, filter->sum_positive = SWS_FILTER_SCALE; av_log(log, AV_LOG_DEBUG, "Generating %s filter with %d taps (radius = %f)\n", - filter->name, filter->filter_size, radius); + ff_sws_scaler_name(scaler), filter->filter_size, radius); filter->num_weights = (size_t) params->dst_size * filter->filter_size; filter->weights = av_refstruct_allocz(filter->num_weights * sizeof(*filter->weights)); @@ -470,14 +468,14 @@ av_unused static double spline(double x, const double *params) } static const SwsFilterFunction filter_functions[SWS_SCALE_NB] = { - [SWS_SCALE_BILINEAR] = { "bilinear", 1.0, triangle }, - [SWS_SCALE_BICUBIC] = { "bicubic", 2.0, cubic, .params = { 0.0, 0.6 } }, - [SWS_SCALE_POINT] = { "point", 0.5, box }, - [SWS_SCALE_GAUSSIAN] = { "gaussian", 4.0, gaussian, .params = { 3.0 } }, - [SWS_SCALE_SINC] = { "sinc", RADIUS_MAX, sinc }, - [SWS_SCALE_LANCZOS] = { "lanczos", -1.0, sinc, sinc, .params = { 3.0 } }, - [SWS_SCALE_SPLINE] = { "spline", RADIUS_MAX, spline }, - [SWS_SCALE_AREA] = { "area", 1.0, area }, + [SWS_SCALE_BILINEAR] = { 1.0, triangle }, + [SWS_SCALE_BICUBIC] = { 2.0, cubic, .params = { 0.0, 0.6 } }, + [SWS_SCALE_POINT] = { 0.5, box }, + [SWS_SCALE_GAUSSIAN] = { 4.0, gaussian, .params = { 3.0 } }, + [SWS_SCALE_SINC] = { RADIUS_MAX, sinc }, + [SWS_SCALE_LANCZOS] = { -1.0, sinc, sinc, .params = { 3.0 } }, + [SWS_SCALE_SPLINE] = { RADIUS_MAX, spline }, + [SWS_SCALE_AREA] = { 1.0, area }, }; const char *ff_sws_scaler_name(SwsScaler scaler) diff --git a/libswscale/filters.h b/libswscale/filters.h index c28995484f6..55dd0979fd1 100644 --- a/libswscale/filters.h +++ b/libswscale/filters.h @@ -122,7 +122,6 @@ typedef struct SwsFilterWeights { * Extra metadata about the filter, used to inform the optimizer / range * tracker about the filter's behavior. */ - char name[16]; /* name of the configured filter kernel */ int sum_positive; /* (maximum) sum of all positive weights */ int sum_negative; /* (minimum) sum of all negative weights */ } SwsFilterWeights; -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
