PR #23590 opened by mkver
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23590
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23590.patch


>From 83d53ebb24a720d0471d66f2d79d47d0c7787817 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Wed, 24 Jun 2026 16:05:20 +0200
Subject: [PATCH 1/4] swscale/graph: Remove ff_sws_graph_create()

Unused since 9fe0ff3d56618c24d95fa4e832148bab54928f18.

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 doc/swscale-v2.txt |  5 +++--
 libswscale/graph.c | 17 -----------------
 libswscale/graph.h |  6 ------
 3 files changed, 3 insertions(+), 25 deletions(-)

diff --git a/doc/swscale-v2.txt b/doc/swscale-v2.txt
index aa2edceca7..a8bc71f426 100644
--- a/doc/swscale-v2.txt
+++ b/doc/swscale-v2.txt
@@ -13,8 +13,9 @@ threaded error diffusion pass following a multi-threaded 
scaling pass.
 
 SwsGraph is internally recreated whenever the image format, dimensions or
 settings change in any way. sws_scale_frame() is itself just a light-weight
-wrapper that runs ff_sws_graph_create() whenever the format changes, splits
-interlaced images into separate fields, and calls ff_sws_graph_run() on each.
+wrapper that runs ff_sws_graph_reinit() initially and on format changes,
+splits interlaced images into separate fields, and calls ff_sws_graph_run()
+on each.
 
 From the point of view of SwsGraph itself, all inputs are progressive.
 
diff --git a/libswscale/graph.c b/libswscale/graph.c
index 019f9207fb..b9d5df3b48 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -880,23 +880,6 @@ error:
     return ret;
 }
 
-int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat 
*src,
-                        SwsGraph **out_graph)
-{
-    SwsGraph *graph = ff_sws_graph_alloc();
-    if (!graph)
-        return AVERROR(ENOMEM);
-
-    int ret = ff_sws_graph_init(graph, ctx, dst, src);
-    if (ret < 0) {
-        ff_sws_graph_free(&graph);
-        return ret;
-    }
-
-    *out_graph = graph;
-    return 0;
-}
-
 void ff_sws_graph_rollback(SwsGraph *graph, int since_idx)
 {
     for (int i = since_idx; i < graph->num_passes; i++)
diff --git a/libswscale/graph.h b/libswscale/graph.h
index e467a43e45..68c6a0b9eb 100644
--- a/libswscale/graph.h
+++ b/libswscale/graph.h
@@ -167,12 +167,6 @@ SwsGraph *ff_sws_graph_alloc(void);
 int ff_sws_graph_init(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst,
                       const SwsFormat *src);
 
-/**
- * Allocate and initialize the filter graph. Returns 0 or a negative error.
- */
-int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat 
*src,
-                        SwsGraph **out_graph);
-
 
 /**
  * Allocate and add a new pass to the filter graph. Takes over ownership of
-- 
2.52.0


>From eeb99c0b9491d7221179ea82b5c174eea374ee00 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Wed, 24 Jun 2026 16:10:48 +0200
Subject: [PATCH 2/4] swscale/ops_chain: Disable
 ff_sws_setup_{clear,clamp,scale}

They are only used on aarch64.

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 libswscale/ops_chain.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c
index c3884a67ad..72b239172b 100644
--- a/libswscale/ops_chain.c
+++ b/libswscale/ops_chain.c
@@ -62,6 +62,7 @@ int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func,
 
 #define q2pixel(type, q) ((q).den ? (type) (q).num / (q).den : 0)
 
+#if ARCH_AARCH64
 int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out)
 {
     const SwsOp *op = params->op;
@@ -112,6 +113,7 @@ int ff_sws_setup_clear(const SwsImplParams *params, 
SwsImplResult *out)
 
     return 0;
 }
+#endif
 
 int ff_sws_uop_lookup(SwsContext *ctx, const SwsUOpTable *const tables[],
                       int num_tables, const SwsUOp *uop, const int block_size,
-- 
2.52.0


>From efff2c4ff0c8df445a9be18d68fdb5719a35122d Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Wed, 24 Jun 2026 16:15:24 +0200
Subject: [PATCH 3/4] swscale/uops: Move test-only stuff to sws_ops test

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 libswscale/tests/sws_ops.c | 370 +++++++++++++++++++++++++++++++++-
 libswscale/uops.c          | 394 +------------------------------------
 libswscale/uops.h          |  12 --
 libswscale/uops_list.h     |  60 ++++++
 4 files changed, 433 insertions(+), 403 deletions(-)
 create mode 100644 libswscale/uops_list.h

diff --git a/libswscale/tests/sws_ops.c b/libswscale/tests/sws_ops.c
index 0da59b473f..e2d01e76cf 100644
--- a/libswscale/tests/sws_ops.c
+++ b/libswscale/tests/sws_ops.c
@@ -20,9 +20,11 @@
 
 #include "libavutil/mem.h"
 #include "libavutil/pixdesc.h"
+#include "libavutil/tree.h"
 #include "libswscale/ops.h"
 #include "libswscale/ops_dispatch.h"
 #include "libswscale/ops_internal.h"
+#include "libswscale/uops_list.h"
 #include "libswscale/format.h"
 
 #ifdef _WIN32
@@ -32,6 +34,372 @@
 
 static int pass_idx;
 
+static const struct {
+    char full[16];
+    char prefix[8];
+} pixel_types[SWS_PIXEL_TYPE_NB] = {
+    [SWS_PIXEL_NONE] = { "SWS_PIXEL_NONE", ""     },
+    [SWS_PIXEL_U8]   = { "SWS_PIXEL_U8",   "U8_"  },
+    [SWS_PIXEL_U16]  = { "SWS_PIXEL_U16",  "U16_" },
+    [SWS_PIXEL_U32]  = { "SWS_PIXEL_U32",  "U32_" },
+    [SWS_PIXEL_F32]  = { "SWS_PIXEL_F32",  "F32_" },
+};
+
+static const struct {
+    char full[32];
+    char abbr[32];
+    char macro[32];
+} uop_names[SWS_UOP_TYPE_NB] = {
+#define UOP_NAME(OP, ABBR) [SWS_UOP_##OP] = { "SWS_UOP_" #OP, ABBR, #OP },
+    UOPS_LIST(UOP_NAME)
+};
+
+static int generate_entry_struct(void *opaque, void *key)
+{
+    const SwsUOp *ref = opaque;
+    const SwsUOp *uop = key;
+    AVBPrint *bp = ref->data.opaque;
+    char name[SWS_UOP_NAME_MAX];
+    ff_sws_uop_name(uop, name);
+    av_bprintf(bp, " \\\n    MACRO(__VA_ARGS__, %-40s", name);
+    av_bprintf(bp, ", .type = %-13s, .uop = %-24s, .mask = 0x%x",
+               pixel_types[uop->type].full, uop_names[uop->uop].full, 
uop->mask);
+
+    const SwsUOpParams *par = &uop->par;
+    switch (uop->uop) {
+    case SWS_UOP_READ_PLANAR_FH:
+    case SWS_UOP_READ_PLANAR_FV:
+    case SWS_UOP_READ_PLANAR_FV_FMA:
+        av_bprintf(bp, ", .par.filter.type = %s", 
pixel_types[par->filter.type].full);
+        break;
+    case SWS_UOP_LSHIFT:
+    case SWS_UOP_RSHIFT:
+        av_bprintf(bp, ", .par.shift.amount = %u", par->shift.amount);
+        break;
+    case SWS_UOP_PERMUTE:
+    case SWS_UOP_COPY:
+        av_bprintf(bp, ", .par.swizzle.in = {%d, %d, %d, %d}",
+                   par->swizzle.in[0], par->swizzle.in[1],
+                   par->swizzle.in[2], par->swizzle.in[3]);
+        break;
+    case SWS_UOP_MOVE:
+        av_bprintf(bp, ", .par.move.num_moves = %d", par->move.num_moves);
+        av_bprintf(bp, ", .par.move.dst = {%d, %d, %d, %d, %d, %d}",
+                   par->move.dst[0], par->move.dst[1], par->move.dst[2],
+                   par->move.dst[3], par->move.dst[4], par->move.dst[5]);
+        av_bprintf(bp, ", .par.move.src = {%d, %d, %d, %d, %d, %d}",
+                   par->move.src[0], par->move.src[1], par->move.src[2],
+                   par->move.src[3], par->move.src[4], par->move.src[5]);
+        break;
+    case SWS_UOP_PACK:
+    case SWS_UOP_UNPACK:
+        av_bprintf(bp, ", .par.pack.pattern = {%d, %d, %d, %d}",
+                   par->pack.pattern[0], par->pack.pattern[1],
+                   par->pack.pattern[2], par->pack.pattern[3]);
+        break;
+    case SWS_UOP_CLEAR:
+        av_bprintf(bp, ", .par.clear.one = 0x%x, .par.clear.zero = 0x%x",
+                   par->clear.one, par->clear.zero);
+        break;
+    case SWS_UOP_LINEAR:
+    case SWS_UOP_LINEAR_FMA:
+        av_bprintf(bp, ", .par.lin.one = 0x%x, .par.lin.zero = 0x%x",
+                   par->lin.one, par->lin.zero);
+        if (uop->uop == SWS_UOP_LINEAR_FMA)
+            av_bprintf(bp, ", .par.lin.exact = 0x%x", par->lin.exact);
+        break;
+    case SWS_UOP_DITHER:
+        av_bprintf(bp, ", .par.dither = { .y_offset = {%u, %u, %u, %u}, 
.size_log2 = %u }",
+                   par->dither.y_offset[0], par->dither.y_offset[1],
+                   par->dither.y_offset[2], par->dither.y_offset[3],
+                   par->dither.size_log2);
+        break;
+    }
+
+    av_bprintf(bp, ")");
+    return 0;
+}
+
+static int generate_entry_args(void *opaque, void *key)
+{
+    const SwsUOp *ref = opaque;
+    const SwsUOp *uop = key;
+    AVBPrint *bp = ref->data.opaque;
+    char name[SWS_UOP_NAME_MAX];
+    ff_sws_uop_name(uop, name);
+    av_bprintf(bp, " \\\n    MACRO(__VA_ARGS__, %-40s, %-13s, %-24s, 0x%x",
+               name, pixel_types[uop->type].full, uop_names[uop->uop].full, 
uop->mask);
+
+    const SwsUOpParams *par = &uop->par;
+    switch (uop->uop) {
+    case SWS_UOP_READ_PLANAR_FH:
+    case SWS_UOP_READ_PLANAR_FV:
+    case SWS_UOP_READ_PLANAR_FV_FMA:
+        av_bprintf(bp, ", %s", pixel_types[par->filter.type].full);
+        break;
+    case SWS_UOP_LSHIFT:
+    case SWS_UOP_RSHIFT:
+        av_bprintf(bp, ", %u", par->shift.amount);
+        break;
+    case SWS_UOP_PERMUTE:
+    case SWS_UOP_COPY:
+        av_bprintf(bp, ", %d, %d, %d, %d",
+                   par->swizzle.in[0], par->swizzle.in[1],
+                   par->swizzle.in[2], par->swizzle.in[3]);
+        break;
+    case SWS_UOP_MOVE:
+        av_bprintf(bp, ", %d", par->move.num_moves);
+        av_bprintf(bp, ", %d, %d, %d, %d, %d, %d",
+                   par->move.dst[0], par->move.dst[1], par->move.dst[2],
+                   par->move.dst[3], par->move.dst[4], par->move.dst[5]);
+        av_bprintf(bp, ", %d, %d, %d, %d, %d, %d",
+                   par->move.src[0], par->move.src[1], par->move.src[2],
+                   par->move.src[3], par->move.src[4], par->move.src[5]);
+        break;
+    case SWS_UOP_PACK:
+    case SWS_UOP_UNPACK:
+        av_bprintf(bp, ", %d, %d, %d, %d",
+                   par->pack.pattern[0], par->pack.pattern[1],
+                   par->pack.pattern[2], par->pack.pattern[3]);
+        break;
+    case SWS_UOP_CLEAR:
+        av_bprintf(bp, ", 0x%05x, 0x%05x", par->clear.one, par->clear.zero);
+        break;
+    case SWS_UOP_LINEAR:
+    case SWS_UOP_LINEAR_FMA:
+        av_bprintf(bp, ", 0x%05x, 0x%05x", par->lin.one, par->lin.zero);
+        if (uop->uop == SWS_UOP_LINEAR_FMA)
+            av_bprintf(bp, ", 0x%05x", par->lin.exact);
+        break;
+    case SWS_UOP_DITHER:
+        av_bprintf(bp, ", %u, %u, %u, %u, %u",
+                   par->dither.y_offset[0], par->dither.y_offset[1],
+                   par->dither.y_offset[2], par->dither.y_offset[3],
+                   par->dither.size_log2);
+        break;
+    }
+
+    av_bprintf(bp, ")");
+    return 0;
+}
+
+static int register_uop(struct AVTreeNode **root, const SwsUOp *uop)
+{
+    SwsUOp *key = av_memdup(uop, sizeof(*uop));
+    if (!key)
+        return AVERROR(ENOMEM);
+    memset(&key->data, 0, sizeof(key->data));
+
+    struct AVTreeNode *node = av_tree_node_alloc();
+    if (!node) {
+        av_free(key);
+        return AVERROR(ENOMEM);
+    }
+
+    av_tree_insert(root, key, ff_sws_uop_cmp_v, &node);
+    if (node) {
+        av_free(node);
+        av_free(key);
+    }
+    return 0;
+}
+
+static int register_flags(SwsContext *ctx, const SwsOpList *ops, SwsUOpFlags 
flags)
+{
+    SwsUOpList *uops = ff_sws_uop_list_alloc();
+    if (!uops)
+        return AVERROR(ENOMEM);
+
+    int ret = ff_sws_ops_translate(ctx, ops, flags, uops);
+    if (ret < 0)
+        goto fail;
+
+    struct AVTreeNode **root = ctx->opaque;
+    for (int i = 0; i < uops->num_ops; i++) {
+        ret = register_uop(root, &uops->ops[i]);
+        if (ret < 0)
+            goto fail;
+    }
+
+fail:
+    ff_sws_uop_list_free(&uops);
+    return ret;
+}
+
+static const SwsUOpFlags uop_flags[] = {
+    0,
+    SWS_UOP_FLAG_FMA | SWS_UOP_FLAG_MOVE, /* x86 backend */
+};
+
+static int register_uops(SwsContext *ctx, const SwsOpList *ops,
+                         SwsCompiledOp *out)
+{
+    for (int i = 0; i < FF_ARRAY_ELEMS(uop_flags); i++) {
+        int ret = register_flags(ctx, ops, uop_flags[i]);
+        if (ret < 0)
+            return ret;
+    }
+
+    *out = (SwsCompiledOp) {0}; /* dummy value, will be immediately freed */
+    return 0;
+}
+
+/* Dummy backend that just registers all seen uops */
+static const SwsOpBackend backend_uops = {
+    .name    = "uops_gen",
+    .compile = register_uops,
+};
+
+static int register_all_uops(SwsContext *ctx, void *graph, SwsOpList *ops)
+{
+    /* ff_sws_compile_pass() takes over ownership of `ops` */
+    SwsOpList *copy = ff_sws_op_list_duplicate(ops);
+    if (!copy)
+        return AVERROR(ENOMEM);
+
+    const int flags = SWS_OP_FLAG_DRY_RUN | SWS_OP_FLAG_SPLIT_MEMCPY;
+    return ff_sws_compile_pass(graph, &backend_uops, &copy, flags, NULL, NULL);
+}
+
+static const SwsFlags flags_list[] = {
+    0,
+    SWS_ACCURATE_RND,   /* may insert extra 1x1 dither ops (for accurate 
rounding) */
+    SWS_BITEXACT,       /* prevents some FMA optimizations */
+    SWS_ACCURATE_RND | SWS_BITEXACT,
+};
+
+/* Limit the range of av_tree_enumerate() to only matching uop and type */
+static int enum_type(void *opaque, void *elem)
+{
+    const SwsUOp *a = opaque, *b = elem;
+    if (a->type != b->type)
+        return (int) b->type - a->type;
+    if (a->uop != b->uop)
+        return (int) b->uop - a->uop;
+    return 0;
+}
+
+static int free_uop_key(void *opaque, void *key)
+{
+    av_free(key);
+    return 0;
+}
+
+/**
+ * Generate a set of boilerplate C preprocessor macros for describing and
+ * programmatically iterating over all possible SwsUOps.
+ *
+ * This function can be quite slow as it iterates over every possible
+ * combination of pixel formats and flags.
+ *
+ * Returns 0 or a negative error code. On success, an allocated string is
+ * returned via `out_str`, and must be av_free()'d by the caller.
+ */
+static int sws_uops_macros_gen(char **out_str)
+{
+    int ret;
+    struct AVTreeNode *root = NULL;
+
+    AVBPrint bprint, *const bp = &bprint;
+    av_bprint_init(bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    /* Allocate dummy graph and context for ff_sws_compile_pass() */
+    SwsGraph *graph = ff_sws_graph_alloc();
+    if (!graph)
+        return AVERROR(ENOMEM);
+
+    SwsContext *ctx = graph->ctx = sws_alloc_context();
+    if (!ctx) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    /* Use this to plumb the tree state through all the layers of abstraction 
*/
+    ctx->opaque = &root;
+    ctx->scaler = SWS_SCALE_BILINEAR; /* cheaper to generate filter kernels */
+
+    /* Register all unique uops over every relevant combination of flags */
+    for (int i = 0; i < FF_ARRAY_ELEMS(flags_list); i++) {
+        ctx->flags = flags_list[i];
+        ret = ff_sws_enum_op_lists(ctx, graph, AV_PIX_FMT_NONE, 
AV_PIX_FMT_NONE,
+                                   register_all_uops);
+        if (ret < 0)
+            goto fail;
+    }
+
+    /**
+     * Additionally make sure planar reads/writes are always available for all
+     * formats, because checkasm depends on them to be able to verify the
+     * input/output of any other operations.
+     */
+    for (enum SwsPixelType type = SWS_PIXEL_NONE+1; type < SWS_PIXEL_TYPE_NB; 
type++) {
+        if (!ff_sws_pixel_type_is_int(type))
+            continue;
+        for (int elems = 1; elems <= 4; elems++) {
+            for (int rw = 0; rw < 2; rw++) {
+                SwsUOp uop = {
+                    .type = type,
+                    .uop  = rw ? SWS_UOP_WRITE_PLANAR : SWS_UOP_READ_PLANAR,
+                    .mask = SWS_COMP_ELEMS(elems),
+                };
+
+                ret = register_uop(&root, &uop);
+                if (ret < 0)
+                    goto fail;
+            }
+        }
+    }
+
+    #define BPRINT_STR(str) av_bprint_append_data(bp, str, strlen(str))
+    BPRINT_STR(
+"/**\n"
+" * This file is automatically generated. Do not edit manually.\n"
+" * To regenerate, run: make fate-sws-uops-macros GEN=1\n"
+" */\n"
+"\n"
+"#ifndef SWSCALE_UOPS_MACROS_H\n"
+"#define SWSCALE_UOPS_MACROS_H\n"
+"\n"
+"/**\n"
+" * Boilerplate helper macros, for template-based backends. These will be\n"
+" * instantiated like this, with parameters in struct order:\n"
+" *   MACRO(__VA_ARGS__, NAME, UOP, TYPE, MASK, [PARAMS,])\n"
+" * The _STRUCT variants pass all arguments in C struct syntax, while the\n"
+" * plain variants give them as separate C values (e.g. for use in calls)\n"
+" */\n"
+"#define SWS_GLUE3(x, y, z) x ## _ ## y ## _ ## z\n"
+"#define SWS_FOR(TYPE, UOP, MACRO, ...) \\\n"
+"    SWS_GLUE3(SWS_FOR, TYPE, UOP)(MACRO, __VA_ARGS__)\n"
+"#define SWS_FOR_STRUCT(TYPE, UOP, MACRO, ...) \\\n"
+"    SWS_GLUE3(SWS_FOR_STRUCT, TYPE, UOP)(MACRO, __VA_ARGS__)\n"
+"\n");
+
+    SwsUOp key = { .data.opaque = bp };
+    for (key.type = SWS_PIXEL_NONE + 1; key.type < SWS_PIXEL_TYPE_NB; 
key.type++) {
+        for (key.uop = SWS_UOP_INVALID + 1; key.uop < SWS_UOP_TYPE_NB; 
key.uop++) {
+            const char *macro  = uop_names[key.uop].macro;
+            const char *prefix = pixel_types[key.type].prefix;
+            av_bprintf(bp, "#define SWS_FOR_%s%s(MACRO, ...)", prefix, macro);
+            av_tree_enumerate(root, &key, enum_type, generate_entry_args);
+            av_bprintf(bp, "\n");
+            av_bprintf(bp, "#define SWS_FOR_STRUCT_%s%s(MACRO, ...)", prefix, 
macro);
+            av_tree_enumerate(root, &key, enum_type, generate_entry_struct);
+            av_bprintf(bp, "\n");
+        }
+    }
+
+    BPRINT_STR("\n#endif /* SWSCALE_UOPS_MACROS_H */");
+    ret = av_bprint_finalize(bp, out_str);
+
+fail:
+    av_bprint_finalize(bp, NULL);
+    av_tree_enumerate(root, NULL, NULL, free_uop_key);
+    av_tree_destroy(root);
+    ff_sws_graph_free(&graph);
+    sws_free_context(&ctx);
+    return ret;
+}
+
 static int print_ops(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out)
 {
     SwsUOpList *uops = ff_sws_uop_list_alloc();
@@ -168,7 +536,7 @@ bad_option:
 
     if (macros_gen) {
         char *macros = NULL;
-        ret = ff_sws_uops_macros_gen(&macros);
+        ret = sws_uops_macros_gen(&macros);
         if (ret >= 0)
             puts(macros);
         av_free(macros);
diff --git a/libswscale/uops.c b/libswscale/uops.c
index 096621466c..f1e4267afa 100644
--- a/libswscale/uops.c
+++ b/libswscale/uops.c
@@ -23,11 +23,10 @@
 #include "libavutil/avassert.h"
 #include "libavutil/mem.h"
 #include "libavutil/refstruct.h"
-#include "libavutil/tree.h"
 
 #include "ops.h"
-#include "ops_internal.h"
 #include "uops.h"
+#include "uops_list.h"
 
 int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b)
 {
@@ -41,59 +40,10 @@ int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b)
 }
 
 static const struct {
-    char full[32];
-    char abbr[32];
-    char macro[32];
+    char abbr[19];
 } uop_names[SWS_UOP_TYPE_NB] = {
-#define UOP_NAME(OP, ABBR) [SWS_UOP_##OP] = { "SWS_UOP_" #OP, ABBR, #OP }
-    UOP_NAME(INVALID,           "invalid"),
-    UOP_NAME(READ_PLANAR,       "read_planar"),
-    UOP_NAME(READ_PLANAR_FH,    "read_planar_fh"),
-    UOP_NAME(READ_PLANAR_FV,    "read_planar_fv"),
-    UOP_NAME(READ_PLANAR_FV_FMA,"read_planar_fv_fma"),
-    UOP_NAME(READ_PACKED,       "read_packed"),
-    UOP_NAME(READ_NIBBLE,       "read_nibble"),
-    UOP_NAME(READ_BIT,          "read_bit"),
-    UOP_NAME(READ_PALETTE,      "read_palette"),
-    UOP_NAME(WRITE_PLANAR,      "write_planar"),
-    UOP_NAME(WRITE_PACKED,      "write_packed"),
-    UOP_NAME(WRITE_NIBBLE,      "write_nibble"),
-    UOP_NAME(WRITE_BIT,         "write_bit"),
-    UOP_NAME(PERMUTE,           "permute"),
-    UOP_NAME(COPY,              "copy"),
-    UOP_NAME(MOVE,              "move"),
-    UOP_NAME(SWAP_BYTES,        "swap_bytes"),
-    UOP_NAME(EXPAND_BIT,        "expand_bit"),
-    UOP_NAME(EXPAND_PAIR,       "expand_pair"),
-    UOP_NAME(EXPAND_QUAD,       "expand_quad"),
-    UOP_NAME(TO_U8,             "to_u8"),
-    UOP_NAME(TO_U16,            "to_u16"),
-    UOP_NAME(TO_U32,            "to_u32"),
-    UOP_NAME(TO_F32,            "to_f32"),
-    UOP_NAME(SCALE,             "scale"),
-    UOP_NAME(LINEAR,            "linear"),
-    UOP_NAME(LINEAR_FMA,        "linear_fma"),
-    UOP_NAME(ADD,               "add"),
-    UOP_NAME(MIN,               "min"),
-    UOP_NAME(MAX,               "max"),
-    UOP_NAME(UNPACK,            "unpack"),
-    UOP_NAME(PACK,              "pack"),
-    UOP_NAME(LSHIFT,            "lshift"),
-    UOP_NAME(RSHIFT,            "rshift"),
-    UOP_NAME(CLEAR,             "clear"),
-    UOP_NAME(DITHER,            "dither"),
-#undef UOP_NAME
-};
-
-static const struct {
-    char full[16];
-    char prefix[8];
-} pixel_types[SWS_PIXEL_TYPE_NB] = {
-    [SWS_PIXEL_NONE] = { "SWS_PIXEL_NONE", ""     },
-    [SWS_PIXEL_U8]   = { "SWS_PIXEL_U8",   "U8_"  },
-    [SWS_PIXEL_U16]  = { "SWS_PIXEL_U16",  "U16_" },
-    [SWS_PIXEL_U32]  = { "SWS_PIXEL_U32",  "U32_" },
-    [SWS_PIXEL_F32]  = { "SWS_PIXEL_F32",  "F32_" },
+#define UOP_NAME(OP, ABBR) [SWS_UOP_##OP] = { ABBR },
+    UOPS_LIST(UOP_NAME)
 };
 
 static SwsPixel pixel_from_q(SwsPixelType type, AVRational val)
@@ -220,135 +170,6 @@ void ff_sws_uop_name(const SwsUOp *op, char 
buf[SWS_UOP_NAME_MAX])
     av_assert0(av_bprint_is_complete(&bp));
 }
 
-static int generate_entry_struct(void *opaque, void *key)
-{
-    const SwsUOp *ref = opaque;
-    const SwsUOp *uop = key;
-    AVBPrint *bp = ref->data.opaque;
-    char name[SWS_UOP_NAME_MAX];
-    ff_sws_uop_name(uop, name);
-    av_bprintf(bp, " \\\n    MACRO(__VA_ARGS__, %-40s", name);
-    av_bprintf(bp, ", .type = %-13s, .uop = %-24s, .mask = 0x%x",
-               pixel_types[uop->type].full, uop_names[uop->uop].full, 
uop->mask);
-
-    const SwsUOpParams *par = &uop->par;
-    switch (uop->uop) {
-    case SWS_UOP_READ_PLANAR_FH:
-    case SWS_UOP_READ_PLANAR_FV:
-    case SWS_UOP_READ_PLANAR_FV_FMA:
-        av_bprintf(bp, ", .par.filter.type = %s", 
pixel_types[par->filter.type].full);
-        break;
-    case SWS_UOP_LSHIFT:
-    case SWS_UOP_RSHIFT:
-        av_bprintf(bp, ", .par.shift.amount = %u", par->shift.amount);
-        break;
-    case SWS_UOP_PERMUTE:
-    case SWS_UOP_COPY:
-        av_bprintf(bp, ", .par.swizzle.in = {%d, %d, %d, %d}",
-                   par->swizzle.in[0], par->swizzle.in[1],
-                   par->swizzle.in[2], par->swizzle.in[3]);
-        break;
-    case SWS_UOP_MOVE:
-        av_bprintf(bp, ", .par.move.num_moves = %d", par->move.num_moves);
-        av_bprintf(bp, ", .par.move.dst = {%d, %d, %d, %d, %d, %d}",
-                   par->move.dst[0], par->move.dst[1], par->move.dst[2],
-                   par->move.dst[3], par->move.dst[4], par->move.dst[5]);
-        av_bprintf(bp, ", .par.move.src = {%d, %d, %d, %d, %d, %d}",
-                   par->move.src[0], par->move.src[1], par->move.src[2],
-                   par->move.src[3], par->move.src[4], par->move.src[5]);
-        break;
-    case SWS_UOP_PACK:
-    case SWS_UOP_UNPACK:
-        av_bprintf(bp, ", .par.pack.pattern = {%d, %d, %d, %d}",
-                   par->pack.pattern[0], par->pack.pattern[1],
-                   par->pack.pattern[2], par->pack.pattern[3]);
-        break;
-    case SWS_UOP_CLEAR:
-        av_bprintf(bp, ", .par.clear.one = 0x%x, .par.clear.zero = 0x%x",
-                   par->clear.one, par->clear.zero);
-        break;
-    case SWS_UOP_LINEAR:
-    case SWS_UOP_LINEAR_FMA:
-        av_bprintf(bp, ", .par.lin.one = 0x%x, .par.lin.zero = 0x%x",
-                   par->lin.one, par->lin.zero);
-        if (uop->uop == SWS_UOP_LINEAR_FMA)
-            av_bprintf(bp, ", .par.lin.exact = 0x%x", par->lin.exact);
-        break;
-    case SWS_UOP_DITHER:
-        av_bprintf(bp, ", .par.dither = { .y_offset = {%u, %u, %u, %u}, 
.size_log2 = %u }",
-                   par->dither.y_offset[0], par->dither.y_offset[1],
-                   par->dither.y_offset[2], par->dither.y_offset[3],
-                   par->dither.size_log2);
-        break;
-    }
-
-    av_bprintf(bp, ")");
-    return 0;
-}
-
-static int generate_entry_args(void *opaque, void *key)
-{
-    const SwsUOp *ref = opaque;
-    const SwsUOp *uop = key;
-    AVBPrint *bp = ref->data.opaque;
-    char name[SWS_UOP_NAME_MAX];
-    ff_sws_uop_name(uop, name);
-    av_bprintf(bp, " \\\n    MACRO(__VA_ARGS__, %-40s, %-13s, %-24s, 0x%x",
-               name, pixel_types[uop->type].full, uop_names[uop->uop].full, 
uop->mask);
-
-    const SwsUOpParams *par = &uop->par;
-    switch (uop->uop) {
-    case SWS_UOP_READ_PLANAR_FH:
-    case SWS_UOP_READ_PLANAR_FV:
-    case SWS_UOP_READ_PLANAR_FV_FMA:
-        av_bprintf(bp, ", %s", pixel_types[par->filter.type].full);
-        break;
-    case SWS_UOP_LSHIFT:
-    case SWS_UOP_RSHIFT:
-        av_bprintf(bp, ", %u", par->shift.amount);
-        break;
-    case SWS_UOP_PERMUTE:
-    case SWS_UOP_COPY:
-        av_bprintf(bp, ", %d, %d, %d, %d",
-                   par->swizzle.in[0], par->swizzle.in[1],
-                   par->swizzle.in[2], par->swizzle.in[3]);
-        break;
-    case SWS_UOP_MOVE:
-        av_bprintf(bp, ", %d", par->move.num_moves);
-        av_bprintf(bp, ", %d, %d, %d, %d, %d, %d",
-                   par->move.dst[0], par->move.dst[1], par->move.dst[2],
-                   par->move.dst[3], par->move.dst[4], par->move.dst[5]);
-        av_bprintf(bp, ", %d, %d, %d, %d, %d, %d",
-                   par->move.src[0], par->move.src[1], par->move.src[2],
-                   par->move.src[3], par->move.src[4], par->move.src[5]);
-        break;
-    case SWS_UOP_PACK:
-    case SWS_UOP_UNPACK:
-        av_bprintf(bp, ", %d, %d, %d, %d",
-                   par->pack.pattern[0], par->pack.pattern[1],
-                   par->pack.pattern[2], par->pack.pattern[3]);
-        break;
-    case SWS_UOP_CLEAR:
-        av_bprintf(bp, ", 0x%05x, 0x%05x", par->clear.one, par->clear.zero);
-        break;
-    case SWS_UOP_LINEAR:
-    case SWS_UOP_LINEAR_FMA:
-        av_bprintf(bp, ", 0x%05x, 0x%05x", par->lin.one, par->lin.zero);
-        if (uop->uop == SWS_UOP_LINEAR_FMA)
-            av_bprintf(bp, ", 0x%05x", par->lin.exact);
-        break;
-    case SWS_UOP_DITHER:
-        av_bprintf(bp, ", %u, %u, %u, %u, %u",
-                   par->dither.y_offset[0], par->dither.y_offset[1],
-                   par->dither.y_offset[2], par->dither.y_offset[3],
-                   par->dither.size_log2);
-        break;
-    }
-
-    av_bprintf(bp, ")");
-    return 0;
-}
-
 static void uop_uninit(SwsUOp *uop)
 {
     switch (uop->uop) {
@@ -872,210 +693,3 @@ int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList 
*ops,
     }
     return 0;
 }
-
-static int register_uop(struct AVTreeNode **root, const SwsUOp *uop)
-{
-    SwsUOp *key = av_memdup(uop, sizeof(*uop));
-    if (!key)
-        return AVERROR(ENOMEM);
-    memset(&key->data, 0, sizeof(key->data));
-
-    struct AVTreeNode *node = av_tree_node_alloc();
-    if (!node) {
-        av_free(key);
-        return AVERROR(ENOMEM);
-    }
-
-    av_tree_insert(root, key, ff_sws_uop_cmp_v, &node);
-    if (node) {
-        av_free(node);
-        av_free(key);
-    }
-    return 0;
-}
-
-static int register_flags(SwsContext *ctx, const SwsOpList *ops, SwsUOpFlags 
flags)
-{
-    SwsUOpList *uops = ff_sws_uop_list_alloc();
-    if (!uops)
-        return AVERROR(ENOMEM);
-
-    int ret = ff_sws_ops_translate(ctx, ops, flags, uops);
-    if (ret < 0)
-        goto fail;
-
-    struct AVTreeNode **root = ctx->opaque;
-    for (int i = 0; i < uops->num_ops; i++) {
-        ret = register_uop(root, &uops->ops[i]);
-        if (ret < 0)
-            goto fail;
-    }
-
-fail:
-    ff_sws_uop_list_free(&uops);
-    return ret;
-}
-
-static const SwsUOpFlags uop_flags[] = {
-    0,
-    SWS_UOP_FLAG_FMA | SWS_UOP_FLAG_MOVE, /* x86 backend */
-};
-
-static int register_uops(SwsContext *ctx, const SwsOpList *ops,
-                         SwsCompiledOp *out)
-{
-    for (int i = 0; i < FF_ARRAY_ELEMS(uop_flags); i++) {
-        int ret = register_flags(ctx, ops, uop_flags[i]);
-        if (ret < 0)
-            return ret;
-    }
-
-    *out = (SwsCompiledOp) {0}; /* dummy value, will be immediately freed */
-    return 0;
-}
-
-/* Dummy backend that just registers all seen uops */
-static const SwsOpBackend backend_uops = {
-    .name    = "uops_gen",
-    .compile = register_uops,
-};
-
-static int register_all_uops(SwsContext *ctx, void *graph, SwsOpList *ops)
-{
-    /* ff_sws_compile_pass() takes over ownership of `ops` */
-    SwsOpList *copy = ff_sws_op_list_duplicate(ops);
-    if (!copy)
-        return AVERROR(ENOMEM);
-
-    const int flags = SWS_OP_FLAG_DRY_RUN | SWS_OP_FLAG_SPLIT_MEMCPY;
-    return ff_sws_compile_pass(graph, &backend_uops, &copy, flags, NULL, NULL);
-}
-
-static const SwsFlags flags[] = {
-    0,
-    SWS_ACCURATE_RND,   /* may insert extra 1x1 dither ops (for accurate 
rounding) */
-    SWS_BITEXACT,       /* prevents some FMA optimizations */
-    SWS_ACCURATE_RND | SWS_BITEXACT,
-};
-
-/* Limit the range of av_tree_enumerate() to only matching uop and type */
-static int enum_type(void *opaque, void *elem)
-{
-    const SwsUOp *a = opaque, *b = elem;
-    if (a->type != b->type)
-        return (int) b->type - a->type;
-    if (a->uop != b->uop)
-        return (int) b->uop - a->uop;
-    return 0;
-}
-
-static int free_uop_key(void *opaque, void *key)
-{
-    av_free(key);
-    return 0;
-}
-
-int ff_sws_uops_macros_gen(char **out_str)
-{
-    int ret;
-    struct AVTreeNode *root = NULL;
-
-    AVBPrint bprint, *const bp = &bprint;
-    av_bprint_init(bp, 0, AV_BPRINT_SIZE_UNLIMITED);
-
-    /* Allocate dummy graph and context for ff_sws_compile_pass() */
-    SwsGraph *graph = ff_sws_graph_alloc();
-    if (!graph)
-        return AVERROR(ENOMEM);
-
-    SwsContext *ctx = graph->ctx = sws_alloc_context();
-    if (!ctx) {
-        ret = AVERROR(ENOMEM);
-        goto fail;
-    }
-
-    /* Use this to plumb the tree state through all the layers of abstraction 
*/
-    ctx->opaque = &root;
-    ctx->scaler = SWS_SCALE_BILINEAR; /* cheaper to generate filter kernels */
-
-    /* Register all unique uops over every relevant combination of flags */
-    for (int i = 0; i < FF_ARRAY_ELEMS(flags); i++) {
-        ctx->flags = flags[i];
-        ret = ff_sws_enum_op_lists(ctx, graph, AV_PIX_FMT_NONE, 
AV_PIX_FMT_NONE,
-                                   register_all_uops);
-        if (ret < 0)
-            goto fail;
-    }
-
-    /**
-     * Additionally make sure planar reads/writes are always available for all
-     * formats, because checkasm depends on them to be able to verify the
-     * input/output of any other operations.
-     */
-    for (enum SwsPixelType type = SWS_PIXEL_NONE+1; type < SWS_PIXEL_TYPE_NB; 
type++) {
-        if (!ff_sws_pixel_type_is_int(type))
-            continue;
-        for (int elems = 1; elems <= 4; elems++) {
-            for (int rw = 0; rw < 2; rw++) {
-                SwsUOp uop = {
-                    .type = type,
-                    .uop  = rw ? SWS_UOP_WRITE_PLANAR : SWS_UOP_READ_PLANAR,
-                    .mask = SWS_COMP_ELEMS(elems),
-                };
-
-                ret = register_uop(&root, &uop);
-                if (ret < 0)
-                    goto fail;
-            }
-        }
-    }
-
-    #define BPRINT_STR(str) av_bprint_append_data(bp, str, strlen(str))
-    BPRINT_STR(
-"/**\n"
-" * This file is automatically generated. Do not edit manually.\n"
-" * To regenerate, run: make fate-sws-uops-macros GEN=1\n"
-" */\n"
-"\n"
-"#ifndef SWSCALE_UOPS_MACROS_H\n"
-"#define SWSCALE_UOPS_MACROS_H\n"
-"\n"
-"/**\n"
-" * Boilerplate helper macros, for template-based backends. These will be\n"
-" * instantiated like this, with parameters in struct order:\n"
-" *   MACRO(__VA_ARGS__, NAME, UOP, TYPE, MASK, [PARAMS,])\n"
-" * The _STRUCT variants pass all arguments in C struct syntax, while the\n"
-" * plain variants give them as separate C values (e.g. for use in calls)\n"
-" */\n"
-"#define SWS_GLUE3(x, y, z) x ## _ ## y ## _ ## z\n"
-"#define SWS_FOR(TYPE, UOP, MACRO, ...) \\\n"
-"    SWS_GLUE3(SWS_FOR, TYPE, UOP)(MACRO, __VA_ARGS__)\n"
-"#define SWS_FOR_STRUCT(TYPE, UOP, MACRO, ...) \\\n"
-"    SWS_GLUE3(SWS_FOR_STRUCT, TYPE, UOP)(MACRO, __VA_ARGS__)\n"
-"\n");
-
-    SwsUOp key = { .data.opaque = bp };
-    for (key.type = SWS_PIXEL_NONE + 1; key.type < SWS_PIXEL_TYPE_NB; 
key.type++) {
-        for (key.uop = SWS_UOP_INVALID + 1; key.uop < SWS_UOP_TYPE_NB; 
key.uop++) {
-            const char *macro  = uop_names[key.uop].macro;
-            const char *prefix = pixel_types[key.type].prefix;
-            av_bprintf(bp, "#define SWS_FOR_%s%s(MACRO, ...)", prefix, macro);
-            av_tree_enumerate(root, &key, enum_type, generate_entry_args);
-            av_bprintf(bp, "\n");
-            av_bprintf(bp, "#define SWS_FOR_STRUCT_%s%s(MACRO, ...)", prefix, 
macro);
-            av_tree_enumerate(root, &key, enum_type, generate_entry_struct);
-            av_bprintf(bp, "\n");
-        }
-    }
-
-    BPRINT_STR("\n#endif /* SWSCALE_UOPS_MACROS_H */");
-    ret = av_bprint_finalize(bp, out_str);
-
-fail:
-    av_bprint_finalize(bp, NULL);
-    av_tree_enumerate(root, NULL, NULL, free_uop_key);
-    av_tree_destroy(root);
-    ff_sws_graph_free(&graph);
-    sws_free_context(&ctx);
-    return ret;
-}
diff --git a/libswscale/uops.h b/libswscale/uops.h
index 284f74042b..1ad14efc58 100644
--- a/libswscale/uops.h
+++ b/libswscale/uops.h
@@ -272,16 +272,4 @@ int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop);
 int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops,
                          SwsUOpFlags flags, SwsUOpList *uops);
 
-/**
- * Generate a set of boilerplate C preprocessor macros for describing and
- * programmatically iterating over all possible SwsUOps.
- *
- * This function can be quite slow as it iterates over every possible
- * combination of pixel formats and flags.
- *
- * Returns 0 or a negative error code. On success, an allocated string is
- * returned via `out_str`, and must be av_free()'d by the caller.
- */
-int ff_sws_uops_macros_gen(char **out_str);
-
 #endif
diff --git a/libswscale/uops_list.h b/libswscale/uops_list.h
new file mode 100644
index 0000000000..a4db78b3c5
--- /dev/null
+++ b/libswscale/uops_list.h
@@ -0,0 +1,60 @@
+/**
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SWSCALE_UOPS_LIST_H
+#define SWSCALE_UOPS_LIST_H
+
+#define UOPS_LIST(ENTRY)                                   \
+    ENTRY(INVALID,             "invalid")                  \
+    ENTRY(READ_PLANAR,         "read_planar")              \
+    ENTRY(READ_PLANAR_FH,      "read_planar_fh")           \
+    ENTRY(READ_PLANAR_FV,      "read_planar_fv")           \
+    ENTRY(READ_PLANAR_FV_FMA,  "read_planar_fv_fma")       \
+    ENTRY(READ_PACKED,         "read_packed")              \
+    ENTRY(READ_NIBBLE,         "read_nibble")              \
+    ENTRY(READ_BIT,            "read_bit")                 \
+    ENTRY(READ_PALETTE,        "read_palette")             \
+    ENTRY(WRITE_PLANAR,        "write_planar")             \
+    ENTRY(WRITE_PACKED,        "write_packed")             \
+    ENTRY(WRITE_NIBBLE,        "write_nibble")             \
+    ENTRY(WRITE_BIT,           "write_bit")                \
+    ENTRY(PERMUTE,             "permute")                  \
+    ENTRY(COPY,                "copy")                     \
+    ENTRY(MOVE,                "move")                     \
+    ENTRY(SWAP_BYTES,          "swap_bytes")               \
+    ENTRY(EXPAND_BIT,          "expand_bit")               \
+    ENTRY(EXPAND_PAIR,         "expand_pair")              \
+    ENTRY(EXPAND_QUAD,         "expand_quad")              \
+    ENTRY(TO_U8,               "to_u8")                    \
+    ENTRY(TO_U16,              "to_u16")                   \
+    ENTRY(TO_U32,              "to_u32")                   \
+    ENTRY(TO_F32,              "to_f32")                   \
+    ENTRY(SCALE,               "scale")                    \
+    ENTRY(LINEAR,              "linear")                   \
+    ENTRY(LINEAR_FMA,          "linear_fma")               \
+    ENTRY(ADD,                 "add")                      \
+    ENTRY(MIN,                 "min")                      \
+    ENTRY(MAX,                 "max")                      \
+    ENTRY(UNPACK,              "unpack")                   \
+    ENTRY(PACK,                "pack")                     \
+    ENTRY(LSHIFT,              "lshift")                   \
+    ENTRY(RSHIFT,              "rshift")                   \
+    ENTRY(CLEAR,               "clear")                    \
+    ENTRY(DITHER,              "dither")                   \
+
+#endif /* SWSCALE_UOPS_LIST_H */
-- 
2.52.0


>From a50b490998ba79de51f77d1e59940f077b475963 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Wed, 24 Jun 2026 16:51:27 +0200
Subject: [PATCH 4/4] swscale/ops: Move test-only stuff to sws_ops tests

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 libswscale/ops.c                   |  76 -------------------
 libswscale/ops.h                   |  15 ----
 libswscale/tests/enum_op_lists.h   | 117 +++++++++++++++++++++++++++++
 libswscale/tests/sws_ops.c         |   2 +
 libswscale/tests/sws_ops_aarch64.c |   2 +
 5 files changed, 121 insertions(+), 91 deletions(-)
 create mode 100644 libswscale/tests/enum_op_lists.h

diff --git a/libswscale/ops.c b/libswscale/ops.c
index 26e6a6171c..beaffb5ccf 100644
--- a/libswscale/ops.c
+++ b/libswscale/ops.c
@@ -1003,79 +1003,3 @@ void ff_sws_op_list_print(void *log, int lev, int 
lev_extra,
 
     av_log(log, lev, "    ('X' unused, 'z' byteswapped, '=' copied, '$' const, 
'+' integer, '0' zero)\n");
 }
-
-#define DUMMY_SIZE 16
-
-static int enum_ops_fmt(SwsContext *ctx, void *opaque,
-                        enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
-                        int (*cb)(SwsContext *ctx, void *opaque, SwsOpList 
*ops))
-{
-    int ret = 0;
-    SwsOpList *ops = NULL;
-    SwsFormat src, dst;
-    ff_fmt_from_pixfmt(src_fmt, &src);
-    ff_fmt_from_pixfmt(dst_fmt, &dst);
-    bool incomplete = ff_infer_colors(&src.color, &dst.color);
-    src.width = src.height = DUMMY_SIZE;
-
-    static const int dst_sizes[][2] = {
-        { DUMMY_SIZE,     DUMMY_SIZE     },
-        { DUMMY_SIZE,     DUMMY_SIZE * 2 },
-        { DUMMY_SIZE * 2, DUMMY_SIZE     },
-        { DUMMY_SIZE * 2, DUMMY_SIZE * 2 },
-    };
-
-    for (int i = 0; i < FF_ARRAY_ELEMS(dst_sizes); i++) {
-        dst.width  = dst_sizes[i][0];
-        dst.height = dst_sizes[i][1];
-
-        ret = ff_sws_op_list_generate(ctx, &src, &dst, &ops, &incomplete);
-        if (ret == AVERROR(ENOTSUP))
-            return 0; /* silently skip unsupported formats */
-        else if (ret < 0)
-            return ret;
-
-        ret = ff_sws_op_list_optimize(ops);
-        if (ret < 0)
-            goto fail;
-
-        ret = cb(ctx, opaque, ops);
-        if (ret < 0)
-            goto fail;
-
-        ff_sws_op_list_free(&ops);
-    }
-
-fail:
-    ff_sws_op_list_free(&ops);
-    return ret;
-}
-
-int ff_sws_enum_op_lists(SwsContext *ctx, void *opaque,
-                         enum AVPixelFormat src_fmt, enum AVPixelFormat 
dst_fmt,
-                         int (*cb)(SwsContext *ctx, void *opaque, SwsOpList 
*ops))
-{
-    const AVPixFmtDescriptor *src_start = av_pix_fmt_desc_next(NULL);
-    const AVPixFmtDescriptor *dst_start = src_start;
-    if (src_fmt != AV_PIX_FMT_NONE)
-        src_start = av_pix_fmt_desc_get(src_fmt);
-    if (dst_fmt != AV_PIX_FMT_NONE)
-        dst_start = av_pix_fmt_desc_get(dst_fmt);
-
-    const AVPixFmtDescriptor *src, *dst;
-    for (src = src_start; src; src = av_pix_fmt_desc_next(src)) {
-        const enum AVPixelFormat src_f = av_pix_fmt_desc_get_id(src);
-        for (dst = dst_start; dst; dst = av_pix_fmt_desc_next(dst)) {
-            const enum AVPixelFormat dst_f = av_pix_fmt_desc_get_id(dst);
-            int ret = enum_ops_fmt(ctx, opaque, src_f, dst_f, cb);
-            if (ret < 0)
-                return ret;
-            if (dst_fmt != AV_PIX_FMT_NONE)
-                break;
-        }
-        if (src_fmt != AV_PIX_FMT_NONE)
-            break;
-    }
-
-    return 0;
-}
diff --git a/libswscale/ops.h b/libswscale/ops.h
index f2a57612b4..06820b3681 100644
--- a/libswscale/ops.h
+++ b/libswscale/ops.h
@@ -343,19 +343,4 @@ void ff_sws_op_list_update_comps(SwsOpList *ops);
  */
 int ff_sws_op_list_optimize(SwsOpList *ops);
 
-/**
- * Helper function to enumerate over all possible (optimized) operation lists,
- * under the current set of options in `ctx`, and run the given callback on
- * each list.
- *
- * @param src_fmt If set (not AV_PIX_FMT_NONE), constrain the source format
- * @param dst_fmt If set (not AV_PIX_FMT_NONE), constrain the destination 
format
- * @return 0 on success, the return value if cb() < 0, or a negative error code
- *
- * @note `ops` belongs to ff_sws_enum_op_lists(), but may be mutated by `cb`.
- */
-int ff_sws_enum_op_lists(SwsContext *ctx, void *opaque,
-                         enum AVPixelFormat src_fmt, enum AVPixelFormat 
dst_fmt,
-                         int (*cb)(SwsContext *ctx, void *opaque, SwsOpList 
*ops));
-
 #endif
diff --git a/libswscale/tests/enum_op_lists.h b/libswscale/tests/enum_op_lists.h
new file mode 100644
index 0000000000..d6b55357af
--- /dev/null
+++ b/libswscale/tests/enum_op_lists.h
@@ -0,0 +1,117 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SWSCALE_TESTS_ENUM_OP_LISTS_H
+#define SWSCALE_TESTS_ENUM_OP_LISTS_H
+
+#include "libswscale/format.h"
+#include "libswscale/ops.h"
+#include "libswscale/swscale.h"
+
+#include "libavutil/error.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/pixfmt.h"
+
+#define DUMMY_SIZE 16
+
+static int enum_ops_fmt(SwsContext *ctx, void *opaque,
+                        enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
+                        int (*cb)(SwsContext *ctx, void *opaque, SwsOpList 
*ops))
+{
+    int ret = 0;
+    SwsOpList *ops = NULL;
+    SwsFormat src, dst;
+    ff_fmt_from_pixfmt(src_fmt, &src);
+    ff_fmt_from_pixfmt(dst_fmt, &dst);
+    bool incomplete = ff_infer_colors(&src.color, &dst.color);
+    src.width = src.height = DUMMY_SIZE;
+
+    static const int dst_sizes[][2] = {
+        { DUMMY_SIZE,     DUMMY_SIZE     },
+        { DUMMY_SIZE,     DUMMY_SIZE * 2 },
+        { DUMMY_SIZE * 2, DUMMY_SIZE     },
+        { DUMMY_SIZE * 2, DUMMY_SIZE * 2 },
+    };
+
+    for (int i = 0; i < FF_ARRAY_ELEMS(dst_sizes); i++) {
+        dst.width  = dst_sizes[i][0];
+        dst.height = dst_sizes[i][1];
+
+        ret = ff_sws_op_list_generate(ctx, &src, &dst, &ops, &incomplete);
+        if (ret == AVERROR(ENOTSUP))
+            return 0; /* silently skip unsupported formats */
+        else if (ret < 0)
+            return ret;
+
+        ret = ff_sws_op_list_optimize(ops);
+        if (ret < 0)
+            goto fail;
+
+        ret = cb(ctx, opaque, ops);
+        if (ret < 0)
+            goto fail;
+
+        ff_sws_op_list_free(&ops);
+    }
+
+fail:
+    ff_sws_op_list_free(&ops);
+    return ret;
+}
+
+/**
+ * Helper function to enumerate over all possible (optimized) operation lists,
+ * under the current set of options in `ctx`, and run the given callback on
+ * each list.
+ *
+ * @param src_fmt If set (not AV_PIX_FMT_NONE), constrain the source format
+ * @param dst_fmt If set (not AV_PIX_FMT_NONE), constrain the destination 
format
+ * @return 0 on success, the return value if cb() < 0, or a negative error code
+ *
+ * @note `ops` belongs to sws_enum_op_lists(), but may be mutated by `cb`.
+ */
+static inline
+int ff_sws_enum_op_lists(SwsContext *ctx, void *opaque,
+                         enum AVPixelFormat src_fmt, enum AVPixelFormat 
dst_fmt,
+                         int (*cb)(SwsContext *ctx, void *opaque, SwsOpList 
*ops))
+{
+    const AVPixFmtDescriptor *src_start = av_pix_fmt_desc_next(NULL);
+    const AVPixFmtDescriptor *dst_start = src_start;
+    if (src_fmt != AV_PIX_FMT_NONE)
+        src_start = av_pix_fmt_desc_get(src_fmt);
+    if (dst_fmt != AV_PIX_FMT_NONE)
+        dst_start = av_pix_fmt_desc_get(dst_fmt);
+
+    const AVPixFmtDescriptor *src, *dst;
+    for (src = src_start; src; src = av_pix_fmt_desc_next(src)) {
+        const enum AVPixelFormat src_f = av_pix_fmt_desc_get_id(src);
+        for (dst = dst_start; dst; dst = av_pix_fmt_desc_next(dst)) {
+            const enum AVPixelFormat dst_f = av_pix_fmt_desc_get_id(dst);
+            int ret = enum_ops_fmt(ctx, opaque, src_f, dst_f, cb);
+            if (ret < 0)
+                return ret;
+            if (dst_fmt != AV_PIX_FMT_NONE)
+                break;
+        }
+        if (src_fmt != AV_PIX_FMT_NONE)
+            break;
+    }
+
+    return 0;
+}
+#endif
diff --git a/libswscale/tests/sws_ops.c b/libswscale/tests/sws_ops.c
index e2d01e76cf..868a4837b6 100644
--- a/libswscale/tests/sws_ops.c
+++ b/libswscale/tests/sws_ops.c
@@ -27,6 +27,8 @@
 #include "libswscale/uops_list.h"
 #include "libswscale/format.h"
 
+#include "enum_op_lists.h"
+
 #ifdef _WIN32
 #include <io.h>
 #include <fcntl.h>
diff --git a/libswscale/tests/sws_ops_aarch64.c 
b/libswscale/tests/sws_ops_aarch64.c
index 3dc68bf5c1..9ef2bb9fc4 100644
--- a/libswscale/tests/sws_ops_aarch64.c
+++ b/libswscale/tests/sws_ops_aarch64.c
@@ -28,6 +28,8 @@
 #include "libswscale/aarch64/ops_impl.c"
 #include "libswscale/aarch64/ops_impl_conv.c"
 
+#include "enum_op_lists.h"
+
 #ifdef _WIN32
 #include <io.h>
 #include <fcntl.h>
-- 
2.52.0

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

Reply via email to