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

Git pushed a commit to branch master
in repository ffmpeg.

commit 05d8891a20de059f901fbb106f45b7699f81bba0
Author:     Niklas Haas <[email protected]>
AuthorDate: Wed Jul 29 14:43:20 2026 +0200
Commit:     Niklas Haas <[email protected]>
CommitDate: Mon Aug 3 09:32:30 2026 +0000

    swscale/format: add SwsLut3D support to the ops list generator
    
    The value range normalization and input range clamp will normally be
    optimized away.
    
    This commit also moves the legacy 3dlut pass to be legacy-exclusive, as the
    ops code now uses the new logic. This ordering ensures that the conversion
    works on every commit in isolation.
    
    Sponsored-by: Sovereign Tech Fund
    Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/format.c               | 62 +++++++++++++++++++++++++++++++++++++--
 libswscale/format.h               | 18 ++++++++++--
 libswscale/graph.c                | 51 ++++++++++++++++----------------
 libswscale/op_list_gen_template.c |  2 +-
 4 files changed, 101 insertions(+), 32 deletions(-)

diff --git a/libswscale/format.c b/libswscale/format.c
index 2531435dae..cbd7e0263e 100644
--- a/libswscale/format.c
+++ b/libswscale/format.c
@@ -1745,9 +1745,62 @@ int ff_sws_add_filters(SwsContext *ctx, SwsPixelType 
type, SwsOpList *ops,
     return add_filter(ctx, type, ops, SWS_OP_FILTER_V, src->height, 
dst->height);
 }
 
+int ff_sws_apply_lut3d(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
+                       const SwsLut3D *lut3d)
+{
+    /* Unnormalize to LUT input domain and clamp */
+    const AVRational64 domain = Q(INPUT_LUT_SIZE - 1);
+
+    RET(ff_sws_op_list_append(ops, &(SwsOp) {
+        .type   = type,
+        .op     = SWS_OP_LINEAR,
+        .lin    = {{
+            { domain,   Q(0),   Q(0), Q(0), Q(0) },
+            {   Q(0), domain,   Q(0), Q(0), Q(0) },
+            {   Q(0),   Q(0), domain, Q(0), Q(0) },
+            {   Q(0),   Q(0),   Q(0), Q(1), Q(0) },
+        }},
+    }));
+
+    RET(ff_sws_op_list_append(ops, &(SwsOp) {
+        .op     = SWS_OP_MAX,
+        .type   = type,
+        .clamp  = {{ Q(0), Q(0), Q(0) }},
+    }));
+
+    RET(ff_sws_op_list_append(ops, &(SwsOp) {
+        .op     = SWS_OP_MIN,
+        .type   = type,
+        .clamp  = {{ domain, domain, domain }},
+    }));
+
+    /* Apply the 3DLUT itself */
+    RET(ff_sws_op_list_append(ops, &(SwsOp) {
+        .op     = SWS_OP_LUT_3D,
+        .type   = type,
+        .lut3d.lut     = av_refstruct_ref_c(lut3d),
+        .lut3d.dynamic = lut3d->dynamic,
+    }));
+
+    /* Normalize back to [0, 1] */
+    const AVRational64 inv = av_inv_q64(Q(UINT16_MAX));
+    RET(ff_sws_op_list_append(ops, &(SwsOp) {
+        .type   = type,
+        .op     = SWS_OP_LINEAR,
+        .lin    = {{
+            {  inv, Q(0), Q(0), Q(0), Q(0) },
+            { Q(0),  inv, Q(0), Q(0), Q(0) },
+            { Q(0), Q(0),  inv, Q(0), Q(0) },
+            { Q(0), Q(0), Q(0), Q(1), Q(0) },
+        }},
+    }));
+
+    return 0;
+}
+
 int ff_sws_op_list_generate(SwsContext *ctx, const SwsFormat *src,
-                            const SwsFormat *dst, SwsOpList **out_ops,
-                            bool *incomplete)
+                            const SwsFormat *dst, const SwsLut3D *lut3d,
+                            SwsOpList **out_ops, bool *incomplete)
 {
     /* The new code does not yet support alpha blending */
     if (src->desc->flags & AV_PIX_FMT_FLAG_ALPHA &&
@@ -1770,6 +1823,11 @@ int ff_sws_op_list_generate(SwsContext *ctx, const 
SwsFormat *src,
     ret = ff_sws_add_filters(ctx, type, ops, src, dst);
     if (ret < 0)
         goto fail;
+    if (lut3d) {
+        ret = ff_sws_apply_lut3d(ctx, type, ops, lut3d);
+        if (ret < 0)
+            goto fail;
+    }
     ret = ff_sws_encode_colors(ctx, type, ops, src, dst, incomplete);
     if (ret < 0)
         goto fail;
diff --git a/libswscale/format.h b/libswscale/format.h
index ea2ab7dc41..35e93faf7b 100644
--- a/libswscale/format.h
+++ b/libswscale/format.h
@@ -201,13 +201,25 @@ int ff_sws_add_filters(SwsContext *ctx, SwsPixelType 
type, SwsOpList *ops,
                        const SwsFormat *src, const SwsFormat *dst);
 
 /**
- * Generate an SwsOpList defining a conversion from `src` to `dst`.
+ * Append a set of operations for applying a gamut/tone mapping 3D LUT to
+ * the pixels. The input and output domain are assumed to be normalized
+ * floating point RGBA in the range [0, 1].
+ *
+ * Returns 0 on success, or a negative error code on failure.
+ */
+typedef struct SwsLut3D SwsLut3D;
+int ff_sws_apply_lut3d(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
+                       const SwsLut3D *lut3d);
+
+/**
+ * Generate an SwsOpList defining a conversion from `src` to `dst`, with an
+ * optional 3DLUT for converting between gamuts.
  *
  * Returns 0 on success, or a negative error code on failure.
  */
 int ff_sws_op_list_generate(SwsContext *ctx, const SwsFormat *src,
-                            const SwsFormat *dst, SwsOpList **out_ops,
-                            bool *incomplete);
+                            const SwsFormat *dst, const SwsLut3D *lut3d,
+                            SwsOpList **out_ops, bool *incomplete);
 
 /**
  * Represents a view into a single field of frame data.
diff --git a/libswscale/graph.c b/libswscale/graph.c
index 3e9bbbe22a..4363862d1e 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -530,9 +530,12 @@ static int init_legacy_subpass(SwsGraph *graph, SwsContext 
*sws,
     return 0;
 }
 
+static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src,
+                                 SwsPass *input, SwsPass **output);
+
 static int add_legacy_sws_pass(SwsGraph *graph, const SwsFormat *src,
-                               const SwsFormat *dst, SwsPass *input,
-                               SwsPass **output)
+                               const SwsFormat *dst, const SwsLut3D *lut3d,
+                               SwsPass *input, SwsPass **output)
 {
     int ret, warned = 0;
     SwsContext *const ctx = graph->ctx;
@@ -547,6 +550,18 @@ static int add_legacy_sws_pass(SwsGraph *graph, const 
SwsFormat *src,
     if (!sws_isSupportedInput(src->format) || 
!sws_isSupportedOutput(dst->format))
         return AVERROR(ENOTSUP);
 
+    /* If we need to apply a 3D LUT, add it as an explicit input prepass */
+    if (lut3d) {
+        ret = add_legacy_3dlut_pass(graph, src, input, &input);
+        if (ret < 0)
+            return ret;
+
+        SwsFormat tmp = *src;
+        tmp.format = input->format;
+        tmp.color  = lut3d->map.dst;
+        return add_legacy_sws_pass(graph, &tmp, dst, NULL, input, output);
+    }
+
     SwsContext *sws = sws_alloc_context();
     if (!sws)
         return AVERROR(ENOMEM);
@@ -621,10 +636,6 @@ static int add_legacy_sws_pass(SwsGraph *graph, const 
SwsFormat *src,
     return init_legacy_subpass(graph, sws, input, output);
 }
 
-static int add_convert_pass(SwsGraph *graph, const SwsFormat *src,
-                            const SwsFormat *dst, const SwsLut3D *lut3d,
-                            SwsPass *input, SwsPass **output);
-
 static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src,
                                  SwsPass *input, SwsPass **output)
 {
@@ -638,7 +649,7 @@ static int add_legacy_3dlut_pass(SwsGraph *graph, const 
SwsFormat *src,
     if (src->format != fmt) {
         SwsFormat tmp = *src;
         tmp.format = fmt;
-        ret = add_convert_pass(graph, src, &tmp, NULL, input, &input);
+        ret = add_legacy_sws_pass(graph, src, &tmp, NULL, input, &input);
         if (ret < 0)
             return ret;
     }
@@ -657,8 +668,8 @@ static int add_legacy_3dlut_pass(SwsGraph *graph, const 
SwsFormat *src,
  *********************************/
 
 static int add_ops_convert_pass(SwsGraph *graph, const SwsFormat *src,
-                                const SwsFormat *dst, SwsPass *input,
-                                SwsPass **output)
+                                const SwsFormat *dst, const SwsLut3D *lut3d,
+                                SwsPass *input, SwsPass **output)
 {
 #if CONFIG_UNSTABLE
     SwsContext *ctx = graph->ctx;
@@ -672,7 +683,7 @@ static int add_ops_convert_pass(SwsGraph *graph, const 
SwsFormat *src,
         return AVERROR(ENOTSUP);
 
     SwsOpList *ops;
-    int ret = ff_sws_op_list_generate(ctx, src, dst, &ops, &graph->incomplete);
+    int ret = ff_sws_op_list_generate(ctx, src, dst, lut3d, &ops, 
&graph->incomplete);
     if (ret < 0)
         return ret;
 
@@ -705,26 +716,14 @@ static int add_convert_pass(SwsGraph *graph, const 
SwsFormat *src,
     SwsContext *ctx = graph->ctx;
     int ret;
 
-    /* If we need to apply a 3D LUT, add it as an explicit input prepass */
-    if (lut3d) {
-        ret = add_legacy_3dlut_pass(graph, src, input, &input);
-        if (ret < 0)
-            return ret;
-
-        SwsFormat tmp = *src;
-        tmp.format = input->format;
-        tmp.color  = lut3d->map.dst;
-        return add_convert_pass(graph, &tmp, dst, NULL, input, output);
-    }
-
     if (prefer_ops_backend(ctx, src, dst)) {
-        ret = add_ops_convert_pass(graph, src, dst, input, output);
+        ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
         if (ret == AVERROR(ENOTSUP))
-            ret = add_legacy_sws_pass(graph, src, dst, input, output);
+            ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
     } else {
-        ret = add_legacy_sws_pass(graph, src, dst, input, output);
+        ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
         if (ret == AVERROR(ENOTSUP))
-            ret = add_ops_convert_pass(graph, src, dst, input, output);
+            ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
     }
 
     return ret;
diff --git a/libswscale/op_list_gen_template.c 
b/libswscale/op_list_gen_template.c
index 0030fddec2..bc7696c51e 100644
--- a/libswscale/op_list_gen_template.c
+++ b/libswscale/op_list_gen_template.c
@@ -52,7 +52,7 @@ static int enum_ops_fmt(SwsContext *ctx, void *opaque,
         dst.width  = dst_sizes[i][0];
         dst.height = dst_sizes[i][1];
 
-        ret = ff_sws_op_list_generate(ctx, &src, &dst, &ops, &incomplete);
+        ret = ff_sws_op_list_generate(ctx, &src, &dst, NULL, &ops, 
&incomplete);
         if (ret == AVERROR(ENOTSUP))
             return 0; /* silently skip unsupported formats */
         else if (ret < 0)

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

Reply via email to