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