Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH] vf_colordetect: add ability to detect fully opaque alpha planes (PR #20257)
@ 2025-08-16 16:25 Niklas Haas via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Niklas Haas via ffmpeg-devel @ 2025-08-16 16:25 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

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".

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2025-08-16 16:25 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-16 16:25 [FFmpeg-devel] [PATCH] vf_colordetect: add ability to detect fully opaque alpha planes (PR #20257) Niklas Haas via ffmpeg-devel

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