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]

Reply via email to