PR #25172 opened by Marti2203
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25172
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25172.patch

The `SWS_DITHER_ED` branches of `yuv2mono_1_c_template()` and 
`yuv2mono_2_c_template()` only store a byte when `(i & 7) == 6`. For widths 
whose last pixel pair does not end a byte, the final partial byte of each row 
is never written and keeps whatever the destination buffer held. 
`yuv2mono_X_c_template()` already writes this tail (`if (i & 6)` after the 
loop).

Reproduction (uniformly white input, so every valid bit should be 1):

```
ffmpeg -f lavfi -i color=c=white:s=100x8:d=0.04 -frames:v 1 \
       -sws_flags +error_diffusion -vf format=monob -f rawvideo out.raw
```

Last byte of rows 0-3 (pixels 96-99 are the top 4 bits):

| width | master (ED) | patched (ED) | default dither |
|-------|-------------|--------------|----------------|
| 100   | ff 00 00 00 | ff f0 f0 f0  | f0 f0 f0 70    |
| 102   | ff 00 00 00 | ff fc fc fc  | fc fc fc 7c    |
| 64    | ff ff ff ff | ff ff ff ff  | ff ff ff 7f    |

The patch writes the pending bits after the loop, shifted into the high bits as 
monob expects. The 28 FATE tests matching `monob`/`monow` (`pixfmt-*`, 
`filter-pixdesc-*`, `lavf-*`) pass.

This issue was found by an LLM-Enhanced Static Analysis Engine and verified 
manually.



>From 926e5ffc1aa465416463270bdbb50202d5093557 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Sun, 18 Oct 2026 16:30:57 +0800
Subject: [PATCH] swscale/output: write the last partial byte in the mono
 error-diffusion paths

The SWS_DITHER_ED branches of yuv2mono_1_c_template() and
yuv2mono_2_c_template() only store a byte when (i & 7) == 6, so for widths
whose last pixel pair does not end a byte the final partial byte of each
row is never written and keeps whatever the destination held. Converting a
white 100x8 frame with -sws_flags +error_diffusion -pix_fmt monob leaves the
last 4 pixels of rows 1-7 black (0x00 instead of 0xf0).

Write the pending bits after the loop, shifted into the high bits as monob
expects, like the non-ED paths.

Signed-off-by: Martin Mirchev <[email protected]>
---
 libswscale/output.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libswscale/output.c b/libswscale/output.c
index 760dca642e..5cc5cf026d 100644
--- a/libswscale/output.c
+++ b/libswscale/output.c
@@ -749,6 +749,8 @@ yuv2mono_2_c_template(SwsInternal *c, const int16_t *buf[2],
                 output_pixel(*dest++, acc);
         }
         c->dither_error[0][i] = err;
+        if (i & 6)
+            output_pixel(*dest, acc << (8 - (i & 7)));
     } else {
         for (i = 0; i < dstW; i += 8) {
             int Y;
@@ -807,6 +809,8 @@ yuv2mono_1_c_template(SwsInternal *c, const int16_t *buf0,
                 output_pixel(*dest++, acc);
         }
         c->dither_error[0][i] = err;
+        if (i & 6)
+            output_pixel(*dest, acc << (8 - (i & 7)));
     } else {
         for (i = 0; i < dstW; i += 8) {
             unsigned acc = 0;
-- 
2.54.0

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

Reply via email to