PR #25178 opened by Marti2203 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25178 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25178.patch
`draw_line()`, used by the oscilloscope filter for its traces, wrote `nb_components` values starting at the pixel for single-plane formats: - for 16-bit packed formats, component `i` was written at byte offset `x * pixelstep + i` instead of `+ 2 * i`, so the 16-bit writes overlapped; - for formats with leading padding (`0rgb`, `0bgr`), only the first three bytes were written, so the last colour component was never set. To check this, the same picture is rendered in each format and compared with the `rgb24` output after conversion back to `rgb24`: ``` ffmpeg -f lavfi -i "testsrc2=s=640x360:d=0.04,format=rgb24,format=rgb48le,oscilloscope=x=0.5:y=0.5:s=1:t=1:g=0:st=0:sc=0" -frames:v 1 -pix_fmt rgb24 -f rawvideo out.raw ``` Before the patch, 1391 trace pixels have the wrong colour for `rgb48le`, `rgb48be`, `bgr48le` and `bgr48be` (e.g. the blue trace drawn green or cyan), and 722 / 781 for `0rgb` / `0bgr`. With the patch, all of these match `rgb24` exactly. Formats that were already correct (`rgba64le/be`, `bgra64le`, `bgr0`, `rgb0`, `argb`, `gbrp16le`) are unchanged, and `yuv420p`, `yuv444p16le` and `gray16le` output is byte-identical. The patch copies the whole pixel from the prepared `FFDrawColor`, as `ff_fill_rectangle()` does. There is no FATE coverage for this filter. This issue was found by an LLM-Enhanced Static Analysis Engine and verified manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code) From d54ec3212b373f10b7296a653454dcf556ddc8ed Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Sun, 18 Oct 2026 17:35:38 +0800 Subject: [PATCH] avfilter/vf_datascope: copy the whole pixel when drawing oscilloscope traces For single-plane formats draw_line() wrote nb_components values starting at the pixel. For 16-bit packed formats the component offset was not scaled by 2, so the 16-bit writes overlapped, and for formats with leading padding (0rgb, 0bgr) the last component was never written. The traces were drawn in the wrong colours for rgb48/bgr48 and 0rgb/0bgr. Copy the whole pixel from the prepared colour, as ff_fill_rectangle() does. Signed-off-by: Martin Mirchev <[email protected]> --- libavfilter/vf_datascope.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c index 8d96d92b57..3a705c0f70 100644 --- a/libavfilter/vf_datascope.c +++ b/libavfilter/vf_datascope.c @@ -837,24 +837,18 @@ static void draw_line(FFDrawContext *draw, int x0, int y0, int x1, int y1, int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; int err = (dx > dy ? dx : -dy) / 2, e2; - int p, i; + int p; for (;;) { if (x0 >= 0 && y0 >= 0 && x0 < out->width && y0 < out->height) { - for (p = 0; p < draw->nb_planes; p++) { - if (draw->desc->comp[p].depth == 8) { - if (draw->nb_planes == 1) { - for (i = 0; i < draw->desc->nb_components; i++) { - out->data[0][y0 * out->linesize[0] + x0 * draw->pixelstep[0] + i] = color->comp[0].u8[i]; - } - } else { + if (draw->nb_planes == 1) { + /* packed: copy the whole pixel, as ff_fill_rectangle() does */ + memcpy(out->data[0] + y0 * out->linesize[0] + x0 * draw->pixelstep[0], + color->comp[0].u8, draw->pixelstep[0]); + } else { + for (p = 0; p < draw->nb_planes; p++) { + if (draw->desc->comp[p].depth == 8) { out->data[p][out->linesize[p] * (y0 >> draw->vsub[p]) + (x0 >> draw->hsub[p])] = color->comp[p].u8[0]; - } - } else { - if (draw->nb_planes == 1) { - for (i = 0; i < draw->desc->nb_components; i++) { - AV_WN16(out->data[0] + y0 * out->linesize[0] + (x0 * draw->pixelstep[0] + i), color->comp[0].u16[i]); - } } else { AV_WN16(out->data[p] + out->linesize[p] * (y0 >> draw->vsub[p]) + (x0 >> draw->hsub[p]) * 2, color->comp[p].u16[0]); } -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
