diff options
14 files changed, 473 insertions, 147 deletions
diff --git a/doc/src/declarative/pics/imageprovider.png b/doc/src/declarative/pics/imageprovider.png Binary files differnew file mode 100644 index 0000000..422103c --- /dev/null +++ b/doc/src/declarative/pics/imageprovider.png diff --git a/doc/src/examples/qml-examples.qdoc b/doc/src/examples/qml-examples.qdoc index dec5441..4ad11d9 100644 --- a/doc/src/examples/qml-examples.qdoc +++ b/doc/src/examples/qml-examples.qdoc @@ -214,8 +214,10 @@ \title C++ Extensions: Image Provider \example declarative/cppextensions/imageprovider - This examples shows how to use QDeclarativeImageProvider to serve images asynchronously - into a QML item. + This examples shows how to use QDeclarativeImageProvider to serve images + to QML image elements. + + \image qml-imageprovider-example.png */ /*! diff --git a/doc/src/images/qml-imageprovider-example.png b/doc/src/images/qml-imageprovider-example.png Binary files differnew file mode 100644 index 0000000..e82548a --- /dev/null +++ b/doc/src/images/qml-imageprovider-example.png diff --git a/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml b/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml index 5890c91..1ef97fa 100644 --- a/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml +++ b/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml @@ -37,29 +37,13 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - import Qt 4.7 -import "ImageProviderCore" -//![0] -ListView { - width: 100; height: 100 - anchors.fill: parent - - model: myModel +import "ImageProviderCore" // import the plugin that registers the color image provider - delegate: Component { - Item { - width: 100 - height: 50 - Text { - text: "Loading..." - anchors.centerIn: parent - } - Image { - source: modelData - sourceSize: "50x25" - } - } - } +//![0] +Column { + Image { source: "image://colors/yellow" } + Image { source: "image://colors/red" } } //![0] + diff --git a/examples/declarative/cppextensions/imageprovider/imageprovider.cpp b/examples/declarative/cppextensions/imageprovider/imageprovider.cpp index 0281b4a..995192a 100644 --- a/examples/declarative/cppextensions/imageprovider/imageprovider.cpp +++ b/examples/declarative/cppextensions/imageprovider/imageprovider.cpp @@ -42,7 +42,6 @@ #include <qdeclarativeextensionplugin.h> #include <qdeclarativeengine.h> -#include <qdeclarativecontext.h> #include <qdeclarative.h> #include <qdeclarativeitem.h> #include <qdeclarativeimageprovider.h> @@ -50,62 +49,57 @@ #include <QImage> #include <QPainter> -/* - This example illustrates using a QDeclarativeImageProvider to serve - images asynchronously. -*/ - //![0] class ColorImageProvider : public QDeclarativeImageProvider { public: - // This is run in a low priority thread. - QImage request(const QString &id, QSize *size, const QSize &req_size) + ColorImageProvider() + : QDeclarativeImageProvider(Pixmap) { - if (size) *size = QSize(100,50); - QImage image( - req_size.width() > 0 ? req_size.width() : 100, - req_size.height() > 0 ? req_size.height() : 50, - QImage::Format_RGB32); - image.fill(QColor(id).rgba()); - QPainter p(&image); - QFont f = p.font(); - f.setPixelSize(30); - p.setFont(f); - p.setPen(Qt::black); - if (req_size.isValid()) - p.scale(req_size.width()/100.0, req_size.height()/50.0); - p.drawText(QRectF(0,0,100,50),Qt::AlignCenter,id); - return image; + } + + QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) + { + int width = 100; + int height = 50; + + if (size) + *size = QSize(width, height); + QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width, + requestedSize.height() > 0 ? requestedSize.height() : height); + pixmap.fill(QColor(id).rgba()); +//![0] + + // write the color name + QPainter painter(&pixmap); + QFont f = painter.font(); + f.setPixelSize(20); + painter.setFont(f); + painter.setPen(Qt::black); + if (requestedSize.isValid()) + painter.scale(requestedSize.width() / width, requestedSize.height() / height); + painter.drawText(QRectF(0, 0, width, height), Qt::AlignCenter, id); + +//![1] + return pixmap; } }; +//![1] class ImageProviderExtensionPlugin : public QDeclarativeExtensionPlugin { Q_OBJECT public: - void registerTypes(const char *uri) { + void registerTypes(const char *uri) + { Q_UNUSED(uri); - } - void initializeEngine(QDeclarativeEngine *engine, const char *uri) { + void initializeEngine(QDeclarativeEngine *engine, const char *uri) + { Q_UNUSED(uri); - engine->addImageProvider("colors", new ColorImageProvider); - - QStringList dataList; - dataList.append("image://colors/red"); - dataList.append("image://colors/green"); - dataList.append("image://colors/blue"); - dataList.append("image://colors/brown"); - dataList.append("image://colors/orange"); - dataList.append("image://colors/purple"); - dataList.append("image://colors/yellow"); - - QDeclarativeContext *ctxt = engine->rootContext(); - ctxt->setContextProperty("myModel", QVariant::fromValue(dataList)); } }; @@ -113,5 +107,4 @@ public: #include "imageprovider.moc" Q_EXPORT_PLUGIN(ImageProviderExtensionPlugin); -//![0] 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) diff --git a/tests/auto/declarative/qdeclarativeimageprovider/data/exists.png b/tests/auto/declarative/qdeclarativeimageprovider/data/exists.png Binary files differdeleted file mode 100644 index 399bd0b..0000000 --- a/tests/auto/declarative/qdeclarativeimageprovider/data/exists.png +++ /dev/null diff --git a/tests/auto/declarative/qdeclarativeimageprovider/data/exists1.png b/tests/auto/declarative/qdeclarativeimageprovider/data/exists1.png Binary files differdeleted file mode 100644 index 399bd0b..0000000 --- a/tests/auto/declarative/qdeclarativeimageprovider/data/exists1.png +++ /dev/null diff --git a/tests/auto/declarative/qdeclarativeimageprovider/data/exists2.png b/tests/auto/declarative/qdeclarativeimageprovider/data/exists2.png Binary files differdeleted file mode 100644 index 399bd0b..0000000 --- a/tests/auto/declarative/qdeclarativeimageprovider/data/exists2.png +++ /dev/null diff --git a/tests/auto/declarative/qdeclarativeimageprovider/tst_qdeclarativeimageprovider.cpp b/tests/auto/declarative/qdeclarativeimageprovider/tst_qdeclarativeimageprovider.cpp index 4185790..9d62f22 100644 --- a/tests/auto/declarative/qdeclarativeimageprovider/tst_qdeclarativeimageprovider.cpp +++ b/tests/auto/declarative/qdeclarativeimageprovider/tst_qdeclarativeimageprovider.cpp @@ -61,6 +61,8 @@ QVERIFY((expr)); \ } while (false) +Q_DECLARE_METATYPE(QDeclarativeImageProvider*); + class tst_qdeclarativeimageprovider : public QObject { @@ -71,43 +73,103 @@ public: } private slots: - void imageSource(); - void imageSource_data(); + void requestImage_sync_data(); + void requestImage_sync(); + void requestImage_async_data(); + void requestImage_async(); + + void requestPixmap_sync_data(); + void requestPixmap_sync(); + void requestPixmap_async(); + + void removeProvider_data(); void removeProvider(); private: - QDeclarativeEngine engine; + QString newImageFileName() const; + void fillRequestTestsData(const QString &id); + void runTest(bool async, QDeclarativeImageProvider *provider); }; -class TestProvider : public QDeclarativeImageProvider + +class TestQImageProvider : public QDeclarativeImageProvider { public: - QImage request(const QString &id, QSize *size, const QSize& requested_size) { - QImageReader io(SRCDIR "/data/" + id); - if (size) *size = io.size(); - if (requested_size.isValid()) - io.setScaledSize(requested_size); - return io.read(); + TestQImageProvider() + : QDeclarativeImageProvider(Image) + { + } + + QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize) + { + if (id == QLatin1String("no-such-file.png")) + return QImage(); + + int width = 100; + int height = 100; + QImage image(width, height, QImage::Format_RGB32); + if (size) + *size = QSize(width, height); + if (requestedSize.isValid()) + image = image.scaled(requestedSize); + return image; } }; +Q_DECLARE_METATYPE(TestQImageProvider*); -void tst_qdeclarativeimageprovider::imageSource_data() + +class TestQPixmapProvider : public QDeclarativeImageProvider +{ +public: + TestQPixmapProvider() + : QDeclarativeImageProvider(Pixmap) + { + } + + QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) + { + if (id == QLatin1String("no-such-file.png")) + return QPixmap(); + + int width = 100; + int height = 100; + QPixmap image(width, height); + if (size) + *size = QSize(width, height); + if (requestedSize.isValid()) + image = image.scaled(requestedSize); + return image; + } +}; +Q_DECLARE_METATYPE(TestQPixmapProvider*); + + +QString tst_qdeclarativeimageprovider::newImageFileName() const +{ + // need to generate new filenames each time or else images are loaded + // from cache and we won't get loading status changes when testing + // async loading + static int count = 0; + return QString("image://test/image-%1.png").arg(count++); +} + +void tst_qdeclarativeimageprovider::fillRequestTestsData(const QString &id) { QTest::addColumn<QString>("source"); QTest::addColumn<QString>("properties"); QTest::addColumn<QSize>("size"); QTest::addColumn<QString>("error"); - QTest::newRow("exists") << "image://test/exists.png" << "" << QSize(100,100) << ""; - QTest::newRow("scaled") << "image://test/exists.png" << "sourceSize: \"80x30\"" << QSize(80,30) << ""; - QTest::newRow("missing") << "image://test/no-such-file.png" << "" << QSize() + QTest::newRow(QTest::toString(id + " exists")) << newImageFileName() << "" << QSize(100,100) << ""; + QTest::newRow(QTest::toString(id + " scaled")) << newImageFileName() << "sourceSize: \"80x30\"" << QSize(80,30) << ""; + + QTest::newRow(QTest::toString(id + " missing")) << "image://test/no-such-file.png" << "" << QSize() << "file::2:1: QML Image: Failed to get image from provider: image://test/no-such-file.png"; - QTest::newRow("unknown provider") << "image://bogus/exists.png" << "" << QSize() + QTest::newRow(QTest::toString(id + " unknown provider")) << "image://bogus/exists.png" << "" << QSize() << "file::2:1: QML Image: Failed to get image from provider: image://bogus/exists.png"; - } - -void tst_qdeclarativeimageprovider::imageSource() + +void tst_qdeclarativeimageprovider::runTest(bool async, QDeclarativeImageProvider *provider) { QFETCH(QString, source); QFETCH(QString, properties); @@ -117,21 +179,29 @@ void tst_qdeclarativeimageprovider::imageSource() if (!error.isEmpty()) QTest::ignoreMessage(QtWarningMsg, error.toUtf8()); - engine.addImageProvider("test", new TestProvider); + QDeclarativeEngine engine; + + engine.addImageProvider("test", provider); QVERIFY(engine.imageProvider("test") != 0); - QString componentStr = "import Qt 4.7\nImage { source: \"" + source + "\"; " + properties + " }"; + QString componentStr = "import Qt 4.7\nImage { source: \"" + source + "\"; " + + (async ? "asynchronous: true; " : "") + + properties + " }"; QDeclarativeComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QDeclarativeImage *obj = qobject_cast<QDeclarativeImage*>(component.create()); QVERIFY(obj != 0); - TRY_WAIT(obj->status() == QDeclarativeImage::Loading); + if (async) + TRY_WAIT(obj->status() == QDeclarativeImage::Loading); QCOMPARE(obj->source(), QUrl(source)); if (error.isEmpty()) { - TRY_WAIT(obj->status() == QDeclarativeImage::Ready); + if (async) + TRY_WAIT(obj->status() == QDeclarativeImage::Ready); + else + QVERIFY(obj->status() == QDeclarativeImage::Ready); QCOMPARE(obj->width(), 100.0); QCOMPARE(obj->height(), 100.0); QCOMPARE(obj->pixmap().width(), size.width()); @@ -139,40 +209,100 @@ void tst_qdeclarativeimageprovider::imageSource() QCOMPARE(obj->fillMode(), QDeclarativeImage::Stretch); QCOMPARE(obj->progress(), 1.0); } else { - TRY_WAIT(obj->status() == QDeclarativeImage::Error); + if (async) + TRY_WAIT(obj->status() == QDeclarativeImage::Error); + else + QVERIFY(obj->status() == QDeclarativeImage::Error); } delete obj; } +void tst_qdeclarativeimageprovider::requestImage_sync_data() +{ + fillRequestTestsData("qimage|sync"); +} + +void tst_qdeclarativeimageprovider::requestImage_sync() +{ + runTest(false, new TestQImageProvider); +} + +void tst_qdeclarativeimageprovider::requestImage_async_data() +{ + fillRequestTestsData("qimage|async"); +} + +void tst_qdeclarativeimageprovider::requestImage_async() +{ + runTest(true, new TestQImageProvider); +} + +void tst_qdeclarativeimageprovider::requestPixmap_sync_data() +{ + fillRequestTestsData("qpixmap"); +} + +void tst_qdeclarativeimageprovider::requestPixmap_sync() +{ + runTest(false, new TestQPixmapProvider); +} + +void tst_qdeclarativeimageprovider::requestPixmap_async() +{ + QDeclarativeEngine engine; + QDeclarativeImageProvider *provider = new TestQPixmapProvider; + + engine.addImageProvider("test", provider); + QVERIFY(engine.imageProvider("test") != 0); + + QTest::ignoreMessage(QtWarningMsg, + "Pixmaps must be loaded synchronously, ignoring asynchronous property for Image with source: \"image://test/pixmap-async-test.png\""); + + QString componentStr = "import Qt 4.7\nImage { asynchronous: true; source: \"image://test/pixmap-async-test.png\" }"; + QDeclarativeComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QDeclarativeImage *obj = qobject_cast<QDeclarativeImage*>(component.create()); + QVERIFY(obj != 0); + + delete obj; +} + +void tst_qdeclarativeimageprovider::removeProvider_data() +{ + QTest::addColumn<QDeclarativeImageProvider*>("provider"); + + QTest::newRow("qimage") << static_cast<QDeclarativeImageProvider*>(new TestQImageProvider); + QTest::newRow("qpixmap") << static_cast<QDeclarativeImageProvider*>(new TestQPixmapProvider); +} + void tst_qdeclarativeimageprovider::removeProvider() { - engine.addImageProvider("test2", new TestProvider); - QVERIFY(engine.imageProvider("test2") != 0); + QFETCH(QDeclarativeImageProvider*, provider); + + QDeclarativeEngine engine; + + engine.addImageProvider("test", provider); + QVERIFY(engine.imageProvider("test") != 0); // add provider, confirm it works - QString componentStr = "import Qt 4.7\nImage { source: \"image://test2/exists1.png\" }"; + QString componentStr = "import Qt 4.7\nImage { source: \"" + newImageFileName() + "\" }"; QDeclarativeComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QDeclarativeImage *obj = qobject_cast<QDeclarativeImage*>(component.create()); QVERIFY(obj != 0); - TRY_WAIT(obj->status() == QDeclarativeImage::Loading); - TRY_WAIT(obj->status() == QDeclarativeImage::Ready); - - QCOMPARE(obj->width(), 100.0); + QCOMPARE(obj->status(), QDeclarativeImage::Ready); // remove the provider and confirm - QString error("file::2:1: QML Image: Failed to get image from provider: image://test2/exists2.png"); - + QString fileName = newImageFileName(); + QString error("file::2:1: QML Image: Failed to get image from provider: " + fileName); QTest::ignoreMessage(QtWarningMsg, error.toUtf8()); - engine.removeImageProvider("test2"); - - obj->setSource(QUrl("image://test2/exists2.png")); + engine.removeImageProvider("test"); - TRY_WAIT(obj->status() == QDeclarativeImage::Loading); - TRY_WAIT(obj->status() == QDeclarativeImage::Error); + obj->setSource(QUrl(fileName)); + QCOMPARE(obj->status(), QDeclarativeImage::Error); delete obj; } |