1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativepixmapcache_p.h"
43 #include <qdeclarativenetworkaccessmanagerfactory.h>
44 #include <qdeclarativeimageprovider.h>
46 #include <qdeclarativeengine.h>
47 #include <private/qdeclarativeglobal_p.h>
48 #include <private/qdeclarativeengine_p.h>
50 #include <QtQuick/private/qsgtexture_p.h>
51 #include <QtQuick/private/qsgcontext_p.h>
53 #include <QCoreApplication>
54 #include <QImageReader>
56 #include <QNetworkReply>
57 #include <QPixmapCache>
61 #include <QMutexLocker>
62 #include <QWaitCondition>
64 #include <QWaitCondition>
65 #include <QtCore/qdebug.h>
66 #include <private/qobject_p.h>
69 #define IMAGEREQUEST_MAX_REQUEST_COUNT 8
70 #define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16
71 #define CACHE_EXPIRE_TIME 30
72 #define CACHE_REMOVAL_FRACTION 4
76 // The cache limit describes the maximum "junk" in the cache.
77 static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded in qpixmapcache.cpp
79 QSGTexture *QDeclarativeDefaultTextureFactory::createTexture() const
81 QSGPlainTexture *t = new QSGPlainTexture();
86 class QDeclarativePixmapReader;
87 class QDeclarativePixmapData;
88 class QDeclarativePixmapReply : public QObject
92 enum ReadError { NoError, Loading, Decoding };
94 QDeclarativePixmapReply(QDeclarativePixmapData *);
95 ~QDeclarativePixmapReply();
97 QDeclarativePixmapData *data;
98 QDeclarativeEngine *engineForReader; // always access reader inside readerMutex
105 class Event : public QEvent {
107 Event(ReadError, const QString &, const QSize &, QDeclarativeTextureFactory *factory, const QImage &image);
113 QDeclarativeTextureFactory *textureFactory;
115 void postReply(ReadError, const QString &, const QSize &, const QImage &image);
116 void postReply(ReadError, const QString &, const QSize &, QDeclarativeTextureFactory *factory, const QImage &image);
121 void downloadProgress(qint64, qint64);
124 bool event(QEvent *event);
127 Q_DISABLE_COPY(QDeclarativePixmapReply)
130 static int finishedIndex;
131 static int downloadProgressIndex;
134 class QDeclarativePixmapReaderThreadObject : public QObject {
137 QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *);
139 virtual bool event(QEvent *e);
141 void networkRequestDone();
143 QDeclarativePixmapReader *reader;
146 class QDeclarativePixmapData;
147 class QDeclarativePixmapReader : public QThread
151 QDeclarativePixmapReader(QDeclarativeEngine *eng);
152 ~QDeclarativePixmapReader();
154 QDeclarativePixmapReply *getImage(QDeclarativePixmapData *);
155 void cancel(QDeclarativePixmapReply *rep);
157 static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine);
158 static QDeclarativePixmapReader *existingInstance(QDeclarativeEngine *engine);
164 friend class QDeclarativePixmapReaderThreadObject;
166 void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &);
167 void networkRequestDone(QNetworkReply *);
169 QList<QDeclarativePixmapReply*> jobs;
170 QList<QDeclarativePixmapReply*> cancelled;
171 QDeclarativeEngine *engine;
172 QObject *eventLoopQuitHack;
175 QDeclarativePixmapReaderThreadObject *threadObject;
176 QWaitCondition waitCondition;
178 QNetworkAccessManager *networkAccessManager();
179 QNetworkAccessManager *accessManager;
181 QHash<QNetworkReply*,QDeclarativePixmapReply*> replies;
183 static int replyDownloadProgress;
184 static int replyFinished;
185 static int downloadProgress;
186 static int threadNetworkRequestDone;
187 static QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> readers;
189 static QMutex readerMutex;
192 class QDeclarativePixmapData
195 QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QSize &s, const QString &e)
196 : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error),
197 url(u), errorString(e), requestSize(s), textureFactory(0), reply(0), prevUnreferenced(0),
198 prevUnreferencedPtr(0), nextUnreferenced(0)
200 declarativePixmaps.insert(pixmap);
203 QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QSize &r)
204 : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading),
205 url(u), requestSize(r), textureFactory(0), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0),
208 declarativePixmaps.insert(pixmap);
211 QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QImage &p, const QSize &s, const QSize &r)
212 : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
213 url(u), image(p), implicitSize(s), requestSize(r), textureFactory(new QDeclarativeDefaultTextureFactory(p)), reply(0), prevUnreferenced(0),
214 prevUnreferencedPtr(0), nextUnreferenced(0)
216 declarativePixmaps.insert(pixmap);
219 QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, QDeclarativeTextureFactory *factory, const QImage &p, const QSize &s, const QSize &r)
220 : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
221 url(u), image(p), implicitSize(s), requestSize(r), textureFactory(factory), reply(0), prevUnreferenced(0),
222 prevUnreferencedPtr(0), nextUnreferenced(0)
224 declarativePixmaps.insert(pixmap);
227 QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QImage &p)
228 : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready),
229 image(p), implicitSize(p.size()), requestSize(p.size()), textureFactory(new QDeclarativeDefaultTextureFactory(p)), reply(0), prevUnreferenced(0),
230 prevUnreferencedPtr(0), nextUnreferenced(0)
232 declarativePixmaps.insert(pixmap);
235 ~QDeclarativePixmapData()
237 while (!declarativePixmaps.isEmpty()) {
238 QDeclarativePixmap *referencer = declarativePixmaps.first();
239 declarativePixmaps.remove(referencer);
242 delete textureFactory;
249 void removeFromCache();
254 bool privatePixmap:1;
256 QDeclarativePixmap::Status pixmapStatus;
263 QDeclarativeTextureFactory *textureFactory;
265 QIntrusiveList<QDeclarativePixmap, &QDeclarativePixmap::dataListNode> declarativePixmaps;
266 QDeclarativePixmapReply *reply;
268 QDeclarativePixmapData *prevUnreferenced;
269 QDeclarativePixmapData**prevUnreferencedPtr;
270 QDeclarativePixmapData *nextUnreferenced;
273 int QDeclarativePixmapReply::finishedIndex = -1;
274 int QDeclarativePixmapReply::downloadProgressIndex = -1;
277 QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> QDeclarativePixmapReader::readers;
278 QMutex QDeclarativePixmapReader::readerMutex;
280 int QDeclarativePixmapReader::replyDownloadProgress = -1;
281 int QDeclarativePixmapReader::replyFinished = -1;
282 int QDeclarativePixmapReader::downloadProgress = -1;
283 int QDeclarativePixmapReader::threadNetworkRequestDone = -1;
286 void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
287 const QSize &implicitSize, const QImage &image)
290 QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, new QDeclarativeDefaultTextureFactory(image), image));
293 void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
294 const QSize &implicitSize, QDeclarativeTextureFactory *factory,
298 QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, factory, image));
301 QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, QDeclarativeTextureFactory *factory, const QImage &i)
302 : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i), textureFactory(factory)
306 QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager()
308 if (!accessManager) {
309 Q_ASSERT(threadObject);
310 accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
312 return accessManager;
315 static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
316 const QSize &requestSize)
318 QImageReader imgio(dev);
320 bool force_scale = false;
321 if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) {
322 imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053
327 if (requestSize.width() > 0 || requestSize.height() > 0) {
328 QSize s = imgio.size();
329 if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
330 if (requestSize.height() <= 0)
331 s.setHeight(s.height()*requestSize.width()/s.width());
332 s.setWidth(requestSize.width()); scaled = true;
334 if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
335 if (requestSize.width() <= 0)
336 s.setWidth(s.width()*requestSize.height()/s.height());
337 s.setHeight(requestSize.height()); scaled = true;
339 if (scaled) { imgio.setScaledSize(s); }
343 *impsize = imgio.size();
345 if (imgio.read(image)) {
346 if (impsize && impsize->width() < 0)
347 *impsize = image->size();
351 *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString())
352 .arg(imgio.errorString());
357 QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng)
358 : QThread(eng), engine(eng), threadObject(0), accessManager(0)
360 eventLoopQuitHack = new QObject;
361 eventLoopQuitHack->moveToThread(this);
362 connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
363 start(QThread::LowestPriority);
366 QDeclarativePixmapReader::~QDeclarativePixmapReader()
369 readers.remove(engine);
370 readerMutex.unlock();
373 // manually cancel all outstanding jobs.
374 foreach (QDeclarativePixmapReply *reply, jobs) {
378 QList<QDeclarativePixmapReply*> activeJobs = replies.values();
379 foreach (QDeclarativePixmapReply *reply, activeJobs) {
380 if (reply->loading) {
381 cancelled.append(reply);
385 if (threadObject) threadObject->processJobs();
388 eventLoopQuitHack->deleteLater();
392 void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply)
394 QDeclarativePixmapReply *job = replies.take(reply);
397 job->redirectCount++;
398 if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
399 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
400 if (redirect.isValid()) {
401 QUrl url = reply->url().resolved(redirect.toUrl());
402 QNetworkRequest req(url);
403 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
405 reply->deleteLater();
406 reply = networkAccessManager()->get(req);
408 QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
409 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
411 replies.insert(reply, job);
417 QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError;
420 if (reply->error()) {
421 error = QDeclarativePixmapReply::Loading;
422 errorString = reply->errorString();
424 QByteArray all = reply->readAll();
426 buff.open(QIODevice::ReadOnly);
427 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize))
428 error = QDeclarativePixmapReply::Decoding;
430 // send completion event to the QDeclarativePixmapReply
432 if (!cancelled.contains(job)) {
433 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
435 job->postReply(error, errorString, readSize, factory, image);
437 job->postReply(error, errorString, readSize, image);
441 reply->deleteLater();
443 // kick off event loop again incase we have dropped below max request count
444 threadObject->processJobs();
447 QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i)
452 void QDeclarativePixmapReaderThreadObject::processJobs()
454 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
457 bool QDeclarativePixmapReaderThreadObject::event(QEvent *e)
459 if (e->type() == QEvent::User) {
460 reader->processJobs();
463 return QObject::event(e);
467 void QDeclarativePixmapReaderThreadObject::networkRequestDone()
469 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
470 reader->networkRequestDone(reply);
473 void QDeclarativePixmapReader::processJobs()
475 QMutexLocker locker(&mutex);
478 if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT))
479 return; // Nothing else to do
481 // Clean cancelled jobs
482 if (cancelled.count()) {
483 for (int i = 0; i < cancelled.count(); ++i) {
484 QDeclarativePixmapReply *job = cancelled.at(i);
485 QNetworkReply *reply = replies.key(job, 0);
486 if (reply && reply->isRunning()) {
487 // cancel any jobs already started
488 replies.remove(reply);
491 // deleteLater, since not owned by this thread
497 if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
498 QDeclarativePixmapReply *runningJob = jobs.takeLast();
499 runningJob->loading = true;
501 QUrl url = runningJob->url;
502 QSize requestSize = runningJob->requestSize;
504 processJob(runningJob, url, requestSize);
510 void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url,
511 const QSize &requestSize)
514 if (url.scheme() == QLatin1String("image")) {
515 // Use QmlImageProvider
517 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
518 QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
519 if (imageType == QDeclarativeImageProvider::Invalid) {
520 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::Loading;
521 QString errorStr = QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString());
524 if (!cancelled.contains(runningJob))
525 runningJob->postReply(errorCode, errorStr, readSize, image);
527 } else if (imageType == QDeclarativeImageProvider::Image) {
528 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
529 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
531 if (image.isNull()) {
532 errorCode = QDeclarativePixmapReply::Loading;
533 errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString());
536 if (!cancelled.contains(runningJob)) {
537 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
539 runningJob->postReply(errorCode, errorStr, readSize, factory, image);
541 runningJob->postReply(errorCode, errorStr, readSize, image);
546 QDeclarativeTextureFactory *t = ep->getTextureFromProvider(url, &readSize, requestSize);
547 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
550 errorCode = QDeclarativePixmapReply::Loading;
551 errorStr = QDeclarativePixmap::tr("Failed to get texture from provider: %1").arg(url.toString());
554 if (!cancelled.contains(runningJob))
555 runningJob->postReply(errorCode, errorStr, readSize, t, QImage());
561 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
563 // Image is local - load/decode immediately
565 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
569 if (f.open(QIODevice::ReadOnly)) {
570 if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize))
571 errorCode = QDeclarativePixmapReply::Loading;
573 errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
574 errorCode = QDeclarativePixmapReply::Loading;
577 if (!cancelled.contains(runningJob)) {
578 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
580 runningJob->postReply(errorCode, errorStr, readSize, factory, image);
582 runningJob->postReply(errorCode, errorStr, readSize, image);
587 QNetworkRequest req(url);
588 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
589 QNetworkReply *reply = networkAccessManager()->get(req);
591 QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
592 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
594 replies.insert(reply, runningJob);
599 QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine)
601 // XXX NOTE: must be called within readerMutex locking.
602 QDeclarativePixmapReader *reader = readers.value(engine);
604 reader = new QDeclarativePixmapReader(engine);
605 readers.insert(engine, reader);
611 QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine)
613 // XXX NOTE: must be called within readerMutex locking.
614 return readers.value(engine, 0);
617 QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data)
620 QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data);
621 reply->engineForReader = engine;
624 if (threadObject) threadObject->processJobs();
629 void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply)
632 if (reply->loading) {
633 cancelled.append(reply);
636 if (threadObject) threadObject->processJobs();
638 jobs.removeAll(reply);
644 void QDeclarativePixmapReader::run()
646 if (replyDownloadProgress == -1) {
647 const QMetaObject *nr = &QNetworkReply::staticMetaObject;
648 const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject;
649 const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject;
650 replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
651 replyFinished = nr->indexOfSignal("finished()");
652 downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
653 threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
657 threadObject = new QDeclarativePixmapReaderThreadObject(this);
667 class QDeclarativePixmapKey
674 inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs)
676 return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
679 inline uint qHash(const QDeclarativePixmapKey &key)
681 return qHash(*key.url) ^ key.size->width() ^ key.size->height();
686 class QDeclarativePixmapStore : public QObject
690 QDeclarativePixmapStore();
691 ~QDeclarativePixmapStore();
693 void unreferencePixmap(QDeclarativePixmapData *);
694 void referencePixmap(QDeclarativePixmapData *);
697 virtual void timerEvent(QTimerEvent *);
700 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *> m_cache;
703 void shrinkCache(int remove);
705 QDeclarativePixmapData *m_unreferencedPixmaps;
706 QDeclarativePixmapData *m_lastUnreferencedPixmap;
708 int m_unreferencedCost;
711 Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore);
714 QDeclarativePixmapStore::QDeclarativePixmapStore()
715 : m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1)
719 QDeclarativePixmapStore::~QDeclarativePixmapStore()
721 int leakedPixmaps = 0;
722 QList<QDeclarativePixmapData*> cachedData = m_cache.values();
724 // unreference all (leaked) pixmaps
725 foreach (QDeclarativePixmapData* pixmap, cachedData) {
726 int currRefCount = pixmap->refCount;
729 while (currRefCount > 0) {
736 // free all unreferenced pixmaps
737 while (m_lastUnreferencedPixmap) {
742 qDebug("Number of leaked pixmaps: %i", leakedPixmaps);
745 void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data)
747 Q_ASSERT(data->prevUnreferenced == 0);
748 Q_ASSERT(data->prevUnreferencedPtr == 0);
749 Q_ASSERT(data->nextUnreferenced == 0);
751 data->nextUnreferenced = m_unreferencedPixmaps;
752 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
754 m_unreferencedPixmaps = data;
755 if (m_unreferencedPixmaps->nextUnreferenced) {
756 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
757 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
760 if (!m_lastUnreferencedPixmap)
761 m_lastUnreferencedPixmap = data;
763 m_unreferencedCost += data->cost();
765 shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
767 if (m_timerId == -1 && m_unreferencedPixmaps)
768 m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
771 void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data)
773 Q_ASSERT(data->prevUnreferencedPtr);
775 *data->prevUnreferencedPtr = data->nextUnreferenced;
776 if (data->nextUnreferenced) {
777 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
778 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
780 if (m_lastUnreferencedPixmap == data)
781 m_lastUnreferencedPixmap = data->prevUnreferenced;
783 data->nextUnreferenced = 0;
784 data->prevUnreferencedPtr = 0;
785 data->prevUnreferenced = 0;
787 m_unreferencedCost -= data->cost();
790 void QDeclarativePixmapStore::shrinkCache(int remove)
792 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
793 QDeclarativePixmapData *data = m_lastUnreferencedPixmap;
794 Q_ASSERT(data->nextUnreferenced == 0);
796 *data->prevUnreferencedPtr = 0;
797 m_lastUnreferencedPixmap = data->prevUnreferenced;
798 data->prevUnreferencedPtr = 0;
799 data->prevUnreferenced = 0;
801 remove -= data->cost();
802 m_unreferencedCost -= data->cost();
803 data->removeFromCache();
808 void QDeclarativePixmapStore::timerEvent(QTimerEvent *)
810 int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
812 shrinkCache(removalCost);
814 if (m_unreferencedPixmaps == 0) {
815 killTimer(m_timerId);
820 QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d)
821 : data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0)
823 if (finishedIndex == -1) {
824 finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()");
825 downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
829 QDeclarativePixmapReply::~QDeclarativePixmapReply()
833 bool QDeclarativePixmapReply::event(QEvent *event)
835 if (event->type() == QEvent::User) {
838 Event *de = static_cast<Event *>(event);
839 data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error;
841 if (data->pixmapStatus == QDeclarativePixmap::Ready) {
842 if (de->textureFactory) {
843 data->textureFactory = de->textureFactory;
845 data->image = de->image;
846 data->implicitSize = de->implicitSize;
848 data->errorString = de->errorString;
849 data->removeFromCache(); // We don't continue to cache error'd pixmaps
859 return QObject::event(event);
863 int QDeclarativePixmapData::cost() const
866 return textureFactory->textureByteCount();
867 return image.byteCount();
870 void QDeclarativePixmapData::addref()
873 if (prevUnreferencedPtr)
874 pixmapStore()->referencePixmap(this);
877 void QDeclarativePixmapData::release()
879 Q_ASSERT(refCount > 0);
883 QDeclarativePixmapReply *cancelReply = reply;
886 QDeclarativePixmapReader::readerMutex.lock();
887 QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader);
889 reader->cancel(cancelReply);
890 QDeclarativePixmapReader::readerMutex.unlock();
893 if (pixmapStatus == QDeclarativePixmap::Ready) {
894 pixmapStore()->unreferencePixmap(this);
902 void QDeclarativePixmapData::addToCache()
905 QDeclarativePixmapKey key = { &url, &requestSize };
906 pixmapStore()->m_cache.insert(key, this);
911 void QDeclarativePixmapData::removeFromCache()
914 QDeclarativePixmapKey key = { &url, &requestSize };
915 pixmapStore()->m_cache.remove(key);
920 static QDeclarativePixmapData* createPixmapDataSync(QDeclarativePixmap *declarativePixmap, QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
922 if (url.scheme() == QLatin1String("image")) {
924 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
925 QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
928 case QDeclarativeImageProvider::Invalid:
929 return new QDeclarativePixmapData(declarativePixmap, url, requestSize,
930 QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString()));
931 case QDeclarativeImageProvider::Texture:
933 QDeclarativeTextureFactory *texture = ep->getTextureFromProvider(url, &readSize, requestSize);
936 return new QDeclarativePixmapData(declarativePixmap, url, texture, QImage(), readSize, requestSize);
940 case QDeclarativeImageProvider::Image:
942 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
943 if (!image.isNull()) {
945 return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize);
948 case QDeclarativeImageProvider::Pixmap:
950 QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
951 if (!pixmap.isNull()) {
953 return new QDeclarativePixmapData(declarativePixmap, url, pixmap.toImage(), readSize, requestSize);
958 // provider has bad image type, or provider returned null image
959 return new QDeclarativePixmapData(declarativePixmap, url, requestSize,
960 QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
963 QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
964 if (localFile.isEmpty())
971 if (f.open(QIODevice::ReadOnly)) {
974 if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) {
976 return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize);
978 errorString = QDeclarativePixmap::tr("Invalid image data: %1").arg(url.toString());
981 errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
983 return new QDeclarativePixmapData(declarativePixmap, url, requestSize, errorString);
987 struct QDeclarativePixmapNull {
992 Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap);
994 QDeclarativePixmap::QDeclarativePixmap()
999 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url)
1005 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1008 load(engine, url, size);
1011 QDeclarativePixmap::~QDeclarativePixmap()
1014 d->declarativePixmaps.remove(this);
1020 bool QDeclarativePixmap::isNull() const
1025 bool QDeclarativePixmap::isReady() const
1027 return status() == Ready;
1030 bool QDeclarativePixmap::isError() const
1032 return status() == Error;
1035 bool QDeclarativePixmap::isLoading() const
1037 return status() == Loading;
1040 QString QDeclarativePixmap::error() const
1043 return d->errorString;
1048 QDeclarativePixmap::Status QDeclarativePixmap::status() const
1051 return d->pixmapStatus;
1056 const QUrl &QDeclarativePixmap::url() const
1061 return nullPixmap()->url;
1064 const QSize &QDeclarativePixmap::implicitSize() const
1067 return d->implicitSize;
1069 return nullPixmap()->size;
1072 const QSize &QDeclarativePixmap::requestSize() const
1075 return d->requestSize;
1077 return nullPixmap()->size;
1080 QDeclarativeTextureFactory *QDeclarativePixmap::textureFactory() const
1083 return d->textureFactory;
1088 const QImage &QDeclarativePixmap::image() const
1093 return nullPixmap()->image;
1096 void QDeclarativePixmap::setImage(const QImage &p)
1101 d = new QDeclarativePixmapData(this, p);
1104 int QDeclarativePixmap::width() const
1107 return d->textureFactory ? d->textureFactory->textureSize().width() : d->image.width();
1112 int QDeclarativePixmap::height() const
1115 return d->textureFactory ? d->textureFactory->textureSize().height() : d->image.height();
1120 QRect QDeclarativePixmap::rect() const
1123 return d->textureFactory ? QRect(QPoint(), d->textureFactory->textureSize()) : d->image.rect();
1128 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url)
1130 load(engine, url, QSize(), QDeclarativePixmap::Cache);
1133 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options)
1135 load(engine, url, QSize(), options);
1138 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1140 load(engine, url, size, QDeclarativePixmap::Cache);
1143 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options)
1146 d->declarativePixmaps.remove(this);
1151 QDeclarativePixmapKey key = { &url, &requestSize };
1152 QDeclarativePixmapStore *store = pixmapStore();
1154 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key);
1156 if (iter == store->m_cache.end()) {
1157 if (options & QDeclarativePixmap::Asynchronous) {
1158 // pixmaps can only be loaded synchronously
1159 if (url.scheme() == QLatin1String("image")
1160 && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) {
1161 options &= ~QDeclarativePixmap::Asynchronous;
1165 if (!(options & QDeclarativePixmap::Asynchronous)) {
1167 d = createPixmapDataSync(this, engine, url, requestSize, &ok);
1169 if (options & QDeclarativePixmap::Cache)
1173 if (d) // loadable, but encountered error while loading
1180 d = new QDeclarativePixmapData(this, url, requestSize);
1181 if (options & QDeclarativePixmap::Cache)
1184 QDeclarativePixmapReader::readerMutex.lock();
1185 d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d);
1186 QDeclarativePixmapReader::readerMutex.unlock();
1190 d->declarativePixmaps.insert(this);
1194 void QDeclarativePixmap::clear()
1197 d->declarativePixmaps.remove(this);
1203 void QDeclarativePixmap::clear(QObject *obj)
1207 QObject::disconnect(d->reply, 0, obj, 0);
1208 d->declarativePixmaps.remove(this);
1214 bool QDeclarativePixmap::connectFinished(QObject *object, const char *method)
1216 if (!d || !d->reply) {
1217 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1221 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
1224 bool QDeclarativePixmap::connectFinished(QObject *object, int method)
1226 if (!d || !d->reply) {
1227 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1231 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method);
1234 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method)
1236 if (!d || !d->reply) {
1237 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1241 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
1244 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method)
1246 if (!d || !d->reply) {
1247 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1251 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method);
1256 #include <qdeclarativepixmapcache.moc>