blob: 2b3bb22a8f0d84f9d6761297960f2da47e7a29ba [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "private/qdeclarativepixmapcache_p.h"
#include "qdeclarativenetworkaccessmanagerfactory.h"
#include "qdeclarativeimageprovider.h"
#include <qdeclarativeengine.h>
#include <private/qdeclarativeglobal_p.h>
#include <private/qdeclarativeengine_p.h>
#include <QCoreApplication>
#include <QImageReader>
#include <QHash>
#include <QNetworkReply>
#include <QPixmapCache>
#include <QFile>
#include <QThread>
#include <QMutex>
#include <QMutexLocker>
#include <QWaitCondition>
#include <QBuffer>
#include <QWaitCondition>
#include <QtCore/qdebug.h>
#include <private/qobject_p.h>
#include <QSslError>
#define IMAGEREQUEST_MAX_REQUEST_COUNT 8
#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16
#define CACHE_EXPIRE_TIME 30
#define CACHE_REMOVAL_FRACTION 4
QT_BEGIN_NAMESPACE
// The cache limit describes the maximum "junk" in the cache.
// These are the same defaults as QPixmapCache
#if defined(Q_WS_QWS) || defined(Q_WS_WINCE)
static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded
#else
static int cache_limit = 10240 * 1024; // 10 MB cache limit for desktop
#endif
class QDeclarativePixmapReader;
class QDeclarativePixmapData;
class QDeclarativePixmapReply : public QObject
{
Q_OBJECT
public:
enum ReadError { NoError, Loading, Decoding };
QDeclarativePixmapReply(QDeclarativePixmapData *);
~QDeclarativePixmapReply();
QDeclarativePixmapData *data;
QDeclarativePixmapReader *reader;
QSize requestSize;
bool loading;
int redirectCount;
class Event : public QEvent {
public:
Event(ReadError, const QString &, const QSize &, const QImage &);
ReadError error;
QString errorString;
QSize implicitSize;
QImage image;
};
void postReply(ReadError, const QString &, const QSize &, const QImage &);
Q_SIGNALS:
void finished();
void downloadProgress(qint64, qint64);
protected:
bool event(QEvent *event);
private:
Q_DISABLE_COPY(QDeclarativePixmapReply)
public:
static int finishedIndex;
static int downloadProgressIndex;
};
class QDeclarativePixmapReaderThreadObject : public QObject {
Q_OBJECT
public:
QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *);
void processJobs();
virtual bool event(QEvent *e);
private slots:
void networkRequestDone();
private:
QDeclarativePixmapReader *reader;
};
class QDeclarativePixmapData;
class QDeclarativePixmapReader : public QThread
{
Q_OBJECT
public:
QDeclarativePixmapReader(QDeclarativeEngine *eng);
~QDeclarativePixmapReader();
QDeclarativePixmapReply *getImage(QDeclarativePixmapData *);
void cancel(QDeclarativePixmapReply *rep);
static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine);
protected:
void run();
private:
friend class QDeclarativePixmapReaderThreadObject;
void processJobs();
void processJob(QDeclarativePixmapReply *, const QUrl &, const QSize &);
void networkRequestDone(QNetworkReply *);
QList<QDeclarativePixmapReply*> jobs;
QList<QDeclarativePixmapReply*> cancelled;
QDeclarativeEngine *engine;
QObject *eventLoopQuitHack;
QMutex mutex;
QDeclarativePixmapReaderThreadObject *threadObject;
QWaitCondition waitCondition;
QNetworkAccessManager *networkAccessManager();
QNetworkAccessManager *accessManager;
QHash<QNetworkReply*,QDeclarativePixmapReply*> replies;
static int replyDownloadProgress;
static int replyFinished;
static int downloadProgress;
static int threadNetworkRequestDone;
static QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> readers;
static QMutex readerMutex;
};
class QDeclarativePixmapData
{
public:
QDeclarativePixmapData(const QUrl &u, const QSize &s, const QString &e)
: refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Error),
url(u), errorString(e), requestSize(s), reply(0), prevUnreferenced(0),
prevUnreferencedPtr(0), nextUnreferenced(0)
{
}
QDeclarativePixmapData(const QUrl &u, const QSize &r)
: refCount(1), inCache(false), pixmapStatus(QDeclarativePixmap::Loading),
url(u), requestSize(r), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0),
nextUnreferenced(0)
{
}
QDeclarativePixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r)
: refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarativePixmap::Ready),
url(u), pixmap(p), implicitSize(s), requestSize(r), reply(0), prevUnreferenced(0),
prevUnreferencedPtr(0), nextUnreferenced(0)
{
}
QDeclarativePixmapData(const QPixmap &p)
: refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarativePixmap::Ready),
pixmap(p), implicitSize(p.size()), requestSize(p.size()), reply(0), prevUnreferenced(0),
prevUnreferencedPtr(0), nextUnreferenced(0)
{
}
int cost() const;
void addref();
void release();
void addToCache();
void removeFromCache();
uint refCount;
bool inCache:1;
bool privatePixmap:1;
QDeclarativePixmap::Status pixmapStatus;
QUrl url;
QString errorString;
QPixmap pixmap;
QSize implicitSize;
QSize requestSize;
QDeclarativePixmapReply *reply;
QDeclarativePixmapData *prevUnreferenced;
QDeclarativePixmapData**prevUnreferencedPtr;
QDeclarativePixmapData *nextUnreferenced;
};
int QDeclarativePixmapReply::finishedIndex = -1;
int QDeclarativePixmapReply::downloadProgressIndex = -1;
// XXX
QHash<QDeclarativeEngine *,QDeclarativePixmapReader*> QDeclarativePixmapReader::readers;
QMutex QDeclarativePixmapReader::readerMutex;
int QDeclarativePixmapReader::replyDownloadProgress = -1;
int QDeclarativePixmapReader::replyFinished = -1;
int QDeclarativePixmapReader::downloadProgress = -1;
int QDeclarativePixmapReader::threadNetworkRequestDone = -1;
void QDeclarativePixmapReply::postReply(ReadError error, const QString &errorString,
const QSize &implicitSize, const QImage &image)
{
loading = false;
QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, image));
}
QDeclarativePixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, const QImage &i)
: QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i)
{
}
QNetworkAccessManager *QDeclarativePixmapReader::networkAccessManager()
{
if (!accessManager) {
Q_ASSERT(threadObject);
accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject);
}
return accessManager;
}
static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
const QSize &requestSize)
{
QImageReader imgio(dev);
bool force_scale = false;
if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) {
imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053
force_scale = true;
}
bool scaled = false;
if (requestSize.width() > 0 || requestSize.height() > 0) {
QSize s = imgio.size();
if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
if (requestSize.height() <= 0)
s.setHeight(s.height()*requestSize.width()/s.width());
s.setWidth(requestSize.width()); scaled = true;
}
if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
if (requestSize.width() <= 0)
s.setWidth(s.width()*requestSize.height()/s.height());
s.setHeight(requestSize.height()); scaled = true;
}
if (scaled) { imgio.setScaledSize(s); }
}
if (impsize)
*impsize = imgio.size();
if (imgio.read(image)) {
if (impsize && impsize->width() < 0)
*impsize = image->size();
return true;
} else {
if (errorString)
*errorString = QDeclarativePixmap::tr("Error decoding: %1: %2").arg(url.toString())
.arg(imgio.errorString());
return false;
}
}
QDeclarativePixmapReader::QDeclarativePixmapReader(QDeclarativeEngine *eng)
: QThread(eng), engine(eng), threadObject(0), accessManager(0)
{
eventLoopQuitHack = new QObject;
eventLoopQuitHack->moveToThread(this);
connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
start(QThread::IdlePriority);
}
QDeclarativePixmapReader::~QDeclarativePixmapReader()
{
readerMutex.lock();
readers.remove(engine);
readerMutex.unlock();
eventLoopQuitHack->deleteLater();
wait();
}
void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply)
{
QDeclarativePixmapReply *job = replies.take(reply);
if (job) {
job->redirectCount++;
if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) {
QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirect.isValid()) {
QUrl url = reply->url().resolved(redirect.toUrl());
QNetworkRequest req(url);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
reply->deleteLater();
reply = networkAccessManager()->get(req);
QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress);
QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
replies.insert(reply, job);
return;
}
}
QImage image;
QDeclarativePixmapReply::ReadError error = QDeclarativePixmapReply::NoError;
QString errorString;
QSize readSize;
if (reply->error()) {
error = QDeclarativePixmapReply::Loading;
errorString = reply->errorString();
} else {
QByteArray all = reply->readAll();
QBuffer buff(&all);
buff.open(QIODevice::ReadOnly);
if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize)) {
error = QDeclarativePixmapReply::Decoding;
}
}
// send completion event to the QDeclarativePixmapReply
mutex.lock();
if (!cancelled.contains(job)) job->postReply(error, errorString, readSize, image);
mutex.unlock();
}
reply->deleteLater();
// kick off event loop again incase we have dropped below max request count
threadObject->processJobs();
}
QDeclarativePixmapReaderThreadObject::QDeclarativePixmapReaderThreadObject(QDeclarativePixmapReader *i)
: reader(i)
{
}
void QDeclarativePixmapReaderThreadObject::processJobs()
{
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
}
bool QDeclarativePixmapReaderThreadObject::event(QEvent *e)
{
if (e->type() == QEvent::User) {
reader->processJobs();
return true;
} else {
return QObject::event(e);
}
}
void QDeclarativePixmapReaderThreadObject::networkRequestDone()
{
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
reader->networkRequestDone(reply);
}
void QDeclarativePixmapReader::processJobs()
{
QMutexLocker locker(&mutex);
while (true) {
if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT))
return; // Nothing else to do
// Clean cancelled jobs
if (cancelled.count()) {
for (int i = 0; i < cancelled.count(); ++i) {
QDeclarativePixmapReply *job = cancelled.at(i);
QNetworkReply *reply = replies.key(job, 0);
if (reply && reply->isRunning()) {
// cancel any jobs already started
replies.remove(reply);
reply->close();
}
// deleteLater, since not owned by this thread
job->deleteLater();
}
cancelled.clear();
}
if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) {
QDeclarativePixmapReply *runningJob = jobs.takeLast();
runningJob->loading = true;
QUrl url = runningJob->data->url;
QSize requestSize = runningJob->data->requestSize;
locker.unlock();
processJob(runningJob, url, requestSize);
locker.relock();
}
}
}
void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, const QUrl &url,
const QSize &requestSize)
{
// fetch
if (url.scheme() == QLatin1String("image")) {
// Use QmlImageProvider
QSize readSize;
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
QString errorStr;
if (image.isNull()) {
errorCode = QDeclarativePixmapReply::Loading;
errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString());
}
mutex.lock();
if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image);
mutex.unlock();
} else {
QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
if (!lf.isEmpty()) {
// Image is local - load/decode immediately
QImage image;
QDeclarativePixmapReply::ReadError errorCode = QDeclarativePixmapReply::NoError;
QString errorStr;
QFile f(lf);
QSize readSize;
if (f.open(QIODevice::ReadOnly)) {
if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize))
errorCode = QDeclarativePixmapReply::Loading;
} else {
errorStr = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
errorCode = QDeclarativePixmapReply::Loading;
}
mutex.lock();
if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image);
mutex.unlock();
} else {
// Network resource
QNetworkRequest req(url);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
QNetworkReply *reply = networkAccessManager()->get(req);
QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress);
QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
replies.insert(reply, runningJob);
}
}
}
QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine)
{
readerMutex.lock();
QDeclarativePixmapReader *reader = readers.value(engine);
if (!reader) {
reader = new QDeclarativePixmapReader(engine);
readers.insert(engine, reader);
}
readerMutex.unlock();
return reader;
}
QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data)
{
mutex.lock();
QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data);
reply->reader = this;
jobs.append(reply);
// XXX
if (threadObject) threadObject->processJobs();
mutex.unlock();
return reply;
}
void QDeclarativePixmapReader::cancel(QDeclarativePixmapReply *reply)
{
mutex.lock();
if (reply->loading) {
cancelled.append(reply);
reply->data = 0;
// XXX
if (threadObject) threadObject->processJobs();
} else {
jobs.removeAll(reply);
delete reply;
}
mutex.unlock();
}
void QDeclarativePixmapReader::run()
{
if (replyDownloadProgress == -1) {
const QMetaObject *nr = &QNetworkReply::staticMetaObject;
const QMetaObject *pr = &QDeclarativePixmapReply::staticMetaObject;
const QMetaObject *ir = &QDeclarativePixmapReaderThreadObject::staticMetaObject;
replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)");
replyFinished = nr->indexOfSignal("finished()");
downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)");
threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
}
mutex.lock();
threadObject = new QDeclarativePixmapReaderThreadObject(this);
mutex.unlock();
processJobs();
exec();
delete threadObject;
threadObject = 0;
}
class QDeclarativePixmapKey
{
public:
const QUrl *url;
const QSize *size;
};
inline bool operator==(const QDeclarativePixmapKey &lhs, const QDeclarativePixmapKey &rhs)
{
return *lhs.size == *rhs.size && *lhs.url == *rhs.url;
}
inline uint qHash(const QDeclarativePixmapKey &key)
{
return qHash(*key.url) ^ key.size->width() ^ key.size->height();
}
class QDeclarativePixmapStore : public QObject
{
Q_OBJECT
public:
QDeclarativePixmapStore();
void unreferencePixmap(QDeclarativePixmapData *);
void referencePixmap(QDeclarativePixmapData *);
void flushCache();
protected:
virtual void timerEvent(QTimerEvent *);
public:
QHash<QDeclarativePixmapKey, QDeclarativePixmapData *> m_cache;
private:
void shrinkCache(int remove);
QDeclarativePixmapData *m_unreferencedPixmaps;
QDeclarativePixmapData *m_lastUnreferencedPixmap;
int m_unreferencedCost;
int m_timerId;
};
Q_GLOBAL_STATIC(QDeclarativePixmapStore, pixmapStore);
QDeclarativePixmapStore::QDeclarativePixmapStore()
: m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1)
{
}
void QDeclarativePixmapStore::unreferencePixmap(QDeclarativePixmapData *data)
{
Q_ASSERT(data->prevUnreferenced == 0);
Q_ASSERT(data->prevUnreferencedPtr == 0);
Q_ASSERT(data->nextUnreferenced == 0);
data->nextUnreferenced = m_unreferencedPixmaps;
data->prevUnreferencedPtr = &m_unreferencedPixmaps;
m_unreferencedPixmaps = data;
if (m_unreferencedPixmaps->nextUnreferenced) {
m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
}
if (!m_lastUnreferencedPixmap)
m_lastUnreferencedPixmap = data;
m_unreferencedCost += data->cost();
shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit
if (m_timerId == -1 && m_unreferencedPixmaps)
m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
}
void QDeclarativePixmapStore::referencePixmap(QDeclarativePixmapData *data)
{
Q_ASSERT(data->prevUnreferencedPtr);
*data->prevUnreferencedPtr = data->nextUnreferenced;
if (data->nextUnreferenced) {
data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
}
if (m_lastUnreferencedPixmap == data)
m_lastUnreferencedPixmap = data->prevUnreferenced;
data->nextUnreferenced = 0;
data->prevUnreferencedPtr = 0;
data->prevUnreferenced = 0;
m_unreferencedCost -= data->cost();
}
void QDeclarativePixmapStore::shrinkCache(int remove)
{
while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
QDeclarativePixmapData *data = m_lastUnreferencedPixmap;
Q_ASSERT(data->nextUnreferenced == 0);
*data->prevUnreferencedPtr = 0;
m_lastUnreferencedPixmap = data->prevUnreferenced;
data->prevUnreferencedPtr = 0;
data->prevUnreferenced = 0;
remove -= data->cost();
m_unreferencedCost -= data->cost();
data->removeFromCache();
delete data;
}
}
void QDeclarativePixmapStore::timerEvent(QTimerEvent *)
{
int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
shrinkCache(removalCost);
if (m_unreferencedPixmaps == 0) {
killTimer(m_timerId);
m_timerId = -1;
}
}
/*
Remove all unreferenced pixmaps from the cache.
*/
void QDeclarativePixmapStore::flushCache()
{
shrinkCache(m_unreferencedCost);
}
QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d)
: data(d), reader(0), requestSize(d->requestSize), loading(false), redirectCount(0)
{
if (finishedIndex == -1) {
finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()");
downloadProgressIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
}
}
QDeclarativePixmapReply::~QDeclarativePixmapReply()
{
}
bool QDeclarativePixmapReply::event(QEvent *event)
{
if (event->type() == QEvent::User) {
if (data) {
Event *de = static_cast<Event *>(event);
data->pixmapStatus = (de->error == NoError) ? QDeclarativePixmap::Ready : QDeclarativePixmap::Error;
if (data->pixmapStatus == QDeclarativePixmap::Ready) {
data->pixmap = QPixmap::fromImage(de->image);
data->implicitSize = de->implicitSize;
} else {
data->errorString = de->errorString;
data->removeFromCache(); // We don't continue to cache error'd pixmaps
}
data->reply = 0;
emit finished();
}
delete this;
return true;
} else {
return QObject::event(event);
}
}
int QDeclarativePixmapData::cost() const
{
return (pixmap.width() * pixmap.height() * pixmap.depth()) / 8;
}
void QDeclarativePixmapData::addref()
{
++refCount;
if (prevUnreferencedPtr)
pixmapStore()->referencePixmap(this);
}
void QDeclarativePixmapData::release()
{
Q_ASSERT(refCount > 0);
--refCount;
if (refCount == 0) {
if (reply) {
reply->reader->cancel(reply);
reply = 0;
}
if (pixmapStatus == QDeclarativePixmap::Ready) {
pixmapStore()->unreferencePixmap(this);
} else {
removeFromCache();
delete this;
}
}
}
void QDeclarativePixmapData::addToCache()
{
if (!inCache) {
QDeclarativePixmapKey key = { &url, &requestSize };
pixmapStore()->m_cache.insert(key, this);
inCache = true;
}
}
void QDeclarativePixmapData::removeFromCache()
{
if (inCache) {
QDeclarativePixmapKey key = { &url, &requestSize };
pixmapStore()->m_cache.remove(key);
inCache = false;
}
}
static QDeclarativePixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok)
{
if (url.scheme() == QLatin1String("image")) {
QSize readSize;
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url);
switch (imageType) {
case QDeclarativeImageProvider::Image:
{
QImage image = ep->getImageFromProvider(url, &readSize, requestSize);
if (!image.isNull()) {
*ok = true;
return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
}
}
case QDeclarativeImageProvider::Pixmap:
{
QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize);
if (!pixmap.isNull()) {
*ok = true;
return new QDeclarativePixmapData(url, pixmap, readSize, requestSize);
}
}
}
// no matching provider, or provider has bad image type, or provider returned null image
return new QDeclarativePixmapData(url, requestSize,
QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
}
QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
if (localFile.isEmpty())
return 0;
QFile f(localFile);
QSize readSize;
QString errorString;
if (f.open(QIODevice::ReadOnly)) {
QImage image;
if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) {
*ok = true;
return new QDeclarativePixmapData(url, QPixmap::fromImage(image), readSize, requestSize);
}
} else {
errorString = QDeclarativePixmap::tr("Cannot open: %1").arg(url.toString());
}
return new QDeclarativePixmapData(url, requestSize, errorString);
}
struct QDeclarativePixmapNull {
QUrl url;
QPixmap pixmap;
QSize size;
};
Q_GLOBAL_STATIC(QDeclarativePixmapNull, nullPixmap);
QDeclarativePixmap::QDeclarativePixmap()
: d(0)
{
}
QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url)
: d(0)
{
load(engine, url);
}
QDeclarativePixmap::QDeclarativePixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
: d(0)
{
load(engine, url, size);
}
QDeclarativePixmap::~QDeclarativePixmap()
{
if (d) {
d->release();
d = 0;
}
}
bool QDeclarativePixmap::isNull() const
{
return d == 0;
}
bool QDeclarativePixmap::isReady() const
{
return status() == Ready;
}
bool QDeclarativePixmap::isError() const
{
return status() == Error;
}
bool QDeclarativePixmap::isLoading() const
{
return status() == Loading;
}
QString QDeclarativePixmap::error() const
{
if (d)
return d->errorString;
else
return QString();
}
QDeclarativePixmap::Status QDeclarativePixmap::status() const
{
if (d)
return d->pixmapStatus;
else
return Null;
}
const QUrl &QDeclarativePixmap::url() const
{
if (d)
return d->url;
else
return nullPixmap()->url;
}
const QSize &QDeclarativePixmap::implicitSize() const
{
if (d)
return d->implicitSize;
else
return nullPixmap()->size;
}
const QSize &QDeclarativePixmap::requestSize() const
{
if (d)
return d->requestSize;
else
return nullPixmap()->size;
}
const QPixmap &QDeclarativePixmap::pixmap() const
{
if (d)
return d->pixmap;
else
return nullPixmap()->pixmap;
}
void QDeclarativePixmap::setPixmap(const QPixmap &p)
{
clear();
if (!p.isNull())
d = new QDeclarativePixmapData(p);
}
int QDeclarativePixmap::width() const
{
if (d)
return d->pixmap.width();
else
return 0;
}
int QDeclarativePixmap::height() const
{
if (d)
return d->pixmap.height();
else
return 0;
}
QRect QDeclarativePixmap::rect() const
{
if (d)
return d->pixmap.rect();
else
return QRect();
}
void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url)
{
load(engine, url, QSize(), QDeclarativePixmap::Cache);
}
void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarativePixmap::Options options)
{
load(engine, url, QSize(), options);
}
void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size)
{
load(engine, url, size, QDeclarativePixmap::Cache);
}
void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarativePixmap::Options options)
{
if (d) { d->release(); d = 0; }
QDeclarativePixmapKey key = { &url, &requestSize };
QDeclarativePixmapStore *store = pixmapStore();
QHash<QDeclarativePixmapKey, QDeclarativePixmapData *>::Iterator iter = store->m_cache.find(key);
if (iter == store->m_cache.end()) {
if (options & QDeclarativePixmap::Asynchronous) {
// pixmaps can only be loaded synchronously
if (url.scheme() == QLatin1String("image")
&& QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) {
options &= ~QDeclarativePixmap::Asynchronous;
}
}
if (!(options & QDeclarativePixmap::Asynchronous)) {
bool ok = false;
d = createPixmapDataSync(engine, url, requestSize, &ok);
if (ok) {
if (options & QDeclarativePixmap::Cache)
d->addToCache();
return;
}
if (d) // loadable, but encountered error while loading
return;
}
if (!engine)
return;
QDeclarativePixmapReader *reader = QDeclarativePixmapReader::instance(engine);
d = new QDeclarativePixmapData(url, requestSize);
if (options & QDeclarativePixmap::Cache)
d->addToCache();
d->reply = reader->getImage(d);
} else {
d = *iter;
d->addref();
}
}
void QDeclarativePixmap::clear()
{
if (d) {
d->release();
d = 0;
}
}
void QDeclarativePixmap::clear(QObject *obj)
{
if (d) {
if (d->reply)
QObject::disconnect(d->reply, 0, obj, 0);
d->release();
d = 0;
}
}
bool QDeclarativePixmap::connectFinished(QObject *object, const char *method)
{
if (!d || !d->reply) {
qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
return false;
}
return QObject::connect(d->reply, SIGNAL(finished()), object, method);
}
bool QDeclarativePixmap::connectFinished(QObject *object, int method)
{
if (!d || !d->reply) {
qWarning("QDeclarativePixmap: connectFinished() called when not loading.");
return false;
}
return QMetaObject::connect(d->reply, QDeclarativePixmapReply::finishedIndex, object, method);
}
bool QDeclarativePixmap::connectDownloadProgress(QObject *object, const char *method)
{
if (!d || !d->reply) {
qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
return false;
}
return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method);
}
bool QDeclarativePixmap::connectDownloadProgress(QObject *object, int method)
{
if (!d || !d->reply) {
qWarning("QDeclarativePixmap: connectDownloadProgress() called when not loading.");
return false;
}
return QMetaObject::connect(d->reply, QDeclarativePixmapReply::downloadProgressIndex, object, method);
}
void QDeclarativePixmap::flushCache()
{
pixmapStore()->flushCache();
}
QT_END_NAMESPACE
#include <qdeclarativepixmapcache.moc>