From 125f4ceb393886015574a3c3fd0fc264a4f2deb6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 8 May 2012 10:28:24 +0200 Subject: [PATCH] Allow image providers to force their loading to be asynchronous MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The request methods of an image provider are assumed to be synchronous, but sometimes the provider will be implemented in an async manner, eg. through network request or doing the I/O on a different thread. In that case, the provider can't expose this async behavior to clients, but needs to block in the request method. This is less then ideal for clients, since the default behvior of an image element is to load synchronously, so we introduce a new flag to image providers that let's the provider force the loading to happen on the async image loading thread. Similar to network requests (which are always async), this does not affect the 'asynchronous' property of the image element. Change-Id: I9542abbcc594b9aab565210bc3005a95592c1e9c Reviewed-by: Tor Arne Vestbø Reviewed-by: Michalina Ziemba --- src/qml/qml/qqmlengine.cpp | 10 +++++++++ src/qml/qml/qqmlengine.h | 7 ++++++ src/quick/util/qquickimageprovider.cpp | 18 +++++++++++++++- src/quick/util/qquickimageprovider.h | 3 ++- src/quick/util/qquickpixmapcache.cpp | 11 +++++----- .../tst_qquickimageprovider.cpp | 25 +++++++++++++++++++--- 6 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 13344f0..b6a3060 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -204,6 +204,16 @@ void QQmlEnginePrivate::defineModule() The QQuickImageProvider::requestTexture() method will be called for all image requests. \omitvalue */ +/*! + \enum QQmlImageProviderBase::Flag + + Defines specific requirements or features of this image provider. + + \value ForceAsynchronousImageLoading Ensures that image requests to the provider are + run in a separate thread, which allows the provider to spend as much time as needed + on producing the image without blocking the main thread. +*/ + /*! \internal */ QQmlImageProviderBase::QQmlImageProviderBase() { diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 8d250ed..8640f1a 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -64,14 +64,21 @@ public: Invalid }; + enum Flag { + ForceAsynchronousImageLoading = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + virtual ~QQmlImageProviderBase(); virtual ImageType imageType() const = 0; + virtual Flags flags() const = 0; private: friend class QQuickImageProvider; QQmlImageProviderBase(); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlImageProviderBase::Flags) class QQmlComponent; class QQmlEnginePrivate; diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index b306ab3..5ded343 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -47,6 +47,7 @@ class QQuickImageProviderPrivate { public: QQuickImageProvider::ImageType type; + QQuickImageProvider::Flags flags; }; /*! @@ -203,6 +204,12 @@ QImage QQuickTextureFactory::image() const allowing image loading to be executed in the background, and reducing the performance impact on the user interface. + To force asynchronous image loading, even for image sources that do not + have the \c asynchronous property set to \c true, you may pass the + \c QQuickImageProvider::ForceAsynchronousImageLoading flag to the image + provider constructor. This ensures that all image requests for the + provider are handled in a separate thread. + Asynchronous loading is not supported for image providers that provide QPixmap rather than QImage values, as pixmaps can only be created in the main thread. In this case, if \l {Image::}{asynchronous} is set to @@ -228,10 +235,11 @@ QImage QQuickTextureFactory::image() const /*! Creates an image provider that will provide images of the given \a type. */ -QQuickImageProvider::QQuickImageProvider(ImageType type) +QQuickImageProvider::QQuickImageProvider(ImageType type, Flags flags) : d(new QQuickImageProviderPrivate) { d->type = type; + d->flags = flags; } /*! @@ -253,6 +261,14 @@ QQuickImageProvider::ImageType QQuickImageProvider::imageType() const } /*! + Returns the flags set for this provider. +*/ +QQuickImageProvider::Flags QQuickImageProvider::flags() const +{ + return d->flags; +} + +/*! Implement this method to return the image with \a id. The default implementation returns an empty image. diff --git a/src/quick/util/qquickimageprovider.h b/src/quick/util/qquickimageprovider.h index 252d57b..459b3ea 100644 --- a/src/quick/util/qquickimageprovider.h +++ b/src/quick/util/qquickimageprovider.h @@ -71,10 +71,11 @@ public: class Q_QUICK_EXPORT QQuickImageProvider : public QQmlImageProviderBase { public: - QQuickImageProvider(ImageType type); + QQuickImageProvider(ImageType type, Flags flags = 0); virtual ~QQuickImageProvider(); ImageType imageType() const; + Flags flags() const; virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize); virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize); diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 0400c95..2ef95a5 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -1168,12 +1168,13 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques QHash::Iterator iter = store->m_cache.find(key); if (iter == store->m_cache.end()) { - if (options & QQuickPixmap::Asynchronous) { - // pixmaps can only be loaded synchronously - if (url.scheme() == QLatin1String("image")) { - QQuickImageProvider *provider = static_cast(engine->imageProvider(imageProviderId(url))); - if (provider && provider->imageType() == QQuickImageProvider::Pixmap) { + if (url.scheme() == QLatin1String("image")) { + if (QQuickImageProvider *provider = static_cast(engine->imageProvider(imageProviderId(url)))) { + if (provider->imageType() == QQuickImageProvider::Pixmap) { + // pixmaps can only be loaded synchronously options &= ~QQuickPixmap::Asynchronous; + } else if (provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading) { + options |= QQuickPixmap::Asynchronous; } } } diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp index 7f9a0ef..b44fbfe 100644 --- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp +++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp @@ -61,6 +61,8 @@ private slots: void requestImage_sync(); void requestImage_async_data(); void requestImage_async(); + void requestImage_async_forced_data(); + void requestImage_async_forced(); void requestPixmap_sync_data(); void requestPixmap_sync(); @@ -81,8 +83,9 @@ private: class TestQImageProvider : public QQuickImageProvider { public: - TestQImageProvider(bool *deleteWatch = 0) - : QQuickImageProvider(Image), deleteWatch(deleteWatch) + TestQImageProvider(bool *deleteWatch = 0, bool forceAsync = false) + : QQuickImageProvider(Image, (forceAsync ? ForceAsynchronousImageLoading : Flags())) + , deleteWatch(deleteWatch) { } @@ -231,7 +234,11 @@ void tst_qquickimageprovider::runTest(bool async, QQuickImageProvider *provider) QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); - if (async) + // From this point on, treat forced async providers as async behaviour-wise + if (engine.imageProvider(QUrl(source).host()) == provider) + async |= provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading; + + if (async) QTRY_VERIFY(obj->status() == QQuickImage::Loading); QCOMPARE(obj->source(), QUrl(source)); @@ -284,6 +291,18 @@ void tst_qquickimageprovider::requestImage_async() QVERIFY(deleteWatch); } +void tst_qquickimageprovider::requestImage_async_forced_data() +{ + fillRequestTestsData("qimage|async_forced"); +} + +void tst_qquickimageprovider::requestImage_async_forced() +{ + bool deleteWatch = false; + runTest(false, new TestQImageProvider(&deleteWatch, true)); + QVERIFY(deleteWatch); +} + void tst_qquickimageprovider::requestPixmap_sync_data() { fillRequestTestsData("qpixmap"); -- 2.7.4