From dae23694cb89b853785a5772cc7e0477f65ac5bf Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Mon, 16 Aug 2010 15:14:43 +0200 Subject: Fix tabArray support for boundingRect measurement QFontMetrics::boundingRect() and size() accept a tabArray as argument to measure the size of string, but tabArray argument has no effect because qt_format_text() just ignore that. This patch make it handle tabArray so that measurement for tab aligned text can be handled correctly. Task-number: QTBUG-4904 Reviewed-by: Eskil --- src/gui/painting/qpainter.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 314f349..c0f195a 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7997,7 +7997,7 @@ void qt_format_text(const QFont &fnt, const QRectF &_r, } void qt_format_text(const QFont &fnt, const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect, - int tabstops, int *, int tabarraylen, + int tabstops, int *ta, int tabarraylen, QPainter *painter) { @@ -8120,6 +8120,13 @@ start_lengthVariant: if (engine.option.tabStop() < 0 && tabstops > 0) engine.option.setTabStop(tabstops); + if (engine.option.tabs().isEmpty() && ta) { + QList tabs; + for (int i = 0; i < tabarraylen; i++) + tabs.append(qreal(ta[i])); + engine.option.setTabArray(tabs); + } + engine.option.setTextDirection(layout_direction); if (tf & Qt::AlignJustify) engine.option.setAlignment(Qt::AlignJustify); -- cgit v0.12 From b7fd2930986a9be5dd6115bfc4a5c5c517344371 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 23 Aug 2010 16:49:06 +0200 Subject: QFutureWatcher: display a warning when connecting after calling setFuture() Reviewed-by: brad --- src/corelib/concurrent/qfuturewatcher.cpp | 9 +++++++++ tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp | 24 +++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/corelib/concurrent/qfuturewatcher.cpp b/src/corelib/concurrent/qfuturewatcher.cpp index d4573c6..21b0789 100644 --- a/src/corelib/concurrent/qfuturewatcher.cpp +++ b/src/corelib/concurrent/qfuturewatcher.cpp @@ -359,6 +359,15 @@ void QFutureWatcherBase::connectNotify(const char * signal) Q_D(QFutureWatcherBase); if (qstrcmp(signal, SIGNAL(resultReadyAt(int))) == 0) d->resultAtConnected.ref(); +#ifndef QT_NO_DEBUG + if (qstrcmp(signal, SIGNAL(finished())) == 0) { + if (futureInterface().isRunning()) { + //connections should be established before calling stFuture to avoid race. + // (The future could finish before the connection is made.) + qWarning("QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race"); + } + } +#endif } void QFutureWatcherBase::disconnectNotify(const char * signal) diff --git a/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp index c53c67a..8b52761 100644 --- a/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp +++ b/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp @@ -81,6 +81,7 @@ private slots: void incrementalMapResults(); void incrementalFilterResults(); void qfutureSynchornizer(); + void warnRace(); }; QTEST_MAIN(tst_QFutureWatcher) @@ -466,12 +467,12 @@ void tst_QFutureWatcher::toMuchProgress() ProgressObject o; QFutureWatcher f; - f.setFuture((new ProgressEmitterTask())->start()); QObject::connect(&f, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); #ifdef PRINT QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(printProgress(int))); #endif QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int))); + f.setFuture((new ProgressEmitterTask())->start()); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); @@ -886,6 +887,27 @@ void tst_QFutureWatcher::qfutureSynchornizer() QVERIFY(t.elapsed() < taskCount * 10); } +class DummyObject : public QObject { + Q_OBJECT +public slots: + void dummySlot() {} +}; + +void tst_QFutureWatcher::warnRace() +{ +#ifndef QT_NO_DEBUG + QTest::ignoreMessage(QtWarningMsg, "QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race"); +#endif + QFutureWatcher watcher; + DummyObject object; + + QFuture future = QtConcurrent::run(sleeper); + watcher.setFuture(future); + connect(&watcher, SIGNAL(finished()), &object, SLOT(dummySlot())); + future.waitForFinished(); +} + + #include "tst_qfuturewatcher.moc" #else -- cgit v0.12 From bbc8536f6e1fb31d1b4f652750f76b4a26e1d54a Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 24 Aug 2010 09:45:56 +0200 Subject: Stabilize tst_QFutureWatcher::warnRace --- tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp index 8b52761..183f62c 100644 --- a/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp +++ b/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp @@ -47,6 +47,7 @@ #include #include #include +#include "../../shared/util.h" #ifndef QT_NO_CONCURRENT_TEST #include @@ -891,6 +892,13 @@ class DummyObject : public QObject { Q_OBJECT public slots: void dummySlot() {} +public: + static void function(QWaitCondition *cond) + { + QMutex m; + QMutexLocker lock(&m); + cond->wait(&m); + } }; void tst_QFutureWatcher::warnRace() @@ -900,14 +908,16 @@ void tst_QFutureWatcher::warnRace() #endif QFutureWatcher watcher; DummyObject object; + QWaitCondition cond; - QFuture future = QtConcurrent::run(sleeper); + QFuture future = QtConcurrent::run(DummyObject::function, &cond); watcher.setFuture(future); + QTRY_VERIFY(future.isStarted()); connect(&watcher, SIGNAL(finished()), &object, SLOT(dummySlot())); + cond.wakeAll(); future.waitForFinished(); } - #include "tst_qfuturewatcher.moc" #else -- cgit v0.12 From d959edb97e039c6dbe3c3f11ddfea47ff7ff420f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 24 Aug 2010 13:45:46 +0200 Subject: Add a couple of GL thread tests. Drawing onto QGLWidgets in a thread should work on all supported systems. Drawing onto QPixmaps in a thread seems to work on everything except my desktop X11 system. There seems to be a bug in libXrender or possibly libX11 which causes XRenderCompositeText32() to flonk out. --- tests/auto/qglthreads/tst_qglthreads.cpp | 169 +++++++++++++++++++++++++++++-- tests/auto/qglthreads/tst_qglthreads.h | 2 + 2 files changed, 162 insertions(+), 9 deletions(-) diff --git a/tests/auto/qglthreads/tst_qglthreads.cpp b/tests/auto/qglthreads/tst_qglthreads.cpp index cce3161..eb31e07 100644 --- a/tests/auto/qglthreads/tst_qglthreads.cpp +++ b/tests/auto/qglthreads/tst_qglthreads.cpp @@ -45,10 +45,6 @@ #include #include "tst_qglthreads.h" -#ifdef Q_WS_X11 -#include -#endif - #define RUNNING_TIME 5000 tst_QGLThreads::tst_QGLThreads(QObject *parent) @@ -56,8 +52,6 @@ tst_QGLThreads::tst_QGLThreads(QObject *parent) { } - - /* swapInThread @@ -339,6 +333,7 @@ void renderAScene(int w, int h) class ThreadSafeGLWidget : public QGLWidget { public: + ThreadSafeGLWidget(QWidget *parent = 0) : QGLWidget(parent) {} void paintEvent(QPaintEvent *) { // ignored as we're anyway swapping as fast as we can @@ -426,7 +421,7 @@ void tst_QGLThreads::renderInThread_data() void tst_QGLThreads::renderInThread() { #ifdef Q_OS_MAC - QSKIP("OpenGL threading tests are currently disabled on mac as they were causing reboots", SkipAll); + QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll); #endif QFETCH(bool, resize); @@ -461,15 +456,171 @@ void tst_QGLThreads::renderInThread() QVERIFY(!thread.failure); } +class ThreadPainter : public QObject +{ + Q_OBJECT +public: + ThreadPainter(QPaintDevice *pd) : paintDevice(pd), fail(true) { + pixmap = QPixmap(40, 40); + pixmap.fill(Qt::green); + QPainter p(&pixmap); + p.drawLine(0, 0, 40, 40); + p.drawLine(0, 40, 40, 0); + } + +public slots: + void draw() { + bool beginFailed = false; + QTime time; + time.start(); + int rotAngle = 10; + QSize s(paintDevice->width(), paintDevice->height()); + while (time.elapsed() < RUNNING_TIME) { + QPainter p; + if (!p.begin(paintDevice)) { + beginFailed = true; + break; + } + p.translate(s.width()/2, s.height()/2); + p.rotate(rotAngle); + p.translate(-s.width()/2, -s.height()/2); + p.fillRect(0, 0, s.width(), s.height(), Qt::red); + QRect rect(QPoint(0, 0), s); + p.drawPixmap(10, 10, pixmap); + p.drawTiledPixmap(50, 50, 100, 100, pixmap); + p.drawText(rect.center(), "This is a piece of text"); + p.end(); + rotAngle += 2; +#ifdef Q_WS_WIN + Sleep(20); +#else + usleep(20 * 1000); +#endif + } + + fail = beginFailed; + QThread::currentThread()->quit(); + } + bool failed() { return fail; } +private: + QPixmap pixmap; + QPaintDevice *paintDevice; + bool fail; +}; -int main(int argc, char **argv) +class PainterThreads +{ +public: + PainterThreads(int count, bool drawOnWidgets) : numThreads(count) + , useWidgets(drawOnWidgets) + { + for (int i=0; iresize(150, 150); + widgets.at(i)->show(); + QTest::qWaitForWindowShown(widgets.at(i)); + widgets.at(i)->doneCurrent(); + } else { + painters.append(new ThreadPainter(pixmaps.at(i))); + } + painters.at(i)->moveToThread(threads.at(i)); + painters.at(i)->connect(threads.at(i), SIGNAL(started()), painters.at(i), SLOT(draw())); + } + } + + ~PainterThreads() { + qDeleteAll(threads); + qDeleteAll(painters); + if (useWidgets) + qDeleteAll(widgets); + else + qDeleteAll(pixmaps); + } + + + void start() { + for (int i=0; istart(); + } + + bool areRunning() { + bool running = false; + for (int i=0; iisRunning()) + running = true; + } + + return running; + } + + bool failed() { + for (int i=0; ifailed()) + return true; + } + + return false; + } + +private: + QList threads; + QList pixmaps; + QList widgets; + QList painters; + int numThreads; + bool useWidgets; +}; + + +void tst_QGLThreads::painterOnGLWidgetInThread() +{ +#ifdef Q_OS_MAC + QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll); +#endif + PainterThreads threads(5, true); + threads.start(); + + while (threads.areRunning()) { + qApp->processEvents(); +#ifdef Q_WS_WIN + Sleep(100); +#else + usleep(100 * 1000); +#endif + } + QVERIFY(!threads.failed()); +} + +void tst_QGLThreads::painterOnPixmapInThread() { #ifdef Q_WS_X11 - XInitThreads(); + QSKIP("Drawing text to XPixmaps in threads currently doesn't work.", SkipAll); +#endif + PainterThreads threads(5, false); + threads.start(); + + while (threads.areRunning()) { + qApp->processEvents(); +#ifdef Q_WS_WIN + Sleep(100); +#else + usleep(100 * 1000); #endif + } + QVERIFY(!threads.failed()); +} +int main(int argc, char **argv) +{ + QApplication::setAttribute(Qt::AA_X11InitThreads); QApplication app(argc, argv); QTEST_DISABLE_KEYPAD_NAVIGATION \ diff --git a/tests/auto/qglthreads/tst_qglthreads.h b/tests/auto/qglthreads/tst_qglthreads.h index 9e97909..ae6b953 100644 --- a/tests/auto/qglthreads/tst_qglthreads.h +++ b/tests/auto/qglthreads/tst_qglthreads.h @@ -56,6 +56,8 @@ private slots: void renderInThread_data(); void renderInThread(); + void painterOnGLWidgetInThread(); + void painterOnPixmapInThread(); }; #endif // TST_QGLTHREADS_H -- cgit v0.12 From 3ba16e3923003a7919eb157ae3375792b714484e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Tue, 24 Aug 2010 13:53:49 +0200 Subject: Call eglReleaseThread() when a thread exits, and fix warnings. In Qt we track the current context in a thread via a QThreadStorage. The thread storage contents are deleted in the thread context, just before it's destroyed. This means we can safely call eglReleaseThread() in the QGLThreadContext destructor. We can then get rid of unnecessary context swaps, since we don't need to take care to reset a thread context back to 0 after having used e.g. the QGLShareContextScope mechanism, which is good. Reviewed-by: Samuel --- src/opengl/qgl.cpp | 5 +++++ src/opengl/qgl_x11egl.cpp | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 71d42a5..d9ba100 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -107,6 +107,11 @@ extern const QX11Info *qt_x11Info(const QPaintDevice *pd); #endif struct QGLThreadContext { +#ifdef QT_OPENGL_ES + ~QGLThreadContext() { + eglReleaseThread(); + } +#endif QGLContext *context; }; diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 9d28de0..1f7e60a 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -97,7 +97,6 @@ QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *) XVisualInfo visualInfo; XVisualInfo *vi; int numVisuals; - EGLint id = 0; visualInfo.visualid = QEgl::getCompatibleVisualId(config); vi = XGetVisualInfo(X11->display, VisualIDMask, &visualInfo, &numVisuals); @@ -346,7 +345,7 @@ void QGLWidgetPrivate::recreateEglSurface() // old surface before re-creating a new one. Note: This should not be the case as the // surface should be deleted before the old window id. if (glcx->d_func()->eglSurface != EGL_NO_SURFACE && (currentId != eglSurfaceWindowId)) { - qWarning("EGL surface for deleted window %x was not destroyed", eglSurfaceWindowId); + qWarning("EGL surface for deleted window %lx was not destroyed", eglSurfaceWindowId); glcx->d_func()->destroyEglSurfaceForDevice(); } -- cgit v0.12 From 5988bb71724bf28811b8a7324aea91e3ca75492f Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 24 Aug 2010 17:40:55 +0200 Subject: Stabilize tst_QFutureWatcher::warnRace (second attempt) --- tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp index 183f62c..c980d80 100644 --- a/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp +++ b/tests/auto/qfuturewatcher/tst_qfuturewatcher.cpp @@ -893,11 +893,9 @@ class DummyObject : public QObject { public slots: void dummySlot() {} public: - static void function(QWaitCondition *cond) + static void function(QMutex *m) { - QMutex m; - QMutexLocker lock(&m); - cond->wait(&m); + QMutexLocker lock(m); } }; @@ -908,13 +906,14 @@ void tst_QFutureWatcher::warnRace() #endif QFutureWatcher watcher; DummyObject object; - QWaitCondition cond; + QMutex mutex; + mutex.lock(); - QFuture future = QtConcurrent::run(DummyObject::function, &cond); + QFuture future = QtConcurrent::run(DummyObject::function, &mutex); watcher.setFuture(future); QTRY_VERIFY(future.isStarted()); connect(&watcher, SIGNAL(finished()), &object, SLOT(dummySlot())); - cond.wakeAll(); + mutex.unlock(); future.waitForFinished(); } -- cgit v0.12 From d585ece2e2e71b49db9500a50211127f75256154 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 25 Aug 2010 11:31:15 +0200 Subject: qdrawhelper: fix assert in fetchTransformedBilinear There can be rounding errors. Reviewed-by: paul --- src/gui/painting/qdrawhelper.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 054f96f..2184fef 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -747,7 +747,6 @@ const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator * if (fdx <= fixed_scale && fdx > 0) { // scale up on X int disty = (fy & 0x0000ffff) >> 8; int idisty = 256 - disty; - int count = length * data->m11 + 2; int x = fx >> 16; // The idea is first to do the interpolation between the row s1 and the row s2 @@ -756,7 +755,9 @@ const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator * // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG quint32 intermediate_buffer[2][buffer_size + 2]; - Q_ASSERT(length * data->m11 <= buffer_size); + // count is the size used in the intermediate_buffer. + int count = qCeil(length * data->m11) + 2; //+1 for the last pixel to interpolate with, and +1 for rounding errors. + Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case int f = 0; int lim = count; if (blendType == BlendTransformedBilinearTiled) { -- cgit v0.12