PR #25514 opened by Marti2203 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25514 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25514.patch
When the VMnc cursor is partially off the left or top edge of the screen (cursor position minus hot spot < 0), `put_cursor()` clips the destination rectangle but still reads the cursor image and mask from their first column and row. So the visible part of the cursor is drawn with the pixels of the part that is off screen, shifted by the clipped amount. This happens with any cursor whose hot spot is not at (0,0) (for example the 32x32 cursor with hot spot (10,10) in `VMnc/VS2k5DebugDemo-01-partial.avi`) whenever it gets close to the left or top edge. Nothing is logged. The fix starts reading the cursor image and mask at the first visible column and row. The save/restore of the area under the cursor was already consistent and is unchanged. Verification (master 38c6f36f93, release and ASan builds): - A synthetic 16x8 32 bpp stream with a 4x4 cursor that has a distinct colour per pixel, placed off the left edge, off the top edge, off both, and fully on screen. Before the fix, 8, 8 and 4 composited pixels are wrong in the three clipped frames. After the fix, every pixel matches the expected glyph pixel. The unclipped frame is identical before and after. Both builds are ASan-clean. - The existing `fate-vmnc-16bit` and `fate-vmnc-32bit` pass unchanged. - The new `fate-vmnc-cursor-clip` (second patch) needs no new sample. It remuxes the first 21 frames of `VS2k5DebugDemo-01-partial.avi` and patches one cursor position update (with `run_with_patched_temp`) so the real cursor lands at (-6,-7). It fails without the fix (5 frames differ) and passes with it. This issue was found by an LLM-Enhanced Static Analysis Engine and verified manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code) From 13f87badba8e8db7b3b0f21475fc31a92f0120b7 Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Tue, 27 Oct 2026 10:47:50 +0800 Subject: [PATCH 1/2] avcodec/vmnc: fix cursor drawing when clipped at the left or top edge When the cursor is partially off the left or top edge of the screen, put_cursor() clips the destination rectangle but still reads the cursor image and mask from their first column and row. The visible part of the cursor is thus drawn with the pixels of the part that is off screen, i.e. shifted by the clipped amount. Start reading the cursor image and mask at the first visible column and row. Signed-off-by: Martin Mirchev <[email protected]> --- libavcodec/vmnc.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libavcodec/vmnc.c b/libavcodec/vmnc.c index 162e8ee9bb7..b5198dd949a 100644 --- a/libavcodec/vmnc.c +++ b/libavcodec/vmnc.c @@ -131,6 +131,7 @@ static void put_cursor(uint8_t *dst, int stride, VmncContext *c, int dx, int dy) { int i, j; int w, h, x, y; + int skip = 0; w = c->cur_w; if (c->width < c->cur_x + c->cur_w) w = c->width - c->cur_x; @@ -140,12 +141,14 @@ static void put_cursor(uint8_t *dst, int stride, VmncContext *c, int dx, int dy) x = c->cur_x; y = c->cur_y; if (x < 0) { - w += x; - x = 0; + w += x; + skip = -x; + x = 0; } if (y < 0) { - h += y; - y = 0; + h += y; + skip -= y * c->cur_w; + y = 0; } if ((w < 1) || (h < 1)) @@ -153,7 +156,7 @@ static void put_cursor(uint8_t *dst, int stride, VmncContext *c, int dx, int dy) dst += x * c->bpp2 + y * stride; if (c->bpp2 == 1) { - uint8_t *cd = c->curbits, *msk = c->curmask; + uint8_t *cd = c->curbits + skip, *msk = c->curmask + skip; for (j = 0; j < h; j++) { for (i = 0; i < w; i++) dst[i] = (dst[i] & cd[i]) ^ msk[i]; @@ -162,7 +165,7 @@ static void put_cursor(uint8_t *dst, int stride, VmncContext *c, int dx, int dy) dst += stride; } } else if (c->bpp2 == 2) { - uint16_t *cd = (uint16_t*)c->curbits, *msk = (uint16_t*)c->curmask; + uint16_t *cd = (uint16_t*)c->curbits + skip, *msk = (uint16_t*)c->curmask + skip; uint16_t *dst2; for (j = 0; j < h; j++) { dst2 = (uint16_t*)dst; @@ -173,7 +176,7 @@ static void put_cursor(uint8_t *dst, int stride, VmncContext *c, int dx, int dy) dst += stride; } } else if (c->bpp2 == 4) { - uint32_t *cd = (uint32_t*)c->curbits, *msk = (uint32_t*)c->curmask; + uint32_t *cd = (uint32_t*)c->curbits + skip, *msk = (uint32_t*)c->curmask + skip; uint32_t *dst2; for (j = 0; j < h; j++) { dst2 = (uint32_t*)dst; -- 2.54.0 From 3fbba1ef1ff225607bd97a0691311c8545ef755b Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Tue, 27 Oct 2026 10:55:37 +0800 Subject: [PATCH 2/2] fate/screen: add a test for VMnc cursor clipping Move the cursor of an existing sample partially off the left and top edges of the screen by patching one cursor position update of a remuxed copy. Signed-off-by: Martin Mirchev <[email protected]> --- tests/fate/screen.mak | 11 +++++++++++ tests/ref/fate/vmnc-cursor-clip | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/ref/fate/vmnc-cursor-clip diff --git a/tests/fate/screen.mak b/tests/fate/screen.mak index 1320d646889..833505d670b 100644 --- a/tests/fate/screen.mak +++ b/tests/fate/screen.mak @@ -114,6 +114,17 @@ FATE_VMNC += fate-vmnc-32bit fate-vmnc-32bit: CMD = framecrc -i $(TARGET_SAMPLES)/VMnc/VS2k5DebugDemo-01-partial.avi -pix_fmt rgb24 -vf scale FATE_VMNC-$(call FRAMECRC, AVI, VMNC, SCALE_FILTER) += $(FATE_VMNC) + +# Remux the first 21 frames of the 32-bit sample and set the cursor position +# update of frame 19 (at offset 271008) to (4,3). With the hot spot of the +# 32x32 cursor at (10,10), the cursor is then clipped at the left and top edges. +FATE_VMNC-$(call FRAMECRC, AVI, VMNC, SCALE_FILTER AVI_MUXER) += fate-vmnc-cursor-clip +fate-vmnc-cursor-clip: CMD = run_with_patched_temp \ + "$(FFMPEG) -nostdin -v error -i $(TARGET_SAMPLES)/VMnc/VS2k5DebugDemo-01-partial.avi \ + -c copy -frames:v 21 -fflags +bitexact -y" \ + "$(FFMPEG) -nostdin -v error -bitexact -pix_fmt rgb24 -vf scale -f framecrc - -i" \ + avi 271008 "\0000\0004\0000\0003" + FATE_SCREEN += $(FATE_VMNC-yes) fate-vmnc: $(FATE_VMNC-yes) diff --git a/tests/ref/fate/vmnc-cursor-clip b/tests/ref/fate/vmnc-cursor-clip new file mode 100644 index 00000000000..3582da66ffb --- /dev/null +++ b/tests/ref/fate/vmnc-cursor-clip @@ -0,0 +1,26 @@ +#tb 0: 1/5 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 1268x961 +#sar 0: 0/1 +0, 0, 0, 1, 3655644, 0x87973530 +0, 1, 1, 1, 3655644, 0x87973530 +0, 2, 2, 1, 3655644, 0x3c3167fd +0, 5, 5, 1, 3655644, 0x87973530 +0, 7, 7, 1, 3655644, 0x3c3167fd +0, 10, 10, 1, 3655644, 0x87973530 +0, 11, 11, 1, 3655644, 0x87973530 +0, 12, 12, 1, 3655644, 0x3c3167fd +0, 15, 15, 1, 3655644, 0x87973530 +0, 18, 18, 1, 3655644, 0x4f0da763 +0, 19, 19, 1, 3655644, 0xc95be61d +0, 20, 20, 1, 3655644, 0x14d0b350 +0, 23, 23, 1, 3655644, 0xc95be61d +0, 24, 24, 1, 3655644, 0xf90c7594 +0, 26, 26, 1, 3655644, 0x311ebfa8 +0, 49, 49, 1, 3655644, 0xa993ef3d +0, 61, 61, 1, 3655644, 0x73564014 +0, 64, 64, 1, 3655644, 0x2a6e1e8c +0, 68, 68, 1, 3655644, 0xbae02e7c +0, 72, 72, 1, 3655644, 0x55af4a2d +0, 73, 73, 1, 3655644, 0x54b7ff2d -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
