src/declarative/util/qdeclarativepixmapcache.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 <QBuffer>
       
    59 #include <QWaitCondition>
       
    60 #include <QtCore/qdebug.h>
       
    61 #include <private/qobject_p.h>
       
    62 #include <QSslError>
       
    63 
       
    64 // Maximum number of simultaneous image requests to send.
       
    65 static const int maxImageRequestCount = 8;
       
    66 
       
    67 QT_BEGIN_NAMESPACE
       
    68 
       
    69 class QDeclarativeImageReaderEvent : public QEvent
       
    70 {
       
    71 public:
       
    72     enum ReadError { NoError, Loading, Decoding };
       
    73 
       
    74     QDeclarativeImageReaderEvent(QDeclarativeImageReaderEvent::ReadError err, const QString &errStr, const QImage &img)
       
    75         : QEvent(QEvent::User), error(err), errorString(errStr), image(img) {}
       
    76 
       
    77     ReadError error;
       
    78     QString errorString;
       
    79     QImage image;
       
    80 };
       
    81 
       
    82 class QDeclarativeImageRequestHandler;
       
    83 class QDeclarativeImageReader : public QThread
       
    84 {
       
    85     Q_OBJECT
       
    86 public:
       
    87     QDeclarativeImageReader(QDeclarativeEngine *eng);
       
    88     ~QDeclarativeImageReader();
       
    89 
       
    90     QDeclarativePixmapReply *getImage(const QUrl &url, int req_width, int req_height);
       
    91     void cancel(QDeclarativePixmapReply *rep);
       
    92 
       
    93     static QDeclarativeImageReader *instance(QDeclarativeEngine *engine);
       
    94 
       
    95 protected:
       
    96     void run();
       
    97 
       
    98 private:
       
    99     QList<QDeclarativePixmapReply*> jobs;
       
   100     QList<QDeclarativePixmapReply*> cancelled;
       
   101     QDeclarativeEngine *engine;
       
   102     QDeclarativeImageRequestHandler *handler;
       
   103     QObject *eventLoopQuitHack;
       
   104     QMutex mutex;
       
   105 
       
   106     static QHash<QDeclarativeEngine *,QDeclarativeImageReader*> readers;
       
   107     static QMutex readerMutex;
       
   108     friend class QDeclarativeImageRequestHandler;
       
   109 };
       
   110 
       
   111 QHash<QDeclarativeEngine *,QDeclarativeImageReader*> QDeclarativeImageReader::readers;
       
   112 QMutex QDeclarativeImageReader::readerMutex;
       
   113 
       
   114 
       
   115 class QDeclarativeImageRequestHandler : public QObject
       
   116 {
       
   117     Q_OBJECT
       
   118 public:
       
   119     QDeclarativeImageRequestHandler(QDeclarativeImageReader *read, QDeclarativeEngine *eng)
       
   120         : QObject(), accessManager(0), engine(eng), reader(read), redirectCount(0)
       
   121     {
       
   122         QCoreApplication::postEvent(this, new QEvent(QEvent::User));
       
   123     }
       
   124 
       
   125     QDeclarativePixmapReply *getImage(const QUrl &url, int req_width, int req_height);
       
   126     void cancel(QDeclarativePixmapReply *reply);
       
   127 
       
   128 protected:
       
   129     bool event(QEvent *event);
       
   130 
       
   131 private slots:
       
   132     void networkRequestDone();
       
   133 
       
   134 private:
       
   135     QNetworkAccessManager *networkAccessManager() {
       
   136         if (!accessManager)
       
   137             accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(this);
       
   138         return accessManager;
       
   139     }
       
   140 
       
   141     QHash<QNetworkReply*,QDeclarativePixmapReply*> replies;
       
   142     QNetworkAccessManager *accessManager;
       
   143     QDeclarativeEngine *engine;
       
   144     QDeclarativeImageReader *reader;
       
   145     int redirectCount;
       
   146 
       
   147     static int replyDownloadProgress;
       
   148     static int replyFinished;
       
   149     static int downloadProgress;
       
   150     static int thisNetworkRequestDone;
       
   151 };
       
   152 
       
   153 //===========================================================================
       
   154 
       
   155 static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int req_width, int req_height)
       
   156 {
       
   157     QImageReader imgio(dev);
       
   158 
       
   159     bool force_scale = false;
       
   160     if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) {
       
   161         imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053
       
   162         force_scale = true;
       
   163     }
       
   164 
       
   165     bool scaled = false;
       
   166     if (req_width > 0 || req_height > 0) {
       
   167         QSize s = imgio.size();
       
   168         if (req_width && (force_scale || req_width < s.width())) {
       
   169             if (req_height <= 0)
       
   170                 s.setHeight(s.height()*req_width/s.width());
       
   171             s.setWidth(req_width); scaled = true;
       
   172         }
       
   173         if (req_height && (force_scale || req_height < s.height())) {
       
   174             if (req_width <= 0)
       
   175                 s.setWidth(s.width()*req_height/s.height());
       
   176             s.setHeight(req_height); scaled = true;
       
   177         }
       
   178         if (scaled) { imgio.setScaledSize(s); }
       
   179     }
       
   180 
       
   181     if (impsize)
       
   182         *impsize = imgio.size();
       
   183 
       
   184     if (imgio.read(image)) {
       
   185         if (impsize && impsize->width() < 0)
       
   186             *impsize = image->size();
       
   187         return true;
       
   188     } else {
       
   189         if (errorString)
       
   190             *errorString = QDeclarativePixmapCache::tr("Error decoding: %1: %2").arg(url.toString())
       
   191                                 .arg(imgio.errorString());
       
   192         return false;
       
   193     }
       
   194 }
       
   195 
       
   196 
       
   197 //===========================================================================
       
   198 
       
   199 int QDeclarativeImageRequestHandler::replyDownloadProgress = -1;
       
   200 int QDeclarativeImageRequestHandler::replyFinished = -1;
       
   201 int QDeclarativeImageRequestHandler::downloadProgress = -1;
       
   202 int QDeclarativeImageRequestHandler::thisNetworkRequestDone = -1;
       
   203 
       
   204 typedef QHash<QUrl, QSize> QDeclarativePixmapSizeHash;
       
   205 Q_GLOBAL_STATIC(QDeclarativePixmapSizeHash, qmlOriginalSizes);
       
   206 
       
   207 bool QDeclarativeImageRequestHandler::event(QEvent *event)
       
   208 {
       
   209     if (event->type() == QEvent::User) {
       
   210         if (replyDownloadProgress == -1) {
       
   211             replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
       
   212             replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()");
       
   213             downloadProgress = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
       
   214             thisNetworkRequestDone = QDeclarativeImageRequestHandler::staticMetaObject.indexOfSlot("networkRequestDone()");
       
   215         }
       
   216 
       
   217         while (1) {
       
   218             reader->mutex.lock();
       
   219 
       
   220             if (reader->cancelled.count()) {
       
   221                 for (int i = 0; i < reader->cancelled.count(); ++i) {
       
   222                     QDeclarativePixmapReply *job = reader->cancelled.at(i);
       
   223                     // cancel any jobs already started
       
   224                     QNetworkReply *reply = replies.key(job, 0);
       
   225                     if (reply && reply->isRunning()) {
       
   226                         replies.remove(reply);
       
   227                         reply->close();
       
   228                     }
       
   229                     // remove from pending job list
       
   230                     for (int j = 0; j < reader->jobs.count(); ++j) {
       
   231                         if (reader->jobs.at(j) == job) {
       
   232                             reader->jobs.removeAt(j);
       
   233                             job->release(true);
       
   234                             break;
       
   235                         }
       
   236                     }
       
   237                 }
       
   238                 reader->cancelled.clear();
       
   239             }
       
   240 
       
   241             if (!reader->jobs.count() || replies.count() > maxImageRequestCount) {
       
   242                 reader->mutex.unlock();
       
   243                 break;
       
   244             }
       
   245 
       
   246             QDeclarativePixmapReply *runningJob = reader->jobs.takeLast();
       
   247             QUrl url = runningJob->url();
       
   248             reader->mutex.unlock();
       
   249 
       
   250             // fetch
       
   251             if (url.scheme() == QLatin1String("image")) {
       
   252                 // Use QmlImageProvider
       
   253                 QSize read_impsize;
       
   254                 QImage image = QDeclarativeEnginePrivate::get(engine)->getImageFromProvider(url, &read_impsize, QSize(runningJob->forcedWidth(),runningJob->forcedHeight()));
       
   255                 qmlOriginalSizes()->insert(url, read_impsize);
       
   256                 QDeclarativeImageReaderEvent::ReadError errorCode = QDeclarativeImageReaderEvent::NoError;
       
   257                 QString errorStr;
       
   258                 if (image.isNull()) {
       
   259                     errorCode = QDeclarativeImageReaderEvent::Loading;
       
   260                     errorStr = QDeclarativePixmapCache::tr("Failed to get image from provider: %1").arg(url.toString());
       
   261                 }
       
   262                 QCoreApplication::postEvent(runningJob, new QDeclarativeImageReaderEvent(errorCode, errorStr, image));
       
   263             } else {
       
   264                 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
       
   265                 if (!lf.isEmpty()) {
       
   266                     // Image is local - load/decode immediately
       
   267                     QImage image;
       
   268                     QDeclarativeImageReaderEvent::ReadError errorCode = QDeclarativeImageReaderEvent::NoError;
       
   269                     QString errorStr;
       
   270                     QFile f(lf);
       
   271                     if (f.open(QIODevice::ReadOnly)) {
       
   272                         QSize read_impsize;
       
   273                         if (readImage(url, &f, &image, &errorStr, &read_impsize, runningJob->forcedWidth(),runningJob->forcedHeight())) {
       
   274                             qmlOriginalSizes()->insert(url, read_impsize);
       
   275                         } else {
       
   276                             errorCode = QDeclarativeImageReaderEvent::Loading;
       
   277                         }
       
   278                     } else {
       
   279                         errorStr = QDeclarativePixmapCache::tr("Cannot open: %1").arg(url.toString());
       
   280                         errorCode = QDeclarativeImageReaderEvent::Loading;
       
   281                     }
       
   282                     QCoreApplication::postEvent(runningJob, new QDeclarativeImageReaderEvent(errorCode, errorStr, image));
       
   283                 } else {
       
   284                     // Network resource
       
   285                     QNetworkRequest req(url);
       
   286                     req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
       
   287                     QNetworkReply *reply = networkAccessManager()->get(req);
       
   288 
       
   289                     QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
       
   290                     QMetaObject::connect(reply, replyFinished, this, thisNetworkRequestDone);
       
   291 
       
   292                     replies.insert(reply, runningJob);
       
   293                 }
       
   294             }
       
   295         }
       
   296         return true;
       
   297     }
       
   298 
       
   299     return QObject::event(event);
       
   300 }
       
   301 
       
   302 #define IMAGEREQUESTHANDLER_MAX_REDIRECT_RECURSION 16
       
   303 
       
   304 void QDeclarativeImageRequestHandler::networkRequestDone()
       
   305 {
       
   306     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
       
   307     QDeclarativePixmapReply *job = replies.take(reply);
       
   308 
       
   309     redirectCount++;
       
   310     if (redirectCount < IMAGEREQUESTHANDLER_MAX_REDIRECT_RECURSION) {
       
   311         QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
       
   312         if (redirect.isValid()) {
       
   313             QUrl url = reply->url().resolved(redirect.toUrl());
       
   314             QNetworkRequest req(url);
       
   315             req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
       
   316 
       
   317             reply->deleteLater();
       
   318             reply = networkAccessManager()->get(req);
       
   319 
       
   320             QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
       
   321             QMetaObject::connect(reply, replyFinished, this, thisNetworkRequestDone);
       
   322 
       
   323             replies.insert(reply, job);
       
   324             return;
       
   325         }
       
   326     }
       
   327     redirectCount=0;
       
   328 
       
   329     if (job) {
       
   330         QImage image;
       
   331         QDeclarativeImageReaderEvent::ReadError error;
       
   332         QString errorString;
       
   333         if (reply->error()) {
       
   334             error = QDeclarativeImageReaderEvent::Loading;
       
   335             errorString = reply->errorString();
       
   336         } else {
       
   337             QSize read_impsize;
       
   338             QByteArray all = reply->readAll();
       
   339             QBuffer buff(&all);
       
   340             buff.open(QIODevice::ReadOnly);
       
   341             if (readImage(reply->url(), &buff, &image, &errorString, &read_impsize, job->forcedWidth(), job->forcedHeight())) {
       
   342                 qmlOriginalSizes()->insert(reply->url(), read_impsize);
       
   343                 error = QDeclarativeImageReaderEvent::NoError;
       
   344             } else {
       
   345                 error = QDeclarativeImageReaderEvent::Decoding;
       
   346             }
       
   347         }
       
   348         // send completion event to the QDeclarativePixmapReply
       
   349         QCoreApplication::postEvent(job, new QDeclarativeImageReaderEvent(error, errorString, image));
       
   350     }
       
   351     // kick off event loop again if we have dropped below max request count
       
   352     if (replies.count() == maxImageRequestCount)
       
   353         QCoreApplication::postEvent(this, new QEvent(QEvent::User));
       
   354     reply->deleteLater();
       
   355 }
       
   356 
       
   357 //===========================================================================
       
   358 
       
   359 QDeclarativeImageReader::QDeclarativeImageReader(QDeclarativeEngine *eng)
       
   360     : QThread(eng), engine(eng), handler(0)
       
   361 {
       
   362     eventLoopQuitHack = new QObject;
       
   363     eventLoopQuitHack->moveToThread(this);
       
   364     connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
       
   365     start(QThread::IdlePriority);
       
   366 }
       
   367 
       
   368 QDeclarativeImageReader::~QDeclarativeImageReader()
       
   369 {
       
   370     readerMutex.lock();
       
   371     readers.remove(engine);
       
   372     readerMutex.unlock();
       
   373 
       
   374     eventLoopQuitHack->deleteLater();
       
   375     wait();
       
   376 }
       
   377 
       
   378 QDeclarativeImageReader *QDeclarativeImageReader::instance(QDeclarativeEngine *engine)
       
   379 {
       
   380     readerMutex.lock();
       
   381     QDeclarativeImageReader *reader = readers.value(engine);
       
   382     if (!reader) {
       
   383         reader = new QDeclarativeImageReader(engine);
       
   384         readers.insert(engine, reader);
       
   385     }
       
   386     readerMutex.unlock();
       
   387 
       
   388     return reader;
       
   389 }
       
   390 
       
   391 QDeclarativePixmapReply *QDeclarativeImageReader::getImage(const QUrl &url, int req_width, int req_height)
       
   392 {
       
   393     mutex.lock();
       
   394     QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(this, url, req_width, req_height);
       
   395     reply->addRef();
       
   396     reply->setLoading();
       
   397     jobs.append(reply);
       
   398     if (jobs.count() == 1 && handler)
       
   399         QCoreApplication::postEvent(handler, new QEvent(QEvent::User));
       
   400     mutex.unlock();
       
   401     return reply;
       
   402 }
       
   403 
       
   404 void QDeclarativeImageReader::cancel(QDeclarativePixmapReply *reply)
       
   405 {
       
   406     mutex.lock();
       
   407     if (reply->isLoading()) {
       
   408         // Add to cancel list to be cancelled in reader thread.
       
   409         cancelled.append(reply);
       
   410         if (cancelled.count() == 1 && handler)
       
   411             QCoreApplication::postEvent(handler, new QEvent(QEvent::User));
       
   412     }
       
   413     mutex.unlock();
       
   414 }
       
   415 
       
   416 void QDeclarativeImageReader::run()
       
   417 {
       
   418     readerMutex.lock();
       
   419     handler = new QDeclarativeImageRequestHandler(this, engine);
       
   420     readerMutex.unlock();
       
   421 
       
   422     exec();
       
   423 
       
   424     delete handler;
       
   425     handler = 0;
       
   426 }
       
   427 
       
   428 //===========================================================================
       
   429 
       
   430 /*!
       
   431     \internal
       
   432     \class QDeclarativePixmapCache
       
   433     \brief Enacapsultes a pixmap for QDeclarativeGraphics items.
       
   434 
       
   435     This class is NOT reentrant.
       
   436  */
       
   437 
       
   438 typedef QHash<QUrl, QDeclarativePixmapReply *> QDeclarativePixmapReplyHash;
       
   439 Q_GLOBAL_STATIC(QDeclarativePixmapReplyHash, qmlActivePixmapReplies);
       
   440 
       
   441 class QDeclarativePixmapReplyPrivate : public QObjectPrivate
       
   442 {
       
   443     Q_DECLARE_PUBLIC(QDeclarativePixmapReply)
       
   444 
       
   445 public:
       
   446     QDeclarativePixmapReplyPrivate(QDeclarativeImageReader *r, const QUrl &u, int req_width, int req_height)
       
   447         : QObjectPrivate(), refCount(1), url(u), status(QDeclarativePixmapReply::Loading), loading(false), reader(r),
       
   448             forced_width(req_width), forced_height(req_height)
       
   449     {
       
   450     }
       
   451 
       
   452     int refCount;
       
   453     QUrl url;
       
   454     QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard
       
   455     QDeclarativePixmapReply::Status status;
       
   456     bool loading;
       
   457     QDeclarativeImageReader *reader;
       
   458     int forced_width, forced_height;
       
   459     QString errorString;
       
   460 };
       
   461 
       
   462 
       
   463 QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativeImageReader *reader, const QUrl &url, int req_width, int req_height)
       
   464   : QObject(*new QDeclarativePixmapReplyPrivate(reader, url, req_width, req_height), 0)
       
   465 {
       
   466 }
       
   467 
       
   468 QDeclarativePixmapReply::~QDeclarativePixmapReply()
       
   469 {
       
   470 }
       
   471 
       
   472 const QUrl &QDeclarativePixmapReply::url() const
       
   473 {
       
   474     Q_D(const QDeclarativePixmapReply);
       
   475     return d->url;
       
   476 }
       
   477 
       
   478 int QDeclarativePixmapReply::forcedWidth() const
       
   479 {
       
   480     Q_D(const QDeclarativePixmapReply);
       
   481     return d->forced_width;
       
   482 }
       
   483 
       
   484 int QDeclarativePixmapReply::forcedHeight() const
       
   485 {
       
   486     Q_D(const QDeclarativePixmapReply);
       
   487     return d->forced_height;
       
   488 }
       
   489 
       
   490 QSize QDeclarativePixmapReply::implicitSize() const
       
   491 {
       
   492     Q_D(const QDeclarativePixmapReply);
       
   493     QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(d->url);
       
   494     if (iter != qmlOriginalSizes()->end())
       
   495         return *iter;
       
   496     else
       
   497         return QSize();
       
   498 }
       
   499 
       
   500 bool QDeclarativePixmapReply::event(QEvent *event)
       
   501 {
       
   502     Q_D(QDeclarativePixmapReply);
       
   503     if (event->type() == QEvent::User) {
       
   504         d->loading = false;
       
   505         if (!release(true)) {
       
   506             QDeclarativeImageReaderEvent *de = static_cast<QDeclarativeImageReaderEvent*>(event);
       
   507             d->status = (de->error == QDeclarativeImageReaderEvent::NoError) ? Ready : Error;
       
   508             if (d->status == Ready)
       
   509                 d->pixmap = QPixmap::fromImage(de->image);
       
   510             else
       
   511                 d->errorString =  de->errorString;
       
   512             QByteArray key = d->url.toEncoded(QUrl::FormattingOption(0x100));
       
   513             QString strKey = QString::fromLatin1(key.constData(), key.count());
       
   514             QPixmapCache::insert(strKey, d->pixmap); // note: may fail (returns false)
       
   515             emit finished();
       
   516         }
       
   517         return true;
       
   518     }
       
   519 
       
   520     return QObject::event(event);
       
   521 }
       
   522 
       
   523 QString QDeclarativePixmapReply::errorString() const
       
   524 {
       
   525     Q_D(const QDeclarativePixmapReply);
       
   526     return d->errorString;
       
   527 }
       
   528 
       
   529 QDeclarativePixmapReply::Status QDeclarativePixmapReply::status() const
       
   530 {
       
   531     Q_D(const QDeclarativePixmapReply);
       
   532     return d->status;
       
   533 }
       
   534 
       
   535 bool QDeclarativePixmapReply::isLoading() const
       
   536 {
       
   537     Q_D(const QDeclarativePixmapReply);
       
   538     return d->loading;
       
   539 }
       
   540 
       
   541 void QDeclarativePixmapReply::setLoading()
       
   542 {
       
   543     Q_D(QDeclarativePixmapReply);
       
   544     d->loading = true;
       
   545 }
       
   546 
       
   547 void QDeclarativePixmapReply::addRef()
       
   548 {
       
   549     Q_D(QDeclarativePixmapReply);
       
   550     ++d->refCount;
       
   551 }
       
   552 
       
   553 bool QDeclarativePixmapReply::release(bool defer)
       
   554 {
       
   555     Q_D(QDeclarativePixmapReply);
       
   556     Q_ASSERT(d->refCount > 0);
       
   557     --d->refCount;
       
   558     if (d->refCount == 0) {
       
   559         qmlActivePixmapReplies()->remove(d->url);
       
   560         if (defer)
       
   561             deleteLater();
       
   562         else
       
   563             delete this;
       
   564         return true;
       
   565     } else if (d->refCount == 1 && d->loading) {
       
   566         // The only reference left is the reader thread.
       
   567         qmlActivePixmapReplies()->remove(d->url);
       
   568         d->reader->cancel(this);
       
   569     }
       
   570 
       
   571     return false;
       
   572 }
       
   573 
       
   574 /*!
       
   575     Finds the cached pixmap corresponding to \a url.
       
   576     If the image is a network resource and has not yet
       
   577     been retrieved and cached, request() must be called.
       
   578 
       
   579     Returns Ready, or Error if the image has been retrieved,
       
   580     otherwise the current retrieval status.
       
   581 
       
   582     If \a async is false the image will be loaded and decoded immediately;
       
   583     otherwise the image will be loaded and decoded in a separate thread.
       
   584 
       
   585     If \a req_width and \a req_height are non-zero, they are used for
       
   586     the size of the rendered pixmap rather than the intrinsic size of the image.
       
   587     Different request sizes add different cache items.
       
   588 
       
   589     Note that images sourced from the network will always be loaded and
       
   590     decoded asynchonously.
       
   591 */
       
   592 QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QPixmap *pixmap, QString *errorString, QSize *impsize, bool async, int req_width, int req_height)
       
   593 {
       
   594     QDeclarativePixmapReply::Status status = QDeclarativePixmapReply::Unrequested;
       
   595     QByteArray key = url.toEncoded(QUrl::FormattingOption(0x100));
       
   596 
       
   597     if (req_width > 0 || req_height > 0) {
       
   598         key += ':';
       
   599         key += QByteArray::number(req_width);
       
   600         key += 'x';
       
   601         key += QByteArray::number(req_height);
       
   602     }
       
   603 
       
   604     QString strKey = QString::fromLatin1(key.constData(), key.count());
       
   605 
       
   606 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
       
   607     if (!async) {
       
   608         QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
       
   609         if (!lf.isEmpty()) {
       
   610             status = QDeclarativePixmapReply::Ready;
       
   611             if (!QPixmapCache::find(strKey,pixmap)) {
       
   612                 QFile f(lf);
       
   613                 QSize read_impsize;
       
   614                 if (f.open(QIODevice::ReadOnly)) {
       
   615                     QImage image;
       
   616                     if (readImage(url, &f, &image, errorString, &read_impsize, req_width, req_height)) {
       
   617                         *pixmap = QPixmap::fromImage(image);
       
   618                     } else {
       
   619                         *pixmap = QPixmap();
       
   620                         status = QDeclarativePixmapReply::Error;
       
   621                     }
       
   622                 } else {
       
   623                     if (errorString)
       
   624                         *errorString = tr("Cannot open: %1").arg(url.toString());
       
   625                     *pixmap = QPixmap();
       
   626                     status = QDeclarativePixmapReply::Error;
       
   627                 }
       
   628                 if (status == QDeclarativePixmapReply::Ready) {
       
   629                     QPixmapCache::insert(strKey, *pixmap);
       
   630                     qmlOriginalSizes()->insert(url, read_impsize);
       
   631                 }
       
   632                 if (impsize)
       
   633                     *impsize = read_impsize;
       
   634             } else {
       
   635                 if (impsize) {
       
   636                     QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(url);
       
   637                     if (iter != qmlOriginalSizes()->end())
       
   638                         *impsize = *iter;
       
   639                 }
       
   640             }
       
   641             return status;
       
   642         }
       
   643     }
       
   644 #endif
       
   645 
       
   646     QDeclarativePixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url);
       
   647     if (iter != qmlActivePixmapReplies()->end() && (*iter)->status() == QDeclarativePixmapReply::Ready) {
       
   648         // Must check this, since QPixmapCache::insert may have failed.
       
   649         *pixmap = (*iter)->d_func()->pixmap;
       
   650         status = (*iter)->status();
       
   651         (*iter)->release();
       
   652     } else if (QPixmapCache::find(strKey, pixmap)) {
       
   653         if (iter != qmlActivePixmapReplies()->end()) {
       
   654             status = (*iter)->status();
       
   655             if (errorString)
       
   656                 *errorString = (*iter)->errorString();
       
   657             (*iter)->release();
       
   658         } else if (pixmap->isNull()) {
       
   659             status = QDeclarativePixmapReply::Error;
       
   660             if (errorString)
       
   661                 *errorString = tr("Unknown Error loading %1").arg(url.toString());
       
   662         } else {
       
   663             status = QDeclarativePixmapReply::Ready;
       
   664         }
       
   665     } else if (iter != qmlActivePixmapReplies()->end()) {
       
   666         status = QDeclarativePixmapReply::Loading;
       
   667     }
       
   668     if (impsize) {
       
   669         QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(url);
       
   670         if (iter != qmlOriginalSizes()->end())
       
   671             *impsize = *iter;
       
   672     }
       
   673 
       
   674     return status;
       
   675 }
       
   676 
       
   677 /*!
       
   678     Starts a network request to load \a url.
       
   679 
       
   680     Returns a QDeclarativePixmapReply.  Caller should connect to QDeclarativePixmapReply::finished()
       
   681     and call get() when the image is available.
       
   682 
       
   683     The returned QDeclarativePixmapReply will be deleted when all request() calls are
       
   684     matched by a corresponding get() call.
       
   685 */
       
   686 QDeclarativePixmapReply *QDeclarativePixmapCache::request(QDeclarativeEngine *engine, const QUrl &url, int req_width, int req_height)
       
   687 {
       
   688     QDeclarativePixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url);
       
   689     if (iter == qmlActivePixmapReplies()->end()) {
       
   690         QDeclarativeImageReader *reader = QDeclarativeImageReader::instance(engine);
       
   691         QDeclarativePixmapReply *item = reader->getImage(url, req_width, req_height);
       
   692         iter = qmlActivePixmapReplies()->insert(url, item);
       
   693     } else {
       
   694         (*iter)->addRef();
       
   695     }
       
   696 
       
   697     return (*iter);
       
   698 }
       
   699 
       
   700 /*!
       
   701     Cancels a previous call to request().
       
   702 
       
   703     May also cancel loading (eg. if no other pending request).
       
   704 
       
   705     Any connections from the QDeclarativePixmapReply returned by request() to \a obj will be
       
   706     disconnected.
       
   707 */
       
   708 void QDeclarativePixmapCache::cancel(const QUrl& url, QObject *obj)
       
   709 {
       
   710     QDeclarativePixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url);
       
   711     if (iter == qmlActivePixmapReplies()->end())
       
   712         return;
       
   713 
       
   714     QDeclarativePixmapReply *reply = *iter;
       
   715     if (obj)
       
   716         QObject::disconnect(reply, 0, obj, 0);
       
   717     reply->release();
       
   718 }
       
   719 
       
   720 /*!
       
   721     This function is mainly for test verification. It returns the number of
       
   722     requests that are still unfinished.
       
   723 */
       
   724 int QDeclarativePixmapCache::pendingRequests()
       
   725 {
       
   726     return qmlActivePixmapReplies()->count();
       
   727 }
       
   728 
       
   729 QT_END_NAMESPACE
       
   730 
       
   731 #include <qdeclarativepixmapcache.moc>