From 09f07b98dfdaec2e48749768b967a48e588d3f7f Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Mon, 5 Jul 2010 14:20:14 +1000 Subject: Make declarative pixmap cache easier to use The QDeclarativePixmapCache was both slow, and very trickey to use correctly. Many QML elements did not correctly cancel outstanding requests, which leads to pixmaps leaking indefinately. Other elements, such as Text, were subject to race conditions that meant they may never actually load all their images. QDeclarativePixmap is a single class than encapsulates the action of fetching a pixmap, as well as the pixmap itself and the responsibility of canceling outstanding requests. Rather than relying on Qt's pixmap cache that doesn't cache all the information QML needs, QDeclarativePixmap implements its own cache, that correctly degrades over time (unlike QPixmapCache that can stop expiring items in some conditions). Reviewed-by: Warwick Allison --- .../graphicsitems/qdeclarativeborderimage.cpp | 100 +- .../graphicsitems/qdeclarativeborderimage_p_p.h | 2 - .../graphicsitems/qdeclarativeimage.cpp | 4 +- .../graphicsitems/qdeclarativeimagebase.cpp | 65 +- .../graphicsitems/qdeclarativeimagebase_p_p.h | 5 +- src/declarative/graphicsitems/qdeclarativetext.cpp | 148 +-- src/declarative/graphicsitems/qdeclarativetext_p.h | 3 - .../graphicsitems/qdeclarativetext_p_p.h | 4 + src/declarative/util/qdeclarativepixmapcache.cpp | 1115 ++++++++++++-------- src/declarative/util/qdeclarativepixmapcache_p.h | 85 +- src/imports/particles/qdeclarativeparticles.cpp | 33 +- .../tst_qdeclarativepixmapcache.cpp | 99 +- 12 files changed, 930 insertions(+), 733 deletions(-) diff --git a/src/declarative/graphicsitems/qdeclarativeborderimage.cpp b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp index d4ca9eb..44c206b 100644 --- a/src/declarative/graphicsitems/qdeclarativeborderimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp @@ -94,8 +94,6 @@ QDeclarativeBorderImage::~QDeclarativeBorderImage() Q_D(QDeclarativeBorderImage); if (d->sciReply) d->sciReply->deleteLater(); - if (d->sciPendingPixmapCache) - QDeclarativePixmapCache::cancel(d->sciurl, this); } /*! \qmlproperty enumeration BorderImage::status @@ -164,15 +162,6 @@ void QDeclarativeBorderImage::setSource(const QUrl &url) d->sciReply = 0; } - if (d->pendingPixmapCache) { - QDeclarativePixmapCache::cancel(d->url, this); - d->pendingPixmapCache = false; - } - if (d->sciPendingPixmapCache) { - QDeclarativePixmapCache::cancel(d->sciurl, this); - d->sciPendingPixmapCache = false; - } - d->url = url; d->sciurl = QUrl(); emit sourceChanged(d->url); @@ -190,7 +179,7 @@ void QDeclarativeBorderImage::load() } if (d->url.isEmpty()) { - d->pix = QPixmap(); + d->pix.clear(); d->status = Null; setImplicitWidth(0); setImplicitHeight(0); @@ -224,26 +213,24 @@ void QDeclarativeBorderImage::load() thisSciRequestFinished, Qt::DirectConnection); } } else { - QSize impsize; - QString errorString; - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->pix, &errorString, &impsize, d->async); - if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) { - QDeclarativePixmapReply *reply = QDeclarativePixmapCache::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))); + + d->pix.load(qmlEngine(this), d->url, d->async); + + if (d->pix.isLoading()) { + d->pix.connectFinished(this, SLOT(requestFinished())); + d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64))); } else { - //### should be unified with requestFinished + QSize impsize = d->pix.implicitSize(); setImplicitWidth(impsize.width()); setImplicitHeight(impsize.height()); - if (d->pix.isNull()) { + if (d->pix.isReady()) { + d->status = Ready; + } else { d->status = Error; - qmlInfo(this) << errorString; + qmlInfo(this) << d->pix.error(); } - if (d->status == Loading) - d->status = Ready; + d->progress = 1.0; emit statusChanged(d->status); emit progressChanged(d->progress); @@ -343,47 +330,40 @@ void QDeclarativeBorderImage::setGridScaledImage(const QDeclarativeGridScaledIma d->verticalTileMode = sci.verticalTileRule(); d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl())); - QSize impsize; - QString errorString; - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->sciurl, &d->pix, &errorString, &impsize, d->async); - if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) { - QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->sciurl); - d->sciPendingPixmapCache = true; - - static int replyDownloadProgress = -1; - static int replyFinished = -1; + + d->pix.load(qmlEngine(this), d->sciurl, d->async); + + if (d->pix.isLoading()) { static int thisRequestProgress = -1; static int thisRequestFinished = -1; - if (replyDownloadProgress == -1) { - replyDownloadProgress = - QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - replyFinished = - QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); + if (thisRequestProgress == -1) { thisRequestProgress = QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); thisRequestFinished = QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestFinished()"); } - QMetaObject::connect(reply, replyFinished, this, - thisRequestFinished, Qt::DirectConnection); - QMetaObject::connect(reply, replyDownloadProgress, this, - thisRequestProgress, Qt::DirectConnection); + d->pix.connectFinished(this, thisRequestFinished); + d->pix.connectDownloadProgress(this, thisRequestProgress); + } else { - //### should be unified with requestFinished + + QSize impsize = d->pix.implicitSize(); setImplicitWidth(impsize.width()); setImplicitHeight(impsize.height()); - if (d->pix.isNull()) { + if (d->pix.isReady()) { + d->status = Ready; + } else { d->status = Error; - qmlInfo(this) << errorString; + qmlInfo(this) << d->pix.error(); } - if (d->status == Loading) - d->status = Ready; + d->progress = 1.0; emit statusChanged(d->status); emit progressChanged(1.0); update(); + } } } @@ -392,27 +372,17 @@ void QDeclarativeBorderImage::requestFinished() { Q_D(QDeclarativeBorderImage); - QSize impsize; - if (d->url.path().endsWith(QLatin1String(".sci"))) { - d->sciPendingPixmapCache = false; - QString errorString; - if (QDeclarativePixmapCache::get(d->sciurl, &d->pix, &errorString, &impsize, d->async) != QDeclarativePixmapReply::Ready) { - d->status = Error; - qmlInfo(this) << errorString; - } + QSize impsize = d->pix.implicitSize(); + if (d->pix.isError()) { + d->status = Error; + qmlInfo(this) << d->pix.error(); } else { - d->pendingPixmapCache = false; - QString errorString; - if (QDeclarativePixmapCache::get(d->url, &d->pix, &errorString, &impsize, d->async) != QDeclarativePixmapReply::Ready) { - d->status = Error; - qmlInfo(this) << errorString; - } + d->status = Ready; } + setImplicitWidth(impsize.width()); setImplicitHeight(impsize.height()); - if (d->status == Loading) - d->status = Ready; d->progress = 1.0; emit statusChanged(d->status); emit progressChanged(1.0); diff --git a/src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h b/src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h index 01e4a00..65583d6 100644 --- a/src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeborderimage_p_p.h @@ -66,7 +66,6 @@ class QDeclarativeBorderImagePrivate : public QDeclarativeImageBasePrivate public: QDeclarativeBorderImagePrivate() : border(0), sciReply(0), - sciPendingPixmapCache(false), horizontalTileMode(QDeclarativeBorderImage::Stretch), verticalTileMode(QDeclarativeBorderImage::Stretch), redirectCount(0) @@ -97,7 +96,6 @@ public: QDeclarativeScaleGrid *border; QUrl sciurl; QNetworkReply *sciReply; - bool sciPendingPixmapCache; QDeclarativeBorderImage::TileMode horizontalTileMode; QDeclarativeBorderImage::TileMode verticalTileMode; int redirectCount; diff --git a/src/declarative/graphicsitems/qdeclarativeimage.cpp b/src/declarative/graphicsitems/qdeclarativeimage.cpp index ff61302..e0db580 100644 --- a/src/declarative/graphicsitems/qdeclarativeimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeimage.cpp @@ -126,7 +126,7 @@ QDeclarativeImage::~QDeclarativeImage() QPixmap QDeclarativeImage::pixmap() const { Q_D(const QDeclarativeImage); - return d->pix; + return d->pix.pixmap(); } void QDeclarativeImage::setPixmap(const QPixmap &pix) @@ -140,7 +140,7 @@ void QDeclarativeImage::setPixmap(const QPixmap &pix) void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap) { Q_Q(QDeclarativeImage); - pix = pixmap; + pix.setPixmap(pixmap); q->setImplicitWidth(pix.width()); q->setImplicitHeight(pix.height()); diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase.cpp b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp index c3f8195..67f2327 100644 --- a/src/declarative/graphicsitems/qdeclarativeimagebase.cpp +++ b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp @@ -57,9 +57,6 @@ QDeclarativeImageBase::QDeclarativeImageBase(QDeclarativeImageBasePrivate &dd, Q QDeclarativeImageBase::~QDeclarativeImageBase() { - Q_D(QDeclarativeImageBase); - if (d->pendingPixmapCache) - QDeclarativePixmapCache::cancel(d->url, this); } QDeclarativeImageBase::Status QDeclarativeImageBase::status() const @@ -91,7 +88,6 @@ void QDeclarativeImageBase::setAsynchronous(bool async) } } - QUrl QDeclarativeImageBase::source() const { Q_D(const QDeclarativeImageBase); @@ -105,11 +101,6 @@ void QDeclarativeImageBase::setSource(const QUrl &url) if ((d->url.isEmpty() == url.isEmpty()) && url == d->url) return; - if (d->pendingPixmapCache) { - QDeclarativePixmapCache::cancel(d->url, this); - d->pendingPixmapCache = false; - } - d->url = url; emit sourceChanged(d->url); @@ -122,6 +113,7 @@ void QDeclarativeImageBase::setSourceSize(const QSize& size) Q_D(QDeclarativeImageBase); if (d->sourcesize == size) return; + d->sourcesize = size; emit sourceSizeChanged(); if (isComponentComplete()) @@ -143,7 +135,7 @@ void QDeclarativeImageBase::load() } if (d->url.isEmpty()) { - d->pix = QPixmap(); + d->pix.clear(); d->status = Null; setImplicitWidth(0); setImplicitHeight(0); @@ -152,48 +144,37 @@ void QDeclarativeImageBase::load() update(); } else { d->status = Loading; - int reqwidth = d->sourcesize.width(); - int reqheight = d->sourcesize.height(); - QSize impsize; - QString errorString; - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->pix, &errorString, &impsize, d->async, reqwidth, reqheight); - if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) { - QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->url, reqwidth, reqheight); - d->pendingPixmapCache = true; - - static int replyDownloadProgress = -1; - static int replyFinished = -1; + + d->pix.load(qmlEngine(this), d->url, d->sourcesize, d->async); + + if (d->pix.isLoading()) { + static int thisRequestProgress = -1; static int thisRequestFinished = -1; - if (replyDownloadProgress == -1) { - replyDownloadProgress = - QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - replyFinished = - QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); + if (thisRequestProgress == -1) { thisRequestProgress = QDeclarativeImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); thisRequestFinished = QDeclarativeImageBase::staticMetaObject.indexOfSlot("requestFinished()"); } - QMetaObject::connect(reply, replyFinished, this, - thisRequestFinished, Qt::DirectConnection); - QMetaObject::connect(reply, replyDownloadProgress, this, - thisRequestProgress, Qt::DirectConnection); + d->pix.connectFinished(this, thisRequestFinished); + d->pix.connectDownloadProgress(this, thisRequestProgress); + } else { - //### should be unified with requestFinished - if (status == QDeclarativePixmapReply::Ready) { - setImplicitWidth(impsize.width()); - setImplicitHeight(impsize.height()); + QSize impsize = d->pix.implicitSize(); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); - if (d->status == Loading) - d->status = Ready; + if (d->pix.isReady()) { + d->status = Ready; if (!d->sourcesize.isValid()) emit sourceSizeChanged(); + } else { d->status = Error; - qmlInfo(this) << errorString; + qmlInfo(this) << d->pix.error(); } d->progress = 1.0; emit statusChanged(d->status); @@ -201,6 +182,7 @@ void QDeclarativeImageBase::load() pixmapChange(); update(); } + } emit statusChanged(d->status); @@ -210,14 +192,13 @@ void QDeclarativeImageBase::requestFinished() { Q_D(QDeclarativeImageBase); - d->pendingPixmapCache = false; + QSize impsize = d->pix.implicitSize(); - QSize impsize; - QString errorString; - if (QDeclarativePixmapCache::get(d->url, &d->pix, &errorString, &impsize, d->async, d->sourcesize.width(), d->sourcesize.height()) != QDeclarativePixmapReply::Ready) { + if (d->pix.isError()) { d->status = Error; - qmlInfo(this) << errorString; + qmlInfo(this) << d->pix.error(); } + setImplicitWidth(impsize.width()); setImplicitHeight(impsize.height()); diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h b/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h index 392c1db..aee8b28 100644 --- a/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h @@ -54,6 +54,7 @@ // #include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativepixmapcache_p.h" #include @@ -68,18 +69,16 @@ public: QDeclarativeImageBasePrivate() : status(QDeclarativeImageBase::Null), progress(0.0), - pendingPixmapCache(false), async(false) { QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; } - QPixmap pix; + QDeclarativePixmap pix; QDeclarativeImageBase::Status status; QUrl url; qreal progress; QSize sourcesize; - bool pendingPixmapCache : 1; bool async : 1; }; diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp index 0bd9a53..a7e2ed0 100644 --- a/src/declarative/graphicsitems/qdeclarativetext.cpp +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -61,52 +61,100 @@ class QTextDocumentWithImageResources : public QTextDocument { Q_OBJECT public: - QTextDocumentWithImageResources(QDeclarativeText *parent) : - QTextDocument(parent), - outstanding(0) - { - } + QTextDocumentWithImageResources(QDeclarativeText *parent); + virtual ~QTextDocumentWithImageResources(); + void setText(const QString &); int resourcesLoading() const { return outstanding; } protected: - QVariant loadResource(int type, const QUrl &name) - { - QUrl url = qmlContext(parent())->resolvedUrl(name); - - if (type == QTextDocument::ImageResource) { - QPixmap pm; - QString errorString; - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(url, &pm, &errorString, 0, false, 0, 0); - if (status == QDeclarativePixmapReply::Ready) - return pm; - if (status == QDeclarativePixmapReply::Error) { - if (!errors.contains(url)) { - errors.insert(url); - qmlInfo(parent()) << errorString; - } - } else { - QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(parent()), url); - connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); + QVariant loadResource(int type, const QUrl &name); + +private slots: + void requestFinished(); + +private: + QHash m_resources; + + int outstanding; + static QSet errors; +}; + +QTextDocumentWithImageResources::QTextDocumentWithImageResources(QDeclarativeText *parent) +: QTextDocument(parent), outstanding(0) +{ +} + +QTextDocumentWithImageResources::~QTextDocumentWithImageResources() +{ + if (!m_resources.isEmpty()) + qDeleteAll(m_resources); +} + +QVariant QTextDocumentWithImageResources::loadResource(int type, const QUrl &name) +{ + QDeclarativeContext *context = qmlContext(parent()); + QUrl url = context->resolvedUrl(name); + + if (type == QTextDocument::ImageResource) { + QHash::Iterator iter = m_resources.find(url); + + if (iter == m_resources.end()) { + QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url); + iter = m_resources.insert(name, p); + + if (p->isLoading()) { + p->connectFinished(this, SLOT(requestFinished())); outstanding++; } } - return QTextDocument::loadResource(type,url); // The *resolved* URL + QDeclarativePixmap *p = *iter; + if (p->isReady()) { + return p->pixmap(); + } else if (p->isError()) { + if (!errors.contains(url)) { + errors.insert(url); + qmlInfo(parent()) << p->error(); + } + } } -private slots: - void requestFinished() - { - outstanding--; - if (outstanding == 0) - static_cast(parent())->reloadWithResources(); + return QTextDocument::loadResource(type,url); // The *resolved* URL +} + +void QTextDocumentWithImageResources::requestFinished() +{ + outstanding--; + if (outstanding == 0) { + QDeclarativeText *textItem = static_cast(parent()); + QString text = textItem->text(); +#ifndef QT_NO_TEXTHTMLPARSER + setHtml(text); +#else + setPlainText(text); +#endif + QDeclarativeTextPrivate *d = QDeclarativeTextPrivate::get(textItem); + d->updateLayout(); + d->markImgDirty(); } +} -private: - int outstanding; - static QSet errors; -}; +void QTextDocumentWithImageResources::setText(const QString &text) +{ + if (!m_resources.isEmpty()) { + qWarning("CLEAR"); + qDeleteAll(m_resources); + m_resources.clear(); + outstanding = 0; + } + +#ifndef QT_NO_TEXTHTMLPARSER + setHtml(text); +#else + setPlainText(text); +#endif +} QSet QTextDocumentWithImageResources::errors; @@ -314,11 +362,7 @@ void QDeclarativeText::setText(const QString &n) if (d->richText) { if (isComponentComplete()) { d->ensureDoc(); -#ifndef QT_NO_TEXTHTMLPARSER - d->doc->setHtml(n); -#else - d->doc->setPlainText(n); -#endif + d->doc->setText(n); } } @@ -607,11 +651,7 @@ void QDeclarativeText::setTextFormat(TextFormat format) } else if (!wasRich && d->richText) { if (isComponentComplete()) { d->ensureDoc(); -#ifndef QT_NO_TEXTHTMLPARSER - d->doc->setHtml(d->text); -#else - d->doc->setPlainText(d->text); -#endif + d->doc->setText(d->text); } d->updateLayout(); d->markImgDirty(); @@ -1074,20 +1114,6 @@ void QDeclarativeTextPrivate::ensureDoc() } } -void QDeclarativeText::reloadWithResources() -{ - Q_D(QDeclarativeText); - if (!d->richText) - return; -#ifndef QT_NO_TEXTHTMLPARSER - d->doc->setHtml(d->text); -#else - d->doc->setPlainText(d->text); -#endif - d->updateLayout(); - d->markImgDirty(); -} - /*! Returns the number of resources (images) that are being loaded asynchronously. */ @@ -1173,11 +1199,7 @@ void QDeclarativeText::componentComplete() if (d->dirty) { if (d->richText) { d->ensureDoc(); -#ifndef QT_NO_TEXTHTMLPARSER - d->doc->setHtml(d->text); -#else - d->doc->setPlainText(d->text); -#endif + d->doc->setText(d->text); } d->updateLayout(); d->dirty = false; diff --git a/src/declarative/graphicsitems/qdeclarativetext_p.h b/src/declarative/graphicsitems/qdeclarativetext_p.h index cd97df3..2cc4d52 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p.h @@ -168,9 +168,6 @@ protected: private: Q_DISABLE_COPY(QDeclarativeText) Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeText) - - friend class QTextDocumentWithImageResources; - void reloadWithResources(); }; QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativetext_p_p.h b/src/declarative/graphicsitems/qdeclarativetext_p_p.h index 332f99e..51a5514 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p_p.h @@ -124,6 +124,10 @@ public: QSize cachedLayoutSize; QDeclarativeText::TextFormat format; QDeclarativeText::WrapMode wrapMode; + + static inline QDeclarativeTextPrivate *get(QDeclarativeText *t) { + return t->d_func(); + } }; QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index 0c2f23d..fdc2455 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -55,104 +55,209 @@ #include #include #include +#include +#include #include #include #include #include #include -// Maximum number of simultaneous image requests to send. -static const int maxImageRequestCount = 8; +#define IMAGEREQUEST_MAX_REQUEST_COUNT 8 +#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16 +#define CACHE_EXPIRE_TIME 30 +#define CACHE_REMOVAL_FRACTION 4 QT_BEGIN_NAMESPACE -class QDeclarativeImageReaderEvent : public QEvent +class QDeclarativePixmapReader; +class QDeclarativePixmapData; +class QDeclarativePixmapReply : public QObject { + Q_OBJECT public: enum ReadError { NoError, Loading, Decoding }; - QDeclarativeImageReaderEvent(QDeclarativeImageReaderEvent::ReadError err, const QString &errStr, const QImage &img) - : QEvent(QEvent::User), error(err), errorString(errStr), image(img) {} + QDeclarativePixmapReply(QDeclarativePixmapData *); + ~QDeclarativePixmapReply(); - ReadError error; - QString errorString; - QImage image; + QDeclarativePixmapData *data; + QDeclarativePixmapReader *reader; + + bool loading; + int redirectCount; + + class Event : public QEvent { + public: + Event(ReadError, const QString &, const QSize &, const QImage &); + + ReadError error; + QString errorString; + QSize implicitSize; + QImage image; + }; + void postReply(ReadError, const QString &, const QSize &, const QImage &); + + +Q_SIGNALS: + void finished(); + void downloadProgress(qint64, qint64); + +protected: + bool event(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarativePixmapReply) + +public: + static int finishedIndex; + static int downloadProgressIndex; }; -class QDeclarativeImageRequestHandler; -class QDeclarativeImageReader : public QThread +class QDeclarativePixmapData; +class QDeclarativePixmapReader : public QThread { Q_OBJECT public: - QDeclarativeImageReader(QDeclarativeEngine *eng); - ~QDeclarativeImageReader(); + QDeclarativePixmapReader(QDeclarativeEngine *eng); + ~QDeclarativePixmapReader(); - QDeclarativePixmapReply *getImage(const QUrl &url, int req_width, int req_height); + QDeclarativePixmapReply *getImage(QDeclarativePixmapData *); void cancel(QDeclarativePixmapReply *rep); - static QDeclarativeImageReader *instance(QDeclarativeEngine *engine); + static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine); protected: void run(); +private slots: + void networkRequestDone(); + private: + void processJobs(); + void processJob(QDeclarativePixmapReply *); + QList jobs; QList cancelled; QDeclarativeEngine *engine; - QDeclarativeImageRequestHandler *handler; QObject *eventLoopQuitHack; + QMutex mutex; + class ThreadObject : public QObject { + public: + ThreadObject(QDeclarativePixmapReader *); + void processJobs(); + virtual bool event(QEvent *e); + private: + QDeclarativePixmapReader *reader; + } *threadObject; + QWaitCondition waitCondition; + + QNetworkAccessManager *networkAccessManager(); + QNetworkAccessManager *accessManager; - static QHash readers; + QHash replies; + + static int replyDownloadProgress; + static int replyFinished; + static int downloadProgress; + static int thisNetworkRequestDone; + static QHash readers; static QMutex readerMutex; - friend class QDeclarativeImageRequestHandler; }; -QHash QDeclarativeImageReader::readers; -QMutex QDeclarativeImageReader::readerMutex; - - -class QDeclarativeImageRequestHandler : public QObject +class QDeclarativePixmapData { - Q_OBJECT public: - QDeclarativeImageRequestHandler(QDeclarativeImageReader *read, QDeclarativeEngine *eng) - : QObject(), accessManager(0), engine(eng), reader(read), redirectCount(0) + QDeclarativePixmapData(const QUrl &u, const QSize &s, const QString &e) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error), + url(u), errorString(e), requestSize(s), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) { - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); } - QDeclarativePixmapReply *getImage(const QUrl &url, int req_width, int req_height); - void cancel(QDeclarativePixmapReply *reply); - -protected: - bool event(QEvent *event); + QDeclarativePixmapData(const QUrl &u, const QSize &r) + : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading), + url(u), requestSize(r), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), + nextUnreferenced(0) + { + } -private slots: - void networkRequestDone(); + QDeclarativePixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r) + : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready), + url(u), pixmap(p), implicitSize(s), requestSize(r), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } -private: - QNetworkAccessManager *networkAccessManager() { - if (!accessManager) - accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(this); - return accessManager; + QDeclarativePixmapData(const QPixmap &p) + : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready), + pixmap(p), implicitSize(p.size()), requestSize(p.size()), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { } - QHash replies; - QNetworkAccessManager *accessManager; - QDeclarativeEngine *engine; - QDeclarativeImageReader *reader; - int redirectCount; + int cost() const; + void addref(); + void release(); + void addToCache(); + void removeFromCache(); - static int replyDownloadProgress; - static int replyFinished; - static int downloadProgress; - static int thisNetworkRequestDone; + uint refCount; + + bool inCache:1; + bool privatePixmap:1; + + QDeclarativePixmap::Status pixmapStatus; + QUrl url; + QString errorString; + QPixmap pixmap; + QSize implicitSize; + QSize requestSize; + + QDeclarativePixmapReply *reply; + + QDeclarativePixmapData *prevUnreferenced; + QDeclarativePixmapData**prevUnreferencedPtr; + QDeclarativePixmapData *nextUnreferenced; }; -//=========================================================================== +int QDeclarativePixmapReply::finishedIndex = -1; +int QDeclarativePixmapReply::downloadProgressIndex = -1; + +// XXX +QHash QDeclarativePixmapReader::readers; +QMutex QDeclarativePixmapReader::readerMutex; -static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int req_width, int req_height) +int QDeclarativePixmapReader::replyDownloadProgress = -1; +int QDeclarativePixmapReader::replyFinished = -1; +int QDeclarativePixmapReader::downloadProgress = -1; +int QDeclarativePixmapReader::thisNetworkRequestDone = -1; + + +void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString, + const QSize &implicitSize, const QImage &image) +{ + loading = false; + QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, image)); +} + +QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, const QImage &i) +: QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i) +{ +} + +QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager() +{ + if (!accessManager) { + Q_ASSERT(threadObject); + accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject); + } + return accessManager; +} + +static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, + const QSize &requestSize) { QImageReader imgio(dev); @@ -163,17 +268,17 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e } bool scaled = false; - if (req_width > 0 || req_height > 0) { + if (requestSize.width() > 0 || requestSize.height() > 0) { QSize s = imgio.size(); - if (req_width && (force_scale || req_width < s.width())) { - if (req_height <= 0) - s.setHeight(s.height()*req_width/s.width()); - s.setWidth(req_width); scaled = true; + if (requestSize.width() && (force_scale || requestSize.width() < s.width())) { + if (requestSize.height() <= 0) + s.setHeight(s.height()*requestSize.width()/s.width()); + s.setWidth(requestSize.width()); scaled = true; } - if (req_height && (force_scale || req_height < s.height())) { - if (req_width <= 0) - s.setWidth(s.width()*req_height/s.height()); - s.setHeight(req_height); scaled = true; + if (requestSize.height() && (force_scale || requestSize.height() < s.height())) { + if (requestSize.width() <= 0) + s.setWidth(s.width()*requestSize.height()/s.height()); + s.setHeight(requestSize.height()); scaled = true; } if (scaled) { imgio.setScaledSize(s); } } @@ -187,130 +292,39 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e return true; } else { if (errorString) - *errorString = QDeclarativePixmapCache::tr("Error decoding: %1: %2").arg(url.toString()) + *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString()) .arg(imgio.errorString()); return false; } } - -//=========================================================================== - -int QDeclarativeImageRequestHandler::replyDownloadProgress = -1; -int QDeclarativeImageRequestHandler::replyFinished = -1; -int QDeclarativeImageRequestHandler::downloadProgress = -1; -int QDeclarativeImageRequestHandler::thisNetworkRequestDone = -1; - -typedef QHash QDeclarativePixmapSizeHash; -Q_GLOBAL_STATIC(QDeclarativePixmapSizeHash, qmlOriginalSizes); - -bool QDeclarativeImageRequestHandler::event(QEvent *event) +QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng) +: QThread(eng), engine(eng), threadObject(0), accessManager(0) { - if (event->type() == QEvent::User) { - if (replyDownloadProgress == -1) { - replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()"); - downloadProgress = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - thisNetworkRequestDone = QDeclarativeImageRequestHandler::staticMetaObject.indexOfSlot("networkRequestDone()"); - } - - while (1) { - reader->mutex.lock(); - - if (reader->cancelled.count()) { - for (int i = 0; i < reader->cancelled.count(); ++i) { - QDeclarativePixmapReply *job = reader->cancelled.at(i); - QNetworkReply *reply = replies.key(job, 0); - if (reply && reply->isRunning()) { - // cancel any jobs already started - replies.remove(reply); - reply->close(); - job->release(true); - } else { - // remove from pending job list - for (int j = 0; j < reader->jobs.count(); ++j) { - if (reader->jobs.at(j) == job) { - reader->jobs.removeAt(j); - job->release(true); - break; - } - } - } - } - reader->cancelled.clear(); - } - - if (!reader->jobs.count() || replies.count() > maxImageRequestCount) { - reader->mutex.unlock(); - break; - } - - QDeclarativePixmapReply *runningJob = reader->jobs.takeLast(); - QUrl url = runningJob->url(); - reader->mutex.unlock(); - - // fetch - if (url.scheme() == QLatin1String("image")) { - // Use QmlImageProvider - QSize read_impsize; - QImage image = QDeclarativeEnginePrivate::get(engine)->getImageFromProvider(url, &read_impsize, QSize(runningJob->forcedWidth(),runningJob->forcedHeight())); - qmlOriginalSizes()->insert(url, read_impsize); - QDeclarativeImageReaderEvent::ReadError errorCode = QDeclarativeImageReaderEvent::NoError; - QString errorStr; - if (image.isNull()) { - errorCode = QDeclarativeImageReaderEvent::Loading; - errorStr = QDeclarativePixmapCache::tr("Failed to get image from provider: %1").arg(url.toString()); - } - QCoreApplication::postEvent(runningJob, new QDeclarativeImageReaderEvent(errorCode, errorStr, image)); - } else { - QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); - if (!lf.isEmpty()) { - // Image is local - load/decode immediately - QImage image; - QDeclarativeImageReaderEvent::ReadError errorCode = QDeclarativeImageReaderEvent::NoError; - QString errorStr; - QFile f(lf); - if (f.open(QIODevice::ReadOnly)) { - QSize read_impsize; - if (readImage(url, &f, &image, &errorStr, &read_impsize, runningJob->forcedWidth(),runningJob->forcedHeight())) { - qmlOriginalSizes()->insert(url, read_impsize); - } else { - errorCode = QDeclarativeImageReaderEvent::Loading; - } - } else { - errorStr = QDeclarativePixmapCache::tr("Cannot open: %1").arg(url.toString()); - errorCode = QDeclarativeImageReaderEvent::Loading; - } - QCoreApplication::postEvent(runningJob, new QDeclarativeImageReaderEvent(errorCode, errorStr, image)); - } else { - // Network resource - 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); + eventLoopQuitHack = new QObject; + eventLoopQuitHack->moveToThread(this); + connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); +} - replies.insert(reply, runningJob); - } - } - } - return true; - } +QDeclarativePixmapReader::~QDeclarativePixmapReader() +{ + readerMutex.lock(); + readers.remove(engine); + readerMutex.unlock(); - return QObject::event(event); + eventLoopQuitHack->deleteLater(); + wait(); } -#define IMAGEREQUESTHANDLER_MAX_REDIRECT_RECURSION 16 - -void QDeclarativeImageRequestHandler::networkRequestDone() +void QDeclarativePixmapReader::networkRequestDone() { QNetworkReply *reply = static_cast(sender()); QDeclarativePixmapReply *job = replies.take(reply); if (job) { - redirectCount++; - if (redirectCount < IMAGEREQUESTHANDLER_MAX_REDIRECT_RECURSION) { + job->redirectCount++; + if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) { QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if (redirect.isValid()) { QUrl url = reply->url().resolved(redirect.toUrl()); @@ -327,62 +341,141 @@ void QDeclarativeImageRequestHandler::networkRequestDone() return; } } - redirectCount=0; QImage image; - QDeclarativeImageReaderEvent::ReadError error; + QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError; QString errorString; + QSize readSize; if (reply->error()) { - error = QDeclarativeImageReaderEvent::Loading; + error = QDeclarativePixmapReply::Loading; errorString = reply->errorString(); } else { - QSize read_impsize; QByteArray all = reply->readAll(); QBuffer buff(&all); buff.open(QIODevice::ReadOnly); - if (readImage(reply->url(), &buff, &image, &errorString, &read_impsize, job->forcedWidth(), job->forcedHeight())) { - qmlOriginalSizes()->insert(reply->url(), read_impsize); - error = QDeclarativeImageReaderEvent::NoError; - } else { - error = QDeclarativeImageReaderEvent::Decoding; + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->data->requestSize)) { + error = QDeclarativePixmapReply::Decoding; } } // send completion event to the QDeclarativePixmapReply - QCoreApplication::postEvent(job, new QDeclarativeImageReaderEvent(error, errorString, image)); + job->postReply(error, errorString, readSize, image); } - // kick off event loop again if we have dropped below max request count - if (replies.count() == maxImageRequestCount) - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); reply->deleteLater(); + + // kick off event loop again incase we have dropped below max request count + threadObject->processJobs(); } -//=========================================================================== +QDeclarativePixmapReader::ThreadObject::ThreadObject(QDeclarativePixmapReader *i) +: reader(i) +{ +} + +void QDeclarativePixmapReader::ThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} -QDeclarativeImageReader::QDeclarativeImageReader(QDeclarativeEngine *eng) - : QThread(eng), engine(eng), handler(0) +bool QDeclarativePixmapReader::ThreadObject::event(QEvent *e) { - eventLoopQuitHack = new QObject; - eventLoopQuitHack->moveToThread(this); - connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); - start(QThread::IdlePriority); + if (e->type() == QEvent::User) { + reader->processJobs(); + return true; + } else { + return QObject::event(e); + } } -QDeclarativeImageReader::~QDeclarativeImageReader() +void QDeclarativePixmapReader::processJobs() { - readerMutex.lock(); - readers.remove(engine); - readerMutex.unlock(); + QMutexLocker locker(&mutex); + + while (true) { + if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT)) + return; // Nothing else to do + + // Clean cancelled jobs + if (cancelled.count()) { + for (int i = 0; i < cancelled.count(); ++i) { + QDeclarativePixmapReply *job = cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + // cancel any jobs already started + replies.remove(reply); + reply->close(); + } + delete job; + } + cancelled.clear(); + } - eventLoopQuitHack->deleteLater(); - wait(); + if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { + QDeclarativePixmapReply *runningJob = jobs.takeLast(); + runningJob->loading = true; + + locker.unlock(); + processJob(runningJob); + locker.relock(); + } + } +} + +void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob) +{ + QUrl url = runningJob->data->url; + + // fetch + if (url.scheme() == QLatin1String("image")) { + // Use QmlImageProvider + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QImage image = ep->getImageFromProvider(url, &readSize, runningJob->data->requestSize); + + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QDeclarativePixmapReply::Loading; + errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + + runningJob->postReply(errorCode, errorStr, readSize, image); + } else { + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (!lf.isEmpty()) { + // Image is local - load/decode immediately + QImage image; + QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError; + QString errorStr; + QFile f(lf); + QSize readSize; + if (f.open(QIODevice::ReadOnly)) { + if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->data->requestSize)) + errorCode = QDeclarativePixmapReply::Loading; + } else { + errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString()); + errorCode = QDeclarativePixmapReply::Loading; + } + runningJob->postReply(errorCode, errorStr, readSize, image); + } else { + // Network resource + 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); + + replies.insert(reply, runningJob); + } + } } -QDeclarativeImageReader *QDeclarativeImageReader::instance(QDeclarativeEngine *engine) +QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine) { readerMutex.lock(); - QDeclarativeImageReader *reader = readers.value(engine); + QDeclarativePixmapReader *reader = readers.value(engine); if (!reader) { - reader = new QDeclarativeImageReader(engine); + reader = new QDeclarativePixmapReader(engine); readers.insert(engine, reader); } readerMutex.unlock(); @@ -390,348 +483,510 @@ QDeclarativeImageReader *QDeclarativeImageReader::instance(QDeclarativeEngine *e return reader; } -QDeclarativePixmapReply *QDeclarativeImageReader::getImage(const QUrl &url, int req_width, int req_height) +QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data) { mutex.lock(); - QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(this, url, req_width, req_height); - reply->addRef(); - reply->setLoading(); + QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data); + reply->reader = this; jobs.append(reply); - if (jobs.count() == 1 && handler) - QCoreApplication::postEvent(handler, new QEvent(QEvent::User)); + // XXX + if (threadObject) threadObject->processJobs(); mutex.unlock(); return reply; } -void QDeclarativeImageReader::cancel(QDeclarativePixmapReply *reply) +void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply) { mutex.lock(); - if (reply->isLoading()) { - // Add to cancel list to be cancelled in reader thread. + if (reply->loading) { cancelled.append(reply); - if (cancelled.count() == 1 && handler) - QCoreApplication::postEvent(handler, new QEvent(QEvent::User)); + // XXX + if (threadObject) threadObject->processJobs(); + } else { + jobs.removeAll(reply); + delete reply; } mutex.unlock(); } -void QDeclarativeImageReader::run() +void QDeclarativePixmapReader::run() { - readerMutex.lock(); - handler = new QDeclarativeImageRequestHandler(this, engine); - readerMutex.unlock(); + if (replyDownloadProgress == -1) { + const QMetaObject *nr = &QNetworkReply::staticMetaObject; + const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject; + const QMetaObject *ir = &QDeclarativePixmapReader::staticMetaObject; + replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)"); + replyFinished = nr->indexOfSignal("finished()"); + downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)"); + thisNetworkRequestDone = ir->indexOfSlot("networkRequestDone()"); + } + mutex.lock(); + threadObject = new ThreadObject(this); + mutex.unlock(); + + processJobs(); exec(); - delete handler; - handler = 0; + delete threadObject; + threadObject = 0; } -//=========================================================================== - -/*! - \internal - \class QDeclarativePixmapCache - \brief Enacapsultes a pixmap for QDeclarativeGraphics items. +class QDeclarativePixmapKey +{ +public: + const QUrl *url; + const QSize *size; +}; - This class is NOT reentrant. - */ +inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs) +{ + return *lhs.size == *rhs.size && *lhs.url == *rhs.url; +} -typedef QHash QDeclarativePixmapReplyHash; -Q_GLOBAL_STATIC(QDeclarativePixmapReplyHash, qmlActivePixmapReplies); +inline uint qHash(const QDeclarativePixmapKey &key) +{ + return qHash(*key.url) ^ key.size->width() ^ key.size->height(); +} -class QDeclarativePixmapReplyPrivate : public QObjectPrivate +class QDeclarativePixmapStore : public QObject { - Q_DECLARE_PUBLIC(QDeclarativePixmapReply) + Q_OBJECT +public: + QDeclarativePixmapStore(); + + void unreferencePixmap(QDeclarativePixmapData *); + void referencePixmap(QDeclarativePixmapData *); + +protected: + virtual void timerEvent(QTimerEvent *); public: - QDeclarativePixmapReplyPrivate(QDeclarativeImageReader *r, const QUrl &u, int req_width, int req_height) - : QObjectPrivate(), refCount(1), url(u), status(QDeclarativePixmapReply::Loading), loading(false), reader(r), - forced_width(req_width), forced_height(req_height) - { - } + QHash m_cache; - int refCount; - QUrl url; - QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard - QDeclarativePixmapReply::Status status; - bool loading; - QDeclarativeImageReader *reader; - int forced_width, forced_height; - QString errorString; -}; +private: + QDeclarativePixmapData *m_unreferencedPixmaps; + QDeclarativePixmapData *m_lastUnreferencedPixmap; + int m_unreferencedCost; + int m_timerId; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore); -QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativeImageReader *reader, const QUrl &url, int req_width, int req_height) - : QObject(*new QDeclarativePixmapReplyPrivate(reader, url, req_width, req_height), 0) +QDeclarativePixmapStore::QDeclarativePixmapStore() +: m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1) { } -QDeclarativePixmapReply::~QDeclarativePixmapReply() +void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data) { + Q_ASSERT(data->prevUnreferenced == 0); + Q_ASSERT(data->prevUnreferencedPtr == 0); + Q_ASSERT(data->nextUnreferenced == 0); + + data->nextUnreferenced = m_unreferencedPixmaps; + data->prevUnreferencedPtr = &m_unreferencedPixmaps; + + m_unreferencedPixmaps = data; + if (m_unreferencedPixmaps->nextUnreferenced) { + m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps; + m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced; + } + + if (!m_lastUnreferencedPixmap) + m_lastUnreferencedPixmap = data; + + m_unreferencedCost += data->cost(); + + if (m_timerId == -1) + startTimer(CACHE_EXPIRE_TIME * 1000); } -const QUrl &QDeclarativePixmapReply::url() const +void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data) { - Q_D(const QDeclarativePixmapReply); - return d->url; + Q_ASSERT(data->prevUnreferencedPtr); + + *data->prevUnreferencedPtr = data->nextUnreferenced; + if (data->nextUnreferenced) { + data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr; + data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced; + } + if (m_lastUnreferencedPixmap == data) + m_lastUnreferencedPixmap = data->prevUnreferenced; + + data->nextUnreferenced = 0; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + m_unreferencedCost -= data->cost(); } -int QDeclarativePixmapReply::forcedWidth() const +void QDeclarativePixmapStore::timerEvent(QTimerEvent *) { - Q_D(const QDeclarativePixmapReply); - return d->forced_width; + int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION; + + while (removalCost > 0 && m_lastUnreferencedPixmap) { + QDeclarativePixmapData *data = m_lastUnreferencedPixmap; + Q_ASSERT(data->nextUnreferenced == 0); + + *data->prevUnreferencedPtr = 0; + m_lastUnreferencedPixmap = data->prevUnreferenced; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + removalCost -= data->cost(); + data->removeFromCache(); + delete data; + } + + if (m_unreferencedPixmaps == 0) { + killTimer(m_timerId); + m_timerId = -1; + } } -int QDeclarativePixmapReply::forcedHeight() const +QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d) +: data(d), reader(0), loading(false), redirectCount(0) { - Q_D(const QDeclarativePixmapReply); - return d->forced_height; + if (finishedIndex == -1) { + finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); + downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + } } -QSize QDeclarativePixmapReply::implicitSize() const +QDeclarativePixmapReply::~QDeclarativePixmapReply() { - Q_D(const QDeclarativePixmapReply); - QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(d->url); - if (iter != qmlOriginalSizes()->end()) - return *iter; - else - return QSize(); } bool QDeclarativePixmapReply::event(QEvent *event) { - Q_D(QDeclarativePixmapReply); if (event->type() == QEvent::User) { - d->loading = false; - if (!release(true)) { - QDeclarativeImageReaderEvent *de = static_cast(event); - d->status = (de->error == QDeclarativeImageReaderEvent::NoError) ? Ready : Error; - if (d->status == Ready) - d->pixmap = QPixmap::fromImage(de->image); - else - d->errorString = de->errorString; - QByteArray key = d->url.toEncoded(QUrl::FormattingOption(0x100)); - if (d->forced_width > 0 || d->forced_height > 0) { - key += ':'; - key += QByteArray::number(d->forced_width); - key += 'x'; - key += QByteArray::number(d->forced_height); + + if (data) { + Event *de = static_cast(event); + data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error; + + if (data->pixmapStatus == QDeclarativePixmap::Ready) { + data->pixmap = QPixmap::fromImage(de->image); + data->implicitSize = de->implicitSize; + } else { + data->errorString = de->errorString; + data->removeFromCache(); // We don't continue to cache error'd pixmaps } - QString strKey = QString::fromLatin1(key.constData(), key.count()); - QPixmapCache::insert(strKey, d->pixmap); // note: may fail (returns false) + + data->reply = 0; emit finished(); } + + delete this; return true; + } else { + return QObject::event(event); } +} - return QObject::event(event); +int QDeclarativePixmapData::cost() const +{ + return pixmap.width() * pixmap.height() * pixmap.depth(); } -QString QDeclarativePixmapReply::errorString() const +void QDeclarativePixmapData::addref() { - Q_D(const QDeclarativePixmapReply); - return d->errorString; + ++refCount; + if (prevUnreferencedPtr) + pixmapStore()->referencePixmap(this); } -QDeclarativePixmapReply::Status QDeclarativePixmapReply::status() const +void QDeclarativePixmapData::release() { - Q_D(const QDeclarativePixmapReply); - return d->status; + Q_ASSERT(refCount > 0); + --refCount; + + if (refCount == 0) { + if (reply) { + reply->data = 0; + reply->reader->cancel(reply); + reply = 0; + } + + if (pixmapStatus == QDeclarativePixmap::Ready) { + pixmapStore()->unreferencePixmap(this); + } else { + removeFromCache(); + delete this; + } + } } -bool QDeclarativePixmapReply::isLoading() const +void QDeclarativePixmapData::addToCache() { - Q_D(const QDeclarativePixmapReply); - return d->loading; + if (!inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.insert(key, this); + inCache = true; + } } -void QDeclarativePixmapReply::setLoading() +void QDeclarativePixmapData::removeFromCache() { - Q_D(QDeclarativePixmapReply); - d->loading = true; + if (inCache) { + QDeclarativePixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.remove(key); + inCache = false; + } } -void QDeclarativePixmapReply::addRef() +struct QDeclarativePixmapNull { + QUrl url; + QPixmap pixmap; + QSize size; +}; +Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap); + +QDeclarativePixmap::QDeclarativePixmap() +: d(0) { - Q_D(QDeclarativePixmapReply); - ++d->refCount; } -bool QDeclarativePixmapReply::release(bool defer) +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url) +: d(0) { - Q_D(QDeclarativePixmapReply); - Q_ASSERT(d->refCount > 0); - --d->refCount; - if (d->refCount == 0) { - qmlActivePixmapReplies()->remove(d->url); - if (defer) - deleteLater(); - else - delete this; - return true; - } else if (d->refCount == 1 && d->loading) { - // The only reference left is the reader thread. - qmlActivePixmapReplies()->remove(d->url); - d->reader->cancel(this); + load(engine, url); +} + +QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +: d(0) +{ + load(engine, url, size); +} + +QDeclarativePixmap::~QDeclarativePixmap() +{ + if (d) { + d->release(); + d = 0; } +} - return false; +bool QDeclarativePixmap::isNull() const +{ + return d == 0; } -/*! - 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. +bool QDeclarativePixmap::isReady() const +{ + return status() == Ready; +} - Returns Ready, or Error if the image has been retrieved, - otherwise the current retrieval status. +bool QDeclarativePixmap::isError() const +{ + return status() == Error; +} - If \a async is false the image will be loaded and decoded immediately; - otherwise the image will be loaded and decoded in a separate thread. +bool QDeclarativePixmap::isLoading() const +{ + return status() == Loading; +} - If \a req_width and \a req_height are non-zero, they are used for - the size of the rendered pixmap rather than the intrinsic size of the image. - Different request sizes add different cache items. +QString QDeclarativePixmap::error() const +{ + if (d) + return d->errorString; + else + return QString(); +} - Note that images sourced from the network will always be loaded and - decoded asynchonously. -*/ -QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QPixmap *pixmap, QString *errorString, QSize *impsize, bool async, int req_width, int req_height) +QDeclarativePixmap::Status QDeclarativePixmap::status() const { - QDeclarativePixmapReply::Status status = QDeclarativePixmapReply::Unrequested; - QByteArray key = url.toEncoded(QUrl::FormattingOption(0x100)); + if (d) + return d->pixmapStatus; + else + return Null; +} - if (req_width > 0 || req_height > 0) { - key += ':'; - key += QByteArray::number(req_width); - key += 'x'; - key += QByteArray::number(req_height); - } +const QUrl &QDeclarativePixmap::url() const +{ + if (d) + return d->url; + else + return nullPixmap()->url; +} - QString strKey = QString::fromLatin1(key.constData(), key.count()); +const QSize &QDeclarativePixmap::implicitSize() const +{ + if (d) + return d->implicitSize; + else + return nullPixmap()->size; +} + +const QSize &QDeclarativePixmap::requestSize() const +{ + if (d) + return d->requestSize; + else + return nullPixmap()->size; +} + +const QPixmap &QDeclarativePixmap::pixmap() const +{ + if (d) + return d->pixmap; + else + return nullPixmap()->pixmap; +} + +void QDeclarativePixmap::setPixmap(const QPixmap &p) +{ + clear(); + + if (!p.isNull()) + d = new QDeclarativePixmapData(p); +} + +int QDeclarativePixmap::width() const +{ + if (d) + return d->pixmap.width(); + else + return 0; +} + +int QDeclarativePixmap::height() const +{ + if (d) + return d->pixmap.height(); + else + return 0; +} + +QRect QDeclarativePixmap::rect() const +{ + if (d) + return d->pixmap.rect(); + else + return QRect(); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url) +{ + load(engine, url, QSize(), false); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, bool async) +{ + load(engine, url, QSize(), async); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +{ + load(engine, url, size, false); +} + +void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool async) +{ + if (d) { d->release(); d = 0; } + + QDeclarativePixmapKey key = { &url, &requestSize }; + QDeclarativePixmapStore *store = pixmapStore(); + + QHash::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; -#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML - if (!async) { - QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); - if (!lf.isEmpty()) { - status = QDeclarativePixmapReply::Ready; - if (!QPixmapCache::find(strKey,pixmap)) { - QFile f(lf); - QSize read_impsize; if (f.open(QIODevice::ReadOnly)) { QImage image; - if (readImage(url, &f, &image, errorString, &read_impsize, req_width, req_height)) { - *pixmap = QPixmap::fromImage(image); - } else { - *pixmap = QPixmap(); - status = QDeclarativePixmapReply::Error; - } + if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { + d = new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + d->addToCache(); + return; + } } else { - if (errorString) - *errorString = tr("Cannot open: %1").arg(url.toString()); - *pixmap = QPixmap(); - status = QDeclarativePixmapReply::Error; - } - if (status == QDeclarativePixmapReply::Ready) { - QPixmapCache::insert(strKey, *pixmap); - qmlOriginalSizes()->insert(url, read_impsize); - } - if (impsize) - *impsize = read_impsize; - } else { - if (impsize) { - QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(url); - if (iter != qmlOriginalSizes()->end()) - *impsize = *iter; + errorString = tr("Cannot open: %1").arg(url.toString()); } + + d = new QDeclarativePixmapData(url, requestSize, errorString); + return; } - return status; - } - } -#endif - - QDeclarativePixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); - if (iter != qmlActivePixmapReplies()->end() && (*iter)->status() == QDeclarativePixmapReply::Ready) { - // Must check this, since QPixmapCache::insert may have failed. - *pixmap = (*iter)->d_func()->pixmap; - status = (*iter)->status(); - (*iter)->release(); - } else if (QPixmapCache::find(strKey, pixmap)) { - if (iter != qmlActivePixmapReplies()->end()) { - status = (*iter)->status(); - if (errorString) - *errorString = (*iter)->errorString(); - (*iter)->release(); - } else if (pixmap->isNull()) { - status = QDeclarativePixmapReply::Error; - if (errorString) - *errorString = tr("Unknown Error loading %1").arg(url.toString()); - } else { - status = QDeclarativePixmapReply::Ready; - } - } else if (iter != qmlActivePixmapReplies()->end()) { - status = QDeclarativePixmapReply::Loading; - } - if (impsize) { - QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(url); - if (iter != qmlOriginalSizes()->end()) - *impsize = *iter; - } + } + + if (!engine) + return; + + QDeclarativePixmapReader *reader = QDeclarativePixmapReader::instance(engine); + + d = new QDeclarativePixmapData(url, requestSize); + d->addToCache(); - return status; + d->reply = reader->getImage(d); + } else { + d = *iter; + d->addref(); + } } -/*! - Starts a network request to load \a url. +void QDeclarativePixmap::clear() +{ + if (d) { + d->release(); + d = 0; + } +} - Returns a QDeclarativePixmapReply. Caller should connect to QDeclarativePixmapReply::finished() - and call get() when the image is available. +void QDeclarativePixmap::clear(QObject *obj) +{ + if (d) { + if (d->reply) + QObject::disconnect(d->reply, 0, obj, 0); + d->release(); + d = 0; + } +} - The returned QDeclarativePixmapReply will be deleted when all request() calls are - matched by a corresponding get() call. -*/ -QDeclarativePixmapReply *QDeclarativePixmapCache::request(QDeclarativeEngine *engine, const QUrl &url, int req_width, int req_height) +bool QDeclarativePixmap::connectFinished(QObject *object, const char *method) { - QDeclarativePixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); - if (iter == qmlActivePixmapReplies()->end()) { - QDeclarativeImageReader *reader = QDeclarativeImageReader::instance(engine); - QDeclarativePixmapReply *item = reader->getImage(url, req_width, req_height); - iter = qmlActivePixmapReplies()->insert(url, item); - } else { - (*iter)->addRef(); + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; } - return (*iter); + return QObject::connect(d->reply, SIGNAL(finished()), object, method); } -/*! - Cancels a previous call to request(). +bool QDeclarativePixmap::connectFinished(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectFinished() called when not loading."); + return false; + } - May also cancel loading (eg. if no other pending request). + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method); +} - Any connections from the QDeclarativePixmapReply returned by request() to \a obj will be - disconnected. -*/ -void QDeclarativePixmapCache::cancel(const QUrl& url, QObject *obj) +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method) { - QDeclarativePixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); - if (iter == qmlActivePixmapReplies()->end()) - return; + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } - QDeclarativePixmapReply *reply = *iter; - if (obj) - QObject::disconnect(reply, 0, obj, 0); - reply->release(); + return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method); } -/*! - This function is mainly for test verification. It returns the number of - requests that are still unfinished. -*/ -int QDeclarativePixmapCache::pendingRequests() +bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method) { - return qmlActivePixmapReplies()->count(); + if (!d || !d->reply) { + qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method); } QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativepixmapcache_p.h b/src/declarative/util/qdeclarativepixmapcache_p.h index 33d9de1..8278c35 100644 --- a/src/declarative/util/qdeclarativepixmapcache_p.h +++ b/src/declarative/util/qdeclarativepixmapcache_p.h @@ -42,69 +42,70 @@ #ifndef QDECLARATIVEPIXMAPCACHE_H #define QDECLARATIVEPIXMAPCACHE_H -#include -#include +#include +#include +#include #include -#include QT_BEGIN_HEADER QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class QDeclarativeEngine; -class QNetworkReply; -class QDeclarativeImageReader; -class QDeclarativePixmapReplyPrivate; -class Q_DECLARATIVE_EXPORT QDeclarativePixmapReply : public QObject +class QDeclarativeEngine; +class QDeclarativePixmapData; +class Q_AUTOTEST_EXPORT QDeclarativePixmap { - Q_OBJECT + Q_DECLARE_TR_FUNCTIONS(QDeclarativePixmap) public: - ~QDeclarativePixmapReply(); + QDeclarativePixmap(); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &); + QDeclarativePixmap(QDeclarativeEngine *, const QUrl &, const QSize &); + ~QDeclarativePixmap(); - enum Status { Ready, Error, Unrequested, Loading }; - Status status() const; - QString errorString() const; + enum Status { Null, Ready, Error, Loading }; + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + Status status() const; + QString error() const; const QUrl &url() const; - int forcedWidth() const; - int forcedHeight() const; - QSize implicitSize() const; + const QSize &implicitSize() const; + const QSize &requestSize() const; + const QPixmap &pixmap() const; + void setPixmap(const QPixmap &); -Q_SIGNALS: - void finished(); - void downloadProgress(qint64, qint64); + QRect rect() const; + int width() const; + int height() const; + inline operator const QPixmap &() const; -protected: - bool event(QEvent *event); + void load(QDeclarativeEngine *, const QUrl &); + void load(QDeclarativeEngine *, const QUrl &, bool); + void load(QDeclarativeEngine *, const QUrl &, const QSize &); + void load(QDeclarativeEngine *, const QUrl &, const QSize &, bool); -private: - void addRef(); - bool release(bool defer=false); - bool isLoading() const; - void setLoading(); + void clear(); + void clear(QObject *); + + bool connectFinished(QObject *, const char *); + bool connectFinished(QObject *, int); + bool connectDownloadProgress(QObject *, const char *); + bool connectDownloadProgress(QObject *, int); private: - QDeclarativePixmapReply(QDeclarativeImageReader *reader, const QUrl &url, int req_width, int req_height); - Q_DISABLE_COPY(QDeclarativePixmapReply) - Q_DECLARE_PRIVATE(QDeclarativePixmapReply) - friend class QDeclarativeImageRequestHandler; - friend class QDeclarativeImageReader; - friend class QDeclarativePixmapCache; + Q_DISABLE_COPY(QDeclarativePixmap); + QDeclarativePixmapData *d; }; -class Q_DECLARATIVE_EXPORT QDeclarativePixmapCache +inline QDeclarativePixmap::operator const QPixmap &() const { - Q_DECLARE_TR_FUNCTIONS(QDeclarativePixmapCache) -public: - static QDeclarativePixmapReply::Status get(const QUrl& url, QPixmap *pixmap, QString *errorString, QSize *impsize=0, bool async=false, int req_width=0, int req_height=0); - static QDeclarativePixmapReply *request(QDeclarativeEngine *, const QUrl& url, int req_width=0, int req_height=0); - static void cancel(const QUrl& url, QObject *obj); - static int pendingRequests(); -}; - - + return pixmap(); +} QT_END_NAMESPACE diff --git a/src/imports/particles/qdeclarativeparticles.cpp b/src/imports/particles/qdeclarativeparticles.cpp index 630c068..a7c445d 100644 --- a/src/imports/particles/qdeclarativeparticles.cpp +++ b/src/imports/particles/qdeclarativeparticles.cpp @@ -437,7 +437,7 @@ public: , lifeSpanDev(1000), fadeInDur(200), fadeOutDur(300) , angle(0), angleDev(0), velocity(0), velocityDev(0), emissionCarry(0.) , addParticleTime(0), addParticleCount(0), lastAdvTime(0) - , motion(0), pendingPixmapCache(false), clock(this) + , motion(0), clock(this) { } @@ -456,7 +456,7 @@ public: void updateOpacity(QDeclarativeParticle &p, int age); QUrl url; - QPixmap image; + QDeclarativePixmap image; int count; int emissionRate; qreal emissionVariance; @@ -475,7 +475,6 @@ public: QDeclarativeParticleMotion *motion; QDeclarativeParticlesPainter *paintItem; - bool pendingPixmapCache; QList > bursts;//countLeft, emissionRate pairs QList particles; @@ -709,9 +708,6 @@ QDeclarativeParticles::QDeclarativeParticles(QDeclarativeItem *parent) QDeclarativeParticles::~QDeclarativeParticles() { - Q_D(QDeclarativeParticles); - if (d->pendingPixmapCache) - QDeclarativePixmapCache::cancel(d->url, this); } /*! @@ -732,10 +728,8 @@ QUrl QDeclarativeParticles::source() const void QDeclarativeParticles::imageLoaded() { Q_D(QDeclarativeParticles); - d->pendingPixmapCache = false; - QString errorString; - if (QDeclarativePixmapCache::get(d->url, &d->image, &errorString)==QDeclarativePixmapReply::Error) - qmlInfo(this) << errorString; + if (d->image.isError()) + qmlInfo(this) << d->image.error(); d->paintItem->updateSize(); d->paintItem->update(); } @@ -747,27 +741,20 @@ void QDeclarativeParticles::setSource(const QUrl &name) if ((d->url.isEmpty() == name.isEmpty()) && name == d->url) return; - if (d->pendingPixmapCache) { - QDeclarativePixmapCache::cancel(d->url, this); - d->pendingPixmapCache = false; - } if (name.isEmpty()) { d->url = name; - d->image = QPixmap(); + d->image.clear(this); d->paintItem->updateSize(); d->paintItem->update(); } else { d->url = name; Q_ASSERT(!name.isRelative()); - QString errorString; - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->image, &errorString); - if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) { - QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->url); - connect(reply, SIGNAL(finished()), this, SLOT(imageLoaded())); - d->pendingPixmapCache = true; + d->image.load(qmlEngine(this), d->url); + if (d->image.isLoading()) { + d->image.connectFinished(this, SLOT(imageLoaded())); } else { - if (status == QDeclarativePixmapReply::Error) - qmlInfo(this) << errorString; + if (d->image.isError()) + qmlInfo(this) << d->image.error(); //### unify with imageLoaded d->paintItem->updateSize(); d->paintItem->update(); diff --git a/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp b/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp index f1018b2..0c7780c 100644 --- a/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp +++ b/tests/auto/declarative/qdeclarativepixmapcache/tst_qdeclarativepixmapcache.cpp @@ -138,42 +138,37 @@ void tst_qdeclarativepixmapcache::single() expectedError = "Cannot open: " + target.toString(); } - QPixmap pixmap; + QDeclarativePixmap pixmap; QVERIFY(pixmap.width() <= 0); // Check Qt assumption - QString errorString; - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(target, &pixmap, &errorString); + + pixmap.load(&engine, target); if (incache) { - QCOMPARE(errorString, expectedError); + QCOMPARE(pixmap.error(), expectedError); if (exists) { - QVERIFY(status == QDeclarativePixmapReply::Ready); + QVERIFY(pixmap.status() == QDeclarativePixmap::Ready); QVERIFY(pixmap.width() > 0); } else { - QVERIFY(status == QDeclarativePixmapReply::Error); + QVERIFY(pixmap.status() == QDeclarativePixmap::Error); QVERIFY(pixmap.width() <= 0); } } else { - QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(&engine, target); - QVERIFY(reply); QVERIFY(pixmap.width() <= 0); Slotter getter; - connect(reply, SIGNAL(finished()), &getter, SLOT(got())); + pixmap.connectFinished(&getter, SLOT(got())); QTestEventLoop::instance().enterLoop(10); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(getter.gotslot); - QString errorString; if (exists) { - QVERIFY(QDeclarativePixmapCache::get(target, &pixmap, &errorString) == QDeclarativePixmapReply::Ready); + QVERIFY(pixmap.status() == QDeclarativePixmap::Ready); QVERIFY(pixmap.width() > 0); } else { - QVERIFY(QDeclarativePixmapCache::get(target, &pixmap, &errorString) == QDeclarativePixmapReply::Error); + QVERIFY(pixmap.status() == QDeclarativePixmap::Error); QVERIFY(pixmap.width() <= 0); } - QCOMPARE(errorString, expectedError); + QCOMPARE(pixmap.error(), expectedError); } - - QCOMPARE(QDeclarativePixmapCache::pendingRequests(), 0); } void tst_qdeclarativepixmapcache::parallel_data() @@ -185,47 +180,36 @@ void tst_qdeclarativepixmapcache::parallel_data() QTest::addColumn("target2"); QTest::addColumn("incache"); QTest::addColumn("cancel"); // which one to cancel - QTest::addColumn("requests"); QTest::newRow("local") << thisfile.resolved(QUrl("data/exists1.png")) << thisfile.resolved(QUrl("data/exists2.png")) << (localfile_optimized ? 2 : 0) - << -1 - << (localfile_optimized ? 0 : 2) - ; + << -1; QTest::newRow("remote") << QUrl("http://127.0.0.1:14452/exists2.png") << QUrl("http://127.0.0.1:14452/exists3.png") << 0 - << -1 - << 2 - ; + << -1; QTest::newRow("remoteagain") << QUrl("http://127.0.0.1:14452/exists2.png") << QUrl("http://127.0.0.1:14452/exists3.png") << 2 - << -1 - << 0 - ; + << -1; QTest::newRow("remotecopy") << QUrl("http://127.0.0.1:14452/exists4.png") << QUrl("http://127.0.0.1:14452/exists4.png") << 0 - << -1 - << 1 - ; + << -1; QTest::newRow("remotecopycancel") << QUrl("http://127.0.0.1:14452/exists5.png") << QUrl("http://127.0.0.1:14452/exists5.png") << 0 - << 0 - << 1 - ; + << 0; } void tst_qdeclarativepixmapcache::parallel() @@ -234,38 +218,38 @@ void tst_qdeclarativepixmapcache::parallel() QFETCH(QUrl, target2); QFETCH(int, incache); QFETCH(int, cancel); - QFETCH(int, requests); QList targets; targets << target1 << target2; - QList replies; + QList pixmaps; + QList pending; QList getters; + for (int i=0; i 0); + QDeclarativePixmap *pixmap = new QDeclarativePixmap; + + pixmap->load(&engine, target); + + QVERIFY(pixmap->status() != QDeclarativePixmap::Error); + pixmaps.append(pixmap); + if (pixmap->isReady()) { + QVERIFY(pixmap->width() > 0); getters.append(0); + pending.append(false); } else { - QVERIFY(pixmap.width() <= 0); + QVERIFY(pixmap->width() <= 0); getters.append(new Slotter); - connect(reply, SIGNAL(finished()), getters[i], SLOT(got())); + pixmap->connectFinished(getters[i], SLOT(got())); + pending.append(true); } } QCOMPARE(incache+slotters, targets.count()); - QCOMPARE(QDeclarativePixmapCache::pendingRequests(), requests); if (cancel >= 0) { - QDeclarativePixmapCache::cancel(targets.at(cancel), getters[cancel]); + pixmaps.at(cancel)->clear(getters[cancel]); slotters--; } @@ -275,22 +259,21 @@ void tst_qdeclarativepixmapcache::parallel() } for (int i=0; igotslot); - } else { + QDeclarativePixmap *pixmap = pixmaps[i]; + + if (i == cancel) { + QVERIFY(!getters[i]->gotslot); + } else { + if (pending[i]) QVERIFY(getters[i]->gotslot); - QPixmap pixmap; - QString errorString; - QVERIFY(QDeclarativePixmapCache::get(targets[i], &pixmap, &errorString) == QDeclarativePixmapReply::Ready); - QVERIFY(pixmap.width() > 0); - } + + QVERIFY(pixmap->isReady()); + QVERIFY(pixmap->width() > 0); delete getters[i]; } } - QCOMPARE(QDeclarativePixmapCache::pendingRequests(), 0); + qDeleteAll(pixmaps); } QTEST_MAIN(tst_qdeclarativepixmapcache) -- cgit v0.12