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 ®ion) 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 ®ion) 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 ®ion) { 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 ®ion) { 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 ®ion) // 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 ®ion) 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 ®ion) 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 ®ion) 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 ®ion) 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 ®ion) 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 ®ion) 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)