From 30767891615c1c9fc20e8a26da923f94d1728b4b Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Mon, 14 Dec 2009 16:30:07 +1000 Subject: Decode images loaded via network in a separate thread. --- .../graphicsitems/qmlgraphicsborderimage.cpp | 20 +- .../graphicsitems/qmlgraphicsimagebase.cpp | 14 +- .../graphicsitems/qmlgraphicsparticles.cpp | 11 +- src/declarative/util/qmlpixmapcache.cpp | 424 ++++++++++++++------- src/declarative/util/qmlpixmapcache_p.h | 44 ++- 5 files changed, 344 insertions(+), 169 deletions(-) diff --git a/src/declarative/graphicsitems/qmlgraphicsborderimage.cpp b/src/declarative/graphicsitems/qmlgraphicsborderimage.cpp index 01c3e37..877e141 100644 --- a/src/declarative/graphicsitems/qmlgraphicsborderimage.cpp +++ b/src/declarative/graphicsitems/qmlgraphicsborderimage.cpp @@ -79,7 +79,7 @@ QmlGraphicsBorderImage::~QmlGraphicsBorderImage() if (d->sciReply) d->sciReply->deleteLater(); if (d->sciPendingPixmapCache) - QmlPixmapCache::cancelGet(d->sciurl, this); + QmlPixmapCache::cancel(d->sciurl, this); } /*! \qmlproperty enum BorderImage::status @@ -159,11 +159,11 @@ void QmlGraphicsBorderImage::setSource(const QUrl &url) } if (d->pendingPixmapCache) { - QmlPixmapCache::cancelGet(d->url, this); + QmlPixmapCache::cancel(d->url, this); d->pendingPixmapCache = false; } if (d->sciPendingPixmapCache) { - QmlPixmapCache::cancelGet(d->sciurl, this); + QmlPixmapCache::cancel(d->sciurl, this); d->sciPendingPixmapCache = false; } @@ -200,8 +200,9 @@ void QmlGraphicsBorderImage::setSource(const QUrl &url) this, SLOT(sciRequestFinished())); } } else { - QNetworkReply *reply = QmlPixmapCache::get(qmlEngine(this), d->url, &d->pix); - if (reply) { + QmlPixmapReply::Status status = QmlPixmapCache::get(d->url, &d->pix); + if (status != QmlPixmapReply::Ready && status != QmlPixmapReply::Error) { + QmlPixmapReply *reply = QmlPixmapCache::request(qmlEngine(this), d->url); d->pendingPixmapCache = true; connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); connect(reply, SIGNAL(downloadProgress(qint64,qint64)), @@ -319,8 +320,9 @@ void QmlGraphicsBorderImage::setGridScaledImage(const QmlGraphicsGridScaledImage d->verticalTileMode = sci.verticalTileRule(); d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl())); - QNetworkReply *reply = QmlPixmapCache::get(qmlEngine(this), d->sciurl, &d->pix); - if (reply) { + QmlPixmapReply::Status status = QmlPixmapCache::get(d->sciurl, &d->pix); + if (status != QmlPixmapReply::Ready && status != QmlPixmapReply::Error) { + QmlPixmapReply *reply = QmlPixmapCache::request(qmlEngine(this), d->sciurl); d->sciPendingPixmapCache = true; connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); connect(reply, SIGNAL(downloadProgress(qint64,qint64)), @@ -349,10 +351,10 @@ void QmlGraphicsBorderImage::requestFinished() if (d->url.path().endsWith(QLatin1String(".sci"))) { d->sciPendingPixmapCache = false; - QmlPixmapCache::find(d->sciurl, &d->pix); + QmlPixmapCache::get(d->sciurl, &d->pix); } else { d->pendingPixmapCache = false; - if (!QmlPixmapCache::find(d->url, &d->pix)) + if (QmlPixmapCache::get(d->url, &d->pix) != QmlPixmapReply::Ready) d->status = Error; } setImplicitWidth(d->pix.width()); diff --git a/src/declarative/graphicsitems/qmlgraphicsimagebase.cpp b/src/declarative/graphicsitems/qmlgraphicsimagebase.cpp index 70ca59c..9dd9f1b 100644 --- a/src/declarative/graphicsitems/qmlgraphicsimagebase.cpp +++ b/src/declarative/graphicsitems/qmlgraphicsimagebase.cpp @@ -61,7 +61,7 @@ QmlGraphicsImageBase::~QmlGraphicsImageBase() { Q_D(QmlGraphicsImageBase); if (d->pendingPixmapCache) - QmlPixmapCache::cancelGet(d->url, this); + QmlPixmapCache::cancel(d->url, this); } QmlGraphicsImageBase::Status QmlGraphicsImageBase::status() const @@ -91,7 +91,7 @@ void QmlGraphicsImageBase::setSource(const QUrl &url) return; if (d->pendingPixmapCache) { - QmlPixmapCache::cancelGet(d->url, this); + QmlPixmapCache::cancel(d->url, this); d->pendingPixmapCache = false; } @@ -112,16 +112,16 @@ void QmlGraphicsImageBase::setSource(const QUrl &url) update(); } else { d->status = Loading; - bool ok = true; - QNetworkReply *reply = QmlPixmapCache::get(qmlEngine(this), d->url, &d->pix, &ok); - if (reply) { + QmlPixmapReply::Status status = QmlPixmapCache::get(d->url, &d->pix); + if (status != QmlPixmapReply::Ready && status != QmlPixmapReply::Error) { + QmlPixmapReply *reply = QmlPixmapCache::request(qmlEngine(this), d->url); d->pendingPixmapCache = true; connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(requestProgress(qint64,qint64))); } else { //### should be unified with requestFinished - if (ok) { + if (status == QmlPixmapReply::Ready) { setImplicitWidth(d->pix.width()); setImplicitHeight(d->pix.height()); @@ -148,7 +148,7 @@ void QmlGraphicsImageBase::requestFinished() d->pendingPixmapCache = false; - if (!QmlPixmapCache::find(d->url, &d->pix)) + if (QmlPixmapCache::get(d->url, &d->pix) != QmlPixmapReply::Ready) d->status = Error; setImplicitWidth(d->pix.width()); setImplicitHeight(d->pix.height()); diff --git a/src/declarative/graphicsitems/qmlgraphicsparticles.cpp b/src/declarative/graphicsitems/qmlgraphicsparticles.cpp index 63c7658..8c5fb4f 100644 --- a/src/declarative/graphicsitems/qmlgraphicsparticles.cpp +++ b/src/declarative/graphicsitems/qmlgraphicsparticles.cpp @@ -641,7 +641,7 @@ QmlGraphicsParticles::~QmlGraphicsParticles() { Q_D(QmlGraphicsParticles); if (d->pendingPixmapCache) - QmlPixmapCache::cancelGet(d->url, this); + QmlPixmapCache::cancel(d->url, this); } /*! @@ -663,7 +663,7 @@ void QmlGraphicsParticles::imageLoaded() { Q_D(QmlGraphicsParticles); d->pendingPixmapCache = false; - QmlPixmapCache::find(d->url, &d->image); + QmlPixmapCache::get(d->url, &d->image); d->paintItem->updateSize(); d->paintItem->update(); } @@ -676,7 +676,7 @@ void QmlGraphicsParticles::setSource(const QUrl &name) return; if (d->pendingPixmapCache) { - QmlPixmapCache::cancelGet(d->url, this); + QmlPixmapCache::cancel(d->url, this); d->pendingPixmapCache = false; } if (name.isEmpty()) { @@ -687,8 +687,9 @@ void QmlGraphicsParticles::setSource(const QUrl &name) } else { d->url = name; Q_ASSERT(!name.isRelative()); - QNetworkReply *reply = QmlPixmapCache::get(qmlEngine(this), d->url, &d->image); - if (reply) { + QmlPixmapReply::Status status = QmlPixmapCache::get(d->url, &d->image); + if (status != QmlPixmapReply::Ready && status != QmlPixmapReply::Error) { + QmlPixmapReply *reply = QmlPixmapCache::request(qmlEngine(this), d->url); connect(reply, SIGNAL(finished()), this, SLOT(imageLoaded())); d->pendingPixmapCache = true; } else { diff --git a/src/declarative/util/qmlpixmapcache.cpp b/src/declarative/util/qmlpixmapcache.cpp index 16e3dc8..ef40c7f 100644 --- a/src/declarative/util/qmlpixmapcache.cpp +++ b/src/declarative/util/qmlpixmapcache.cpp @@ -44,81 +44,164 @@ #include "qfxperf_p_p.h" #include +#include +#include #include #include #include #include #include +#include +#include +#include #include +#include QT_BEGIN_NAMESPACE -class QSharedNetworkReply; -typedef QHash QmlGraphicsSharedNetworkReplyHash; -static QmlGraphicsSharedNetworkReplyHash qfxActiveNetworkReplies; +class QmlImageReader : public QThread +{ + Q_OBJECT +public: + QmlImageReader(QObject *parent=0); + ~QmlImageReader(); + + void read(QmlPixmapReply *rep); + void cancel(QmlPixmapReply *rep); + +protected: + void run(); + +private: + struct Job { + Job() : reply(0), error(false) {} + QmlPixmapReply *reply; + QImage img; + bool error; + }; + + void loadImage(Job &job); + + QList jobs; + Job runningJob; + QMutex mutex; + QWaitCondition haveJob; + bool quit; +}; -class QSharedNetworkReply +class QmlImageDecodeEvent : public QCustomEvent { public: - QSharedNetworkReply(QNetworkReply *r) : reply(r), refCount(1) {} - ~QSharedNetworkReply() - { - reply->deleteLater(); + QmlImageDecodeEvent(bool err, QImage &img) : QCustomEvent(QEvent::User), error(err), image(img) {} + + bool error; + QImage image; +}; + +Q_GLOBAL_STATIC(QmlImageReader, qmlImageReader) + + +QmlImageReader::QmlImageReader(QObject *parent) : QThread(parent), quit(false) +{ + start(QThread::LowPriority); +} + +QmlImageReader::~QmlImageReader() +{ + quit = true; + haveJob.wakeOne(); +} + +void QmlImageReader::read(QmlPixmapReply *reply) +{ + mutex.lock(); + Job job; + job.reply = reply; + jobs.append(job); + haveJob.wakeOne(); + mutex.unlock(); +} + +void QmlImageReader::cancel(QmlPixmapReply *reply) +{ + mutex.lock(); + if (runningJob.reply != reply) { + QList::iterator it = jobs.begin(); + while (it != jobs.end()) { + if ((*it).reply == reply) { + jobs.erase(it); + break; + } + ++it; + } } - QNetworkReply *reply; - QPixmap pixmap; // ensure reference to pixmap to QPixmapCache does not discard + mutex.unlock(); +} - int refCount; - void addRef() - { - ++refCount; +void QmlImageReader::loadImage(Job &job) +{ + QImageReader imgio(job.reply->device()); + if (imgio.read(&job.img)) { + job.error = false; + } else { + job.error = true; + qWarning() << imgio.errorString(); } - void release() - { - Q_ASSERT(refCount > 0); - --refCount; - if (refCount == 0) { - QString key = reply->url().toString(); - qfxActiveNetworkReplies.remove(key); - delete this; - } +} + +void QmlImageReader::run() +{ + while (1) { + mutex.lock(); + if (!jobs.count()) + haveJob.wait(&mutex); + if (quit) + break; + runningJob = jobs.takeFirst(); + runningJob.reply->addRef(); + mutex.unlock(); + loadImage(runningJob); + mutex.lock(); + QCoreApplication::postEvent(runningJob.reply, new QmlImageDecodeEvent(runningJob.error, runningJob.img)); + runningJob.reply = 0; + mutex.unlock(); } -}; +} static bool readImage(QIODevice *dev, QPixmap *pixmap) - { - QImageReader imgio(dev); +{ + QImageReader imgio(dev); //#define QT_TEST_SCALED_SIZE #ifdef QT_TEST_SCALED_SIZE - /* - Some mechanism is needed for loading images at a limited size, especially - for remote images. Loading only thumbnails of remote progressive JPEG - images can be efficient. (Qt jpeg handler does not do so currently) - */ - - QSize limit(60,60); - QSize sz = imgio.size(); - if (sz.width() > limit.width() || sz.height() > limit.height()) { - sz.scale(limit,Qt::KeepAspectRatio); - imgio.setScaledSize(sz); - } + /* + Some mechanism is needed for loading images at a limited size, especially + for remote images. Loading only thumbnails of remote progressive JPEG + images can be efficient. (Qt jpeg handler does not do so currently) + */ + + QSize limit(60,60); + QSize sz = imgio.size(); + if (sz.width() > limit.width() || sz.height() > limit.height()) { + sz.scale(limit,Qt::KeepAspectRatio); + imgio.setScaledSize(sz); + } #endif - QImage img; - if (imgio.read(&img)) { + QImage img; + if (imgio.read(&img)) { #ifdef QT_TEST_SCALED_SIZE - if (!sz.isValid()) - img = img.scaled(limit,Qt::KeepAspectRatio); + if (!sz.isValid()) + img = img.scaled(limit,Qt::KeepAspectRatio); #endif - *pixmap = QPixmap::fromImage(img); - return true; - } else { - qWarning() << imgio.errorString(); - return false; - } + *pixmap = QPixmap::fromImage(img); + return true; + } else { + qWarning() << imgio.errorString(); + return false; } +} /*! \internal @@ -136,155 +219,208 @@ static QString toLocalFileOrQrc(const QUrl& url) return r; } -/*! - Finds the cached pixmap corresponding to \a url. - A previous call to get() must have requested the URL, - and the QNetworkReply must have finished before calling - this function. +typedef QHash QmlPixmapReplyHash; +static QmlPixmapReplyHash qmlActivePixmapReplies; - Returns true if the image was loaded without error. -*/ -bool QmlPixmapCache::find(const QUrl& url, QPixmap *pixmap) +class QmlPixmapReplyPrivate : public QObjectPrivate { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QmlPerfTimer perf; -#endif + Q_DECLARE_PUBLIC(QmlPixmapReply) - bool ok = true; -#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML - QString lf = toLocalFileOrQrc(url); - if (!lf.isEmpty()) { - if (!QPixmapCache::find(lf,pixmap)) { - QFile f(lf); - if (f.open(QIODevice::ReadOnly)) { - if (!readImage(&f, pixmap)) { - qWarning() << "Format error loading" << url; - *pixmap = QPixmap(); - ok = false; - } else { - QPixmapCache::insert(lf, *pixmap); - ok = !pixmap->isNull(); - } - } else { - *pixmap = QPixmap(); - ok = false; - } - } else { - ok = !pixmap->isNull(); - } - return ok; +public: + QmlPixmapReplyPrivate(const QString &url, QNetworkReply *r) + : QObjectPrivate(), refCount(1), urlKey(url), reply(r), status(QmlPixmapReply::Loading) { } -#endif - QString key = url.toString(); - if (!QPixmapCache::find(key,pixmap)) { - QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key); - if (iter == qfxActiveNetworkReplies.end()) { - // API usage error - qWarning() << "QmlPixmapCache: URL not loaded" << url; - ok = false; - } else { - if ((*iter)->reply->error()) { - qWarning() << "Network error loading" << url << (*iter)->reply->errorString(); - *pixmap = QPixmap(); - ok = false; - } else if (!readImage((*iter)->reply, pixmap)) { - qWarning() << "Format error loading" << url; - *pixmap = QPixmap(); - ok = false; + int refCount; + QString urlKey; + QNetworkReply *reply; + QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard + QImage image; + QmlPixmapReply::Status status; +}; + + +QmlPixmapReply::QmlPixmapReply(const QString &key, QNetworkReply *reply) + : QObject(*new QmlPixmapReplyPrivate(key, reply), 0) +{ + Q_D(QmlPixmapReply); + connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64))); + connect(d->reply, SIGNAL(finished()), this, SLOT(networkRequestDone())); +} + +QmlPixmapReply::~QmlPixmapReply() +{ + Q_D(QmlPixmapReply); + if (d->status == Decoding) + qmlImageReader()->cancel(this); + delete d->reply; +} + +void QmlPixmapReply::networkRequestDone() +{ + Q_D(QmlPixmapReply); + if (d->reply->error()) { + d->pixmap = QPixmap(); + d->status = Error; + emit finished(); + } else { + qmlImageReader()->read(this); + d->status = Decoding; + } +} + +bool QmlPixmapReply::event(QEvent *event) +{ + Q_D(QmlPixmapReply); + if (event->type() == QEvent::User) { + if (!release(true)) { + QmlImageDecodeEvent *de = static_cast(event); + d->status = de->error ? Error : Ready; + if (d->status == Ready) { + d->pixmap = QPixmap::fromImage(de->image); + QPixmapCache::insert(d->urlKey, d->pixmap); + d->image = QImage(); } else { - if ((*iter)->refCount > 1) - (*iter)->pixmap = *pixmap; + qWarning() << "Error decoding" << d->urlKey; } - (*iter)->release(); + emit finished(); } - QPixmapCache::insert(key, *pixmap); - } else { - ok = !pixmap->isNull(); - - // We may be the second finder. Still need to check for active replies. - QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key); - if (iter != qfxActiveNetworkReplies.end()) - (*iter)->release(); + return true; } - return ok; + + return QObject::event(event); } -/*! - Starts a network request to load \a url. +QmlPixmapReply::Status QmlPixmapReply::status() const +{ + Q_D(const QmlPixmapReply); + return d->status; +} + +QIODevice *QmlPixmapReply::device() +{ + Q_D(QmlPixmapReply); + return d->reply; +} + +void QmlPixmapReply::addRef() +{ + Q_D(QmlPixmapReply); + ++d->refCount; +} + +bool QmlPixmapReply::release(bool defer) +{ + Q_D(QmlPixmapReply); + Q_ASSERT(d->refCount > 0); + --d->refCount; + if (d->refCount == 0) { + qmlActivePixmapReplies.remove(d->urlKey); + if (defer) + deleteLater(); + else + delete this; + return true; + } - Returns a QNetworkReply if the image is not immediately available, otherwise - returns 0. Caller should connect to QNetworkReply::finished() to then call - find() when the image is available. + return false; +} - The returned QNetworkReply will be deleted when all get() calls are - matched by a corresponding find() call. +/*! + Finds the cached pixmap corresponding to \a url. + If the image is a network resource and has not yet + been retrieved and cached, request() must be called. - If the \a ok parameter is passed and \a url is a local file, - its value will be set to false if the pixmap could not be loaded; - otherwise the pixmap was loaded and *ok will be true. + Returns Ready, or Error if the image has been retrieved, + otherwise the current retrieval status. */ -QNetworkReply *QmlPixmapCache::get(QmlEngine *engine, const QUrl& url, QPixmap *pixmap, bool *ok) +QmlPixmapReply::Status QmlPixmapCache::get(const QUrl& url, QPixmap *pixmap) { + QmlPixmapReply::Status status = QmlPixmapReply::Unrequested; + #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML QString lf = toLocalFileOrQrc(url); if (!lf.isEmpty()) { + status = QmlPixmapReply::Ready; if (!QPixmapCache::find(lf,pixmap)) { - bool loaded = true; QFile f(lf); if (f.open(QIODevice::ReadOnly)) { if (!readImage(&f, pixmap)) { qWarning() << "Format error loading" << url; *pixmap = QPixmap(); - loaded = false; + status = QmlPixmapReply::Error; } } else { qWarning() << "Cannot open" << url; *pixmap = QPixmap(); - loaded = false; + status = QmlPixmapReply::Error; } - if (loaded) + if (status == QmlPixmapReply::Ready) QPixmapCache::insert(lf, *pixmap); - if (ok) *ok = loaded; } - return 0; + return status; } #endif QString key = url.toString(); - if (QPixmapCache::find(key,pixmap)) { - return 0; + QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key); + if (QPixmapCache::find(key, pixmap)) { + if (iter != qmlActivePixmapReplies.end()) { + status = (*iter)->status(); + (*iter)->release(); + } else { + status = pixmap->isNull() ? QmlPixmapReply::Error : QmlPixmapReply::Ready; + } + } else if (iter != qmlActivePixmapReplies.end()) { + status = QmlPixmapReply::Loading; } - QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key); - if (iter == qfxActiveNetworkReplies.end()) { + return status; +} + +/*! + Starts a network request to load \a url. + + Returns a QmlPixmapReply. Caller should connect to QmlPixmapReply::finished() + and call get() when the image is available. + + The returned QmlPixmapReply will be deleted when all request() calls are + matched by a corresponding get() call. +*/ +QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url) +{ + QString key = url.toString(); + QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key); + if (iter == qmlActivePixmapReplies.end()) { QNetworkRequest req(url); - QSharedNetworkReply *item = new QSharedNetworkReply(engine->networkAccessManager()->get(req)); - iter = qfxActiveNetworkReplies.insert(key, item); + QmlPixmapReply *item = new QmlPixmapReply(key, engine->networkAccessManager()->get(req)); + iter = qmlActivePixmapReplies.insert(key, item); } else { (*iter)->addRef(); } - return (*iter)->reply; + return (*iter); } /*! - Cancels a previous call to get(). + Cancels a previous call to request(). May also cancel loading (eg. if no other pending request). - Any connections from the QNetworkReply returned by get() to \a obj will be + Any connections from the QmlPixmapReply returned by request() to \a obj will be disconnected. */ -void QmlPixmapCache::cancelGet(const QUrl& url, QObject* obj) +void QmlPixmapCache::cancel(const QUrl& url, QObject *obj) { QString key = url.toString(); - QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key); - if (iter == qfxActiveNetworkReplies.end()) + QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key); + if (iter == qmlActivePixmapReplies.end()) return; + + QmlPixmapReply *reply = *iter; if (obj) - QObject::disconnect((*iter)->reply, 0, obj, 0); - (*iter)->release(); + QObject::disconnect(reply, 0, obj, 0); + reply->release(); } /*! @@ -293,7 +429,9 @@ void QmlPixmapCache::cancelGet(const QUrl& url, QObject* obj) */ int QmlPixmapCache::pendingRequests() { - return qfxActiveNetworkReplies.count(); + return qmlActivePixmapReplies.count(); } +#include + QT_END_NAMESPACE diff --git a/src/declarative/util/qmlpixmapcache_p.h b/src/declarative/util/qmlpixmapcache_p.h index d2e272c..b71873f 100644 --- a/src/declarative/util/qmlpixmapcache_p.h +++ b/src/declarative/util/qmlpixmapcache_p.h @@ -53,17 +53,51 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QmlEngine; class QNetworkReply; -class Q_DECLARATIVE_EXPORT QmlPixmapCache + +class QmlPixmapReplyPrivate; +class QmlPixmapReply : public QObject { + Q_OBJECT public: - static QNetworkReply *get(QmlEngine *, const QUrl& url, QPixmap *pixmap, bool *ok=0); - static void cancelGet(const QUrl& url, QObject* obj); + QmlPixmapReply(const QString &key, QNetworkReply *reply); + ~QmlPixmapReply(); + + enum Status { Ready, Error, Unrequested, Loading, Decoding }; + Status status() const; + +Q_SIGNALS: + void finished(); + void downloadProgress(qint64, qint64); - static bool find(const QUrl& url, QPixmap *pixmap); // url must have been passed to QmlPixmapCache::get, and any returned reply finished. +protected: + bool event(QEvent *event); - static int pendingRequests(); // mainly for test verification +private: + QIODevice *device(); + void addRef(); + bool release(bool defer=false); + +private Q_SLOTS: + void networkRequestDone(); + +private: + Q_DISABLE_COPY(QmlPixmapReply) + Q_DECLARE_PRIVATE(QmlPixmapReply) + friend class QmlImageReader; + friend class QmlPixmapCache; }; +class QmlPixmapCache +{ +public: + static QmlPixmapReply::Status get(const QUrl& url, QPixmap *pixmap); + static QmlPixmapReply *request(QmlEngine *, const QUrl& url); + static void cancel(const QUrl& url, QObject *obj); + static int pendingRequests(); +}; + + + QT_END_NAMESPACE QT_END_HEADER -- cgit v0.12