From: Niklas Haas via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Cc: Niklas Haas <code@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH] vf_colordetect: add ability to detect fully opaque alpha planes (PR #20257) Date: Sat, 16 Aug 2025 19:25:12 +0300 (EEST) Message-ID: <20250816162512.8E1D668D232@ffbox0-bg.ffmpeg.org> (raw) PR #20257 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20257 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20257.patch From d7335fdb74a3adf45ae47fa5df0cffa4afda6213 Mon Sep 17 00:00:00 2001 From: Niklas Haas <git@haasn.dev> Date: Fri, 15 Aug 2025 22:55:30 +0200 Subject: [PATCH 1/5] avfilter/vf_colordetect: slightly change detect_alpha() signature Basically cosmetic. I want to expand this to detect more than a single property about the alpha channel at the same time; so we first need a way for this function to return a more complex result. Move the enum AlphaMode to the header and formally generalize the return signature a bit to allow returning more than just one value. --- libavfilter/vf_colordetect.c | 31 +++++++++++++------------------ libavfilter/vf_colordetect.h | 17 ++++++++++++----- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/libavfilter/vf_colordetect.c b/libavfilter/vf_colordetect.c index 11e23ccbff..bc29cfb1b0 100644 --- a/libavfilter/vf_colordetect.c +++ b/libavfilter/vf_colordetect.c @@ -39,13 +39,6 @@ #include "vf_colordetect.h" -enum AlphaMode { - ALPHA_NONE = -1, - ALPHA_UNDETERMINED = 0, - ALPHA_STRAIGHT, - /* No way to positively identify premultiplied alpha */ -}; - enum ColorDetectMode { COLOR_DETECT_COLOR_RANGE = 1 << 0, COLOR_DETECT_ALPHA_MODE = 1 << 1, @@ -64,7 +57,7 @@ typedef struct ColorDetectContext { int mpeg_max; atomic_int detected_range; // enum AVColorRange - atomic_int detected_alpha; // enum AlphaMode + atomic_int detected_alpha; // enum FFAlphaDetect } ColorDetectContext; #define OFFSET(x) offsetof(ColorDetectContext, x) @@ -124,9 +117,9 @@ static int config_input(AVFilterLink *inlink) if (desc->flags & AV_PIX_FMT_FLAG_ALPHA) { s->idx_a = desc->comp[desc->nb_components - 1].plane; - atomic_init(&s->detected_alpha, ALPHA_UNDETERMINED); + atomic_init(&s->detected_alpha, FF_ALPHA_UNDETERMINED); } else { - atomic_init(&s->detected_alpha, ALPHA_NONE); + atomic_init(&s->detected_alpha, FF_ALPHA_NONE); } ff_color_detect_dsp_init(&s->dsp, depth, inlink->color_range); @@ -182,12 +175,14 @@ static int detect_alpha(AVFilterContext *ctx, void *arg, const int q = s->mpeg_max - s->mpeg_min; const int k = p * s->mpeg_min + q + (1 << (s->depth - 1)); + int ret = 0; for (int i = 0; i < nb_planes; i++) { const ptrdiff_t stride = in->linesize[i]; - if (s->dsp.detect_alpha(in->data[i] + y_start * stride, stride, - alpha, alpha_stride, w, h_slice, p, q, k)) { - atomic_store(&s->detected_alpha, ALPHA_STRAIGHT); - return 0; + ret = s->dsp.detect_alpha(in->data[i] + y_start * stride, stride, + alpha, alpha_stride, w, h_slice, p, q, k); + if (ret) { + atomic_store(&s->detected_alpha, ret); + break; } } @@ -202,7 +197,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->mode & COLOR_DETECT_COLOR_RANGE && s->detected_range == AVCOL_RANGE_UNSPECIFIED) ff_filter_execute(ctx, detect_range, in, NULL, nb_threads); - if (s->mode & COLOR_DETECT_ALPHA_MODE && s->detected_alpha == ALPHA_UNDETERMINED) + if (s->mode & COLOR_DETECT_ALPHA_MODE && s->detected_alpha == FF_ALPHA_UNDETERMINED) ff_filter_execute(ctx, detect_alpha, in, NULL, nb_threads); return ff_filter_frame(inlink->dst->outputs[0], in); @@ -223,9 +218,9 @@ static av_cold void uninit(AVFilterContext *ctx) if (s->mode & COLOR_DETECT_ALPHA_MODE) { av_log(ctx, AV_LOG_INFO, " Alpha mode: %s\n", - s->detected_alpha == ALPHA_NONE ? "none" : - s->detected_alpha == ALPHA_STRAIGHT ? "straight / independent" - : "undetermined"); + s->detected_alpha == FF_ALPHA_NONE ? "none" : + s->detected_alpha == FF_ALPHA_STRAIGHT ? "straight / independent" + : "undertermined"); } } diff --git a/libavfilter/vf_colordetect.h b/libavfilter/vf_colordetect.h index 24279643d3..6626f88d26 100644 --- a/libavfilter/vf_colordetect.h +++ b/libavfilter/vf_colordetect.h @@ -26,13 +26,20 @@ #include <libavutil/macros.h> #include <libavutil/pixfmt.h> +enum FFAlphaDetect { + FF_ALPHA_NONE = -1, + FF_ALPHA_UNDETERMINED = 0, + FF_ALPHA_STRAIGHT, + /* No way to positively identify premultiplied alpha */ +}; + typedef struct FFColorDetectDSPContext { /* Returns 1 if an out-of-range value was detected, 0 otherwise */ int (*detect_range)(const uint8_t *data, ptrdiff_t stride, ptrdiff_t width, ptrdiff_t height, int mpeg_min, int mpeg_max); - /* Returns 1 if the color value exceeds the alpha value, 0 otherwise */ + /* Returns an FFAlphaDetect enum value */ int (*detect_alpha)(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, @@ -111,7 +118,7 @@ ff_detect_alpha_full_c(const uint8_t *color, ptrdiff_t color_stride, for (int x = 0; x < width; x++) cond |= color[x] > alpha[x]; if (cond) - return 1; + return FF_ALPHA_STRAIGHT; color += color_stride; alpha += alpha_stride; } @@ -129,7 +136,7 @@ ff_detect_alpha_limited_c(const uint8_t *color, ptrdiff_t color_stride, for (int x = 0; x < width; x++) cond |= p * color[x] - k > q * alpha[x]; if (cond) - return 1; + return FF_ALPHA_STRAIGHT; color += color_stride; alpha += alpha_stride; } @@ -149,7 +156,7 @@ ff_detect_alpha16_full_c(const uint8_t *color, ptrdiff_t color_stride, for (int x = 0; x < width; x++) cond |= color16[x] > alpha16[x]; if (cond) - return 1; + return FF_ALPHA_STRAIGHT; color += color_stride; alpha += alpha_stride; } @@ -167,7 +174,7 @@ ff_detect_alpha16_limited_c(const uint8_t *color, ptrdiff_t color_stride, const uint16_t *alpha16 = (const uint16_t *) alpha; for (int x = 0; x < width; x++) { if ((int64_t) p * color16[x] - k > (int64_t) q * alpha16[x]) - return 1; + return FF_ALPHA_STRAIGHT; } color += color_stride; alpha += alpha_stride; -- 2.49.1 From bcb1b1c2fba49db68b92e7371e52b1ceec7c0bf6 Mon Sep 17 00:00:00 2001 From: Niklas Haas <git@haasn.dev> Date: Fri, 15 Aug 2025 23:07:28 +0200 Subject: [PATCH 2/5] tests/checkasm/vf_colordetect: also test opaque alpha base case Preemptively adding a check for a following commit. --- tests/checkasm/vf_colordetect.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/checkasm/vf_colordetect.c b/tests/checkasm/vf_colordetect.c index 4979931cd3..785fc08892 100644 --- a/tests/checkasm/vf_colordetect.c +++ b/tests/checkasm/vf_colordetect.c @@ -76,6 +76,7 @@ static void check_alpha_detect(int depth, enum AVColorRange range) const int p = (1 << depth) - 1; const int q = mpeg_max - mpeg_min; const int k = p * mpeg_min + q + (1 << (depth - 1)); + int res_ref, res_new; FFColorDetectDSPContext dsp = {0}; ff_color_detect_dsp_init(&dsp, depth, range); @@ -114,14 +115,19 @@ static void check_alpha_detect(int depth, enum AVColorRange range) if (check_func(dsp.detect_alpha, "detect_alpha_%d_%s", depth, range == AVCOL_RANGE_JPEG ? "full" : "limited")) { /* Test increasing height, to ensure we hit the placed 0 eventually */ for (int h = 1; h <= HEIGHT; h++) { - int res_ref = call_ref(luma, STRIDE, alpha, STRIDE, w, h, p, q, k); - int res_new = call_new(luma, STRIDE, alpha, STRIDE, w, h, p, q, k); + res_ref = call_ref(luma, STRIDE, alpha, STRIDE, w, h, p, q, k); + res_new = call_new(luma, STRIDE, alpha, STRIDE, w, h, p, q, k); if (res_ref != res_new) fail(); } - /* Test performance of base case without any out-of-range values */ + /* Test base case without any out-of-range values */ memset(alpha, 0xFF, HEIGHT * STRIDE); + res_ref = call_ref(luma, STRIDE, alpha, STRIDE, w, HEIGHT, p, q, k); + res_new = call_new(luma, STRIDE, alpha, STRIDE, w, HEIGHT, p, q, k); + if (res_ref != res_new) + fail(); + bench_new(luma, STRIDE, alpha, STRIDE, w, HEIGHT, p, q, k); } } -- 2.49.1 From 1a03a3a9c5f2d418e78fe848ffaf5dd36913f165 Mon Sep 17 00:00:00 2001 From: Niklas Haas <git@haasn.dev> Date: Fri, 15 Aug 2025 23:13:16 +0200 Subject: [PATCH 3/5] avfilter/vf_colordetect: rename p, q, k variables for clarity Purely cosmetic. Motivated in part because I want to depend on the assumption that P represents the maximum alpha channel value. --- libavfilter/vf_colordetect.c | 13 +++++++------ libavfilter/vf_colordetect.h | 14 +++++++------- libavfilter/x86/vf_colordetect.asm | 6 +++--- tests/checkasm/vf_colordetect.c | 16 ++++++++-------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/libavfilter/vf_colordetect.c b/libavfilter/vf_colordetect.c index bc29cfb1b0..68b0e5b0fa 100644 --- a/libavfilter/vf_colordetect.c +++ b/libavfilter/vf_colordetect.c @@ -165,21 +165,22 @@ static int detect_alpha(AVFilterContext *ctx, void *arg, * * This simplifies to: * (x - mpeg_min) * pixel_max > (mpeg_max - mpeg_min) * a - * = P * x - K > Q * a in the below formula. + * = alpha_max * x - offset > mpeg_range * a in the below formula. * * We subtract an additional offset of (1 << (depth - 1)) to account for * rounding errors in the value of `x`, and an extra safety margin of - * Q because vf_premultiply.c et al. add an offset of (a >> 1) & 1. + * mpeg_range because vf_premultiply.c et al. add an offset of (a >> 1) & 1. */ - const int p = (1 << s->depth) - 1; - const int q = s->mpeg_max - s->mpeg_min; - const int k = p * s->mpeg_min + q + (1 << (s->depth - 1)); + const int alpha_max = (1 << s->depth) - 1; + const int mpeg_range = s->mpeg_max - s->mpeg_min; + const int offset = alpha_max * s->mpeg_min + mpeg_range + (1 << (s->depth - 1)); int ret = 0; for (int i = 0; i < nb_planes; i++) { const ptrdiff_t stride = in->linesize[i]; ret = s->dsp.detect_alpha(in->data[i] + y_start * stride, stride, - alpha, alpha_stride, w, h_slice, p, q, k); + alpha, alpha_stride, w, h_slice, alpha_max, + mpeg_range, offset); if (ret) { atomic_store(&s->detected_alpha, ret); break; diff --git a/libavfilter/vf_colordetect.h b/libavfilter/vf_colordetect.h index 6626f88d26..22fdbf6189 100644 --- a/libavfilter/vf_colordetect.h +++ b/libavfilter/vf_colordetect.h @@ -43,7 +43,7 @@ typedef struct FFColorDetectDSPContext { int (*detect_alpha)(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, - int p, int q, int k); + int alpha_max, int mpeg_range, int offset); } FFColorDetectDSPContext; void ff_color_detect_dsp_init(FFColorDetectDSPContext *dsp, int depth, @@ -111,7 +111,7 @@ static inline int ff_detect_alpha_full_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, - int p, int q, int k) + int alpha_max, int mpeg_range, int offset) { while (height--) { uint8_t cond = 0; @@ -129,12 +129,12 @@ static inline int ff_detect_alpha_limited_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, - int p, int q, int k) + int alpha_max, int mpeg_range, int offset) { while (height--) { uint8_t cond = 0; for (int x = 0; x < width; x++) - cond |= p * color[x] - k > q * alpha[x]; + cond |= alpha_max * color[x] - offset > mpeg_range * alpha[x]; if (cond) return FF_ALPHA_STRAIGHT; color += color_stride; @@ -147,7 +147,7 @@ static inline int ff_detect_alpha16_full_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, - int p, int q, int k) + int alpha_max, int mpeg_range, int offset) { while (height--) { const uint16_t *color16 = (const uint16_t *) color; @@ -167,13 +167,13 @@ static inline int ff_detect_alpha16_limited_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, - int p, int q, int k) + int alpha_max, int mpeg_range, int offset) { while (height--) { const uint16_t *color16 = (const uint16_t *) color; const uint16_t *alpha16 = (const uint16_t *) alpha; for (int x = 0; x < width; x++) { - if ((int64_t) p * color16[x] - k > (int64_t) q * alpha16[x]) + if ((int64_t) alpha_max * color16[x] - offset > (int64_t) mpeg_range * alpha16[x]) return FF_ALPHA_STRAIGHT; } color += color_stride; diff --git a/libavfilter/x86/vf_colordetect.asm b/libavfilter/x86/vf_colordetect.asm index 44562bc57a..53ff17cf5c 100644 --- a/libavfilter/x86/vf_colordetect.asm +++ b/libavfilter/x86/vf_colordetect.asm @@ -76,9 +76,9 @@ cglobal detect_alpha%1_%3, 6, 7, 6, color, color_stride, alpha, alpha_stride, wi add alphaq, widthq neg widthq %ifidn %3, limited - vpbroadcast%2 m3, r6m ; p - vpbroadcast%2 m4, r7m ; q - vpbroadcast%2 m5, r8m ; k + vpbroadcast%2 m3, r6m ; alpha_max + vpbroadcast%2 m4, r7m ; mpeg_range + vpbroadcast%2 m5, r8m ; offset %endif .lineloop: mov xq, widthq diff --git a/tests/checkasm/vf_colordetect.c b/tests/checkasm/vf_colordetect.c index 785fc08892..9c5f68f855 100644 --- a/tests/checkasm/vf_colordetect.c +++ b/tests/checkasm/vf_colordetect.c @@ -73,9 +73,9 @@ static void check_alpha_detect(int depth, enum AVColorRange range) { const int mpeg_min = 16 << (depth - 8); const int mpeg_max = 235 << (depth - 8); - const int p = (1 << depth) - 1; - const int q = mpeg_max - mpeg_min; - const int k = p * mpeg_min + q + (1 << (depth - 1)); + const int alpha_max = (1 << depth) - 1; + const int mpeg_range = mpeg_max - mpeg_min; + const int offset = alpha_max * mpeg_min + mpeg_range + (1 << (depth - 1)); int res_ref, res_new; FFColorDetectDSPContext dsp = {0}; @@ -115,20 +115,20 @@ static void check_alpha_detect(int depth, enum AVColorRange range) if (check_func(dsp.detect_alpha, "detect_alpha_%d_%s", depth, range == AVCOL_RANGE_JPEG ? "full" : "limited")) { /* Test increasing height, to ensure we hit the placed 0 eventually */ for (int h = 1; h <= HEIGHT; h++) { - res_ref = call_ref(luma, STRIDE, alpha, STRIDE, w, h, p, q, k); - res_new = call_new(luma, STRIDE, alpha, STRIDE, w, h, p, q, k); + res_ref = call_ref(luma, STRIDE, alpha, STRIDE, w, h, alpha_max, mpeg_range, offset); + res_new = call_new(luma, STRIDE, alpha, STRIDE, w, h, alpha_max, mpeg_range, offset); if (res_ref != res_new) fail(); } /* Test base case without any out-of-range values */ memset(alpha, 0xFF, HEIGHT * STRIDE); - res_ref = call_ref(luma, STRIDE, alpha, STRIDE, w, HEIGHT, p, q, k); - res_new = call_new(luma, STRIDE, alpha, STRIDE, w, HEIGHT, p, q, k); + res_ref = call_ref(luma, STRIDE, alpha, STRIDE, w, HEIGHT, alpha_max, mpeg_range, offset); + res_new = call_new(luma, STRIDE, alpha, STRIDE, w, HEIGHT, alpha_max, mpeg_range, offset); if (res_ref != res_new) fail(); - bench_new(luma, STRIDE, alpha, STRIDE, w, HEIGHT, p, q, k); + bench_new(luma, STRIDE, alpha, STRIDE, w, HEIGHT, alpha_max, mpeg_range, offset); } } -- 2.49.1 From 7171aade3b135b4f1ac74d693642720f41b13459 Mon Sep 17 00:00:00 2001 From: Niklas Haas <git@haasn.dev> Date: Fri, 15 Aug 2025 23:15:06 +0200 Subject: [PATCH 4/5] avfilter/vf_colordetect: remove extra safety margin on premul check This safety margin was motivated by the fact that vf_premultiply sometimes produces such illegally high values, but this has since been fixed by 603334a04362e7, so there's no more reason to have this safety margin, at least for our own code. (Of course, other sources may also produce such broken files, but we shouldn't work around that - garbage in, garbage out.) See-Also: 603334a04362e7a466cbdf9a25892ded167e3ce0 --- libavfilter/vf_colordetect.c | 5 ++--- tests/checkasm/vf_colordetect.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libavfilter/vf_colordetect.c b/libavfilter/vf_colordetect.c index 68b0e5b0fa..fadb5c38f8 100644 --- a/libavfilter/vf_colordetect.c +++ b/libavfilter/vf_colordetect.c @@ -168,12 +168,11 @@ static int detect_alpha(AVFilterContext *ctx, void *arg, * = alpha_max * x - offset > mpeg_range * a in the below formula. * * We subtract an additional offset of (1 << (depth - 1)) to account for - * rounding errors in the value of `x`, and an extra safety margin of - * mpeg_range because vf_premultiply.c et al. add an offset of (a >> 1) & 1. + * rounding errors in the value of `x`. */ const int alpha_max = (1 << s->depth) - 1; const int mpeg_range = s->mpeg_max - s->mpeg_min; - const int offset = alpha_max * s->mpeg_min + mpeg_range + (1 << (s->depth - 1)); + const int offset = alpha_max * s->mpeg_min + (1 << (s->depth - 1)); int ret = 0; for (int i = 0; i < nb_planes; i++) { diff --git a/tests/checkasm/vf_colordetect.c b/tests/checkasm/vf_colordetect.c index 9c5f68f855..7498d6141a 100644 --- a/tests/checkasm/vf_colordetect.c +++ b/tests/checkasm/vf_colordetect.c @@ -75,7 +75,7 @@ static void check_alpha_detect(int depth, enum AVColorRange range) const int mpeg_max = 235 << (depth - 8); const int alpha_max = (1 << depth) - 1; const int mpeg_range = mpeg_max - mpeg_min; - const int offset = alpha_max * mpeg_min + mpeg_range + (1 << (depth - 1)); + const int offset = alpha_max * mpeg_min + (1 << (depth - 1)); int res_ref, res_new; FFColorDetectDSPContext dsp = {0}; -- 2.49.1 From 4e7138c8038aa7f9597798de30f55dee2c488608 Mon Sep 17 00:00:00 2001 From: Niklas Haas <git@haasn.dev> Date: Sat, 16 Aug 2025 18:18:53 +0200 Subject: [PATCH 5/5] avfilter/vf_colordetect: detect fully opaque alpha plains It can be useful to know if the alpha plane consists of fully opaque pixels or not, in which case it can e.g. safely be stripped. This only requires a very minor modification to the AVX2 routines, adding an extra AND on the read alpha value with the reference alpha value, and a single extra cheap test per line. detect_alpha_8_full_c: 2849.1 ( 1.00x) detect_alpha_8_full_avx2: 260.3 (10.95x) detect_alpha_8_full_avx512icl: 130.2 (21.87x) detect_alpha_8_limited_c: 8349.2 ( 1.00x) detect_alpha_8_limited_avx2: 756.6 (11.04x) detect_alpha_8_limited_avx512icl: 364.2 (22.93x) detect_alpha_16_full_c: 1652.8 ( 1.00x) detect_alpha_16_full_avx2: 236.5 ( 6.99x) detect_alpha_16_full_avx512icl: 134.6 (12.28x) detect_alpha_16_limited_c: 5263.1 ( 1.00x) detect_alpha_16_limited_avx2: 797.4 ( 6.60x) detect_alpha_16_limited_avx512icl: 400.3 (13.15x) --- doc/filters.texi | 2 +- libavfilter/vf_colordetect.c | 11 ++++--- libavfilter/vf_colordetect.h | 46 +++++++++++++++++++----------- libavfilter/x86/vf_colordetect.asm | 26 +++++++++++++++-- tests/checkasm/vf_colordetect.c | 2 +- 5 files changed, 61 insertions(+), 26 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 908c98a3cf..fbd1d1959b 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -9880,7 +9880,7 @@ which indicates that this is a full range YUV source. @item alpha_mode Detect if the source contains color values above the alpha channel, which indicates that the alpha channel is independent (straight), rather than -premultiplied. +premultiplied. Also detects if the alpha plane is fully opaque or not. @item all Enable detection of all of the above properties. This is the default. @end table diff --git a/libavfilter/vf_colordetect.c b/libavfilter/vf_colordetect.c index fadb5c38f8..046fa7568c 100644 --- a/libavfilter/vf_colordetect.c +++ b/libavfilter/vf_colordetect.c @@ -197,7 +197,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->mode & COLOR_DETECT_COLOR_RANGE && s->detected_range == AVCOL_RANGE_UNSPECIFIED) ff_filter_execute(ctx, detect_range, in, NULL, nb_threads); - if (s->mode & COLOR_DETECT_ALPHA_MODE && s->detected_alpha == FF_ALPHA_UNDETERMINED) + + if (s->mode & COLOR_DETECT_ALPHA_MODE && + (s->detected_alpha == FF_ALPHA_UNDETERMINED || s->detected_alpha == FF_ALPHA_NONOPAQUE)) ff_filter_execute(ctx, detect_alpha, in, NULL, nb_threads); return ff_filter_frame(inlink->dst->outputs[0], in); @@ -218,9 +220,10 @@ static av_cold void uninit(AVFilterContext *ctx) if (s->mode & COLOR_DETECT_ALPHA_MODE) { av_log(ctx, AV_LOG_INFO, " Alpha mode: %s\n", - s->detected_alpha == FF_ALPHA_NONE ? "none" : - s->detected_alpha == FF_ALPHA_STRAIGHT ? "straight / independent" - : "undertermined"); + s->detected_alpha == FF_ALPHA_NONE ? "none" : + s->detected_alpha == FF_ALPHA_STRAIGHT ? "straight / independent" : + s->detected_alpha == FF_ALPHA_NONOPAQUE ? "undetermined" + : "fully opaque"); } } diff --git a/libavfilter/vf_colordetect.h b/libavfilter/vf_colordetect.h index 22fdbf6189..3454b64305 100644 --- a/libavfilter/vf_colordetect.h +++ b/libavfilter/vf_colordetect.h @@ -29,7 +29,8 @@ enum FFAlphaDetect { FF_ALPHA_NONE = -1, FF_ALPHA_UNDETERMINED = 0, - FF_ALPHA_STRAIGHT, + FF_ALPHA_NONOPAQUE = 0x1, ///< alpha value is less than the maximum + FF_ALPHA_STRAIGHT = 0x3, ///< alpha value is less than pixel; implies nonopaque /* No way to positively identify premultiplied alpha */ }; @@ -113,16 +114,19 @@ ff_detect_alpha_full_c(const uint8_t *color, ptrdiff_t color_stride, ptrdiff_t width, ptrdiff_t height, int alpha_max, int mpeg_range, int offset) { + uint8_t nonopaque = 0; while (height--) { - uint8_t cond = 0; - for (int x = 0; x < width; x++) - cond |= color[x] > alpha[x]; - if (cond) + uint8_t straight = 0; + for (int x = 0; x < width; x++) { + straight |= color[x] > alpha[x]; + nonopaque |= alpha[x] != alpha_max; + } + if (straight) return FF_ALPHA_STRAIGHT; color += color_stride; alpha += alpha_stride; } - return 0; + return nonopaque ? FF_ALPHA_NONOPAQUE : 0; } static inline int @@ -131,16 +135,19 @@ ff_detect_alpha_limited_c(const uint8_t *color, ptrdiff_t color_stride, ptrdiff_t width, ptrdiff_t height, int alpha_max, int mpeg_range, int offset) { + uint8_t nonopaque = 0; while (height--) { - uint8_t cond = 0; - for (int x = 0; x < width; x++) - cond |= alpha_max * color[x] - offset > mpeg_range * alpha[x]; - if (cond) + uint8_t straight = 0; + for (int x = 0; x < width; x++) { + straight |= alpha_max * color[x] - offset > mpeg_range * alpha[x]; + nonopaque |= alpha[x] != alpha_max; + } + if (straight) return FF_ALPHA_STRAIGHT; color += color_stride; alpha += alpha_stride; } - return 0; + return nonopaque ? FF_ALPHA_NONOPAQUE : 0; } static inline int @@ -149,18 +156,21 @@ ff_detect_alpha16_full_c(const uint8_t *color, ptrdiff_t color_stride, ptrdiff_t width, ptrdiff_t height, int alpha_max, int mpeg_range, int offset) { + uint8_t nonopaque = 0; while (height--) { const uint16_t *color16 = (const uint16_t *) color; const uint16_t *alpha16 = (const uint16_t *) alpha; - uint8_t cond = 0; - for (int x = 0; x < width; x++) - cond |= color16[x] > alpha16[x]; - if (cond) + uint8_t straight = 0; + for (int x = 0; x < width; x++) { + straight |= color16[x] > alpha16[x]; + nonopaque |= alpha16[x] != alpha_max; + } + if (straight) return FF_ALPHA_STRAIGHT; color += color_stride; alpha += alpha_stride; } - return 0; + return nonopaque ? FF_ALPHA_NONOPAQUE : 0; } static inline int @@ -169,17 +179,19 @@ ff_detect_alpha16_limited_c(const uint8_t *color, ptrdiff_t color_stride, ptrdiff_t width, ptrdiff_t height, int alpha_max, int mpeg_range, int offset) { + uint8_t nonopaque = 0; while (height--) { const uint16_t *color16 = (const uint16_t *) color; const uint16_t *alpha16 = (const uint16_t *) alpha; for (int x = 0; x < width; x++) { if ((int64_t) alpha_max * color16[x] - offset > (int64_t) mpeg_range * alpha16[x]) return FF_ALPHA_STRAIGHT; + nonopaque |= alpha16[x] != alpha_max; } color += color_stride; alpha += alpha_stride; } - return 0; + return nonopaque ? FF_ALPHA_NONOPAQUE : 0; } #endif /* AVFILTER_COLORDETECT_H */ diff --git a/libavfilter/x86/vf_colordetect.asm b/libavfilter/x86/vf_colordetect.asm index 53ff17cf5c..624fad2da9 100644 --- a/libavfilter/x86/vf_colordetect.asm +++ b/libavfilter/x86/vf_colordetect.asm @@ -69,8 +69,11 @@ cglobal detect_range%1, 4, 7, 5, data, stride, width, height, mpeg_min, mpeg_max RET %endmacro +%define FF_ALPHA_NONOPAQUE 1 +%define FF_ALPHA_STRAIGHT 3 + %macro detect_alpha_fn 3 ; suffix, hsuffix, range -cglobal detect_alpha%1_%3, 6, 7, 6, color, color_stride, alpha, alpha_stride, width, height, x +cglobal detect_alpha%1_%3, 6, 9, 7, color, color_stride, alpha, alpha_stride, width, height, x, ret, tmp pxor m0, m0 add colorq, widthq add alphaq, widthq @@ -79,17 +82,23 @@ cglobal detect_alpha%1_%3, 6, 7, 6, color, color_stride, alpha, alpha_stride, wi vpbroadcast%2 m3, r6m ; alpha_max vpbroadcast%2 m4, r7m ; mpeg_range vpbroadcast%2 m5, r8m ; offset +%else + vpbroadcast%1 m3, r6m ; alpha_max %endif + mova m6, m3 + xor retd, retd .lineloop: mov xq, widthq .loop: %ifidn %3, full movu m1, [colorq + xq] movu m2, [alphaq + xq] + pand m6, m2 pmaxu%1 m1, m2 %else pmovzx%1%2 m1, [colorq + xq] pmovzx%1%2 m2, [alphaq + xq] + pand m6, m2 pmull%2 m1, m3 pmull%2 m2, m4 %ifidn %1, b @@ -121,15 +130,26 @@ cglobal detect_alpha%1_%3, 6, 7, 6, color, color_stride, alpha, alpha_stride, wi %endif jnz .found +%if cpuflag(avx512) + vpandnq m1, m6, m3 ; m1 = ~m6 & m3 + vptestmq k1, m1, m1 + kortestb k1, k1 + setnz tmpb +%else + ptest m6, m3 + setnc tmpb ; CF = !(~m6 & m3) +%endif + or retb, tmpb + add colorq, color_strideq add alphaq, alpha_strideq dec heightq jg .lineloop - xor eax, eax + mov eax, retd RET .found: - mov eax, 1 + mov eax, FF_ALPHA_STRAIGHT RET %endmacro diff --git a/tests/checkasm/vf_colordetect.c b/tests/checkasm/vf_colordetect.c index 7498d6141a..9a16de7392 100644 --- a/tests/checkasm/vf_colordetect.c +++ b/tests/checkasm/vf_colordetect.c @@ -87,7 +87,7 @@ static void check_alpha_detect(int depth, enum AVColorRange range) LOCAL_ALIGNED_32(uint8_t, luma, [HEIGHT * STRIDE]); LOCAL_ALIGNED_32(uint8_t, alpha, [HEIGHT * STRIDE]); memset(luma, 0x80, HEIGHT * STRIDE); - memset(alpha, 0xFF, HEIGHT * STRIDE); + memset(alpha, 0xF0, HEIGHT * STRIDE); /* Try and force overflow */ if (depth > 8 && range == AVCOL_RANGE_MPEG) { -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
reply other threads:[~2025-08-16 16:25 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20250816162512.8E1D668D232@ffbox0-bg.ffmpeg.org \ --to=ffmpeg-devel@ffmpeg.org \ --cc=code@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git