From 919f514f29f33d653e492b0dbf9fc95d7cd93287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 31 Mar 2022 16:59:50 +0200 Subject: macOS: Don't scroll individual rects of a region when inline scrolling The QPlatformBackingStore::scroll() API takes a QRegion as input, and we kept this data type in the implementation of QCALayerBackingStore::scroll for the logic of figuring out whether we could use the existing back buffer as source of the scroll or if we needed to pull data from the front buffer. We then iterated the rects of the resulting in-line region, and called qt_scrollRectInImage for each of them. This will unfortunately not work whenever the scroll of one of these rects ends up overwriting parts of the back buffer that later rects of the region use as their source rect. This could be reproduced by moving the cursor within a text document, which would dirty a small area around the cursor, making the subsequent scroll of the viewport result in garbled text. This issue exists in the other platform backingstore implementations too, but since the QWidgetRepaintManager never calls scroll() with anything but a single rect they would not have any issues. The problem could potentially be solved by sorting the rects before blitting them, but now this quick fix solves the regression on macOS. Fixes: QTBUG-102181 Pick-to: 6.2 6.3 6.3.0 Change-Id: I80f6d26117321e3c09e068cdb03eb320e244a5de Reviewed-by: Eike Ziller Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoabackingstore.mm | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 8f3e3036ad..a8e6791e84 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -271,20 +271,22 @@ bool QCALayerBackingStore::scroll(const QRegion ®ion, int dx, int dy) m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess); if (!inPlaceRegion.isEmpty()) { - qCDebug(lcQpaBackingStore) << "Scrolling" << inPlaceRegion << "in place"; + // We have to scroll everything in one go, instead of scrolling the + // individual rects of the region, as otherwise we may end up reading + // already overwritten (scrolled) pixels. + const QRect inPlaceBoundingRect = inPlaceRegion.boundingRect(); + + qCDebug(lcQpaBackingStore) << "Scrolling" << inPlaceBoundingRect << "in place"; QImage *backBufferImage = m_buffers.back()->asImage(); const qreal devicePixelRatio = backBufferImage->devicePixelRatio(); const QPoint devicePixelDelta = scrollDelta * devicePixelRatio; extern void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &); - for (const QRect &rect : inPlaceRegion) { - qt_scrollRectInImage(*backBufferImage, - QRect(rect.topLeft() * devicePixelRatio, - rect.size() * devicePixelRatio), - devicePixelDelta); - } - + qt_scrollRectInImage(*backBufferImage, + QRect(inPlaceBoundingRect.topLeft() * devicePixelRatio, + inPlaceBoundingRect.size() * devicePixelRatio), + devicePixelDelta); } if (!frontBufferRegion.isEmpty()) { -- cgit v1.2.3