5#include <QtQuick/private/qquickpixmapcache_p.h>
6#include <QtQuick/private/qquickimageprovider_p.h>
7#include <QtQuick/private/qquickprofiler_p.h>
8#include <QtQuick/private/qsgcontext_p.h>
9#include <QtQuick/private/qsgrenderer_p.h>
10#include <QtQuick/private/qsgtexturereader_p.h>
11#include <QtQuick/qquickwindow.h>
13#include <QtGui/private/qguiapplication_p.h>
14#include <QtGui/private/qimage_p.h>
15#include <QtGui/qpa/qplatformintegration.h>
16#include <QtGui/qimagereader.h>
17#include <QtGui/qpixmapcache.h>
19#include <QtQml/private/qqmlglobal_p.h>
20#include <QtQml/private/qqmlengine_p.h>
21#include <QtQml/qqmlfile.h>
23#include <QtCore/private/qobject_p.h>
24#include <QtCore/qcoreapplication.h>
25#include <QtCore/qhash.h>
26#include <QtCore/qfile.h>
27#include <QtCore/qthread.h>
28#include <QtCore/qmutex.h>
29#include <QtCore/qbuffer.h>
30#include <QtCore/qdebug.h>
31#include <QtCore/qmetaobject.h>
32#include <QtCore/qscopeguard.h>
34#if QT_CONFIG(qml_network)
35#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
36#include <QtNetwork/qnetworkreply.h>
37#include <QtNetwork/qsslerror.h>
40#include <private/qdebug_p.h>
42#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8
45#define CACHE_EXPIRE_TIME 30
48#define CACHE_REMOVAL_FRACTION 4
50#define PIXMAP_PROFILE(Code) Q_QUICK_PROFILE(QQuickProfiler::ProfilePixmapCache, Code)
52#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
53# define USE_THREADED_DOWNLOAD 1
55# define USE_THREADED_DOWNLOAD 0
60using namespace Qt::Literals::StringLiterals;
62#if defined(QT_DEBUG) && QT_CONFIG(thread)
63class ThreadAffinityMarker
66 ThreadAffinityMarker() { attachToCurrentThread(); }
68 void assertOnAssignedThread()
70 QMutexLocker locker(&m_mutex);
71 if (!m_assignedThread)
72 attachToCurrentThread();
73 Q_ASSERT_X(m_assignedThread == QThread::currentThreadId(), Q_FUNC_INFO,
74 "Running on a wrong thread!");
77 void detachFromCurrentThread()
79 QMutexLocker locker(&m_mutex);
80 m_assignedThread =
nullptr;
83 void attachToCurrentThread() { m_assignedThread = QThread::currentThreadId(); }
86 Qt::HANDLE m_assignedThread;
89# define Q_THREAD_AFFINITY_MARKER(x) ThreadAffinityMarker x
90# define Q_ASSERT_CALLED_ON_VALID_THREAD(x) x.assertOnAssignedThread()
91# define Q_DETACH_THREAD_AFFINITY_MARKER(x) x.detachFromCurrentThread()
93# define Q_THREAD_AFFINITY_MARKER(x)
94# define Q_ASSERT_CALLED_ON_VALID_THREAD(x)
95# define Q_DETACH_THREAD_AFFINITY_MARKER(x)
98const QLatin1String QQuickPixmap::itemGrabberScheme = QLatin1String(
"itemgrabber");
103
104
105
115 return url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1);
118QQuickDefaultTextureFactory::QQuickDefaultTextureFactory(
const QImage &image)
120 if (image.format() == QImage::Format_ARGB32_Premultiplied ||
121 image.format() == QImage::Format_RGB32 ||
122 image.format() == QImage::Format_RGBA16FPx4_Premultiplied ||
123 image.format() == QImage::Format_RGBA16FPx4 ||
124 image.format() == QImage::Format_RGBX16FPx4 ||
125 image.format() == QImage::Format_RGBA32FPx4_Premultiplied ||
126 image.format() == QImage::Format_RGBA32FPx4 ||
127 image.format() == QImage::Format_RGBX32FPx4) {
130 im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
136QSGTexture *QQuickDefaultTextureFactory::createTexture(QQuickWindow *window)
const
138 QSGTexture *t = window->createTextureFromImage(im, QQuickWindow::TextureCanUseAtlas);
139 static bool transient = qEnvironmentVariableIsSet(
"QSG_TRANSIENT_IMAGES");
141 const_cast<QQuickDefaultTextureFactory *>(
this)->im = QImage();
195
196
246 void processJob(
QQuickPixmapReply *,
const QUrl &,
const QString &, QQuickImageProvider::ImageType,
const QSharedPointer<QQuickImageProvider> &);
247#if QT_CONFIG(qml_network)
250 void asyncResponseFinished(QQuickImageResponse *);
252 QList<QQuickPixmapReply*> jobs;
253 QList<QQuickPixmapReply *> cancelledJobs;
256#if QT_CONFIG(quick_pixmap_cache_threaded_download)
258
259
269
270
273 return ownedReaderThreadExecutionEnforcer.get();
275 std::unique_ptr<ReaderThreadExecutionEnforcer> ownedReaderThreadExecutionEnforcer;
278#if QT_CONFIG(qml_network)
283 QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses;
288 static int replyDownloadProgressMethodIndex;
289 static int replyFinishedMethodIndex;
290 static int downloadProgressMethodIndex;
291 static int threadNetworkRequestDoneMethodIndex;
292 static QHash<QQmlEngine *,QQuickPixmapReader*> readers;
297#if QT_CONFIG(quick_pixmap_cache_threaded_download)
298# define PIXMAP_READER_LOCK() QMutexLocker locker(&mutex)
300# define PIXMAP_READER_LOCK()
303class QQuickPixmapCache;
306
307
312 const QQuickImageProviderOptions &po,
const QString &e)
324 QQuickPixmapData(
const QUrl &u,
const QRect &r,
const QSize &s,
const QQuickImageProviderOptions &po,
325 QQuickImageProviderOptions::AutoTransform aTransform,
int frame=0,
int frameCount=1, qreal dpr = 1)
338 const QSize &s,
const QRect &r,
const QSize &rs,
const QQuickImageProviderOptions &po,
339 QQuickImageProviderOptions::AutoTransform aTransform,
int frame=0,
int frameCount=1, qreal dpr = 1)
361 requestSize = implicitSize = texture->textureSize();
366 delete textureFactory;
371 void release(QQuickPixmapCache *store =
nullptr);
427 const QSize &implicitSize, QQuickTextureFactory *factory)
430 QCoreApplication::postEvent(
this,
new Event(error, errorString, implicitSize, factory));
440 delete textureFactory;
443#if QT_CONFIG(qml_network)
444QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
446 if (!accessManager) {
447 Q_ASSERT(readerThreadExecutionEnforcer());
448 accessManager = QQmlTypeLoader::get(engine)->createNetworkAccessManager(
449 readerThreadExecutionEnforcer());
451 return accessManager;
458 if (image->hasAlphaChannel() && image->data_ptr()
459 && !image->data_ptr()->checkForAlphaPixels()) {
460 switch (image->format()) {
461 case QImage::Format_RGBA8888:
462 case QImage::Format_RGBA8888_Premultiplied:
463 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX8888, Qt::AutoColor))
466 *image = image->convertToFormat(QImage::Format_RGBX8888);
468 case QImage::Format_A2BGR30_Premultiplied:
469 if (image->data_ptr()->convertInPlace(QImage::Format_BGR30, Qt::AutoColor))
472 *image = image->convertToFormat(QImage::Format_BGR30);
474 case QImage::Format_A2RGB30_Premultiplied:
475 if (image->data_ptr()->convertInPlace(QImage::Format_RGB30, Qt::AutoColor))
478 *image = image->convertToFormat(QImage::Format_RGB30);
480 case QImage::Format_RGBA16FPx4:
481 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX16FPx4, Qt::AutoColor))
484 *image = image->convertToFormat(QImage::Format_RGBX16FPx4);
486 case QImage::Format_RGBA32FPx4:
487 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX32FPx4, Qt::AutoColor))
490 *image = image->convertToFormat(QImage::Format_RGBX32FPx4);
493 if (image->data_ptr()->convertInPlace(QImage::Format_RGB32, Qt::AutoColor))
496 *image = image->convertToFormat(QImage::Format_RGB32);
502static bool readImage(
const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
int *frameCount,
503 const QRect &requestRegion,
const QSize &requestSize,
const QQuickImageProviderOptions &providerOptions,
504 QQuickImageProviderOptions::AutoTransform *appliedTransform =
nullptr,
int frame = 0,
505 qreal devicePixelRatio = 1.0)
507 QImageReader imgio(dev);
508 if (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform)
509 imgio.setAutoTransform(providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform);
510 else if (appliedTransform)
511 *appliedTransform = imgio.autoTransform() ? QQuickImageProviderOptions::ApplyTransform : QQuickImageProviderOptions::DoNotApplyTransform;
513 if (frame < imgio.imageCount())
514 imgio.jumpToImage(frame);
517 *frameCount = imgio.imageCount();
519 QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions, devicePixelRatio);
520 if (scSize.isValid())
521 imgio.setScaledSize(scSize);
522 if (!requestRegion.isNull())
523 imgio.setScaledClipRect(requestRegion);
524 const QSize originalSize = imgio.size();
525 qCDebug(lcImg) << url <<
"frame" << frame <<
"of" << imgio.imageCount()
526 <<
"requestRegion" << requestRegion <<
"QImageReader size" << originalSize <<
"-> scSize" << scSize;
529 *impsize = originalSize;
531 if (imgio.read(image)) {
532 maybeRemoveAlpha(image);
533 if (impsize && impsize->width() < 0)
534 *impsize = image->size();
535 if (providerOptions.targetColorSpace().isValid()) {
536 if (image->colorSpace().isValid())
537 image->convertToColorSpace(providerOptions.targetColorSpace());
539 image->setColorSpace(providerOptions.targetColorSpace());
544 *errorString = QQuickPixmap::tr(
"Error decoding: %1: %2").arg(url.toString())
545 .arg(imgio.errorString());
553 res.reserve(list.size());
554 for (
const QByteArray &item : list)
555 res.append(QString::fromLatin1(item));
564 delete QSGContext::createTextureFactoryFromImage(QImage());
565 hasOpenGL = QQuickWindow::sceneGraphBackend().isEmpty();
566 QList<QByteArray> list;
568 list.append(QSGTextureReader::supportedFileFormats());
569 list.append(QImageReader::supportedImageFormats());
570 fileSuffixes = fromLatin1List(list);
582 QFileInfo fi(localFile);
583 if (!fi.suffix().isEmpty() || fi.exists())
586 QString tryFile = localFile + QStringLiteral(
".xxxx");
587 const int suffixIdx = localFile.size() + 1;
588 for (
const QString &suffix : backendSupport()->fileSuffixes) {
589 tryFile.replace(suffixIdx, 10, suffix);
590 if (QFileInfo::exists(tryFile))
597: QThread(eng), engine(eng)
598#if QT_CONFIG(qml_network)
599, accessManager(
nullptr)
605 engine->handle()->typeLoader();
608#if QT_CONFIG(quick_pixmap_cache_threaded_download)
609 eventLoopQuitHack =
new QObject;
610 eventLoopQuitHack->moveToThread(
this);
611 QObject::connect(eventLoopQuitHack, &QObject::destroyed,
this, &QThread::quit, Qt::DirectConnection);
612 start(QThread::LowestPriority);
623 readers.remove(engine);
624 readerMutex.unlock();
629 for (QQuickPixmapReply *reply : std::as_const(jobs)) {
630 if (reply->data && reply->data->reply == reply)
631 reply->data->reply =
nullptr;
637 cancelledJobs.append(reply);
641#if QT_CONFIG(qml_network)
642 for (
auto *reply : std::as_const(networkJobs))
645 for (
auto *reply : std::as_const(asyncResponses))
647#if !QT_CONFIG(quick_pixmap_cache_threaded_download)
653 if (readerThreadExecutionEnforcer())
658#if QT_CONFIG(quick_pixmap_cache_threaded_download)
661 eventLoopQuitHack->deleteLater();
675#if QT_CONFIG(qml_network)
676 for (QQuickPixmapReply *reply : std::as_const(networkJobs))
679 for (QQuickPixmapReply *reply : std::as_const(asyncResponses))
682#if QT_CONFIG(qml_network)
685 asyncResponses.clear();
688#if QT_CONFIG(qml_network)
689void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
691 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
693 QQuickPixmapReply *job = networkJobs.take(reply);
697 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
700 QQuickTextureFactory *factory =
nullptr;
701 if (reply->error()) {
702 error = QQuickPixmapReply::Loading;
703 errorString = reply->errorString();
705 QByteArray all = reply->readAll();
707 buff.open(QIODevice::ReadOnly);
708 QSGTextureReader texReader(&buff, reply->url().fileName());
709 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
710 factory = texReader.read();
712 readSize = factory->textureSize();
714 error = QQuickPixmapReply::Decoding;
715 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(reply->url().toString());
719 int const frame = job->data ? job->data->frame : 0;
720 const qreal dpr = job->data ? job->data->devicePixelRatio : 1;
721 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount,
722 job->requestRegion, job->requestSize, job->providerOptions,
nullptr, frame, dpr))
723 error = QQuickPixmapReply::Decoding;
725 job->data->frameCount = frameCount;
730 factory = QQuickTextureFactory::textureFactoryForImage(image);
732 PIXMAP_READER_LOCK();
733 if (!cancelledJobs.contains(job))
734 job->postReply(error, errorString, readSize, factory);
736 reply->deleteLater();
739 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
750 QQuickTextureFactory *t =
nullptr;
751 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
753 if (!response->errorString().isEmpty()) {
754 error = QQuickPixmapReply::Loading;
755 errorString = response->errorString();
757 t = response->textureFactory();
761 if (!cancelledJobs.contains(job))
762 job->postReply(error, errorString, t ? t->textureSize() : QSize(), t);
766 response->deleteLater();
776 QCoreApplication::postEvent(
777 this,
new QEvent(QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs)));
783 case QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs):
784 reader->processJobs();
787 return QObject::event(e);
793#if QT_CONFIG(qml_network)
794 QNetworkReply *reply =
static_cast<QNetworkReply *>(sender());
795 reader->networkRequestDone(reply);
801 reader->asyncResponseFinished(response);
806 QQuickImageResponse *response =
static_cast<QQuickImageResponse *>(sender());
816 if (cancelledJobs.isEmpty() && jobs.isEmpty())
820 if (!cancelledJobs.isEmpty()) {
821 for (
int i = 0; i < cancelledJobs.size(); ++i) {
823#if QT_CONFIG(qml_network)
824 QNetworkReply *reply = networkJobs.key(job, 0);
826 networkJobs.remove(reply);
827 if (reply->isRunning()) {
834 QQuickImageResponse *asyncResponse = asyncResponses.key(job);
836 asyncResponses.remove(asyncResponse);
837 asyncResponse->cancel();
840 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url));
844 cancelledJobs.clear();
847 if (!jobs.isEmpty()) {
849 bool usableJob =
false;
850 for (
int i = jobs.size() - 1; !usableJob && i >= 0; i--) {
852 const QUrl url = job->url;
854 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
855 QSharedPointer<QQuickImageProvider> provider;
857 if (url.scheme() == QLatin1String(
"image")) {
858 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
859 provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>();
861 imageType = provider->imageType();
865 localFile = QQmlFile::urlToLocalFileOrQrc(url);
866 usableJob = !localFile.isEmpty()
867#if QT_CONFIG(qml_network)
868 || networkJobs.size() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
878 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
880#if QT_CONFIG(quick_pixmap_cache_threaded_download)
882 auto relockMutexGuard = qScopeGuard(([&locker]() {
886 processJob(job, url, localFile, imageType, provider);
897 QQuickImageProvider::ImageType imageType,
const QSharedPointer<QQuickImageProvider> &provider)
902 if (url.scheme() == QLatin1String(
"image")) {
906 if (imageType == QQuickImageProvider::Invalid) {
907 QString errorStr = QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString());
909 if (!cancelledJobs.contains(runningJob))
910 runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize,
nullptr);
915 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
918 case QQuickImageProvider::Invalid:
924 case QQuickImageProvider::Image:
928 image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
930 image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize);
932 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
934 if (image.isNull()) {
935 errorCode = QQuickPixmapReply::Loading;
936 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
939 if (!cancelledJobs.contains(runningJob)) {
940 runningJob->postReply(errorCode, errorStr, readSize,
941 QQuickTextureFactory::textureFactoryForImage(image));
946 case QQuickImageProvider::Pixmap:
950 pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
952 pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize);
954 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
956 if (pixmap.isNull()) {
957 errorCode = QQuickPixmapReply::Loading;
958 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
962 if (!cancelledJobs.contains(runningJob)) {
963 runningJob->postReply(
964 errorCode, errorStr, readSize,
965 QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()));
970 case QQuickImageProvider::Texture:
972 QQuickTextureFactory *t;
974 t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
976 t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize);
978 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
981 errorCode = QQuickPixmapReply::Loading;
982 errorStr = QQuickPixmap::tr(
"Failed to get texture from provider: %1").arg(url.toString());
985 if (!cancelledJobs.contains(runningJob))
986 runningJob->postReply(errorCode, errorStr, readSize, t);
992 case QQuickImageProvider::ImageResponse:
994 QQuickImageResponse *response;
996 response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions);
998 QQuickAsyncImageProvider *asyncProvider =
static_cast<QQuickAsyncImageProvider*>(provider.get());
999 response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
1003 QObject::connect(response, &QQuickImageResponse::finished, readerThreadExecutionEnforcer(),
1004 qOverload<>(&ReaderThreadExecutionEnforcer::asyncResponseFinished));
1007 auto provider_copy = provider;
1008 QObject::connect(response, &QQuickImageResponse::destroyed, response, [provider_copy]() {
1016 if (
static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
1017 QMetaObject::invokeMethod(readerThreadExecutionEnforcer(),
"asyncResponseFinished",
1018 Qt::QueuedConnection,
1019 Q_ARG(QQuickImageResponse *, response));
1022 asyncResponses.insert(response, runningJob);
1028 if (!localFile.isEmpty()) {
1031 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
1036 auto specialDevice = runningJob
->data->specialDevice;
1037 if (specialDevice.isNull() || QObjectPrivate::get(specialDevice.data())->deleteLaterCalled) {
1038 qCDebug(lcImg) <<
"readImage job aborted" << url;
1045 if (!specialDevice->thread()) {
1046 qCDebug(lcQsgLeak) << specialDevice.data() <<
": changing thread affinity so that"
1047 << QThread::currentThread() <<
"will handle any deleteLater() calls";
1048 specialDevice->moveToThread(QThread::currentThread());
1050 if (!readImage(url, specialDevice.data(), &image, &errorStr, &readSize, &frameCount,
1051 runningJob->requestRegion, runningJob->requestSize,
1052 runningJob->providerOptions,
nullptr, runningJob->data->frame,
1053 runningJob->data->devicePixelRatio)) {
1054 errorCode = QQuickPixmapReply::Loading;
1055 }
else if (runningJob
->data) {
1059 QFile f(existingImageFileForPath(localFile));
1060 if (f.open(QIODevice::ReadOnly)) {
1061 QSGTextureReader texReader(&f, localFile);
1062 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1063 QQuickTextureFactory *factory = texReader.read();
1065 readSize = factory->textureSize();
1067 errorStr = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1068 if (f.fileName() != localFile)
1069 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1070 errorCode = QQuickPixmapReply::Decoding;
1073 if (!cancelledJobs.contains(runningJob))
1074 runningJob->postReply(errorCode, errorStr, readSize, factory);
1079 const qreal dpr = runningJob
->data ? runningJob
->data->devicePixelRatio : 1;
1080 if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
1081 runningJob->requestRegion, runningJob->requestSize,
1082 runningJob->providerOptions,
nullptr, frame, dpr)) {
1083 errorCode = QQuickPixmapReply::Loading;
1084 if (f.fileName() != localFile)
1085 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1086 }
else if (runningJob
->data) {
1091 errorStr = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1092 errorCode = QQuickPixmapReply::Loading;
1096 if (!cancelledJobs.contains(runningJob)) {
1097 runningJob->postReply(errorCode, errorStr, readSize,
1098 QQuickTextureFactory::textureFactoryForImage(image));
1101#if QT_CONFIG(qml_network)
1103 QNetworkRequest req(url);
1104 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
1105 QNetworkReply *reply = networkAccessManager()->get(req);
1107 QMetaObject::connect(reply, replyDownloadProgressMethodIndex, runningJob,
1108 downloadProgressMethodIndex);
1109 QMetaObject::connect(reply, replyFinishedMethodIndex, readerThreadExecutionEnforcer(),
1110 threadNetworkRequestDoneMethodIndex);
1112 networkJobs.insert(reply, runningJob);
1126 readers.insert(engine, reader);
1135 return readers.value(engine, 0);
1141 reply->engineForReader = engine;
1149 if (readerThreadExecutionEnforcer())
1157 cancelledJobs.append(reply);
1160 if (readerThreadExecutionEnforcer())
1165 if (jobs.removeAll(reply) == 0) {
1166 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(reply->url));
1176 if (replyDownloadProgressMethodIndex == -1) {
1177#if QT_CONFIG(qml_network)
1178 replyDownloadProgressMethodIndex =
1179 QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
1180 replyFinishedMethodIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
1181 const QMetaObject *ir = &ReaderThreadExecutionEnforcer::staticMetaObject;
1182 threadNetworkRequestDoneMethodIndex = ir->indexOfSlot(
"networkRequestDone()");
1184 downloadProgressMethodIndex =
1185 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1188#if QT_CONFIG(quick_pixmap_cache_threaded_download)
1189 const auto guard = qScopeGuard([
this]() {
1191 PIXMAP_READER_LOCK();
1192 delete runLoopReaderThreadExecutionEnforcer;
1193 runLoopReaderThreadExecutionEnforcer =
nullptr;
1197 PIXMAP_READER_LOCK();
1198 Q_ASSERT(!runLoopReaderThreadExecutionEnforcer);
1199 runLoopReaderThreadExecutionEnforcer =
new ReaderThreadExecutionEnforcer(
this);
1205 ownedReaderThreadExecutionEnforcer = std::make_unique<ReaderThreadExecutionEnforcer>(
this);
1210inline bool operator==(
const QQuickPixmapKey &lhs,
const QQuickPixmapKey &rhs)
1212 return *lhs.url == *rhs.url &&
1213 *lhs.region == *rhs.region &&
1214 *lhs.size == *rhs.size &&
1215 lhs.frame == rhs.frame &&
1216 lhs.options == rhs.options;
1221 return qHashMulti(seed, *key.url, *key.region, *key.size, key.frame, key.options.autoTransform());
1224#ifndef QT_NO_DEBUG_STREAM
1227 QDebugStateSaver saver(debug);
1230 debug <<
"QQuickPixmapKey(0)";
1234 debug <<
"QQuickPixmapKey(" << key.url->toString() <<
" frame=" << key.frame;
1235 if (!key.region->isEmpty()) {
1236 debug <<
" region=";
1237 QtDebugUtils::formatQRect(debug, *key.region);
1239 if (!key.size->isEmpty()) {
1241 QtDebugUtils::formatQSize(debug, *key.size);
1248QQuickPixmapCache *QQuickPixmapCache::instance()
1250 static QQuickPixmapCache self;
1254QQuickPixmapCache::~QQuickPixmapCache()
1260
1261
1262
1263
1264
1265
1266
1267int QQuickPixmapCache::destroyCache()
1272 m_destroying =
true;
1280 int leakedPixmaps = 0;
1281 const auto cache = m_cache;
1282 for (
auto *pixmap : cache) {
1283 auto currRefCount = pixmap->refCount;
1286 qCDebug(lcQsgLeak) <<
"leaked pixmap: refCount" << pixmap->refCount << pixmap->url <<
"frame" << pixmap->frame
1287 <<
"size" << pixmap->requestSize <<
"region" << pixmap->requestRegion;
1288 while (currRefCount > 0) {
1289 pixmap->release(
this);
1296 while (m_lastUnreferencedPixmap)
1299 qCDebug(lcQsgLeak,
"Number of leaked pixmaps: %i", leakedPixmaps);
1300 return leakedPixmaps;
1303qsizetype QQuickPixmapCache::referencedCost()
const
1306 QMutexLocker locker(&m_cacheMutex);
1307 for (
const auto *pixmap : std::as_const(m_cache)) {
1308 if (pixmap->refCount)
1309 ret += pixmap->cost();
1315
1316
1317
1318void QQuickPixmapCache::unreferencePixmap(QQuickPixmapData *data)
1320 Q_ASSERT(data->prevUnreferenced ==
nullptr);
1321 Q_ASSERT(data->prevUnreferencedPtr ==
nullptr);
1322 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1324 data->nextUnreferenced = m_unreferencedPixmaps;
1325 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
1326 if (!m_destroying) {
1327 m_unreferencedCost += data->cost();
1328 qCDebug(lcImg) << data->url <<
"had cost" << data->cost() <<
"of total unreferenced" << m_unreferencedCost;
1331 m_unreferencedPixmaps = data;
1332 if (m_unreferencedPixmaps->nextUnreferenced) {
1333 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
1334 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
1337 if (!m_lastUnreferencedPixmap)
1338 m_lastUnreferencedPixmap = data;
1342 if (m_timerId == -1 && m_unreferencedPixmaps
1343 && !m_destroying && !QCoreApplication::closingDown()) {
1349
1350
1351
1352void QQuickPixmapCache::referencePixmap(QQuickPixmapData *data)
1354 Q_ASSERT(data->prevUnreferencedPtr);
1356 *data->prevUnreferencedPtr = data->nextUnreferenced;
1357 if (data->nextUnreferenced) {
1358 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
1359 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
1361 if (m_lastUnreferencedPixmap == data)
1362 m_lastUnreferencedPixmap = data->prevUnreferenced;
1364 data->nextUnreferenced =
nullptr;
1365 data->prevUnreferencedPtr =
nullptr;
1366 data->prevUnreferenced =
nullptr;
1368 m_unreferencedCost -= data->cost();
1369 qCDebug(lcImg) << data->url <<
"subtracts cost" << data->cost() <<
"of total" << m_unreferencedCost;
1373
1374
1375
1376void QQuickPixmapCache::shrinkCache(
int remove)
1378 qCDebug(lcImg) <<
"reduce unreferenced cost" << m_unreferencedCost <<
"to less than limit" << cache_limit;
1379 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
1380 QQuickPixmapData *data = m_lastUnreferencedPixmap;
1381 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1383 *data->prevUnreferencedPtr =
nullptr;
1384 m_lastUnreferencedPixmap = data->prevUnreferenced;
1385 data->prevUnreferencedPtr =
nullptr;
1386 data->prevUnreferenced =
nullptr;
1388 if (!m_destroying) {
1389 remove -= data->cost();
1390 m_unreferencedCost -= data->cost();
1392 data->removeFromCache(
this);
1397void QQuickPixmapCache::timerEvent(QTimerEvent *)
1401 shrinkCache(removalCost);
1403 if (m_unreferencedPixmaps ==
nullptr) {
1404 killTimer(m_timerId);
1409void QQuickPixmapCache::purgeCache()
1411 shrinkCache(m_unreferencedCost);
1414void QQuickPixmap::purgeCache()
1416 QQuickPixmapCache::instance()->purgeCache();
1423 if (finishedMethodIndex == -1) {
1424 finishedMethodIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex();
1425 downloadProgressMethodIndex =
1426 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1438 if (event->type() == QEvent::User) {
1442 data->pixmapStatus = (de->error == NoError) ? QQuickPixmap::Ready : QQuickPixmap::Error;
1443 if (data->pixmapStatus == QQuickPixmap::Ready) {
1444 data->textureFactory = de->textureFactory;
1445 de->textureFactory =
nullptr;
1446 data->implicitSize = de->implicitSize;
1448 data->textureFactory !=
nullptr &&
data->textureFactory->textureSize().isValid() ?
1449 data->textureFactory->textureSize() :
1450 (
data->requestSize.isValid() ?
data->requestSize :
data->implicitSize)));
1452 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(data->url));
1453 data->errorString = de->errorString;
1460 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1466 return QObject::event(event);
1473 return textureFactory->textureByteCount();
1480 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1481 if (prevUnreferencedPtr)
1482 QQuickPixmapCache::instance()->referencePixmap(
this);
1487 Q_ASSERT(refCount > 0);
1489 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1490 if (refCount == 0) {
1495 QQuickPixmapReader::readerMutex.lock();
1496 QQuickPixmapReader *reader = QQuickPixmapReader::existingInstance(cancelReply->engineForReader);
1499 QQuickPixmapReader::readerMutex.unlock();
1502 store = store ? store : QQuickPixmapCache::instance();
1503 if (pixmapStatus == QQuickPixmap::Ready
1509 store->unreferencePixmap(
this);
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1533 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1534 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1535 if (lcImg().isDebugEnabled()) {
1536 qCDebug(lcImg) <<
"adding" << key <<
"to total" << QQuickPixmapCache::instance()->m_cache.size();
1537 for (
auto it = QQuickPixmapCache::instance()->m_cache.keyBegin(); it != QQuickPixmapCache::instance()->m_cache.keyEnd(); ++it) {
1538 if (*(it->url) == url && it->frame == frame)
1539 qCDebug(lcImg) <<
" similar pre-existing:" << *it;
1542 QQuickPixmapCache::instance()->m_cache.insert(key,
this);
1544 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1545 url, QQuickPixmapCache::instance()->m_cache.size()));
1553 store = QQuickPixmapCache::instance();
1554 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1555 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1556 store->m_cache.remove(key);
1557 qCDebug(lcImg) <<
"removed" << key << implicitSize <<
"; total remaining" << QQuickPixmapCache::instance()->m_cache.size();
1559 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1560 url, store->m_cache.size()));
1565 const QRect &requestRegion,
const QSize &requestSize,
1566 const QQuickImageProviderOptions &providerOptions,
int frame,
bool *ok,
1567 qreal devicePixelRatio)
1569 if (url.scheme() == QLatin1String(
"image")) {
1572 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
1573 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1574 QSharedPointer<QQuickImageProvider> provider = enginePrivate->imageProvider(imageProviderId(url)).objectCast<QQuickImageProvider>();
1576 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
1578 imageType = provider->imageType();
1580 switch (imageType) {
1581 case QQuickImageProvider::Invalid:
1582 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1583 QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString()));
1584 case QQuickImageProvider::Texture:
1586 QQuickTextureFactory *texture = providerV2 ? providerV2->requestTexture(imageId(url), &readSize, requestSize, providerOptions)
1587 : provider->requestTexture(imageId(url), &readSize, requestSize);
1590 return new QQuickPixmapData(url, texture, readSize, requestRegion, requestSize,
1591 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1596 case QQuickImageProvider::Image:
1598 QImage image = providerV2 ? providerV2->requestImage(imageId(url), &readSize, requestSize, providerOptions)
1599 : provider->requestImage(imageId(url), &readSize, requestSize);
1600 if (!image.isNull()) {
1602 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image),
1603 readSize, requestRegion, requestSize, providerOptions,
1604 QQuickImageProviderOptions::UsePluginDefaultTransform,
1605 frame, 1, devicePixelRatio);
1609 case QQuickImageProvider::Pixmap:
1611 QPixmap pixmap = providerV2 ? providerV2->requestPixmap(imageId(url), &readSize, requestSize, providerOptions)
1612 : provider->requestPixmap(imageId(url), &readSize, requestSize);
1613 if (!pixmap.isNull()) {
1615 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()),
1616 readSize, requestRegion, requestSize, providerOptions,
1617 QQuickImageProviderOptions::UsePluginDefaultTransform,
1618 frame, 1, devicePixelRatio);
1622 case QQuickImageProvider::ImageResponse:
1625 Q_ASSERT(imageType != QQuickImageProvider::ImageResponse &&
"Sync call to ImageResponse provider");
1630 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1631 QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString()));
1634 QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
1635 if (localFile.isEmpty())
1638 QFile f(existingImageFileForPath(localFile));
1640 QString errorString;
1642 if (f.open(QIODevice::ReadOnly)) {
1643 QSGTextureReader texReader(&f, localFile);
1644 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1645 QQuickTextureFactory *factory = texReader.read();
1648 return new QQuickPixmapData(url, factory, factory->textureSize(), requestRegion, requestSize,
1649 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1651 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1652 if (f.fileName() != localFile)
1653 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1657 QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
1659 if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
1660 providerOptions, &appliedTransform, frame, devicePixelRatio)) {
1662 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
1663 providerOptions, appliedTransform, frame, frameCount);
1664 }
else if (f.fileName() != localFile) {
1665 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1669 errorString = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1671 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, errorString);
1682QQuickPixmap::QQuickPixmap()
1687QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url)
1693QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url, Options options)
1696 load(engine, url, options);
1699QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url,
const QRect ®ion,
const QSize &size)
1702 load(engine, url, region, size);
1705QQuickPixmap::QQuickPixmap(
const QUrl &url,
const QImage &image)
1707 d =
new QQuickPixmapData(url,
new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
1708 QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform);
1712QQuickPixmap::~QQuickPixmap()
1720bool QQuickPixmap::isNull()
const
1722 return d ==
nullptr;
1725bool QQuickPixmap::isReady()
const
1727 return status() == Ready;
1730bool QQuickPixmap::isError()
const
1732 return status() == Error;
1735bool QQuickPixmap::isLoading()
const
1737 return status() == Loading;
1740QString QQuickPixmap::error()
const
1743 return d->errorString;
1748QQuickPixmap::Status QQuickPixmap::status()
const
1751 return d->pixmapStatus;
1756const QUrl &QQuickPixmap::url()
const
1761 return nullPixmap()->url;
1764const QSize &QQuickPixmap::implicitSize()
const
1767 return d->implicitSize;
1769 return nullPixmap()->size;
1772const QSize &QQuickPixmap::requestSize()
const
1775 return d->requestSize;
1777 return nullPixmap()->size;
1780const QRect &QQuickPixmap::requestRegion()
const
1783 return d->requestRegion;
1785 return nullPixmap()->region;
1788QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform()
const
1791 return d->appliedTransform;
1793 return QQuickImageProviderOptions::UsePluginDefaultTransform;
1796int QQuickPixmap::frameCount()
const
1799 return d->frameCount;
1803QQuickTextureFactory *QQuickPixmap::textureFactory()
const
1806 return d->textureFactory;
1811QImage QQuickPixmap::image()
const
1813 if (d && d->textureFactory)
1814 return d->textureFactory->image();
1818void QQuickPixmap::setImage(
const QImage &p)
1825 d =
new QQuickPixmapData(QQuickTextureFactory::textureFactoryForImage(p));
1829void QQuickPixmap::setPixmap(
const QQuickPixmap &other)
1843int QQuickPixmap::width()
const
1845 if (d && d->textureFactory)
1846 return d->textureFactory->textureSize().width();
1851int QQuickPixmap::height()
const
1853 if (d && d->textureFactory)
1854 return d->textureFactory->textureSize().height();
1859QRect QQuickPixmap::rect()
const
1861 if (d && d->textureFactory)
1862 return QRect(QPoint(), d->textureFactory->textureSize());
1867void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url)
1869 load(engine, url, QRect(), QSize(), QQuickPixmap::Cache);
1872void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url, QQuickPixmap::Options options)
1874 load(engine, url, QRect(), QSize(), options);
1877void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize)
1879 load(engine, url, requestRegion, requestSize, QQuickPixmap::Cache);
1882void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize, QQuickPixmap::Options options)
1884 load(engine, url, requestRegion, requestSize, options, QQuickImageProviderOptions());
1887void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
1888 QQuickPixmap::Options options,
const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount,
1889 qreal devicePixelRatio)
1896 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1897 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1899 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1900 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1903 QQuickPixmap::Options orgOptions = options;
1906 options |= QQuickPixmap::Cache;
1912 if (url.scheme() == itemGrabberScheme) {
1915 if (requestSize != dummySize)
1916 qWarning() <<
"Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead.";
1917 const QQuickPixmapKey grabberKey = { &url, &dummyRegion, &dummySize, 0, QQuickImageProviderOptions() };
1918 iter = store->m_cache.find(grabberKey);
1919 }
else if (options & QQuickPixmap::Cache)
1920 iter = store->m_cache.find(key);
1922 if (iter == store->m_cache.end()) {
1928 if (url.scheme() == QLatin1String(
"image")) {
1929 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1930 if (
auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
1931 const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps);
1932 if (!threadedPixmaps && provider->imageType() == QQuickImageProvider::Pixmap) {
1934 options &= ~QQuickPixmap::Asynchronous;
1935 }
else if (provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading) {
1936 options |= QQuickPixmap::Asynchronous;
1941 if (!(options & QQuickPixmap::Asynchronous)) {
1943 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
1944 d = createPixmapDataSync(engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
1946 PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
1947 if (options & QQuickPixmap::Cache)
1950 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1955 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1960 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1961 QQuickImageProviderOptions::UsePluginDefaultTransform, frame,
1962 frameCount, devicePixelRatio);
1963 if (options & QQuickPixmap::Cache)
1966 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1969 QQuickPixmapReader::readerMutex.lock();
1970 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
1971 d->reply = reader->getImage(d);
1972 reader->startJob(d->reply);
1973 QQuickPixmapReader::readerMutex.unlock();
1977 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame;
1982
1983
1984
1985
1986
1987
1988void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device,
const QUrl &url,
1989 const QRect &requestRegion,
const QSize &requestSize,
1990 const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount)
1993 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1994 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1995 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1996 QMutexLocker locker(&store->m_cacheMutex);
1997 iter = store->m_cache.find(key);
1998 if (iter == store->m_cache.end()) {
2003 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
2004 QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
2005 d->specialDevice = device;
2006 d->fromSpecialDevice =
true;
2009 QQuickPixmapReader::readerMutex.lock();
2010 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
2011 d->reply = reader->getImage(d);
2013 QObject::connect(d->reply, &QQuickPixmapReply::destroyed, store, [oldD]() {
2015 }, Qt::QueuedConnection);
2017 reader->startJob(d->reply);
2018 QQuickPixmapReader::readerMutex.unlock();
2022 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame <<
"refCount" << d->refCount;
2029void QQuickPixmap::clear()
2037void QQuickPixmap::clear(QObject *obj)
2041 QObject::disconnect(d->reply,
nullptr, obj,
nullptr);
2047bool QQuickPixmap::isCached(
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
2048 const int frame,
const QQuickImageProviderOptions &options)
2050 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, options };
2051 QQuickPixmapCache *store = QQuickPixmapCache::instance();
2053 return store->m_cache.contains(key);
2056bool QQuickPixmap::isScalableImageFormat(
const QUrl &url)
2058 if (url.scheme() ==
"image"_L1)
2061 const QString stringUrl = url.path(QUrl::PrettyDecoded);
2062 return stringUrl.endsWith(
"svg"_L1)
2063 || stringUrl.endsWith(
"svgz"_L1)
2064 || stringUrl.endsWith(
"pdf"_L1);
2067bool QQuickPixmap::connectFinished(QObject *object,
const char *method)
2069 if (!d || !d->reply) {
2070 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2074 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
2077bool QQuickPixmap::connectFinished(QObject *object,
int method)
2079 if (!d || !d->reply) {
2080 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2084 return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedMethodIndex, object, method);
2087bool QQuickPixmap::connectDownloadProgress(QObject *object,
const char *method)
2089 if (!d || !d->reply) {
2090 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2094 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object,
2098bool QQuickPixmap::connectDownloadProgress(QObject *object,
int method)
2100 if (!d || !d->reply) {
2101 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2105 return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressMethodIndex, object,
2109QColorSpace QQuickPixmap::colorSpace()
const
2111 if (!d || !d->textureFactory)
2112 return QColorSpace();
2113 return d->textureFactory->image().colorSpace();
2118#include <qquickpixmapcache.moc>
2120#include "moc_qquickpixmap_p.cpp"
2121#include "moc_qquickpixmapcache_p.cpp"
friend bool operator==(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept
Returns true if lhs and rhs are equal, otherwise returns false.
QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1, qreal dpr=1)
QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po, const QString &e)
QQuickImageProviderOptions::AutoTransform appliedTransform
QQuickPixmapData(const QUrl &u, QQuickTextureFactory *texture, const QSize &s, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1, qreal dpr=1)
QColorSpace targetColorSpace
QQuickPixmapData(QQuickTextureFactory *texture)
void release(QQuickPixmapCache *store=nullptr)
QPointer< QIODevice > specialDevice
QQuickPixmapData ** prevUnreferencedPtr
QQuickPixmap::Status pixmapStatus
QQuickImageProviderOptions providerOptions
QQuickPixmapData * nextUnreferenced
QQuickTextureFactory * textureFactory
void removeFromCache(QQuickPixmapCache *store=nullptr)
QQuickPixmapData * prevUnreferenced
QQuickPixmapReply * reply
static QQuickPixmapReader * instance(QQmlEngine *engine)
static QMutex readerMutex
void startJob(QQuickPixmapReply *job)
void cancel(QQuickPixmapReply *rep)
QQuickPixmapReply * getImage(QQuickPixmapData *)
static QQuickPixmapReader * existingInstance(QQmlEngine *engine)
QQuickTextureFactory * textureFactory
Event(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory)
QQmlEngine * engineForReader
void downloadProgress(qint64, qint64)
static int downloadProgressMethodIndex
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
void postReply(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory)
QQuickImageProviderOptions providerOptions
QQuickPixmapReply(QQuickPixmapData *)
void processJobsOnReaderThreadLater()
void asyncResponseFinished()
ReaderThreadExecutionEnforcer(QQuickPixmapReader *reader)
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
Q_GLOBAL_STATIC(BackendSupport, backendSupport)
#define CACHE_EXPIRE_TIME
static QString imageId(const QUrl &url)
#define Q_ASSERT_CALLED_ON_VALID_THREAD(x)
static QString existingImageFileForPath(const QString &localFile)
#define PIXMAP_READER_LOCK()
static void maybeRemoveAlpha(QImage *image)
static QQuickPixmapData * createPixmapDataSync(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, int frame, bool *ok, qreal devicePixelRatio)
#define Q_DETACH_THREAD_AFFINITY_MARKER(x)
#define PIXMAP_PROFILE(Code)
Q_GLOBAL_STATIC(QQuickPixmapNull, nullPixmap)
static bool readImage(const QUrl &url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int *frameCount, const QRect &requestRegion, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, QQuickImageProviderOptions::AutoTransform *appliedTransform=nullptr, int frame=0, qreal devicePixelRatio=1.0)
#define Q_THREAD_AFFINITY_MARKER(x)
static QStringList fromLatin1List(const QList< QByteArray > &list)
#define CACHE_REMOVAL_FRACTION
static QString imageProviderId(const QUrl &url)
constexpr size_t qHash(const QSize &s, size_t seed=0) noexcept