source: trunk/src/declarative/util/qdeclarativepixmapcache.cpp@ 917

Last change on this file since 917 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 31.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativepixmapcache_p.h"
43#include "qdeclarativenetworkaccessmanagerfactory.h"
44#include "qdeclarativeimageprovider.h"
45
46#include <qdeclarativeengine.h>
47#include <private/qdeclarativeglobal_p.h>
48#include <private/qdeclarativeengine_p.h>
49
50#include <QCoreApplication>
51#include <QImageReader>
52#include <QHash>
53#include <QNetworkReply>
54#include <QPixmapCache>
55#include <QFile>
56#include <QThread>
57#include <QMutex>
58#include <QMutexLocker>
59#include <QWaitCondition>
60#include <QBuffer>
61#include <QWaitCondition>
62#include <QtCore/qdebug.h>
63#include <private/qobject_p.h>
64#include <QSslError>
65
66#define IMAGEREQUEST_MAX_REQUEST_COUNT 8
67#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16
68#define CACHE_EXPIRE_TIME 30
69#define CACHE_REMOVAL_FRACTION 4
70
71QT_BEGIN_NAMESPACE
72
73// The cache limit describes the maximum "junk" in the cache.
74// These are the same defaults as QPixmapCache
75#if defined(Q_OS_SYMBIAN)
76static int cache_limit = 1024 * 1024; // 1048 KB cache limit for symbian
77#elif defined(Q_WS_QWS) || defined(Q_WS_WINCE)
78static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded
79#else
80static int cache_limit = 10240 * 1024; // 10 MB cache limit for desktop
81#endif
82
83class QDeclarativePixmapReader;
84class QDeclarativePixmapData;
85class QDeclarativePixmapReply : public QObject
86{
87 Q_OBJECT
88public:
89 enum ReadError { NoError, Loading, Decoding };
90
91 QDeclarativePixmapReply(QDeclarativePixmapData *);
92 ~QDeclarativePixmapReply();
93
94 QDeclarativePixmapData *data;
95 QDeclarativePixmapReader *reader;
96 QSize requestSize;
97
98 bool loading;
99 int redirectCount;
100
101 class Event : public QEvent {
102 public:
103 Event(ReadError, const QString &, const QSize &, const QImage &);
104
105 ReadError error;
106 QString errorString;
107 QSize implicitSize;
108 QImage image;
109 };
110 void postReply(ReadError, const QString &, const QSize &, const QImage &);
111
112
113Q_SIGNALS:
114 void finished();
115 void downloadProgress(qint64, qint64);
116
117protected:
118 bool event(QEvent *event);
119
120private:
121 Q_DISABLE_COPY(QDeclarativePixmapReply)
122
123public:
124 static int finishedIndex;
125 static int downloadProgressIndex;
126};
127
128class QDeclarativePixmapReaderThreadObject : public QObject {
129 Q_OBJECT
130public:
131 QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *);
132 void processJobs();
133 virtual bool event(QEvent *e);
134private slots:
135 void networkRequestDone();
136private:
137 QDeclarativePixmapReader *reader;
138};
139
140class QDeclarativePixmapData;
141class QDeclarativePixmapReader : public QThread
142{
143 Q_OBJECT
144public:
145 QDeclarativePixmapReader(QDeclarativeEngine *eng);
146 ~QDeclarativePixmapReader();
147
148 QDeclarativePixmapReply *getImage(QDeclarativePixmapData *);
149 void cancel(QDeclarativePixmapReply *rep);
150
151 static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine);
152
153protected:
154 void run();
155
156private:
157 friend class QDeclarativePixmapReaderThreadObject;
158 void processJobs();
159 void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &);
160 void networkRequestDone(QNetworkReply *);
161
162 QList<QDeclarativePixmapReply*> jobs;
163 QList<QDeclarativePixmapReply*> cancelled;
164 QDeclarativeEngine *engine;
165 QObject *eventLoopQuitHack;
166
167 QMutex mutex;
168 QDeclarativePixmapReaderThreadObject *threadObject;
169 QWaitCondition waitCondition;
170
171 QNetworkAccessManager *networkAccessManager();
172 QNetworkAccessManager *accessManager;
173
174 QHash<QNetworkReply*,QDeclarativePixmapReply*> replies;
175
176 static int replyDownloadProgress;
177 static int replyFinished;
178 static int downloadProgress;
179 static int threadNetworkRequestDone;
180 static QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> readers;
181 static QMutex readerMutex;
182};
183
184class QDeclarativePixmapData
185{
186public:
187 QDeclarativePixmapData(const QUrl &u, const QSize &s, const QString &e)
188 : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error),
189 url(u), errorString(e), requestSize(s), reply(0), prevUnreferenced(0),
190 prevUnreferencedPtr(0), nextUnreferenced(0)
191 {
192 }
193
194 QDeclarativePixmapData(const QUrl &u, const QSize &r)
195 : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading),
196 url(u), requestSize(r), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0),
197 nextUnreferenced(0)
198 {
199 }
200
201 QDeclarativePixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r)
202 : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
203 url(u), pixmap(p), implicitSize(s), requestSize(r), reply(0), prevUnreferenced(0),
204 prevUnreferencedPtr(0), nextUnreferenced(0)
205 {
206 }
207
208 QDeclarativePixmapData(const QPixmap &p)
209 : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready),
210 pixmap(p), implicitSize(p.size()), requestSize(p.size()), reply(0), prevUnreferenced(0),
211 prevUnreferencedPtr(0), nextUnreferenced(0)
212 {
213 }
214
215 int cost() const;
216 void addref();
217 void release();
218 void addToCache();
219 void removeFromCache();
220
221 uint refCount;
222
223 bool inCache:1;
224 bool privatePixmap:1;
225
226 QDeclarativePixmap::Status pixmapStatus;
227 QUrl url;
228 QString errorString;
229 QPixmap pixmap;
230 QSize implicitSize;
231 QSize requestSize;
232
233 QDeclarativePixmapReply *reply;
234
235 QDeclarativePixmapData *prevUnreferenced;
236 QDeclarativePixmapData**prevUnreferencedPtr;
237 QDeclarativePixmapData *nextUnreferenced;
238};
239
240int QDeclarativePixmapReply::finishedIndex = -1;
241int QDeclarativePixmapReply::downloadProgressIndex = -1;
242
243// XXX
244QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> QDeclarativePixmapReader::readers;
245QMutex QDeclarativePixmapReader::readerMutex;
246
247int QDeclarativePixmapReader::replyDownloadProgress = -1;
248int QDeclarativePixmapReader::replyFinished = -1;
249int QDeclarativePixmapReader::downloadProgress = -1;
250int QDeclarativePixmapReader::threadNetworkRequestDone = -1;
251
252
253void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
254 const QSize &implicitSize, const QImage &image)
255{
256 loading = false;
257 QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, image));
258}
259
260QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, const QImage &i)
261: QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i)
262{
263}
264
265QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager()
266{
267 if (!accessManager) {
268 Q_ASSERT(threadObject);
269 accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
270 }
271 return accessManager;
272}
273
274static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
275 const QSize &requestSize)
276{
277 QImageReader imgio(dev);
278
279 bool force_scale = false;
280 if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) {
281 imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053
282 force_scale = true;
283 }
284
285 bool scaled = false;
286 if (requestSize.width() > 0 || requestSize.height() > 0) {
287 QSize s = imgio.size();
288 if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
289 if (requestSize.height() <= 0)
290 s.setHeight(s.height()*requestSize.width()/s.width());
291 s.setWidth(requestSize.width()); scaled = true;
292 }
293 if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
294 if (requestSize.width() <= 0)
295 s.setWidth(s.width()*requestSize.height()/s.height());
296 s.setHeight(requestSize.height()); scaled = true;
297 }
298 if (scaled) { imgio.setScaledSize(s); }
299 }
300
301 if (impsize)
302 *impsize = imgio.size();
303
304 if (imgio.read(image)) {
305 if (impsize && impsize->width() < 0)
306 *impsize = image->size();
307 return true;
308 } else {
309 if (errorString)
310 *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString())
311 .arg(imgio.errorString());
312 return false;
313 }
314}
315
316QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng)
317: QThread(eng), engine(eng), threadObject(0), accessManager(0)
318{
319 eventLoopQuitHack = new QObject;
320 eventLoopQuitHack->moveToThread(this);
321 connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
322 start(QThread::IdlePriority);
323}
324
325QDeclarativePixmapReader::~QDeclarativePixmapReader()
326{
327 readerMutex.lock();
328 readers.remove(engine);
329 readerMutex.unlock();
330
331 eventLoopQuitHack->deleteLater();
332 wait();
333}
334
335void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply)
336{
337 QDeclarativePixmapReply *job = replies.take(reply);
338
339 if (job) {
340 job->redirectCount++;
341 if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
342 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
343 if (redirect.isValid()) {
344 QUrl url = reply->url().resolved(redirect.toUrl());
345 QNetworkRequest req(url);
346 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
347
348 reply->deleteLater();
349 reply = networkAccessManager()->get(req);
350
351 QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
352 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
353
354 replies.insert(reply, job);
355 return;
356 }
357 }
358
359 QImage image;
360 QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError;
361 QString errorString;
362 QSize readSize;
363 if (reply->error()) {
364 error = QDeclarativePixmapReply::Loading;
365 errorString = reply->errorString();
366 } else {
367 QByteArray all = reply->readAll();
368 QBuffer buff(&all);
369 buff.open(QIODevice::ReadOnly);
370 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize)) {
371 error = QDeclarativePixmapReply::Decoding;
372 }
373 }
374 // send completion event to the QDeclarativePixmapReply
375 mutex.lock();
376 if (!cancelled.contains(job)) job->postReply(error, errorString, readSize, image);
377 mutex.unlock();
378 }
379 reply->deleteLater();
380
381 // kick off event loop again incase we have dropped below max request count
382 threadObject->processJobs();
383}
384
385QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i)
386: reader(i)
387{
388}
389
390void QDeclarativePixmapReaderThreadObject::processJobs()
391{
392 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
393}
394
395bool QDeclarativePixmapReaderThreadObject::event(QEvent *e)
396{
397 if (e->type() == QEvent::User) {
398 reader->processJobs();
399 return true;
400 } else {
401 return QObject::event(e);
402 }
403}
404
405void QDeclarativePixmapReaderThreadObject::networkRequestDone()
406{
407 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
408 reader->networkRequestDone(reply);
409}
410
411void QDeclarativePixmapReader::processJobs()
412{
413 QMutexLocker locker(&mutex);
414
415 while (true) {
416 if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT))
417 return; // Nothing else to do
418
419 // Clean cancelled jobs
420 if (cancelled.count()) {
421 for (int i = 0; i < cancelled.count(); ++i) {
422 QDeclarativePixmapReply *job = cancelled.at(i);
423 QNetworkReply *reply = replies.key(job, 0);
424 if (reply && reply->isRunning()) {
425 // cancel any jobs already started
426 replies.remove(reply);
427 reply->close();
428 }
429 delete job;
430 }
431 cancelled.clear();
432 }
433
434 if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
435 QDeclarativePixmapReply *runningJob = jobs.takeLast();
436 runningJob->loading = true;
437
438 QUrl url = runningJob->data->url;
439 QSize requestSize = runningJob->data->requestSize;
440 locker.unlock();
441 processJob(runningJob, url, requestSize);
442 locker.relock();
443 }
444 }
445}
446
447void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url,
448 const QSize &requestSize)
449{
450 // fetch
451 if (url.scheme() == QLatin1String("image")) {
452 // Use QmlImageProvider
453 QSize readSize;
454 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
455 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
456
457 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
458 QString errorStr;
459 if (image.isNull()) {
460 errorCode = QDeclarativePixmapReply::Loading;
461 errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString());
462 }
463
464 mutex.lock();
465 if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image);
466 mutex.unlock();
467 } else {
468 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
469 if (!lf.isEmpty()) {
470 // Image is local - load/decode immediately
471 QImage image;
472 QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
473 QString errorStr;
474 QFile f(lf);
475 QSize readSize;
476 if (f.open(QIODevice::ReadOnly)) {
477 if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize))
478 errorCode = QDeclarativePixmapReply::Loading;
479 } else {
480 errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
481 errorCode = QDeclarativePixmapReply::Loading;
482 }
483 mutex.lock();
484 if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image);
485 mutex.unlock();
486 } else {
487 // Network resource
488 QNetworkRequest req(url);
489 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
490 QNetworkReply *reply = networkAccessManager()->get(req);
491
492 QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
493 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
494
495 replies.insert(reply, runningJob);
496 }
497 }
498}
499
500QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine)
501{
502 readerMutex.lock();
503 QDeclarativePixmapReader *reader = readers.value(engine);
504 if (!reader) {
505 reader = new QDeclarativePixmapReader(engine);
506 readers.insert(engine, reader);
507 }
508 readerMutex.unlock();
509
510 return reader;
511}
512
513QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data)
514{
515 mutex.lock();
516 QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data);
517 reply->reader = this;
518 jobs.append(reply);
519 // XXX
520 if (threadObject) threadObject->processJobs();
521 mutex.unlock();
522 return reply;
523}
524
525void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply)
526{
527 mutex.lock();
528 if (reply->loading) {
529 cancelled.append(reply);
530 reply->data = 0;
531 // XXX
532 if (threadObject) threadObject->processJobs();
533 } else {
534 jobs.removeAll(reply);
535 delete reply;
536 }
537 mutex.unlock();
538}
539
540void QDeclarativePixmapReader::run()
541{
542 if (replyDownloadProgress == -1) {
543 const QMetaObject *nr = &QNetworkReply::staticMetaObject;
544 const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject;
545 const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject;
546 replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
547 replyFinished = nr->indexOfSignal("finished()");
548 downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
549 threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
550 }
551
552 mutex.lock();
553 threadObject = new QDeclarativePixmapReaderThreadObject(this);
554 mutex.unlock();
555
556 processJobs();
557 exec();
558
559 delete threadObject;
560 threadObject = 0;
561}
562
563class QDeclarativePixmapKey
564{
565public:
566 const QUrl *url;
567 const QSize *size;
568};
569
570inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs)
571{
572 return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
573}
574
575inline uint qHash(const QDeclarativePixmapKey &key)
576{
577 return qHash(*key.url) ^ key.size->width() ^ key.size->height();
578}
579
580class QDeclarativePixmapStore : public QObject
581{
582 Q_OBJECT
583public:
584 QDeclarativePixmapStore();
585
586 void unreferencePixmap(QDeclarativePixmapData *);
587 void referencePixmap(QDeclarativePixmapData *);
588
589protected:
590 virtual void timerEvent(QTimerEvent *);
591
592public:
593 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *> m_cache;
594
595private:
596 void shrinkCache(int remove);
597
598 QDeclarativePixmapData *m_unreferencedPixmaps;
599 QDeclarativePixmapData *m_lastUnreferencedPixmap;
600
601 int m_unreferencedCost;
602 int m_timerId;
603};
604Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore);
605
606QDeclarativePixmapStore::QDeclarativePixmapStore()
607: m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1)
608{
609}
610
611void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data)
612{
613 Q_ASSERT(data->prevUnreferenced == 0);
614 Q_ASSERT(data->prevUnreferencedPtr == 0);
615 Q_ASSERT(data->nextUnreferenced == 0);
616
617 data->nextUnreferenced = m_unreferencedPixmaps;
618 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
619
620 m_unreferencedPixmaps = data;
621 if (m_unreferencedPixmaps->nextUnreferenced) {
622 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
623 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
624 }
625
626 if (!m_lastUnreferencedPixmap)
627 m_lastUnreferencedPixmap = data;
628
629 m_unreferencedCost += data->cost();
630
631 shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
632
633 if (m_timerId == -1 && m_unreferencedPixmaps)
634 m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
635}
636
637void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data)
638{
639 Q_ASSERT(data->prevUnreferencedPtr);
640
641 *data->prevUnreferencedPtr = data->nextUnreferenced;
642 if (data->nextUnreferenced) {
643 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
644 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
645 }
646 if (m_lastUnreferencedPixmap == data)
647 m_lastUnreferencedPixmap = data->prevUnreferenced;
648
649 data->nextUnreferenced = 0;
650 data->prevUnreferencedPtr = 0;
651 data->prevUnreferenced = 0;
652
653 m_unreferencedCost -= data->cost();
654}
655
656void QDeclarativePixmapStore::shrinkCache(int remove)
657{
658 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
659 QDeclarativePixmapData *data = m_lastUnreferencedPixmap;
660 Q_ASSERT(data->nextUnreferenced == 0);
661
662 *data->prevUnreferencedPtr = 0;
663 m_lastUnreferencedPixmap = data->prevUnreferenced;
664 data->prevUnreferencedPtr = 0;
665 data->prevUnreferenced = 0;
666
667 remove -= data->cost();
668 m_unreferencedCost -= data->cost();
669 data->removeFromCache();
670 delete data;
671 }
672}
673
674void QDeclarativePixmapStore::timerEvent(QTimerEvent *)
675{
676 int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
677
678 shrinkCache(removalCost);
679
680 if (m_unreferencedPixmaps == 0) {
681 killTimer(m_timerId);
682 m_timerId = -1;
683 }
684}
685
686QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d)
687: data(d), reader(0), requestSize(d->requestSize), loading(false), redirectCount(0)
688{
689 if (finishedIndex == -1) {
690 finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()");
691 downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
692 }
693}
694
695QDeclarativePixmapReply::~QDeclarativePixmapReply()
696{
697}
698
699bool QDeclarativePixmapReply::event(QEvent *event)
700{
701 if (event->type() == QEvent::User) {
702
703 if (data) {
704 Event *de = static_cast<Event *>(event);
705 data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error;
706
707 if (data->pixmapStatus == QDeclarativePixmap::Ready) {
708 data->pixmap = QPixmap::fromImage(de->image);
709 data->implicitSize = de->implicitSize;
710 } else {
711 data->errorString = de->errorString;
712 data->removeFromCache(); // We don't continue to cache error'd pixmaps
713 }
714
715 data->reply = 0;
716 emit finished();
717 }
718
719 delete this;
720 return true;
721 } else {
722 return QObject::event(event);
723 }
724}
725
726int QDeclarativePixmapData::cost() const
727{
728 return (pixmap.width() * pixmap.height() * pixmap.depth()) / 8;
729}
730
731void QDeclarativePixmapData::addref()
732{
733 ++refCount;
734 if (prevUnreferencedPtr)
735 pixmapStore()->referencePixmap(this);
736}
737
738void QDeclarativePixmapData::release()
739{
740 Q_ASSERT(refCount > 0);
741 --refCount;
742
743 if (refCount == 0) {
744 if (reply) {
745 reply->reader->cancel(reply);
746 reply = 0;
747 }
748
749 if (pixmapStatus == QDeclarativePixmap::Ready) {
750 pixmapStore()->unreferencePixmap(this);
751 } else {
752 removeFromCache();
753 delete this;
754 }
755 }
756}
757
758void QDeclarativePixmapData::addToCache()
759{
760 if (!inCache) {
761 QDeclarativePixmapKey key = { &url, &requestSize };
762 pixmapStore()->m_cache.insert(key, this);
763 inCache = true;
764 }
765}
766
767void QDeclarativePixmapData::removeFromCache()
768{
769 if (inCache) {
770 QDeclarativePixmapKey key = { &url, &requestSize };
771 pixmapStore()->m_cache.remove(key);
772 inCache = false;
773 }
774}
775
776static QDeclarativePixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
777{
778 if (url.scheme() == QLatin1String("image")) {
779 QSize readSize;
780 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
781 QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
782
783 switch (imageType) {
784 case QDeclarativeImageProvider::Image:
785 {
786 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
787 if (!image.isNull()) {
788 *ok = true;
789 return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
790 }
791 }
792 case QDeclarativeImageProvider::Pixmap:
793 {
794 QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
795 if (!pixmap.isNull()) {
796 *ok = true;
797 return new QDeclarativePixmapData(url, pixmap, readSize, requestSize);
798 }
799 }
800 }
801
802 // no matching provider, or provider has bad image type, or provider returned null image
803 return new QDeclarativePixmapData(url, requestSize,
804 QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
805 }
806
807 QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
808 if (localFile.isEmpty())
809 return 0;
810
811 QFile f(localFile);
812 QSize readSize;
813 QString errorString;
814
815 if (f.open(QIODevice::ReadOnly)) {
816 QImage image;
817 if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) {
818 *ok = true;
819 return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
820 }
821 } else {
822 errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
823 }
824 return new QDeclarativePixmapData(url, requestSize, errorString);
825}
826
827
828struct QDeclarativePixmapNull {
829 QUrl url;
830 QPixmap pixmap;
831 QSize size;
832};
833Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap);
834
835QDeclarativePixmap::QDeclarativePixmap()
836: d(0)
837{
838}
839
840QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url)
841: d(0)
842{
843 load(engine, url);
844}
845
846QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
847: d(0)
848{
849 load(engine, url, size);
850}
851
852QDeclarativePixmap::~QDeclarativePixmap()
853{
854 if (d) {
855 d->release();
856 d = 0;
857 }
858}
859
860bool QDeclarativePixmap::isNull() const
861{
862 return d == 0;
863}
864
865bool QDeclarativePixmap::isReady() const
866{
867 return status() == Ready;
868}
869
870bool QDeclarativePixmap::isError() const
871{
872 return status() == Error;
873}
874
875bool QDeclarativePixmap::isLoading() const
876{
877 return status() == Loading;
878}
879
880QString QDeclarativePixmap::error() const
881{
882 if (d)
883 return d->errorString;
884 else
885 return QString();
886}
887
888QDeclarativePixmap::Status QDeclarativePixmap::status() const
889{
890 if (d)
891 return d->pixmapStatus;
892 else
893 return Null;
894}
895
896const QUrl &QDeclarativePixmap::url() const
897{
898 if (d)
899 return d->url;
900 else
901 return nullPixmap()->url;
902}
903
904const QSize &QDeclarativePixmap::implicitSize() const
905{
906 if (d)
907 return d->implicitSize;
908 else
909 return nullPixmap()->size;
910}
911
912const QSize &QDeclarativePixmap::requestSize() const
913{
914 if (d)
915 return d->requestSize;
916 else
917 return nullPixmap()->size;
918}
919
920const QPixmap &QDeclarativePixmap::pixmap() const
921{
922 if (d)
923 return d->pixmap;
924 else
925 return nullPixmap()->pixmap;
926}
927
928void QDeclarativePixmap::setPixmap(const QPixmap &p)
929{
930 clear();
931
932 if (!p.isNull())
933 d = new QDeclarativePixmapData(p);
934}
935
936int QDeclarativePixmap::width() const
937{
938 if (d)
939 return d->pixmap.width();
940 else
941 return 0;
942}
943
944int QDeclarativePixmap::height() const
945{
946 if (d)
947 return d->pixmap.height();
948 else
949 return 0;
950}
951
952QRect QDeclarativePixmap::rect() const
953{
954 if (d)
955 return d->pixmap.rect();
956 else
957 return QRect();
958}
959
960void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url)
961{
962 load(engine, url, QSize(), false);
963}
964
965void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, bool async)
966{
967 load(engine, url, QSize(), async);
968}
969
970void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
971{
972 load(engine, url, size, false);
973}
974
975void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool async)
976{
977 if (d) { d->release(); d = 0; }
978
979 QDeclarativePixmapKey key = { &url, &requestSize };
980 QDeclarativePixmapStore *store = pixmapStore();
981
982 QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key);
983
984 if (iter == store->m_cache.end()) {
985 if (async) {
986 // pixmaps can only be loaded synchronously
987 if (url.scheme() == QLatin1String("image")
988 && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) {
989 async = false;
990 }
991 }
992
993 if (!async) {
994 bool ok = false;
995 d = createPixmapDataSync(engine, url, requestSize, &ok);
996 if (ok) {
997 d->addToCache();
998 return;
999 }
1000 if (d) // loadable, but encountered error while loading
1001 return;
1002 }
1003
1004 if (!engine)
1005 return;
1006
1007 QDeclarativePixmapReader *reader = QDeclarativePixmapReader::instance(engine);
1008
1009 d = new QDeclarativePixmapData(url, requestSize);
1010 d->addToCache();
1011
1012 d->reply = reader->getImage(d);
1013 } else {
1014 d = *iter;
1015 d->addref();
1016 }
1017}
1018
1019void QDeclarativePixmap::clear()
1020{
1021 if (d) {
1022 d->release();
1023 d = 0;
1024 }
1025}
1026
1027void QDeclarativePixmap::clear(QObject *obj)
1028{
1029 if (d) {
1030 if (d->reply)
1031 QObject::disconnect(d->reply, 0, obj, 0);
1032 d->release();
1033 d = 0;
1034 }
1035}
1036
1037bool QDeclarativePixmap::connectFinished(QObject *object, const char *method)
1038{
1039 if (!d || !d->reply) {
1040 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1041 return false;
1042 }
1043
1044 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
1045}
1046
1047bool QDeclarativePixmap::connectFinished(QObject *object, int method)
1048{
1049 if (!d || !d->reply) {
1050 qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1051 return false;
1052 }
1053
1054 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method);
1055}
1056
1057bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method)
1058{
1059 if (!d || !d->reply) {
1060 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1061 return false;
1062 }
1063
1064 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
1065}
1066
1067bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method)
1068{
1069 if (!d || !d->reply) {
1070 qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1071 return false;
1072 }
1073
1074 return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method);
1075}
1076
1077QT_END_NAMESPACE
1078
1079#include <qdeclarativepixmapcache.moc>
Note: See TracBrowser for help on using the repository browser.