This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit b99247953902b62986801053a9f252dc8fa11a75
Author:     Niklas Haas <[email protected]>
AuthorDate: Sat Jun 20 16:12:51 2026 +0200
Commit:     Niklas Haas <[email protected]>
CommitDate: Tue Jun 23 11:48:13 2026 +0000

    swscale/ops: keep track of copied/cleared components
    
    These represent components which have not (yet) been modified from their
    input values (i.e. after a read, or clear). Such components can be
    basically passed through via a refcopy (where applicable), as well as 
helping
    to distinguish dissimilar types of plane for (plane splitting).
    
    Generates benign diffs like:
    
     gray 16x16 -> yuv444p 16x16:
    -  [ u8 +XXX] SWS_OP_READ         : 1 elem(s) planar >> 0
    +  [ u8 =XXX] SWS_OP_READ         : 1 elem(s) planar >> 0
         min: {0 _ _ _}, max: {255 _ _ _}
    -  [ u8 +XXX] SWS_OP_CONVERT      : u8 -> f32
    +  [ u8 =XXX] SWS_OP_CONVERT      : u8 -> f32
         min: {0 _ _ _}, max: {255 _ _ _}
       [f32 .XXX] SWS_OP_LINEAR       : luma [[73/85 0 0 0 16] [0 1 0 0 0] [0 0 
1 0 0] [0 0 0 1 0]]
         min: {16 _ _ _}, max: {235 _ _ _}
       [f32 .XXX] SWS_OP_DITHER       : 16x16 matrix + {0 -1 -1 -1}
         min: {16.001953 _ _ _}, max: {235.998047 _ _ _}
       [f32 +XXX] SWS_OP_CONVERT      : f32 -> u8
         min: {16 _ _ _}, max: {235 _ _ _}
    -  [ u8 +++X] SWS_OP_CLEAR        : {_ 128 128 _}
    +  [ u8 +$$X] SWS_OP_CLEAR        : {_ 128 128 _}
         min: {16 128 128 _}, max: {235 128 128 _}
       [ u8 XXXX] SWS_OP_WRITE        : 3 elem(s) planar >> 0
    -    (X = unused, z = byteswapped, + = exact, 0 = zero)
    +    ('X' unused, 'z' byteswapped, '=' copied, '$' const, '+' integer, '0' 
zero)
      translated micro-ops:
         u8_read_planar_x
         u8_to_f32_x
         f32_linear_x_x000x
         f32_dither_x_0_16x16
         f32_to_u8_x
         u8_clear_yz_xx
         u8_write_planar_xyz
    
    Sponsored-by: Sovereign Tech Fund
    Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops.c            | 91 ++++++++++++++++++++++++++-------------------
 libswscale/ops.h            |  2 +
 tests/ref/fate/sws-ops-list |  2 +-
 3 files changed, 56 insertions(+), 39 deletions(-)

diff --git a/libswscale/ops.c b/libswscale/ops.c
index ec7f358005..28739276f1 100644
--- a/libswscale/ops.c
+++ b/libswscale/ops.c
@@ -305,11 +305,14 @@ void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
     av_unreachable("Invalid operation type!");
 }
 
-/* merge_comp_flags() forms a monoid with SWS_COMP_IDENTITY as the null 
element */
 enum {
-    SWS_COMP_IDENTITY = SWS_COMP_ZERO | SWS_COMP_EXACT,
+    SWS_COMP_IDENTITY = SWS_COMP_ZERO | SWS_COMP_EXACT |
+                        SWS_COMP_COPY | SWS_COMP_CONST,
+
+    SWS_COMP_DIRTY = ~(SWS_COMP_COPY | SWS_COMP_CONST),
 };
 
+/* merge_comp_flags() forms a monoid with SWS_COMP_IDENTITY as the null 
element */
 static SwsCompFlags merge_comp_flags(SwsCompFlags a, SwsCompFlags b)
 {
     const SwsCompFlags flags_or  = SWS_COMP_GARBAGE;
@@ -317,29 +320,13 @@ static SwsCompFlags merge_comp_flags(SwsCompFlags a, 
SwsCompFlags b)
     return ((a & b) & flags_and) | ((a | b) & flags_or);
 }
 
-/* Linearly propagate flags per component */
-static void propagate_flags(SwsOp *op, const SwsComps *prev)
-{
-    for (int i = 0; i < 4; i++)
-        op->comps.flags[i] = prev->flags[i];
-}
-
-/* Clear undefined values in dst with src */
-static void clear_undefined_values(AVRational dst[4], const AVRational src[4])
-{
-    for (int i = 0; i < 4; i++) {
-        if (dst[i].den == 0)
-            dst[i] = src[i];
-    }
-}
-
 static void apply_filter_weights(SwsComps *comps, const SwsComps *prev,
                                  const SwsFilterWeights *weights)
 {
     const AVRational posw = { weights->sum_positive, SWS_FILTER_SCALE };
     const AVRational negw = { weights->sum_negative, SWS_FILTER_SCALE };
     for (int i = 0; i < 4; i++) {
-        comps->flags[i] = prev->flags[i];
+        comps->flags[i] = prev->flags[i] & SWS_COMP_DIRTY;
         /* Only point sampling preserves exactness */
         if (weights->filter_size != 1)
             comps->flags[i] &= ~SWS_COMP_EXACT;
@@ -391,9 +378,19 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                 }
 
                 av_assert0(!(ops->comps_src.flags[idx] & SWS_COMP_GARBAGE));
-                op->comps.flags[i] = ops->comps_src.flags[idx];
+                op->comps.flags[i] = ops->comps_src.flags[idx] & 
SWS_COMP_DIRTY;
                 op->comps.min[i]   = ops->comps_src.min[idx];
                 op->comps.max[i]   = ops->comps_src.max[idx];
+
+                /**
+                 * Don't mark packed or fractional reads as a copy, because the
+                 * read operation implicitly unpacks the data into separate
+                 * components. The only case in which op lists involving such
+                 * reads can be refcopies is in the case of a true noop, which
+                 * is already covered by the no-op check.
+                 */
+                if (op->rw.mode == SWS_RW_PLANAR && !op->rw.frac)
+                    op->comps.flags[i] |= SWS_COMP_COPY;
             }
 
             if (op->rw.filter.op) {
@@ -403,7 +400,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
             break;
         case SWS_OP_SWAP_BYTES:
             for (int i = 0; i < 4; i++) {
-                op->comps.flags[i] = prev.flags[i] ^ SWS_COMP_SWAPPED;
+                op->comps.flags[i] = (prev.flags[i] ^ SWS_COMP_SWAPPED) & 
SWS_COMP_DIRTY;
                 op->comps.min[i]   = prev.min[i];
                 op->comps.max[i]   = prev.max[i];
             }
@@ -411,27 +408,35 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
         case SWS_OP_WRITE:
             for (int i = 0; i < op->rw.elems; i++)
                 av_assert1(!(prev.flags[i] & SWS_COMP_GARBAGE));
-            av_fallthrough;
+            for (int i = 0; i < 4; i++)
+                op->comps.flags[i] = prev.flags[i];
+            break;
         case SWS_OP_LSHIFT:
         case SWS_OP_RSHIFT:
-            propagate_flags(op, &prev);
+            for (int i = 0; i < 4; i++)
+                op->comps.flags[i] = prev.flags[i] & SWS_COMP_DIRTY;
             break;
         case SWS_OP_MIN:
-            propagate_flags(op, &prev);
-            clear_undefined_values(op->comps.max, op->clamp.limit);
-            break;
-        case SWS_OP_MAX:
-            propagate_flags(op, &prev);
-            clear_undefined_values(op->comps.min, op->clamp.limit);
+        case SWS_OP_MAX: {
+            AVRational *bound = op->op == SWS_OP_MIN ? op->comps.max : 
op->comps.min;
+            for (int i = 0; i < 4; i++) {
+                op->comps.flags[i] = prev.flags[i];
+                if (op->clamp.limit[i].den)
+                    op->comps.flags[i] &= SWS_COMP_DIRTY;
+                if (!bound[i].den) /* reset undefined bounds to known range */
+                    bound[i] = op->clamp.limit[i];
+            }
             break;
+        }
         case SWS_OP_DITHER:
             for (int i = 0; i < 4; i++) {
-                op->comps.min[i] = prev.min[i];
-                op->comps.max[i] = prev.max[i];
+                op->comps.flags[i] = prev.flags[i];
+                op->comps.min[i]   = prev.min[i];
+                op->comps.max[i]   = prev.max[i];
                 if (op->dither.y_offset[i] < 0)
                     continue;
                 /* Strip zero flag because of the nonzero dithering offset */
-                op->comps.flags[i] = prev.flags[i] & ~SWS_COMP_ZERO;
+                op->comps.flags[i] &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
                 op->comps.min[i] = av_add_q(op->comps.min[i], op->dither.min);
                 op->comps.max[i] = av_add_q(op->comps.max[i], op->dither.max);
             }
@@ -441,7 +446,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                 const int pattern = op->pack.pattern[i];
                 if (pattern) {
                     av_assert1(pattern < 32);
-                    op->comps.flags[i] = prev.flags[0];
+                    op->comps.flags[i] = prev.flags[0] & SWS_COMP_DIRTY;
                     op->comps.min[i]   = Q(0);
                     op->comps.max[i]   = Q((1ULL << pattern) - 1);
                 } else
@@ -456,13 +461,13 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                 if (i > 0) /* clear remaining comps for sanity */
                     op->comps.flags[i] = SWS_COMP_GARBAGE;
             }
-            op->comps.flags[0] = flags;
+            op->comps.flags[0] = flags & SWS_COMP_DIRTY;
             break;
         }
         case SWS_OP_CLEAR:
             for (int i = 0; i < 4; i++) {
                 if (SWS_COMP_TEST(op->clear.mask, i)) {
-                    op->comps.flags[i] = 0;
+                    op->comps.flags[i] = SWS_COMP_CONST;
                     if (op->clear.value[i].num == 0)
                         op->comps.flags[i] |= SWS_COMP_ZERO;
                     if (op->clear.value[i].den == 1)
@@ -479,6 +484,8 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
         case SWS_OP_CONVERT:
             for (int i = 0; i < 4; i++) {
                 op->comps.flags[i] = prev.flags[i];
+                if (!(prev.flags[i] & SWS_COMP_EXACT) || op->convert.expand)
+                    op->comps.flags[i] &= SWS_COMP_DIRTY;
                 if (ff_sws_pixel_type_is_int(op->convert.to))
                     op->comps.flags[i] |= SWS_COMP_EXACT;
             }
@@ -487,6 +494,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
             for (int i = 0; i < 4; i++) {
                 SwsCompFlags flags = SWS_COMP_IDENTITY;
                 AVRational min = Q(0), max = Q(0);
+                bool first = true;
                 for (int j = 0; j < 4; j++) {
                     const AVRational k = op->lin.m[i][j];
                     AVRational mink = av_mul_q(prev.min[j], k);
@@ -499,10 +507,13 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                             FFSWAP(AVRational, mink, maxk);
                         min = av_add_q(min, mink);
                         max = av_add_q(max, maxk);
+                        if (!first || av_cmp_q(k, Q(1)))
+                            flags &= SWS_COMP_DIRTY;
+                        first = false;
                     }
                 }
                 if (op->lin.m[i][4].num) { /* nonzero offset */
-                    flags &= ~SWS_COMP_ZERO;
+                    flags &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
                     if (op->lin.m[i][4].den != 1) /* fractional offset */
                         flags &= ~SWS_COMP_EXACT;
                     min = av_add_q(min, op->lin.m[i][4]);
@@ -515,7 +526,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
             break;
         case SWS_OP_SCALE:
             for (int i = 0; i < 4; i++) {
-                op->comps.flags[i] = prev.flags[i];
+                op->comps.flags[i] = prev.flags[i] & SWS_COMP_DIRTY;
                 if (op->scale.factor.den != 1) /* fractional scale */
                     op->comps.flags[i] &= ~SWS_COMP_EXACT;
                 if (op->scale.factor.num < 0)
@@ -831,6 +842,10 @@ static char describe_comp_flags(SwsCompFlags flags)
         return '0';
     else if (flags & SWS_COMP_SWAPPED)
         return 'z';
+    else if (flags & SWS_COMP_CONST)
+        return '$';
+    else if (flags & SWS_COMP_COPY)
+        return '=';
     else if (flags & SWS_COMP_EXACT)
         return '+';
     else
@@ -1026,7 +1041,7 @@ void ff_sws_op_list_print(void *log, int lev, int 
lev_extra,
 
     }
 
-    av_log(log, lev, "    (X = unused, z = byteswapped, + = exact, 0 = 
zero)\n");
+    av_log(log, lev, "    ('X' unused, 'z' byteswapped, '=' copied, '$' const, 
'+' integer, '0' zero)\n");
 }
 
 #define DUMMY_SIZE 16
diff --git a/libswscale/ops.h b/libswscale/ops.h
index 25ab7b752f..a7d8066b94 100644
--- a/libswscale/ops.h
+++ b/libswscale/ops.h
@@ -74,6 +74,8 @@ typedef enum SwsCompFlags {
     SWS_COMP_EXACT   = 1 << 1, /* value is an exact integer */
     SWS_COMP_ZERO    = 1 << 2, /* known to be a constant zero */
     SWS_COMP_SWAPPED = 1 << 3, /* byte order is swapped */
+    SWS_COMP_COPY    = 1 << 4, /* value is unmodified from the source plane */
+    SWS_COMP_CONST   = 1 << 5, /* value is a fixed constant */
 } SwsCompFlags;
 
 typedef struct SwsComps {
diff --git a/tests/ref/fate/sws-ops-list b/tests/ref/fate/sws-ops-list
index f5ed4c676d..d62bc51dbe 100644
--- a/tests/ref/fate/sws-ops-list
+++ b/tests/ref/fate/sws-ops-list
@@ -1 +1 @@
-31a9d8a35355bed66e8c64dda5246828
+e9435c106ceeb7a19638e982380f3660

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to