summaryrefslogtreecommitdiffstats
path: root/src/declarative/graphicsitems/qmlgraphicspixmapcache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/graphicsitems/qmlgraphicspixmapcache.cpp')
-rw-r--r--src/declarative/graphicsitems/qmlgraphicspixmapcache.cpp282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/declarative/graphicsitems/qmlgraphicspixmapcache.cpp b/src/declarative/graphicsitems/qmlgraphicspixmapcache.cpp
new file mode 100644
index 0000000..fdf489c
--- /dev/null
+++ b/src/declarative/graphicsitems/qmlgraphicspixmapcache.cpp
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmlgraphicspixmapcache_p.h"
+#include <QImageReader>
+#include <QHash>
+#include <QNetworkReply>
+#include <QPixmapCache>
+#include <private/qfxperf_p_p.h>
+#include <QtDeclarative/qmlengine.h>
+#include <QFile>
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+class QSharedNetworkReply;
+typedef QHash<QString, QSharedNetworkReply *> QmlGraphicsSharedNetworkReplyHash;
+static QmlGraphicsSharedNetworkReplyHash qfxActiveNetworkReplies;
+
+class QSharedNetworkReply
+{
+public:
+ QSharedNetworkReply(QNetworkReply *r) : reply(r), refCount(1) {}
+ ~QSharedNetworkReply()
+ {
+ reply->deleteLater();
+ }
+ QNetworkReply *reply;
+ QPixmap pixmap; // ensure reference to pixmap to QPixmapCache does not discard
+
+ int refCount;
+ void addRef()
+ {
+ ++refCount;
+ }
+ void release()
+ {
+ Q_ASSERT(refCount > 0);
+ --refCount;
+ if (refCount == 0) {
+ QString key = reply->url().toString();
+ qfxActiveNetworkReplies.remove(key);
+ delete this;
+ }
+ }
+};
+
+static bool readImage(QIODevice *dev, QPixmap *pixmap)
+ {
+ QImageReader imgio(dev);
+
+//#define QT_TEST_SCALED_SIZE
+#ifdef QT_TEST_SCALED_SIZE
+ /*
+ Some mechanism is needed for loading images at a limited size, especially
+ for remote images. Loading only thumbnails of remote progressive JPEG
+ images can be efficient. (Qt jpeg handler does not do so currently)
+ */
+
+ QSize limit(60,60);
+ QSize sz = imgio.size();
+ if (sz.width() > limit.width() || sz.height() > limit.height()) {
+ sz.scale(limit,Qt::KeepAspectRatio);
+ imgio.setScaledSize(sz);
+ }
+#endif
+
+ QImage img;
+ if (imgio.read(&img)) {
+#ifdef QT_TEST_SCALED_SIZE
+ if (!sz.isValid())
+ img = img.scaled(limit,Qt::KeepAspectRatio);
+#endif
+ *pixmap = QPixmap::fromImage(img);
+ return true;
+ } else {
+ qWarning() << imgio.errorString();
+ return false;
+ }
+ }
+
+/*!
+ \internal
+ \class QmlGraphicsPixmapCache
+ \brief Enacapsultes a pixmap for QmlGraphics items.
+
+ This class is NOT reentrant.
+ */
+
+static QString toLocalFileOrQrc(const QUrl& url)
+{
+ QString r = url.toLocalFile();
+ if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
+ r = QLatin1Char(':') + url.path();
+ return r;
+}
+
+/*!
+ Finds the cached pixmap corresponding to \a url.
+ A previous call to get() must have requested the URL,
+ and the QNetworkReply must have finished before calling
+ this function.
+
+ Returns true if the image was loaded without error.
+*/
+bool QmlGraphicsPixmapCache::find(const QUrl& url, QPixmap *pixmap)
+{
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QmlPerfTimer<QmlPerf::PixmapLoad> perf;
+#endif
+
+ QString key = url.toString();
+ bool ok = true;
+ if (!QPixmapCache::find(key,pixmap)) {
+#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
+ QString lf = toLocalFileOrQrc(url);
+ if (!lf.isEmpty()) {
+ QFile f(lf);
+ if (f.open(QIODevice::ReadOnly)) {
+ if (!readImage(&f, pixmap)) {
+ qWarning() << "Format error loading" << url;
+ *pixmap = QPixmap();
+ ok = false;
+ }
+ } else {
+ *pixmap = QPixmap();
+ ok = false;
+ }
+ } else
+#endif
+ {
+ QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
+ if (iter == qfxActiveNetworkReplies.end()) {
+ // API usage error
+ qWarning() << "QmlGraphicsPixmapCache: URL not loaded" << url;
+ ok = false;
+ } else {
+ if ((*iter)->reply->error()) {
+ qWarning() << "Network error loading" << url << (*iter)->reply->errorString();
+ *pixmap = QPixmap();
+ ok = false;
+ } else if (!readImage((*iter)->reply, pixmap)) {
+ qWarning() << "Format error loading" << url;
+ *pixmap = QPixmap();
+ ok = false;
+ } else {
+ if ((*iter)->refCount > 1)
+ (*iter)->pixmap = *pixmap;
+ }
+ (*iter)->release();
+ }
+ }
+ QPixmapCache::insert(key, *pixmap);
+ } else {
+ ok = !pixmap->isNull();
+#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
+ if (url.scheme()!=QLatin1String("file"))
+#endif
+ // We may be the second finder. Still need to check for active replies.
+ {
+ QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
+ if (iter != qfxActiveNetworkReplies.end())
+ (*iter)->release();
+ }
+ }
+ return ok;
+}
+
+/*!
+ Starts a network request to load \a url.
+
+ Returns a QNetworkReply if the image is not immediately available, otherwise
+ returns 0. Caller should connect to QNetworkReply::finished() to then call
+ find() when the image is available.
+
+ The returned QNetworkReply will be deleted when all get() calls are
+ matched by a corresponding find() call.
+*/
+QNetworkReply *QmlGraphicsPixmapCache::get(QmlEngine *engine, const QUrl& url, QPixmap *pixmap)
+{
+#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
+ QString lf = toLocalFileOrQrc(url);
+ if (!lf.isEmpty()) {
+ QString key = url.toString();
+ if (!QPixmapCache::find(key,pixmap)) {
+ QFile f(lf);
+ if (f.open(QIODevice::ReadOnly)) {
+ if (!readImage(&f, pixmap)) {
+ qWarning() << "Format error loading" << url;
+ *pixmap = QPixmap();
+ }
+ } else
+ *pixmap = QPixmap();
+ QPixmapCache::insert(key, *pixmap);
+ }
+ return 0;
+ }
+#endif
+
+ QString key = url.toString();
+ if (QPixmapCache::find(key,pixmap)) {
+ return 0;
+ }
+
+ QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
+ if (iter == qfxActiveNetworkReplies.end()) {
+ QNetworkRequest req(url);
+ QSharedNetworkReply *item = new QSharedNetworkReply(engine->networkAccessManager()->get(req));
+ iter = qfxActiveNetworkReplies.insert(key, item);
+ } else {
+ (*iter)->addRef();
+ }
+
+ return (*iter)->reply;
+}
+
+/*!
+ Cancels a previous call to get().
+
+ May also cancel loading (eg. if no other pending request).
+
+ Any connections from the QNetworkReply returned by get() to \a obj will be
+ disconnected.
+*/
+void QmlGraphicsPixmapCache::cancelGet(const QUrl& url, QObject* obj)
+{
+ QString key = url.toString();
+ QmlGraphicsSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
+ if (iter == qfxActiveNetworkReplies.end())
+ return;
+ if (obj)
+ QObject::disconnect((*iter)->reply, 0, obj, 0);
+ (*iter)->release();
+}
+
+/*!
+ This function is mainly for test verification. It returns the number of
+ requests that are still unfinished.
+*/
+int QmlGraphicsPixmapCache::pendingRequests()
+{
+ return qfxActiveNetworkReplies.count();
+}
+
+QT_END_NAMESPACE