From 0bdb0d61d8eea356b1ac7d09ced6dee26d89ee8d Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Tue, 6 Oct 2009 09:45:49 +1000 Subject: Implement the drop shadow filter for OpenGL Task-number: QTBUG-4583 Reviewed-by: trustme --- .../gl2paintengineex/qpaintengineex_opengl2_p.h | 2 + src/opengl/qglpixmapfilter.cpp | 167 ++++++++++++++++++++- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 4f42082..12123f3 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -278,6 +278,8 @@ public: QScopedPointer colorizeFilter; QScopedPointer blurFilter; QScopedPointer fastBlurFilter; + QScopedPointer dropShadowFilter; + QScopedPointer fastDropShadowFilter; }; QT_END_NAMESPACE diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 1ae3866..0603369 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -104,11 +104,12 @@ public: void setUniforms(QGLShaderProgram *program); + static QByteArray generateGaussianShader(int radius, bool dropShadow = false); + protected: bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; private: - static QByteArray generateGaussianShader(int radius); mutable QSize m_textureSize; mutable bool m_horizontalBlur; @@ -118,6 +119,25 @@ private: mutable Qt::RenderHint m_hint; }; +class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter +{ +public: + QGLPixmapDropShadowFilter(Qt::RenderHint hint); + + void setUniforms(QGLShaderProgram *program); + +protected: + bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; + +private: + mutable QSize m_textureSize; + mutable bool m_horizontalBlur; + + mutable bool m_haveCached; + mutable int m_cachedRadius; + mutable Qt::RenderHint m_hint; +}; + extern QGLWidget *qt_gl_share_widget(); QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *prototype) @@ -141,6 +161,18 @@ QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *pr return d->blurFilter.data(); } + case QPixmapFilter::DropShadowFilter: { + const QPixmapDropShadowFilter *proto = static_cast(prototype); + if (proto->blurRadius() <= 5) { + if (!d->fastDropShadowFilter) + d->fastDropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::PerformanceHint)); + return d->fastDropShadowFilter.data(); + } + if (!d->dropShadowFilter) + d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::QualityHint)); + return d->dropShadowFilter.data(); + } + case QPixmapFilter::ConvolutionFilter: if (!d->convolutionFilter) d->convolutionFilter.reset(new QGLPixmapConvolutionFilter); @@ -279,6 +311,20 @@ static const char *qt_gl_blur_filter_fast = " return color * (1.0 / float(samples));" "}"; +static const char *qt_gl_drop_shadow_filter_fast = + "const int samples = 9;" + "uniform mediump vec2 delta;" + "uniform mediump vec4 shadowColor;" + "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {" + " mediump vec4 color = vec4(0.0, 0.0, 0.0, 0.0);" + " mediump float offset = (float(samples) - 1.0) / 2.0;" + " for (int i = 0; i < samples; i++) {" + " mediump vec2 coord = srcCoords + delta * (offset - float(i)) / offset;" + " color += texture2D(src, coord).a * shadowColor;" + " }" + " return color * (1.0 / float(samples));" + "}"; + QGLPixmapBlurFilter::QGLPixmapBlurFilter(Qt::RenderHint hint) : m_haveCached(false) , m_cachedRadius(5) @@ -380,7 +426,7 @@ static inline qreal gaussian(qreal dx, qreal sigma) return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma); } -QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) +QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShadow) { Q_ASSERT(radius >= 1); @@ -388,6 +434,8 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) source.reserve(1000); source.append("uniform highp vec2 delta;\n"); + if (dropShadow) + source.append("uniform mediump vec4 shadowColor;\n"); source.append("lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {\n"); QVector sampleOffsets; @@ -444,7 +492,10 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) source.append(coordinate); source.append(";\n"); - source.append(" sample += texture2D(src, coord)"); + if (dropShadow) + source.append(" sample += texture2D(src, coord).a * shadowColor"); + else + source.append(" sample += texture2D(src, coord)"); weightSum += weights.at(i); if (weights.at(i) != qreal(1)) { @@ -463,4 +514,114 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) return source; } +QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(Qt::RenderHint hint) + : m_haveCached(false) + , m_cachedRadius(5) + , m_hint(hint) +{ + if (hint == Qt::PerformanceHint) { + QGLPixmapDropShadowFilter *filter = const_cast(this); + filter->setSource(qt_gl_drop_shadow_filter_fast); + m_haveCached = true; + } +} + +bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const +{ + QGLPixmapDropShadowFilter *filter = const_cast(this); + + int radius = this->blurRadius(); + if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) { + // Only regenerate the shader from source if parameters have changed. + m_haveCached = true; + m_cachedRadius = radius; + filter->setSource(QGLPixmapBlurFilter::generateGaussianShader(radius, true)); + } + + QGLFramebufferObjectFormat format; + format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB)); + QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format); + + if (!fbo) + return false; + + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + // prepare for updateUniforms + m_textureSize = src.size(); + + // horizontal pass, to pixmap + m_horizontalBlur = true; + + QPainter fboPainter(fbo); + + if (src.hasAlphaChannel()) { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + } + + // ensure GL_LINEAR filtering is used + fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); + filter->setOnPainter(&fboPainter); + fboPainter.drawPixmap(0, 0, src); + filter->removeFromPainter(&fboPainter); + fboPainter.end(); + + QGL2PaintEngineEx *engine = static_cast(painter->paintEngine()); + + // vertical pass, to painter + m_horizontalBlur = false; + + painter->save(); + // ensure GL_LINEAR filtering is used + painter->setRenderHint(QPainter::SmoothPixmapTransform); + filter->setOnPainter(painter); + QPointF ofs = offset(); + engine->drawTexture(src.rect().translated(pos.x() + ofs.x(), pos.y() + ofs.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height())); + filter->removeFromPainter(painter); + painter->restore(); + + qgl_fbo_pool()->release(fbo); + + // Now draw the actual pixmap over the top. + painter->drawPixmap(pos, src, srcRect); + + return true; +} + +void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program) +{ + QColor col = color(); + if (m_horizontalBlur) { + program->setUniformValue("shadowColor", 1.0f, 1.0f, 1.0f, 1.0f); + } else { + qreal alpha = col.alphaF(); + program->setUniformValue("shadowColor", col.redF() * alpha, + col.greenF() * alpha, + col.blueF() * alpha, + alpha); + } + if (m_hint == Qt::QualityHint) { + if (m_horizontalBlur) + program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0); + else + program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height()); + } else { + // 1.4 is chosen to most closely match the blurriness of the gaussian blur + // at low radii + qreal blur = blurRadius() / 1.4f; + + if (m_horizontalBlur) + program->setUniformValue("delta", blur / m_textureSize.width(), 0.0); + else + program->setUniformValue("delta", 0.0, blur / m_textureSize.height()); + } +} + QT_END_NAMESPACE -- cgit v0.12 From c768694764e8bc32a7152b80653eef564631452a Mon Sep 17 00:00:00 2001 From: Bill King Date: Tue, 6 Oct 2009 11:41:39 +1000 Subject: Update documentation for setForwardOnly. --- src/sql/kernel/qsqlquery.cpp | 9 +++++++-- src/sql/kernel/qsqlresult.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp index dfe30e7..5125546 100644 --- a/src/sql/kernel/qsqlquery.cpp +++ b/src/sql/kernel/qsqlquery.cpp @@ -811,13 +811,18 @@ bool QSqlQuery::isForwardOnly() const Forward only mode can be (depending on the driver) more memory efficient since results do not need to be cached. It will also improve performance on some databases. For this to be true, you must - call \c setForwardMode() before the query is prepared or executed. + call \c setForwardOnly() before the query is prepared or executed. Note that the constructor that takes a query and a database may execute the query. Forward only mode is off by default. - \sa isForwardOnly(), next(), seek() + Setting forward only to false is a suggestion to the database engine, + which has the final say on whether a result set is forward only or + scrollable. isForwardOnly() will always return the correct status of + the result set. + + \sa isForwardOnly(), next(), seek(), QSqlResult::setForwardOnly() */ void QSqlQuery::setForwardOnly(bool forward) { diff --git a/src/sql/kernel/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp index 791b8a6..efca595 100644 --- a/src/sql/kernel/qsqlresult.cpp +++ b/src/sql/kernel/qsqlresult.cpp @@ -559,7 +559,12 @@ bool QSqlResult::isForwardOnly() const mode needs much less memory since results do not have to be cached. By default, this feature is disabled. - \sa isForwardOnly(), fetchNext() + Setting forward only to false is a suggestion to the database engine, + which has the final say on whether a result set is forward only or + scrollable. isForwardOnly() will always return the correct status of + the result set. + + \sa isForwardOnly(), fetchNext(), QSqlQuery::setForwardOnly() */ void QSqlResult::setForwardOnly(bool forward) { -- cgit v0.12 From 2420c3b6c728ed4c8a8efd4c93427932420c7368 Mon Sep 17 00:00:00 2001 From: Bill King Date: Tue, 6 Oct 2009 11:42:36 +1000 Subject: Add autotest for mysql savepoints. --- tests/auto/qsqldatabase/tst_qsqldatabase.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/auto/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/qsqldatabase/tst_qsqldatabase.cpp index 4175bef..c9c8f5e 100644 --- a/tests/auto/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/qsqldatabase/tst_qsqldatabase.cpp @@ -157,6 +157,8 @@ private slots: void mysqlOdbc_unsignedIntegers(); void mysql_multiselect_data() { generic_data("QMYSQL"); } void mysql_multiselect(); // For task 144331 + void mysql_savepointtest_data() { generic_data("QMYSQL"); } + void mysql_savepointtest(); void accessOdbc_strings_data() { generic_data(); } void accessOdbc_strings(); @@ -2433,6 +2435,19 @@ void tst_QSqlDatabase::sqlStatementUseIsNull_189093() QCOMPARE(statment.count("IS NULL", Qt::CaseInsensitive), 2); } +void tst_QSqlDatabase::mysql_savepointtest() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + if ( db.driverName().startsWith( "QMYSQL" ) && tst_Databases::getMySqlVersion( db ).section( QChar('.'), 0, 0 ).toInt()<5 ) + QSKIP( "Test requires MySQL >= 5.0", SkipSingle ); + + QSqlQuery q(db); + QVERIFY_SQL(q, exec("begin")); + QVERIFY_SQL(q, exec("insert into "+qTableName("qtest")+" VALUES (54, 'foo', 'foo', 54.54)")); + QVERIFY_SQL(q, exec("savepoint foo")); +} QTEST_MAIN(tst_QSqlDatabase) #include "tst_qsqldatabase.moc" -- cgit v0.12