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> |