diff options
Diffstat (limited to 'src/quick/util/qquickpixmapcache.cpp')
-rw-r--r-- | src/quick/util/qquickpixmapcache.cpp | 271 |
1 files changed, 175 insertions, 96 deletions
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 2066d1e405..810629cdb6 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -44,7 +44,6 @@ #include <qpa/qplatformintegration.h> #include <QtQuick/private/qsgtexture_p.h> -#include <QtQuick/private/qsgcontext_p.h> #include <QQuickWindow> #include <QCoreApplication> @@ -67,7 +66,7 @@ #include <private/qquickprofiler_p.h> -#define IMAGEREQUEST_MAX_REQUEST_COUNT 8 +#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8 #define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16 #define CACHE_EXPIRE_TIME 30 #define CACHE_REMOVAL_FRACTION 4 @@ -115,16 +114,6 @@ QSGTexture *QQuickDefaultTextureFactory::createTexture(QQuickWindow *window) con return t; } -static QQuickTextureFactory *textureFactoryForImage(const QImage &image) -{ - if (image.isNull()) - return 0; - QQuickTextureFactory *texture = QSGContext::createTextureFactoryFromImage(image); - if (texture) - return texture; - return new QQuickDefaultTextureFactory(image); -} - class QQuickPixmapReader; class QQuickPixmapData; class QQuickPixmapReply : public QObject @@ -179,6 +168,7 @@ public: virtual bool event(QEvent *e); private slots: void networkRequestDone(); + void asyncResponseFinished(); private: QQuickPixmapReader *reader; }; @@ -203,8 +193,9 @@ protected: private: friend class QQuickPixmapReaderThreadObject; void processJobs(); - void processJob(QQuickPixmapReply *, const QUrl &, const QSize &); + void processJob(QQuickPixmapReply *, const QUrl &, const QString &, QQuickImageProvider::ImageType, QQuickImageProvider *); void networkRequestDone(QNetworkReply *); + void asyncResponseFinished(QQuickImageResponse *); QList<QQuickPixmapReply*> jobs; QList<QQuickPixmapReply*> cancelled; @@ -218,7 +209,8 @@ private: QNetworkAccessManager *networkAccessManager(); QNetworkAccessManager *accessManager; - QHash<QNetworkReply*,QQuickPixmapReply*> replies; + QHash<QNetworkReply*,QQuickPixmapReply*> networkJobs; + QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses; static int replyDownloadProgress; static int replyFinished; @@ -423,8 +415,8 @@ QQuickPixmapReader::~QQuickPixmapReader() delete reply; } jobs.clear(); - QList<QQuickPixmapReply*> activeJobs = replies.values(); - foreach (QQuickPixmapReply *reply, activeJobs) { + QList<QQuickPixmapReply*> activeJobs = networkJobs.values() + asyncResponses.values(); + foreach (QQuickPixmapReply *reply, activeJobs ) { if (reply->loading) { cancelled.append(reply); reply->data = 0; @@ -439,7 +431,7 @@ QQuickPixmapReader::~QQuickPixmapReader() void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) { - QQuickPixmapReply *job = replies.take(reply); + QQuickPixmapReply *job = networkJobs.take(reply); if (job) { job->redirectCount++; @@ -456,7 +448,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress); QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); - replies.insert(reply, job); + networkJobs.insert(reply, job); return; } } @@ -478,7 +470,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) // send completion event to the QQuickPixmapReply mutex.lock(); if (!cancelled.contains(job)) - job->postReply(error, errorString, readSize, textureFactoryForImage(image)); + job->postReply(error, errorString, readSize, QQuickTextureFactory::textureFactoryForImage(image)); mutex.unlock(); } reply->deleteLater(); @@ -487,6 +479,32 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) threadObject->processJobs(); } +void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response) +{ + QQuickPixmapReply *job = asyncResponses.take(response); + + if (job) { + QQuickTextureFactory *t = 0; + QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError; + QString errorString; + QSize readSize; + if (!response->errorString().isEmpty()) { + error = QQuickPixmapReply::Loading; + errorString = response->errorString(); + } else { + t = response->textureFactory(); + } + mutex.lock(); + if (!cancelled.contains(job)) + job->postReply(error, errorString, t->textureSize(), t); + mutex.unlock(); + } + response->deleteLater(); + + // kick off event loop again incase we have dropped below max request count + threadObject->processJobs(); +} + QQuickPixmapReaderThreadObject::QQuickPixmapReaderThreadObject(QQuickPixmapReader *i) : reader(i) { @@ -513,23 +531,37 @@ void QQuickPixmapReaderThreadObject::networkRequestDone() reader->networkRequestDone(reply); } +void QQuickPixmapReaderThreadObject::asyncResponseFinished() +{ + QQuickImageResponse *response = static_cast<QQuickImageResponse *>(sender()); + reader->asyncResponseFinished(response); +} + void QQuickPixmapReader::processJobs() { QMutexLocker locker(&mutex); while (true) { - if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT)) + if (cancelled.isEmpty() && jobs.isEmpty()) return; // Nothing else to do // Clean cancelled jobs - if (cancelled.count()) { + if (!cancelled.isEmpty()) { for (int i = 0; i < cancelled.count(); ++i) { QQuickPixmapReply *job = cancelled.at(i); - QNetworkReply *reply = replies.key(job, 0); - if (reply && reply->isRunning()) { - // cancel any jobs already started - replies.remove(reply); - reply->close(); + QNetworkReply *reply = networkJobs.key(job, 0); + if (reply) { + networkJobs.remove(reply); + if (reply->isRunning()) { + // cancel any jobs already started + reply->close(); + } + } else { + QQuickImageResponse *asyncResponse = asyncResponses.key(job); + if (asyncResponse) { + asyncResponses.remove(asyncResponse); + asyncResponse->cancel(); + } } PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url)); // deleteLater, since not owned by this thread @@ -538,94 +570,138 @@ void QQuickPixmapReader::processJobs() cancelled.clear(); } - if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { - QQuickPixmapReply *runningJob = jobs.takeLast(); - runningJob->loading = true; + if (!jobs.isEmpty()) { + // Find a job we can use + bool usableJob = false; + for (int i = jobs.count() - 1; !usableJob && i >= 0; i--) { + QQuickPixmapReply *job = jobs[i]; + const QUrl url = job->url; + QString localFile; + QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid; + QQuickImageProvider *provider = 0; + + if (url.scheme() == QLatin1String("image")) { + provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url))); + if (provider) + imageType = provider->imageType(); + + usableJob = true; + } else { + localFile = QQmlFile::urlToLocalFileOrQrc(url); + usableJob = !localFile.isEmpty() || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT; + } - QUrl url = runningJob->url; - PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url)); - QSize requestSize = runningJob->requestSize; - locker.unlock(); - processJob(runningJob, url, requestSize); - locker.relock(); + if (usableJob) { + jobs.removeAt(i); + + job->loading = true; + + PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url)); + + locker.unlock(); + processJob(job, url, localFile, imageType, provider); + locker.relock(); + } + } + + if (!usableJob) + return; } } } -void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, - const QSize &requestSize) +void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, const QString &localFile, + QQuickImageProvider::ImageType imageType, QQuickImageProvider *provider) { // fetch if (url.scheme() == QLatin1String("image")) { // Use QQuickImageProvider QSize readSize; - QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid; - QQuickImageProvider *provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url))); - if (provider) - imageType = provider->imageType(); + switch (imageType) { + case QQuickImageProvider::Invalid: + { + QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()); + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize, 0); + mutex.unlock(); + break; + } - if (imageType == QQuickImageProvider::Invalid) { - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::Loading; - QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()); - QImage image; - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image)); - mutex.unlock(); - } else if (imageType == QQuickImageProvider::Image) { - QImage image = provider->requestImage(imageId(url), &readSize, requestSize); - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; - QString errorStr; - if (image.isNull()) { - errorCode = QQuickPixmapReply::Loading; - errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + case QQuickImageProvider::Image: + { + QImage image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize); + QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QQuickPixmapReply::Loading; + errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image)); + mutex.unlock(); + break; } - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image)); - mutex.unlock(); - } else if (imageType == QQuickImageProvider::Pixmap) { - const QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, requestSize); - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; - QString errorStr; - if (pixmap.isNull()) { - errorCode = QQuickPixmapReply::Loading; - errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + + case QQuickImageProvider::Pixmap: + { + const QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize); + QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; + QString errorStr; + if (pixmap.isNull()) { + errorCode = QQuickPixmapReply::Loading; + errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage())); + mutex.unlock(); + break; } - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(pixmap.toImage())); - mutex.unlock(); - } else { - QQuickTextureFactory *t = provider->requestTexture(imageId(url), &readSize, requestSize); - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; - QString errorStr; - if (!t) { - errorCode = QQuickPixmapReply::Loading; - errorStr = QQuickPixmap::tr("Failed to get texture from provider: %1").arg(url.toString()); + + case QQuickImageProvider::Texture: + { + QQuickTextureFactory *t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize); + QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; + QString errorStr; + if (!t) { + errorCode = QQuickPixmapReply::Loading; + errorStr = QQuickPixmap::tr("Failed to get texture from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, t); + else + delete t; + mutex.unlock(); + break; } - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, t); - else - delete t; - mutex.unlock(); + case QQuickImageProvider::ImageResponse: + { + QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider); + QQuickImageResponse *response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize); + + QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished())); + + asyncResponses.insert(response, runningJob); + break; + } } } else { - QString lf = QQmlFile::urlToLocalFileOrQrc(url); - if (!lf.isEmpty()) { + if (!localFile.isEmpty()) { // Image is local - load/decode immediately QImage image; QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; QString errorStr; - QFile f(lf); + QFile f(localFile); QSize readSize; if (f.open(QIODevice::ReadOnly)) { - if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize)) + if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize)) errorCode = QQuickPixmapReply::Loading; } else { errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); @@ -633,7 +709,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u } mutex.lock(); if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image)); + runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image)); mutex.unlock(); } else { // Network resource @@ -644,7 +720,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); - replies.insert(reply, runningJob); + networkJobs.insert(reply, runningJob); } } } @@ -736,8 +812,6 @@ inline uint qHash(const QQuickPixmapKey &key) return qHash(*key.url) ^ key.size->width() ^ key.size->height(); } -class QSGContext; - class QQuickPixmapStore : public QObject { Q_OBJECT @@ -1044,7 +1118,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QImage image = provider->requestImage(imageId(url), &readSize, requestSize); if (!image.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(image), readSize, requestSize); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize); } } case QQuickImageProvider::Pixmap: @@ -1052,9 +1126,14 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, requestSize); if (!pixmap.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(pixmap.toImage()), readSize, requestSize); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize); } } + case QQuickImageProvider::ImageResponse: + { + // Fall through, ImageResponse providers never get here + Q_ASSERT(imageType != QQuickImageProvider::ImageResponse && "Sync call to ImageResponse provider"); + } } // provider has bad image type, or provider returned null image @@ -1075,7 +1154,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(image), readSize, requestSize); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize); } errorString = QQuickPixmap::tr("Invalid image data: %1").arg(url.toString()); @@ -1204,7 +1283,7 @@ void QQuickPixmap::setImage(const QImage &p) clear(); if (!p.isNull()) - d = new QQuickPixmapData(this, textureFactoryForImage(p)); + d = new QQuickPixmapData(this, QQuickTextureFactory::textureFactoryForImage(p)); } void QQuickPixmap::setPixmap(const QQuickPixmap &other) |