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]

Reply via email to