PR #22402 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22402 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22402.patch
The old practice of using `sws_flags` to control the scaler by setting a single flag is awkward and hard to extend; in particular, also preventing any separate selection of the scaler between the luma/chroma planes (expect for the BICUBLIN hack). This series replaces these flags by a proper enum, enabling independent selection of scalers, and paving the way for the new scaling code that will be introduced shortly. >From e87e8ae04faf408e899eb9190f8b7335bc0b87a1 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Thu, 19 Feb 2026 19:34:14 +0100 Subject: [PATCH 1/6] swscale: fix SWS_SPLINE documentation This was incorrectly inferred to be a Keys spline when the documentation was first added; but it's actually an "unwindowed" (in theory) natural cubic spline with C2 continuity everywhere, which is a completely different thing. (SWS_BICUBIC is closer to being a Keys spline) Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/swscale.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libswscale/swscale.h b/libswscale/swscale.h index 1cfd4068f9..1ee24c22ad 100644 --- a/libswscale/swscale.h +++ b/libswscale/swscale.h @@ -107,7 +107,7 @@ typedef enum SwsFlags { SWS_GAUSS = 1 << 7, ///< gaussian approximation SWS_SINC = 1 << 8, ///< unwindowed sinc SWS_LANCZOS = 1 << 9, ///< 3-tap sinc/sinc - SWS_SPLINE = 1 << 10, ///< cubic Keys spline + SWS_SPLINE = 1 << 10, ///< unwindowed natural cubic spline /** * Return an error on underspecified conversions. Without this flag, -- 2.52.0 >From 6b718ef1366eb08b6d036f489b1ebd3066f362b1 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 18 Feb 2026 16:44:01 +0100 Subject: [PATCH 2/6] swscale: don't hard code number of scaler params In case we ever need to increase this number in the future. I won't bother bumping the ABI version for this new #define, since it doesn't affect ABI, and I'm about to bump the ABI version in a following commit. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/graph.c | 4 ++-- libswscale/swscale.h | 3 ++- libswscale/utils.c | 16 +++++++--------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/libswscale/graph.c b/libswscale/graph.c index 81b8ef35d7..dd49c4cfb0 100644 --- a/libswscale/graph.c +++ b/libswscale/graph.c @@ -516,8 +516,8 @@ static int add_legacy_sws_pass(SwsGraph *graph, const SwsFormat *src, legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned); legacy_chr_pos(graph, &sws->dst_v_chr_pos, ctx->dst_v_chr_pos, &warned); - sws->scaler_params[0] = ctx->scaler_params[0]; - sws->scaler_params[1] = ctx->scaler_params[1]; + for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++) + sws->scaler_params[i] = ctx->scaler_params[i]; ret = sws_init_context(sws, NULL, NULL); if (ret < 0) { diff --git a/libswscale/swscale.h b/libswscale/swscale.h index 1ee24c22ad..043b4c15ef 100644 --- a/libswscale/swscale.h +++ b/libswscale/swscale.h @@ -204,7 +204,8 @@ typedef struct SwsContext { /** * Extra parameters for fine-tuning certain scalers. */ - double scaler_params[2]; +#define SWS_NUM_SCALER_PARAMS 2 + double scaler_params[SWS_NUM_SCALER_PARAMS]; /** * How many threads to use for processing, or 0 for automatic selection. diff --git a/libswscale/utils.c b/libswscale/utils.c index 8a3462c4a3..a5d620bde3 100644 --- a/libswscale/utils.c +++ b/libswscale/utils.c @@ -87,10 +87,8 @@ static SwsContext *alloc_set_opts(int srcW, int srcH, enum AVPixelFormat srcForm sws->src_format = srcFormat; sws->dst_format = dstFormat; - if (param) { - sws->scaler_params[0] = param[0]; - sws->scaler_params[1] = param[1]; - } + for (int i = 0; param && i < SWS_NUM_SCALER_PARAMS; i++) + sws->scaler_params[i] = param[i]; return sws; } @@ -200,7 +198,7 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, int dstW, int filterAlign, int one, int flags, int cpu_flags, SwsVector *srcFilter, SwsVector *dstFilter, - double param[2], int srcPos, int dstPos) + double param[SWS_NUM_SCALER_PARAMS], int srcPos, int dstPos) { int i; int filterSize; @@ -2357,8 +2355,8 @@ SwsContext *sws_getCachedContext(SwsContext *prev, int srcW, prev->dst_h == dstH && prev->dst_format == dstFormat && prev->flags == flags && - prev->scaler_params[0] == param[0] && - prev->scaler_params[1] == param[1])) { + !memcmp(prev->scaler_params, param, + sizeof(prev->scaler_params)))) { return prev; } @@ -2379,8 +2377,8 @@ SwsContext *sws_getCachedContext(SwsContext *prev, int srcW, sws->dst_h = dstH; sws->dst_format = dstFormat; sws->flags = flags; - sws->scaler_params[0] = param[0]; - sws->scaler_params[1] = param[1]; + for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++) + sws->scaler_params[i] = param[i]; if (sws_init_context(sws, srcFilter, dstFilter) < 0) sws_free_context(&sws); -- 2.52.0 >From 02ed982e4fe471270f938daf5aac553ffa0404fb Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 18 Feb 2026 18:11:59 +0100 Subject: [PATCH 3/6] swscale/utils: separate luma and chroma scaler selection Pre-requisite for adding support for configuring these independently. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/utils.c | 61 ++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/libswscale/utils.c b/libswscale/utils.c index a5d620bde3..c828393345 100644 --- a/libswscale/utils.c +++ b/libswscale/utils.c @@ -196,7 +196,7 @@ static const ScaleAlgorithm scale_algorithms[] = { static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, int *outFilterSize, int xInc, int srcW, int dstW, int filterAlign, int one, - int flags, int cpu_flags, + int scaler, int flags, int cpu_flags, SwsVector *srcFilter, SwsVector *dstFilter, double param[SWS_NUM_SCALER_PARAMS], int srcPos, int dstPos) { @@ -225,7 +225,7 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, filter[i * filterSize] = fone; (*filterPos)[i] = i; } - } else if (flags & SWS_POINT) { // lame looking point sampling mode + } else if (scaler == SWS_POINT) { // lame looking point sampling mode int i; int64_t xDstInSrc; filterSize = 1; @@ -240,8 +240,8 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, filter[i] = fone; xDstInSrc += xInc; } - } else if ((xInc <= (1 << 16) && (flags & SWS_AREA)) || - (flags & SWS_FAST_BILINEAR)) { // bilinear upscale + } else if ((xInc <= (1 << 16) && (scaler == SWS_AREA)) || + (scaler == SWS_FAST_BILINEAR)) { // bilinear upscale int i; int64_t xDstInSrc; filterSize = 2; @@ -269,12 +269,12 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, int sizeFactor = -1; for (i = 0; i < FF_ARRAY_ELEMS(scale_algorithms); i++) { - if (flags & scale_algorithms[i].flag && scale_algorithms[i].size_factor > 0) { + if (scaler == scale_algorithms[i].flag && scale_algorithms[i].size_factor > 0) { sizeFactor = scale_algorithms[i].size_factor; break; } } - if (flags & SWS_LANCZOS) + if (scaler == SWS_LANCZOS) sizeFactor = param[0] != SWS_PARAM_DEFAULT ? ceil(2 * param[0]) : 6; av_assert0(sizeFactor > 0); @@ -308,7 +308,7 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, d = d * dstW / srcW; floatd = d * (1.0 / (1 << 30)); - if (flags & SWS_BICUBIC) { + if (scaler == SWS_BICUBIC) { int64_t B = (param[0] != SWS_PARAM_DEFAULT ? param[0] : 0) * (1 << 24); int64_t C = (param[1] != SWS_PARAM_DEFAULT ? param[1] : 0.6) * (1 << 24); @@ -329,7 +329,7 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, (8 * B + 24 * C) * (1 << 30); } coeff /= (1LL<<54)/fone; - } else if (flags & SWS_X) { + } else if (scaler == SWS_X) { double A = param[0] != SWS_PARAM_DEFAULT ? param[0] : 1.0; double c; @@ -342,7 +342,7 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, else c = pow(c, A); coeff = (c * 0.5 + 0.5) * fone; - } else if (flags & SWS_AREA) { + } else if (scaler == SWS_AREA) { int64_t d2 = d - (1 << 29); if (d2 * xInc < -(1LL << (29 + 16))) coeff = 1.0 * (1LL << (30 + 16)); @@ -351,23 +351,23 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, else coeff = 0.0; coeff *= fone >> (30 + 16); - } else if (flags & SWS_GAUSS) { + } else if (scaler == SWS_GAUSS) { double p = param[0] != SWS_PARAM_DEFAULT ? param[0] : 3.0; coeff = exp2(-p * floatd * floatd) * fone; - } else if (flags & SWS_SINC) { + } else if (scaler == SWS_SINC) { coeff = (d ? sin(floatd * M_PI) / (floatd * M_PI) : 1.0) * fone; - } else if (flags & SWS_LANCZOS) { + } else if (scaler == SWS_LANCZOS) { double p = param[0] != SWS_PARAM_DEFAULT ? param[0] : 3.0; coeff = (d ? sin(floatd * M_PI) * sin(floatd * M_PI / p) / (floatd * floatd * M_PI * M_PI / p) : 1.0) * fone; if (floatd > p) coeff = 0; - } else if (flags & SWS_BILINEAR) { + } else if (scaler == SWS_BILINEAR) { coeff = (1 << 30) - d; if (coeff < 0) coeff = 0; coeff *= fone >> 30; - } else if (flags & SWS_SPLINE) { + } else if (scaler == SWS_SPLINE) { double p = -2.196152422706632; coeff = getSplineCoeff(1.0, 0.0, p, -p - 1.0, floatd) * fone; } else { @@ -1191,17 +1191,30 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, /* provide a default scaler if not set by caller */ if (!i) { if (dstW < srcW && dstH < srcH) - flags |= SWS_BICUBIC; + i = SWS_BICUBIC; else if (dstW > srcW && dstH > srcH) - flags |= SWS_BICUBIC; + i = SWS_BICUBIC; else - flags |= SWS_BICUBIC; + i = SWS_BICUBIC; + flags |= i; sws->flags = flags; } else if (i & (i - 1)) { av_log(c, AV_LOG_ERROR, "Exactly one scaler algorithm must be chosen, got %X\n", i); return AVERROR(EINVAL); } + + if (i == SWS_FAST_BILINEAR) { + if (srcW < 8 || dstW <= 8) { + i = SWS_BILINEAR; + flags ^= SWS_FAST_BILINEAR | i; + sws->flags = flags; + } + } + + int lum_scaler = i == SWS_BICUBLIN ? SWS_BICUBIC : i; + int chr_scaler = i == SWS_BICUBLIN ? SWS_BILINEAR : i; + /* sanity check */ if (srcW < 1 || srcH < 1 || dstW < 1 || dstH < 1) { /* FIXME check if these are enough and try to lower them after @@ -1210,12 +1223,6 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, srcW, srcH, dstW, dstH); return AVERROR(EINVAL); } - if (flags & SWS_FAST_BILINEAR) { - if (srcW < 8 || dstW <= 8) { - flags ^= SWS_FAST_BILINEAR | SWS_BILINEAR; - sws->flags = flags; - } - } if (!dstFilter) dstFilter = &dummyFilter; @@ -1680,7 +1687,7 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, if ((ret = initFilter(&c->hLumFilter, &c->hLumFilterPos, &c->hLumFilterSize, c->lumXInc, srcW, dstW, filterAlign, 1 << 14, - (flags & SWS_BICUBLIN) ? (flags | SWS_BICUBIC) : flags, + lum_scaler, flags, cpu_flags, srcFilter->lumH, dstFilter->lumH, sws->scaler_params, get_local_pos(c, 0, 0, 0), @@ -1691,7 +1698,7 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, if ((ret = initFilter(&c->hChrFilter, &c->hChrFilterPos, &c->hChrFilterSize, c->chrXInc, c->chrSrcW, c->chrDstW, filterAlign, 1 << 14, - (flags & SWS_BICUBLIN) ? (flags | SWS_BILINEAR) : flags, + chr_scaler, flags, cpu_flags, srcFilter->chrH, dstFilter->chrH, sws->scaler_params, get_local_pos(c, c->chrSrcHSubSample, sws->src_h_chr_pos, 0), @@ -1710,7 +1717,7 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, if ((ret = initFilter(&c->vLumFilter, &c->vLumFilterPos, &c->vLumFilterSize, c->lumYInc, srcH, dstH, filterAlign, (1 << 12), - (flags & SWS_BICUBLIN) ? (flags | SWS_BICUBIC) : flags, + lum_scaler, flags, cpu_flags, srcFilter->lumV, dstFilter->lumV, sws->scaler_params, get_local_pos(c, 0, 0, 1), @@ -1719,7 +1726,7 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, if ((ret = initFilter(&c->vChrFilter, &c->vChrFilterPos, &c->vChrFilterSize, c->chrYInc, c->chrSrcH, c->chrDstH, filterAlign, (1 << 12), - (flags & SWS_BICUBLIN) ? (flags | SWS_BILINEAR) : flags, + chr_scaler, flags, cpu_flags, srcFilter->chrV, dstFilter->chrV, sws->scaler_params, get_local_pos(c, c->chrSrcVSubSample, sws->src_v_chr_pos, 1), -- 2.52.0 >From f6e9cd5961a72175dbeaf3513a2ccf0966a0724e Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 18 Feb 2026 17:43:41 +0100 Subject: [PATCH 4/6] swscale: add enum SwsScaler, SwsContext.scaler to replace legacy flags Another step towards a cleaner API, with a cleaner separation of purposes. Also avoids wasting a whopping one third of the flag space on what really shouldn't have been a flag to begin with. I pre-emptively decided to separate the scaler selection between "scaler" and "scaler_sub", the latter defining what's used for things like 4:2:0 subsampling. This allows us to get rid of the awkwardly defined SWS_BICUBLIN flag, in favor of that just being the natural consequence of using a different scaler_sub. Lastly, I also decided to pre-emptively axe the poorly defined and questionable SWS_X scaler, which I doubt ever saw much use. The old flag is still available as a deprecated flag, anyhow. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- doc/APIchanges | 3 +++ libswscale/graph.c | 4 ++++ libswscale/options.c | 13 +++++++++++++ libswscale/swscale.h | 30 ++++++++++++++++++++++++++++++ libswscale/utils.c | 21 +++++++++++++++++++-- libswscale/version.h | 2 +- 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 88005bb28e..1057644b8b 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2025-03-28 API changes, most recent first: +2026-02-xx - xxxxxxxxxx - lsws 9.5.100 - swscale.h + Add enum SwsScaler, and SwsContext.scaler/scaler_sub. + 2026-02-xx - xxxxxxxxxx - lsws 9.4.100 - swscale.h Add sws_test_hw_format(). diff --git a/libswscale/graph.c b/libswscale/graph.c index dd49c4cfb0..27d0dafe56 100644 --- a/libswscale/graph.c +++ b/libswscale/graph.c @@ -494,6 +494,8 @@ static int add_legacy_sws_pass(SwsGraph *graph, const SwsFormat *src, sws->dither = ctx->dither; sws->alpha_blend = ctx->alpha_blend; sws->gamma_flag = ctx->gamma_flag; + sws->scaler = ctx->scaler; + sws->scaler_sub = ctx->scaler_sub; sws->src_w = src->width; sws->src_h = src->height; @@ -843,6 +845,8 @@ static int opts_equal(const SwsContext *c1, const SwsContext *c2) c1->dst_h_chr_pos == c2->dst_h_chr_pos && c1->dst_v_chr_pos == c2->dst_v_chr_pos && c1->intent == c2->intent && + c1->scaler == c2->scaler && + c1->scaler_sub == c2->scaler_sub && !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params)); } diff --git a/libswscale/options.c b/libswscale/options.c index 06e51dcfe9..004988488c 100644 --- a/libswscale/options.c +++ b/libswscale/options.c @@ -52,6 +52,19 @@ static const AVOption swscale_options[] = { { "error_diffusion", "error diffusion dither", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_ERROR_DIFFUSION}, .flags = VE, .unit = "sws_flags" }, { "unstable", "allow experimental new code", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_UNSTABLE }, .flags = VE, .unit = "sws_flags" }, + { "scaler", "set scaling algorithm", OFFSET(scaler), AV_OPT_TYPE_INT, { .i64 = SWS_SCALE_AUTO }, .flags = VE, .unit = "sws_scaler", .max = SWS_SCALE_NB - 1 }, + { "scaler_sub", "set subsampling algorithm", OFFSET(scaler_sub), AV_OPT_TYPE_INT, { .i64 = SWS_SCALE_AUTO }, .flags = VE, .unit = "sws_scaler", .max = SWS_SCALE_NB - 1 }, + { "auto", "automatic selection", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_AUTO }, .flags = VE, .unit = "sws_scaler" }, + { "bilinear", "bilinear filtering", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_BILINEAR }, .flags = VE, .unit = "sws_scaler" }, + { "bicubic", "2-tap cubic B-spline", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_BICUBIC }, .flags = VE, .unit = "sws_scaler" }, + { "point", "point sampling", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_POINT }, .flags = VE, .unit = "sws_scaler" }, + { "neighbor", "nearest neighbor", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_POINT }, .flags = VE, .unit = "sws_scaler" }, + { "area", "area averaging", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_AREA }, .flags = VE, .unit = "sws_scaler" }, + { "gaussian", "2-tap gaussian approximation", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_GAUSSIAN }, .flags = VE, .unit = "sws_scaler" }, + { "sinc", "unwindowed sinc", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_SINC }, .flags = VE, .unit = "sws_scaler" }, + { "lanczos", "3-tap sinc/sinc", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_LANCZOS }, .flags = VE, .unit = "sws_scaler" }, + { "spline", "2-tap cubic BC spline", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_SCALE_SPLINE }, .flags = VE, .unit = "sws_scaler" }, + { "param0", "scaler param 0", OFFSET(scaler_params[0]), AV_OPT_TYPE_DOUBLE, { .dbl = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, VE }, { "param1", "scaler param 1", OFFSET(scaler_params[1]), AV_OPT_TYPE_DOUBLE, { .dbl = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, VE }, diff --git a/libswscale/swscale.h b/libswscale/swscale.h index 043b4c15ef..14c7d4cc57 100644 --- a/libswscale/swscale.h +++ b/libswscale/swscale.h @@ -93,6 +93,20 @@ typedef enum SwsAlphaBlend { SWS_ALPHA_BLEND_MAX_ENUM = 0x7FFFFFFF, /* force size to 32 bits, not a valid blend mode */ } SwsAlphaBlend; +typedef enum SwsScaler { + SWS_SCALE_AUTO = 0, + SWS_SCALE_BILINEAR, ///< bilinear filtering + SWS_SCALE_BICUBIC, ///< 2-tap cubic BC-spline + SWS_SCALE_POINT, ///< nearest neighbor (point sampling) + SWS_SCALE_AREA, ///< area averaging + SWS_SCALE_GAUSSIAN, ///< 2-tap gaussian approximation + SWS_SCALE_SINC, ///< unwindowed sinc + SWS_SCALE_LANCZOS, ///< 3-tap sinc/sinc + SWS_SCALE_SPLINE, ///< unwindowned natural cubic spline + SWS_SCALE_NB, ///< not part of the ABI + SWS_SCALE_MAX_ENUM = 0x7FFFFFFF, ///< force size to 32 bits, not a valid filter type +} SwsScaler; + typedef enum SwsFlags { /** * Scaler selection options. Only one may be active at a time. @@ -249,6 +263,22 @@ typedef struct SwsContext { */ int intent; + /** + * Scaling filter. If set to something other than SWS_SCALE_AUTO, this will + * override the filter implied by `SwsContext.flags`. + * + * Note: Does not affect the legacy (stateful) API. + */ + SwsScaler scaler; + + /** + * Scaler used specifically for up/downsampling subsampled (chroma) planes. + * If set to something other than SWS_SCALE_AUTO, this will override the + * filter implied by `SwsContext.scaler`. Otherwise, the same filter + * will be used for both main scaling and chroma subsampling. + */ + SwsScaler scaler_sub; + /* Remember to add new fields to graph.c:opts_equal() */ } SwsContext; diff --git a/libswscale/utils.c b/libswscale/utils.c index c828393345..c28c8e0f02 100644 --- a/libswscale/utils.c +++ b/libswscale/utils.c @@ -1117,6 +1117,22 @@ static enum AVPixelFormat alphaless_fmt(enum AVPixelFormat fmt) } } +static int scaler_flag(SwsScaler scaler, int fallback) +{ + switch (scaler) { + case SWS_SCALE_BILINEAR: return SWS_BILINEAR; break; + case SWS_SCALE_BICUBIC: return SWS_BICUBIC; break; + case SWS_SCALE_POINT: return SWS_POINT; break; + case SWS_SCALE_AREA: return SWS_AREA; break; + case SWS_SCALE_GAUSSIAN: return SWS_GAUSS; break; + case SWS_SCALE_SINC: return SWS_SINC; break; + case SWS_SCALE_LANCZOS: return SWS_LANCZOS; break; + case SWS_SCALE_SPLINE: return SWS_SPLINE; break; + default: + return fallback; + } +} + av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, SwsFilter *dstFilter) { @@ -1212,8 +1228,9 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, } } - int lum_scaler = i == SWS_BICUBLIN ? SWS_BICUBIC : i; - int chr_scaler = i == SWS_BICUBLIN ? SWS_BILINEAR : i; + SwsScaler scaler_sub = sws->scaler_sub ? sws->scaler_sub : sws->scaler; + int lum_scaler = scaler_flag(sws->scaler, i == SWS_BICUBLIN ? SWS_BICUBIC : i); + int chr_scaler = scaler_flag(scaler_sub, i == SWS_BICUBLIN ? SWS_BILINEAR : i); /* sanity check */ if (srcW < 1 || srcH < 1 || dstW < 1 || dstH < 1) { diff --git a/libswscale/version.h b/libswscale/version.h index 12412bd538..c13db31c43 100644 --- a/libswscale/version.h +++ b/libswscale/version.h @@ -28,7 +28,7 @@ #include "version_major.h" -#define LIBSWSCALE_VERSION_MINOR 4 +#define LIBSWSCALE_VERSION_MINOR 5 #define LIBSWSCALE_VERSION_MICRO 100 #define LIBSWSCALE_VERSION_INT AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \ -- 2.52.0 >From 64a83de64e89494d7b608917ab08fd34ccc704b2 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 18 Feb 2026 17:51:12 +0100 Subject: [PATCH 5/6] swscale: mark scale-related SwsFlags as deprecated Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/swscale.h | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/libswscale/swscale.h b/libswscale/swscale.h index 14c7d4cc57..5532826a99 100644 --- a/libswscale/swscale.h +++ b/libswscale/swscale.h @@ -108,21 +108,6 @@ typedef enum SwsScaler { } SwsScaler; typedef enum SwsFlags { - /** - * Scaler selection options. Only one may be active at a time. - */ - SWS_FAST_BILINEAR = 1 << 0, ///< fast bilinear filtering - SWS_BILINEAR = 1 << 1, ///< bilinear filtering - SWS_BICUBIC = 1 << 2, ///< 2-tap cubic B-spline - SWS_X = 1 << 3, ///< experimental - SWS_POINT = 1 << 4, ///< nearest neighbor - SWS_AREA = 1 << 5, ///< area averaging - SWS_BICUBLIN = 1 << 6, ///< bicubic luma, bilinear chroma - SWS_GAUSS = 1 << 7, ///< gaussian approximation - SWS_SINC = 1 << 8, ///< unwindowed sinc - SWS_LANCZOS = 1 << 9, ///< 3-tap sinc/sinc - SWS_SPLINE = 1 << 10, ///< unwindowed natural cubic spline - /** * Return an error on underspecified conversions. Without this flag, * unspecified fields are defaulted to sensible values. @@ -183,6 +168,22 @@ typedef enum SwsFlags { */ SWS_DIRECT_BGR = 1 << 15, ///< This flag has no effect SWS_ERROR_DIFFUSION = 1 << 23, ///< Set `SwsContext.dither` instead + + /** + * Scaler selection options. Only one may be active at a time. + * Deprecated in favor of `SwsContext.scaler`. + */ + SWS_FAST_BILINEAR = 1 << 0, ///< fast bilinear filtering + SWS_BILINEAR = 1 << 1, ///< bilinear filtering + SWS_BICUBIC = 1 << 2, ///< 2-tap cubic B-spline + SWS_X = 1 << 3, ///< experimental + SWS_POINT = 1 << 4, ///< nearest neighbor + SWS_AREA = 1 << 5, ///< area averaging + SWS_BICUBLIN = 1 << 6, ///< bicubic luma, bilinear chroma + SWS_GAUSS = 1 << 7, ///< gaussian approximation + SWS_SINC = 1 << 8, ///< unwindowed sinc + SWS_LANCZOS = 1 << 9, ///< 3-tap sinc/sinc + SWS_SPLINE = 1 << 10, ///< unwindowed natural cubic spline } SwsFlags; typedef enum SwsIntent { -- 2.52.0 >From 0c9be01412e117deda2032d1bcaf7459c749a797 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Thu, 5 Mar 2026 18:52:38 +0100 Subject: [PATCH 6/6] doc/scaler: document new sws scaler flags And label the old ones as deprecated. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- doc/scaler.texi | 66 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/doc/scaler.texi b/doc/scaler.texi index 0c2b952395..aecc3ad248 100644 --- a/doc/scaler.texi +++ b/doc/scaler.texi @@ -11,48 +11,88 @@ For programmatic use, they can be set explicitly in the @table @option +@anchor{scaler} +@item scaler, scaler_sub +Choose the scaling algorithm to use. Default value is @samp{auto} for both. +It accepts the following values: + +@table @samp +@item auto +Aumotic choice. For @samp{scaler_sub}, this means the same algorithm as +@samp{scaler}. For @samp{scaler}, this defaults to the scaler flag selected +by @samp{sws_flags}. + +@item bilinear +Bilinear filter. (AKA triangle filter) + +@item bicubic +2-tap cubic BC-spline (AKA Mitchell-Netravali spline). The B and C parameters +can be configured by setting @code{param0} and @code{param1}, defaulting to +0.0 and 0.6 respectively. + +@item point, neighbor +Point sampling (AKA nearest neighbor). + +@item area +Area averaging. Equivalent to @samp{bilinear} for upscaling. + +@item gaussian +2-tap Gaussian filter approximation. The sharpness parameter can be configured +by setting @code{param0}, defaulting to 3.0. + +@item sinc +Unwindowed sinc filter. + +@item lanczos +Lanczos resampling (sinc windowed sinc). The number of filter taps can +be configured by setting @code{param0}, defaulting to 3. + +@item spline +Unwindowed natural bicubic spline. +@end table + @anchor{sws_flags} @item sws_flags Set the scaler flags. This is also used to set the scaling -algorithm. Only a single algorithm should be selected. Default -value is @samp{bicubic}. +algorithm, though this usage is deprecated in favor of setting @samp{scaler}. +Only a single algorithm may be selected. Default value is @samp{bicubic}. It accepts the following values: @table @samp @item fast_bilinear -Select fast bilinear scaling algorithm. +Select fast bilinear scaling algorithm. (Deprecated) @item bilinear -Select bilinear scaling algorithm. +Select bilinear scaling algorithm. (Deprecated) @item bicubic -Select bicubic scaling algorithm. +Select bicubic scaling algorithm. (Deprecated) @item experimental -Select experimental scaling algorithm. +Select experimental scaling algorithm. (Deprecated) @item neighbor -Select nearest neighbor rescaling algorithm. +Select nearest neighbor rescaling algorithm. (Deprecated) @item area -Select averaging area rescaling algorithm. +Select averaging area rescaling algorithm. (Deprecated) @item bicublin Select bicubic scaling algorithm for the luma component, bilinear for -chroma components. +chroma components. (Deprecated) @item gauss -Select Gaussian rescaling algorithm. +Select Gaussian rescaling algorithm. (Deprecated) @item sinc -Select sinc rescaling algorithm. +Select sinc rescaling algorithm. (Deprecated) @item lanczos Select Lanczos rescaling algorithm. The default width (alpha) is 3 and can be -changed by setting @code{param0}. +changed by setting @code{param0}. (Deprecated) @item spline -Select natural bicubic spline rescaling algorithm. +Select natural bicubic spline rescaling algorithm. (Deprecated) @item print_info Enable printing/debug logging. -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
