From b77e592cf9709c31f61c3e1d229b2a6c446ab8bc Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Tue, 16 Feb 2010 11:27:24 +1000 Subject: Add QmlImageProvider to allow asynchronous access to images. Setting an image source to image://providerid/imageid will pass the request for imageid to the provider registered for providerid. QmlImageProvider::request() is run in a low priority thread, so the main thread is not blocked while the image is loaded/rendered. Reviewed-by: Aaron Kennedy --- .../declarative/imageprovider/imageprovider.pro | 9 ++ .../declarative/imageprovider/imageprovider.qrc | 5 + examples/declarative/imageprovider/main.cpp | 99 +++++++++++++ examples/declarative/imageprovider/view.qml | 22 +++ src/declarative/qml/qml.pri | 2 + src/declarative/qml/qmlengine.cpp | 89 ++++++++++- src/declarative/qml/qmlengine.h | 5 + src/declarative/qml/qmlengine_p.h | 7 + src/declarative/qml/qmlimageprovider.cpp | 68 +++++++++ src/declarative/qml/qmlimageprovider.h | 64 ++++++++ src/declarative/util/qmlpixmapcache.cpp | 38 +++-- tests/auto/declarative/declarative.pro | 1 + .../declarative/qmlimageprovider/data/exists.png | Bin 0 -> 2738 bytes .../declarative/qmlimageprovider/data/exists1.png | Bin 0 -> 2738 bytes .../declarative/qmlimageprovider/data/exists2.png | Bin 0 -> 2738 bytes .../qmlimageprovider/qmlimageprovider.pro | 12 ++ .../qmlimageprovider/tst_qmlimageprovider.cpp | 164 +++++++++++++++++++++ 17 files changed, 562 insertions(+), 23 deletions(-) create mode 100644 examples/declarative/imageprovider/imageprovider.pro create mode 100644 examples/declarative/imageprovider/imageprovider.qrc create mode 100644 examples/declarative/imageprovider/main.cpp create mode 100644 examples/declarative/imageprovider/view.qml create mode 100644 src/declarative/qml/qmlimageprovider.cpp create mode 100644 src/declarative/qml/qmlimageprovider.h create mode 100644 tests/auto/declarative/qmlimageprovider/data/exists.png create mode 100644 tests/auto/declarative/qmlimageprovider/data/exists1.png create mode 100644 tests/auto/declarative/qmlimageprovider/data/exists2.png create mode 100644 tests/auto/declarative/qmlimageprovider/qmlimageprovider.pro create mode 100644 tests/auto/declarative/qmlimageprovider/tst_qmlimageprovider.cpp diff --git a/examples/declarative/imageprovider/imageprovider.pro b/examples/declarative/imageprovider/imageprovider.pro new file mode 100644 index 0000000..60423ab --- /dev/null +++ b/examples/declarative/imageprovider/imageprovider.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = imageprovider +DEPENDPATH += . +INCLUDEPATH += . +QT += declarative + +# Input +SOURCES += main.cpp +RESOURCES += imageprovider.qrc diff --git a/examples/declarative/imageprovider/imageprovider.qrc b/examples/declarative/imageprovider/imageprovider.qrc new file mode 100644 index 0000000..17e9301 --- /dev/null +++ b/examples/declarative/imageprovider/imageprovider.qrc @@ -0,0 +1,5 @@ + + + view.qml + + diff --git a/examples/declarative/imageprovider/main.cpp b/examples/declarative/imageprovider/main.cpp new file mode 100644 index 0000000..718e9bb --- /dev/null +++ b/examples/declarative/imageprovider/main.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + This example illustrates using a QmlImageProvider to serve + images asynchronously. +*/ + +//![0] +class ColorImageProvider : public QmlImageProvider +{ +public: + // This is run in a low priority thread. + QImage request(const QString &id) { + QImage image(100, 50, QImage::Format_RGB32); + image.fill(QColor(id).rgba()); + QPainter p(&image); + p.setPen(Qt::black); + p.drawText(QRectF(0,0,100,50),Qt::AlignCenter,id); + return image; + } +}; + +int main(int argc, char ** argv) +{ + QApplication app(argc, argv); + + QmlView view; + view.setUrl(QUrl("qrc:view.qml")); + + view.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"); + + QmlContext *ctxt = view.rootContext(); + ctxt->setContextProperty("myModel", QVariant::fromValue(dataList)); + + view.execute(); + view.show(); + + return app.exec(); +} +//![0] diff --git a/examples/declarative/imageprovider/view.qml b/examples/declarative/imageprovider/view.qml new file mode 100644 index 0000000..2ab729d --- /dev/null +++ b/examples/declarative/imageprovider/view.qml @@ -0,0 +1,22 @@ +import Qt 4.6 +//![0] +ListView { + width: 100 + height: 100 + anchors.fill: parent + model: myModel + delegate: Component { + Item { + width: 100 + height: 50 + Text { + text: "Loading..." + anchors.centerIn: parent + } + Image { + source: modelData + } + } + } +} +//![0] diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index cd2fbff..021ef9e 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -50,6 +50,7 @@ SOURCES += \ $$PWD/qmltypenamescriptclass.cpp \ $$PWD/qmllistscriptclass.cpp \ $$PWD/qmlworkerscript.cpp \ + $$PWD/qmlimageprovider.cpp \ $$PWD/qmlnetworkaccessmanagerfactory.cpp HEADERS += \ $$PWD/qmlparser_p.h \ @@ -117,6 +118,7 @@ HEADERS += \ $$PWD/qmlworkerscript_p.h \ $$PWD/qmlscriptclass_p.h \ $$PWD/qmlguard_p.h \ + $$PWD/qmlimageprovider.h \ $$PWD/qmlnetworkaccessmanagerfactory.h QT += sql include(parser/parser.pri) diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index cdbe5f3..4edab4d 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -65,6 +65,7 @@ #include "qmlcomponent_p.h" #include "qmlscriptclass_p.h" #include "qmlnetworkaccessmanagerfactory.h" +#include "qmlimageprovider.h" #include @@ -85,6 +86,7 @@ #include #include #include +#include #include #include #include @@ -424,6 +426,7 @@ QmlContext *QmlEngine::rootContext() void QmlEngine::setNetworkAccessManagerFactory(QmlNetworkAccessManagerFactory *factory) { Q_D(QmlEngine); + QMutexLocker locker(&d->mutex); d->networkAccessManagerFactory = factory; } @@ -438,17 +441,24 @@ QmlNetworkAccessManagerFactory *QmlEngine::networkAccessManagerFactory() const return d->networkAccessManagerFactory; } +QNetworkAccessManager *QmlEnginePrivate::createNetworkAccessManager(QObject *parent) const +{ + QMutexLocker locker(&mutex); + QNetworkAccessManager *nam; + if (networkAccessManagerFactory) { + nam = networkAccessManagerFactory->create(parent); + } else { + nam = new QNetworkAccessManager(parent); + } + + return nam; +} + QNetworkAccessManager *QmlEnginePrivate::getNetworkAccessManager() const { Q_Q(const QmlEngine); - - if (!networkAccessManager) { - if (networkAccessManagerFactory) { - networkAccessManager = networkAccessManagerFactory->create(const_cast(q)); - } else { - networkAccessManager = new QNetworkAccessManager(const_cast(q)); - } - } + if (!networkAccessManager) + networkAccessManager = createNetworkAccessManager(const_cast(q)); return networkAccessManager; } @@ -470,6 +480,69 @@ QNetworkAccessManager *QmlEngine::networkAccessManager() const } /*! + Sets the \a provider to use for images requested via the \e image: url + scheme, with host \a providerId. + + QmlImageProvider 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. + + Note that images loaded from a QmlImageProvider are cached by + QPixmapCache, similar to any image loaded by QML. + + The QmlEngine assumes ownership of the provider. + + This example creates a provider with id \e colors: + + \snippet examples/declarative/imageprovider/main.cpp 0 + + \snippet examples/declarative/imageprovider/view.qml 0 + + \sa removeImageProvider() +*/ +void QmlEngine::addImageProvider(const QString &providerId, QmlImageProvider *provider) +{ + Q_D(QmlEngine); + QMutexLocker locker(&d->mutex); + d->imageProviders.insert(providerId, provider); +} + +/*! + Returns the QmlImageProvider set for \a providerId. +*/ +QmlImageProvider *QmlEngine::imageProvider(const QString &providerId) const +{ + Q_D(const QmlEngine); + QMutexLocker locker(&d->mutex); + return d->imageProviders.value(providerId); +} + +/*! + Removes the QmlImageProvider for \a providerId. + + Returns the provider if it was found; otherwise returns 0. + + \sa addImageProvider() +*/ +void QmlEngine::removeImageProvider(const QString &providerId) +{ + Q_D(QmlEngine); + QMutexLocker locker(&d->mutex); + delete d->imageProviders.take(providerId); +} + +QImage QmlEnginePrivate::getImageFromProvider(const QUrl &url) +{ + QMutexLocker locker(&mutex); + QImage image; + QmlImageProvider *provider = imageProviders.value(url.host()); + if (provider) + image = provider->request(url.path().mid(1)); + return image; +} + +/*! Return the base URL for this engine. The base URL is only used to resolve components when a relative URL is passed to the QmlComponent constructor. diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h index 7ee014a..a59a1ba 100644 --- a/src/declarative/qml/qmlengine.h +++ b/src/declarative/qml/qmlengine.h @@ -62,6 +62,7 @@ class QmlType; class QUrl; class QScriptEngine; class QScriptContext; +class QmlImageProvider; class QNetworkAccessManager; class QmlNetworkAccessManagerFactory; class Q_DECLARATIVE_EXPORT QmlEngine : public QObject @@ -83,6 +84,10 @@ public: QNetworkAccessManager *networkAccessManager() const; + void addImageProvider(const QString &id, QmlImageProvider *); + QmlImageProvider *imageProvider(const QString &id) const; + void removeImageProvider(const QString &id); + void setOfflineStoragePath(const QString& dir); QString offlineStoragePath() const; diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 13ed5ef..87864b7 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -211,10 +212,16 @@ public: bool inBeginCreate; + QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; QNetworkAccessManager *getNetworkAccessManager() const; mutable QNetworkAccessManager *networkAccessManager; mutable QmlNetworkAccessManagerFactory *networkAccessManagerFactory; + QHash imageProviders; + QImage getImageFromProvider(const QUrl &url); + + mutable QMutex mutex; + QmlCompositeTypeManager typeManager; QStringList fileImportPath; QString offlineStoragePath; diff --git a/src/declarative/qml/qmlimageprovider.cpp b/src/declarative/qml/qmlimageprovider.cpp new file mode 100644 index 0000000..ebb8656 --- /dev/null +++ b/src/declarative/qml/qmlimageprovider.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlimageprovider.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QmlImageProvider + \brief The QmlImageProvider class provides an interface for threaded image requests. + + Note: the request() method may be called by multiple threads, so ensure the + implementation of this method is reentrant. + + \sa QmlEngine::addImageProvider() +*/ +QmlImageProvider::~QmlImageProvider() +{ +} + +/*! + \fn QImage QmlImageProvider::request(const QString &id) + + Implement this method to return the image with \a id. + + Note: this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlimageprovider.h b/src/declarative/qml/qmlimageprovider.h new file mode 100644 index 0000000..9804815 --- /dev/null +++ b/src/declarative/qml/qmlimageprovider.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLIMAGEPROVIDER_H +#define QMLIMAGEPROVIDER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QmlImageProvider +{ +public: + virtual ~QmlImageProvider(); + virtual QImage request(const QString &id) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLIMAGEPROVIDER diff --git a/src/declarative/util/qmlpixmapcache.cpp b/src/declarative/util/qmlpixmapcache.cpp index c03b5df..623aaf0 100644 --- a/src/declarative/util/qmlpixmapcache.cpp +++ b/src/declarative/util/qmlpixmapcache.cpp @@ -41,11 +41,13 @@ #include "qmlpixmapcache_p.h" #include "qmlnetworkaccessmanagerfactory.h" +#include "qmlimageprovider.h" #include "qfxperf_p_p.h" #include #include +#include #include #include @@ -82,7 +84,7 @@ class QmlImageReaderEvent : public QEvent public: enum ReadError { NoError, Loading, Decoding }; - QmlImageReaderEvent(QmlImageReaderEvent::ReadError err, const QString &errStr, QImage &img) + QmlImageReaderEvent(QmlImageReaderEvent::ReadError err, const QString &errStr, const QImage &img) : QEvent(QEvent::User), error(err), errorString(errStr), image(img) {} ReadError error; @@ -143,13 +145,8 @@ private slots: private: QNetworkAccessManager *networkAccessManager() { - if (!accessManager) { - if (engine && engine->networkAccessManagerFactory()) { - accessManager = engine->networkAccessManagerFactory()->create(this); - } else { - accessManager = new QNetworkAccessManager(this); - } - } + if (!accessManager) + accessManager = QmlEnginePrivate::get(engine)->createNetworkAccessManager(this); return accessManager; } @@ -197,21 +194,32 @@ bool QmlImageRequestHandler::event(QEvent *event) break; } - QmlPixmapReply *runningJob = reader->jobs.takeFirst(); + QmlPixmapReply *runningJob = reader->jobs.takeLast(); runningJob->addRef(); runningJob->setLoading(); QUrl url = runningJob->url(); reader->mutex.unlock(); // fetch - QNetworkRequest req(url); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - QNetworkReply *reply = networkAccessManager()->get(req); + if (url.scheme() == QLatin1String("image")) { + QImage image = QmlEnginePrivate::get(engine)->getImageFromProvider(url); + QmlImageReaderEvent::ReadError errorCode = QmlImageReaderEvent::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QmlImageReaderEvent::Loading; + errorStr = QLatin1String("Failed to get image from provider: ") + url.toString(); + } + QCoreApplication::postEvent(runningJob, new QmlImageReaderEvent(errorCode, errorStr, image)); + } else { + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply *reply = networkAccessManager()->get(req); - QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); - QMetaObject::connect(reply, replyFinished, this, thisNetworkRequestDone); + QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); + QMetaObject::connect(reply, replyFinished, this, thisNetworkRequestDone); - replies.insert(reply, runningJob); + replies.insert(reply, runningJob); + } } return true; } diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index 8773026..fba63d5 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -57,6 +57,7 @@ SUBDIRS += \ qmlgraphicsrepeater \ # Cover qmlvaluetypes \ # Cover qmlxmlhttprequest \ # Cover + qmlimageprovider \ # Cover sql # Cover diff --git a/tests/auto/declarative/qmlimageprovider/data/exists.png b/tests/auto/declarative/qmlimageprovider/data/exists.png new file mode 100644 index 0000000..399bd0b Binary files /dev/null and b/tests/auto/declarative/qmlimageprovider/data/exists.png differ diff --git a/tests/auto/declarative/qmlimageprovider/data/exists1.png b/tests/auto/declarative/qmlimageprovider/data/exists1.png new file mode 100644 index 0000000..399bd0b Binary files /dev/null and b/tests/auto/declarative/qmlimageprovider/data/exists1.png differ diff --git a/tests/auto/declarative/qmlimageprovider/data/exists2.png b/tests/auto/declarative/qmlimageprovider/data/exists2.png new file mode 100644 index 0000000..399bd0b Binary files /dev/null and b/tests/auto/declarative/qmlimageprovider/data/exists2.png differ diff --git a/tests/auto/declarative/qmlimageprovider/qmlimageprovider.pro b/tests/auto/declarative/qmlimageprovider/qmlimageprovider.pro new file mode 100644 index 0000000..0ef7739 --- /dev/null +++ b/tests/auto/declarative/qmlimageprovider/qmlimageprovider.pro @@ -0,0 +1,12 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += declarative +QT += network +macx:CONFIG -= app_bundle + +SOURCES += tst_qmlimageprovider.cpp + +# QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage +# LIBS += -lgcov + +# Define SRCDIR equal to test's source directory +DEFINES += SRCDIR=\\\"$$PWD\\\" diff --git a/tests/auto/declarative/qmlimageprovider/tst_qmlimageprovider.cpp b/tests/auto/declarative/qmlimageprovider/tst_qmlimageprovider.cpp new file mode 100644 index 0000000..475cb7a --- /dev/null +++ b/tests/auto/declarative/qmlimageprovider/tst_qmlimageprovider.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include + +#define TRY_WAIT(expr) \ + do { \ + for (int ii = 0; ii < 6; ++ii) { \ + if ((expr)) break; \ + QTest::qWait(50); \ + } \ + QVERIFY((expr)); \ + } while (false) + + +class tst_qmlimageprovider : public QObject +{ + Q_OBJECT +public: + tst_qmlimageprovider() + { + } + +private slots: + void imageSource(); + void imageSource_data(); + void removeProvider(); + +private: + QmlEngine engine; +}; + +class TestProvider : public QmlImageProvider +{ +public: + QImage request(const QString &id) { + QImage image; + image.load(SRCDIR "/data/" + id); + return image; + } +}; + +void tst_qmlimageprovider::imageSource_data() +{ + QTest::addColumn("source"); + QTest::addColumn("error"); + + QTest::newRow("exists") << "image://test/exists.png" << ""; + QTest::newRow("missing") << "image://test/no-such-file.png" + << "\"Failed to get image from provider: image://test/no-such-file.png\" "; + QTest::newRow("unknown provider") << "image://bogus/exists.png" + << "\"Failed to get image from provider: image://bogus/exists.png\" "; +} + +void tst_qmlimageprovider::imageSource() +{ + QFETCH(QString, source); + QFETCH(QString, error); + + if (!error.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, error.toUtf8()); + + engine.addImageProvider("test", new TestProvider); + QVERIFY(engine.imageProvider("test") != 0); + + QString componentStr = "import Qt 4.6\nImage { source: \"" + source + "\" }"; + QmlComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QmlGraphicsImage *obj = qobject_cast(component.create()); + QVERIFY(obj != 0); + + TRY_WAIT(obj->status() == QmlGraphicsImage::Loading); + + QCOMPARE(obj->source(), QUrl(source)); + + if (error.isEmpty()) { + TRY_WAIT(obj->status() == QmlGraphicsImage::Ready); + QCOMPARE(obj->width(), 100.); + QCOMPARE(obj->height(), 100.); + QCOMPARE(obj->fillMode(), QmlGraphicsImage::Stretch); + QCOMPARE(obj->progress(), 1.0); + } else { + TRY_WAIT(obj->status() == QmlGraphicsImage::Error); + } + + delete obj; +} + +void tst_qmlimageprovider::removeProvider() +{ + engine.addImageProvider("test2", new TestProvider); + QVERIFY(engine.imageProvider("test2") != 0); + + // add provider, confirm it works + QString componentStr = "import Qt 4.6\nImage { source: \"image://test2/exists1.png\" }"; + QmlComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QmlGraphicsImage *obj = qobject_cast(component.create()); + QVERIFY(obj != 0); + + TRY_WAIT(obj->status() == QmlGraphicsImage::Loading); + TRY_WAIT(obj->status() == QmlGraphicsImage::Ready); + + QCOMPARE(obj->width(), 100.0); + + // remove the provider and confirm + QString error("\"Failed to get image from provider: image://test2/exists2.png\" "); + QTest::ignoreMessage(QtWarningMsg, error.toUtf8()); + + engine.removeImageProvider("test2"); + + obj->setSource(QUrl("image://test2/exists2.png")); + + TRY_WAIT(obj->status() == QmlGraphicsImage::Loading); + TRY_WAIT(obj->status() == QmlGraphicsImage::Error); + + delete obj; +} + + +QTEST_MAIN(tst_qmlimageprovider) + +#include "tst_qmlimageprovider.moc" -- cgit v0.12