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

Git pushed a commit to branch master
in repository ffmpeg.

commit 618492489278457bbf14833d57568bd04bf459f0
Author:     Niklas Haas <[email protected]>
AuthorDate: Tue Dec 9 10:52:07 2025 +0100
Commit:     Niklas Haas <[email protected]>
CommitDate: Mon Dec 15 14:31:58 2025 +0000

    swscale/format: don't add chroma noise when dithering grayscale content
    
    On the surface, this trades a tiny bit of PSNR for not introducing chroma
    noise into grayscale images. However, the main reason for this change is
    actually motivated by a desire to avoid regressing the status quo of
    duplicating swizzles being able to be commuted past dither ops.
---
 libswscale/format.c         | 17 ++++++++++++++++-
 tests/ref/fate/sws-ops-list |  2 +-
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/libswscale/format.c b/libswscale/format.c
index 7b4500ace4..be03adcdca 100644
--- a/libswscale/format.c
+++ b/libswscale/format.c
@@ -1162,11 +1162,11 @@ static int fmt_dither(SwsContext *ctx, SwsOpList *ops,
 {
     SwsDither mode = ctx->dither;
     SwsDitherOp dither;
+    const int bpc = dst.desc->comp[0].depth;
 
     if (mode == SWS_DITHER_AUTO) {
         /* Visual threshold of perception: 12 bits for SDR, 14 bits for HDR */
         const int jnd_bits = trc_is_hdr(dst.color.trc) ? 14 : 12;
-        const int bpc = dst.desc->comp[0].depth;
         mode = bpc >= jnd_bits ? SWS_DITHER_NONE : SWS_DITHER_BAYER;
     }
 
@@ -1203,6 +1203,21 @@ static int fmt_dither(SwsContext *ctx, SwsOpList *ops,
         for (int i = 0; i < 4; i++)
             dither.y_offset[i] = offsets_16x16[i];
 
+        if (src.desc->nb_components < 3 && bpc >= 8) {
+            /**
+             * For high-bit-depth sources without chroma, use same matrix
+             * offset for all color channels. This prevents introducing color
+             * noise in grayscale images; and also allows optimizing the dither
+             * operation. Skipped for low bit depth (<8 bpc) as the loss in
+             * PSNR, from the inability to diffuse error among all three
+             * channels, can be substantial.
+             *
+             * This shifts: { X, Y, Z, W } -> { X, X, X, Y }
+             */
+            dither.y_offset[3] = dither.y_offset[1];
+            dither.y_offset[1] = dither.y_offset[2] = dither.y_offset[0];
+        }
+
         return ff_sws_op_list_append(ops, &(SwsOp) {
             .op     = SWS_OP_DITHER,
             .type   = type,
diff --git a/tests/ref/fate/sws-ops-list b/tests/ref/fate/sws-ops-list
index caa39b44a3..bff44d5446 100644
--- a/tests/ref/fate/sws-ops-list
+++ b/tests/ref/fate/sws-ops-list
@@ -1 +1 @@
-0f38d0a1cb1f9367352c92b23bcb954e
+0c7f5082617b0b4b83111b67bec74f2d

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

Reply via email to