This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 814f862832359165f7835d0cfa007b6ffd43a742
Author:     Niklas Haas <[email protected]>
AuthorDate: Fri Mar 6 19:19:28 2026 +0100
Commit:     Niklas Haas <[email protected]>
CommitDate: Sat Mar 28 18:50:14 2026 +0100

    swscale/graph: add scaling ops when required
    
    The question of whether to do vertical or horizontal scaling first is a 
tricky
    one. There are several valid philosophies:
    
    1. Prefer horizontal scaling on the smaller pixel size, since this lowers 
the
       cost of gather-based kernels.
    2. Prefer minimizing the number of total filter taps, i.e. minimizing the 
size
       of the intermediate image.
    3. Prefer minimizing the number of rows horizontal scaling is applied to.
    
    Empirically, I'm still not sure which approach is best overall, and it 
probably
    depends at least a bit on the exact filter kernels in use. But for now, I
    opted to implement approach 3, which seems to work well. I will re-evaluate
    this once the filter kernels are actually finalized.
    
    The 'scale' in 'libswscale' can now stand for 'scaling'.
    
    Sponsored-by: Sovereign Tech Fund
    Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/graph.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 80 insertions(+), 7 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index f65f338229..1c36ea3957 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -553,11 +553,75 @@ static int add_legacy_sws_pass(SwsGraph *graph, const 
SwsFormat *src,
     return init_legacy_subpass(graph, sws, input, output);
 }
 
-/*********************
- * Format conversion *
- *********************/
+/*********************************
+ * Format conversion and scaling *
+ *********************************/
 
 #if CONFIG_UNSTABLE
+static SwsScaler get_scaler_fallback(SwsContext *ctx)
+{
+    if (ctx->scaler != SWS_SCALE_AUTO)
+        return ctx->scaler;
+
+    /* Backwards compatibility with legacy flags API */
+    if (ctx->flags & SWS_BILINEAR) {
+        return SWS_SCALE_BILINEAR;
+    } else if (ctx->flags & (SWS_BICUBIC | SWS_BICUBLIN)) {
+        return SWS_SCALE_BICUBIC;
+    } else if (ctx->flags & SWS_POINT) {
+        return SWS_SCALE_POINT;
+    } else if (ctx->flags & SWS_AREA) {
+        return SWS_SCALE_AREA;
+    } else if (ctx->flags & SWS_GAUSS) {
+        return SWS_SCALE_GAUSSIAN;
+    } else if (ctx->flags & SWS_SINC) {
+        return SWS_SCALE_SINC;
+    } else if (ctx->flags & SWS_LANCZOS) {
+        return SWS_SCALE_LANCZOS;
+    } else if (ctx->flags & SWS_SPLINE) {
+        return SWS_SCALE_SPLINE;
+    } else {
+        return SWS_SCALE_AUTO;
+    }
+}
+
+static int add_filter(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
+                      SwsOpType filter, int src_size, int dst_size)
+{
+    if (src_size == dst_size)
+        return 0; /* no-op */
+
+    SwsFilterParams params = {
+        .scaler   = get_scaler_fallback(ctx),
+        .src_size = src_size,
+        .dst_size = dst_size,
+    };
+
+    for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++)
+        params.scaler_params[i] = ctx->scaler_params[i];
+
+    SwsFilterWeights *kernel;
+    int ret = ff_sws_filter_generate(ctx, &params, &kernel);
+    if (ret == AVERROR(ENOTSUP)) {
+        /* Filter size exceeds limit; cascade with geometric mean size */
+        int mean = sqrt((int64_t) src_size * dst_size);
+        if (mean == src_size || mean == dst_size)
+            return AVERROR_BUG; /* sanity, prevent infinite loop */
+        ret = add_filter(ctx, type, ops, filter, src_size, mean);
+        if (ret < 0)
+            return ret;
+        return add_filter(ctx, type, ops, filter, mean, dst_size);
+    } else if (ret < 0) {
+        return ret;
+    }
+
+    return ff_sws_op_list_append(ops, &(SwsOp) {
+        .type = type,
+        .op   = filter,
+        .filter.kernel = kernel,
+    });
+}
+
 static int add_convert_pass(SwsGraph *graph, const SwsFormat *src,
                             const SwsFormat *dst, SwsPass *input,
                             SwsPass **output)
@@ -572,10 +636,6 @@ static int add_convert_pass(SwsGraph *graph, const 
SwsFormat *src,
     if (!(ctx->flags & SWS_UNSTABLE))
         goto fail;
 
-    /* The new format conversion layer cannot scale for now */
-    if (src->width != dst->width || src->height != dst->height)
-        goto fail;
-
     /* The new code does not yet support alpha blending */
     if (src->desc->flags & AV_PIX_FMT_FLAG_ALPHA &&
         ctx->alpha_blend != SWS_ALPHA_BLEND_NONE)
@@ -593,6 +653,19 @@ static int add_convert_pass(SwsGraph *graph, const 
SwsFormat *src,
     ret = ff_sws_decode_colors(ctx, type, ops, src, &graph->incomplete);
     if (ret < 0)
         goto fail;
+
+    /**
+     * Always perform horizontal scaling first, since it's much more likely to
+     * benefit from small integer optimizations; we should maybe flip the order
+     * here if we're downscaling the vertical resolution by a lot, though.
+     */
+    ret = add_filter(ctx, type, ops, SWS_OP_FILTER_H, src->width, dst->width);
+    if (ret < 0)
+        goto fail;
+    ret = add_filter(ctx, type, ops, SWS_OP_FILTER_V, src->height, 
dst->height);
+    if (ret < 0)
+        goto fail;
+
     ret = ff_sws_encode_colors(ctx, type, ops, src, dst, &graph->incomplete);
     if (ret < 0)
         goto fail;

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to