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