[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1036100: Red and blue channels are swapped when screencasting in certain applications



Package: kwin-wayland
Version: 4:5.27.2-2
Severity: important
Tags: patch

Dear Maintainer,

current version of KWin in bookworm has a bug that causes colors (red and blue) to be inverted when sharing screen (e.g. in the browser) on certain configurations using Wayland, as reported here:
https://bugs.kde.org/show_bug.cgi?id=466655

There is already a patch for that issue upstream (included in 5.27.3, also attached): https://invent.kde.org/plasma/kwin/commit/85b614e75c41cbdeb63b276162ed9777232daf28

It would be great if this problem could be fixed in Debian.

-- System Information:
Debian Release: 12.0
  APT prefers testing-security
APT policy: (500, 'testing-security'), (500, 'stable-security'), (500, 'testing')
Architecture: amd64 (x86_64)

Kernel: Linux 6.1.0-9-amd64 (SMP w/8 CPU threads; PREEMPT)
Kernel taint flags: TAINT_WARN
Locale: LANG=pl_PL.UTF-8, LC_CTYPE=pl_PL.UTF-8 (charmap=UTF-8), LANGUAGE=en_US
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages kwin-wayland depends on:
ii  kwayland-integration              5.27.2-1
ii  kwin-common                       4:5.27.2-2
ii  libc6                             2.36-9
ii  libcap2-bin                       1:2.66-3
ii  libepoxy0                         1.5.10-1
ii  libfontconfig1                    2.14.1-4
ii  libfreetype6                      2.12.1+dfsg-5
ii  libkdecorations2-5v5              4:5.27.2-1
ii  libkf5configcore5                 5.103.0-1
ii  libkf5configgui5                  5.103.0-1
ii  libkf5configwidgets5              5.103.0-1
ii  libkf5coreaddons5                 5.103.0-1
ii  libkf5crash5                      5.103.0-1
ii  libkf5dbusaddons5                 5.103.0-1
ii  libkf5globalaccel-bin             5.103.0-1
ii  libkf5globalaccel5                5.103.0-1
ii  libkf5globalaccelprivate5         5.103.0-1
ii  libkf5i18n5                       5.103.0-1
ii  libkf5idletime5                   5.103.0-2
ii  libkf5notifications5              5.103.0-1
ii  libkf5plasma5                     5.103.0-1
ii  libkf5service-bin                 5.103.0-1
ii  libkf5service5                    5.103.0-1
ii  libkf5windowsystem5               5.103.0-1
ii  libkwineffects14                  4:5.27.2-2
ii  libkwinglutils14                  4:5.27.2-2
ii  libpipewire-0.3-0                 0.3.65-3
ii  libqaccessibilityclient-qt5-0     0.4.1-1+b1
ii  libqt5core5a [qtbase-abi-5-15-8]  5.15.8+dfsg-7
ii  libqt5dbus5                       5.15.8+dfsg-7
ii  libqt5gui5                        5.15.8+dfsg-7
ii  libqt5network5                    5.15.8+dfsg-7
ii  libqt5qml5                        5.15.8+dfsg-3
ii  libqt5quick5                      5.15.8+dfsg-3
ii  libqt5widgets5                    5.15.8+dfsg-7
ii  libstdc++6                        12.2.0-14
ii  libxcb-randr0                     1.15-1
ii  libxcb-xfixes0                    1.15-1
ii  libxcb1                           1.15-1
ii  xwayland                          2:22.1.9-1

kwin-wayland recommends no packages.

kwin-wayland suggests no packages.

-- no debconf information
>From 85b614e75c41cbdeb63b276162ed9777232daf28 Mon Sep 17 00:00:00 2001
From: Aleix Pol <aleixpol@kde.org>
Date: Fri, 3 Mar 2023 19:34:17 +0100
Subject: [PATCH] screencasting: on memfd, skip the QImage step

We were using QImage as an intermediary step. GL -> QImage -> spa
buffer. While it abstracted things out neatly and eventually helped with
debugging, it was unnecessary and woudl present some handicaps, such as
the lack of a QImage::Format_BGRA.
So we just it out to download right into the buffer.

BUG: 466655
(cherry picked from commit 121454580711c409b612d06865ab9d221dcbac6b)
---
 .../screencast/outputscreencastsource.cpp     |  4 +--
 .../screencast/outputscreencastsource.h       |  2 +-
 .../screencast/regionscreencastsource.cpp     |  4 +--
 .../screencast/regionscreencastsource.h       |  2 +-
 src/plugins/screencast/screencastsource.h     |  4 ++-
 src/plugins/screencast/screencaststream.cpp   | 10 +++---
 src/plugins/screencast/screencastutils.h      | 33 ++++++++++---------
 .../screencast/windowscreencastsource.cpp     |  4 +--
 .../screencast/windowscreencastsource.h       |  2 +-
 9 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/src/plugins/screencast/outputscreencastsource.cpp b/src/plugins/screencast/outputscreencastsource.cpp
index 0c0706a2a0d..ac853171da9 100644
--- a/src/plugins/screencast/outputscreencastsource.cpp
+++ b/src/plugins/screencast/outputscreencastsource.cpp
@@ -44,11 +44,11 @@ QSize OutputScreenCastSource::textureSize() const
     return m_output->pixelSize();
 }
 
-void OutputScreenCastSource::render(QImage *image)
+void OutputScreenCastSource::render(spa_data *spa, spa_video_format format)
 {
     const std::shared_ptr<GLTexture> outputTexture = Compositor::self()->scene()->textureForOutput(m_output);
     if (outputTexture) {
-        grabTexture(outputTexture.get(), image);
+        grabTexture(outputTexture.get(), spa, format);
     }
 }
 
diff --git a/src/plugins/screencast/outputscreencastsource.h b/src/plugins/screencast/outputscreencastsource.h
index 81bf511b18a..1e12eba2005 100644
--- a/src/plugins/screencast/outputscreencastsource.h
+++ b/src/plugins/screencast/outputscreencastsource.h
@@ -27,7 +27,7 @@ public:
     quint32 drmFormat() const override;
 
     void render(GLFramebuffer *target) override;
-    void render(QImage *image) override;
+    void render(spa_data *spa, spa_video_format format) override;
     std::chrono::nanoseconds clock() const override;
 
 private:
diff --git a/src/plugins/screencast/regionscreencastsource.cpp b/src/plugins/screencast/regionscreencastsource.cpp
index daeb69d7cbf..8b747f57a62 100644
--- a/src/plugins/screencast/regionscreencastsource.cpp
+++ b/src/plugins/screencast/regionscreencastsource.cpp
@@ -110,10 +110,10 @@ void RegionScreenCastSource::render(GLFramebuffer *target)
     GLFramebuffer::popFramebuffer();
 }
 
-void RegionScreenCastSource::render(QImage *image)
+void RegionScreenCastSource::render(spa_data *spa, spa_video_format format)
 {
     ensureTexture();
-    grabTexture(m_renderedTexture.get(), image);
+    grabTexture(m_renderedTexture.get(), spa, format);
 }
 
 }
diff --git a/src/plugins/screencast/regionscreencastsource.h b/src/plugins/screencast/regionscreencastsource.h
index 09ea033248b..560c5d64dc1 100644
--- a/src/plugins/screencast/regionscreencastsource.h
+++ b/src/plugins/screencast/regionscreencastsource.h
@@ -28,7 +28,7 @@ public:
     QSize textureSize() const override;
 
     void render(GLFramebuffer *target) override;
-    void render(QImage *image) override;
+    void render(spa_data *spa, spa_video_format format) override;
     std::chrono::nanoseconds clock() const override;
 
     QRect region() const
diff --git a/src/plugins/screencast/screencastsource.h b/src/plugins/screencast/screencastsource.h
index 81d78762c99..6dcafc84c2e 100644
--- a/src/plugins/screencast/screencastsource.h
+++ b/src/plugins/screencast/screencastsource.h
@@ -7,6 +7,8 @@
 #pragma once
 
 #include <QObject>
+#include <spa/buffer/buffer.h>
+#include <spa/param/video/raw.h>
 
 namespace KWin
 {
@@ -25,7 +27,7 @@ public:
     virtual QSize textureSize() const = 0;
 
     virtual void render(GLFramebuffer *target) = 0;
-    virtual void render(QImage *image) = 0;
+    virtual void render(spa_data *spa, spa_video_format format) = 0;
     virtual std::chrono::nanoseconds clock() const = 0;
 
 Q_SIGNALS:
diff --git a/src/plugins/screencast/screencaststream.cpp b/src/plugins/screencast/screencaststream.cpp
index fad5114c061..b79c422bd68 100644
--- a/src/plugins/screencast/screencaststream.cpp
+++ b/src/plugins/screencast/screencaststream.cpp
@@ -450,20 +450,20 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
         const int bpp = data && !hasAlpha ? 3 : 4;
         const uint stride = SPA_ROUND_UP_N(size.width() * bpp, 4);
 
-        QImage dest(data, size.width(), size.height(), stride, hasAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
-        if (dest.sizeInBytes() > spa_data->maxsize) {
+        if ((stride * size.height()) > spa_data->maxsize) {
             qCDebug(KWIN_SCREENCAST) << "Failed to record frame: frame is too big";
             pw_stream_queue_buffer(pwStream, buffer);
             return;
         }
 
-        spa_data->chunk->size = dest.sizeInBytes();
-        spa_data->chunk->stride = dest.bytesPerLine();
+        spa_data->chunk->stride = stride;
+        spa_data->chunk->size = stride * size.height();
 
-        m_source->render(&dest);
+        m_source->render(spa_data, videoFormat.format);
 
         auto cursor = Cursors::self()->currentCursor();
         if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded && exclusiveContains(m_cursor.viewport, cursor->pos())) {
+            QImage dest(data, size.width(), size.height(), stride, hasAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
             QPainter painter(&dest);
             const auto position = (cursor->pos() - m_cursor.viewport.topLeft() - cursor->hotspot()) * m_cursor.scale;
             painter.drawImage(QRect{position.toPoint(), cursor->image().size()}, cursor->image());
diff --git a/src/plugins/screencast/screencastutils.h b/src/plugins/screencast/screencastutils.h
index 55efb604fc4..f20ec322c86 100644
--- a/src/plugins/screencast/screencastutils.h
+++ b/src/plugins/screencast/screencastutils.h
@@ -8,6 +8,8 @@
 
 #include "kwinglplatform.h"
 #include "kwingltexture.h"
+#include <spa/buffer/buffer.h>
+#include <spa/param/video/raw.h>
 
 namespace KWin
 {
@@ -25,28 +27,29 @@ static void mirrorVertically(uchar *data, int height, int stride)
     }
 }
 
-static GLenum closestGLType(const QImage &image)
+static GLenum closestGLType(spa_video_format format)
 {
-    switch (image.format()) {
-    case QImage::Format_RGB888:
+    switch (format) {
+    case SPA_VIDEO_FORMAT_RGB:
         return GL_RGB;
-    case QImage::Format_BGR888:
+    case SPA_VIDEO_FORMAT_BGR:
         return GL_BGR;
-    case QImage::Format_RGB32:
-    case QImage::Format_RGBX8888:
-    case QImage::Format_RGBA8888:
-    case QImage::Format_RGBA8888_Premultiplied:
+    case SPA_VIDEO_FORMAT_RGBx:
+    case SPA_VIDEO_FORMAT_RGBA:
         return GL_RGBA;
+    case SPA_VIDEO_FORMAT_BGRA:
+    case SPA_VIDEO_FORMAT_BGRx:
+        return GL_BGRA;
     default:
-        qDebug() << "unknown format" << image.format();
+        qDebug() << "unknown format" << format;
         return GL_RGBA;
     }
 }
 
-static void grabTexture(GLTexture *texture, QImage *image)
+static void grabTexture(GLTexture *texture, spa_data *spa, spa_video_format format)
 {
     const bool invert = !texture->isYInverted();
-    Q_ASSERT(texture->size() == image->size());
+    const QSize size = texture->size();
     bool isGLES = GLPlatform::instance()->isGLES();
     bool invertNeeded = isGLES ^ invert;
     const bool invertNeededAndSupported = invertNeeded && GLPlatform::instance()->supports(PackInvert);
@@ -58,11 +61,11 @@ static void grabTexture(GLTexture *texture, QImage *image)
 
     texture->bind();
     if (GLPlatform::instance()->isGLES()) {
-        glReadPixels(0, 0, image->width(), image->height(), closestGLType(*image), GL_UNSIGNED_BYTE, (GLvoid *)image->bits());
+        glReadPixels(0, 0, size.width(), size.height(), closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
     } else if (GLPlatform::instance()->glVersion() >= kVersionNumber(4, 5)) {
-        glGetTextureImage(texture->texture(), 0, closestGLType(*image), GL_UNSIGNED_BYTE, image->sizeInBytes(), image->bits());
+        glGetTextureImage(texture->texture(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->chunk->size, spa->data);
     } else {
-        glGetTexImage(texture->target(), 0, closestGLType(*image), GL_UNSIGNED_BYTE, image->bits());
+        glGetTexImage(texture->target(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
     }
 
     if (invertNeededAndSupported) {
@@ -70,7 +73,7 @@ static void grabTexture(GLTexture *texture, QImage *image)
             glPixelStorei(GL_PACK_INVERT_MESA, prev);
         }
     } else if (invertNeeded) {
-        mirrorVertically(image->bits(), image->height(), image->bytesPerLine());
+        mirrorVertically(static_cast<uchar *>(spa->data), size.height(), spa->chunk->stride);
     }
 }
 
diff --git a/src/plugins/screencast/windowscreencastsource.cpp b/src/plugins/screencast/windowscreencastsource.cpp
index 3c247a3c923..fe651ce8544 100644
--- a/src/plugins/screencast/windowscreencastsource.cpp
+++ b/src/plugins/screencast/windowscreencastsource.cpp
@@ -46,13 +46,13 @@ QSize WindowScreenCastSource::textureSize() const
     return m_window->clientGeometry().size().toSize();
 }
 
-void WindowScreenCastSource::render(QImage *image)
+void WindowScreenCastSource::render(spa_data *spa, spa_video_format format)
 {
     GLTexture offscreenTexture(hasAlphaChannel() ? GL_RGBA8 : GL_RGB8, textureSize());
     GLFramebuffer offscreenTarget(&offscreenTexture);
 
     render(&offscreenTarget);
-    grabTexture(&offscreenTexture, image);
+    grabTexture(&offscreenTexture, spa, format);
 }
 
 void WindowScreenCastSource::render(GLFramebuffer *target)
diff --git a/src/plugins/screencast/windowscreencastsource.h b/src/plugins/screencast/windowscreencastsource.h
index 69cd989d534..4efb7850a3e 100644
--- a/src/plugins/screencast/windowscreencastsource.h
+++ b/src/plugins/screencast/windowscreencastsource.h
@@ -27,7 +27,7 @@ public:
     QSize textureSize() const override;
 
     void render(GLFramebuffer *target) override;
-    void render(QImage *image) override;
+    void render(spa_data *spa, spa_video_format format) override;
     std::chrono::nanoseconds clock() const override;
 
 private:
-- 
GitLab



Reply to: