236b93ecbd3b550d0a5351615c457b1d532ae16b
[profile/ivi/qtdeclarative.git] / src / quick / 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 "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 <QtQuick/private/qsgtexture_p.h>
51 #include <QtQuick/private/qsgcontext_p.h>
52
53 #include <QCoreApplication>
54 #include <QImageReader>
55 #include <QHash>
56 #include <QNetworkReply>
57 #include <QPixmapCache>
58 #include <QFile>
59 #include <QThread>
60 #include <QMutex>
61 #include <QMutexLocker>
62 #include <QWaitCondition>
63 #include <QBuffer>
64 #include <QWaitCondition>
65 #include <QtCore/qdebug.h>
66 #include <private/qobject_p.h>
67 #include <QSslError>
68
69 #define IMAGEREQUEST_MAX_REQUEST_COUNT       8
70 #define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16
71 #define CACHE_EXPIRE_TIME 30
72 #define CACHE_REMOVAL_FRACTION 4
73
74 QT_BEGIN_NAMESPACE
75
76 // The cache limit describes the maximum "junk" in the cache.
77 static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded in qpixmapcache.cpp
78
79 QSGTexture *QDeclarativeDefaultTextureFactory::createTexture() const
80 {
81     QSGPlainTexture *t = new QSGPlainTexture();
82     t->setImage(im);
83     return t;
84 }
85
86 class QDeclarativePixmapReader;
87 class QDeclarativePixmapData;
88 class QDeclarativePixmapReply : public QObject
89 {
90     Q_OBJECT
91 public:
92     enum ReadError { NoError, Loading, Decoding };
93
94     QDeclarativePixmapReply(QDeclarativePixmapData *);
95     ~QDeclarativePixmapReply();
96
97     QDeclarativePixmapData *data;
98     QDeclarativeEngine *engineForReader; // always access reader inside readerMutex
99     QSize requestSize;
100     QUrl url;
101
102     bool loading;
103     int redirectCount;
104
105     class Event : public QEvent {
106     public:
107         Event(ReadError, const QString &, const QSize &, QDeclarativeTextureFactory *factory, const QImage &image);
108
109         ReadError error;
110         QString errorString;
111         QSize implicitSize;
112         QImage image;
113         QDeclarativeTextureFactory *textureFactory;
114     };
115     void postReply(ReadError, const QString &, const QSize &, const QImage &image);
116     void postReply(ReadError, const QString &, const QSize &, QDeclarativeTextureFactory *factory, const QImage &image);
117
118
119 Q_SIGNALS:
120     void finished();
121     void downloadProgress(qint64, qint64);
122
123 protected:
124     bool event(QEvent *event);
125
126 private:
127     Q_DISABLE_COPY(QDeclarativePixmapReply)
128
129 public:
130     static int finishedIndex;
131     static int downloadProgressIndex;
132 };
133
134 class QDeclarativePixmapReaderThreadObject : public QObject {
135     Q_OBJECT
136 public:
137     QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *);
138     void processJobs();
139     virtual bool event(QEvent *e);
140 private slots:
141     void networkRequestDone();
142 private:
143     QDeclarativePixmapReader *reader;
144 };
145
146 class QDeclarativePixmapData;
147 class QDeclarativePixmapReader : public QThread
148 {
149     Q_OBJECT
150 public:
151     QDeclarativePixmapReader(QDeclarativeEngine *eng);
152     ~QDeclarativePixmapReader();
153
154     QDeclarativePixmapReply *getImage(QDeclarativePixmapData *);
155     void cancel(QDeclarativePixmapReply *rep);
156
157     static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine);
158     static QDeclarativePixmapReader *existingInstance(QDeclarativeEngine *engine);
159
160 protected:
161     void run();
162
163 private:
164     friend class QDeclarativePixmapReaderThreadObject;
165     void processJobs();
166     void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &);
167     void networkRequestDone(QNetworkReply *);
168
169     QList<QDeclarativePixmapReply*> jobs;
170     QList<QDeclarativePixmapReply*> cancelled;
171     QDeclarativeEngine *engine;
172     QObject *eventLoopQuitHack;
173
174     QMutex mutex;
175     QDeclarativePixmapReaderThreadObject *threadObject;
176     QWaitCondition waitCondition;
177
178     QNetworkAccessManager *networkAccessManager();
179     QNetworkAccessManager *accessManager;
180
181     QHash<QNetworkReply*,QDeclarativePixmapReply*> replies;
182
183     static int replyDownloadProgress;
184     static int replyFinished;
185     static int downloadProgress;
186     static int threadNetworkRequestDone;
187     static QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> readers;
188 public:
189     static QMutex readerMutex;
190 };
191
192 class QDeclarativePixmapData
193 {
194 public:
195     QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QSize &s, const QString &e)
196     : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error), 
197       url(u), errorString(e), requestSize(s), textureFactory(0), reply(0), prevUnreferenced(0),
198       prevUnreferencedPtr(0), nextUnreferenced(0)
199     {
200         declarativePixmaps.insert(pixmap);
201     }
202
203     QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QSize &r)
204     : refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading), 
205       url(u), requestSize(r), textureFactory(0), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0),
206       nextUnreferenced(0)
207     {
208         declarativePixmaps.insert(pixmap);
209     }
210
211     QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, const QImage &p, const QSize &s, const QSize &r)
212     : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
213       url(u), image(p), implicitSize(s), requestSize(r), textureFactory(new QDeclarativeDefaultTextureFactory(p)), reply(0), prevUnreferenced(0),
214       prevUnreferencedPtr(0), nextUnreferenced(0)
215     {
216         declarativePixmaps.insert(pixmap);
217     }
218
219     QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QUrl &u, QDeclarativeTextureFactory *factory, const QImage &p, const QSize &s, const QSize &r)
220     : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
221       url(u), image(p), implicitSize(s), requestSize(r), textureFactory(factory), reply(0), prevUnreferenced(0),
222       prevUnreferencedPtr(0), nextUnreferenced(0)
223     {
224         declarativePixmaps.insert(pixmap);
225     }
226
227     QDeclarativePixmapData(QDeclarativePixmap *pixmap, const QImage &p)
228     : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready),
229       image(p), implicitSize(p.size()), requestSize(p.size()), textureFactory(new QDeclarativeDefaultTextureFactory(p)), reply(0), prevUnreferenced(0),
230       prevUnreferencedPtr(0), nextUnreferenced(0)
231     {
232         declarativePixmaps.insert(pixmap);
233     }
234
235     ~QDeclarativePixmapData()
236     {
237         while (!declarativePixmaps.isEmpty()) {
238             QDeclarativePixmap *referencer = declarativePixmaps.first();
239             declarativePixmaps.remove(referencer);
240             referencer->d = 0;
241         }
242         delete textureFactory;
243     }
244
245     int cost() const;
246     void addref();
247     void release();
248     void addToCache();
249     void removeFromCache();
250
251     uint refCount;
252
253     bool inCache:1;
254     bool privatePixmap:1;
255     
256     QDeclarativePixmap::Status pixmapStatus;
257     QUrl url;
258     QString errorString;
259     QImage image;
260     QSize implicitSize;
261     QSize requestSize;
262
263     QDeclarativeTextureFactory *textureFactory;
264
265     QIntrusiveList<QDeclarativePixmap, &QDeclarativePixmap::dataListNode> declarativePixmaps;
266     QDeclarativePixmapReply *reply;
267
268     QDeclarativePixmapData *prevUnreferenced;
269     QDeclarativePixmapData**prevUnreferencedPtr;
270     QDeclarativePixmapData *nextUnreferenced;
271 };
272
273 int QDeclarativePixmapReply::finishedIndex = -1;
274 int QDeclarativePixmapReply::downloadProgressIndex = -1;
275
276 // XXX
277 QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> QDeclarativePixmapReader::readers;
278 QMutex QDeclarativePixmapReader::readerMutex;
279
280 int QDeclarativePixmapReader::replyDownloadProgress = -1;
281 int QDeclarativePixmapReader::replyFinished = -1;
282 int QDeclarativePixmapReader::downloadProgress = -1;
283 int QDeclarativePixmapReader::threadNetworkRequestDone = -1;
284
285
286 void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
287                                         const QSize &implicitSize, const QImage &image)
288 {
289     loading = false;
290     QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, new QDeclarativeDefaultTextureFactory(image), image));
291 }
292
293 void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
294                                         const QSize &implicitSize, QDeclarativeTextureFactory *factory,
295                                         const QImage &image)
296 {
297     loading = false;
298     QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, factory, image));
299 }
300
301 QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, QDeclarativeTextureFactory *factory, const QImage &i)
302     : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i), textureFactory(factory)
303 {
304 }
305
306 QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager()
307 {
308     if (!accessManager) {
309         Q_ASSERT(threadObject);
310         accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
311     }
312     return accessManager;
313 }
314
315 static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, 
316                       const QSize &requestSize)
317 {
318     QImageReader imgio(dev);
319
320     bool force_scale = false;
321     if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) {
322         imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053
323         force_scale = true;
324     }
325
326     bool scaled = false;
327     if (requestSize.width() > 0 || requestSize.height() > 0) {
328         QSize s = imgio.size();
329         if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
330             if (requestSize.height() <= 0)
331                 s.setHeight(s.height()*requestSize.width()/s.width());
332             s.setWidth(requestSize.width()); scaled = true;
333         }
334         if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
335             if (requestSize.width() <= 0)
336                 s.setWidth(s.width()*requestSize.height()/s.height());
337             s.setHeight(requestSize.height()); scaled = true;
338         }
339         if (scaled) { imgio.setScaledSize(s); }
340     }
341
342     if (impsize)
343         *impsize = imgio.size();
344
345     if (imgio.read(image)) {
346         if (impsize && impsize->width() < 0)
347             *impsize = image->size();
348         return true;
349     } else {
350         if (errorString)
351             *errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString())
352                                 .arg(imgio.errorString());
353         return false;
354     }
355 }
356
357 QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng)
358 : QThread(eng), engine(eng), threadObject(0), accessManager(0)
359 {
360     eventLoopQuitHack = new QObject;
361     eventLoopQuitHack->moveToThread(this);
362     connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
363     start(QThread::LowestPriority);
364 }
365
366 QDeclarativePixmapReader::~QDeclarativePixmapReader()
367 {
368     readerMutex.lock();
369     readers.remove(engine);
370     readerMutex.unlock();
371
372     mutex.lock();
373     // manually cancel all outstanding jobs.
374     foreach (QDeclarativePixmapReply *reply, jobs) {
375         delete reply;
376     }
377     jobs.clear();
378     QList<QDeclarativePixmapReply*> activeJobs = replies.values();
379     foreach (QDeclarativePixmapReply *reply, activeJobs) {
380         if (reply->loading) {
381             cancelled.append(reply);
382             reply->data = 0;
383         }
384     }
385     if (threadObject) threadObject->processJobs();
386     mutex.unlock();
387
388     eventLoopQuitHack->deleteLater();
389     wait();
390 }
391
392 void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply)
393 {
394     QDeclarativePixmapReply *job = replies.take(reply);
395
396     if (job) {
397         job->redirectCount++;
398         if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
399             QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
400             if (redirect.isValid()) {
401                 QUrl url = reply->url().resolved(redirect.toUrl());
402                 QNetworkRequest req(url);
403                 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
404
405                 reply->deleteLater();
406                 reply = networkAccessManager()->get(req);
407
408                 QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
409                 QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
410
411                 replies.insert(reply, job);
412                 return;
413             }
414         }
415
416         QImage image;
417         QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError;
418         QString errorString;
419         QSize readSize;
420         if (reply->error()) {
421             error = QDeclarativePixmapReply::Loading;
422             errorString = reply->errorString();
423         } else {
424             QByteArray all = reply->readAll();
425             QBuffer buff(&all);
426             buff.open(QIODevice::ReadOnly);
427             if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize))
428                 error = QDeclarativePixmapReply::Decoding;
429        }
430         // send completion event to the QDeclarativePixmapReply
431         mutex.lock();
432         if (!cancelled.contains(job)) {
433             QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
434             if (factory)
435                 job->postReply(error, errorString, readSize, factory, image);
436             else
437                 job->postReply(error, errorString, readSize, image);
438         }
439         mutex.unlock();
440     }
441     reply->deleteLater();
442
443     // kick off event loop again incase we have dropped below max request count
444     threadObject->processJobs();
445 }
446
447 QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i)
448 : reader(i)
449 {
450 }
451
452 void QDeclarativePixmapReaderThreadObject::processJobs() 
453
454     QCoreApplication::postEvent(this, new QEvent(QEvent::User)); 
455 }
456
457 bool QDeclarativePixmapReaderThreadObject::event(QEvent *e) 
458 {
459     if (e->type() == QEvent::User) { 
460         reader->processJobs(); 
461         return true; 
462     } else { 
463         return QObject::event(e);
464     }
465 }
466
467 void QDeclarativePixmapReaderThreadObject::networkRequestDone()
468 {
469     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
470     reader->networkRequestDone(reply);
471 }
472
473 void QDeclarativePixmapReader::processJobs()
474 {
475     QMutexLocker locker(&mutex);
476
477     while (true) {
478         if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT)) 
479             return; // Nothing else to do
480
481         // Clean cancelled jobs
482         if (cancelled.count()) {
483             for (int i = 0; i < cancelled.count(); ++i) {
484                 QDeclarativePixmapReply *job = cancelled.at(i);
485                 QNetworkReply *reply = replies.key(job, 0);
486                 if (reply && reply->isRunning()) {
487                     // cancel any jobs already started
488                     replies.remove(reply);
489                     reply->close();
490                 }
491                 // deleteLater, since not owned by this thread
492                 job->deleteLater();
493             }
494             cancelled.clear();
495         }
496
497         if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
498             QDeclarativePixmapReply *runningJob = jobs.takeLast();
499             runningJob->loading = true;
500
501             QUrl url = runningJob->url;
502             QSize requestSize = runningJob->requestSize;
503             locker.unlock();
504             processJob(runningJob, url, requestSize);
505             locker.relock();
506         }
507     }
508 }
509
510 void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url, 
511                                           const QSize &requestSize)
512 {
513     // fetch
514     if (url.scheme() == QLatin1String("image")) {
515         // Use QmlImageProvider
516         QSize readSize;
517         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
518         QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
519         if (imageType == QDeclarativeImageProvider::Invalid) {
520             QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::Loading;
521             QString errorStr = QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString());
522             QImage image;
523             mutex.lock();
524             if (!cancelled.contains(runningJob))
525                 runningJob->postReply(errorCode, errorStr, readSize, image);
526             mutex.unlock();
527         } else if (imageType == QDeclarativeImageProvider::Image) {
528             QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
529             QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
530             QString errorStr;
531             if (image.isNull()) {
532                 errorCode = QDeclarativePixmapReply::Loading;
533                 errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString());
534             }
535             mutex.lock();
536             if (!cancelled.contains(runningJob)) {
537                 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
538                 if (factory)
539                     runningJob->postReply(errorCode, errorStr, readSize, factory, image);
540                 else
541                     runningJob->postReply(errorCode, errorStr, readSize, image);
542             }
543
544             mutex.unlock();
545         } else {
546             QDeclarativeTextureFactory *t = ep->getTextureFromProvider(url, &readSize, requestSize);
547             QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
548             QString errorStr;
549             if (!t) {
550                 errorCode = QDeclarativePixmapReply::Loading;
551                 errorStr = QDeclarativePixmap::tr("Failed to get texture from provider: %1").arg(url.toString());
552             }
553             mutex.lock();
554             if (!cancelled.contains(runningJob))
555                 runningJob->postReply(errorCode, errorStr, readSize, t, QImage());
556             mutex.unlock();
557
558         }
559
560     } else {
561         QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
562         if (!lf.isEmpty()) {
563             // Image is local - load/decode immediately
564             QImage image;
565             QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
566             QString errorStr;
567             QFile f(lf);
568             QSize readSize;
569             if (f.open(QIODevice::ReadOnly)) {
570                 if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize))
571                     errorCode = QDeclarativePixmapReply::Loading;
572             } else {
573                 errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
574                 errorCode = QDeclarativePixmapReply::Loading;
575             }
576             mutex.lock();
577             if (!cancelled.contains(runningJob)) {
578                 QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
579                 if (factory)
580                     runningJob->postReply(errorCode, errorStr, readSize, factory, image);
581                 else
582                     runningJob->postReply(errorCode, errorStr, readSize, image);
583             }
584             mutex.unlock();
585         } else {
586             // Network resource
587             QNetworkRequest req(url);
588             req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
589             QNetworkReply *reply = networkAccessManager()->get(req);
590
591             QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
592             QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
593
594             replies.insert(reply, runningJob);
595         }
596     }
597 }
598
599 QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine)
600 {
601     // XXX NOTE: must be called within readerMutex locking.
602     QDeclarativePixmapReader *reader = readers.value(engine);
603     if (!reader) {
604         reader = new QDeclarativePixmapReader(engine);
605         readers.insert(engine, reader);
606     }
607
608     return reader;
609 }
610
611 QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine)
612 {
613     // XXX NOTE: must be called within readerMutex locking.
614     return readers.value(engine, 0);
615 }
616
617 QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data)
618 {
619     mutex.lock();
620     QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data);
621     reply->engineForReader = engine;
622     jobs.append(reply);
623     // XXX 
624     if (threadObject) threadObject->processJobs();
625     mutex.unlock();
626     return reply;
627 }
628
629 void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply)
630 {
631     mutex.lock();
632     if (reply->loading) {
633         cancelled.append(reply);
634         reply->data = 0;
635         // XXX 
636         if (threadObject) threadObject->processJobs();
637     } else {
638         jobs.removeAll(reply);
639         delete reply;
640     }
641     mutex.unlock();
642 }
643
644 void QDeclarativePixmapReader::run()
645 {
646     if (replyDownloadProgress == -1) {
647         const QMetaObject *nr = &QNetworkReply::staticMetaObject;
648         const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject;
649         const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject;
650         replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
651         replyFinished = nr->indexOfSignal("finished()");
652         downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
653         threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
654     }
655
656     mutex.lock();
657     threadObject = new QDeclarativePixmapReaderThreadObject(this);
658     mutex.unlock();
659
660     processJobs();
661     exec();
662
663     delete threadObject;
664     threadObject = 0;
665 }
666
667 class QDeclarativePixmapKey
668 {
669 public:
670     const QUrl *url;
671     const QSize *size;
672 };
673
674 inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs)
675 {
676     return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
677 }
678
679 inline uint qHash(const QDeclarativePixmapKey &key)
680 {
681     return qHash(*key.url) ^ key.size->width() ^ key.size->height();
682 }
683
684 class QSGContext;
685
686 class QDeclarativePixmapStore : public QObject
687 {
688     Q_OBJECT
689 public:
690     QDeclarativePixmapStore();
691     ~QDeclarativePixmapStore();
692
693     void unreferencePixmap(QDeclarativePixmapData *);
694     void referencePixmap(QDeclarativePixmapData *);
695
696 protected:
697     virtual void timerEvent(QTimerEvent *);
698
699 public:
700     QHash<QDeclarativePixmapKey, QDeclarativePixmapData *> m_cache;
701
702 private:
703     void shrinkCache(int remove);
704
705     QDeclarativePixmapData *m_unreferencedPixmaps;
706     QDeclarativePixmapData *m_lastUnreferencedPixmap;
707
708     int m_unreferencedCost;
709     int m_timerId;
710 };
711 Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore);
712
713
714 QDeclarativePixmapStore::QDeclarativePixmapStore()
715 : m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1)
716 {
717 }
718
719 QDeclarativePixmapStore::~QDeclarativePixmapStore()
720 {
721     int leakedPixmaps = 0;
722     QList<QDeclarativePixmapData*> cachedData = m_cache.values();
723
724     // unreference all (leaked) pixmaps
725     foreach (QDeclarativePixmapData* pixmap, cachedData) {
726         int currRefCount = pixmap->refCount;
727         if (currRefCount) {
728             leakedPixmaps++;
729             while (currRefCount > 0) {
730                 pixmap->release();
731                 currRefCount--;
732             }
733         }
734     }
735
736     // free all unreferenced pixmaps
737     while (m_lastUnreferencedPixmap) {
738         shrinkCache(20);
739     }
740
741     if (leakedPixmaps)
742         qDebug("Number of leaked pixmaps: %i", leakedPixmaps);
743 }
744
745 void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data)
746 {
747     Q_ASSERT(data->prevUnreferenced == 0);
748     Q_ASSERT(data->prevUnreferencedPtr == 0);
749     Q_ASSERT(data->nextUnreferenced == 0);
750
751     data->nextUnreferenced = m_unreferencedPixmaps;
752     data->prevUnreferencedPtr = &m_unreferencedPixmaps;
753
754     m_unreferencedPixmaps = data;
755     if (m_unreferencedPixmaps->nextUnreferenced) {
756         m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
757         m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
758     }
759
760     if (!m_lastUnreferencedPixmap)
761         m_lastUnreferencedPixmap = data;
762
763     m_unreferencedCost += data->cost();
764
765     shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
766
767     if (m_timerId == -1 && m_unreferencedPixmaps) 
768         m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
769 }
770
771 void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data)
772 {
773     Q_ASSERT(data->prevUnreferencedPtr);
774
775     *data->prevUnreferencedPtr = data->nextUnreferenced;
776     if (data->nextUnreferenced) { 
777         data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
778         data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
779     }
780     if (m_lastUnreferencedPixmap == data)
781         m_lastUnreferencedPixmap = data->prevUnreferenced;
782
783     data->nextUnreferenced = 0;
784     data->prevUnreferencedPtr = 0;
785     data->prevUnreferenced = 0;
786
787     m_unreferencedCost -= data->cost();
788 }
789
790 void QDeclarativePixmapStore::shrinkCache(int remove)
791 {
792     while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
793         QDeclarativePixmapData *data = m_lastUnreferencedPixmap;
794         Q_ASSERT(data->nextUnreferenced == 0);
795
796         *data->prevUnreferencedPtr = 0;
797         m_lastUnreferencedPixmap = data->prevUnreferenced;
798         data->prevUnreferencedPtr = 0;
799         data->prevUnreferenced = 0;
800
801         remove -= data->cost();
802         m_unreferencedCost -= data->cost();
803         data->removeFromCache();
804         delete data;
805     }
806 }
807
808 void QDeclarativePixmapStore::timerEvent(QTimerEvent *)
809 {
810     int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
811
812     shrinkCache(removalCost);
813
814     if (m_unreferencedPixmaps == 0) {
815         killTimer(m_timerId);
816         m_timerId = -1;
817     }
818 }
819
820 QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d)
821 : data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0)
822 {
823     if (finishedIndex == -1) {
824         finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()");
825         downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
826     }
827 }
828
829 QDeclarativePixmapReply::~QDeclarativePixmapReply()
830 {
831 }
832
833 bool QDeclarativePixmapReply::event(QEvent *event)
834 {
835     if (event->type() == QEvent::User) {
836
837         if (data) {
838             Event *de = static_cast<Event *>(event);
839             data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error;
840
841             if (data->pixmapStatus == QDeclarativePixmap::Ready) {
842                 if (de->textureFactory) {
843                     data->textureFactory = de->textureFactory;
844                 }
845                 data->image = de->image;
846                 data->implicitSize = de->implicitSize;
847             } else {
848                 data->errorString = de->errorString;
849                 data->removeFromCache(); // We don't continue to cache error'd pixmaps
850             }
851
852             data->reply = 0;
853             emit finished();
854         }
855
856         delete this;
857         return true;
858     } else {
859         return QObject::event(event);
860     }
861 }
862
863 int QDeclarativePixmapData::cost() const
864 {
865     if (textureFactory)
866         return textureFactory->textureByteCount();
867     return image.byteCount();
868 }
869
870 void QDeclarativePixmapData::addref()
871 {
872     ++refCount;
873     if (prevUnreferencedPtr) 
874         pixmapStore()->referencePixmap(this);
875 }
876
877 void QDeclarativePixmapData::release()
878 {
879     Q_ASSERT(refCount > 0);
880     --refCount;
881     if (refCount == 0) {
882         if (reply) {
883             QDeclarativePixmapReply *cancelReply = reply;
884             reply->data = 0;
885             reply = 0;
886             QDeclarativePixmapReader::readerMutex.lock();
887             QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader);
888             if (reader)
889                 reader->cancel(cancelReply);
890             QDeclarativePixmapReader::readerMutex.unlock();
891         }
892
893         if (pixmapStatus == QDeclarativePixmap::Ready) {
894             pixmapStore()->unreferencePixmap(this);
895         } else {
896             removeFromCache();
897             delete this;
898         }
899     }
900 }
901
902 void QDeclarativePixmapData::addToCache()
903 {
904     if (!inCache) {
905         QDeclarativePixmapKey key = { &url, &requestSize };
906         pixmapStore()->m_cache.insert(key, this);
907         inCache = true;
908     }
909 }
910
911 void QDeclarativePixmapData::removeFromCache()
912 {
913     if (inCache) {
914         QDeclarativePixmapKey key = { &url, &requestSize };
915         pixmapStore()->m_cache.remove(key);
916         inCache = false;
917     }
918 }
919
920 static QDeclarativePixmapData* createPixmapDataSync(QDeclarativePixmap *declarativePixmap, QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
921 {
922     if (url.scheme() == QLatin1String("image")) {
923         QSize readSize;
924         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
925         QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
926
927         switch (imageType) {
928             case QDeclarativeImageProvider::Invalid:
929                 return new QDeclarativePixmapData(declarativePixmap, url, requestSize,
930                     QDeclarativePixmap::tr("Invalid image provider: %1").arg(url.toString()));
931             case QDeclarativeImageProvider::Texture:
932             {
933                 QDeclarativeTextureFactory *texture = ep->getTextureFromProvider(url, &readSize, requestSize);
934                 if (texture) {
935                     *ok = true;
936                     return new QDeclarativePixmapData(declarativePixmap, url, texture, QImage(), readSize, requestSize);
937                 }
938             }
939
940             case QDeclarativeImageProvider::Image:
941             {
942                 QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
943                 if (!image.isNull()) {
944                     *ok = true;
945                     return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize);
946                 }
947             }
948             case QDeclarativeImageProvider::Pixmap:
949             {
950                 QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
951                 if (!pixmap.isNull()) {
952                     *ok = true;
953                     return new QDeclarativePixmapData(declarativePixmap, url, pixmap.toImage(), readSize, requestSize);
954                 }
955             }
956         }
957
958         // provider has bad image type, or provider returned null image
959         return new QDeclarativePixmapData(declarativePixmap, url, requestSize,
960             QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
961     }
962
963     QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
964     if (localFile.isEmpty()) 
965         return 0;
966
967     QFile f(localFile);
968     QSize readSize;
969     QString errorString;
970
971     if (f.open(QIODevice::ReadOnly)) {
972         QImage image;
973
974         if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) {
975             *ok = true;
976             return new QDeclarativePixmapData(declarativePixmap, url, image, readSize, requestSize);
977         }
978         errorString = QDeclarativePixmap::tr("Invalid image data: %1").arg(url.toString());
979
980     } else {
981         errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
982     }
983     return new QDeclarativePixmapData(declarativePixmap, url, requestSize, errorString);
984 }
985
986
987 struct QDeclarativePixmapNull {
988     QUrl url;
989     QImage image;
990     QSize size;
991 };
992 Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap);
993
994 QDeclarativePixmap::QDeclarativePixmap()
995 : d(0)
996 {
997 }
998
999 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url)
1000 : d(0)
1001 {
1002     load(engine, url);
1003 }
1004
1005 QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1006 : d(0)
1007 {
1008     load(engine, url, size);
1009 }
1010
1011 QDeclarativePixmap::~QDeclarativePixmap()
1012 {
1013     if (d) {
1014         d->declarativePixmaps.remove(this);
1015         d->release();
1016         d = 0;
1017     }
1018 }
1019
1020 bool QDeclarativePixmap::isNull() const
1021 {
1022     return d == 0;
1023 }
1024
1025 bool QDeclarativePixmap::isReady() const
1026 {
1027     return status() == Ready;
1028 }
1029
1030 bool QDeclarativePixmap::isError() const
1031 {
1032     return status() == Error;
1033 }
1034
1035 bool QDeclarativePixmap::isLoading() const
1036 {
1037     return status() == Loading;
1038 }
1039
1040 QString QDeclarativePixmap::error() const
1041 {
1042     if (d)
1043         return d->errorString;
1044     else
1045         return QString();
1046 }
1047
1048 QDeclarativePixmap::Status QDeclarativePixmap::status() const
1049 {
1050     if (d)
1051         return d->pixmapStatus;
1052     else
1053         return Null;
1054 }
1055
1056 const QUrl &QDeclarativePixmap::url() const
1057 {
1058     if (d)
1059         return d->url;
1060     else
1061         return nullPixmap()->url;
1062 }
1063
1064 const QSize &QDeclarativePixmap::implicitSize() const
1065 {
1066     if (d) 
1067         return d->implicitSize;
1068     else
1069         return nullPixmap()->size;
1070 }
1071
1072 const QSize &QDeclarativePixmap::requestSize() const
1073 {
1074     if (d)
1075         return d->requestSize;
1076     else
1077         return nullPixmap()->size;
1078 }
1079
1080 QDeclarativeTextureFactory *QDeclarativePixmap::textureFactory() const
1081 {
1082     if (d)
1083         return d->textureFactory;
1084
1085     return 0;
1086 }
1087
1088 const QImage &QDeclarativePixmap::image() const
1089 {
1090     if (d) 
1091         return d->image;
1092     else
1093         return nullPixmap()->image;
1094 }
1095
1096 void QDeclarativePixmap::setImage(const QImage &p)
1097 {
1098     clear();
1099
1100     if (!p.isNull())
1101         d = new QDeclarativePixmapData(this, p);
1102 }
1103
1104 int QDeclarativePixmap::width() const
1105 {
1106     if (d) 
1107         return d->textureFactory ? d->textureFactory->textureSize().width() : d->image.width();
1108     else
1109         return 0;
1110 }
1111
1112 int QDeclarativePixmap::height() const
1113 {
1114     if (d) 
1115         return d->textureFactory ? d->textureFactory->textureSize().height() : d->image.height();
1116     else
1117         return 0;
1118 }
1119
1120 QRect QDeclarativePixmap::rect() const
1121 {
1122     if (d)
1123         return d->textureFactory ? QRect(QPoint(), d->textureFactory->textureSize()) : d->image.rect();
1124     else
1125         return QRect();
1126 }
1127
1128 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url)
1129 {
1130     load(engine, url, QSize(), QDeclarativePixmap::Cache);
1131 }
1132
1133 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options)
1134 {
1135     load(engine, url, QSize(), options);
1136 }
1137
1138 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
1139 {
1140     load(engine, url, size, QDeclarativePixmap::Cache);
1141 }
1142
1143 void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options)
1144 {
1145     if (d) {
1146         d->declarativePixmaps.remove(this);
1147         d->release();
1148         d = 0;
1149     }
1150
1151     QDeclarativePixmapKey key = { &url, &requestSize };
1152     QDeclarativePixmapStore *store = pixmapStore();
1153
1154     QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key);
1155
1156     if (iter == store->m_cache.end()) {
1157         if (options & QDeclarativePixmap::Asynchronous) {
1158             // pixmaps can only be loaded synchronously
1159             if (url.scheme() == QLatin1String("image") 
1160                     && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) {
1161                 options &= ~QDeclarativePixmap::Asynchronous;
1162             }
1163         }
1164
1165         if (!(options & QDeclarativePixmap::Asynchronous)) {
1166             bool ok = false;
1167             d = createPixmapDataSync(this, engine, url, requestSize, &ok);
1168             if (ok) {
1169                 if (options & QDeclarativePixmap::Cache)
1170                     d->addToCache();
1171                 return;
1172             }
1173             if (d)  // loadable, but encountered error while loading
1174                 return;
1175         } 
1176
1177         if (!engine)
1178             return;
1179
1180         d = new QDeclarativePixmapData(this, url, requestSize);
1181         if (options & QDeclarativePixmap::Cache)
1182             d->addToCache();
1183
1184         QDeclarativePixmapReader::readerMutex.lock();
1185         d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d);
1186         QDeclarativePixmapReader::readerMutex.unlock();
1187     } else {
1188         d = *iter;
1189         d->addref();
1190         d->declarativePixmaps.insert(this);
1191     }
1192 }
1193
1194 void QDeclarativePixmap::clear()
1195 {
1196     if (d) {
1197         d->declarativePixmaps.remove(this);
1198         d->release();
1199         d = 0;
1200     }
1201 }
1202
1203 void QDeclarativePixmap::clear(QObject *obj)
1204 {
1205     if (d) {
1206         if (d->reply) 
1207             QObject::disconnect(d->reply, 0, obj, 0);
1208         d->declarativePixmaps.remove(this);
1209         d->release();
1210         d = 0;
1211     }
1212 }
1213
1214 bool QDeclarativePixmap::connectFinished(QObject *object, const char *method)
1215 {
1216     if (!d || !d->reply) {
1217         qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1218         return false;
1219     }
1220
1221     return QObject::connect(d->reply, SIGNAL(finished()), object, method);
1222 }
1223
1224 bool QDeclarativePixmap::connectFinished(QObject *object, int method)
1225 {
1226     if (!d || !d->reply) {
1227         qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
1228         return false;
1229     }
1230
1231     return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method);
1232 }
1233
1234 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method)
1235 {
1236     if (!d || !d->reply) {
1237         qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1238         return false;
1239     }
1240
1241     return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
1242 }
1243
1244 bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method)
1245 {
1246     if (!d || !d->reply) {
1247         qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
1248         return false;
1249     }
1250
1251     return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method);
1252 }
1253
1254 QT_END_NAMESPACE
1255
1256 #include <qdeclarativepixmapcache.moc>