Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-125435

Transparent lines with windows having alpha, platform devicePixelRatio > 1 and fractional QHighDpiScaling factor

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 5.15, 6.2, 6.5, 6.7, 6.8
    • None

    Description

      I have personally caught that with high-dpi downscaling on Wayland, although during debugging I noticed that this happens due to filling the rect with transparency in QWaylandShmBackingStore::beginPaint and is reproducible even if I set system scaling to 200% (platform devicePixelRatio = 2) and then apply QT_SCALE_FACTOR=1.25 on top (QHighDpiScaling::factor = 1.25, cross-platform devicePixelRatio = 2.5). Such code exists in almost every platform's Q*BackingStore::beginPaint which perhaps means this should be reproducible on macOS, too. I tried to move the code to the cross-platform QBackingStore::beginPaint and that fixed the issue. This perhaps means QHighDpiScaling and QPainter have a different rounding algorithm?

      Here's the patch I tried to fix the issue locally:

      diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
      index ac89e106c8..6f81b4f7c9 100644
      --- a/src/gui/painting/qbackingstore.cpp
      +++ b/src/gui/painting/qbackingstore.cpp
      @@ -143,6 +143,14 @@ void QBackingStore::beginPaint(const QRegion &region)
                       new QImage(source->bits(), source->width(), source->height(), source->bytesPerLine(), source->format()));
       
               d_ptr->highDpiBackingstore->setDevicePixelRatio(d_ptr->backingStoreDevicePixelRatio());
      +
      +        if (d_ptr->highDpiBackingstore->hasAlphaChannel()) {
      +            QPainter p(d_ptr->highDpiBackingstore.data());
      +            p.setCompositionMode(QPainter::CompositionMode_Source);
      +            const QColor blank = Qt::transparent;
      +            for (const QRect &rect : region)
      +                p.fillRect(rect, blank);
      +        }
           } else {
               d_ptr->highDpiBackingstore.reset();
           }
      diff --git a/src/gui/painting/qrasterbackingstore.cpp b/src/gui/painting/qrasterbackingstore.cpp
      index 3b3ef2fd2e..6d257a8ee7 100644
      --- a/src/gui/painting/qrasterbackingstore.cpp
      +++ b/src/gui/painting/qrasterbackingstore.cpp
      @@ -71,7 +71,7 @@ void QRasterBackingStore::beginPaint(const QRegion &region)
                   m_image.fill(Qt::transparent);
           }
       
      -    if (!m_image.hasAlphaChannel())
      +    if (backingStore()->paintDevice() != paintDevice() || !m_image.hasAlphaChannel())
               return;
       
           QPainter painter(&m_image);
      diff --git a/src/opengl/qopenglcompositorbackingstore.cpp b/src/opengl/qopenglcompositorbackingstore.cpp
      index 8efccce579..cc576be132 100644
      --- a/src/opengl/qopenglcompositorbackingstore.cpp
      +++ b/src/opengl/qopenglcompositorbackingstore.cpp
      @@ -3,6 +3,7 @@
       
       #include <QtGui/QOpenGLContext>
       #include <QtGui/QWindow>
      +#include <QtGui/QBackingStore>
       #include <QtGui/QPainter>
       #include <qpa/qplatformbackingstore.h>
       #include <private/qwindow_p.h>
      @@ -235,7 +236,7 @@ void QOpenGLCompositorBackingStore::beginPaint(const QRegion &region)
       {
           m_dirty |= region;
       
      -    if (m_image.hasAlphaChannel()) {
      +    if (backingStore()->paintDevice() == paintDevice() && m_image.hasAlphaChannel()) {
               QPainter p(&m_image);
               p.setCompositionMode(QPainter::CompositionMode_Source);
               for (const QRect &r : region)
      diff --git a/src/platformsupport/fbconvenience/qfbbackingstore.cpp b/src/platformsupport/fbconvenience/qfbbackingstore.cpp
      index 8443c77dd3..6e164dddc4 100644
      --- a/src/platformsupport/fbconvenience/qfbbackingstore.cpp
      +++ b/src/platformsupport/fbconvenience/qfbbackingstore.cpp
      @@ -7,6 +7,7 @@
       
       #include <qpa/qplatformwindow.h>
       #include <QtGui/qscreen.h>
      +#include <QtGui/qbackingstore.h>
       #include <QtGui/qpainter.h>
       
       QT_BEGIN_NAMESPACE
      @@ -65,7 +66,7 @@ void QFbBackingStore::beginPaint(const QRegion &region)
       {
           lock();
       
      -    if (mImage.hasAlphaChannel()) {
      +    if (backingStore()->paintDevice() == paintDevice() && mImage.hasAlphaChannel()) {
               QPainter p(&mImage);
               p.setCompositionMode(QPainter::CompositionMode_Source);
               for (const QRect &r : region)
      diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
      index 8f50bc5834..53bc3fe921 100644
      --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
      +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
      @@ -10,6 +10,7 @@
       
       #include <QtCore/qmath.h>
       #include <QtCore/private/qcore_mac_p.h>
      +#include <QtGui/qbackingstore.h>
       #include <QtGui/qpainter.h>
       
       #include <QuartzCore/CATransaction.h>
      @@ -97,7 +98,7 @@ void QCALayerBackingStore::beginPaint(const QRegion &region)
           // Although undocumented, QBackingStore::beginPaint expects the painted region
           // to be cleared before use if the window has a surface format with an alpha.
           // Fresh IOSurfaces are already cleared, so we don't need to clear those.
      -    if (m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) {
      +    if (backingStore()->paintDevice() == paintDevice() && m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) {
               qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use";
               QPainter painter(m_buffers.back()->asImage());
               painter.setCompositionMode(QPainter::CompositionMode_Source);
      diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.cpp
      index 7ababe6c65..1a84013ec2 100644
      --- a/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.cpp
      +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.cpp
      @@ -12,6 +12,7 @@
       
       #include "qwindowscontext.h"
       
      +#include <QtGui/qbackingstore.h>
       #include <QtGui/qpainter.h>
       #include <QtGui/qwindow.h>
       #include <QtCore/qdebug.h>
      @@ -53,6 +54,9 @@ void QWindowsDirect2DBackingStore::beginPaint(const QRegion &region)
           QPixmap *pixmap = nativeWindow(window())->pixmap();
           bitmap(pixmap)->deviceContext()->begin();
       
      +    if (backingStore()->paintDevice() != paintDevice())
      +        return;
      +
           QPainter painter(pixmap);
           QColor clear(Qt::transparent);
       
      diff --git a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
      index cf80e44f84..a52d7effad 100644
      --- a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
      +++ b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
      @@ -7,6 +7,7 @@
       #include "qqnxglobal.h"
       
       #include <QtCore/QDebug>
      +#include <QtGui/QBackingStore>
       
       #include <errno.h>
       
      @@ -96,7 +97,7 @@ void QQnxRasterBackingStore::beginPaint(const QRegion &region)
       
           platformWindow()->adjustBufferSize();
       
      -    if (window()->requestedFormat().alphaBufferSize() > 0) {
      +    if (backingStore()->paintDevice() == paintDevice() && window()->requestedFormat().alphaBufferSize() > 0) {
               auto platformScreen = static_cast<QQnxScreen *>(platformWindow()->screen());
               for (const QRect &r : region) {
                   // Clear transparent regions
      diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
      index f6d219dbde..80aa1319b4 100644
      --- a/src/plugins/platforms/wasm/qwasmbackingstore.cpp
      +++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
      @@ -121,7 +121,7 @@ void QWasmBackingStore::beginPaint(const QRegion &region)
       
           QPainter painter(&m_image);
       
      -    if (m_image.hasAlphaChannel()) {
      +    if (backingStore()->paintDevice() == paintDevice() && m_image.hasAlphaChannel()) {
               painter.setCompositionMode(QPainter::CompositionMode_Source);
               const QColor blank = Qt::transparent;
               for (const QRect &rect : region)
      diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
      index 0f9d0172d9..ad71a6862b 100644
      --- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp
      +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
      @@ -6,6 +6,7 @@
       #include "qwindowscontext.h"
       
       #include <QtGui/qwindow.h>
      +#include <QtGui/qbackingstore.h>
       #include <QtGui/qpainter.h>
       #include <QtGui/private/qwindowsnativeimage_p.h>
       #include <private/qhighdpiscaling_p.h>
      @@ -157,7 +158,7 @@ void QWindowsBackingStore::beginPaint(const QRegion &region)
           if (QWindowsContext::verbose > 1)
               qCDebug(lcQpaBackingStore) <<__FUNCTION__ << region;
       
      -    if (m_alphaNeedsFill) {
      +    if (backingStore()->paintDevice() == paintDevice() && m_alphaNeedsFill) {
               QPainter p(&m_image->image());
               p.setCompositionMode(QPainter::CompositionMode_Source);
               const QColor blank = Qt::transparent;
      diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
      index 0659cf9fc4..95b8213bc0 100644
      --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
      +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
      @@ -21,6 +21,7 @@
       #include <unistd.h>
       
       #include <qdebug.h>
      +#include <qbackingstore.h>
       #include <qpainter.h>
       #include <qscreen.h>
       #include <QtGui/private/qhighdpiscaling_p.h>
      @@ -768,7 +769,7 @@ void QXcbBackingStore::beginPaint(const QRegion &region)
           m_paintRegions.push(region);
           m_image->preparePaint(region);
       
      -    if (m_image->hasAlpha()) {
      +    if (backingStore()->paintDevice() == paintDevice() && m_image->hasAlpha()) {
               QPainter p(paintDevice());
               p.setCompositionMode(QPainter::CompositionMode_Source);
               const QColor blank = Qt::transparent;
      
      
      diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp
      index 02d830ad..aadebac0 100644
      --- a/src/client/qwaylandshmbackingstore.cpp
      +++ b/src/client/qwaylandshmbackingstore.cpp
      @@ -10,6 +10,7 @@
       #include <QtCore/qdebug.h>
       #include <QtCore/qstandardpaths.h>
       #include <QtCore/qtemporaryfile.h>
      +#include <QtGui/QBackingStore>
       #include <QtGui/QPainter>
       #include <QtGui/QTransform>
       #include <QMutexLocker>
      @@ -171,7 +172,7 @@ void QWaylandShmBackingStore::beginPaint(const QRegion &region)
           mPainting = true;
           ensureSize();
       
      -    if (mBackBuffer->image()->hasAlphaChannel()) {
      +    if (backingStore()->paintDevice() == paintDevice() && mBackBuffer->image()->hasAlphaChannel()) {
               QPainter p(paintDevice());
               p.setCompositionMode(QPainter::CompositionMode_Source);
               const QColor blank = Qt::transparent;
      

      I can supply those patches through gerrit if they're considered ok but perhaps it's way too invasive and the difference between QPainter and QHighDpiScaling coordinate conversion shouldn't exist in first place?

      This is easily reproducible with widgets/painting/composition example after changing it this way:

      diff --git a/examples/widgets/painting/composition/main.cpp b/examples/widgets/painting/composition/main.cpp
      index bc468d0424..bc082ef250 100644
      --- a/examples/widgets/painting/composition/main.cpp
      +++ b/examples/widgets/painting/composition/main.cpp
      @@ -11,6 +11,7 @@ int main(int argc, char *argv[])
       
           QScopedPointer<QStyle> arthurStyle(new ArthurStyle());
           CompositionWidget compWidget(nullptr);
      +    compWidget.setAttribute(Qt::WA_TranslucentBackground);
           compWidget.setStyle(arthurStyle.data());
       
           const QList<QWidget *> widgets = compWidget.findChildren<QWidget *>();
      
      

      (likely also needs FramelessWindowHint if you will run it on macOS)

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            sorvig Morten Sørvig
            ilya-fedin Ilya Fedin
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes