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 QtQml 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 "qquickpixmapcache_p.h"
43 #include <qqmlnetworkaccessmanagerfactory.h>
44 #include <qqmlimageprovider.h>
46 #include <qqmlengine.h>
47 #include <private/qqmlglobal_p.h>
48 #include <private/qqmlengine_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 *QQuickDefaultTextureFactory::createTexture(QQuickCanvas *) const
81 QSGPlainTexture *t = new QSGPlainTexture();
86 static QQuickTextureFactory *textureFactoryForImage(const QImage &image)
90 QQuickTextureFactory *texture = QSGContext::createTextureFactoryFromImage(image);
93 return new QQuickDefaultTextureFactory(image);
96 class QQuickPixmapReader;
97 class QQuickPixmapData;
98 class QQuickPixmapReply : public QObject
102 enum ReadError { NoError, Loading, Decoding };
104 QQuickPixmapReply(QQuickPixmapData *);
105 ~QQuickPixmapReply();
107 QQuickPixmapData *data;
108 QQmlEngine *engineForReader; // always access reader inside readerMutex
115 class Event : public QEvent {
117 Event(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory);
122 QQuickTextureFactory *textureFactory;
124 void postReply(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory);
129 void downloadProgress(qint64, qint64);
132 bool event(QEvent *event);
135 Q_DISABLE_COPY(QQuickPixmapReply)
138 static int finishedIndex;
139 static int downloadProgressIndex;
142 class QQuickPixmapReaderThreadObject : public QObject {
145 QQuickPixmapReaderThreadObject(QQuickPixmapReader *);
147 virtual bool event(QEvent *e);
149 void networkRequestDone();
151 QQuickPixmapReader *reader;
154 class QQuickPixmapData;
155 class QQuickPixmapReader : public QThread
159 QQuickPixmapReader(QQmlEngine *eng);
160 ~QQuickPixmapReader();
162 QQuickPixmapReply *getImage(QQuickPixmapData *);
163 void cancel(QQuickPixmapReply *rep);
165 static QQuickPixmapReader *instance(QQmlEngine *engine);
166 static QQuickPixmapReader *existingInstance(QQmlEngine *engine);
172 friend class QQuickPixmapReaderThreadObject;
174 void processJob(QQuickPixmapReply *, const QUrl &, const QSize &);
175 void networkRequestDone(QNetworkReply *);
177 QList<QQuickPixmapReply*> jobs;
178 QList<QQuickPixmapReply*> cancelled;
180 QObject *eventLoopQuitHack;
183 QQuickPixmapReaderThreadObject *threadObject;
184 QWaitCondition waitCondition;
186 QNetworkAccessManager *networkAccessManager();
187 QNetworkAccessManager *accessManager;
189 QHash<QNetworkReply*,QQuickPixmapReply*> replies;
191 static int replyDownloadProgress;
192 static int replyFinished;
193 static int downloadProgress;
194 static int threadNetworkRequestDone;
195 static QHash<QQmlEngine *,QQuickPixmapReader*> readers;
197 static QMutex readerMutex;
200 class QQuickPixmapData
203 QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &s, const QString &e)
204 : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Error),
205 url(u), errorString(e), requestSize(s), textureFactory(0), reply(0), prevUnreferenced(0),
206 prevUnreferencedPtr(0), nextUnreferenced(0)
208 declarativePixmaps.insert(pixmap);
211 QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &r)
212 : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Loading),
213 url(u), requestSize(r), textureFactory(0), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0),
216 declarativePixmaps.insert(pixmap);
219 QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, QQuickTextureFactory *texture, const QSize &s, const QSize &r)
220 : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QQuickPixmap::Ready),
221 url(u), implicitSize(s), requestSize(r), textureFactory(texture), reply(0), prevUnreferenced(0),
222 prevUnreferencedPtr(0), nextUnreferenced(0)
224 declarativePixmaps.insert(pixmap);
227 QQuickPixmapData(QQuickPixmap *pixmap, QQuickTextureFactory *texture)
228 : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QQuickPixmap::Ready),
229 textureFactory(texture), reply(0), prevUnreferenced(0),
230 prevUnreferencedPtr(0), nextUnreferenced(0)
233 requestSize = implicitSize = texture->textureSize();
234 declarativePixmaps.insert(pixmap);
239 while (!declarativePixmaps.isEmpty()) {
240 QQuickPixmap *referencer = declarativePixmaps.first();
241 declarativePixmaps.remove(referencer);
244 delete textureFactory;
251 void removeFromCache();
256 bool privatePixmap:1;
258 QQuickPixmap::Status pixmapStatus;
264 QQuickTextureFactory *textureFactory;
266 QIntrusiveList<QQuickPixmap, &QQuickPixmap::dataListNode> declarativePixmaps;
267 QQuickPixmapReply *reply;
269 QQuickPixmapData *prevUnreferenced;
270 QQuickPixmapData**prevUnreferencedPtr;
271 QQuickPixmapData *nextUnreferenced;
274 int QQuickPixmapReply::finishedIndex = -1;
275 int QQuickPixmapReply::downloadProgressIndex = -1;
278 QHash<QQmlEngine *,QQuickPixmapReader*> QQuickPixmapReader::readers;
279 QMutex QQuickPixmapReader::readerMutex;
281 int QQuickPixmapReader::replyDownloadProgress = -1;
282 int QQuickPixmapReader::replyFinished = -1;
283 int QQuickPixmapReader::downloadProgress = -1;
284 int QQuickPixmapReader::threadNetworkRequestDone = -1;
287 void QQuickPixmapReply::postReply(ReadError error, const QString &errorString,
288 const QSize &implicitSize, QQuickTextureFactory *factory)
291 QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, factory));
294 QQuickPixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, QQuickTextureFactory *factory)
295 : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), textureFactory(factory)
299 QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
301 if (!accessManager) {
302 Q_ASSERT(threadObject);
303 accessManager = QQmlEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
305 return accessManager;
308 static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
309 const QSize &requestSize)
311 QImageReader imgio(dev);
313 bool force_scale = false;
314 if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) {
315 imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053
319 if (requestSize.width() > 0 || requestSize.height() > 0) {
320 QSize s = imgio.size();
322 if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
323 ratio = qreal(requestSize.width())/s.width();
325 if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
326 qreal hr = qreal(requestSize.height())/s.height();
327 if (ratio == 0.0 || hr < ratio)
331 s.setHeight(qRound(s.height() * ratio));
332 s.setWidth(qRound(s.width() * ratio));
333 imgio.setScaledSize(s);
338 *impsize = imgio.size();
340 if (imgio.read(image)) {
341 if (impsize && impsize->width() < 0)
342 *impsize = image->size();
346 *errorString = QQuickPixmap::tr("Error decoding: %1: %2").arg(url.toString())
347 .arg(imgio.errorString());
352 QQuickPixmapReader::QQuickPixmapReader(QQmlEngine *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::LowestPriority);
361 QQuickPixmapReader::~QQuickPixmapReader()
364 readers.remove(engine);
365 readerMutex.unlock();
368 // manually cancel all outstanding jobs.
369 foreach (QQuickPixmapReply *reply, jobs) {
373 QList<QQuickPixmapReply*> activeJobs = replies.values();
374 foreach (QQuickPixmapReply *reply, activeJobs) {
375 if (reply->loading) {
376 cancelled.append(reply);
380 if (threadObject) threadObject->processJobs();
383 eventLoopQuitHack->deleteLater();
387 void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
389 QQuickPixmapReply *job = replies.take(reply);
392 job->redirectCount++;
393 if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
394 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
395 if (redirect.isValid()) {
396 QUrl url = reply->url().resolved(redirect.toUrl());
397 QNetworkRequest req(url);
398 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
400 reply->deleteLater();
401 reply = networkAccessManager()->get(req);
403 QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
404 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
406 replies.insert(reply, job);
412 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
415 if (reply->error()) {
416 error = QQuickPixmapReply::Loading;
417 errorString = reply->errorString();
419 QByteArray all = reply->readAll();
421 buff.open(QIODevice::ReadOnly);
422 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize))
423 error = QQuickPixmapReply::Decoding;
425 // send completion event to the QQuickPixmapReply
427 if (!cancelled.contains(job))
428 job->postReply(error, errorString, readSize, textureFactoryForImage(image));
431 reply->deleteLater();
433 // kick off event loop again incase we have dropped below max request count
434 threadObject->processJobs();
437 QQuickPixmapReaderThreadObject::QQuickPixmapReaderThreadObject(QQuickPixmapReader *i)
442 void QQuickPixmapReaderThreadObject::processJobs()
444 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
447 bool QQuickPixmapReaderThreadObject::event(QEvent *e)
449 if (e->type() == QEvent::User) {
450 reader->processJobs();
453 return QObject::event(e);
457 void QQuickPixmapReaderThreadObject::networkRequestDone()
459 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
460 reader->networkRequestDone(reply);
463 void QQuickPixmapReader::processJobs()
465 QMutexLocker locker(&mutex);
468 if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT))
469 return; // Nothing else to do
471 // Clean cancelled jobs
472 if (cancelled.count()) {
473 for (int i = 0; i < cancelled.count(); ++i) {
474 QQuickPixmapReply *job = cancelled.at(i);
475 QNetworkReply *reply = replies.key(job, 0);
476 if (reply && reply->isRunning()) {
477 // cancel any jobs already started
478 replies.remove(reply);
481 // deleteLater, since not owned by this thread
487 if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
488 QQuickPixmapReply *runningJob = jobs.takeLast();
489 runningJob->loading = true;
491 QUrl url = runningJob->url;
492 QSize requestSize = runningJob->requestSize;
494 processJob(runningJob, url, requestSize);
500 void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url,
501 const QSize &requestSize)
504 if (url.scheme() == QLatin1String("image")) {
505 // Use QmlImageProvider
507 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
508 QQmlImageProvider::ImageType imageType = ep->getImageProviderType(url);
509 if (imageType == QQmlImageProvider::Invalid) {
510 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::Loading;
511 QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString());
514 if (!cancelled.contains(runningJob))
515 runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image));
517 } else if (imageType == QQmlImageProvider::Image) {
518 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
519 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
521 if (image.isNull()) {
522 errorCode = QQuickPixmapReply::Loading;
523 errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString());
526 if (!cancelled.contains(runningJob))
527 runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image));
530 QQuickTextureFactory *t = ep->getTextureFromProvider(url, &readSize, requestSize);
531 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
534 errorCode = QQuickPixmapReply::Loading;
535 errorStr = QQuickPixmap::tr("Failed to get texture from provider: %1").arg(url.toString());
538 if (!cancelled.contains(runningJob))
539 runningJob->postReply(errorCode, errorStr, readSize, t);
545 QString lf = QQmlEnginePrivate::urlToLocalFileOrQrc(url);
547 // Image is local - load/decode immediately
549 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
553 if (f.open(QIODevice::ReadOnly)) {
554 if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize))
555 errorCode = QQuickPixmapReply::Loading;
557 errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
558 errorCode = QQuickPixmapReply::Loading;
561 if (!cancelled.contains(runningJob))
562 runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image));
566 QNetworkRequest req(url);
567 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
568 QNetworkReply *reply = networkAccessManager()->get(req);
570 QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
571 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
573 replies.insert(reply, runningJob);
578 QQuickPixmapReader *QQuickPixmapReader::instance(QQmlEngine *engine)
580 // XXX NOTE: must be called within readerMutex locking.
581 QQuickPixmapReader *reader = readers.value(engine);
583 reader = new QQuickPixmapReader(engine);
584 readers.insert(engine, reader);
590 QQuickPixmapReader *QQuickPixmapReader::existingInstance(QQmlEngine *engine)
592 // XXX NOTE: must be called within readerMutex locking.
593 return readers.value(engine, 0);
596 QQuickPixmapReply *QQuickPixmapReader::getImage(QQuickPixmapData *data)
599 QQuickPixmapReply *reply = new QQuickPixmapReply(data);
600 reply->engineForReader = engine;
603 if (threadObject) threadObject->processJobs();
608 void QQuickPixmapReader::cancel(QQuickPixmapReply *reply)
611 if (reply->loading) {
612 cancelled.append(reply);
615 if (threadObject) threadObject->processJobs();
617 jobs.removeAll(reply);
623 void QQuickPixmapReader::run()
625 if (replyDownloadProgress == -1) {
626 const QMetaObject *nr = &QNetworkReply::staticMetaObject;
627 const QMetaObject *pr = &QQuickPixmapReply::staticMetaObject;
628 const QMetaObject *ir = &QQuickPixmapReaderThreadObject::staticMetaObject;
629 replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
630 replyFinished = nr->indexOfSignal("finished()");
631 downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
632 threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
636 threadObject = new QQuickPixmapReaderThreadObject(this);
646 class QQuickPixmapKey
653 inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs)
655 return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
658 inline uint qHash(const QQuickPixmapKey &key)
660 return qHash(*key.url) ^ key.size->width() ^ key.size->height();
665 class QQuickPixmapStore : public QObject
670 ~QQuickPixmapStore();
672 void unreferencePixmap(QQuickPixmapData *);
673 void referencePixmap(QQuickPixmapData *);
678 virtual void timerEvent(QTimerEvent *);
681 QHash<QQuickPixmapKey, QQuickPixmapData *> m_cache;
684 void shrinkCache(int remove);
686 QQuickPixmapData *m_unreferencedPixmaps;
687 QQuickPixmapData *m_lastUnreferencedPixmap;
689 int m_unreferencedCost;
693 Q_GLOBAL_STATIC(QQuickPixmapStore, pixmapStore);
696 QQuickPixmapStore::QQuickPixmapStore()
697 : m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1), m_destroying(false)
701 QQuickPixmapStore::~QQuickPixmapStore()
705 int leakedPixmaps = 0;
706 QList<QQuickPixmapData*> cachedData = m_cache.values();
708 // Prevent unreferencePixmap() from assuming it needs to kick
709 // off the cache expiry timer, as we're shrinking the cache
710 // manually below after releasing all the pixmaps.
713 // unreference all (leaked) pixmaps
714 foreach (QQuickPixmapData* pixmap, cachedData) {
715 int currRefCount = pixmap->refCount;
718 while (currRefCount > 0) {
725 // free all unreferenced pixmaps
726 while (m_lastUnreferencedPixmap) {
731 qDebug("Number of leaked pixmaps: %i", leakedPixmaps);
734 void QQuickPixmapStore::unreferencePixmap(QQuickPixmapData *data)
736 Q_ASSERT(data->prevUnreferenced == 0);
737 Q_ASSERT(data->prevUnreferencedPtr == 0);
738 Q_ASSERT(data->nextUnreferenced == 0);
740 data->nextUnreferenced = m_unreferencedPixmaps;
741 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
742 if (!m_destroying) // the texture factories may have been cleaned up already.
743 m_unreferencedCost += data->cost();
745 m_unreferencedPixmaps = data;
746 if (m_unreferencedPixmaps->nextUnreferenced) {
747 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
748 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
751 if (!m_lastUnreferencedPixmap)
752 m_lastUnreferencedPixmap = data;
754 shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
756 if (m_timerId == -1 && m_unreferencedPixmaps && !m_destroying)
757 m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
760 void QQuickPixmapStore::referencePixmap(QQuickPixmapData *data)
762 Q_ASSERT(data->prevUnreferencedPtr);
764 *data->prevUnreferencedPtr = data->nextUnreferenced;
765 if (data->nextUnreferenced) {
766 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
767 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
769 if (m_lastUnreferencedPixmap == data)
770 m_lastUnreferencedPixmap = data->prevUnreferenced;
772 data->nextUnreferenced = 0;
773 data->prevUnreferencedPtr = 0;
774 data->prevUnreferenced = 0;
776 m_unreferencedCost -= data->cost();
779 void QQuickPixmapStore::shrinkCache(int remove)
781 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
782 QQuickPixmapData *data = m_lastUnreferencedPixmap;
783 Q_ASSERT(data->nextUnreferenced == 0);
785 *data->prevUnreferencedPtr = 0;
786 m_lastUnreferencedPixmap = data->prevUnreferenced;
787 data->prevUnreferencedPtr = 0;
788 data->prevUnreferenced = 0;
791 remove -= data->cost();
792 m_unreferencedCost -= data->cost();
794 data->removeFromCache();
799 void QQuickPixmapStore::timerEvent(QTimerEvent *)
801 int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
803 shrinkCache(removalCost);
805 if (m_unreferencedPixmaps == 0) {
806 killTimer(m_timerId);
811 void QQuickPixmapStore::purgeCache()
813 shrinkCache(m_unreferencedCost);
816 void QQuickPixmap::purgeCache()
818 pixmapStore()->purgeCache();
821 QQuickPixmapReply::QQuickPixmapReply(QQuickPixmapData *d)
822 : data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0)
824 if (finishedIndex == -1) {
825 finishedIndex = QQuickPixmapReply::staticMetaObject.indexOfSignal("finished()");
826 downloadProgressIndex = QQuickPixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
830 QQuickPixmapReply::~QQuickPixmapReply()
834 bool QQuickPixmapReply::event(QEvent *event)
836 if (event->type() == QEvent::User) {
839 Event *de = static_cast<Event *>(event);
840 data->pixmapStatus = (de->error == NoError) ? QQuickPixmap::Ready : QQuickPixmap::Error;
842 if (data->pixmapStatus == QQuickPixmap::Ready) {
843 data->textureFactory = de->textureFactory;
844 data->implicitSize = de->implicitSize;
846 data->errorString = de->errorString;
847 data->removeFromCache(); // We don't continue to cache error'd pixmaps
857 return QObject::event(event);
861 int QQuickPixmapData::cost() const
864 return textureFactory->textureByteCount();
868 void QQuickPixmapData::addref()
871 if (prevUnreferencedPtr)
872 pixmapStore()->referencePixmap(this);
875 void QQuickPixmapData::release()
877 Q_ASSERT(refCount > 0);
881 QQuickPixmapReply *cancelReply = reply;
884 QQuickPixmapReader::readerMutex.lock();
885 QQuickPixmapReader *reader = QQuickPixmapReader::existingInstance(cancelReply->engineForReader);
887 reader->cancel(cancelReply);
888 QQuickPixmapReader::readerMutex.unlock();
891 if (pixmapStatus == QQuickPixmap::Ready) {
892 pixmapStore()->unreferencePixmap(this);
900 void QQuickPixmapData::addToCache()
903 QQuickPixmapKey key = { &url, &requestSize };
904 pixmapStore()->m_cache.insert(key, this);
909 void QQuickPixmapData::removeFromCache()
912 QQuickPixmapKey key = { &url, &requestSize };
913 pixmapStore()->m_cache.remove(key);
918 static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
920 if (url.scheme() == QLatin1String("image")) {
922 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
923 QQmlImageProvider::ImageType imageType = ep->getImageProviderType(url);
926 case QQmlImageProvider::Invalid:
927 return new QQuickPixmapData(declarativePixmap, url, requestSize,
928 QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()));
929 case QQmlImageProvider::Texture:
931 QQuickTextureFactory *texture = ep->getTextureFromProvider(url, &readSize, requestSize);
934 return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestSize);
938 case QQmlImageProvider::Image:
940 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
941 if (!image.isNull()) {
943 return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(image), readSize, requestSize);
946 case QQmlImageProvider::Pixmap:
948 QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
949 if (!pixmap.isNull()) {
951 return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(pixmap.toImage()), readSize, requestSize);
956 // provider has bad image type, or provider returned null image
957 return new QQuickPixmapData(declarativePixmap, url, requestSize,
958 QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
961 QString localFile = QQmlEnginePrivate::urlToLocalFileOrQrc(url);
962 if (localFile.isEmpty())
969 if (f.open(QIODevice::ReadOnly)) {
972 if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) {
974 return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(image), readSize, requestSize);
976 errorString = QQuickPixmap::tr("Invalid image data: %1").arg(url.toString());
979 errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
981 return new QQuickPixmapData(declarativePixmap, url, requestSize, errorString);
985 struct QQuickPixmapNull {
989 Q_GLOBAL_STATIC(QQuickPixmapNull, nullPixmap);
991 QQuickPixmap::QQuickPixmap()
996 QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url)
1002 QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QSize &size)
1005 load(engine, url, size);
1008 QQuickPixmap::~QQuickPixmap()
1011 d->declarativePixmaps.remove(this);
1017 bool QQuickPixmap::isNull() const
1022 bool QQuickPixmap::isReady() const
1024 return status() == Ready;
1027 bool QQuickPixmap::isError() const
1029 return status() == Error;
1032 bool QQuickPixmap::isLoading() const
1034 return status() == Loading;
1037 QString QQuickPixmap::error() const
1040 return d->errorString;
1045 QQuickPixmap::Status QQuickPixmap::status() const
1048 return d->pixmapStatus;
1053 const QUrl &QQuickPixmap::url() const
1058 return nullPixmap()->url;
1061 const QSize &QQuickPixmap::implicitSize() const
1064 return d->implicitSize;
1066 return nullPixmap()->size;
1069 const QSize &QQuickPixmap::requestSize() const
1072 return d->requestSize;
1074 return nullPixmap()->size;
1077 QQuickTextureFactory *QQuickPixmap::textureFactory() const
1080 return d->textureFactory;
1085 QImage QQuickPixmap::image() const
1087 if (d && d->textureFactory)
1088 return d->textureFactory->image();
1092 void QQuickPixmap::setImage(const QImage &p)
1097 d = new QQuickPixmapData(this, textureFactoryForImage(p));
1100 int QQuickPixmap::width() const
1102 if (d && d->textureFactory)
1103 return d->textureFactory->textureSize().width();
1108 int QQuickPixmap::height() const
1110 if (d && d->textureFactory)
1111 return d->textureFactory->textureSize().height();
1116 QRect QQuickPixmap::rect() const
1118 if (d && d->textureFactory)
1119 return QRect(QPoint(), d->textureFactory->textureSize());
1124 void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url)
1126 load(engine, url, QSize(), QQuickPixmap::Cache);
1129 void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, QQuickPixmap::Options options)
1131 load(engine, url, QSize(), options);
1134 void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &size)
1136 load(engine, url, size, QQuickPixmap::Cache);
1139 void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options)
1142 d->declarativePixmaps.remove(this);
1147 QQuickPixmapKey key = { &url, &requestSize };
1148 QQuickPixmapStore *store = pixmapStore();
1150 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.find(key);
1152 if (iter == store->m_cache.end()) {
1153 if (options & QQuickPixmap::Asynchronous) {
1154 // pixmaps can only be loaded synchronously
1155 if (url.scheme() == QLatin1String("image")
1156 && QQmlEnginePrivate::get(engine)->getImageProviderType(url) == QQmlImageProvider::Pixmap) {
1157 options &= ~QQuickPixmap::Asynchronous;
1161 if (!(options & QQuickPixmap::Asynchronous)) {
1163 d = createPixmapDataSync(this, engine, url, requestSize, &ok);
1165 if (options & QQuickPixmap::Cache)
1169 if (d) // loadable, but encountered error while loading
1176 d = new QQuickPixmapData(this, url, requestSize);
1177 if (options & QQuickPixmap::Cache)
1180 QQuickPixmapReader::readerMutex.lock();
1181 d->reply = QQuickPixmapReader::instance(engine)->getImage(d);
1182 QQuickPixmapReader::readerMutex.unlock();
1186 d->declarativePixmaps.insert(this);
1190 void QQuickPixmap::clear()
1193 d->declarativePixmaps.remove(this);
1199 void QQuickPixmap::clear(QObject *obj)
1203 QObject::disconnect(d->reply, 0, obj, 0);
1204 d->declarativePixmaps.remove(this);
1210 bool QQuickPixmap::connectFinished(QObject *object, const char *method)
1212 if (!d || !d->reply) {
1213 qWarning("QQuickPixmap: connectFinished() called when not loading.");
1217 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
1220 bool QQuickPixmap::connectFinished(QObject *object, int method)
1222 if (!d || !d->reply) {
1223 qWarning("QQuickPixmap: connectFinished() called when not loading.");
1227 return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedIndex, object, method);
1230 bool QQuickPixmap::connectDownloadProgress(QObject *object, const char *method)
1232 if (!d || !d->reply) {
1233 qWarning("QQuickPixmap: connectDownloadProgress() called when not loading.");
1237 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
1240 bool QQuickPixmap::connectDownloadProgress(QObject *object, int method)
1242 if (!d || !d->reply) {
1243 qWarning("QQuickPixmap: connectDownloadProgress() called when not loading.");
1247 return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressIndex, object, method);
1252 #include <qquickpixmapcache.moc>