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]
