Clip QOpenGLWidget and QQuickWidget correctly
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 <jorgen.lind@theqtcompany.com>
This commit is contained in:
parent
069be16543
commit
78e3354083
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
#include <QScrollArea>
|
||||
|
||||
#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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user