diff options
Diffstat (limited to 'src/declarative')
-rw-r--r-- | src/declarative/qml/qdeclarativeengine.cpp | 39 | ||||
-rw-r--r-- | src/declarative/qml/qdeclarativeengine_p.h | 3 | ||||
-rw-r--r-- | src/declarative/qml/qdeclarativeimageprovider.cpp | 164 | ||||
-rw-r--r-- | src/declarative/qml/qdeclarativeimageprovider.h | 18 | ||||
-rw-r--r-- | src/declarative/util/qdeclarativepixmapcache.cpp | 85 |
5 files changed, 263 insertions, 46 deletions
diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index c5ebe7a..2e37af7 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -621,24 +621,16 @@ QNetworkAccessManager *QDeclarativeEngine::networkAccessManager() const /*! Sets the \a provider to use for images requested via the \e - image: url scheme, with host \a providerId. + image: url scheme, with host \a providerId. The QDeclarativeEngine + takes ownership of \a provider. - QDeclarativeImageProvider allows images to be provided to QML - asynchronously. The image request will be run in a low priority - thread. This allows potentially costly image loading to be done in - the background, without affecting the performance of the UI. + Image providers enable support for pixmap and threaded image + requests. See the QDeclarativeImageProvider documentation for details on + implementing and using image providers. Note that images loaded from a QDeclarativeImageProvider are cached by QPixmapCache, similar to any image loaded by QML. - The QDeclarativeEngine assumes ownership of the provider. - - This example creates a provider with id \e colors: - - \snippet examples/declarative/cppextensions/imageprovider/imageprovider.cpp 0 - - \snippet examples/declarative/cppextensions/imageprovider/imageprovider-example.qml 0 - \sa removeImageProvider() */ void QDeclarativeEngine::addImageProvider(const QString &providerId, QDeclarativeImageProvider *provider) @@ -672,16 +664,35 @@ void QDeclarativeEngine::removeImageProvider(const QString &providerId) delete d->imageProviders.take(providerId); } +QDeclarativeImageProvider::ImageType QDeclarativeEnginePrivate::getImageProviderType(const QUrl &url) +{ + QMutexLocker locker(&mutex); + QDeclarativeImageProvider *provider = imageProviders.value(url.host()); + if (provider) + return provider->imageType(); + return static_cast<QDeclarativeImageProvider::ImageType>(-1); +} + QImage QDeclarativeEnginePrivate::getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size) { QMutexLocker locker(&mutex); QImage image; QDeclarativeImageProvider *provider = imageProviders.value(url.host()); if (provider) - image = provider->request(url.path().mid(1), size, req_size); + image = provider->requestImage(url.path().mid(1), size, req_size); return image; } +QPixmap QDeclarativeEnginePrivate::getPixmapFromProvider(const QUrl &url, QSize *size, const QSize& req_size) +{ + QMutexLocker locker(&mutex); + QPixmap pixmap; + QDeclarativeImageProvider *provider = imageProviders.value(url.host()); + if (provider) + pixmap = provider->requestPixmap(url.path().mid(1), size, req_size); + return pixmap; +} + /*! Return the base URL for this engine. The base URL is only used to resolve components when a relative URL is passed to the diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h index cfa9d73..f457b53 100644 --- a/src/declarative/qml/qdeclarativeengine_p.h +++ b/src/declarative/qml/qdeclarativeengine_p.h @@ -64,6 +64,7 @@ #include "qdeclarativecontext.h" #include "private/qdeclarativecontext_p.h" #include "qdeclarativeexpression.h" +#include "qdeclarativeimageprovider.h" #include "private/qdeclarativeproperty_p.h" #include "private/qdeclarativepropertycache_p.h" #include "private/qdeclarativeobjectscriptclass_p.h" @@ -233,7 +234,9 @@ public: mutable QDeclarativeNetworkAccessManagerFactory *networkAccessManagerFactory; QHash<QString,QDeclarativeImageProvider*> imageProviders; + QDeclarativeImageProvider::ImageType getImageProviderType(const QUrl &url); QImage getImageFromProvider(const QUrl &url, QSize *size, const QSize& req_size); + QPixmap getPixmapFromProvider(const QUrl &url, QSize *size, const QSize& req_size); mutable QMutex mutex; diff --git a/src/declarative/qml/qdeclarativeimageprovider.cpp b/src/declarative/qml/qdeclarativeimageprovider.cpp index f4a8588..f38da4e 100644 --- a/src/declarative/qml/qdeclarativeimageprovider.cpp +++ b/src/declarative/qml/qdeclarativeimageprovider.cpp @@ -43,35 +43,142 @@ QT_BEGIN_NAMESPACE +class QDeclarativeImageProviderPrivate +{ +public: + QDeclarativeImageProvider::ImageType type; +}; + /*! \class QDeclarativeImageProvider \since 4.7 - \brief The QDeclarativeImageProvider class provides an interface for threaded image requests in QML. + \brief The QDeclarativeImageProvider class provides an interface for supporting pixmaps and threaded image requests in QML. - QDeclarativeImageProvider can be used by a QDeclarativeEngine to provide images to QML asynchronously. - The image request will be run in a low priority thread, allowing potentially costly image - loading to be done in the background, without affecting the performance of the UI. + QDeclarativeImageProvider is used to provide advanced image loading features + in QML applications. It allows images in QML to be: - See the QDeclarativeEngine::addImageProvider() documentation for an - example of how a custom QDeclarativeImageProvider can be constructed and used. + \list + \o Loaded using QPixmaps rather than actual image files + \o Loaded asynchronously in a separate thread, if imageType() is \l ImageType::Image + \endlist - \note the request() method may be called by multiple threads, so ensure the - implementation of this method is reentrant. + To specify that an image should be loaded by an image provider, use the + \bold {"image:"} scheme for the URL source of the image, followed by the + identifiers of the image provider and the requested image. For example: + + \qml + Image { source: "image://myimageprovider/image.png" } + \endqml + + This specifies that the image should be loaded by the image provider named + "myimageprovider", and the image to be loaded is named "image.png". The QML engine + invokes the appropriate image provider according to the providers that have + been registered through QDeclarativeEngine::addImageProvider(). + + + \section2 An example + + Here are two images. Their \c source values indicate they should be loaded by + an image provider named "colors", and the images to be loaded are "yellow" + and "red", respectively: + + \snippet examples/declarative/cppextensions/imageprovider/imageprovider-example.qml 0 + + When these images are loaded by QML, it looks for a matching image provider + and calls its requestImage() or requestPixmap() method (depending on its + imageType()) to load the image. The method is called with the \c id + parameter set to "yellow" for the first image, and "red" for the second. + + Here is an image provider implementation that can load the images + requested by the above QML. This implementation dynamically + generates QPixmap images that are filled with the requested color: + + \snippet examples/declarative/cppextensions/imageprovider/imageprovider.cpp 0 + \codeline + \snippet examples/declarative/cppextensions/imageprovider/imageprovider.cpp 1 + + To make this provider accessible to QML, it is registered with the QML engine + with a "colors" identifier: + + \code + int main(int argc, char *argv[]) + { + ... + + QDeclarativeEngine engine; + engine->addImageProvider(QLatin1String("colors"), new ColorPixmapProvider); + + ... + } + \endcode + + Now the images can be succesfully loaded in QML: + + \image imageprovider.png + + A complete example is available in Qt's + \l {declarative/cppextensions/imageprovider}{examples/declarative/cppextensions/imageprovider} + directory. Note the example registers the provider via a \l{QDeclarativeExtensionPlugin}{plugin} + instead of registering it in the application \c main() function as shown above. + + + \section2 Asynchronous image loading + + Image providers that support QImage loading automatically include support + for asychronous loading of images. To enable asynchronous loading for an + \l Image source, set \l Image::asynchronous to \c true. When this is enabled, + the image request to the provider is run in a low priority thread, + allowing image loading to be executed in the background, and reducing the + performance impact on the user interface. + + 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 + \c true, the value is ignored and the image is loaded + synchronously. + + \sa QDeclarativeEngine::addImageProvider() +*/ + +/*! + \enum QDeclarativeImageProvider::ImageType + + Defines the type of image supported by this image provider. - \sa QDeclarativeEngine::addImageProvider(), {declarative/cppextensions/imageprovider}{ImageProvider example} + \value Image The Image Provider provides QImage images. The + requestImage() method will be called for all image requests. + \value Pixmap The Image Provider provides QPixmap images. The + requestPixmap() method will be called for all image requests. */ /*! - Destroys the image provider. - */ + Creates an image provider that will provide images of the given \a type. +*/ +QDeclarativeImageProvider::QDeclarativeImageProvider(ImageType type) + : d(new QDeclarativeImageProviderPrivate) +{ + d->type = type; +} + +/*! + \internal +*/ QDeclarativeImageProvider::~QDeclarativeImageProvider() { + delete d; } /*! - \fn QImage QDeclarativeImageProvider::request(const QString &id, QSize *size, const QSize& requestedSize) + Returns the image type supported by this provider. +*/ +QDeclarativeImageProvider::ImageType QDeclarativeImageProvider::imageType() const +{ + return d->type; +} - Implement this method to return the image with \a id. +/*! + Implement this method to return the image with \a id. The default + implementation returns an empty image. If \a requestedSize is a valid size, the image returned should be of that size. @@ -80,5 +187,36 @@ QDeclarativeImageProvider::~QDeclarativeImageProvider() \note this method may be called by multiple threads, so ensure the implementation of this method is reentrant. */ +QImage QDeclarativeImageProvider::requestImage(const QString &id, QSize *size, const QSize& requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Image) + qWarning("ImageProvider supports Image type but has not implemented requestImage()"); + return QImage(); +} + +/*! + Implement this method to return the pixmap with \a id. The default + implementation returns an empty pixmap. + + If \a requestedSize is a valid size, the image returned should be of that size. + + In all cases, \a size must be set to the original size of the image. + + \note this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ +QPixmap QDeclarativeImageProvider::requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(size); + Q_UNUSED(requestedSize); + if (d->type == Pixmap) + qWarning("ImageProvider supports Pixmap type but has not implemented requestPixmap()"); + return QPixmap(); +} QT_END_NAMESPACE + diff --git a/src/declarative/qml/qdeclarativeimageprovider.h b/src/declarative/qml/qdeclarativeimageprovider.h index cc9c9af..5a72943 100644 --- a/src/declarative/qml/qdeclarativeimageprovider.h +++ b/src/declarative/qml/qdeclarativeimageprovider.h @@ -43,6 +43,7 @@ #define QDECLARATIVEIMAGEPROVIDER_H #include <QtGui/qimage.h> +#include <QtGui/qpixmap.h> QT_BEGIN_HEADER @@ -50,11 +51,26 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) +class QDeclarativeImageProviderPrivate; + class Q_DECLARATIVE_EXPORT QDeclarativeImageProvider { public: + enum ImageType { + Image, + Pixmap + }; + + QDeclarativeImageProvider(ImageType type); virtual ~QDeclarativeImageProvider(); - virtual QImage request(const QString &id, QSize *size, const QSize& requestedSize) = 0; + + ImageType imageType() const; + + virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize); + virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize); + +private: + QDeclarativeImageProviderPrivate *d; }; QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index fdc2455..3f496b3 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -736,6 +736,58 @@ void QDeclarativePixmapData::removeFromCache() } } +static QDeclarativePixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok) +{ + if (url.scheme() == QLatin1String("image")) { + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url); + + switch (imageType) { + case QDeclarativeImageProvider::Image: + { + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + if (!image.isNull()) { + *ok = true; + return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } + case QDeclarativeImageProvider::Pixmap: + { + QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize); + if (!pixmap.isNull()) { + *ok = true; + return new QDeclarativePixmapData(url, pixmap, readSize, requestSize); + } + } + } + + // no matching provider, or provider has bad image type, or provider returned null image + return new QDeclarativePixmapData(url, requestSize, + QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString())); + } + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (localFile.isEmpty()) + return 0; + + QFile f(localFile); + QSize readSize; + QString errorString; + + if (f.open(QIODevice::ReadOnly)) { + QImage image; + if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { + *ok = true; + return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } else { + errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + } + return new QDeclarativePixmapData(url, requestSize, errorString); +} + + struct QDeclarativePixmapNull { QUrl url; QPixmap pixmap; @@ -893,27 +945,24 @@ void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key); if (iter == store->m_cache.end()) { - if (!async) { - QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); - if (!localFile.isEmpty()) { - QFile f(localFile); - QSize readSize; - QString errorString; - - if (f.open(QIODevice::ReadOnly)) { - QImage image; - if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { - d = new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); - d->addToCache(); - return; - } - } else { - errorString = tr("Cannot open: %1").arg(url.toString()); - } + if (async) { + if (url.scheme() == QLatin1String("image") + && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) { + qWarning().nospace() << "Pixmaps must be loaded synchronously, ignoring asynchronous property for Image with source: " + << url.toString(); + async = false; + } + } - d = new QDeclarativePixmapData(url, requestSize, errorString); + if (!async) { + bool ok = false; + d = createPixmapDataSync(engine, url, requestSize, &ok); + if (ok) { + d->addToCache(); return; } + if (d) // loadable, but encountered error while loading + return; } if (!engine) |