diff options
-rw-r--r-- | src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 163 | ||||
-rw-r--r-- | src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h | 4 | ||||
-rw-r--r-- | tests/auto/qgl/tst_qgl.cpp | 184 |
3 files changed, 255 insertions, 96 deletions
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index ba2a0ea..fb493e6 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -750,6 +750,7 @@ void QGL2PaintEngineEx::beginNativePainting() #endif d->lastTexture = GLuint(-1); + d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); d->resetGLState(); d->needsSync = true; @@ -894,12 +895,12 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& ve // qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()"); glStencilMask(0xFFFF); // Enable stencil writes - if (stencilBufferDirty) { + if (dirtyStencilRegion.intersects(currentScissorBounds)) { // Clear the stencil buffer to zeros glDisable(GL_STENCIL_TEST); glClearStencil(0); // Clear to zero glClear(GL_STENCIL_BUFFER_BIT); - stencilBufferDirty = false; + dirtyStencilRegion -= currentScissorBounds; } glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes @@ -1419,13 +1420,14 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) d->brushUniformsDirty = true; d->matrixDirty = true; d->compositionModeDirty = true; - d->stencilBufferDirty = true; d->simpleShaderDepthUniformDirty = true; d->depthUniformDirty = true; d->opacityUniformDirty = true; d->needsSync = true; d->use_system_clip = !systemClip().isEmpty(); + d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); + // Calling begin paint should make the correct context current. So, any // code which calls into GL or otherwise needs a current context *must* // go after beginPaint: @@ -1521,19 +1523,26 @@ void QGL2PaintEngineExPrivate::updateDepthScissorTest() else glDisable(GL_DEPTH_TEST); - if (q->state()->scissorTestEnabled) { - QRect bounds = q->state()->rectangleClip; - if (bounds.isNull() || !q->painter()->hasClipping()) { - if (use_system_clip) - bounds = systemClip.boundingRect(); - else - bounds = QRect(0, 0, width, height); - } + QRect bounds = q->state()->rectangleClip; + if (!q->state()->clipEnabled) { + if (use_system_clip) + bounds = systemClip.boundingRect(); + else + bounds = QRect(0, 0, width, height); + } else { + if (use_system_clip) + bounds = bounds.intersected(systemClip.boundingRect()); + else + bounds = bounds.intersected(QRect(0, 0, width, height)); + } + currentScissorBounds = bounds; + + if (bounds == QRect(0, 0, width, height)) { + glDisable(GL_SCISSOR_TEST); + } else { glEnable(GL_SCISSOR_TEST); setScissor(bounds); - } else { - glDisable(GL_SCISSOR_TEST); } } @@ -1573,7 +1582,6 @@ void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint depth) if (matrixDirty) updateMatrix(); - if (q->state()->needsDepthBufferClear) { glDepthMask(true); glClearDepth(rawDepth(2)); @@ -1624,90 +1632,81 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) ensureActive(); - if (op == Qt::ReplaceClip && !d->hasClipOperations()) + if (op == Qt::ReplaceClip) { op = Qt::IntersectClip; + if (d->hasClipOperations()) { + d->systemStateChanged(); + state()->canRestoreClip = false; + } + } if (!path.isEmpty() && op == Qt::IntersectClip && (path.shape() == QVectorPath::RectangleHint)) { const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); QRectF rect(points[0], points[2]); if (state()->matrix.type() <= QTransform::TxScale) { - rect = state()->matrix.mapRect(rect); - - if ((d->use_system_clip && rect.contains(d->systemClip.boundingRect())) - || rect.contains(QRect(0, 0, d->width, d->height))) - return; - - if (state()->rectangleClip.isValid()) { - state()->rectangleClip = state()->rectangleClip.intersected(rect.toRect()); - - state()->hasRectangleClip = true; - state()->scissorTestEnabled = true; - - glEnable(GL_SCISSOR_TEST); - d->setScissor(state()->rectangleClip); - - return; - } + state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toRect()); + d->updateDepthScissorTest(); + return; } } - if (!state()->hasRectangleClip) - state()->rectangleClip = QRect(); - - if (state()->rectangleClip.isValid() && op != Qt::NoClip && op != Qt::ReplaceClip) { - QPainterPath path; - path.addRect(state()->rectangleClip); - path = state()->matrix.inverted().map(path); - - state()->rectangleClip = QRect(); - d->updateDepthScissorTest(); - - glDepthFunc(GL_ALWAYS); - - state()->maxDepth = 4; - d->writeClip(qtVectorPathForPath(path), state()->maxDepth); - state()->currentDepth = 3; - state()->depthTestEnabled = true; - - glDepthFunc(GL_LESS); - glEnable(GL_DEPTH_TEST); - } + const QRect pathRect = state()->matrix.mapRect(path.controlPointRect()).toAlignedRect(); switch (op) { case Qt::NoClip: if (d->use_system_clip) { - glEnable(GL_DEPTH_TEST); state()->depthTestEnabled = true; state()->currentDepth = 0; } else { - glDisable(GL_DEPTH_TEST); state()->depthTestEnabled = false; } + state()->rectangleClip = QRect(0, 0, d->width, d->height); state()->canRestoreClip = false; + d->updateDepthScissorTest(); break; case Qt::IntersectClip: + state()->rectangleClip = state()->rectangleClip.intersected(pathRect); + d->updateDepthScissorTest(); ++state()->maxDepth; d->writeClip(path, state()->maxDepth); state()->currentDepth = state()->maxDepth - 1; state()->depthTestEnabled = true; break; - case Qt::ReplaceClip: - d->systemStateChanged(); - state()->rectangleClip = QRect(); - state()->maxDepth = 4; - glDepthFunc(GL_ALWAYS); - d->writeClip(path, state()->maxDepth); - state()->currentDepth = 3; - state()->canRestoreClip = false; - state()->depthTestEnabled = true; - break; - case Qt::UniteClip: + case Qt::UniteClip: { + if (state()->rectangleClip.isValid()) { + ++state()->maxDepth; + + QPainterPath path; + path.addRect(state()->rectangleClip); + + // flush the existing clip rectangle to the depth buffer + d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), state()->maxDepth); + } + + QRect oldRectangleClip = state()->rectangleClip; + + state()->rectangleClip = state()->rectangleClip.united(pathRect); + d->updateDepthScissorTest(); + + QRegion extendRegion = QRegion(state()->rectangleClip) - oldRectangleClip; + glDepthFunc(GL_ALWAYS); + if (!extendRegion.isEmpty()) { + QPainterPath extendPath; + extendPath.addRegion(extendRegion); + + // first clear the depth buffer in the extended region + d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(extendPath)), 0); + } + + // now write the clip path d->writeClip(path, state()->maxDepth); state()->canRestoreClip = false; + state()->currentDepth = state()->maxDepth - 1; state()->depthTestEnabled = true; break; + } } glDepthFunc(GL_LESS); @@ -1739,39 +1738,22 @@ void QGL2PaintEngineExPrivate::systemStateChanged() } } - glDisable(GL_DEPTH_TEST); q->state()->depthTestEnabled = false; - q->state()->scissorTestEnabled = false; q->state()->needsDepthBufferClear = true; - q->state()->hasRectangleClip = false; - - glDisable(GL_SCISSOR_TEST); q->state()->currentDepth = 1; q->state()->maxDepth = 4; - q->state()->rectangleClip = QRect(0, 0, width, height); + q->state()->rectangleClip = use_system_clip ? systemClip.boundingRect() : QRect(0, 0, width, height); + updateDepthScissorTest(); if (use_system_clip) { if (systemClip.numRects() == 1) { - QRect bounds = systemClip.boundingRect(); - if (bounds == QRect(0, 0, width, height)) { + if (q->state()->rectangleClip == QRect(0, 0, width, height)) { use_system_clip = false; return; } - - q->state()->rectangleClip = bounds; - q->state()->scissorTestEnabled = true; - updateDepthScissorTest(); } else { - q->state()->rectangleClip = QRect(); - q->state()->scissorTestEnabled = true; - updateDepthScissorTest(); - - QTransform transform = q->state()->matrix; - q->state()->matrix = QTransform(); - q->transformChanged(); - q->state()->needsDepthBufferClear = false; glDepthMask(true); @@ -1783,17 +1765,13 @@ void QGL2PaintEngineExPrivate::systemStateChanged() path.addRegion(systemClip); glDepthFunc(GL_ALWAYS); - writeClip(qtVectorPathForPath(path), 2); + writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 2); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); q->state()->depthTestEnabled = true; - - q->state()->matrix = transform; - q->transformChanged(); } - q->state()->currentDepth = 1; simpleShaderDepthUniformDirty = true; depthUniformDirty = true; } @@ -1870,18 +1848,15 @@ QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &oth maxDepth = other.maxDepth; canRestoreClip = other.canRestoreClip; rectangleClip = other.rectangleClip; - hasRectangleClip = other.hasRectangleClip; } QOpenGL2PaintEngineState::QOpenGL2PaintEngineState() { needsDepthBufferClear = true; depthTestEnabled = false; - scissorTestEnabled = false; currentDepth = 1; maxDepth = 4; canRestoreClip = true; - hasRectangleClip = false; } QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState() diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 8b09f41..049994f 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -217,11 +217,13 @@ public: bool brushUniformsDirty; bool simpleShaderMatrixUniformDirty; bool shaderMatrixUniformDirty; - bool stencilBufferDirty; bool depthUniformDirty; bool simpleShaderDepthUniformDirty; bool opacityUniformDirty; + QRegion dirtyStencilRegion; + QRect currentScissorBounds; + const QBrush* currentBrush; // May not be the state's brush! GLfloat inverseScale; diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index 21f7359..999d119 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -81,8 +81,9 @@ private slots: void stackedFBOs(); void colormap(); void fboFormat(); - void testDontCrashOnDanglingResources(); + void replaceClipping(); + void clipTest(); }; tst_QGL::tst_QGL() @@ -1539,5 +1540,186 @@ void tst_QGL::testDontCrashOnDanglingResources() widget->hide(); } +class ReplaceClippingGLWidget : public QGLWidget +{ +public: + void paint(QPainter *painter) + { + painter->fillRect(rect(), Qt::white); + + QPainterPath path; + path.addRect(0, 0, 100, 100); + path.addRect(50, 50, 100, 100); + + painter->setClipRect(0, 0, 150, 150); + painter->fillPath(path, Qt::red); + + painter->translate(150, 150); + painter->setClipRect(0, 0, 150, 150); + painter->fillPath(path, Qt::red); + } + +protected: + void paintEvent(QPaintEvent*) + { + // clear the stencil with junk + glStencilMask(0xFFFF); + glClearStencil(0xFFFF); + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + glClear(GL_STENCIL_BUFFER_BIT); + + QPainter painter(this); + paint(&painter); + } +}; + +void tst_QGL::replaceClipping() +{ + ReplaceClippingGLWidget glw; + glw.resize(300, 300); + glw.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&glw); +#endif + QTest::qWait(200); + + QImage reference(300, 300, QImage::Format_RGB32); + QPainter referencePainter(&reference); + glw.paint(&referencePainter); + referencePainter.end(); + + const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + + QCOMPARE(widgetFB, reference); +} + +class ClipTestGLWidget : public QGLWidget +{ +public: + void paint(QPainter *painter) + { + painter->fillRect(rect(), Qt::white); + painter->setClipRect(10, 10, width()-20, height()-20); + painter->fillRect(rect(), Qt::cyan); + + painter->save(); + painter->setClipRect(10, 10, 100, 100, Qt::IntersectClip); + + painter->fillRect(rect(), Qt::blue); + + painter->save(); + painter->setClipRect(10, 10, 50, 50, Qt::IntersectClip); + painter->fillRect(rect(), Qt::red); + painter->restore(); + painter->fillRect(0, 0, 40, 40, Qt::white); + painter->save(); + + painter->setClipRect(0, 0, 35, 35, Qt::IntersectClip); + painter->fillRect(rect(), Qt::black); + painter->restore(); + + painter->fillRect(0, 0, 30, 30, Qt::magenta); + + painter->save(); + painter->setClipRect(60, 10, 50, 50, Qt::ReplaceClip); + painter->fillRect(rect(), Qt::green); + painter->restore(); + + painter->save(); + painter->setClipRect(0, 60, 60, 25, Qt::IntersectClip); + painter->setClipRect(60, 60, 50, 25, Qt::UniteClip); + painter->fillRect(rect(), Qt::yellow); + painter->restore(); + + painter->restore(); + + painter->translate(100, 100); + + { + QPainterPath path; + path.addRect(10, 10, 100, 100); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + + painter->fillRect(rect(), Qt::blue); + + painter->save(); + { + QPainterPath path; + path.addRect(10, 10, 50, 50); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->fillRect(rect(), Qt::red); + painter->restore(); + painter->fillRect(0, 0, 40, 40, Qt::white); + painter->save(); + + { + QPainterPath path; + path.addRect(0, 0, 35, 35); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->fillRect(rect(), Qt::black); + painter->restore(); + + painter->fillRect(0, 0, 30, 30, Qt::magenta); + + painter->save(); + { + QPainterPath path; + path.addRect(60, 10, 50, 50); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::ReplaceClip); + } + painter->fillRect(rect(), Qt::green); + painter->restore(); + + painter->save(); + { + QPainterPath path; + path.addRect(0, 60, 60, 25); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->setClipRect(60, 60, 50, 25, Qt::UniteClip); + painter->fillRect(rect(), Qt::yellow); + painter->restore(); + } + +protected: + void paintEvent(QPaintEvent*) + { + QPainter painter(this); + paint(&painter); + } +}; + +void tst_QGL::clipTest() +{ + ClipTestGLWidget glw; + glw.resize(220, 220); + glw.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&glw); +#endif + QTest::qWait(200); + + QImage reference(glw.size(), QImage::Format_RGB32); + QPainter referencePainter(&reference); + glw.paint(&referencePainter); + referencePainter.end(); + + const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + + QCOMPARE(widgetFB, reference); +} + + QTEST_MAIN(tst_QGL) #include "tst_qgl.moc" |