From 21bbd98e7b05542e9be5661a8532d2513af8c22c Mon Sep 17 00:00:00 2001 From: jiangjie 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..46ee2c54f3 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 = UINT32_MAX >> (32 - s->depth); + 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 48895ee93c..e0decc9b78 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 SCALE SPLIT) += fate-filter-blend-expr-clipping +fate-filter-blend-expr-clipping: CMD = framecrc -lavfi "nullsrc=s=1x1:d=1:r=1,format=gray10,split[a][b];[a][b]blend=c0_expr=1024,scale" -pix_fmt gray10le + 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..b1a7103e76 --- /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: 1x1 +#sar 0: 1/1 +0, 0, 0, 1, 2, 0x02010102