1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** 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(QQuickCanvas *) 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
326 if (requestSize.width() > 0 || requestSize.height() > 0) {
327 QSize s = imgio.size();
329 if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
330 ratio = qreal(requestSize.width())/s.width();
332 if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
333 qreal hr = qreal(requestSize.height())/s.height();
334 if (ratio == 0.0 || hr < ratio)
338 s.setHeight(qRound(s.height() * ratio));
339 s.setWidth(qRound(s.width() * ratio));
340 imgio.setScaledSize(s);
345 *impsize = imgio.size();
347 if (imgio.read(image)) {
348 if (impsize && impsize->width() < 0)
349 *impsize = image->size();
353 *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString())
354 .arg(imgio.errorString());
359 QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng)
360 : QThread(eng), engine(eng), threadObject(0), accessManager(0)
362 eventLoopQuitHack = new QObject;
363 eventLoopQuitHack->moveToThread(this);
364 connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
365 start(QThread::LowestPriority);
368 QDeclarativePixmapReader::~QDeclarativePixmapReader()
371 readers.remove(engine);
372 readerMutex.unlock();
375 // manually cancel all outstanding jobs.
376 foreach (QDeclarativePixmapReply *reply, jobs) {
380 QList<QDeclarativePixmapReply*> activeJobs = replies.values();
381 foreach (QDeclarativePixmapReply *reply, activeJobs) {
382 if (reply->loading) {
383 cancelled.append(reply);
387 if (threadObject) threadObject->processJobs();
390 eventLoopQuitHack->deleteLater();
394 void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply)
396 QDeclarativePixmapReply *job = replies.take(reply);
399 job->redirectCount++;
400 if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
401 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
402 if (redirect.isValid()) {
403 QUrl url = reply->url().resolved(redirect.toUrl());
404 QNetworkRequest req(url);
405 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
407 reply->deleteLater();
408 reply = networkAccessManager()->get(req);
410 QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
411 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
413 replies.insert(reply, job);
419 QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError;
422 if (reply->error()) {
423 error = QDeclarativePixmapReply::Loading;
424 errorString = reply->errorString();
426 QByteArray all = reply->readAll();
428 buff.open(QIODevice::ReadOnly);
429 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize))
430 error = QDeclarativePixmapReply::Decoding;
432 // send completion event to the QDeclarativePixmapReply
434 if (!cancelled.contains(job)) {
435 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
437 job->postReply(error, errorString, readSize, factory, image);
439 job->postReply(error, errorString, readSize, image);
443 reply->deleteLater();
445 // kick off event loop again incase we have dropped below max request count
446 threadObject->processJobs();
449 QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i)
454 void QDeclarativePixmapReaderThreadObject::processJobs()
456 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
459 bool QDeclarativePixmapReaderThreadObject::event(QEvent *e)
461 if (e->type() == QEvent::User) {
462 reader->processJobs();
465 return QObject::event(e);
469 void QDeclarativePixmapReaderThreadObject::networkRequestDone()
471 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
472 reader->networkRequestDone(reply);
475 void QDeclarativePixmapReader::processJobs()
477 QMutexLocker locker(&mutex);
480 if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT))
481 return; // Nothing else to do
483 // Clean cancelled jobs
484 if (cancelled.count()) {
485 for (int i = 0; i < cancelled.count(); ++i) {
486 QDeclarativePixmapReply *job = cancelled.at(i);
487 QNetworkReply *reply = replies.key(job, 0);
488 if (reply && reply->isRunning()) {
489 // cancel any jobs already started
490 replies.remove(reply);
493 // deleteLater, since not owned by this thread
499 if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
500 QDeclarativePixmapReply *runningJob = jobs.takeLast();
501 runningJob->loading = true;
503 QUrl url = runningJob->url;
504 QSize requestSize = runningJob->requestSize;
506 processJob(runningJob, url, requestSize);
512 void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url,
513 const QSize &requestSize)
516 if (url.scheme() == QLatin1String("image")) {
517 // Use QmlImageProvider
519 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
520 QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
521 if (imageType == QDeclarativeImageProvider::Invalid) {
522 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::Loading;
523 QString errorStr = QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString());
526 if (!cancelled.contains(runningJob))
527 runningJob->postReply(errorCode, errorStr, readSize, image);
529 } else if (imageType == QDeclarativeImageProvider::Image) {
530 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
531 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
533 if (image.isNull()) {
534 errorCode = QDeclarativePixmapReply::Loading;
535 errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString());
538 if (!cancelled.contains(runningJob)) {
539 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
541 runningJob->postReply(errorCode, errorStr, readSize, factory, image);
543 runningJob->postReply(errorCode, errorStr, readSize, image);
548 QDeclarativeTextureFactory *t = ep->getTextureFromProvider(url, &readSize, requestSize);
549 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
552 errorCode = QDeclarativePixmapReply::Loading;
553 errorStr = QDeclarativePixmap::tr("Failed to get texture from provider: %1").arg(url.toString());
556 if (!cancelled.contains(runningJob))
557 runningJob->postReply(errorCode, errorStr, readSize, t, QImage());
563 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
565 // Image is local - load/decode immediately
567 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
571 if (f.open(QIODevice::ReadOnly)) {
572 if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize))
573 errorCode = QDeclarativePixmapReply::Loading;
575 errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
576 errorCode = QDeclarativePixmapReply::Loading;
579 if (!cancelled.contains(runningJob)) {
580 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
582 runningJob->postReply(errorCode, errorStr, readSize, factory, image);
584 runningJob->postReply(errorCode, errorStr, readSize, image);
589 QNetworkRequest req(url);
590 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
591 QNetworkReply *reply = networkAccessManager()->get(req);
593 QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
594 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
596 replies.insert(reply, runningJob);
601 QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine)
603 // XXX NOTE: must be called within readerMutex locking.
604 QDeclarativePixmapReader *reader = readers.value(engine);
606 reader = new QDeclarativePixmapReader(engine);
607 readers.insert(engine, reader);
613 QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine)
615 // XXX NOTE: must be called within readerMutex locking.
616 return readers.value(engine, 0);
619 QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data)
622 QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data);
623 reply->engineForReader = engine;
626 if (threadObject) threadObject->processJobs();
631 void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply)
634 if (reply->loading) {
635 cancelled.append(reply);
638 if (threadObject) threadObject->processJobs();
640 jobs.removeAll(reply);
646 void QDeclarativePixmapReader::run()
648 if (replyDownloadProgress == -1) {
649 const QMetaObject *nr = &QNetworkReply::staticMetaObject;
650 const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject;
651 const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject;
652 replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
653 replyFinished = nr->indexOfSignal("finished()");
654 downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
655 threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
659 threadObject = new QDeclarativePixmapReaderThreadObject(this);
669 class QDeclarativePixmapKey
676 inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs)
678 return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
681 inline uint qHash(const QDeclarativePixmapKey &key)
683 return qHash(*key.url) ^ key.size->width() ^ key.size->height();
688 class QDeclarativePixmapStore : public QObject
692 QDeclarativePixmapStore();
693 ~QDeclarativePixmapStore();
695 void unreferencePixmap(QDeclarativePixmapData *);
696 void referencePixmap(QDeclarativePixmapData *);
699 virtual void timerEvent(QTimerEvent *);
702 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *> m_cache;
705 void shrinkCache(int remove);
707 QDeclarativePixmapData *m_unreferencedPixmaps;
708 QDeclarativePixmapData *m_lastUnreferencedPixmap;
710 int m_unreferencedCost;
714 Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore);
717 QDeclarativePixmapStore::QDeclarativePixmapStore()
718 : m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1), m_destroying(false)
722 QDeclarativePixmapStore::~QDeclarativePixmapStore()
726 int leakedPixmaps = 0;
727 QList<QDeclarativePixmapData*> cachedData = m_cache.values();
729 // Prevent unreferencePixmap() from assuming it needs to kick
730 // off the cache expiry timer, as we're shrinking the cache
731 // manually below after releasing all the pixmaps.
734 // unreference all (leaked) pixmaps
735 foreach (QDeclarativePixmapData* pixmap, cachedData) {
736 int currRefCount = pixmap->refCount;
739 while (currRefCount > 0) {
746 // free all unreferenced pixmaps
747 while (m_lastUnreferencedPixmap) {
752 qDebug("Number of leaked pixmaps: %i", leakedPixmaps);
755 void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data)
757 Q_ASSERT(data->prevUnreferenced == 0);
758 Q_ASSERT(data->prevUnreferencedPtr == 0);
759 Q_ASSERT(data->nextUnreferenced == 0);
761 data->nextUnreferenced = m_unreferencedPixmaps;
762 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
764 m_unreferencedPixmaps = data;
765 if (m_unreferencedPixmaps->nextUnreferenced) {
766 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
767 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
770 if (!m_lastUnreferencedPixmap)
771 m_lastUnreferencedPixmap = data;
773 m_unreferencedCost += data->cost();
775 shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
777 if (m_timerId == -1 && m_unreferencedPixmaps && !m_destroying)
778 m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
781 void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data)
783 Q_ASSERT(data->prevUnreferencedPtr);
785 *data->prevUnreferencedPtr = data->nextUnreferenced;
786 if (data->nextUnreferenced) {
787 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
788 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
790 if (m_lastUnreferencedPixmap == data)
791 m_lastUnreferencedPixmap = data->prevUnreferenced;
793 data->nextUnreferenced = 0;
794 data->prevUnreferencedPtr = 0;
795 data->prevUnreferenced = 0;
797 m_unreferencedCost -= data->cost();
800 void QDeclarativePixmapStore::shrinkCache(int remove)
802 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
803 QDeclarativePixmapData *data = m_lastUnreferencedPixmap;
804 Q_ASSERT(data->nextUnreferenced == 0);
806 *data->prevUnreferencedPtr = 0;
807 m_lastUnreferencedPixmap = data->prevUnreferenced;
808 data->prevUnreferencedPtr = 0;
809 data->prevUnreferenced = 0;
811 remove -= data->cost();
812 m_unreferencedCost -= data->cost();
813 data->removeFromCache();
818 void QDeclarativePixmapStore::timerEvent(QTimerEvent *)
820 int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
822 shrinkCache(removalCost);
824 if (m_unreferencedPixmaps == 0) {
825 killTimer(m_timerId);
830 QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d)
831 : data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0)
833 if (finishedIndex == -1) {
834 finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()");
835 downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
839 QDeclarativePixmapReply::~QDeclarativePixmapReply()
843 bool QDeclarativePixmapReply::event(QEvent *event)
845 if (event->type() == QEvent::User) {
848 Event *de = static_cast<Event *>(event);
849 data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error;
851 if (data->pixmapStatus == QDeclarativePixmap::Ready) {
852 if (de->textureFactory) {
853 data->textureFactory = de->textureFactory;
855 data->image = de->image;
856 data->implicitSize = de->implicitSize;
858 data->errorString = de->errorString;
859 data->removeFromCache(); // We don't continue to cache error'd pixmaps
869 return QObject::event(event);
873 int QDeclarativePixmapData::cost() const
876 return textureFactory->textureByteCount();
877 return image.byteCount();
880 void QDeclarativePixmapData::addref()
883 if (prevUnreferencedPtr)
884 pixmapStore()->referencePixmap(this);
887 void QDeclarativePixmapData::release()
889 Q_ASSERT(refCount > 0);
893 QDeclarativePixmapReply *cancelReply = reply;
896 QDeclarativePixmapReader::readerMutex.lock();
897 QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader);
899 reader->cancel(cancelReply);
900 QDeclarativePixmapReader::readerMutex.unlock();
903 if (pixmapStatus == QDeclarativePixmap::Ready) {
904 pixmapStore()->unreferencePixmap(this);
912 void QDeclarativePixmapData::addToCache()
915 QDeclarativePixmapKey key = { &url, &requestSize };
916 pixmapStore()->m_cache.insert(key, this);
921 void QDeclarativePixmapData::removeFromCache()
924 QDeclarativePixmapKey key = { &url, &requestSize };
925 pixmapStore()->m_cache.remove(key);
930 static QDeclarativePixmapData* createPixmapDataSync(QDeclarativePixmap *declarativePixmap, QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
932 if (url.scheme() == QLatin1String("image")) {
934 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
935 QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
938 case QDeclarativeImageProvider::Invalid:
939 return new QDeclarativePixmapData(declarativePixmap, url, requestSize,
940 QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString()));
941 case QDeclarativeImageProvider::Texture:
943 QDeclarativeTextureFactory *texture = ep->getTextureFromProvider(url, &readSize, requestSize);
946 return new QDeclarativePixmapData(declarativePixmap, url, texture, QImage(), readSize, requestSize);
950 case QDeclarativeImageProvider::Image:
952 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
953 if (!image.isNull()) {
955 return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize);
958 case QDeclarativeImageProvider::Pixmap:
960 QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
961 if (!pixmap.isNull()) {
963 return new QDeclarativePixmapData(declarativePixmap, url, pixmap.toImage(), readSize, requestSize);
968 // provider has bad image type, or provider returned null image
969 return new QDeclarativePixmapData(declarativePixmap, url, requestSize,
970 QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
973 QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
974 if (localFile.isEmpty())
981 if (f.open(QIODevice::ReadOnly)) {
984 if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) {
986 return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize);
988 errorString = QDeclarativePixmap::tr("Invalid image data: %1").arg(url.toString());
991 errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
993 return new QDeclarativePixmapData(declarativePixmap, url, requestSize, errorString);
997 struct QDeclarativePixmapNull {
1002 Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap);
1004 QDeclarativePixmap::QDeclarativePixmap()
1009 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url)
1015 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1018 load(engine, url, size);
1021 QDeclarativePixmap::~QDeclarativePixmap()
1024 d->declarativePixmaps.remove(this);
1030 bool QDeclarativePixmap::isNull() const
1035 bool QDeclarativePixmap::isReady() const
1037 return status() == Ready;
1040 bool QDeclarativePixmap::isError() const
1042 return status() == Error;
1045 bool QDeclarativePixmap::isLoading() const
1047 return status() == Loading;
1050 QString QDeclarativePixmap::error() const
1053 return d->errorString;
1058 QDeclarativePixmap::Status QDeclarativePixmap::status() const
1061 return d->pixmapStatus;
1066 const QUrl &QDeclarativePixmap::url() const
1071 return nullPixmap()->url;
1074 const QSize &QDeclarativePixmap::implicitSize() const
1077 return d->implicitSize;
1079 return nullPixmap()->size;
1082 const QSize &QDeclarativePixmap::requestSize() const
1085 return d->requestSize;
1087 return nullPixmap()->size;
1090 QDeclarativeTextureFactory *QDeclarativePixmap::textureFactory() const
1093 return d->textureFactory;
1098 const QImage &QDeclarativePixmap::image() const
1103 return nullPixmap()->image;
1106 void QDeclarativePixmap::setImage(const QImage &p)
1111 d = new QDeclarativePixmapData(this, p);
1114 int QDeclarativePixmap::width() const
1117 return d->textureFactory ? d->textureFactory->textureSize().width() : d->image.width();
1122 int QDeclarativePixmap::height() const
1125 return d->textureFactory ? d->textureFactory->textureSize().height() : d->image.height();
1130 QRect QDeclarativePixmap::rect() const
1133 return d->textureFactory ? QRect(QPoint(), d->textureFactory->textureSize()) : d->image.rect();
1138 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url)
1140 load(engine, url, QSize(), QDeclarativePixmap::Cache);
1143 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options)
1145 load(engine, url, QSize(), options);
1148 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1150 load(engine, url, size, QDeclarativePixmap::Cache);
1153 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options)
1156 d->declarativePixmaps.remove(this);
1161 QDeclarativePixmapKey key = { &url, &requestSize };
1162 QDeclarativePixmapStore *store = pixmapStore();
1164 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key);
1166 if (iter == store->m_cache.end()) {
1167 if (options & QDeclarativePixmap::Asynchronous) {
1168 // pixmaps can only be loaded synchronously
1169 if (url.scheme() == QLatin1String("image")
1170 && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) {
1171 options &= ~QDeclarativePixmap::Asynchronous;
1175 if (!(options & QDeclarativePixmap::Asynchronous)) {
1177 d = createPixmapDataSync(this, engine, url, requestSize, &ok);
1179 if (options & QDeclarativePixmap::Cache)
1183 if (d) // loadable, but encountered error while loading
1190 d = new QDeclarativePixmapData(this, url, requestSize);
1191 if (options & QDeclarativePixmap::Cache)
1194 QDeclarativePixmapReader::readerMutex.lock();
1195 d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d);
1196 QDeclarativePixmapReader::readerMutex.unlock();
1200 d->declarativePixmaps.insert(this);
1204 void QDeclarativePixmap::clear()
1207 d->declarativePixmaps.remove(this);
1213 void QDeclarativePixmap::clear(QObject *obj)
1217 QObject::disconnect(d->reply, 0, obj, 0);
1218 d->declarativePixmaps.remove(this);
1224 bool QDeclarativePixmap::connectFinished(QObject *object, const char *method)
1226 if (!d || !d->reply) {
1227 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1231 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
1234 bool QDeclarativePixmap::connectFinished(QObject *object, int method)
1236 if (!d || !d->reply) {
1237 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1241 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method);
1244 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method)
1246 if (!d || !d->reply) {
1247 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1251 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
1254 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method)
1256 if (!d || !d->reply) {
1257 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1261 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method);
1266 #include <qdeclarativepixmapcache.moc>