PR #25183 opened by Marti2203 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25183 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25183.patch
Follow-up to #25172, from its review. In `yuv2mono_X_c_template()` (the multi-tap path, e.g. when scaling vertically) the final partial byte of each row was stored as `acc`, without moving its bits to the most significant positions. For widths that are not a multiple of 8 the last pixels of every row were therefore wrong: their bits went into the padding, and the visible positions showed the preceding pixels. Example, 100x16 picture scaled to 100x4 (bicubic), last byte of each row: | input | format | before | after | |---|---|---|---| | white at x=96..97, rest black | monob | `0c` | `c0` | | white at x=96..99, rest black | monob | `0f` | `f0` | | white at x=0..95, tail black | monob | `f0` | `00` | | white at x=0..95, tail black | monow | `0f` | `f0` | The same with error diffusion and ordered dithering. The fix shifts the accumulator into place before the store, as suggested in the review. The `vsynth3-bpp1` (34x34, monow) reference changes with it: only the last byte of each row differs (1624 of 8500 bytes), PSNR against the source goes from 9.57 to 9.66, and in those last two columns the output now agrees with the thresholded source as often as in the rest of the row (0.71/0.67, before 0.55/0.52). All other sample-free FATE tests pass. The second commit adds `sws-monob-x-tail` and `sws-monow-x-tail`, which differ before the fix and match after. 🤖 Generated with [Claude Code](https://claude.com/claude-code) From 0305574fc074594d4352641200ac1665404d13c9 Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Sun, 18 Oct 2026 19:04:03 +0800 Subject: [PATCH 1/2] swscale/output: align the last partial byte in the mono _X path yuv2mono_X_c_template() stored the bits of a final partial byte without moving them to the most significant positions, so for widths that are not a multiple of 8 the last pixels of every row were wrong: their bits ended up in the padding, and the visible positions showed earlier pixels. Shift the accumulator into place before storing it. The vsynth3 (34x34) monow reference changes accordingly: only the last byte of each row differs, and its pixels now agree with the source as often as the rest of the row. Signed-off-by: Martin Mirchev <[email protected]> --- libswscale/output.c | 2 +- tests/ref/vsynth/vsynth3-bpp1 | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libswscale/output.c b/libswscale/output.c index 760dca642e..592867b268 100644 --- a/libswscale/output.c +++ b/libswscale/output.c @@ -710,7 +710,7 @@ yuv2mono_X_c_template(SwsInternal *c, const int16_t *lumFilter, c->dither_error[0][i] = err; if (i & 6) { - output_pixel(*dest, acc); + output_pixel(*dest, acc << (8 - (i & 7))); } } diff --git a/tests/ref/vsynth/vsynth3-bpp1 b/tests/ref/vsynth/vsynth3-bpp1 index 639a2b15de..a79905a1e4 100644 --- a/tests/ref/vsynth/vsynth3-bpp1 +++ b/tests/ref/vsynth/vsynth3-bpp1 @@ -1,4 +1,4 @@ -7eafab02a501b3b0ac9f854cd334f47a *tests/data/fate/vsynth3-bpp1.avi +b6e3c1ebbe2c7c00d79d76608f394026 *tests/data/fate/vsynth3-bpp1.avi 20460 tests/data/fate/vsynth3-bpp1.avi -52ae74ef7910e5b603c12288d425b9ae *tests/data/fate/vsynth3-bpp1.out.rawvideo -stddev: 84.76 PSNR: 9.57 MAXDIFF: 232 bytes: 86700/ 86700 +5a0e703a6b60942d57409654c76d2aea *tests/data/fate/vsynth3-bpp1.out.rawvideo +stddev: 83.82 PSNR: 9.66 MAXDIFF: 217 bytes: 86700/ 86700 -- 2.54.0 From 8cb3855d99a7084efcc0e7f9d3854e57ded078f9 Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Sun, 18 Oct 2026 19:04:04 +0800 Subject: [PATCH 2/2] tests/swscale: cover the last partial byte of the mono _X path Scale a 100-pixel-wide picture vertically so that the multi-tap output path is used, with distinct patterns in the last partial byte for both monochrome polarities. Signed-off-by: Martin Mirchev <[email protected]> --- tests/fate/libswscale.mak | 6 ++++++ tests/ref/fate/sws-monob-x-tail | 6 ++++++ tests/ref/fate/sws-monow-x-tail | 6 ++++++ 3 files changed, 18 insertions(+) create mode 100644 tests/ref/fate/sws-monob-x-tail create mode 100644 tests/ref/fate/sws-monow-x-tail diff --git a/tests/fate/libswscale.mak b/tests/fate/libswscale.mak index 673c28eae9..eb3730140c 100644 --- a/tests/fate/libswscale.mak +++ b/tests/fate/libswscale.mak @@ -22,6 +22,12 @@ $(SWS_SLICE_TEST-yes): tools/scale_slice_test$(EXESUF) $(SWS_SLICE_TEST-yes): REF = /dev/null FATE_LIBSWSCALE_SAMPLES += $(SWS_SLICE_TEST-yes) +# Last partial byte of each row in the multi-tap monochrome output path. +FATE_LIBSWSCALE_FFMPEG-$(call FILTERFRAMECRC, COLOR DRAWBOX SCALE FORMAT) += fate-sws-monob-x-tail \ + fate-sws-monow-x-tail +fate-sws-monob-x-tail: CMD = framecrc -lavfi color=black:s=100x16:r=1,format=yuv420p,drawbox=x=96:y=0:w=2:h=16:color=white:t=fill,scale=100:4:flags=bicubic+bitexact,format=monob -frames:v 1 +fate-sws-monow-x-tail: CMD = framecrc -lavfi color=black:s=100x16:r=1,format=yuv420p,drawbox=x=0:y=0:w=96:h=16:color=white:t=fill,scale=100:4:flags=bicubic+bitexact,format=monow -frames:v 1 + FATE_LIBSWSCALE_FFMPEG-$(call FRAMECRC, RAWVIDEO, RAWVIDEO, SCALE_FILTER) += fate-sws-yuv-colorspace \ fate-sws-yuv-range fate-sws-yuv-colorspace: tests/data/vsynth1.yuv diff --git a/tests/ref/fate/sws-monob-x-tail b/tests/ref/fate/sws-monob-x-tail new file mode 100644 index 0000000000..da9763835b --- /dev/null +++ b/tests/ref/fate/sws-monob-x-tail @@ -0,0 +1,6 @@ +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 100x4 +#sar 0: 1/4 +0, 0, 0, 1, 52, 0x3d800300 diff --git a/tests/ref/fate/sws-monow-x-tail b/tests/ref/fate/sws-monow-x-tail new file mode 100644 index 0000000000..5823283bd2 --- /dev/null +++ b/tests/ref/fate/sws-monow-x-tail @@ -0,0 +1,6 @@ +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 100x4 +#sar 0: 1/4 +0, 0, 0, 1, 52, 0x4ce003c0 -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
