[PR] swscale: Move test-only stuff to test tools (PR #23590)

mkver via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178231506334.59.11800694261863270033@29965ddac10e>
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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.