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