e6b6da636595cbecf4c6cb2079e8dee1ef342600
[profile/ivi/qtdeclarative.git] / src / qtquick1 / util / qdeclarativepixmapcache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "QtQuick1/private/qdeclarativepixmapcache_p.h"
43 #include "QtDeclarative/qdeclarativenetworkaccessmanagerfactory.h"
44 #include "QtDeclarative/qdeclarativeimageprovider.h"
45
46 #include <QtDeclarative/qdeclarativeengine.h>
47 #include <QtDeclarative/private/qdeclarativeglobal_p.h>
48 #include <QtDeclarative/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
74
75 // The cache limit describes the maximum "junk" in the cache.
76 // These are the same defaults as QPixmapCache
77 #if defined(Q_WS_QWS) || defined(Q_WS_WINCE)
78 static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded
79 #else
80 static int cache_limit = 10240 * 1024; // 10 MB cache limit for desktop
81 #endif
82
83 class QDeclarative1PixmapReader;
84 class QDeclarative1PixmapData;
85 class QDeclarative1PixmapReply : public QObject
86 {
87     Q_OBJECT
88 public:
89     enum ReadError { NoError, Loading, Decoding };
90
91     QDeclarative1PixmapReply(QDeclarative1PixmapData *);
92     ~QDeclarative1PixmapReply();
93
94     QDeclarative1PixmapData *data;
95     QDeclarative1PixmapReader *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
113 Q_SIGNALS:
114     void finished();
115     void downloadProgress(qint64, qint64);
116
117 protected:
118     bool event(QEvent *event);
119
120 private:
121     Q_DISABLE_COPY(QDeclarative1PixmapReply)
122
123 public:
124     static int finishedIndex;
125     static int downloadProgressIndex;
126 };
127
128 class QDeclarative1PixmapReaderThreadObject : public QObject {
129     Q_OBJECT
130 public:
131     QDeclarative1PixmapReaderThreadObject(QDeclarative1PixmapReader *);
132     void processJobs();
133     virtual bool event(QEvent *e);
134 private slots:
135     void networkRequestDone();
136 private:
137     QDeclarative1PixmapReader *reader;
138 };
139
140 class QDeclarative1PixmapData;
141 class QDeclarative1PixmapReader : public QThread
142 {
143     Q_OBJECT
144 public:
145     QDeclarative1PixmapReader(QDeclarativeEngine *eng);
146     ~QDeclarative1PixmapReader();
147
148     QDeclarative1PixmapReply *getImage(QDeclarative1PixmapData *);
149     void cancel(QDeclarative1PixmapReply *rep);
150
151     static QDeclarative1PixmapReader *instance(QDeclarativeEngine *engine);
152
153 protected:
154     void run();
155
156 private:
157     friend class QDeclarative1PixmapReaderThreadObject;
158     void processJobs();
159     void processJob(QDeclarative1PixmapReply *, const QUrl &, const QSize &);
160     void networkRequestDone(QNetworkReply *);
161
162     QList<QDeclarative1PixmapReply*> jobs;
163     QList<QDeclarative1PixmapReply*> cancelled;
164     QDeclarativeEngine *engine;
165     QObject *eventLoopQuitHack;
166
167     QMutex mutex;
168     QDeclarative1PixmapReaderThreadObject *threadObject;
169     QWaitCondition waitCondition;
170
171     QNetworkAccessManager *networkAccessManager();
172     QNetworkAccessManager *accessManager;
173
174     QHash<QNetworkReply*,QDeclarative1PixmapReply*> replies;
175
176     static int replyDownloadProgress;
177     static int replyFinished;
178     static int downloadProgress;
179     static int threadNetworkRequestDone;
180     static QHash<QDeclarativeEngine *,QDeclarative1PixmapReader*> readers;
181     static QMutex readerMutex;
182 };
183
184 class QDeclarative1PixmapData
185 {
186 public:
187     QDeclarative1PixmapData(const QUrl &u, const QSize &s, const QString &e)
188     : refCount(1), inCache(false), pixmapStatus(QDeclarative1Pixmap::Error), 
189       url(u), errorString(e), requestSize(s), reply(0), prevUnreferenced(0),
190       prevUnreferencedPtr(0), nextUnreferenced(0)
191     {
192     }
193
194     QDeclarative1PixmapData(const QUrl &u, const QSize &r)
195     : refCount(1), inCache(false), pixmapStatus(QDeclarative1Pixmap::Loading), 
196       url(u), requestSize(r), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), 
197       nextUnreferenced(0)
198     {
199     }
200
201     QDeclarative1PixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r)
202     : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarative1Pixmap::Ready), 
203       url(u), pixmap(p), implicitSize(s), requestSize(r), reply(0), prevUnreferenced(0),
204       prevUnreferencedPtr(0), nextUnreferenced(0)
205     {
206     }
207
208     QDeclarative1PixmapData(const QPixmap &p)
209     : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarative1Pixmap::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     QDeclarative1Pixmap::Status pixmapStatus;
227     QUrl url;
228     QString errorString;
229     QPixmap pixmap;
230     QSize implicitSize;
231     QSize requestSize;
232
233     QDeclarative1PixmapReply *reply;
234
235     QDeclarative1PixmapData *prevUnreferenced;
236     QDeclarative1PixmapData**prevUnreferencedPtr;
237     QDeclarative1PixmapData *nextUnreferenced;
238 };
239
240 int QDeclarative1PixmapReply::finishedIndex = -1;
241 int QDeclarative1PixmapReply::downloadProgressIndex = -1;
242
243 // XXX
244 QHash<QDeclarativeEngine *,QDeclarative1PixmapReader*> QDeclarative1PixmapReader::readers;
245 QMutex QDeclarative1PixmapReader::readerMutex;
246
247 int QDeclarative1PixmapReader::replyDownloadProgress = -1;
248 int QDeclarative1PixmapReader::replyFinished = -1;
249 int QDeclarative1PixmapReader::downloadProgress = -1;
250 int QDeclarative1PixmapReader::threadNetworkRequestDone = -1;
251
252
253 void QDeclarative1PixmapReply::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
260 QDeclarative1PixmapReply::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
265 QNetworkAccessManager *QDeclarative1PixmapReader::networkAccessManager()
266 {
267     if (!accessManager) {
268         Q_ASSERT(threadObject);
269         accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
270     }
271     return accessManager;
272 }
273
274 static 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 = QDeclarative1Pixmap::tr("Error decoding: %1: %2").arg(url.toString())
311                                 .arg(imgio.errorString());
312         return false;
313     }
314 }
315
316 QDeclarative1PixmapReader::QDeclarative1PixmapReader(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
325 QDeclarative1PixmapReader::~QDeclarative1PixmapReader()
326 {
327     readerMutex.lock();
328     readers.remove(engine);
329     readerMutex.unlock();
330
331     eventLoopQuitHack->deleteLater();
332     wait();
333 }
334
335 void QDeclarative1PixmapReader::networkRequestDone(QNetworkReply *reply)
336 {
337     QDeclarative1PixmapReply *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         QDeclarative1PixmapReply::ReadError error = QDeclarative1PixmapReply::NoError;
361         QString errorString;
362         QSize readSize;
363         if (reply->error()) {
364             error = QDeclarative1PixmapReply::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 = QDeclarative1PixmapReply::Decoding;
372             }
373         }
374         // send completion event to the QDeclarative1PixmapReply
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
385 QDeclarative1PixmapReaderThreadObject::QDeclarative1PixmapReaderThreadObject(QDeclarative1PixmapReader *i)
386 : reader(i)
387 {
388 }
389
390 void QDeclarative1PixmapReaderThreadObject::processJobs() 
391
392     QCoreApplication::postEvent(this, new QEvent(QEvent::User)); 
393 }
394
395 bool QDeclarative1PixmapReaderThreadObject::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
405 void QDeclarative1PixmapReaderThreadObject::networkRequestDone()
406 {
407     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
408     reader->networkRequestDone(reply);
409 }
410
411 void QDeclarative1PixmapReader::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                 QDeclarative1PixmapReply *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                 // deleteLater, since not owned by this thread
430                 job->deleteLater();
431             }
432             cancelled.clear();
433         }
434
435         if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
436             QDeclarative1PixmapReply *runningJob = jobs.takeLast();
437             runningJob->loading = true;
438
439             QUrl url = runningJob->data->url;
440             QSize requestSize = runningJob->data->requestSize;
441             locker.unlock();
442             processJob(runningJob, url, requestSize);
443             locker.relock();
444         }
445     }
446 }
447
448 void QDeclarative1PixmapReader::processJob(QDeclarative1PixmapReply *runningJob, const QUrl &url, 
449                                           const QSize &requestSize)
450 {
451     // fetch
452     if (url.scheme() == QLatin1String("image")) {
453         // Use QmlImageProvider
454         QSize readSize;
455         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
456         QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
457
458         QDeclarative1PixmapReply::ReadError errorCode = QDeclarative1PixmapReply::NoError;
459         QString errorStr;
460         if (image.isNull()) {
461             errorCode = QDeclarative1PixmapReply::Loading;
462             errorStr = QDeclarative1Pixmap::tr("Failed to get image from provider: %1").arg(url.toString());
463         }
464
465         mutex.lock();
466         if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image);
467         mutex.unlock();
468     } else {
469         QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
470         if (!lf.isEmpty()) {
471             // Image is local - load/decode immediately
472             QImage image;
473             QDeclarative1PixmapReply::ReadError errorCode = QDeclarative1PixmapReply::NoError;
474             QString errorStr;
475             QFile f(lf);
476             QSize readSize;
477             if (f.open(QIODevice::ReadOnly)) {
478                 if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize))
479                     errorCode = QDeclarative1PixmapReply::Loading;
480             } else {
481                 errorStr = QDeclarative1Pixmap::tr("Cannot open: %1").arg(url.toString());
482                 errorCode = QDeclarative1PixmapReply::Loading;
483             }
484             mutex.lock();
485             if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image);
486             mutex.unlock();
487         } else {
488             // Network resource
489             QNetworkRequest req(url);
490             req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
491             QNetworkReply *reply = networkAccessManager()->get(req);
492
493             QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
494             QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
495
496             replies.insert(reply, runningJob);
497         }
498     }
499 }
500
501 QDeclarative1PixmapReader *QDeclarative1PixmapReader::instance(QDeclarativeEngine *engine)
502 {
503     readerMutex.lock();
504     QDeclarative1PixmapReader *reader = readers.value(engine);
505     if (!reader) {
506         reader = new QDeclarative1PixmapReader(engine);
507         readers.insert(engine, reader);
508     }
509     readerMutex.unlock();
510
511     return reader;
512 }
513
514 QDeclarative1PixmapReply *QDeclarative1PixmapReader::getImage(QDeclarative1PixmapData *data)
515 {
516     mutex.lock();
517     QDeclarative1PixmapReply *reply = new QDeclarative1PixmapReply(data);
518     reply->reader = this;
519     jobs.append(reply);
520     // XXX 
521     if (threadObject) threadObject->processJobs();
522     mutex.unlock();
523     return reply;
524 }
525
526 void QDeclarative1PixmapReader::cancel(QDeclarative1PixmapReply *reply)
527 {
528     mutex.lock();
529     if (reply->loading) {
530         cancelled.append(reply);
531         reply->data = 0;
532         // XXX 
533         if (threadObject) threadObject->processJobs();
534     } else {
535         jobs.removeAll(reply);
536         delete reply;
537     }
538     mutex.unlock();
539 }
540
541 void QDeclarative1PixmapReader::run()
542 {
543     if (replyDownloadProgress == -1) {
544         const QMetaObject *nr = &QNetworkReply::staticMetaObject;
545         const QMetaObject *pr = &QDeclarative1PixmapReply::staticMetaObject;
546         const QMetaObject *ir = &QDeclarative1PixmapReaderThreadObject::staticMetaObject;
547         replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
548         replyFinished = nr->indexOfSignal("finished()");
549         downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
550         threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
551     }
552
553     mutex.lock();
554     threadObject = new QDeclarative1PixmapReaderThreadObject(this);
555     mutex.unlock();
556
557     processJobs();
558     exec();
559
560     delete threadObject;
561     threadObject = 0;
562 }
563
564 class QDeclarative1PixmapKey
565 {
566 public:
567     const QUrl *url;
568     const QSize *size;
569 };
570
571 inline bool operator==(const QDeclarative1PixmapKey &lhs, const QDeclarative1PixmapKey &rhs)
572 {
573     return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
574 }
575
576 inline uint qHash(const QDeclarative1PixmapKey &key)
577 {
578     return qHash(*key.url) ^ key.size->width() ^ key.size->height();
579 }
580
581 class QDeclarative1PixmapStore : public QObject
582 {
583     Q_OBJECT
584 public:
585     QDeclarative1PixmapStore();
586
587     void unreferencePixmap(QDeclarative1PixmapData *);
588     void referencePixmap(QDeclarative1PixmapData *);
589
590 protected:
591     virtual void timerEvent(QTimerEvent *);
592
593 public:
594     QHash<QDeclarative1PixmapKey, QDeclarative1PixmapData *> m_cache;
595
596 private:
597     void shrinkCache(int remove);
598
599     QDeclarative1PixmapData *m_unreferencedPixmaps;
600     QDeclarative1PixmapData *m_lastUnreferencedPixmap;
601
602     int m_unreferencedCost;
603     int m_timerId;
604 };
605
606 Q_GLOBAL_STATIC(QDeclarative1PixmapStore, pixmapStore)
607
608 QDeclarative1PixmapStore::QDeclarative1PixmapStore()
609 : m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1)
610 {
611 }
612
613 void QDeclarative1PixmapStore::unreferencePixmap(QDeclarative1PixmapData *data)
614 {
615     Q_ASSERT(data->prevUnreferenced == 0);
616     Q_ASSERT(data->prevUnreferencedPtr == 0);
617     Q_ASSERT(data->nextUnreferenced == 0);
618
619     data->nextUnreferenced = m_unreferencedPixmaps;
620     data->prevUnreferencedPtr = &m_unreferencedPixmaps;
621
622     m_unreferencedPixmaps = data;
623     if (m_unreferencedPixmaps->nextUnreferenced) {
624         m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
625         m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
626     }
627
628     if (!m_lastUnreferencedPixmap)
629         m_lastUnreferencedPixmap = data;
630
631     m_unreferencedCost += data->cost();
632
633     shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
634
635     if (m_timerId == -1 && m_unreferencedPixmaps) 
636         m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
637 }
638
639 void QDeclarative1PixmapStore::referencePixmap(QDeclarative1PixmapData *data)
640 {
641     Q_ASSERT(data->prevUnreferencedPtr);
642
643     *data->prevUnreferencedPtr = data->nextUnreferenced;
644     if (data->nextUnreferenced) { 
645         data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
646         data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
647     }
648     if (m_lastUnreferencedPixmap == data)
649         m_lastUnreferencedPixmap = data->prevUnreferenced;
650
651     data->nextUnreferenced = 0;
652     data->prevUnreferencedPtr = 0;
653     data->prevUnreferenced = 0;
654
655     m_unreferencedCost -= data->cost();
656 }
657
658 void QDeclarative1PixmapStore::shrinkCache(int remove)
659 {
660     while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
661         QDeclarative1PixmapData *data = m_lastUnreferencedPixmap;
662         Q_ASSERT(data->nextUnreferenced == 0);
663
664         *data->prevUnreferencedPtr = 0;
665         m_lastUnreferencedPixmap = data->prevUnreferenced;
666         data->prevUnreferencedPtr = 0;
667         data->prevUnreferenced = 0;
668
669         remove -= data->cost();
670         m_unreferencedCost -= data->cost();
671         data->removeFromCache();
672         delete data;
673     }
674 }
675
676 void QDeclarative1PixmapStore::timerEvent(QTimerEvent *)
677 {
678     int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
679
680     shrinkCache(removalCost);
681
682     if (m_unreferencedPixmaps == 0) {
683         killTimer(m_timerId);
684         m_timerId = -1;
685     }
686 }
687
688 QDeclarative1PixmapReply::QDeclarative1PixmapReply(QDeclarative1PixmapData *d)
689 : data(d), reader(0), requestSize(d->requestSize), loading(false), redirectCount(0)
690 {
691     if (finishedIndex == -1) {
692         finishedIndex = QDeclarative1PixmapReply::staticMetaObject.indexOfSignal("finished()");
693         downloadProgressIndex = QDeclarative1PixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
694     }
695 }
696
697 QDeclarative1PixmapReply::~QDeclarative1PixmapReply()
698 {
699 }
700
701 bool QDeclarative1PixmapReply::event(QEvent *event)
702 {
703     if (event->type() == QEvent::User) {
704
705         if (data) {
706             Event *de = static_cast<Event *>(event);
707             data->pixmapStatus = (de->error == NoError) ? QDeclarative1Pixmap::Ready : QDeclarative1Pixmap::Error;
708             
709             if (data->pixmapStatus == QDeclarative1Pixmap::Ready) {
710                 data->pixmap = QPixmap::fromImage(de->image);
711                 data->implicitSize = de->implicitSize;
712             } else {
713                 data->errorString = de->errorString;
714                 data->removeFromCache(); // We don't continue to cache error'd pixmaps
715             }
716
717             data->reply = 0;
718             emit finished();
719         }
720
721         delete this;
722         return true;
723     } else {
724         return QObject::event(event);
725     }
726 }
727
728 int QDeclarative1PixmapData::cost() const
729 {
730     return (pixmap.width() * pixmap.height() * pixmap.depth()) / 8;
731 }
732
733 void QDeclarative1PixmapData::addref()
734 {
735     ++refCount;
736     if (prevUnreferencedPtr) 
737         pixmapStore()->referencePixmap(this);
738 }
739
740 void QDeclarative1PixmapData::release()
741 {
742     Q_ASSERT(refCount > 0);
743     --refCount;
744
745     if (refCount == 0) {
746         if (reply) {
747             reply->reader->cancel(reply);
748             reply = 0;
749         }
750
751         if (pixmapStatus == QDeclarative1Pixmap::Ready) {
752             pixmapStore()->unreferencePixmap(this);
753         } else {
754             removeFromCache();
755             delete this;
756         }
757     }
758 }
759
760 void QDeclarative1PixmapData::addToCache()
761 {
762     if (!inCache) {
763         QDeclarative1PixmapKey key = { &url, &requestSize };
764         pixmapStore()->m_cache.insert(key, this);
765         inCache = true;
766     }
767 }
768
769 void QDeclarative1PixmapData::removeFromCache()
770 {
771     if (inCache) {
772         QDeclarative1PixmapKey key = { &url, &requestSize };
773         pixmapStore()->m_cache.remove(key);
774         inCache = false;
775     }
776 }
777
778 static QDeclarative1PixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
779 {
780     if (url.scheme() == QLatin1String("image")) {
781         QSize readSize;
782         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
783         QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
784
785         switch (imageType) {
786             case QDeclarativeImageProvider::Image:
787             {
788                 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
789                 if (!image.isNull()) {
790                     *ok = true;
791                     return new QDeclarative1PixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
792                 }
793             }
794             break;
795             case QDeclarativeImageProvider::Pixmap:
796             {
797                 QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
798                 if (!pixmap.isNull()) {
799                     *ok = true;
800                     return new QDeclarative1PixmapData(url, pixmap, readSize, requestSize);
801                 }
802             }
803             break;
804         case QDeclarativeImageProvider::Texture:
805         case QDeclarativeImageProvider::Invalid:
806             break;
807         }
808
809         // no matching provider, or provider has bad image type, or provider returned null image
810         return new QDeclarative1PixmapData(url, requestSize,
811             QDeclarative1Pixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
812     }
813
814     QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
815     if (localFile.isEmpty()) 
816         return 0;
817
818     QFile f(localFile);
819     QSize readSize;
820     QString errorString;
821
822     if (f.open(QIODevice::ReadOnly)) {
823         QImage image;
824         if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) {
825             *ok = true;
826             return new QDeclarative1PixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
827         }
828     } else {
829         errorString = QDeclarative1Pixmap::tr("Cannot open: %1").arg(url.toString());
830     }
831     return new QDeclarative1PixmapData(url, requestSize, errorString);
832 }
833
834
835 struct QDeclarative1PixmapNull {
836     QUrl url;
837     QPixmap pixmap;
838     QSize size;
839 };
840 Q_GLOBAL_STATIC(QDeclarative1PixmapNull, nullPixmap);
841
842 QDeclarative1Pixmap::QDeclarative1Pixmap()
843 : d(0)
844 {
845 }
846
847 QDeclarative1Pixmap::QDeclarative1Pixmap(QDeclarativeEngine *engine, const QUrl &url)
848 : d(0)
849 {
850     load(engine, url);
851 }
852
853 QDeclarative1Pixmap::QDeclarative1Pixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
854 : d(0)
855 {
856     load(engine, url, size);
857 }
858
859 QDeclarative1Pixmap::~QDeclarative1Pixmap()
860 {
861     if (d) {
862         d->release();
863         d = 0;
864     }
865 }
866
867 bool QDeclarative1Pixmap::isNull() const
868 {
869     return d == 0;
870 }
871
872 bool QDeclarative1Pixmap::isReady() const
873 {
874     return status() == Ready;
875 }
876
877 bool QDeclarative1Pixmap::isError() const
878 {
879     return status() == Error;
880 }
881
882 bool QDeclarative1Pixmap::isLoading() const
883 {
884     return status() == Loading;
885 }
886
887 QString QDeclarative1Pixmap::error() const
888 {
889     if (d)
890         return d->errorString;
891     else
892         return QString();
893 }
894
895 QDeclarative1Pixmap::Status QDeclarative1Pixmap::status() const
896 {
897     if (d)
898         return d->pixmapStatus;
899     else
900         return Null;
901 }
902
903 const QUrl &QDeclarative1Pixmap::url() const
904 {
905     if (d)
906         return d->url;
907     else
908         return nullPixmap()->url;
909 }
910
911 const QSize &QDeclarative1Pixmap::implicitSize() const
912 {
913     if (d) 
914         return d->implicitSize;
915     else
916         return nullPixmap()->size;
917 }
918
919 const QSize &QDeclarative1Pixmap::requestSize() const
920 {
921     if (d)
922         return d->requestSize;
923     else
924         return nullPixmap()->size;
925 }
926
927 const QPixmap &QDeclarative1Pixmap::pixmap() const
928 {
929     if (d) 
930         return d->pixmap;
931     else
932         return nullPixmap()->pixmap;
933 }
934
935 void QDeclarative1Pixmap::setPixmap(const QPixmap &p) 
936 {
937     clear();
938
939     if (!p.isNull())
940         d = new QDeclarative1PixmapData(p);
941 }
942
943 int QDeclarative1Pixmap::width() const
944 {
945     if (d) 
946         return d->pixmap.width();
947     else
948         return 0;
949 }
950
951 int QDeclarative1Pixmap::height() const
952 {
953     if (d) 
954         return d->pixmap.height();
955     else
956         return 0;
957 }
958
959 QRect QDeclarative1Pixmap::rect() const
960 {
961     if (d)
962         return d->pixmap.rect();
963     else
964         return QRect();
965 }
966
967 void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url)
968 {
969     load(engine, url, QSize(), QDeclarative1Pixmap::Cache);
970 }
971
972 void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarative1Pixmap::Options options)
973 {
974     load(engine, url, QSize(), options);
975 }
976
977 void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
978 {
979     load(engine, url, size, QDeclarative1Pixmap::Cache);
980 }
981
982 void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarative1Pixmap::Options options)
983 {
984     if (d) { d->release(); d = 0; }
985
986     QDeclarative1PixmapKey key = { &url, &requestSize };
987     QDeclarative1PixmapStore *store = pixmapStore();
988
989     QHash<QDeclarative1PixmapKey, QDeclarative1PixmapData *>::Iterator iter = store->m_cache.find(key);
990
991     if (iter == store->m_cache.end()) {
992         if (options & QDeclarative1Pixmap::Asynchronous) {
993             // pixmaps can only be loaded synchronously
994             if (url.scheme() == QLatin1String("image") 
995                     && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) {
996                 options &= ~QDeclarative1Pixmap::Asynchronous;
997             }
998         }
999
1000         if (!(options & QDeclarative1Pixmap::Asynchronous)) {
1001             bool ok = false;
1002             d = createPixmapDataSync(engine, url, requestSize, &ok);
1003             if (ok) {
1004                 if (options & QDeclarative1Pixmap::Cache)
1005                     d->addToCache();
1006                 return;
1007             }
1008             if (d)  // loadable, but encountered error while loading
1009                 return;
1010         } 
1011
1012         if (!engine)
1013             return;
1014
1015         QDeclarative1PixmapReader *reader = QDeclarative1PixmapReader::instance(engine);
1016
1017         d = new QDeclarative1PixmapData(url, requestSize);
1018         if (options & QDeclarative1Pixmap::Cache)
1019             d->addToCache();
1020
1021         d->reply = reader->getImage(d);
1022     } else {
1023         d = *iter;
1024         d->addref();
1025     }
1026 }
1027
1028 void QDeclarative1Pixmap::clear()
1029 {
1030     if (d) {
1031         d->release();
1032         d = 0;
1033     }
1034 }
1035
1036 void QDeclarative1Pixmap::clear(QObject *obj)
1037 {
1038     if (d) {
1039         if (d->reply) 
1040             QObject::disconnect(d->reply, 0, obj, 0);
1041         d->release();
1042         d = 0;
1043     }
1044 }
1045
1046 bool QDeclarative1Pixmap::connectFinished(QObject *object, const char *method)
1047 {
1048     if (!d || !d->reply) {
1049         qWarning("QDeclarative1Pixmap: connectFinished() called when not loading.");
1050         return false;
1051     }
1052
1053     return QObject::connect(d->reply, SIGNAL(finished()), object, method);
1054 }
1055
1056 bool QDeclarative1Pixmap::connectFinished(QObject *object, int method)
1057 {
1058     if (!d || !d->reply) {
1059         qWarning("QDeclarative1Pixmap: connectFinished() called when not loading.");
1060         return false;
1061     }
1062
1063     return QMetaObject::connect(d->reply, QDeclarative1PixmapReply::finishedIndex, object, method);
1064 }
1065
1066 bool QDeclarative1Pixmap::connectDownloadProgress(QObject *object, const char *method)
1067 {
1068     if (!d || !d->reply) {
1069         qWarning("QDeclarative1Pixmap: connectDownloadProgress() called when not loading.");
1070         return false;
1071     }
1072
1073     return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
1074 }
1075
1076 bool QDeclarative1Pixmap::connectDownloadProgress(QObject *object, int method)
1077 {
1078     if (!d || !d->reply) {
1079         qWarning("QDeclarative1Pixmap: connectDownloadProgress() called when not loading.");
1080         return false;
1081     }
1082
1083     return QMetaObject::connect(d->reply, QDeclarative1PixmapReply::downloadProgressIndex, object, method);
1084 }
1085
1086
1087
1088 QT_END_NAMESPACE
1089
1090 #include <qdeclarativepixmapcache.moc>