1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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 "private/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 <qsgtexture.h>
51 #include <private/qsgtexture_p.h>
52 #include <private/qsgcontext_p.h>
54 #include <QCoreApplication>
55 #include <QImageReader>
57 #include <QNetworkReply>
58 #include <QPixmapCache>
62 #include <QMutexLocker>
63 #include <QWaitCondition>
65 #include <QWaitCondition>
66 #include <QtCore/qdebug.h>
67 #include <private/qobject_p.h>
70 #define IMAGEREQUEST_MAX_REQUEST_COUNT 8
71 #define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16
72 #define CACHE_EXPIRE_TIME 30
73 #define CACHE_REMOVAL_FRACTION 4
77 // The cache limit describes the maximum "junk" in the cache.
78 // These are the same defaults as QPixmapCache
79 #if defined(Q_WS_QWS) || defined(Q_WS_WINCE)
80 static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded
82 static int cache_limit = 128 * 1024; // 10 MB cache limit for desktop
85 class QDeclarativePixmapReader;
86 class QDeclarativePixmapData;
87 class QDeclarativePixmapReply : public QObject
91 enum ReadError { NoError, Loading, Decoding };
93 QDeclarativePixmapReply(QDeclarativePixmapData *);
94 ~QDeclarativePixmapReply();
96 QDeclarativePixmapData *data;
97 QDeclarativePixmapReader *reader;
103 class Event : public QEvent {
105 Event(ReadError, const QString &, const QSize &, const QImage &image);
106 Event(ReadError, const QString &, const QSize &, QSGTexture *t, QSGContext *context, const QImage &image);
115 void postReply(ReadError, const QString &, const QSize &, const QImage &);
116 void postReply(ReadError, const QString &, const QSize &, QSGTexture *t, QSGContext *context, 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);
163 friend class QDeclarativePixmapReaderThreadObject;
165 void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &);
166 void networkRequestDone(QNetworkReply *);
168 QList<QDeclarativePixmapReply*> jobs;
169 QList<QDeclarativePixmapReply*> cancelled;
170 QDeclarativeEngine *engine;
171 QObject *eventLoopQuitHack;
174 QDeclarativePixmapReaderThreadObject *threadObject;
175 QWaitCondition waitCondition;
177 QNetworkAccessManager *networkAccessManager();
178 QNetworkAccessManager *accessManager;
180 QHash<QNetworkReply*,QDeclarativePixmapReply*> replies;
182 static int replyDownloadProgress;
183 static int replyFinished;
184 static int downloadProgress;
185 static int threadNetworkRequestDone;
186 static QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> readers;
187 static QMutex readerMutex;
190 class QDeclarativePixmapData
193 QDeclarativePixmapData(const QUrl &u, const QSize &s, const QString &e)
194 : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error),
195 url(u), errorString(e), requestSize(s), texture(0), context(0), reply(0), prevUnreferenced(0),
196 prevUnreferencedPtr(0), nextUnreferenced(0)
200 QDeclarativePixmapData(const QUrl &u, const QSize &r)
201 : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading),
202 url(u), requestSize(r), texture(0), context(0), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0),
207 QDeclarativePixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r)
208 : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
209 url(u), pixmap(p), implicitSize(s), requestSize(r), texture(0), context(0), reply(0), prevUnreferenced(0),
210 prevUnreferencedPtr(0), nextUnreferenced(0)
214 QDeclarativePixmapData(const QUrl &u, QSGTexture *t, QSGContext *c, const QPixmap &p, const QSize &s, const QSize &r)
215 : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
216 url(u), pixmap(p), implicitSize(s), requestSize(r), texture(t), context(c), reply(0), prevUnreferenced(0),
217 prevUnreferencedPtr(0), nextUnreferenced(0)
221 QDeclarativePixmapData(const QPixmap &p)
222 : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready),
223 pixmap(p), implicitSize(p.size()), requestSize(p.size()), texture(0), context(0), reply(0), prevUnreferenced(0),
224 prevUnreferencedPtr(0), nextUnreferenced(0)
228 ~QDeclarativePixmapData()
230 if (texture && context) {
231 context->scheduleTextureForCleanup(texture);
239 void removeFromCache();
244 bool privatePixmap:1;
246 QDeclarativePixmap::Status pixmapStatus;
256 QDeclarativePixmapReply *reply;
258 QDeclarativePixmapData *prevUnreferenced;
259 QDeclarativePixmapData**prevUnreferencedPtr;
260 QDeclarativePixmapData *nextUnreferenced;
263 int QDeclarativePixmapReply::finishedIndex = -1;
264 int QDeclarativePixmapReply::downloadProgressIndex = -1;
267 QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> QDeclarativePixmapReader::readers;
268 QMutex QDeclarativePixmapReader::readerMutex;
270 int QDeclarativePixmapReader::replyDownloadProgress = -1;
271 int QDeclarativePixmapReader::replyFinished = -1;
272 int QDeclarativePixmapReader::downloadProgress = -1;
273 int QDeclarativePixmapReader::threadNetworkRequestDone = -1;
276 void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
277 const QSize &implicitSize, const QImage &image)
280 QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, image));
283 void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
284 const QSize &implicitSize, QSGTexture *texture,
285 QSGContext *context, const QImage &image)
288 QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, texture, context, image));
291 QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, const QImage &i)
292 : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i), texture(0), context(0)
296 QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, QSGTexture *t, QSGContext *c, const QImage &i)
297 : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i), texture(t), context(c)
301 QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager()
303 if (!accessManager) {
304 Q_ASSERT(threadObject);
305 accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
307 return accessManager;
310 static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
311 const QSize &requestSize)
313 QImageReader imgio(dev);
315 bool force_scale = false;
316 if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) {
317 imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053
322 if (requestSize.width() > 0 || requestSize.height() > 0) {
323 QSize s = imgio.size();
324 if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
325 if (requestSize.height() <= 0)
326 s.setHeight(s.height()*requestSize.width()/s.width());
327 s.setWidth(requestSize.width()); scaled = true;
329 if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
330 if (requestSize.width() <= 0)
331 s.setWidth(s.width()*requestSize.height()/s.height());
332 s.setHeight(requestSize.height()); scaled = true;
334 if (scaled) { imgio.setScaledSize(s); }
338 *impsize = imgio.size();
340 if (imgio.read(image)) {
341 if (impsize && impsize->width() < 0)
342 *impsize = image->size();
346 *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString())
347 .arg(imgio.errorString());
352 QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng)
353 : QThread(eng), engine(eng), threadObject(0), accessManager(0)
355 eventLoopQuitHack = new QObject;
356 eventLoopQuitHack->moveToThread(this);
357 connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
358 start(QThread::IdlePriority);
361 QDeclarativePixmapReader::~QDeclarativePixmapReader()
364 readers.remove(engine);
365 readerMutex.unlock();
367 eventLoopQuitHack->deleteLater();
371 void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply)
373 QDeclarativePixmapReply *job = replies.take(reply);
376 job->redirectCount++;
377 if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
378 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
379 if (redirect.isValid()) {
380 QUrl url = reply->url().resolved(redirect.toUrl());
381 QNetworkRequest req(url);
382 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
384 reply->deleteLater();
385 reply = networkAccessManager()->get(req);
387 QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
388 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
390 replies.insert(reply, job);
396 QSGTexture *texture = 0;
397 QSGContext *ctx = QDeclarativeEnginePrivate::get(engine)->sgContext;
398 QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError;
401 if (reply->error()) {
402 error = QDeclarativePixmapReply::Loading;
403 errorString = reply->errorString();
405 QByteArray all = reply->readAll();
407 buff.open(QIODevice::ReadOnly);
408 if (ctx && ctx->canDecodeImageToTexture())
409 texture = ctx->decodeImageToTexture(&buff, &readSize, job->requestSize);
411 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize)) {
412 error = QDeclarativePixmapReply::Decoding;
414 texture = ctx->createTexture(image);
418 // send completion event to the QDeclarativePixmapReply
420 if (!cancelled.contains(job)) {
422 job->postReply(error, errorString, readSize, texture, ctx, image);
424 job->postReply(error, errorString, readSize, image);
430 reply->deleteLater();
432 // kick off event loop again incase we have dropped below max request count
433 threadObject->processJobs();
436 QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i)
441 void QDeclarativePixmapReaderThreadObject::processJobs()
443 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
446 bool QDeclarativePixmapReaderThreadObject::event(QEvent *e)
448 if (e->type() == QEvent::User) {
449 reader->processJobs();
452 return QObject::event(e);
456 void QDeclarativePixmapReaderThreadObject::networkRequestDone()
458 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
459 reader->networkRequestDone(reply);
462 void QDeclarativePixmapReader::processJobs()
464 QMutexLocker locker(&mutex);
467 if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT))
468 return; // Nothing else to do
470 // Clean cancelled jobs
471 if (cancelled.count()) {
472 for (int i = 0; i < cancelled.count(); ++i) {
473 QDeclarativePixmapReply *job = cancelled.at(i);
474 QNetworkReply *reply = replies.key(job, 0);
475 if (reply && reply->isRunning()) {
476 // cancel any jobs already started
477 replies.remove(reply);
480 // deleteLater, since not owned by this thread
486 if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
487 QDeclarativePixmapReply *runningJob = jobs.takeLast();
488 runningJob->loading = true;
490 QUrl url = runningJob->data->url;
491 QSize requestSize = runningJob->data->requestSize;
493 processJob(runningJob, url, requestSize);
499 void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url,
500 const QSize &requestSize)
502 QSGContext *sgContext = QDeclarativeEnginePrivate::get(engine)->sgContext;
505 if (url.scheme() == QLatin1String("image")) {
506 // Use QmlImageProvider
508 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
509 QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
510 if (imageType == QDeclarativeImageProvider::Invalid) {
511 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::Loading;
512 QString errorStr = QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString());
515 if (!cancelled.contains(runningJob)) {
517 runningJob->postReply(errorCode, errorStr, readSize, sgContext->createTexture(image), sgContext, image);
519 runningJob->postReply(errorCode, errorStr, readSize, image);
522 } else if (imageType == QDeclarativeImageProvider::Image) {
523 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
524 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
526 if (image.isNull()) {
527 errorCode = QDeclarativePixmapReply::Loading;
528 errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString());
531 if (!cancelled.contains(runningJob)) {
533 runningJob->postReply(errorCode, errorStr, readSize, sgContext->createTexture(image), sgContext, image);
535 runningJob->postReply(errorCode, errorStr, readSize, image);
539 QSGTexture *t = ep->getTextureFromProvider(url, &readSize, requestSize);
540 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
543 errorCode = QDeclarativePixmapReply::Loading;
544 errorStr = QDeclarativePixmap::tr("Failed to get texture from provider: %1").arg(url.toString());
547 if (!cancelled.contains(runningJob))
548 runningJob->postReply(errorCode, errorStr, readSize, t, sgContext, QImage());
554 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
556 // Image is local - load/decode immediately
558 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
562 QSGTexture *texture = 0;
563 if (f.open(QIODevice::ReadOnly)) {
564 if (sgContext && sgContext ->canDecodeImageToTexture())
565 texture = sgContext->decodeImageToTexture(&f, &readSize, requestSize);
567 if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize)) {
568 errorCode = QDeclarativePixmapReply::Loading;
569 } else if (sgContext) {
570 texture = sgContext->createTexture(image);
574 errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
575 errorCode = QDeclarativePixmapReply::Loading;
578 if (!cancelled.contains(runningJob)) {
580 runningJob->postReply(errorCode, errorStr, readSize, texture, sgContext, image);
582 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)
604 QDeclarativePixmapReader *reader = readers.value(engine);
606 reader = new QDeclarativePixmapReader(engine);
607 readers.insert(engine, reader);
609 readerMutex.unlock();
614 QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data)
617 QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data);
618 reply->reader = this;
621 if (threadObject) threadObject->processJobs();
626 void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply)
629 if (reply->loading) {
630 cancelled.append(reply);
633 if (threadObject) threadObject->processJobs();
635 jobs.removeAll(reply);
641 void QDeclarativePixmapReader::run()
643 if (replyDownloadProgress == -1) {
644 const QMetaObject *nr = &QNetworkReply::staticMetaObject;
645 const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject;
646 const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject;
647 replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
648 replyFinished = nr->indexOfSignal("finished()");
649 downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
650 threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
654 threadObject = new QDeclarativePixmapReaderThreadObject(this);
664 class QDeclarativePixmapKey
671 inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs)
673 return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
676 inline uint qHash(const QDeclarativePixmapKey &key)
678 return qHash(*key.url) ^ key.size->width() ^ key.size->height();
683 class QDeclarativePixmapStore : public QObject
687 QDeclarativePixmapStore();
689 void unreferencePixmap(QDeclarativePixmapData *);
690 void referencePixmap(QDeclarativePixmapData *);
693 virtual void timerEvent(QTimerEvent *);
696 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *> m_cache;
698 void cleanTexturesForContext(QSGContext *context);
699 void cleanTextureForContext(QDeclarativePixmapData *data);
702 void shrinkCache(int remove);
704 QDeclarativePixmapData *m_unreferencedPixmaps;
705 QDeclarativePixmapData *m_lastUnreferencedPixmap;
707 int m_unreferencedCost;
710 Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore);
713 void qt_declarative_pixmapstore_clean(QSGContext *context)
715 pixmapStore()->cleanTexturesForContext(context);
719 QDeclarativePixmapStore::QDeclarativePixmapStore()
720 : m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1)
724 void QDeclarativePixmapStore::cleanTextureForContext(QDeclarativePixmapData *data)
727 Q_ASSERT(QGLContext::currentContext());
728 delete data->texture;
734 void QDeclarativePixmapStore::cleanTexturesForContext(QSGContext *context)
736 QDeclarativePixmapData *data = m_unreferencedPixmaps;
738 if (data->context == context)
739 cleanTextureForContext(data);
740 if (data == m_lastUnreferencedPixmap)
742 data = data->nextUnreferenced;
745 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::iterator it = m_cache.begin();
746 while (it != m_cache.end()) {
748 if (data->context == context) {
749 cleanTextureForContext(data);
758 void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data)
760 Q_ASSERT(data->prevUnreferenced == 0);
761 Q_ASSERT(data->prevUnreferencedPtr == 0);
762 Q_ASSERT(data->nextUnreferenced == 0);
764 data->nextUnreferenced = m_unreferencedPixmaps;
765 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
767 m_unreferencedPixmaps = data;
768 if (m_unreferencedPixmaps->nextUnreferenced) {
769 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
770 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
773 if (!m_lastUnreferencedPixmap)
774 m_lastUnreferencedPixmap = data;
776 m_unreferencedCost += data->cost();
778 shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
780 if (m_timerId == -1 && m_unreferencedPixmaps)
781 m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
784 void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data)
786 Q_ASSERT(data->prevUnreferencedPtr);
788 *data->prevUnreferencedPtr = data->nextUnreferenced;
789 if (data->nextUnreferenced) {
790 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
791 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
793 if (m_lastUnreferencedPixmap == data)
794 m_lastUnreferencedPixmap = data->prevUnreferenced;
796 data->nextUnreferenced = 0;
797 data->prevUnreferencedPtr = 0;
798 data->prevUnreferenced = 0;
800 m_unreferencedCost -= data->cost();
803 void QDeclarativePixmapStore::shrinkCache(int remove)
805 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
806 QDeclarativePixmapData *data = m_lastUnreferencedPixmap;
807 Q_ASSERT(data->nextUnreferenced == 0);
809 *data->prevUnreferencedPtr = 0;
810 m_lastUnreferencedPixmap = data->prevUnreferenced;
811 data->prevUnreferencedPtr = 0;
812 data->prevUnreferenced = 0;
814 remove -= data->cost();
815 m_unreferencedCost -= data->cost();
816 data->removeFromCache();
821 void QDeclarativePixmapStore::timerEvent(QTimerEvent *)
823 int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
825 shrinkCache(removalCost);
827 if (m_unreferencedPixmaps == 0) {
828 killTimer(m_timerId);
833 QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d)
834 : data(d), reader(0), requestSize(d->requestSize), loading(false), redirectCount(0)
836 if (finishedIndex == -1) {
837 finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()");
838 downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
842 QDeclarativePixmapReply::~QDeclarativePixmapReply()
846 bool QDeclarativePixmapReply::event(QEvent *event)
848 if (event->type() == QEvent::User) {
851 Event *de = static_cast<Event *>(event);
852 data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error;
854 if (data->pixmapStatus == QDeclarativePixmap::Ready) {
856 data->texture = de->texture;
857 data->context = de->context;
859 data->pixmap = QPixmap::fromImage(de->image);
860 data->implicitSize = de->implicitSize;
862 data->errorString = de->errorString;
863 data->removeFromCache(); // We don't continue to cache error'd pixmaps
873 return QObject::event(event);
877 int QDeclarativePixmapData::cost() const
880 const QSize textureSize = texture->textureSize();
881 return textureSize.width() * textureSize.height();
883 return (pixmap.width() * pixmap.height() * pixmap.depth()) / 8;
886 void QDeclarativePixmapData::addref()
889 if (prevUnreferencedPtr)
890 pixmapStore()->referencePixmap(this);
893 void QDeclarativePixmapData::release()
895 Q_ASSERT(refCount > 0);
900 reply->reader->cancel(reply);
904 if (pixmapStatus == QDeclarativePixmap::Ready) {
905 pixmapStore()->unreferencePixmap(this);
913 void QDeclarativePixmapData::addToCache()
916 QDeclarativePixmapKey key = { &url, &requestSize };
917 pixmapStore()->m_cache.insert(key, this);
922 void QDeclarativePixmapData::removeFromCache()
925 QDeclarativePixmapKey key = { &url, &requestSize };
926 pixmapStore()->m_cache.remove(key);
931 static QDeclarativePixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
933 QSGContext *sgContext = QDeclarativeEnginePrivate::get(engine)->sgContext;
935 if (url.scheme() == QLatin1String("image")) {
937 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
938 QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
941 case QDeclarativeImageProvider::Invalid:
942 return new QDeclarativePixmapData(url, requestSize,
943 QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString()));
944 case QDeclarativeImageProvider::Texture:
946 QSGTexture *texture = ep->getTextureFromProvider(url, &readSize, requestSize);
949 return new QDeclarativePixmapData(url, texture, sgContext, QPixmap(), readSize, requestSize);
953 case QDeclarativeImageProvider::Image:
955 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
956 if (!image.isNull()) {
959 QSGTexture *t = sgContext->createTexture(image);
960 return new QDeclarativePixmapData(url, t, sgContext, QPixmap::fromImage(image), readSize, requestSize);
962 return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
965 case QDeclarativeImageProvider::Pixmap:
967 QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
968 if (!pixmap.isNull()) {
971 QSGTexture *t = sgContext->createTexture(pixmap.toImage());
972 return new QDeclarativePixmapData(url, t, sgContext, pixmap, readSize, requestSize);
974 return new QDeclarativePixmapData(url, pixmap, readSize, requestSize);
979 // provider has bad image type, or provider returned null image
980 return new QDeclarativePixmapData(url, requestSize,
981 QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
984 QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
985 if (localFile.isEmpty())
992 if (f.open(QIODevice::ReadOnly)) {
993 QSGContext *ctx = QDeclarativeEnginePrivate::get(engine)->sgContext;
994 QSGTexture *texture = 0;
997 if (ctx && ctx->canDecodeImageToTexture()) {
998 texture = ctx->decodeImageToTexture(&f, &readSize, requestSize);
1003 if (readImage(url, &f, &image, &errorString, &readSize, requestSize))
1007 texture = ctx->createTexture(image);
1012 return new QDeclarativePixmapData(url, texture, ctx, QPixmap::fromImage(image), readSize, requestSize);
1014 return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
1017 errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
1019 return new QDeclarativePixmapData(url, requestSize, errorString);
1023 struct QDeclarativePixmapNull {
1028 Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap);
1030 QDeclarativePixmap::QDeclarativePixmap()
1035 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url)
1041 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1044 load(engine, url, size);
1047 QDeclarativePixmap::~QDeclarativePixmap()
1055 bool QDeclarativePixmap::isNull() const
1060 bool QDeclarativePixmap::isReady() const
1062 return status() == Ready;
1065 bool QDeclarativePixmap::isError() const
1067 return status() == Error;
1070 bool QDeclarativePixmap::isLoading() const
1072 return status() == Loading;
1075 QString QDeclarativePixmap::error() const
1078 return d->errorString;
1083 QDeclarativePixmap::Status QDeclarativePixmap::status() const
1086 return d->pixmapStatus;
1091 const QUrl &QDeclarativePixmap::url() const
1096 return nullPixmap()->url;
1099 const QSize &QDeclarativePixmap::implicitSize() const
1102 return d->implicitSize;
1104 return nullPixmap()->size;
1107 const QSize &QDeclarativePixmap::requestSize() const
1110 return d->requestSize;
1112 return nullPixmap()->size;
1115 QSGTexture *QDeclarativePixmap::texture(QSGContext *context) const
1120 else if (d->pixmapStatus == Ready) {
1121 d->texture = context->createTexture(d->pixmap.toImage());
1128 const QPixmap &QDeclarativePixmap::pixmap() const
1133 return nullPixmap()->pixmap;
1136 void QDeclarativePixmap::setPixmap(const QPixmap &p)
1141 d = new QDeclarativePixmapData(p);
1144 int QDeclarativePixmap::width() const
1147 return d->texture ? d->texture->textureSize().width() : d->pixmap.width();
1152 int QDeclarativePixmap::height() const
1155 return d->texture? d->texture->textureSize().height() : d->pixmap.height();
1160 QRect QDeclarativePixmap::rect() const
1163 return d->texture ? QRect(QPoint(), d->texture->textureSize()) : d->pixmap.rect();
1168 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url)
1170 load(engine, url, QSize(), QDeclarativePixmap::Cache);
1173 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options)
1175 load(engine, url, QSize(), options);
1178 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1180 load(engine, url, size, QDeclarativePixmap::Cache);
1183 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options)
1185 if (d) { d->release(); d = 0; }
1187 QDeclarativePixmapKey key = { &url, &requestSize };
1188 QDeclarativePixmapStore *store = pixmapStore();
1190 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key);
1192 if (iter == store->m_cache.end()) {
1193 if (options & QDeclarativePixmap::Asynchronous) {
1194 // pixmaps can only be loaded synchronously
1195 if (url.scheme() == QLatin1String("image")
1196 && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) {
1197 options &= ~QDeclarativePixmap::Asynchronous;
1201 if (!(options & QDeclarativePixmap::Asynchronous)) {
1203 d = createPixmapDataSync(engine, url, requestSize, &ok);
1205 if (options & QDeclarativePixmap::Cache)
1209 if (d) // loadable, but encountered error while loading
1216 QDeclarativePixmapReader *reader = QDeclarativePixmapReader::instance(engine);
1218 d = new QDeclarativePixmapData(url, requestSize);
1219 if (options & QDeclarativePixmap::Cache)
1222 d->reply = reader->getImage(d);
1229 void QDeclarativePixmap::clear()
1237 void QDeclarativePixmap::clear(QObject *obj)
1241 QObject::disconnect(d->reply, 0, obj, 0);
1247 bool QDeclarativePixmap::connectFinished(QObject *object, const char *method)
1249 if (!d || !d->reply) {
1250 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1254 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
1257 bool QDeclarativePixmap::connectFinished(QObject *object, int method)
1259 if (!d || !d->reply) {
1260 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1264 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method);
1267 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method)
1269 if (!d || !d->reply) {
1270 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1274 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
1277 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method)
1279 if (!d || !d->reply) {
1280 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1284 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method);
1289 #include <qdeclarativepixmapcache.moc>