[PR] avfilter/blend: clip expression output to pixel depth (PR #24124)

jiangjie via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178661735225.59.17372168955215459131@29965ddac10e>
PR #24124 opened by jiangjie
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24124
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24124.patch

Clip integer expression results in the double domain before storing them. This keeps high-bit-depth samples within their declared range and gives NaN and infinities defined behavior while preserving float formats.

Fixes issue #24121.

# Summary of changes

Briefly describe what this PR does and why.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From f6b7048be2316b47cf9c7c0b82da8a98a9ea0ebf Mon Sep 17 00:00:00 2001
From: jiangjie <[email protected]>
Date: Thu, 13 Aug 2026 18:31:31 +0800
Subject: [PATCH] avfilter/blend: clip expression output to pixel depth

Clip integer expression results in the double domain before storing them. This keeps high-bit-depth samples within their declared range and gives NaN and infinities defined behavior while preserving float formats.

Fixes issue #24121.
---
 libavfilter/blend.h                       |  1 +
 libavfilter/vf_blend.c                    | 13 ++++++++-----
 tests/fate/filter-video.mak               |  3 +++
 tests/ref/fate/filter-blend-expr-clipping |  6 ++++++
 4 files changed, 18 insertions(+), 5 deletions(-)
 create mode 100644 tests/ref/fate/filter-blend-expr-clipping

diff --git a/libavfilter/blend.h b/libavfilter/blend.h
index e6636839db..5c4044e0e6 100644
--- a/libavfilter/blend.h
+++ b/libavfilter/blend.h
@@ -78,6 +78,7 @@ typedef struct SliceParams {
 typedef struct FilterParams {
     enum BlendMode mode;
     double opacity;
+    unsigned max_value;
     AVExpr **e;
     char *expr_str;
     void (*blend)(const uint8_t *top, ptrdiff_t top_linesize,
diff --git a/libavfilter/vf_blend.c b/libavfilter/vf_blend.c
index 21f4b19091..5e345f8153 100644
--- a/libavfilter/vf_blend.c
+++ b/libavfilter/vf_blend.c
@@ -129,7 +129,7 @@ static const AVOption blend_options[] = {
 
 FRAMESYNC_DEFINE_CLASS(blend, BlendContext, fs);
 
-#define DEFINE_BLEND_EXPR(type, name, div)                                     \
+#define DEFINE_BLEND_EXPR(type, name, div, clip)                               \
 static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize,          \
                                const uint8_t *_bottom, ptrdiff_t bottom_linesize,    \
                                uint8_t *_dst, ptrdiff_t dst_linesize,                \
@@ -153,7 +153,8 @@ static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize,
             values[VAR_X]      = x;                                            \
             values[VAR_TOP]    = values[VAR_A] = top[x];                       \
             values[VAR_BOTTOM] = values[VAR_B] = bottom[x];                    \
-            dst[x] = av_expr_eval(e, values, NULL);                            \
+            double value = av_expr_eval(e, values, NULL);                     \
+            dst[x] = clip ? av_clipd(value, 0, param->max_value) : value;      \
         }                                                                      \
         dst    += dst_linesize;                                                \
         top    += top_linesize;                                                \
@@ -161,9 +162,9 @@ static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize,
     }                                                                          \
 }
 
-DEFINE_BLEND_EXPR(uint8_t, 8bit, 1)
-DEFINE_BLEND_EXPR(uint16_t, 16bit, 2)
-DEFINE_BLEND_EXPR(float, 32bit, 4)
+DEFINE_BLEND_EXPR(uint8_t, 8bit, 1, 1)
+DEFINE_BLEND_EXPR(uint16_t, 16bit, 2, 1)
+DEFINE_BLEND_EXPR(float, 32bit, 4, 0)
 
 static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
 {
@@ -306,6 +307,8 @@ static int config_params(AVFilterContext *ctx)
     for (int plane = 0; plane < FF_ARRAY_ELEMS(s->params); plane++) {
         FilterParams *param = &s->params[plane];
 
+        param->max_value = s->depth < 32 ? (1U << s->depth) - 1 : 0;
+
         if (s->all_mode >= 0)
             param->mode = s->all_mode;
         if (s->all_opacity < 1)
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index 6f6a3c7391..08f806253c 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -382,6 +382,9 @@ FATE_FILTER_VSYNTH_PGMYUV-$(CONFIG_SWAPRECT_FILTER) += $(FATE_SWAPRECT)
 FATE_FILTER_VSYNTH_PGMYUV-$(CONFIG_TBLEND_FILTER) += fate-filter-tblend
 fate-filter-tblend: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf tblend=all_mode=difference128
 
+FATE_FILTER-$(call FILTERFRAMECRC, BLEND FORMAT NULLSRC SPLIT) += fate-filter-blend-expr-clipping
+fate-filter-blend-expr-clipping: CMD = framecrc -lavfi "nullsrc=s=4x2:d=1:r=1,format=yuv422p10le,split[a][b];[a][b]blend=all_expr=if(eq(X\,0)\,-1\,if(eq(X\,1)\,2048\,if(eq(X\,2)\,1/0\,0/0)))"
+
 FATE_FILTER_VSYNTH_PGMYUV-$(CONFIG_TELECINE_FILTER) += fate-filter-telecine
 fate-filter-telecine: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf telecine
 
diff --git a/tests/ref/fate/filter-blend-expr-clipping b/tests/ref/fate/filter-blend-expr-clipping
new file mode 100644
index 0000000000..563591706c
--- /dev/null
+++ b/tests/ref/fate/filter-blend-expr-clipping
@@ -0,0 +1,6 @@
+#tb 0: 1/1
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 4x2
+#sar 0: 1/1
+0,          0,          0,        1,       32, 0x84f00810
-- 
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.