From 78e335408303380310dd59fab421e495cf517ead Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 1 Jun 2015 11:44:37 +0200 Subject: Clip QOpenGLWidget and QQuickWidget correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce support for the widgets' clipRect(). Right now render-to-texture widgets in scroll areas placed close to each other result in broken (non-existent) clipping. Similarly, stack-on-top widgets fail to clip when placed inside a scroll area. This is now corrected and the qopenglwidget example is enhanced to utilize a scroll area. Task-number: QTBUG-45860 Change-Id: I859a63d61a50d64ba9e87244f83c5969dce12337 Reviewed-by: Jørgen Lind --- examples/opengl/qopenglwidget/glwidget.cpp | 6 +-- examples/opengl/qopenglwidget/mainwindow.cpp | 20 ++++++-- examples/opengl/qopenglwidget/mainwindow.h | 2 + src/gui/painting/qplatformbackingstore.cpp | 59 ++++++++++++++++------ src/gui/painting/qplatformbackingstore.h | 4 +- .../platformcompositor/qopenglcompositor.cpp | 31 ++++++++++-- .../qopenglcompositorbackingstore.cpp | 3 +- src/widgets/kernel/qwidgetbackingstore.cpp | 3 +- 8 files changed, 99 insertions(+), 29 deletions(-) diff --git a/examples/opengl/qopenglwidget/glwidget.cpp b/examples/opengl/qopenglwidget/glwidget.cpp index f93e667615..df52e3efbf 100644 --- a/examples/opengl/qopenglwidget/glwidget.cpp +++ b/examples/opengl/qopenglwidget/glwidget.cpp @@ -537,14 +537,14 @@ void GLWidget::setTransparent(bool transparent) window()->update(); } -void GLWidget::resizeGL(int w, int h) +void GLWidget::resizeGL(int, int) { if (m_hasButton) { if (!m_btn) { - m_btn = new QPushButton("A widget on top.\nPress me!", this); + m_btn = new QPushButton("A widget on top.\nPress for more widgets.", this); connect(m_btn, &QPushButton::clicked, this, &GLWidget::handleButtonPress); } - m_btn->move(w / 2, h / 2); + m_btn->move(20, 80); } } diff --git a/examples/opengl/qopenglwidget/mainwindow.cpp b/examples/opengl/qopenglwidget/mainwindow.cpp index 6cf1d5d6e2..22111afdcb 100644 --- a/examples/opengl/qopenglwidget/mainwindow.cpp +++ b/examples/opengl/qopenglwidget/mainwindow.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include "glwidget.h" @@ -69,7 +70,7 @@ MainWindow::MainWindow() "and therefore an interval < 16 ms will likely lead to a 60 FPS update rate."); QGroupBox *updateGroupBox = new QGroupBox(this); QCheckBox *timerBased = new QCheckBox("Use timer", this); - timerBased->setChecked(true); + timerBased->setChecked(false); timerBased->setToolTip("Toggles using a timer to trigger update().\n" "When not set, each paintGL() schedules the next update immediately,\n" "expecting the blocking swap to throttle the thread.\n" @@ -87,7 +88,7 @@ MainWindow::MainWindow() slider->setRange(0, 50); slider->setSliderPosition(30); m_timer->setInterval(10); - label->setText("A QOpenGLWidget"); + label->setText("A scrollable QOpenGLWidget"); label->setAlignment(Qt::AlignHCenter); QGroupBox * groupBox = new QGroupBox(this); @@ -96,7 +97,10 @@ MainWindow::MainWindow() m_layout = new QGridLayout(groupBox); - m_layout->addWidget(glwidget,1,0,8,1); + QScrollArea *scrollArea = new QScrollArea; + scrollArea->setWidget(glwidget); + + m_layout->addWidget(scrollArea,1,0,8,1); m_layout->addWidget(label,9,0,1,1); m_layout->addWidget(updateGroupBox, 10, 0, 1, 1); m_layout->addWidget(slider, 11,0,1,1); @@ -134,7 +138,10 @@ MainWindow::MainWindow() connect(timerBased, &QCheckBox::toggled, this, &MainWindow::timerUsageChanged); connect(timerBased, &QCheckBox::toggled, updateInterval, &QWidget::setEnabled); - m_timer->start(); + if (timerBased->isChecked()) + m_timer->start(); + else + updateInterval->setEnabled(false); } void MainWindow::updateIntervalChanged(int value) @@ -170,3 +177,8 @@ void MainWindow::timerUsageChanged(bool enabled) w->update(); } } + +void MainWindow::resizeEvent(QResizeEvent *) +{ + m_glWidgets[0]->setMinimumSize(size() + QSize(128, 128)); +} diff --git a/examples/opengl/qopenglwidget/mainwindow.h b/examples/opengl/qopenglwidget/mainwindow.h index f1b2c51e53..9ad8a01339 100644 --- a/examples/opengl/qopenglwidget/mainwindow.h +++ b/examples/opengl/qopenglwidget/mainwindow.h @@ -56,6 +56,8 @@ public: void addNew(); bool timerEnabled() const { return m_timer->isActive(); } + void resizeEvent(QResizeEvent *); + private slots: void updateIntervalChanged(int value); void timerUsageChanged(bool enabled); diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index 14a9429c71..83077f38ca 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -86,6 +86,7 @@ struct QBackingstoreTextureInfo QWidget *widget; // may be null GLuint textureId; QRect rect; + QRect clipRect; QPlatformTextureList::Flags flags; }; @@ -142,6 +143,12 @@ QRect QPlatformTextureList::geometry(int index) const return d->textures.at(index).rect; } +QRect QPlatformTextureList::clipRect(int index) const +{ + Q_D(const QPlatformTextureList); + return d->textures.at(index).clipRect; +} + void QPlatformTextureList::lock(bool on) { Q_D(QPlatformTextureList); @@ -157,13 +164,15 @@ bool QPlatformTextureList::isLocked() const return d->locked; } -void QPlatformTextureList::appendTexture(QWidget *widget, GLuint textureId, const QRect &geometry, Flags flags) +void QPlatformTextureList::appendTexture(QWidget *widget, GLuint textureId, const QRect &geometry, + const QRect &clipRect, Flags flags) { Q_D(QPlatformTextureList); QBackingstoreTextureInfo bi; bi.widget = widget; bi.textureId = textureId; bi.rect = geometry; + bi.clipRect = clipRect; bi.flags = flags; d->textures.append(bi); } @@ -198,7 +207,7 @@ void QPlatformTextureList::clear() #ifndef QT_NO_OPENGL -static QRect deviceRect(const QRect &rect, QWindow *window) +static inline QRect deviceRect(const QRect &rect, QWindow *window) { QRect deviceRect(rect.topLeft() * window->devicePixelRatio(), rect.size() * window->devicePixelRatio()); @@ -219,6 +228,32 @@ static QRegion deviceRegion(const QRegion ®ion, QWindow *window) return deviceRegion; } +static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight) +{ + return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1, + topLeftRect.width(), topLeftRect.height()); +} + +static void blit(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect, + QOpenGLTextureBlitter *blitter) +{ + const QRect rectInWindow = textures->geometry(idx); + QRect clipRect = textures->clipRect(idx); + if (clipRect.isEmpty()) + clipRect = QRect(QPoint(0, 0), rectInWindow.size()); + const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft()); + const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height()); + + const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(deviceRect(clippedRectInWindow, window), + deviceWindowRect); + + const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(deviceRect(srcRect, window), + deviceRect(rectInWindow, window).size(), + QOpenGLTextureBlitter::OriginBottomLeft); + + blitter->blit(textures->textureId(idx), target, source); +} + /*! Flushes the given \a region from the specified \a window onto the screen, and composes it with the specified \a textures. @@ -254,15 +289,12 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i d_ptr->blitter->bind(); - QRect windowRect(QPoint(), window->size() * window->devicePixelRatio()); + const QRect deviceWindowRect = deviceRect(QRect(QPoint(), window->size()), window); // Textures for renderToTexture widgets. for (int i = 0; i < textures->count(); ++i) { - if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { - QRect targetRect = deviceRect(textures->geometry(i), window); - QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, windowRect); - d_ptr->blitter->blit(textures->textureId(i), target, QOpenGLTextureBlitter::OriginBottomLeft); - } + if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) + blit(textures, i, window, deviceWindowRect, d_ptr->blitter); } funcs->glEnable(GL_BLEND); @@ -272,6 +304,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i // semi-transparency even when it is not wanted. funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + // Backingstore texture with the normal widgets. GLuint textureId = 0; QOpenGLTextureBlitter::Origin origin = QOpenGLTextureBlitter::OriginTopLeft; if (QPlatformGraphicsBuffer *graphicsBuffer = this->graphicsBuffer()) { @@ -307,7 +340,6 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i origin = QOpenGLTextureBlitter::OriginBottomLeft; textureId = d_ptr->textureId; } else { - // Backingstore texture with the normal widgets. TextureFlags flags = 0; textureId = toTexture(deviceRegion(region, window), &d_ptr->textureSize, &flags); d_ptr->needsSwizzle = (flags & TextureSwizzle) != 0; @@ -316,7 +348,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i } if (textureId) { - QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(QRect(QPoint(), d_ptr->textureSize), windowRect); + QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(QRect(QPoint(), d_ptr->textureSize), deviceWindowRect); if (d_ptr->needsSwizzle) d_ptr->blitter->setSwizzleRB(true); d_ptr->blitter->blit(textureId, target, origin); @@ -326,11 +358,8 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. for (int i = 0; i < textures->count(); ++i) { - if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { - QRect targetRect = deviceRect(textures->geometry(i), window); - QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, windowRect); - d_ptr->blitter->blit(textures->textureId(i), target, QOpenGLTextureBlitter::OriginBottomLeft); - } + if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) + blit(textures, i, window, deviceWindowRect, d_ptr->blitter); } funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h index ae7314b6d0..5fa7e6dac0 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -82,12 +82,14 @@ public: bool isEmpty() const { return count() == 0; } GLuint textureId(int index) const; QRect geometry(int index) const; + QRect clipRect(int index) const; QWidget *widget(int index); Flags flags(int index) const; void lock(bool on); bool isLocked() const; - void appendTexture(QWidget *widget, GLuint textureId, const QRect &geometry, Flags flags = 0); + void appendTexture(QWidget *widget, GLuint textureId, const QRect &geometry, + const QRect &clipRect = QRect(), Flags flags = 0); void clear(); Q_SIGNALS: diff --git a/src/platformsupport/platformcompositor/qopenglcompositor.cpp b/src/platformsupport/platformcompositor/qopenglcompositor.cpp index 3fd6c999a2..2e386532e2 100644 --- a/src/platformsupport/platformcompositor/qopenglcompositor.cpp +++ b/src/platformsupport/platformcompositor/qopenglcompositor.cpp @@ -169,6 +169,29 @@ struct BlendStateBinder bool m_blend; }; +static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight) +{ + return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1, + topLeftRect.width(), topLeftRect.height()); +} + +static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &targetWindowRect, QOpenGLTextureBlitter *blitter) +{ + const QRect rectInWindow = textures->geometry(idx); + QRect clipRect = textures->clipRect(idx); + if (clipRect.isEmpty()) + clipRect = QRect(QPoint(0, 0), rectInWindow.size()); + + const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft()); + const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height()); + + const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect); + const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, rectInWindow.size(), + QOpenGLTextureBlitter::OriginBottomLeft); + + blitter->blit(textures->textureId(idx), target, source); +} + void QOpenGLCompositor::render(QOpenGLCompositorWindow *window) { const QPlatformTextureList *textures = window->textures(); @@ -181,7 +204,6 @@ void QOpenGLCompositor::render(QOpenGLCompositorWindow *window) for (int i = 0; i < textures->count(); ++i) { uint textureId = textures->textureId(i); - QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); const float opacity = window->sourceWindow()->opacity(); if (opacity != currentOpacity) { currentOpacity = opacity; @@ -191,24 +213,25 @@ void QOpenGLCompositor::render(QOpenGLCompositorWindow *window) if (textures->count() > 1 && i == textures->count() - 1) { // Backingstore for a widget with QOpenGLWidget subwidgets blend.set(true); + const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft); } else if (textures->count() == 1) { // A regular QWidget window const bool translucent = window->sourceWindow()->requestedFormat().alphaBufferSize() > 0; blend.set(translucent); + const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft); } else if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { // Texture from an FBO belonging to a QOpenGLWidget blend.set(false); - m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginBottomLeft); + clippedBlit(textures, i, targetWindowRect, &m_blitter); } } for (int i = 0; i < textures->count(); ++i) { if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { - QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); blend.set(true); - m_blitter.blit(textures->textureId(i), target, QOpenGLTextureBlitter::OriginBottomLeft); + clippedBlit(textures, i, targetWindowRect, &m_blitter); } } diff --git a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp index 3caec468a6..4cf64e61da 100644 --- a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp +++ b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp @@ -175,7 +175,8 @@ void QOpenGLCompositorBackingStore::composeAndFlush(QWindow *window, const QRegi m_textures->clear(); for (int i = 0; i < textures->count(); ++i) - m_textures->appendTexture(textures->widget(i), textures->textureId(i), textures->geometry(i), textures->flags(i)); + m_textures->appendTexture(textures->widget(i), textures->textureId(i), textures->geometry(i), + textures->clipRect(i), textures->flags(i)); updateTexture(); m_textures->appendTexture(Q_NULLPTR, m_bsTexture, window->geometry()); diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index 485cf82078..5752317924 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -962,7 +962,8 @@ static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatfo QPlatformTextureList::Flags flags = 0; if (widget->testAttribute(Qt::WA_AlwaysStackOnTop)) flags |= QPlatformTextureList::StacksOnTop; - widgetTextures->appendTexture(widget, wd->textureId(), QRect(widget->mapTo(tlw, QPoint()), widget->size()), flags); + const QRect rect(widget->mapTo(tlw, QPoint()), widget->size()); + widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags); } for (int i = 0; i < wd->children.size(); ++i) { -- cgit v1.2.3