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]

Reply via email to